pax_global_header00006660000000000000000000000064132205120300014476gustar00rootroot0000000000000052 comment=ccba52a89ed103ca94c0a6be57581586676c2c3f dcm2niix-1.0.20171215/000077500000000000000000000000001322051203000137545ustar00rootroot00000000000000dcm2niix-1.0.20171215/.gitignore000066400000000000000000000000271322051203000157430ustar00rootroot00000000000000.DS_Store build/ bin/ dcm2niix-1.0.20171215/.gitmodules000066400000000000000000000001201322051203000161220ustar00rootroot00000000000000[submodule "dcm_qa"] path = dcm_qa url = http://github.com/neurolabusc/dcm_qa dcm2niix-1.0.20171215/.travis.yml000066400000000000000000000015641322051203000160730ustar00rootroot00000000000000language: cpp git: depth: 1 matrix: include: - os: linux dist: trusty env: TARGET=lnx - os: osx env: TARGET=mac before_install: - git submodule update --init --depth=3 dcm_qa script: - mkdir build && cd build && cmake -DBATCH_VERSION=ON -DUSE_OPENJPEG=ON .. && make && cd - - export PATH=$PWD/build/bin:$PATH - cd dcm_qa && ./batch.sh && cd - before_deploy: - export DATE=`date +%-d-%b-%Y` - zip -j dcm2niix_${DATE}_${TARGET}.zip build/bin/* - sleep 300 # make sure appveyor deployment is done, thus proper release name is set deploy: provider: releases api_key: secure: sVIYRakcEQdMPEdGSSePtMVCMQvaohqV7NNzEErAgZ+b/4ofv2aPpJb5kNTv3JRl2FrPy7iXJ8lOUQ/95pqvimX6jv5ztksTNXtSMnHZNbjjWwIc99enPY+mSdWMO2lb9vGBWQ9GNfXjmk7MgtDHPjjygbuZfUw9fmGy4ocxkws= file_glob: true file: dcm2niix*.zip skip_cleanup: true on: tags: true dcm2niix-1.0.20171215/BATCH.md000066400000000000000000000014651322051203000151250ustar00rootroot00000000000000**Optional batch processing version:** Perform a batch conversion of multiple dicoms using the configurations specified in a yaml file. ```bash dcm2niibatch batch_config.yml ``` The configuration file should be in yaml format as shown in example `batch_config.yaml` ```yaml Options: isGz: false isFlipY: false isVerbose: false isCreateBIDS: false isOnlySingleFile: false Files: - in_dir: /path/to/first/folder out_dir: /path/to/output/folder filename: dcemri - in_dir: /path/to/second/folder out_dir: /path/to/output/folder filename: fa3 ``` You can add as many files as you want to convert as long as this structure stays consistent. Note that a dash must separate each file. dcm2niix-1.0.20171215/CMakeLists.txt000066400000000000000000000002661322051203000165200ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8.11) if(COMMAND CMAKE_POLICY) CMAKE_POLICY(SET CMP0003 NEW) endif() project(dcm2niix) include(${CMAKE_SOURCE_DIR}/SuperBuild/SuperBuild.cmake) dcm2niix-1.0.20171215/COMPILE.md000066400000000000000000000261201322051203000153670ustar00rootroot00000000000000## About The README.md file describes the typical compilation of the software, using the `make` command to build the software. This document describes advanced methods for compiling and tuning the software. ## Choosing your compiler The text below generally describes how to build dcm2niix using the [GCC](https://gcc.gnu.org) compiler using the `g++` command. However, the code is portable and you can use different compilers. For [clang/llvm](https://clang.llvm.org) compile using `clang++`. If you have the [Intel C compiler](https://software.intel.com/en-us/c-compilers), you can substitute the `icc` command. For [Microsoft's C compiler](http://landinghub.visualstudio.com/visual-cpp-build-tools) you would use the `cl` command. In theory, the code should support other compilers, but this has not been tested. Be aware that if you do not have gcc installed the `g++` command may use a default to a compiler (e.g. clang). To check what compiler was used, run the dcm2niix software: it always reports the version and the compiler used for the build. ## Building the command line version without cmake You can also build the software without C-make. The easiest way to do this is to run the function "make" from the "console" folder. Note that this only creates the default version of dcm2niix, not the optional batch version described above. The make command simply calls the g++ compiler, and if you want you can tune this for your build. In essence, the make function simply calls ``` g++ -dead_strip -O3 -I. main_console.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp nii_foreign.cpp -o dcm2niix -DmyDisableOpenJPEG ``` The following sub-sections list how you can modify this basic recipe for your needs. ## Trouble Shooting Some [Centos/Redhat](https://github.com/rordenlab/dcm2niix/issues/137) may report "/usr/bin/ld: cannot find -lstdc++". This can be resolved by installing static versions of libstd: `yum install libstdc++-static`. ##### ZLIB BUILD If we have zlib, we can use it (-lz) and disable [miniz](https://code.google.com/p/miniz/) (-myDisableMiniZ) ``` g++ -O3 -DmyDisableOpenJPEG -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp jpg_0XC3.cpp ujpeg.cpp nii_foreign.cpp -dead_strip -o dcm2niix -lz -DmyDisableMiniZ ``` ##### MINGW BUILD If you use the (obsolete) compiler MinGW on Windows you will want to include the rare libgcc libraries with your executable so others can use it. Here I also demonstrate the optional "-DmyDisableZLib" to remove zip support. ``` g++ -O3 -s -DmyDisableOpenJPEG -DmyDisableZLib -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp jpg_0XC3.cpp ujpeg.cpp nii_foreign.cpp -o dcm2niix -static-libgcc ``` ##### DISABLING CLASSIC JPEG DICOM images can be stored as either raw data or compressed using one of many formats as described by the [transfer syntaxes](https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#Transfer_Syntaxes_and_Compressed_Images). One of the compressed formats is the lossy classic JPEG format (which is separate from and predates the lossy JPEG 2000 format). This software comes with the [NanoJPEG](http://keyj.emphy.de/nanojpeg/) library to handle these images. However, you can use the `myDisableClassicJPEG` compiler switch to remove this dependency. The resulting executable will be smaller but will not be able to convert images stored with this format. ``` g++ -dead_strip -O3 -I. main_console.cpp nii_dicom.cpp jpg_0XC3.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp nii_foreign.cpp -o dcm2niix -DmyDisableClassicJPEG -DmyDisableOpenJPEG ``` ##### USING LIBJPEG-TURBO TO DECODE CLASSIC JPEG By default, classic JPEG images will be decoded using the [compact NanoJPEG decoder](http://keyj.emphy.de/nanojpeg/). However, the compiler directive `myTurboJPEG` will create an executable based on the [libjpeg-turbo](http://www.libjpeg-turbo.org) library. This library is a faster decoder and is the standard for many Linux distributions. On the other hand, the lossy classic JPEG is rarely used for DICOM images, so this compilation has extra dependencies and can result in a larger executable size (for static builds). ``` g++ -dead_strip -O3 -I. main_console.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp nii_foreign.cpp -o dcm2niix -DmyDisableOpenJPEG -DmyTurboJPEG -I/opt/libjpeg-turbo/include /opt/libjpeg-turbo/lib/libturbojpeg.a ``` ##### JPEG2000 BUILD You can compile dcm2niix to convert DICOM images compressed with the JPEG2000 [transfer syntaxes 1.2.840.10008.1.2.4.90 and 1.2.840.10008.1.2.4.91](https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#Transfer_Syntaxes_and_Compressed_Images). This is optional, as JPEG2000 is very rare in DICOMs (usually only created by the proprietary DCMJP2K or OsiriX). Due to the challenges discussed below this is a poor choice for archiving DICOM data. Rather than support conversion with dcm2niix, a better solution would be to use DCMJP2K to do a DICOM-to-DICOM conversion to a more widely supported transfer syntax. Unfortunately, JPEG2000 saw poor adoption as a general image format. This situation is unlikely to change, as JPEG2000 only offered incremental benefits over the simpler classic JPEG, and is outperformed by the more recent HEIF. This has implications for DICOM, as there is little active development on libraries to decode JPEG2000. Indeed, the two popular open-source libraries that decode JPEG2000 have serious limitations for processing these images. Some JPEG2000 DICOM images can not be decoded by the default compilation of OpenJPEG library after version [2.1.0](https://github.com/uclouvain/openjpeg/issues/962). On the other hand, the Jasper library does not handle lossy [16-bit](https://en.wikipedia.org/wiki/JPEG_2000) images with good precision. You can build dcm2niix with JPEG2000 decompression support using OpenJPEG 2.1.0. You will need to have the OpenJPEG library installed (use the package manager of your linux distribution, Homebrew for macOS, or see [here](https://github.com/uclouvain/openjpeg/blob/master/INSTALL.md) if you want to build it yourself). If you want to use a more recent version of OpenJPEG, it must be custom-compiled with `-DOPJ_DISABLE_TPSOT_FIX` compiler flag. I suggest building static libraries where you would [download the code](https://github.com/uclouvain/openjpeg) and run ``` cmake -DBUILD_SHARED_LIBS:bool=off -DOPJ_DISABLE_TPSOT_FIX:bool=on . make sudo make install ``` You should then be able to run then run: ``` g++ -O3 -dead_strip -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp jpg_0XC3.cpp ujpeg.cpp nii_foreign.cpp -o dcm2niix -lopenjp2 ``` But in my experience this works best if you explicitly tell the software how to find the libraries, so your compile will probably look like one of these options: ``` #for MacOS g++ -O3 -dead_strip -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp jpg_0XC3.cpp ujpeg.cpp nii_foreign.cpp -o dcm2niix -I/usr/local/include/openjpeg-2.1 /usr/local/lib/libopenjp2.a ``` ``` #For older Linux g++ -O3 -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp jpg_0XC3.cpp ujpeg.cpp nii_foreign.cpp -o dcm2niix -I/usr/local/lib /usr/local/lib/libopenjp2.a ``` ``` #For modern Linux g++ -O3 -s -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp jpg_0XC3.cpp ujpeg.cpp nii_foreign.cpp -lpthread -o dcm2niix -I/usr/local/include/openjpeg-2.2 ~/openjpeg-master/build/bin/libopenjp2.a ``` If you want to build this with JPEG2000 decompression support using Jasper: You will need to have the Jasper (http://www.ece.uvic.ca/~frodo/jasper/) and libjpeg (http://www.ijg.org) libraries installed which for Linux users may be as easy as running 'sudo apt-get install libjasper-dev' (otherwise, see http://www.ece.uvic.ca/~frodo/jasper/#doc). You can then run: ``` g++ -O3 -DmyDisableOpenJPEG -DmyEnableJasper -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp jpg_0XC3.cpp ujpeg.cpp nii_foreign.cpp -s -o dcm2niix -ljasper -ljpeg ``` ##### VISUAL STUDIO BUILD This software can be compiled with VisualStudio 2015. This example assumes the compiler is in your path. ``` vcvarsall amd64 cl /EHsc main_console.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp nii_foreign.cpp -DmyDisableOpenJPEG /o dcm2niix ``` ##### OSX BUILD WITH BOTH 32 AND 64-BIT SUPPORT Building command line version universal binary from OSX 64 bit system: This requires a C compiler. With a terminal, change directory to the 'conosle' folder and run the following: ``` g++ -O3 -DmyDisableOpenJPEG -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp jpg_0XC3.cpp ujpeg.cpp nii_foreign.cpp -dead_strip -arch i386 -o dcm2niix32 ``` ``` g++ -O3 -DmyDisableOpenJPEG -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp jpg_0XC3.cpp ujpeg.cpp nii_foreign.cpp -dead_strip -o dcm2niix64 ``` ``` lipo -create dcm2niix32 dcm2niix64 -o dcm2niix ``` To validate that the resulting executable supports both architectures type ``` file ./dcm2niix ``` ##### OSX GRAPHICAL INTERFACE BUILD You can building the OSX graphical user interface using Xcode. First, Copy contents of "console" folder to /xcode/dcm2/core. Next, open and compile the project "dcm2.xcodeproj" with Xcode 4.6 or later ##### THE QT AND wxWIDGETS GUIs ARE NOT YET SUPPORTED - FOLLOWING LINES FOR FUTURE VERSIONS Building QT graphical user interface: Open "dcm2.pro" with QTCreator. This should work on OSX and Linux. On Windows the printf information is not redirected to the user interface If compile gives you grief look at the .pro file which has notes for different operating systems. Building using wxWidgets wxWdigets makefiles are pretty complex and specific for your operating system. For simplicity, we will build the "clipboard" example that comes with wxwidgets and then substitute our own code. The process goes something like this. a.) Install wxwdigets b.) successfully "make" the samples/clipboard program c.) DELETE console/makefile. WE DO NOT WANT TO OVERWRITE the WX MAKEFILE d.) with the exception of "makefile", copy the contents of console to /samples/clipboard e.) overwrite the original /samples/clipboard.cpp with the dcm2niix file of the same name f.) Older Xcodes have problems with .cpp files, whereas wxWidgets's makefiles do not compile with "-x cpp". So the core files are called '.c' but we will rename them to .cpp for wxWidgets: rename 's/\.c$/\.cpp/' * g.) edit the /samples/clipboard makefile: Add "nii_dicom.o nifti1_io_core.o nii_ortho.o nii_dicom_batch.o \" to CLIPBOARD_OBJECTS: CLIPBOARD_OBJECTS = \ nii_dicom.o nifti1_io_core.o nii_ortho.o nii_dicom_batch.o \ $(__clipboard___win32rc) \ $(__clipboard_os2_lib_res) \ clipboard_clipboard.o h.) edit the /samples/clipboard makefile: With wxWidgets we will capture std::cout comments, not printf, so we need to add "-DDmyUseCOut" to CXXFLAGS: CXXFLAGS = -DmyUseCOut -DWX_PRECOMP .... i.) For a full refresh rm clipboard rm *.o make dcm2niix-1.0.20171215/Dockerfile000066400000000000000000000010561322051203000157500ustar00rootroot00000000000000FROM ubuntu:trusty MAINTAINER # feel free to change/adopt # Install Dependencies RUN apt-get update && apt-get upgrade -y && \ apt-get install -y build-essential pkg-config cmake git pigz && \ apt-get clean -y && apt-get autoclean -y && apt-get autoremove -y # Get dcm2niix from github and compile RUN cd /tmp && \ git clone https://github.com/rordenlab/dcm2niix.git && \ cd dcm2niix && mkdir build && cd build && \ cmake -DBATCH_VERSION=ON -DUSE_OPENJPEG=ON .. && \ make && make install ENTRYPOINT ["/usr/local/bin/dcm2niix"] dcm2niix-1.0.20171215/README.md000066400000000000000000000120071322051203000152330ustar00rootroot00000000000000[![Build Status](https://travis-ci.org/rordenlab/dcm2niix.svg?branch=master)](https://travis-ci.org/rordenlab/dcm2niix) [![Build status](https://ci.appveyor.com/api/projects/status/7o0xp2fgbhadkgn1?svg=true)](https://ci.appveyor.com/project/neurolabusc/dcm2niix) ## About dcm2niix is a designed to convert neuroimaging data from the DICOM format to the NIfTI format. This web page hosts the developmental source code - a compiled version for Linux, MacOS, and Windows of the most recent stable release is included with [MRIcroGL](https://www.nitrc.org/projects/mricrogl/). A full manual for this software is available in the form of a [NITRC wiki](http://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage). ## License This software is open source. The bulk of the code is covered by the BSD license. Some units are either public domain (nifti*.*, miniz.c) or use the MIT license (ujpeg.cpp). See the license.txt file for more details. ## Dependencies This software should run on macOS, Linux and Windows typically without requiring any other software. However, if you use dcm2niix to create gz-compressed images it will be faster if you have [pigz](https://github.com/madler/pigz) installed. You can get a version of both dcm2niix and pigz compiled for your operating system by downloading [MRIcroGL](https://www.nitrc.org/projects/mricrogl/). ## Versions [See the VERSIONS.md file for details on releases](./VERSIONS.md). ## Running Command line usage is described in the [NITRC wiki](https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#General_Usage). The minimal command line call would be `dcm2niix /path/to/dicom/folder`. However, you may want to invoke additional options, for example the call `dcm2niix -z y -f %p_%t_%s -o /path/ouput /path/to/dicom/folder` will save data as gzip compressed, with the filename based on the protocol name (%p) acquisition time (%t) and DICOM series number (%s), with all files saved to the folder "output". For more help see help: `dcm2niix -h`. [See the BATCH.md file for instructions on using the batch processing version](./BATCH.md). ## Build ### Build command line version with cmake (Linux, MacOS, Windows) `cmake` and `pkg-config` (optional) can be installed as follows: Ubuntu: `sudo apt-get install cmake pkg-config` MacOS: `brew install cmake pkg-config` **To build:** ```bash mkdir build && cd build cmake .. make ``` `dcm2niix` will be created in the `bin` subfolder. To install on the system run `make install` instead of `make` - this will copy the executable to your path so you do not have to provide the full path to the executable. **optional building with OpenJPEG:** Support for JPEG2000 using OpenJPEG is optional. To build with OpenJPEG change the cmake command to `cmake -DUSE_OPENJPEG=ON ..`: ```bash mkdir build && cd build cmake -DUSE_OPENJPEG=ON .. make ``` **optional batch processing version:** The batch processing binary `dcm2niibatch` is optional. To build `dcm2niibatch` as well change the cmake command to `cmake -DBATCH_VERSION=ON ..`. This requires a compiler that supports c++11. ### Building the command line version without cmake If you have any problems with the cmake build script described above or want to customize the software see the [COMPILE.md file for details on manual compilation](./COMPILE.md). ## Links - [Dcm2Bids](https://github.com/cbedetti/Dcm2Bids) uses dcm2niix to create [BIDS](http://bids.neuroimaging.io/) datasets. - [bidskit](https://github.com/jmtyszka/bidskit) uses dcm2niix to create [BIDS](http://bids.neuroimaging.io/) datasets. - [DAC2BIDS](https://github.com/dangom/dac2bids) uses dcm2niibatch to create [BIDS](http://bids.neuroimaging.io/) datasets. - [heudiconv](https://github.com/nipy/heudiconv) can use dcm2niix to create [BIDS](http://bids.neuroimaging.io/) datasets. - [nipype](https://github.com/nipy/nipype) can use dcm2niix to convert images. - [pydcm2niix is a Python module for working with dcm2niix](https://github.com/jstutters/pydcm2niix). - [dcm2niir](https://github.com/muschellij2/dcm2niir) R wrapper for dcm2niix/dcm2nii. - [divest](https://github.com/jonclayden/divest) R interface to dcm2niix. - [sci-tran dcm2niix](https://github.com/scitran-apps/dcm2niix) docker. - [neuro_docker](https://github.com/Neurita/neuro_docker) includes dcm2niix as part of a single, static Dockerfile. - [https://github.com/lalet/boutiques-dcm2niix boutiques-dcm2niix] is a dockerfile for installing and validating dcm2niix. - [neurodocker](https://github.com/kaczmarj/neurodocker) generates [custom](https://github.com/rordenlab/dcm2niix/issues/138) Dockerfiles given specific versions of neuroimaging software. - [dcm2niix_afni](https://afni.nimh.nih.gov/pub/dist/doc/program_help/dcm2niix_afni.html) is a version of dcm2niix included with the [AFNI](https://afni.nimh.nih.gov/) distribution. - [MRIcroGL](https://github.com/neurolabusc/MRIcroGL) is available for MacOS, Linux and Windows and provides a graphical interface for dcm2niix. You can get compiled copies from the [MRIcroGL NITRC web site](https://www.nitrc.org/projects/mricrogl/).dcm2niix-1.0.20171215/SuperBuild/000077500000000000000000000000001322051203000160325ustar00rootroot00000000000000dcm2niix-1.0.20171215/SuperBuild/External-OPENJPEG.cmake000066400000000000000000000007141322051203000220250ustar00rootroot00000000000000set(OPENJPEG_TAG v2.1-static) # version v2.1-static ExternalProject_Add(openjpeg GIT_REPOSITORY "${git_protocol}://github.com/ningfei/openjpeg.git" GIT_TAG "${OPENJPEG_TAG}" SOURCE_DIR openjpeg BINARY_DIR openjpeg-build CMAKE_ARGS -Wno-dev --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${DEP_INSTALL_DIR} ) set(OpenJPEG_DIR ${DEP_INSTALL_DIR}/lib/openjpeg-2.1) dcm2niix-1.0.20171215/SuperBuild/External-YAML-CPP.cmake000066400000000000000000000007231322051203000220000ustar00rootroot00000000000000set(YAML-CPP_TAG yaml-cpp-0.5.3) # version yaml-cpp-0.5.3 ExternalProject_Add(yaml-cpp GIT_REPOSITORY "${git_protocol}://github.com/ningfei/yaml-cpp.git" GIT_TAG "${YAML-CPP_TAG}" SOURCE_DIR yaml-cpp BINARY_DIR yaml-cpp-build CMAKE_ARGS -Wno-dev --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${DEP_INSTALL_DIR} ) set(YAML-CPP_DIR ${DEP_INSTALL_DIR}/lib/cmake/yaml-cpp) dcm2niix-1.0.20171215/SuperBuild/SuperBuild.cmake000066400000000000000000000100211322051203000211040ustar00rootroot00000000000000# Check if git exists find_package(Git) if(NOT GIT_FOUND) message(ERROR "Cannot find git. git is required for Superbuild") endif() # Use git protocol or not option(USE_GIT_PROTOCOL "If behind a firewall turn this off to use http instead." ON) if(USE_GIT_PROTOCOL) set(git_protocol "git") else() set(git_protocol "https") endif() # Basic CMake build settings set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug;Release;RelWithDebInfo;MinSizeRel") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) option(USE_STATIC_RUNTIME "Use static runtime" ON) option(USE_SYSTEM_ZLIB "Use the system zlib" OFF) option(USE_TURBOJPEG "Use TurboJPEG to decode classic JPEG" OFF) option(USE_JASPER "Build with JPEG2000 support using Jasper" OFF) option(USE_OPENJPEG "Build with JPEG2000 support using OpenJPEG" OFF) option(BATCH_VERSION "Build dcm2niibatch for multiple conversions" OFF) include(ExternalProject) set(DEPENDENCIES) option(INSTALL_DEPENDENCIES "Optionally install built dependent libraries (OpenJPEG and yaml-cpp) for future use." OFF) if(INSTALL_DEPENDENCIES) set(DEP_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}) else() set(DEP_INSTALL_DIR ${CMAKE_BINARY_DIR}) endif() if(USE_OPENJPEG) message("-- Build with OpenJPEG: ${USE_OPENJPEG}") if(OpenJPEG_DIR OR OPENJPEG_DIR) set(OpenJPEG_DIR "${OpenJPEG_DIR}${OPENJPEG_DIR}" CACHE PATH "Path to OpenJPEG configuration file" FORCE) message("-- Using OpenJPEG library from ${OpenJPEG_DIR}") else() find_package(PkgConfig) if(PKG_CONFIG_FOUND) pkg_check_modules(OPENJPEG libopenjp2) endif() if(OPENJPEG_FOUND) set(OpenJPEG_DIR ${OPENJPEG_LIBDIR}/openjepg-2.1 CACHE PATH "Path to OpenJPEG configuration file" FORCE) message("-- Using OpenJPEG library from ${OpenJPEG_DIR}") else() include(${CMAKE_SOURCE_DIR}/SuperBuild/External-OPENJPEG.cmake) list(APPEND DEPENDENCIES openjpeg) set(BUILD_OPENJPEG TRUE) message("-- Will build OpenJPEG library from github") endif() endif() endif() if(BATCH_VERSION) message("-- Build dcm2niibatch: ${BATCH_VERSION}") if(YAML-CPP_DIR) set(YAML-CPP_DIR ${YAML-CPP_DIR} CACHE PATH "Path to yaml-cpp configuration file" FORCE) message("-- Using yaml-cpp library from ${YAML-CPP_DIR}") else() find_package(PkgConfig) if(PKG_CONFIG_FOUND) pkg_check_modules(YAML-CPP yaml-cpp) endif() if(YAML-CPP_FOUND) set(YAML-CPP_DIR ${YAML-CPP_LIBDIR}/cmake/yaml-cpp CACHE PATH "Path to yaml-cpp configuration file" FORCE) message("-- Using yaml-cpp library from ${YAML-CPP_DIR}") else() include(${CMAKE_SOURCE_DIR}/SuperBuild/External-YAML-CPP.cmake) list(APPEND DEPENDENCIES yaml-cpp) set(BUILD_YAML-CPP TRUE) message("-- Will build yaml-cpp library from github") endif() endif() endif() ExternalProject_Add(console DEPENDS ${DEPENDENCIES} DOWNLOAD_COMMAND "" SOURCE_DIR ${CMAKE_SOURCE_DIR}/console BINARY_DIR console-build CMAKE_ARGS -Wno-dev --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -DUSE_STATIC_RUNTIME:BOOL=${USE_STATIC_RUNTIME} -DUSE_SYSTEM_ZLIB:BOOL=${USE_SYSTEM_ZLIB} -DUSE_TURBOJPEG:BOOL=${USE_TURBOJPEG} -DUSE_JASPER:BOOL=${USE_JASPER} # OpenJPEG -DUSE_OPENJPEG:BOOL=${USE_OPENJPEG} -DOpenJPEG_DIR:PATH=${OpenJPEG_DIR} # yaml-cpp -DBATCH_VERSION:BOOL=${BATCH_VERSION} -DYAML-CPP_DIR:PATH=${YAML-CPP_DIR} ) install(DIRECTORY ${CMAKE_BINARY_DIR}/bin/ DESTINATION bin USE_SOURCE_PERMISSIONS) option(BUILD_DOCS "Build documentation (manpages)" OFF) if(BUILD_DOCS) add_subdirectory(docs) endif() dcm2niix-1.0.20171215/VERSIONS.md000066400000000000000000000154721322051203000155570ustar00rootroot00000000000000## Versions 15-Dec-2017 - Support [Siemens XA10 images](https://github.com/rordenlab/dcm2niix/pull/145). - [Ability to select specific series to convert](https://github.com/rordenlab/dcm2niix/pull/146). 4-Dec-2017 - Handle implicit VR DICOMs where [critical values nested in sequence groups (SQ)](https://github.com/rordenlab/dcm2niix/commit/7f5649c6fe6ed366d07776aa54397b50f6641aff) - Better support for [PAR/REC files with segmented 3D EPI](https://github.com/rordenlab/dcm2niix/commit/66cdf2dcc60d55a6ef37f5a6db8d500d3eeb7c88). - Allow Protocol Name to be [empty](https://github.com/rordenlab/dcm2niix/commit/94f3129898ba83bf310c9ff28e994f29feb13068). 17-Oct-2017 - Swap [phase-encoding direction polarity](https://github.com/rordenlab/dcm2niix/issues/125) for Siemens images where PE is in the Column direction. - Sort diffusion volumes by [B-value amplitude](https://www.nitrc.org/forum/forum.php?thread_id=8396&forum_id=4703) (use "-d n"/"-d y" to turn the feature off/on). - BIDS tag [TotalReadoutTime](https://github.com/rordenlab/dcm2niix/issues/130) handles partial fourier, Phase Resolution, etc (Michael Harms). - Additional [json fields](https://github.com/rordenlab/dcm2niix/issues/127). 18-Aug-2017 - Better BVec extraction for [PAR/REC 4.1](https://www.nitrc.org/forum/forum.php?thread_id=8387&forum_id=4703). - Support for [Segami Cerescan volumes](https://www.nitrc.org/forum/forum.php?thread_id=8076&forum_id=4703). 24-July-2017 - Compiles with recent releases of [OpenJPEG](https://github.com/neurolabusc/dcm_qa/issues/5#issuecomment-317443179) for JPEG2000 support. 23-June-2017 - [Ensure slice timing always reported for Siemens EPI](https://github.com/neurolabusc/dcm_qa/issues/4#issuecomment-310707906) - [Integrates validation](https://github.com/neurolabusc/dcm_qa) - JSON fix (InstitutionName -> InstitutionAddress) 21-June-2017 - Read DICOM header in 1Mb segments rather than loading whole file : reduces ram usage and [faster for systems with slow io](https://github.com/rordenlab/dcm2niix/issues/104). - Report [TotalReadoutTime](https://github.com/rordenlab/dcm2niix/issues/98). - Fix JPEG2000 support in [Superbuild](https://github.com/rordenlab/dcm2niix/issues/105). 28-May-2017 - Remove all derived images from [Philips DTI series](http://www.nitrc.org/forum/message.php?msg_id=21025). - Provide some [Siemens EPI sequence details](https://github.com/rordenlab/dcm2niix/issues). 28-April-2017 - Experimental [ECAT support](https://github.com/rordenlab/dcm2niix/issues/95). - Updated cmake to make JPEG2000 support easier with improved Travis and AppVeyor support [Ningfei Li](https://github.com/ningfei). - Supports Data/Time for images that report Data/Time (0008,002A) but not separate Date and Time (0008,0022 and 0008,0032). - [BIDS reports SliceTiming correctly](http://www.nitrc.org/forum/message.php?msg_id=20852). - Options -1..-9 to control [gz compression level](https://github.com/rordenlab/dcm2niix/issues/90). - Includes some [PET details in the BIDS JSON sidecar](https://github.com/rordenlab/dcm2niix/issues/87). - Better detection of image order for Philips 4D DICOM (reported by Jason McMorrow and Stephen Wilson). - [Include StudyInstanceUID and SeriesInstanceUID in filename](https://github.com/rordenlab/dcm2niix/issues/94). 7-Feb-2017 - Can be compiled to use either Philips [Float or Display](http://www.nitrc.org/forum/message.php?msg_id=20213) intensity intercept and slope values. - Handle 3D Philips DICOM and [PAR/REC](https://www.nitrc.org/forum/forum.php?thread_id=7707&forum_id=4703) files where images are not stored in a spatially contiguous order. - Handle DICOM violations where icon is uncompressed but image data is compressed. - Best guess matrix for 2D slices (similar to dcm2nii, SPM and MRIconvert). - Linux (case sensitive filenames) now handles par/rec as well as PAR/REC. - Images with unknown phase encoding do not generate [BIDS entry](https://github.com/rordenlab/dcm2niix/issues/79). - Unified printMessage/printWarning/printError aids embedding in other projects, such as [divest](https://github.com/jonclayden/divest). 1-Nov-2016 - AppVeyor Support (Ningfei Li & Chris Filo Gorgolewski) - Swap 3rd/4th dimensions for GE sequential multi-phase acquisitions (Niels Janssen). 10-Oct-2016 - Restores/improves building for the Windows operating system using MinGW. 30-Sept-2016 - Save ImageType (0x0008,0x0008) to BIDS. - Separate CT scans with different exposures. - Fixed issues where some compilers would generate erratic filenames for zero-padded series (e.g. "-f %3s"). 21-Sept-2016 - Reduce verbosity (reduce number of repeated warnings, less scary warnings for derived rather than raw images). - Re-enable custom output directory "-o" option broken by 30-Apr-2016 version. - Deal with mis-behaved GE CT images where slice direction across images is not consistent. - Add new BIDS fields (field strength, manufacturer, etc). - Philips PAR/REC conversion now reports inconsistent requested vs measured TR (due to prospect. motion corr.?). - GE: Locations In Acquisition (0054, 0081) is inaccurate if slices are interpolated, use Images In Acquisition (0020,1002) if available. - New filename options %d Series description (0008,103E), %z Sequence Name (0018,0024). - New filename options %a antenna (coil) number, %e echo number. - Initialize unused portions of NIfTI header to zero so multiple runs always produce identical results. - Supports 3D lossless JPEG saved as [multiple fragments](http://www.nitrc.org/forum/forum.php?thread_id=5872&forum_id=4703). 5-May-2016 - Crop 3D T1 acquisitions (e.g. ./dcm2niix -x y ~/DICOM). 30-Apr-2016 - Convert multiple files/folders with single command line invocation (e.g. ./dcm2niix -b y ~/tst ~/tst2). 22-Apr-2016 - Detect Siemens Phase maps (phase image names end with "_ph"). - Use current working directory if file name not specified. 12-Apr-2016 - Provide override (command line option "-m y") to stack images of the same series even if they differ in study date/time, echo/coil number, or slice orientation. This mechanism allows users to concatenate images that break strict DICOM compliance. 22-Mar-2016 - Experimental support for [DICOM datasets without DICOM file meta information](http://dicom.nema.org/dicom/2013/output/chtml/part10/chapter_7.html). 12-Dec-2015 - Support PAR/REC FP values when possible (see PMC3998685). 11-Nov-2015 - Minor refinements. 12-June-2015 - Uses less memory (helpful for large datasets). 2-Feb-2015 - Support for Visual Studio. - Remove dependency on zlib (now uses miniz). 1-Jan-2015 - Images separated based on TE (fieldmaps). - Support for JPEG2000 using OpenJPEG or Jasper libraries. - Support for JPEG using NanoJPEG library. - Support for lossless JPEG using custom library. 24-Nov-2014 - Support for CT scans with gantry tilt and varying distance between slices. 11-Oct-2014 - Initial public release.dcm2niix-1.0.20171215/appveyor.yml000066400000000000000000000024721322051203000163510ustar00rootroot00000000000000version: build-{build} branches: only: - master configuration: Release platform: x64 clone_depth: 1 clone_folder: c:\projects\dcm2niix init: - ps: >- $env:DATE = $(Get-Date -Format d-MMM-yyyy) $githash = $env:APPVEYOR_REPO_COMMIT.Substring(0, 7) $gittag = if ($env:APPVEYOR_REPO_TAG -eq $True) {"_$($env:APPVEYOR_REPO_TAG_NAME)"} else {""} Update-AppveyorBuild -Version "$($env:DATE)_g${githash}${gittag}" $env:release_version = $(Get-Date -Format d-MMMM-yyyy) before_build: - cmd: >- echo "Running cmake" mkdir c:\projects\dcm2niix\build cd c:\projects\dcm2niix\build cmake -G "Visual Studio 14 2015 Win64" -DBATCH_VERSION=ON -DUSE_OPENJPEG=ON ..\ build: project: c:\projects\dcm2niix\build\dcm2niix.sln verbosity: normal after_build: - ps: >- cd c:\projects\dcm2niix 7z a dcm2niix_$($env:DATE)_win.zip c:\projects\dcm2niix\build\bin\* >$null artifacts: - path: dcm2niix*.zip name: dcm2niix deploy: - provider: GitHub tag: $(appveyor_repo_tag_name) release: version $(release_version) ($(appveyor_repo_tag_name)) description: "" auth_token: secure: gCltVLQEWsjSTRlsi8qw7FGP54ujBq60apjXkWTV954b65bOHl95hXMxxkQ734L4 artifact: dcm2niix draft: false prerelease: false on: branch: master appveyor_repo_tag: true dcm2niix-1.0.20171215/batch_config.yml000066400000000000000000000006241322051203000171070ustar00rootroot00000000000000Options: isGz: false isFlipY: false isVerbose: false isCreateBIDS: false isOnlySingleFile: false Files: - in_dir: /path/to/first/folder out_dir: /path/to/output/folder filename: dcemri - in_dir: /path/to/second/folder out_dir: /path/to/output/folder filename: fa3dcm2niix-1.0.20171215/console/000077500000000000000000000000001322051203000154165ustar00rootroot00000000000000dcm2niix-1.0.20171215/console/CMakeLists.txt000066400000000000000000000134001322051203000201540ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8.11) project(console) # Option Choose whether to use static runtime include(ucm.cmake) option(USE_STATIC_RUNTIME "Use static runtime" ON) if(USE_STATIC_RUNTIME) ucm_set_runtime(STATIC) else() ucm_set_runtime(DYNAMIC) endif() # Basic CMake build settings set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug;Release;RelWithDebInfo;MinSizeRel") set(PROGRAMS dcm2niix) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") # using Clang add_definitions(-Wno-deprecated-register) add_definitions(-fno-caret-diagnostics) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-dead_strip") elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") # using GCC if (${CMAKE_CXX_COMPILER_VERSION} VERSION_GREATER 4.4.7) add_definitions(-Wno-unused-result) # available since GCC 4.5 endif() if (${CMAKE_CXX_COMPILER_VERSION} VERSION_GREATER 4.7.4) add_definitions(-fno-diagnostics-show-caret) # available since GCC 4.8 endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") # using Intel C++ elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # using Visual Studio C++ add_definitions(-D_CRT_SECURE_NO_DEPRECATE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4018") # '<': signed/unsigned mismatch set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4068") # unknown pragma set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4101") # unreferenced local variable set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4244") # 'initializing': conversion from 'double' to 'int', possible loss of data set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4267") # 'initializing': conversion from 'size_t' to 'int', possible loss of data set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4305") # 'argument': truncation from 'double' to 'float' set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4308") # negative integral constant converted to unsigned type set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4334") # '<<': result of 32-bit shift implicitly converted to 64 bits set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4800") # 'uint32_t' : forcing value to bool 'true' or 'false' set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4819") # The file contains a character that cannot be represented in the current code page set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4996") # 'access': The POSIX name for this item is deprecated endif() add_executable(dcm2niix main_console.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_foreign.cpp nii_ortho.cpp nii_dicom_batch.cpp) option(USE_SYSTEM_ZLIB "Use the system zlib" OFF) if(USE_SYSTEM_ZLIB) find_package(ZLIB REQUIRED) add_definitions(-DmyDisableMiniZ) target_include_directories(dcm2niix PRIVATE ${ZLIB_INCLUDE_DIRS}) target_link_libraries(dcm2niix ${ZLIB_LIBRARIES}) endif() option(USE_TURBOJPEG "Use TurboJPEG to decode classic JPEG" OFF) if(USE_TURBOJPEG) find_package(PkgConfig REQUIRED) pkg_check_modules(TURBOJPEG REQUIRED libturbojpeg) add_definitions(-DmyTurboJPEG) target_include_directories(dcm2niix PRIVATE ${TURBOJPEG_INCLUDEDIR}) target_link_libraries(dcm2niix ${TURBOJPEG_LIBRARIES}) endif() option(USE_JASPER "Build with JPEG2000 support using Jasper" OFF) if(USE_JASPER) find_package(Jasper REQUIRED) add_definitions(-DmyEnableJasper) target_include_directories(dcm2niix PRIVATE ${JASPER_INCLUDE_DIR}) target_link_libraries(dcm2niix ${JASPER_LIBRARIES}) endif() option(USE_OPENJPEG "Build with JPEG2000 support using OpenJPEG" OFF) if(USE_OPENJPEG) set(OpenJPEG_DIR "${OpenJPEG_DIR}${OPENJPEG_DIR}" CACHE PATH "Path to yaml-cpp configuration file" FORCE) find_package(OpenJPEG REQUIRED) if(WIN32) if(BUILD_SHARED_LIBS) add_definitions(-DOPJ_EXPORTS) else() add_definitions(-DOPJ_STATIC) endif() endif() target_include_directories(dcm2niix PRIVATE ${OPENJPEG_INCLUDE_DIRS}) target_link_libraries(dcm2niix ${OPENJPEG_LIBRARIES}) else () add_definitions(-DmyDisableOpenJPEG) endif() option(BATCH_VERSION "Build dcm2niibatch for multiple conversions" OFF) if(BATCH_VERSION) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") add_executable(dcm2niibatch main_console_batch.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_foreign.cpp nii_ortho.cpp nii_dicom_batch.cpp) set(YAML-CPP_DIR ${YAML-CPP_DIR} CACHE PATH "Path to yaml-cpp configuration file" FORCE) find_package(YAML-CPP REQUIRED) target_include_directories(dcm2niibatch PRIVATE ${YAML_CPP_INCLUDE_DIR}) target_link_libraries(dcm2niibatch ${YAML_CPP_LIBRARIES}) if(ZLIB_FOUND) add_definitions(-DmyDisableMiniZ) target_include_directories(dcm2niibatch PRIVATE ${ZLIB_INCLUDE_DIRS}) target_link_libraries(dcm2niibatch z) endif() if(TURBOJPEG_FOUND) add_definitions(-DmyTurboJPEG) target_include_directories(dcm2niibatch PRIVATE ${TURBOJPEG_INCLUDEDIR}) target_link_libraries(dcm2niibatch ${TURBOJPEG_LIBRARIES}) endif() if(JASPER_FOUND) add_definitions(-DmyEnableJasper) target_include_directories(dcm2niibatch PRIVATE ${JASPER_INCLUDE_DIR}) target_link_libraries(dcm2niibatch ${JASPER_LIBRARIES}) endif() if(OPENJPEG_FOUND) target_include_directories(dcm2niibatch PRIVATE ${OPENJPEG_INCLUDE_DIRS}) target_link_libraries(dcm2niibatch ${OPENJPEG_LIBRARIES}) else() add_definitions(-DmyDisableOpenJPEG) endif() list(APPEND PROGRAMS dcm2niibatch) endif() install(TARGETS ${PROGRAMS} DESTINATION bin) dcm2niix-1.0.20171215/console/jpg_0XC3.cpp000066400000000000000000000637301322051203000174500ustar00rootroot00000000000000#include #include #include #include #include #include "jpg_0XC3.h" #include "print.h" unsigned char readByte(unsigned char *lRawRA, long *lRawPos, long lRawSz) { unsigned char ret = 0x00; if (*lRawPos < lRawSz) ret = lRawRA[*lRawPos]; (*lRawPos)++; return ret; }// readByte() uint16_t readWord(unsigned char *lRawRA, long *lRawPos, long lRawSz) { return ( (readByte(lRawRA, lRawPos, lRawSz) << 8) + readByte(lRawRA, lRawPos, lRawSz)); }// readWord() int readBit(unsigned char *lRawRA, long *lRawPos, int *lCurrentBitPos) {//Read the next single bit int result = (lRawRA[*lRawPos] >> (7 - *lCurrentBitPos)) & 1; (*lCurrentBitPos)++; if (*lCurrentBitPos == 8) { (*lRawPos)++; *lCurrentBitPos = 0; } return result; }// readBit() int bitMask(int bits) { return ( (2 << (bits - 1)) -1); }// bitMask() int readBits (unsigned char *lRawRA, long *lRawPos, int *lCurrentBitPos, int lNum) { //lNum: bits to read, not to exceed 16 int result = lRawRA[*lRawPos]; result = (result << 8) + lRawRA[(*lRawPos)+1]; result = (result << 8) + lRawRA[(*lRawPos)+2]; result = (result >> (24 - * lCurrentBitPos -lNum)) & bitMask(lNum); //lCurrentBitPos is incremented from 1, so -1 *lCurrentBitPos = *lCurrentBitPos + lNum; if (*lCurrentBitPos > 7) { *lRawPos = *lRawPos + (*lCurrentBitPos >> 3); // div 8 *lCurrentBitPos = *lCurrentBitPos & 7; //mod 8 } return result; }// readBits() struct HufTables { uint8_t SSSSszRA[18]; uint8_t LookUpRA[256]; int DHTliRA[32]; int DHTstartRA[32]; int HufSz[32]; int HufCode[32]; int HufVal[32]; int MaxHufSi; int MaxHufVal; };// HufTables() int decodePixelDifference(unsigned char *lRawRA, long *lRawPos, int *lCurrentBitPos, struct HufTables l) { int lByte = (lRawRA[*lRawPos] << *lCurrentBitPos) + (lRawRA[*lRawPos+1] >> (8- *lCurrentBitPos)); lByte = lByte & 255; int lHufValSSSS = l.LookUpRA[lByte]; if (lHufValSSSS < 255) { *lCurrentBitPos = l.SSSSszRA[lHufValSSSS] + *lCurrentBitPos; *lRawPos = *lRawPos + (*lCurrentBitPos >> 3); *lCurrentBitPos = *lCurrentBitPos & 7; } else { //full SSSS is not in the first 8-bits int lInput = lByte; int lInputBits = 8; (*lRawPos)++; // forward 8 bits = precisely 1 byte do { lInputBits++; lInput = (lInput << 1) + readBit(lRawRA, lRawPos, lCurrentBitPos); if (l.DHTliRA[lInputBits] != 0) { //if any entires with this length for (int lI = l.DHTstartRA[lInputBits]; lI <= (l.DHTstartRA[lInputBits]+l.DHTliRA[lInputBits]-1); lI++) { if (lInput == l.HufCode[lI]) lHufValSSSS = l.HufVal[lI]; } //check each code } //if any entries with this length if ((lInputBits >= l.MaxHufSi) && (lHufValSSSS > 254)) {//exhausted options CR: added rev13 lHufValSSSS = l.MaxHufVal; } } while (!(lHufValSSSS < 255)); // found; } //answer in first 8 bits //The HufVal is referred to as the SSSS in the Codec, so it is called 'lHufValSSSS' if (lHufValSSSS == 0) //NO CHANGE return 0; if (lHufValSSSS == 1) { if (readBit(lRawRA, lRawPos, lCurrentBitPos) == 0) return -1; else return 1; } if (lHufValSSSS == 16) { //ALL CHANGE 16 bit difference: Codec H.1.2.2 "No extra bits are appended after SSSS = 16 is encoded." Osiris fails here return 32768; } //to get here - there is a 2..15 bit difference int lDiff = readBits(lRawRA, lRawPos, lCurrentBitPos, lHufValSSSS); if (lDiff <= bitMask(lHufValSSSS-1)) //add lDiff = lDiff - bitMask(lHufValSSSS); return lDiff; }// decodePixelDifference() unsigned char * decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbose, int *dimX, int *dimY, int *bits, int *frames, int diskBytes) { //decompress JPEG image named "fn" where image data is located skipBytes into file. diskBytes is compressed size of image (set to 0 if unknown) //next line breaks MSVC // #define abortGoto(...) ({printError(__VA_ARGS__); free(lRawRA); return NULL;}) #define abortGoto(...) do {printError(__VA_ARGS__); free(lRawRA); return NULL;} while(0) unsigned char *lImgRA8 = NULL; FILE *reader = fopen(fn, "rb"); int lSuccess = fseek(reader, 0, SEEK_END); long lRawSz = ftell(reader)- skipBytes; if ((diskBytes > 0) && (diskBytes < lRawSz)) //only if diskBytes is known and does not exceed length of file lRawSz = diskBytes; if ((lSuccess != 0) || (lRawSz <= 8)) { printError("Unable to load 0XC3 JPEG %s\n", fn); return NULL; //read failure } lSuccess = fseek(reader, skipBytes, SEEK_SET); //If successful, the function returns zero if (lSuccess != 0) { printError("Unable to open 0XC3 JPEG %s\n", fn); return NULL; //read failure } unsigned char *lRawRA = (unsigned char*) malloc(lRawSz); size_t lSz = fread(lRawRA, 1, lRawSz, reader); fclose(reader); if ((lSz < (size_t)lRawSz) || (lRawRA[0] != 0xFF) || (lRawRA[1] != 0xD8) || (lRawRA[2] != 0xFF)) { abortGoto("JPEG signature 0xFFD8FF not found at offset %d of %s\n", skipBytes, fn);//signature failure http://en.wikipedia.org/wiki/List_of_file_signatures } if (verbose) printMessage("JPEG signature 0xFFD8FF found at offset %d of %s\n", skipBytes, fn); //next: read header long lRawPos = 2; //Skip initial 0xFFD8, begin with third byte //long lRawPos = 0; //Skip initial 0xFFD8, begin with third byte unsigned char btS1, btS2, SOSse, SOSahal, btMarkerType, SOSns = 0x00; //tag unsigned char SOSpttrans = 0; unsigned char SOSss = 0; uint8_t SOFnf = 0; uint8_t SOFprecision = 0; uint16_t SOFydim = 0; uint16_t SOFxdim = 0; // long SOSarrayPos; //SOFarrayPos int lnHufTables = 0; const int kmaxFrames = 4; struct HufTables l[kmaxFrames+1]; do { //read each marker in the header do { btS1 = readByte(lRawRA, &lRawPos, lRawSz); if (btS1 != 0xFF) { abortGoto("JPEG header tag must begin with 0xFF\n"); } btMarkerType = readByte(lRawRA, &lRawPos, lRawSz); if ((btMarkerType == 0x01) || (btMarkerType == 0xFF) || ((btMarkerType >= 0xD0) && (btMarkerType <= 0xD7) ) ) btMarkerType = 0;//only process segments with length fields } while ((lRawPos < lRawSz) && (btMarkerType == 0)); uint16_t lSegmentLength = readWord (lRawRA, &lRawPos, lRawSz); //read marker length long lSegmentEnd = lRawPos+(lSegmentLength - 2); if (lSegmentEnd > lRawSz) { abortGoto("Segment larger than image\n"); } if (verbose) printMessage("btMarkerType %#02X length %d@%ld\n", btMarkerType, lSegmentLength, lRawPos); if ( ((btMarkerType >= 0xC0) && (btMarkerType <= 0xC3)) || ((btMarkerType >= 0xC5) && (btMarkerType <= 0xCB)) || ((btMarkerType >= 0xCD) && (btMarkerType <= 0xCF)) ) { //if Start-Of-Frame (SOF) marker SOFprecision = readByte(lRawRA, &lRawPos, lRawSz); SOFydim = readWord(lRawRA, &lRawPos, lRawSz); SOFxdim = readWord(lRawRA, &lRawPos, lRawSz); SOFnf = readByte(lRawRA, &lRawPos, lRawSz); //SOFarrayPos = lRawPos; lRawPos = (lSegmentEnd); if (verbose) printMessage(" [Precision %d X*Y %d*%d Frames %d]\n", SOFprecision, SOFxdim, SOFydim, SOFnf); if (btMarkerType != 0xC3) { //lImgTypeC3 = true; abortGoto("This JPEG decoder can only decompress lossless JPEG ITU-T81 images (SoF must be 0XC3, not %#02X)\n",btMarkerType ); } if ( (SOFprecision < 1) || (SOFprecision > 16) || (SOFnf < 1) || (SOFnf == 2) || (SOFnf > 3) || ((SOFnf == 3) && (SOFprecision > 8)) ) { abortGoto("Scalar data must be 1..16 bit, RGB data must be 8-bit (%d-bit, %d frames)\n", SOFprecision, SOFnf); } } else if (btMarkerType == 0xC4) {//if SOF marker else if define-Huffman-tables marker (DHT) if (verbose) printMessage(" [Huffman Length %d]\n", lSegmentLength); int lFrameCount = 1; do { uint8_t DHTnLi = readByte(lRawRA, &lRawPos, lRawSz ); //we read but ignore DHTtcth. #pragma unused(DHTnLi) //we need to increment the input file position, but we do not care what the value is DHTnLi = 0; for (int lInc = 1; lInc <= 16; lInc++) { l[lFrameCount].DHTliRA[lInc] = readByte(lRawRA, &lRawPos, lRawSz); DHTnLi = DHTnLi + l[lFrameCount].DHTliRA[lInc]; if (l[lFrameCount].DHTliRA[lInc] != 0) l[lFrameCount].MaxHufSi = lInc; if (verbose) printMessage("DHT has %d combinations with %d bits\n", l[lFrameCount].DHTliRA[lInc], lInc); } if (DHTnLi > 17) { abortGoto("Huffman table corrupted.\n"); } int lIncY = 0; //frequency for (int lInc = 0; lInc <= 31; lInc++) {//lInc := 0 to 31 do begin l[lFrameCount].HufVal[lInc] = -1; l[lFrameCount].HufSz[lInc] = -1; l[lFrameCount].HufCode[lInc] = -1; } for (int lInc = 1; lInc <= 16; lInc++) {//set the huffman size values if (l[lFrameCount].DHTliRA[lInc] > 0) { l[lFrameCount].DHTstartRA[lInc] = lIncY+1; for (int lIncX = 1; lIncX <= l[lFrameCount].DHTliRA[lInc]; lIncX++) { lIncY++; btS1 = readByte(lRawRA, &lRawPos, lRawSz); l[lFrameCount].HufVal[lIncY] = btS1; l[lFrameCount].MaxHufVal = btS1; if (verbose) printMessage("DHT combination %d has a value of %d\n", lIncY, btS1); if (btS1 <= 16) //unsigned ints ALWAYS >0, so no need for(btS1 >= 0) l[lFrameCount].HufSz[lIncY] = lInc; else { abortGoto("Huffman size array corrupted.\n"); } } } } //set huffman size values int K = 1; int Code = 0; int Si = l[lFrameCount].HufSz[K]; do { while (Si == l[lFrameCount].HufSz[K]) { l[lFrameCount].HufCode[K] = Code; Code = Code + 1; K++; } if (K <= DHTnLi) { while (l[lFrameCount].HufSz[K] > Si) { Code = Code << 1; //Shl!!! Si = Si + 1; }//while Si }//K <= 17 } while (K <= DHTnLi); //if (verbose) // for (int j = 1; j <= DHTnLi; j++) // printMessage(" [%d Sz %d Code %d Value %d]\n", j, l[lFrameCount].HufSz[j], l[lFrameCount].HufCode[j], l[lFrameCount].HufVal[j]); lFrameCount++; } while ((lSegmentEnd-lRawPos) >= 18); lnHufTables = lFrameCount - 1; lRawPos = (lSegmentEnd); if (verbose) printMessage(" [FrameCount %d]\n", lnHufTables); } else if (btMarkerType == 0xDD) { //if DHT marker else if Define restart interval (DRI) marker abortGoto("btMarkerType == 0xDD: unsupported Restart Segments\n"); //lRestartSegmentSz = ReadWord(lRawRA, &lRawPos, lRawSz); //lRawPos = lSegmentEnd; } else if (btMarkerType == 0xDA) { //if DRI marker else if read Start of Scan (SOS) marker SOSns = readByte(lRawRA, &lRawPos, lRawSz); //if Ns = 1 then NOT interleaved, else interleaved: see B.2.3 // SOSarrayPos = lRawPos; //not required... if (SOSns > 0) { for (int lInc = 1; lInc <= SOSns; lInc++) { btS1 = readByte(lRawRA, &lRawPos, lRawSz); //component identifier 1=Y,2=Cb,3=Cr,4=I,5=Q #pragma unused(btS1) //dummy value used to increment file position btS2 = readByte(lRawRA, &lRawPos, lRawSz); //horizontal and vertical sampling factors #pragma unused(btS2) //dummy value used to increment file position } } SOSss = readByte(lRawRA, &lRawPos, lRawSz); //predictor selection B.3 SOSse = readByte(lRawRA, &lRawPos, lRawSz); #pragma unused(SOSse) //dummy value used to increment file position SOSahal = readByte(lRawRA, &lRawPos, lRawSz); //lower 4bits= pointtransform SOSpttrans = SOSahal & 16; if (verbose) printMessage(" [Predictor: %d Transform %d]\n", SOSss, SOSahal); lRawPos = (lSegmentEnd); } else //if SOS marker else skip marker lRawPos = (lSegmentEnd); } while ((lRawPos < lRawSz) && (btMarkerType != 0xDA)); //0xDA=Start of scan: loop for reading header //NEXT: Huffman decoding if (lnHufTables < 1) { abortGoto("Decoding error: no Huffman tables.\n"); } //NEXT: unpad data - delete byte that follows $FF int lIsRestartSegments = 0; long lIncI = lRawPos; //input position long lIncO = lRawPos; //output position do { lRawRA[lIncO] = lRawRA[lIncI]; if (lRawRA[lIncI] == 255) { if (lRawRA[lIncI+1] == 0) lIncI = lIncI+1; else if (lRawRA[lIncI+1] == 0xD9) lIncO = -666; //end of padding else lIsRestartSegments = lRawRA[lIncI+1]; } lIncI++; lIncO++; } while (lIncO > 0); if (lIsRestartSegments != 0) //detects both restart and corruption https://groups.google.com/forum/#!topic/comp.protocols.dicom/JUuz0B_aE5o printWarning("Detected restart segments, decompress with dcmdjpeg or gdcmconv 0xFF%02X.\n", lIsRestartSegments); //NEXT: some RGB images use only a single Huffman table for all 3 colour planes. In this case, replicate the correct values //NEXT: prepare lookup table for (int lFrameCount = 1; lFrameCount <= lnHufTables; lFrameCount ++) { for (int lInc = 0; lInc <= 17; lInc ++) l[lFrameCount].SSSSszRA[lInc] = 123; //Impossible value for SSSS, suggests 8-bits can not describe answer for (int lInc = 0; lInc <= 255; lInc ++) l[lFrameCount].LookUpRA[lInc] = 255; //Impossible value for SSSS, suggests 8-bits can not describe answer } //NEXT: fill lookuptable for (int lFrameCount = 1; lFrameCount <= lnHufTables; lFrameCount ++) { int lIncY = 0; for (int lSz = 1; lSz <= 8; lSz ++) { //set the huffman lookup table for keys with lengths <=8 if (l[lFrameCount].DHTliRA[lSz]> 0) { for (int lIncX = 1; lIncX <= l[lFrameCount].DHTliRA[lSz]; lIncX ++) { lIncY++; int lHufVal = l[lFrameCount].HufVal[lIncY]; //SSSS l[lFrameCount].SSSSszRA[lHufVal] = lSz; int k = (l[lFrameCount].HufCode[lIncY] << (8-lSz )) & 255; //K= most sig bits for hufman table if (lSz < 8) { //fill in all possible bits that exceed the huffman table int lInc = bitMask(8-lSz); for (int lCurrentBitPos = 0; lCurrentBitPos <= lInc; lCurrentBitPos++) { l[lFrameCount].LookUpRA[k+lCurrentBitPos] = lHufVal; } } else l[lFrameCount].LookUpRA[k] = lHufVal; //SSSS //printMessage("Frame %d SSSS %d Size %d Code %d SHL %d EmptyBits %ld\n", lFrameCount, lHufRA[lFrameCount][lIncY].HufVal, lHufRA[lFrameCount][lIncY].HufSz,lHufRA[lFrameCount][lIncY].HufCode, k, lInc); } //Set SSSS } //Length of size lInc > 0 } //for lInc := 1 to 8 } //For each frame, e.g. once each for Red/Green/Blue //NEXT: some RGB images use only a single Huffman table for all 3 colour planes. In this case, replicate the correct values if (lnHufTables < SOFnf) { //use single Hufman table for each frame for (int lFrameCount = 2; lFrameCount <= SOFnf; lFrameCount++) { l[lFrameCount] = l[1]; } //for each frame } // if lnHufTables < SOFnf //NEXT: uncompress data: different loops for different predictors int lItems = SOFxdim*SOFydim*SOFnf; // lRawPos++;// <- only for Pascal where array is indexed from 1 not 0 first byte of data int lCurrentBitPos = 0; //read in a new byte //depending on SOSss, we see Table H.1 int lPredA = 0; int lPredB = 0; int lPredC = 0; if (SOSss == 2) //predictor selection 2: above lPredA = SOFxdim-1; else if (SOSss == 3) //predictor selection 3: above+left lPredA = SOFxdim; else if ((SOSss == 4) || (SOSss == 5)) { //these use left, above and above+left WEIGHT LEFT lPredA = 0; //Ra left lPredB = SOFxdim-1; //Rb directly above lPredC = SOFxdim; //Rc UpperLeft:above and to the left } else if (SOSss == 6) { //also use left, above and above+left, WEIGHT ABOVE lPredB = 0; lPredA = SOFxdim-1; //Rb directly above lPredC = SOFxdim; //Rc UpperLeft:above and to the left } else lPredA = 0; //Ra: directly to left) if (SOFprecision > 8) { //start - 16 bit data *bits = 16; int lPx = -1; //pixel position int lPredicted = 1 << (SOFprecision-1-SOSpttrans); lImgRA8 = (unsigned char*) malloc(lItems * 2); uint16_t *lImgRA16 = (uint16_t*) lImgRA8; for (int i = 0; i < lItems; i++) lImgRA16[i] = 0; //zero array int frame = 1; for (int lIncX = 1; lIncX <= SOFxdim; lIncX++) { //for first row - here we ALWAYS use LEFT as predictor lPx++; //writenext voxel if (lIncX > 1) lPredicted = lImgRA16[lPx-1]; lImgRA16[lPx] = lPredicted+ decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[frame]); } for (int lIncY = 2; lIncY <= SOFydim; lIncY++) {//for all subsequent rows lPx++; //write next voxel lPredicted = lImgRA16[lPx-SOFxdim]; //use ABOVE lImgRA16[lPx] = lPredicted+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[frame]); if (SOSss == 4) { for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { lPredicted = lImgRA16[lPx-lPredA]+lImgRA16[lPx-lPredB]-lImgRA16[lPx-lPredC]; lPx++; //writenext voxel lImgRA16[lPx] = lPredicted+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[frame]); } //for lIncX } else if ((SOSss == 5) || (SOSss == 6)) { for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { lPredicted = lImgRA16[lPx-lPredA]+ ((lImgRA16[lPx-lPredB]-lImgRA16[lPx-lPredC]) >> 1); lPx++; //writenext voxel lImgRA16[lPx] = lPredicted+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[frame]); } //for lIncX } else if (SOSss == 7) { for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { lPx++; //writenext voxel lPredicted = (lImgRA16[lPx-1]+lImgRA16[lPx-SOFxdim]) >> 1; lImgRA16[lPx] = lPredicted+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[frame]); } //for lIncX } else { //SOSss 1,2,3 read single values for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { lPredicted = lImgRA16[lPx-lPredA]; lPx++; //writenext voxel lImgRA16[lPx] = lPredicted+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[frame]); } //for lIncX } // if..else possible predictors }//for lIncY } else if (SOFnf == 3) { //if 16-bit data; else 8-bit 3 frames *bits = 8; lImgRA8 = (unsigned char*) malloc(lItems ); int lPx[kmaxFrames+1], lPredicted[kmaxFrames+1]; //pixel position for (int f = 1; f <= SOFnf; f++) { lPx[f] = ((f-1) * (SOFxdim * SOFydim) ) -1; lPredicted[f] = 1 << (SOFprecision-1-SOSpttrans); } for (int i = 0; i < lItems; i++) lImgRA8[i] = 255; //zero array for (int lIncX = 1; lIncX <= SOFxdim; lIncX++) { //for first row - here we ALWAYS use LEFT as predictor for (int f = 1; f <= SOFnf; f++) { lPx[f]++; //writenext voxel if (lIncX > 1) lPredicted[f] = lImgRA8[lPx[f]-1]; lImgRA8[lPx[f]] = lPredicted[f] + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[f]); } } //first row always predicted by LEFT for (int lIncY = 2; lIncY <= SOFydim; lIncY++) {//for all subsequent rows for (int f = 1; f <= SOFnf; f++) { lPx[f]++; //write next voxel lPredicted[f] = lImgRA8[lPx[f]-SOFxdim]; //use ABOVE lImgRA8[lPx[f]] = lPredicted[f] + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[f]); }//first column of row always predicted by ABOVE if (SOSss == 4) { for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { for (int f = 1; f <= SOFnf; f++) { lPredicted[f] = lImgRA8[lPx[f]-lPredA]+lImgRA8[lPx[f]-lPredB]-lImgRA8[lPx[f]-lPredC]; lPx[f]++; //writenext voxel lImgRA8[lPx[f]] = lPredicted[f]+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[f]); } } //for lIncX } else if ((SOSss == 5) || (SOSss == 6)) { for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { for (int f = 1; f <= SOFnf; f++) { lPredicted[f] = lImgRA8[lPx[f]-lPredA]+ ((lImgRA8[lPx[f]-lPredB]-lImgRA8[lPx[f]-lPredC]) >> 1); lPx[f]++; //writenext voxel lImgRA8[lPx[f]] = lPredicted[f] + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[f]); } } //for lIncX } else if (SOSss == 7) { for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { for (int f = 1; f <= SOFnf; f++) { lPx[f]++; //writenext voxel lPredicted[f] = (lImgRA8[lPx[f]-1]+lImgRA8[lPx[f]-SOFxdim]) >> 1; lImgRA8[lPx[f]] = lPredicted[f] + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[f]); } } //for lIncX } else { //SOSss 1,2,3 read single values for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { for (int f = 1; f <= SOFnf; f++) { lPredicted[f] = lImgRA8[lPx[f]-lPredA]; lPx[f]++; //writenext voxel lImgRA8[lPx[f]] = lPredicted[f] + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[f]); } } //for lIncX } // if..else possible predictors }//for lIncY } else { //if 8-bit data 3frames; else 8-bit 1 frames *bits = 8; lImgRA8 = (unsigned char*) malloc(lItems ); int lPx = -1; //pixel position int lPredicted = 1 << (SOFprecision-1-SOSpttrans); for (int i = 0; i < lItems; i++) lImgRA8[i] = 0; //zero array for (int lIncX = 1; lIncX <= SOFxdim; lIncX++) { //for first row - here we ALWAYS use LEFT as predictor lPx++; //writenext voxel if (lIncX > 1) lPredicted = lImgRA8[lPx-1]; int dx = decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[1]); lImgRA8[lPx] = lPredicted+dx; } for (int lIncY = 2; lIncY <= SOFydim; lIncY++) {//for all subsequent rows lPx++; //write next voxel lPredicted = lImgRA8[lPx-SOFxdim]; //use ABOVE lImgRA8[lPx] = lPredicted+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[1]); if (SOSss == 4) { for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { lPredicted = lImgRA8[lPx-lPredA]+lImgRA8[lPx-lPredB]-lImgRA8[lPx-lPredC]; lPx++; //writenext voxel lImgRA8[lPx] = lPredicted+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[1]); } //for lIncX } else if ((SOSss == 5) || (SOSss == 6)) { for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { lPredicted = lImgRA8[lPx-lPredA]+ ((lImgRA8[lPx-lPredB]-lImgRA8[lPx-lPredC]) >> 1); lPx++; //writenext voxel lImgRA8[lPx] = lPredicted+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[1]); } //for lIncX } else if (SOSss == 7) { for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { lPx++; //writenext voxel lPredicted = (lImgRA8[lPx-1]+lImgRA8[lPx-SOFxdim]) >> 1; lImgRA8[lPx] = lPredicted+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[1]); } //for lIncX } else { //SOSss 1,2,3 read single values for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { lPredicted = lImgRA8[lPx-lPredA]; lPx++; //writenext voxel lImgRA8[lPx] = lPredicted+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[1]); } //for lIncX } // if..else possible predictors }//for lIncY } //if 16bit else 8bit free(lRawRA); *dimX = SOFxdim; *dimY = SOFydim; *frames = SOFnf; if (verbose) printMessage("JPEG ends %ld@%ld\n", lRawPos, lRawPos+skipBytes); return lImgRA8; }// decode_JPEG_SOF_0XC3()dcm2niix-1.0.20171215/console/jpg_0XC3.h000066400000000000000000000014431322051203000171060ustar00rootroot00000000000000//Decode DICOM Transfer Syntax 1.2.840.10008.1.2.4.70 and 1.2.840.10008.1.2.4.57 // JPEG Lossless, Nonhierarchical // see ISO/IEC 10918-1 / ITU T.81 // specifically, format with 'Start of Frame' (SOF) code 0xC3 // http://www.w3.org/Graphics/JPEG/itu-t81.pdf // This code decodes data with 1..16 bits per pixel // It appears unique to medical imaging, and is not supported by most JPEG libraries // http://www.dicomlibrary.com/dicom/transfer-syntax/ // https://en.wikipedia.org/wiki/Lossless_JPEG#Lossless_mode_of_operation #ifndef _JPEG_SOF_0XC3_ #define _JPEG_SOF_0XC3_ #ifdef __cplusplus extern "C" { #endif unsigned char * decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbose, int *dimX, int *dimY, int *bits, int *frames, int diskBytes); #ifdef __cplusplus } #endif #endifdcm2niix-1.0.20171215/console/main_console.cpp000066400000000000000000000410741322051203000205760ustar00rootroot00000000000000// main.m dcm2niix // by Chris Rorden on 3/22/14, see license.txt // Copyright (c) 2014 Chris Rorden. All rights reserved. //g++ -O3 main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -s -o dcm2niix -lz //if you do not have zlib,you can compile without it // g++ -O3 -DmyDisableZLib main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -s -o dcm2niix //or you can build your own copy: // to compile you will first want to build the Z library, then compile the project // cd zlib-1.2.8 // sudo ./configure; // sudo make //to generate combined 32-bit and 64-bit builds for OSX : // g++ -O3 -x c++ main_console.c nii_dicom.c nifti1_io_core.c nii_ortho.c nii_dicom_batch.c -s -arch x86_64 -o dcm2niix64 -lz // g++ -O3 -x c++ main_console.c nii_dicom.c nifti1_io_core.c nii_ortho.c nii_dicom_batch.c -s -arch i386 -o dcm2niix32 -lz // lipo -create dcm2niix32 dcm2niix64 -o dcm2niix //On windows with mingw you may get "fatal error: zlib.h: No such file // to remedy, run "mingw-get install libz-dev" from mingw //Alternatively, windows users with VisualStudio can compile this project // vcvarsall amd64 // cl /EHsc main_console.cpp nii_foreign.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -DmyDisableOpenJPEG /o dcm2niix //#define mydebugtest //automatically process directory specified in main, ignore input arguments #include #include #include #include #include #include #include //#include #include // clock_t, clock, CLOCKS_PER_SEC #include #include "nii_dicom_batch.h" #include "nii_dicom.h" #include #if !defined(_WIN64) && !defined(_WIN32) #include #include double get_wall_time(){ struct timeval time; if (gettimeofday(&time,NULL)){ // Handle error return 0; } return (double)time.tv_sec + (double)time.tv_usec * .000001; } #endif const char* removePath(const char* path) { // "/usr/path/filename.exe" -> "filename.exe" const char* pDelimeter = strrchr (path, '\\'); if (pDelimeter) path = pDelimeter+1; pDelimeter = strrchr (path, '/'); if (pDelimeter) path = pDelimeter+1; return path; } //removePath() void showHelp(const char * argv[], struct TDCMopts opts) { const char *cstr = removePath(argv[0]); printf("usage: %s [options] \n", cstr); printf(" Options :\n"); printf(" -1..-9 : gz compression level (1=fastest..9=smallest, default %d)\n", opts.gzLevel); char bidsCh = 'n'; if (opts.isCreateBIDS) bidsCh = 'y'; printf(" -b : BIDS sidecar (y/n/o(o=only: no NIfTI), default %c)\n", bidsCh); if (opts.isAnonymizeBIDS) bidsCh = 'y'; else bidsCh = 'n'; printf(" -ba : anonymize BIDS (y/n, default %c)\n", bidsCh); printf(" -c : comment stored as NIfTI aux_file (up to 24 characters)\n"); if (opts.isSortDTIbyBVal) bidsCh = 'y'; else bidsCh = 'n'; printf(" -d : diffusion volumes sorted by b-value (y/n, default %c)\n", bidsCh); #ifdef mySegmentByAcq #define kQstr " %%q=sequence number," #else #define kQstr "" #endif printf(" -f : filename (%%a=antenna (coil) number, %%c=comments, %%d=description, %%e echo number, %%f=folder name, %%i ID of patient, %%j seriesInstanceUID, %%k studyInstanceUID, %%m=manufacturer, %%n=name of patient, %%p=protocol,%s %%s=series number, %%t=time, %%u=acquisition number, %%v=vendor, %%x=study ID; %%z sequence name; default '%s')\n", kQstr, opts.filename); printf(" -h : show help\n"); printf(" -i : ignore derived, localizer and 2D images (y/n, default n)\n"); printf(" -m : merge 2D slices from same series regardless of study time, echo, coil, orientation, etc. (y/n, default n)\n"); printf(" -n : only convert this series number - can be used up to %i times (default convert all)\n", MAX_NUM_SERIES); printf(" -o : output directory (omit to save to input folder)\n"); printf(" -p : Philips precise float (not display) scaling (y/n, default y)\n"); printf(" -s : single file mode, do not convert other images in folder (y/n, default n)\n"); printf(" -t : text notes includes private patient details (y/n, default n)\n"); #if !defined(_WIN64) && !defined(_WIN32) //shell script for Unix only printf(" -u : up-to-date check\n"); #endif printf(" -v : verbose (n/y or 0/1/2 [no, yes, logorrheic], default 0)\n"); printf(" -x : crop (y/n, default n)\n"); char gzCh = 'n'; if (opts.isGz) gzCh = 'y'; #ifdef myDisableZLib if (strlen(opts.pigzname) > 0) printf(" -z : gz compress images (y/n/3, default %c) [y=pigz, n=no, 3=no,3D]\n", gzCh); else printf(" -z : gz compress images (y/n/3, default %c) [y=pigz(MISSING!), n=no, 3=no,3D]\n", gzCh); #else #ifdef myDisableMiniZ printf(" -z : gz compress images (y/i/n/3, default %c) [y=pigz, i=internal:zlib, n=no, 3=no,3D]\n", gzCh); #else printf(" -z : gz compress images (y/i/n/3, default %c) [y=pigz, i=internal, n=no, 3=no,3D]\n", gzCh); #endif #endif #if defined(_WIN64) || defined(_WIN32) printf(" Defaults stored in Windows registry\n"); printf(" Examples :\n"); printf(" %s c:\\DICOM\\dir\n", cstr); printf(" %s -c \"my comment\" c:\\DICOM\\dir\n", cstr); printf(" %s -o c:\\out\\dir c:\\DICOM\\dir\n", cstr); printf(" %s -f mystudy%%s c:\\DICOM\\dir\n", cstr); printf(" %s -o \"c:\\dir with spaces\\dir\" c:\\dicomdir\n", cstr); #else printf(" Defaults file : %s\n", opts.optsname); printf(" Examples :\n"); printf(" %s /Users/chris/dir\n", cstr); printf(" %s -c \"my comment\" /Users/chris/dir\n", cstr); printf(" %s -o /users/cr/outdir/ -z y ~/dicomdir\n", cstr); printf(" %s -f %%p_%%s -b y -ba n ~/dicomdir\n", cstr); printf(" %s -f mystudy%%s ~/dicomdir\n", cstr); printf(" %s -o \"~/dir with spaces/dir\" ~/dicomdir\n", cstr); #endif } //showHelp() int invalidParam(int i, const char * argv[]) { if ((argv[i][0] == 'y') || (argv[i][0] == 'Y') || (argv[i][0] == 'n') || (argv[i][0] == 'N') || (argv[i][0] == 'o') || (argv[i][0] == 'O') || (argv[i][0] == 'h') || (argv[i][0] == 'H') || (argv[i][0] == 'i') || (argv[i][0] == 'I') || (argv[i][0] == '0') || (argv[i][0] == '1') || (argv[i][0] == '2') || (argv[i][0] == '3') ) return 0; //if (argv[i][0] != '-') return 0; printf(" Error: invalid option '%s %s'\n", argv[i-1], argv[i]); return 1; } #if !defined(_WIN64) && !defined(_WIN32) //shell script for Unix only int checkUpToDate() { #define URL "/rordenlab/dcm2niix/releases/" #define APIURL "\"https://api.github.com/repos" URL "latest\"" #define HTMURL "https://github.com" URL #define SHELLSCRIPT "#!/usr/bin/env bash\n curl --silent " APIURL " | grep '\"tag_name\":' | sed -E 's/.*\"([^\"]+)\".*/\\1/'" //check first 13 characters, e.g. "v1.0.20171204" #define versionChars 13 FILE *pipe = popen(SHELLSCRIPT, "r"); char ch, gitvers[versionChars+1]; int n = 0; int nMatch = 0; while ((ch = fgetc(pipe)) != EOF) { if (n < versionChars) { gitvers[n] = ch; if (gitvers[n] == kDCMvers[n]) nMatch ++; n ++; } } pclose(pipe); gitvers[n] = 0; //null terminate if (n < 1) { //script reported nothing printf("Error: unable to check version with script:\n %s\n", SHELLSCRIPT); return 3; //different from EXIT_SUCCESS (0) and EXIT_FAILURE (1) } if (nMatch == versionChars) { //versions match printf("Good news: Your version is up to date: %s\n", gitvers); return EXIT_SUCCESS; } //report error char myvers[versionChars+1]; for (int i = 0; i < versionChars; i++) myvers[i] = kDCMvers[i]; myvers[versionChars] = 0; //null terminate int myv = atoi(myvers + 5); //skip "v1.0." int gitv = atoi(gitvers + 5); //skip "v1.0." if (myv > gitv) { printf("Warning: your version ('%s') more recent than stable release ('%s')\n %s\n", myvers, gitvers, HTMURL); return 2; //different from EXIT_SUCCESS (0) and EXIT_FAILURE (1) } printf("Error: your version ('%s') is not the latest release ('%s')\n %s\n", myvers, gitvers, HTMURL); return EXIT_FAILURE; } //checkUpToDate() #endif //shell script for UNIX only //#define mydebugtest int main(int argc, const char * argv[]) { struct TDCMopts opts; readIniFile(&opts, argv); //set default preferences #ifdef mydebugtest //strcpy(opts.indir, "/Users/rorden/desktop/sliceOrder/dicom2/Philips_PARREC_Rotation/NoRotation/DBIEX_4_1.PAR"); //strcpy(opts.indir, "/Users/rorden/desktop/sliceOrder/dicom2/test"); strcpy(opts.indir, "e:\\t1s"); #else #if defined(__APPLE__) #define kOS "MacOS" #elif (defined(__linux) || defined(__linux__)) #define kOS "Linux" #else #define kOS "Windows" #endif printf("Chris Rorden's dcm2niiX version %s (%llu-bit %s)\n",kDCMvers, (unsigned long long) sizeof(size_t)*8, kOS); if (argc < 2) { showHelp(argv, opts); return 0; } //for (int i = 1; i < argc; i++) { printf(" argument %d= '%s'\n", i, argv[i]);} int i = 1; int lastCommandArg = 0; while (i < (argc)) { //-1 as final parameter is DICOM directory if ((strlen(argv[i]) > 1) && (argv[i][0] == '-')) { //command if (argv[i][1] == 'h') showHelp(argv, opts); else if ((argv[i][1] >= '1') && (argv[i][1] <= '9')) { opts.gzLevel = abs((int)strtol(argv[i], NULL, 10)); if (opts.gzLevel > 11) opts.gzLevel = 11; } else if ((argv[i][1] == 'b') && ((i+1) < argc)) { if (strlen(argv[i]) < 3) { //"-b y" i++; if (invalidParam(i, argv)) return 0; if ((argv[i][0] == 'n') || (argv[i][0] == 'N') || (argv[i][0] == '0')) opts.isCreateBIDS = false; else { opts.isCreateBIDS = true; if ((argv[i][0] == 'o') || (argv[i][0] == 'O')) opts.isOnlyBIDS = true; } } else if (argv[i][2] == 'a') {//"-ba y" i++; if (invalidParam(i, argv)) return 0; if ((argv[i][0] == 'n') || (argv[i][0] == 'N') || (argv[i][0] == '0')) opts.isAnonymizeBIDS = false; else opts.isAnonymizeBIDS = true; } else printf("Error: Unknown command line argument: '%s'\n", argv[i]); } else if ((argv[i][1] == 'c') && ((i+1) < argc)) { i++; snprintf(opts.imageComments,24,"%s",argv[i]); } else if ((argv[i][1] == 'd') && ((i+1) < argc)) { i++; if (invalidParam(i, argv)) return 0; if ((argv[i][0] == 'n') || (argv[i][0] == 'N') || (argv[i][0] == '0')) opts.isSortDTIbyBVal = false; else opts.isSortDTIbyBVal = true; } else if ((argv[i][1] == 'i') && ((i+1) < argc)) { i++; if (invalidParam(i, argv)) return 0; if ((argv[i][0] == 'n') || (argv[i][0] == 'N') || (argv[i][0] == '0')) opts.isIgnoreDerivedAnd2D = false; else opts.isIgnoreDerivedAnd2D = true; } else if ((argv[i][1] == 'm') && ((i+1) < argc)) { i++; if (invalidParam(i, argv)) return 0; if ((argv[i][0] == 'n') || (argv[i][0] == 'N') || (argv[i][0] == '0')) opts.isForceStackSameSeries = false; else opts.isForceStackSameSeries = true; } else if ((argv[i][1] == 'p') && ((i+1) < argc)) { i++; if (invalidParam(i, argv)) return 0; if ((argv[i][0] == 'n') || (argv[i][0] == 'N') || (argv[i][0] == '0')) opts.isPhilipsFloatNotDisplayScaling = false; else opts.isPhilipsFloatNotDisplayScaling = true; } else if ((argv[i][1] == 's') && ((i+1) < argc)) { i++; if (invalidParam(i, argv)) return 0; if ((argv[i][0] == 'n') || (argv[i][0] == 'N') || (argv[i][0] == '0')) opts.isOnlySingleFile = false; else opts.isOnlySingleFile = true; } else if ((argv[i][1] == 't') && ((i+1) < argc)) { i++; if (invalidParam(i, argv)) return 0; if ((argv[i][0] == 'n') || (argv[i][0] == 'N') || (argv[i][0] == '0')) opts.isCreateText = false; else opts.isCreateText = true; #if !defined(_WIN64) && !defined(_WIN32) //shell script for Unix only } else if (argv[i][1] == 'u') { return checkUpToDate(); #endif } else if ((argv[i][1] == 'v') && ((i+1) < argc)) { i++; if (invalidParam(i, argv)) return 0; if ((argv[i][0] == 'n') || (argv[i][0] == 'N') || (argv[i][0] == '0')) //0: verbose OFF opts.isVerbose = 0; else if ((argv[i][0] == 'h') || (argv[i][0] == 'H') || (argv[i][0] == '2')) //2: verbose HYPER opts.isVerbose = 2; else opts.isVerbose = 1; //1: verbose ON } else if ((argv[i][1] == 'x') && ((i+1) < argc)) { i++; if (invalidParam(i, argv)) return 0; if ((argv[i][0] == 'n') || (argv[i][0] == 'N') || (argv[i][0] == '0')) opts.isCrop = false; else opts.isCrop = true; } else if ((argv[i][1] == 'z') && ((i+1) < argc)) { i++; if (invalidParam(i, argv)) return 0; if ((argv[i][0] == '3') ) { opts.isGz = false; //uncompressed 3D opts.isSave3D = true; } else if ((argv[i][0] == 'i') || (argv[i][0] == 'I') ) { opts.isGz = true; //force use of internal compression instead of pigz strcpy(opts.pigzname,""); } else if ((argv[i][0] == 'n') || (argv[i][0] == 'N') || (argv[i][0] == '0')) opts.isGz = false; else opts.isGz = true; } else if ((argv[i][1] == 'f') && ((i+1) < argc)) { i++; strcpy(opts.filename,argv[i]); } else if ((argv[i][1] == 'o') && ((i+1) < argc)) { i++; strcpy(opts.outdir,argv[i]); } else if ((argv[i][1] == 'n') && ((i+1) < argc)) { i++; int seriesNumber = atoi(argv[i]); if (seriesNumber < 0) opts.numSeries = -1; //report series: convert none else if ((opts.numSeries >= 0) && (opts.numSeries < MAX_NUM_SERIES)) { opts.seriesNumber[opts.numSeries] = seriesNumber; opts.numSeries += 1; } else { printf("Warning: too many series specified, ignoring -n %s\n", argv[i]); } } else printf(" Error: invalid option '%s %s'\n", argv[i], argv[i+1]);; lastCommandArg = i; } //if parameter is a command i ++; //read next parameter } //while parameters to read //printf("%d %d",argc,lastCommandArg); if (argc == (lastCommandArg+1)) { //+1 as array indexed from 0 //the user did not provide an input filename, report filename structure char niiFilename[1024]; strcpy(opts.outdir,"");//no input supplied nii_createDummyFilename(niiFilename, opts); printf("%s\n",niiFilename); return EXIT_SUCCESS; } #endif #ifndef myEnableMultipleInputs if ((argc-lastCommandArg-1) > 1) { printf("Warning: only processing last of %d input files (recompile with 'myEnableMultipleInputs' to recursively process multiple files)\n", argc-lastCommandArg-1); lastCommandArg = argc - 2; } #endif #if !defined(_WIN64) && !defined(_WIN32) double startWall = get_wall_time(); #endif clock_t start = clock(); for (i = (lastCommandArg+1); i < argc; i++) { strcpy(opts.indir,argv[i]); // [argc-1] int ret = nii_loadDir(&opts); if (ret != EXIT_SUCCESS) return ret; } #if !defined(_WIN64) && !defined(_WIN32) printf ("Conversion required %f seconds (%f for core code).\n",get_wall_time() - startWall, ((float)(clock()-start))/CLOCKS_PER_SEC); #else printf ("Conversion required %f seconds.\n",((float)(clock()-start))/CLOCKS_PER_SEC); #endif saveIniFile(opts); return EXIT_SUCCESS; } dcm2niix-1.0.20171215/console/main_console_batch.cpp000066400000000000000000000106361322051203000217370ustar00rootroot00000000000000// main.m dcm2niix // by Chris Rorden on 3/22/14, see license.txt // Copyright (c) 2014 Chris Rorden. All rights reserved. // yaml batch suport by Benjamin Irving, 2016 - maintains copyright #ifdef _MSC_VER #include //access() #ifndef F_OK #define F_OK 0 /* existence check */ #endif #else #include //access() #endif #include #include #include #include #include #include #include #include // clock_t, clock, CLOCKS_PER_SEC #include #include "nii_dicom.h" #include "nii_dicom_batch.h" const char* removePath(const char* path) { // "/usr/path/filename.exe" -> "filename.exe" const char* pDelimeter = strrchr (path, '\\'); if (pDelimeter) path = pDelimeter+1; pDelimeter = strrchr (path, '/'); if (pDelimeter) path = pDelimeter+1; return path; } //removePath() int rmainbatch(TDCMopts opts) { clock_t start = clock(); nii_loadDir(&opts); printf ("Conversion required %f seconds.\n",((float)(clock()-start))/CLOCKS_PER_SEC); return EXIT_SUCCESS; } //rmainbatch() void showHelp(const char * argv[]) { const char *cstr = removePath(argv[0]); printf("Usage: %s \n", cstr); printf("\n"); printf("The configuration file must be in yaml format as shown below\n"); printf("\n"); printf("### START YAML FILE ###\n"); printf("Options:\n"); printf(" isGz: false\n"); printf(" isFlipY: false\n"); printf(" isVerbose: false\n"); printf(" isCreateBIDS: false\n"); printf(" isOnlySingleFile: false\n"); printf("Files:\n"); printf(" -\n"); printf(" in_dir: /path/to/first/folder\n"); printf(" out_dir: /path/to/output/folder\n"); printf(" filename: dcemri\n"); printf(" -\n"); printf(" in_dir: /path/to/second/folder\n"); printf(" out_dir: /path/to/output/folder\n"); printf(" filename: fa3\n"); printf("### END YAML FILE ###\n"); printf("\n"); #if defined(_WIN64) || defined(_WIN32) printf(" Example :\n"); printf(" %s c:\\dir\\yaml.yml\n", cstr); printf(" %s \"c:\\dir with spaces\\yaml.yml\"\n", cstr); #else printf(" Examples :\n"); printf(" %s /Users/chris/yaml.yml\n", cstr); printf(" %s \"/Users/dir with spaces/yaml.yml\"\n", cstr); #endif } //showHelp() int main(int argc, const char * argv[]) { #if defined(__APPLE__) #define kOS "MacOS" #elif (defined(__linux) || defined(__linux__)) #define kOS "Linux" #else #define kOS "Windows" #endif printf("dcm2niibatch using Chris Rorden's dcm2niiX version %s (%llu-bit %s)\n",kDCMvers, (unsigned long long) sizeof(size_t)*8, kOS); if (argc != 2) { if (argc < 2) printf(" Please provide location of config file\n"); else printf(" Do not include additional inputs with a config file\n"); printf("\n"); showHelp(argv); return EXIT_FAILURE; } if( access( argv[1], F_OK ) == -1 ) { printf(" Please provide location of config file\n"); printf("\n"); showHelp(argv); return EXIT_FAILURE; } // Process it all via a yaml file std::string yaml_file = argv[1]; std::cout << "yaml_path: " << yaml_file << std::endl; YAML::Node config = YAML::LoadFile(yaml_file); struct TDCMopts opts; readIniFile(&opts, argv); //setup defaults, e.g. path to pigz opts.isCreateBIDS = config["Options"]["isCreateBIDS"].as(); opts.isOnlySingleFile = config["Options"]["isOnlySingleFile"].as(); opts.isCreateText = false; opts.isVerbose = 0; opts.isGz = config["Options"]["isGz"].as(); //save data as compressed (.nii.gz) or raw (.nii) /*bool isInternalGz = config["Options"]["isInternalGz"].as(); if (isInternalGz) { strcpy(opts.pigzname, "”); //do NOT use pigz: force internal compressor //in general, pigz is faster unless you have a very slow network, in which case the internal compressor is better }*/ for (auto i: config["Files"]) { std::string indir = i["in_dir"].as(); strcpy(opts.indir, indir.c_str()); std::string outdir = i["out_dir"].as(); strcpy(opts.outdir, outdir.c_str()); std::string filename = i["filename"].as(); strcpy(opts.filename, filename.c_str()); rmainbatch(opts); } return EXIT_SUCCESS; } // main() dcm2niix-1.0.20171215/console/makefile000077500000000000000000000005251322051203000171230ustar00rootroot00000000000000# Regular use CFLAGS=-s -O3 # Debugging #CFLAGS=-g ifneq ($(OS),Windows_NT) OS = $(shell uname) ifeq "$(OS)" "Darwin" CFLAGS=-dead_strip -O3 endif endif all: g++ $(CFLAGS) -I. main_console.cpp nii_foreign.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -o dcm2niix -DmyDisableOpenJPEG dcm2niix-1.0.20171215/console/miniz.c000066400000000000000000007030221322051203000167140ustar00rootroot00000000000000/* miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing See "unlicense" statement at the end of this file. Rich Geldreich , last updated Oct. 13, 2013 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). * Change History 10/13/13 v1.15 r4 - Interim bugfix release while I work on the next major release with Zip64 support (almost there!): - Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug would only have occured in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place() (which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag). - Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size - Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries. Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice). - Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes - mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed - Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6. - Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti - Merged MZ_FORCEINLINE fix from hdeanclark - Fix include before config #ifdef, thanks emil.brink - Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can set it to 1 for real-time compression). - Merged in some compiler fixes from paulharris's github repro. - Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3. - Added example6.c, which dumps an image of the mandelbrot set to a PNG file. - Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more. - In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled - In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch 5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include (thanks fermtect). 5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. - Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. - Eliminated a bunch of warnings when compiling with GCC 32-bit/64. - Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). - Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. - Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. - Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. - Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) - Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). 4/12/12 v1.12 - More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's. level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson for the feedback/bug report. 5/28/11 v1.11 - Added statement from unlicense.org 5/27/11 v1.10 - Substantial compressor optimizations: - Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a - Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). - Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types. - Refactored the compression code for better readability and maintainability. - Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large drop in throughput on some files). 5/15/11 v1.09 - Initial stable release. * Low-level Deflate/Inflate implementation notes: Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses approximately as well as zlib. Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory block large enough to hold the entire file. The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. * zlib-style API notes: miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in zlib replacement in many apps: The z_stream struct, optional memory allocation callbacks deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound inflateInit/inflateInit2/inflate/inflateEnd compress, compress2, compressBound, uncompress CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. Supports raw deflate streams or standard zlib streams with adler-32 checking. Limitations: The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but there are no guarantees that miniz.c pulls this off perfectly. * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by Alex Evans. Supports 1-4 bytes/pixel images. * ZIP archive API notes: The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to get the job done with minimal fuss. There are simple API's to retrieve file information, read files from existing archives, create new archives, append new files to existing archives, or clone archive data from one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), or you can specify custom file read/write callbacks. - Archive reading: Just call this function to read a single file from a disk archive: void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); The locate operation can optionally check file comments too, which (as one example) can be used to identify multiple versions of the same file in an archive. This function uses a simple linear search through the central directory, so it's not very fast. Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and retrieve detailed info on each file by calling mz_zip_reader_file_stat(). - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data to disk and builds an exact image of the central directory in memory. The central directory image is written all at once at the end of the archive file when the archive is finalized. The archive writer can optionally align each file's local header and file data to any power of 2 alignment, which can be useful when the archive will be read from optical media. Also, the writer supports placing arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still readable by any ZIP tool. - Archive appending: The simple way to add a single file to an archive is to call this function: mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); The archive will be created if it doesn't already exist, otherwise it'll be appended to. Note the appending is done in-place and is not an atomic operation, so if something goes wrong during the operation it's possible the archive could be left without a central directory (although the local file headers and file data will be fine, so the archive will be recoverable). For more complex archive modification scenarios: 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and you're done. This is safe but requires a bunch of temporary disk space or heap memory. 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), append new files as needed, then finalize the archive which will write an updated central directory to the original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a possibility that the archive's central directory could be lost with this method if anything goes wrong, though. - ZIP archive support limitations: No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files. Requires streams capable of seeking. * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. * Important: For best perf. be sure to customize the below macros for your target platform: #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #define MINIZ_LITTLE_ENDIAN 1 #define MINIZ_HAS_64BIT_REGISTERS 1 * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). */ #ifndef MINIZ_HEADER_INCLUDED #define MINIZ_HEADER_INCLUDED #include // Defines to completely disable specific portions of miniz.c: // If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. // Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. //#define MINIZ_NO_STDIO // If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or // get/set file times, and the C run-time funcs that get/set times won't be called. // The current downside is the times written to your archives will be from 1979. //#define MINIZ_NO_TIME // Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. //#define MINIZ_NO_ARCHIVE_APIS // Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's. //#define MINIZ_NO_ARCHIVE_WRITING_APIS // Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. //#define MINIZ_NO_ZLIB_APIS // Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. //#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES // Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. // Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc // callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user // functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. //#define MINIZ_NO_MALLOC #if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) // TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux #define MINIZ_NO_TIME #endif #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) #include #endif #if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) // MINIZ_X86_OR_X64_CPU is only used to help set the below macros. #define MINIZ_X86_OR_X64_CPU 1 #endif #if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU // Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. #define MINIZ_LITTLE_ENDIAN 1 #endif #if MINIZ_X86_OR_X64_CPU // Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #endif #if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) // Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). #define MINIZ_HAS_64BIT_REGISTERS 1 #endif #ifdef __cplusplus extern "C" { #endif // ------------------- zlib-style API Definitions. // For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! typedef unsigned long mz_ulong; // mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. void mz_free(void *p); #define MZ_ADLER32_INIT (1) // mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); #define MZ_CRC32_INIT (0) // mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); // Compression strategies. enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; // Method #define MZ_DEFLATED 8 #ifndef MINIZ_NO_ZLIB_APIS // Heap allocation callbacks. // Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); typedef void (*mz_free_func)(void *opaque, void *address); typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); #define MZ_VERSION "9.1.15" #define MZ_VERNUM 0x91F0 #define MZ_VER_MAJOR 9 #define MZ_VER_MINOR 1 #define MZ_VER_REVISION 15 #define MZ_VER_SUBREVISION 0 // Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; // Return status codes. MZ_PARAM_ERROR is non-standard. enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 }; // Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 }; // Window bits #define MZ_DEFAULT_WINDOW_BITS 15 struct mz_internal_state; // Compression/decompression stream struct. typedef struct mz_stream_s { const unsigned char *next_in; // pointer to next byte to read unsigned int avail_in; // number of bytes available at next_in mz_ulong total_in; // total number of bytes consumed so far unsigned char *next_out; // pointer to next byte to write unsigned int avail_out; // number of bytes that can be written to next_out mz_ulong total_out; // total number of bytes produced so far char *msg; // error msg (unused) struct mz_internal_state *state; // internal state, allocated by zalloc/zfree mz_alloc_func zalloc; // optional heap allocation function (defaults to malloc) mz_free_func zfree; // optional heap free function (defaults to free) void *opaque; // heap alloc function user pointer int data_type; // data_type (unused) mz_ulong adler; // adler32 of the source or uncompressed data mz_ulong reserved; // not used } mz_stream; typedef mz_stream *mz_streamp; // Returns the version string of miniz.c. const char *mz_version(void); // mz_deflateInit() initializes a compressor with default options: // Parameters: // pStream must point to an initialized mz_stream struct. // level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. // level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. // (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) // Return values: // MZ_OK on success. // MZ_STREAM_ERROR if the stream is bogus. // MZ_PARAM_ERROR if the input parameters are bogus. // MZ_MEM_ERROR on out of memory. int mz_deflateInit(mz_streamp pStream, int level); // mz_deflateInit2() is like mz_deflate(), except with more control: // Additional parameters: // method must be MZ_DEFLATED // window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) // mem_level must be between [1, 9] (it's checked but ignored by miniz.c) int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); // Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). int mz_deflateReset(mz_streamp pStream); // mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. // Parameters: // pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. // flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. // Return values: // MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). // MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. // MZ_STREAM_ERROR if the stream is bogus. // MZ_PARAM_ERROR if one of the parameters is invalid. // MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) int mz_deflate(mz_streamp pStream, int flush); // mz_deflateEnd() deinitializes a compressor: // Return values: // MZ_OK on success. // MZ_STREAM_ERROR if the stream is bogus. int mz_deflateEnd(mz_streamp pStream); // mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); // Single-call compression functions mz_compress() and mz_compress2(): // Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); // mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). mz_ulong mz_compressBound(mz_ulong source_len); // Initializes a decompressor. int mz_inflateInit(mz_streamp pStream); // mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: // window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). int mz_inflateInit2(mz_streamp pStream, int window_bits); // Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. // Parameters: // pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. // flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. // On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). // MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. // Return values: // MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. // MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. // MZ_STREAM_ERROR if the stream is bogus. // MZ_DATA_ERROR if the deflate stream is invalid. // MZ_PARAM_ERROR if one of the parameters is invalid. // MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again // with more input data, or with more room in the output buffer (except when using single call decompression, described above). int mz_inflate(mz_streamp pStream, int flush); // Deinitializes a decompressor. int mz_inflateEnd(mz_streamp pStream); // Single-call decompression. // Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); // Returns a string description of the specified error code, or NULL if the error code is invalid. const char *mz_error(int err); // Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. // Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES typedef unsigned char Byte; typedef unsigned int uInt; typedef mz_ulong uLong; typedef Byte Bytef; typedef uInt uIntf; typedef char charf; typedef int intf; typedef void *voidpf; typedef uLong uLongf; typedef void *voidp; typedef void *const voidpc; #define Z_NULL 0 #define Z_NO_FLUSH MZ_NO_FLUSH #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH #define Z_SYNC_FLUSH MZ_SYNC_FLUSH #define Z_FULL_FLUSH MZ_FULL_FLUSH #define Z_FINISH MZ_FINISH #define Z_BLOCK MZ_BLOCK #define Z_OK MZ_OK #define Z_STREAM_END MZ_STREAM_END #define Z_NEED_DICT MZ_NEED_DICT #define Z_ERRNO MZ_ERRNO #define Z_STREAM_ERROR MZ_STREAM_ERROR #define Z_DATA_ERROR MZ_DATA_ERROR #define Z_MEM_ERROR MZ_MEM_ERROR #define Z_BUF_ERROR MZ_BUF_ERROR #define Z_VERSION_ERROR MZ_VERSION_ERROR #define Z_PARAM_ERROR MZ_PARAM_ERROR #define Z_NO_COMPRESSION MZ_NO_COMPRESSION #define Z_BEST_SPEED MZ_BEST_SPEED #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY #define Z_FILTERED MZ_FILTERED #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY #define Z_RLE MZ_RLE #define Z_FIXED MZ_FIXED #define Z_DEFLATED MZ_DEFLATED #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS #define alloc_func mz_alloc_func #define free_func mz_free_func #define internal_state mz_internal_state #define z_stream mz_stream #define deflateInit mz_deflateInit #define deflateInit2 mz_deflateInit2 #define deflateReset mz_deflateReset #define deflate mz_deflate #define deflateEnd mz_deflateEnd #define deflateBound mz_deflateBound #define compress mz_compress #define compress2 mz_compress2 #define compressBound mz_compressBound #define inflateInit mz_inflateInit #define inflateInit2 mz_inflateInit2 #define inflate mz_inflate #define inflateEnd mz_inflateEnd #define uncompress mz_uncompress #define crc32 mz_crc32 #define adler32 mz_adler32 #define MAX_WBITS 15 #define MAX_MEM_LEVEL 9 #define zError mz_error #define ZLIB_VERSION MZ_VERSION #define ZLIB_VERNUM MZ_VERNUM #define ZLIB_VER_MAJOR MZ_VER_MAJOR #define ZLIB_VER_MINOR MZ_VER_MINOR #define ZLIB_VER_REVISION MZ_VER_REVISION #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION #define zlibVersion mz_version #define zlib_version mz_version() #endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES #endif // MINIZ_NO_ZLIB_APIS // ------------------- Types and macros typedef unsigned char mz_uint8; typedef signed short mz_int16; typedef unsigned short mz_uint16; typedef unsigned int mz_uint32; typedef unsigned int mz_uint; typedef long long mz_int64; typedef unsigned long long mz_uint64; typedef int mz_bool; #define MZ_FALSE (0) #define MZ_TRUE (1) // An attempt to work around MSVC's spammy "warning C4127: conditional expression is constant" message. #ifdef _MSC_VER #define MZ_MACRO_END while (0, 0) #else #define MZ_MACRO_END while (0) #endif // ------------------- ZIP archive reading/writing #ifndef MINIZ_NO_ARCHIVE_APIS enum { MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256 }; typedef struct { mz_uint32 m_file_index; mz_uint32 m_central_dir_ofs; mz_uint16 m_version_made_by; mz_uint16 m_version_needed; mz_uint16 m_bit_flag; mz_uint16 m_method; #ifndef MINIZ_NO_TIME time_t m_time; #endif mz_uint32 m_crc32; mz_uint64 m_comp_size; mz_uint64 m_uncomp_size; mz_uint16 m_internal_attr; mz_uint32 m_external_attr; mz_uint64 m_local_header_ofs; mz_uint32 m_comment_size; char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; } mz_zip_archive_file_stat; typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); struct mz_zip_internal_state_tag; typedef struct mz_zip_internal_state_tag mz_zip_internal_state; typedef enum { MZ_ZIP_MODE_INVALID = 0, MZ_ZIP_MODE_READING = 1, MZ_ZIP_MODE_WRITING = 2, MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 } mz_zip_mode; typedef struct mz_zip_archive_tag { mz_uint64 m_archive_size; mz_uint64 m_central_directory_file_ofs; mz_uint m_total_files; mz_zip_mode m_zip_mode; mz_uint m_file_offset_alignment; mz_alloc_func m_pAlloc; mz_free_func m_pFree; mz_realloc_func m_pRealloc; void *m_pAlloc_opaque; mz_file_read_func m_pRead; mz_file_write_func m_pWrite; void *m_pIO_opaque; mz_zip_internal_state *m_pState; } mz_zip_archive; typedef enum { MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800 } mz_zip_flags; // ZIP archive reading // Inits a ZIP archive reader. // These functions read and validate the archive's central directory. mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags); mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags); #ifndef MINIZ_NO_STDIO mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); #endif // Returns the total number of files in the archive. mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); // Returns detailed information about an archive file entry. mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); // Determines if an archive file entry is a directory entry. mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); // Retrieves the filename of an archive file entry. // Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); // Attempts to locates a file in the archive's central directory. // Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH // Returns -1 if the file cannot be found. int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); // Extracts a archive file to a memory buffer using no memory allocation. mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); // Extracts a archive file to a memory buffer. mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); // Extracts a archive file to a dynamically allocated heap buffer. void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); // Extracts a archive file using a callback function to output the file's data. mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); #ifndef MINIZ_NO_STDIO // Extracts a archive file to a disk file and sets its last accessed and modified times. // This function only extracts files, not archive directory records. mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); #endif // Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. mz_bool mz_zip_reader_end(mz_zip_archive *pZip); // ZIP archive writing #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS // Inits a ZIP archive writer. mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); #ifndef MINIZ_NO_STDIO mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); #endif // Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. // For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. // For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). // Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. // Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before // the archive is finalized the file's central directory will be hosed. mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); // Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. // To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer. // level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); #ifndef MINIZ_NO_STDIO // Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. // level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); #endif // Adds a file to an archive by fully cloning the data from another archive. // This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields. mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index); // Finalizes the archive by writing the central directory records followed by the end of central directory record. // After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). // An archive must be manually finalized by calling this function for it to be valid. mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize); // Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. // Note for the archive to be valid, it must have been finalized before ending. mz_bool mz_zip_writer_end(mz_zip_archive *pZip); // Misc. high-level helper functions: // mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. // level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); // Reads a single file from an archive into a heap block. // Returns NULL on failure. void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); #endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS #endif // #ifndef MINIZ_NO_ARCHIVE_APIS // ------------------- Low-level Decompression API Definitions // Decompression flags used by tinfl_decompress(). // TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. // TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. // TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). // TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. enum { TINFL_FLAG_PARSE_ZLIB_HEADER = 1, TINFL_FLAG_HAS_MORE_INPUT = 2, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, TINFL_FLAG_COMPUTE_ADLER32 = 8 }; // High level decompression functions: // tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). // On entry: // pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. // On return: // Function returns a pointer to the decompressed data, or NULL on failure. // *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. // The caller must call mz_free() on the returned block when it's no longer needed. void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); // tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. // Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. #define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); // tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. // Returns 1 on success or 0 on failure. typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; // Max size of LZ dictionary. #define TINFL_LZ_DICT_SIZE 32768 // Return status. typedef enum { TINFL_STATUS_BAD_PARAM = -3, TINFL_STATUS_ADLER32_MISMATCH = -2, TINFL_STATUS_FAILED = -1, TINFL_STATUS_DONE = 0, TINFL_STATUS_NEEDS_MORE_INPUT = 1, TINFL_STATUS_HAS_MORE_OUTPUT = 2 } tinfl_status; // Initializes the decompressor to its initial state. #define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END #define tinfl_get_adler32(r) (r)->m_check_adler32 // Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. // This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); // Internal/private bits follow. enum { TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS }; typedef struct { mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; } tinfl_huff_table; #if MINIZ_HAS_64BIT_REGISTERS #define TINFL_USE_64BIT_BITBUF 1 #endif #if TINFL_USE_64BIT_BITBUF typedef mz_uint64 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (64) #else typedef mz_uint32 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (32) #endif struct tinfl_decompressor_tag { mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; tinfl_bit_buf_t m_bit_buf; size_t m_dist_from_out_buf_start; tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; }; // ------------------- Low-level Compression API Definitions // Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). #define TDEFL_LESS_MEMORY 0 // tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): // TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). enum { TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF }; // TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. // TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). // TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. // TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). // TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) // TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. // TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. // TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. // The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). enum { TDEFL_WRITE_ZLIB_HEADER = 0x01000, TDEFL_COMPUTE_ADLER32 = 0x02000, TDEFL_GREEDY_PARSING_FLAG = 0x04000, TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, TDEFL_RLE_MATCHES = 0x10000, TDEFL_FILTER_MATCHES = 0x20000, TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 }; // High level compression functions: // tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). // On entry: // pSrc_buf, src_buf_len: Pointer and size of source block to compress. // flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. // On return: // Function returns a pointer to the compressed data, or NULL on failure. // *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. // The caller must free() the returned block when it's no longer needed. void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); // tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. // Returns 0 on failure. size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); // Compresses an image to a compressed PNG file in memory. // On entry: // pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. // The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. // level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL // If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). // On return: // Function returns a pointer to the compressed data, or NULL on failure. // *pLen_out will be set to the size of the PNG image file. // The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); // Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); // tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 }; // TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). #if TDEFL_LESS_MEMORY enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #else enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #endif // The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. typedef enum { TDEFL_STATUS_BAD_PARAM = -2, TDEFL_STATUS_PUT_BUF_FAILED = -1, TDEFL_STATUS_OKAY = 0, TDEFL_STATUS_DONE = 1, } tdefl_status; // Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums typedef enum { TDEFL_NO_FLUSH = 0, TDEFL_SYNC_FLUSH = 2, TDEFL_FULL_FLUSH = 3, TDEFL_FINISH = 4 } tdefl_flush; // tdefl's compression state structure. typedef struct { tdefl_put_buf_func_ptr m_pPut_buf_func; void *m_pPut_buf_user; mz_uint m_flags, m_max_probes[2]; int m_greedy_parsing; mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; tdefl_status m_prev_return_status; const void *m_pIn_buf; void *m_pOut_buf; size_t *m_pIn_buf_size, *m_pOut_buf_size; tdefl_flush m_flush; const mz_uint8 *m_pSrc; size_t m_src_buf_left, m_out_buf_ofs; mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; } tdefl_compressor; // Initializes the compressor. // There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. // pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. // If pBut_buf_func is NULL the user should always call the tdefl_compress() API. // flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); // Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); // tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. // tdefl_compress_buffer() always consumes the entire input buffer. tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); mz_uint32 tdefl_get_adler32(tdefl_compressor *d); // Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros. #ifndef MINIZ_NO_ZLIB_APIS // Create tdefl_compress() flags given zlib-style compression parameters. // level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) // window_bits may be -15 (raw deflate) or 15 (zlib) // strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); #endif // #ifndef MINIZ_NO_ZLIB_APIS #ifdef __cplusplus } #endif #endif // MINIZ_HEADER_INCLUDED // ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.) #ifndef MINIZ_HEADER_FILE_ONLY typedef unsigned char mz_validate_uint16[sizeof(mz_uint16)==2 ? 1 : -1]; typedef unsigned char mz_validate_uint32[sizeof(mz_uint32)==4 ? 1 : -1]; typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1]; #include #include #define MZ_ASSERT(x) assert(x) #ifdef MINIZ_NO_MALLOC #define MZ_MALLOC(x) NULL #define MZ_FREE(x) (void)x, ((void)0) #define MZ_REALLOC(p, x) NULL #else #define MZ_MALLOC(x) malloc(x) #define MZ_FREE(x) free(x) #define MZ_REALLOC(p, x) realloc(p, x) #endif #define MZ_MAX(a,b) (((a)>(b))?(a):(b)) #define MZ_MIN(a,b) (((a)<(b))?(a):(b)) #define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) #else #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) #endif #ifdef _MSC_VER #define MZ_FORCEINLINE __forceinline #elif defined(__GNUC__) #define MZ_FORCEINLINE inline __attribute__((__always_inline__)) #else #define MZ_FORCEINLINE inline #endif #ifdef __cplusplus extern "C" { #endif // ------------------- zlib-style API's mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) { mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; if (!ptr) return MZ_ADLER32_INIT; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } return (s2 << 16) + s1; } // Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; mz_uint32 crcu32 = (mz_uint32)crc; if (!ptr) return MZ_CRC32_INIT; crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; } return ~crcu32; } void mz_free(void *p) { MZ_FREE(p); } #ifndef MINIZ_NO_ZLIB_APIS static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } static void def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } const char *mz_version(void) { return MZ_VERSION; } int mz_deflateInit(mz_streamp pStream, int level) { return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); } int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) { tdefl_compressor *pComp; mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); if (!pStream) return MZ_STREAM_ERROR; if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR; pStream->data_type = 0; pStream->adler = MZ_ADLER32_INIT; pStream->msg = NULL; pStream->reserved = 0; pStream->total_in = 0; pStream->total_out = 0; if (!pStream->zalloc) pStream->zalloc = def_alloc_func; if (!pStream->zfree) pStream->zfree = def_free_func; pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); if (!pComp) return MZ_MEM_ERROR; pStream->state = (struct mz_internal_state *)pComp; if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) { mz_deflateEnd(pStream); return MZ_PARAM_ERROR; } return MZ_OK; } int mz_deflateReset(mz_streamp pStream) { if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR; pStream->total_in = pStream->total_out = 0; tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, ((tdefl_compressor*)pStream->state)->m_flags); return MZ_OK; } int mz_deflate(mz_streamp pStream, int flush) { size_t in_bytes, out_bytes; mz_ulong orig_total_in, orig_total_out; int mz_status = MZ_OK; if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR; if (!pStream->avail_out) return MZ_BUF_ERROR; if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; orig_total_in = pStream->total_in; orig_total_out = pStream->total_out; for ( ; ; ) { tdefl_status defl_status; in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state); pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; if (defl_status < 0) { mz_status = MZ_STREAM_ERROR; break; } else if (defl_status == TDEFL_STATUS_DONE) { mz_status = MZ_STREAM_END; break; } else if (!pStream->avail_out) break; else if ((!pStream->avail_in) && (flush != MZ_FINISH)) { if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) break; return MZ_BUF_ERROR; // Can't make forward progress without some input. } } return mz_status; } int mz_deflateEnd(mz_streamp pStream) { if (!pStream) return MZ_STREAM_ERROR; if (pStream->state) { pStream->zfree(pStream->opaque, pStream->state); pStream->state = NULL; } return MZ_OK; } mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) { (void)pStream; // This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); } int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) { int status; mz_stream stream; memset(&stream, 0, sizeof(stream)); // In case mz_ulong is 64-bits (argh I hate longs). if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; stream.avail_in = (mz_uint32)source_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; status = mz_deflateInit(&stream, level); if (status != MZ_OK) return status; status = mz_deflate(&stream, MZ_FINISH); if (status != MZ_STREAM_END) { mz_deflateEnd(&stream); return (status == MZ_OK) ? MZ_BUF_ERROR : status; } *pDest_len = stream.total_out; return mz_deflateEnd(&stream); } int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); } mz_ulong mz_compressBound(mz_ulong source_len) { return mz_deflateBound(NULL, source_len); } typedef struct { tinfl_decompressor m_decomp; mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; tinfl_status m_last_status; } inflate_state; int mz_inflateInit2(mz_streamp pStream, int window_bits) { inflate_state *pDecomp; if (!pStream) return MZ_STREAM_ERROR; if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; pStream->data_type = 0; pStream->adler = 0; pStream->msg = NULL; pStream->total_in = 0; pStream->total_out = 0; pStream->reserved = 0; if (!pStream->zalloc) pStream->zalloc = def_alloc_func; if (!pStream->zfree) pStream->zfree = def_free_func; pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); if (!pDecomp) return MZ_MEM_ERROR; pStream->state = (struct mz_internal_state *)pDecomp; tinfl_init(&pDecomp->m_decomp); pDecomp->m_dict_ofs = 0; pDecomp->m_dict_avail = 0; pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; pDecomp->m_first_call = 1; pDecomp->m_has_flushed = 0; pDecomp->m_window_bits = window_bits; return MZ_OK; } int mz_inflateInit(mz_streamp pStream) { return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); } int mz_inflate(mz_streamp pStream, int flush) { inflate_state* pState; mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; size_t in_bytes, out_bytes, orig_avail_in; tinfl_status status; if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; pState = (inflate_state*)pStream->state; if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; orig_avail_in = pStream->avail_in; first_call = pState->m_first_call; pState->m_first_call = 0; if (pState->m_last_status < 0) return MZ_DATA_ERROR; if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; pState->m_has_flushed |= (flush == MZ_FINISH); if ((flush == MZ_FINISH) && (first_call)) { // MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); pState->m_last_status = status; pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; if (status < 0) return MZ_DATA_ERROR; else if (status != TINFL_STATUS_DONE) { pState->m_last_status = TINFL_STATUS_FAILED; return MZ_BUF_ERROR; } return MZ_STREAM_END; } // flush != MZ_FINISH then we must assume there's more input. if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; if (pState->m_dict_avail) { n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } for ( ; ; ) { in_bytes = pStream->avail_in; out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); pState->m_last_status = status; pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); pState->m_dict_avail = (mz_uint)out_bytes; n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); if (status < 0) return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. else if (flush == MZ_FINISH) { // The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. if (status == TINFL_STATUS_DONE) return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; // status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. else if (!pStream->avail_out) return MZ_BUF_ERROR; } else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) break; } return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } int mz_inflateEnd(mz_streamp pStream) { if (!pStream) return MZ_STREAM_ERROR; if (pStream->state) { pStream->zfree(pStream->opaque, pStream->state); pStream->state = NULL; } return MZ_OK; } int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { mz_stream stream; int status; memset(&stream, 0, sizeof(stream)); // In case mz_ulong is 64-bits (argh I hate longs). if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; stream.avail_in = (mz_uint32)source_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; status = mz_inflateInit(&stream); if (status != MZ_OK) return status; status = mz_inflate(&stream, MZ_FINISH); if (status != MZ_STREAM_END) { mz_inflateEnd(&stream); return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; } *pDest_len = stream.total_out; return mz_inflateEnd(&stream); } const char *mz_error(int err) { static struct { int m_err; const char *m_pDesc; } s_error_descs[] = { { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } }; mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc; return NULL; } #endif //MINIZ_NO_ZLIB_APIS // ------------------- Low-level Decompression (completely independent from all compression API's) #define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) #define TINFL_MEMSET(p, c, l) memset(p, c, l) #define TINFL_CR_BEGIN switch(r->m_state) { case 0: #define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END #define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END #define TINFL_CR_FINISH } // TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never // reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. #define TINFL_GET_BYTE(state_index, c) do { \ if (pIn_buf_cur >= pIn_buf_end) { \ for ( ; ; ) { \ if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ if (pIn_buf_cur < pIn_buf_end) { \ c = *pIn_buf_cur++; \ break; \ } \ } else { \ c = 0; \ break; \ } \ } \ } else c = *pIn_buf_cur++; } MZ_MACRO_END #define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n)) #define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END #define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END // TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. // It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a // Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the // bit buffer contains >=15 bits (deflate's max. Huffman code size). #define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ do { \ temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ if (temp >= 0) { \ code_len = temp >> 9; \ if ((code_len) && (num_bits >= code_len)) \ break; \ } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do { \ temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \ } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \ } while (num_bits < 15); // TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read // beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully // decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. // The slow path is only executed at the very end of the input buffer. #define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \ int temp; mz_uint code_len, c; \ if (num_bits < 15) { \ if ((pIn_buf_end - pIn_buf_cur) < 2) { \ TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ } else { \ bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \ } \ } \ if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ code_len = temp >> 9, temp &= 511; \ else { \ code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \ } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) { static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; static const int s_min_table_sizes[3] = { 257, 1, 4 }; tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; TINFL_CR_BEGIN bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } } do { TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; if (r->m_type == 0) { TINFL_SKIP_BITS(5, num_bits & 7); for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } while ((counter) && (num_bits)) { TINFL_GET_BITS(51, dist, 8); while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)dist; counter--; } while (counter) { size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } while (pIn_buf_cur >= pIn_buf_end) { if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); } else { TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); } } n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; } } else if (r->m_type == 3) { TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); } else { if (r->m_type == 1) { mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; } else { for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } r->m_table_sizes[2] = 19; } for ( ; (int)r->m_type >= 0; r->m_type--) { int tree_next, tree_cur; tinfl_huff_table *pTable; mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree); for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } if ((65536 != total) && (used_syms > 1)) { TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); } for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) { mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) { tree_cur -= ((rev_code >>= 1) & 1); if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1]; } tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; } if (r->m_type == 2) { for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); ) { mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } if ((dist == 16) && (!counter)) { TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); } num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; } if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) { TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); } TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); } } for ( ; ; ) { mz_uint8 *pSrc; for ( ; ; ) { if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) { TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); if (counter >= 256) break; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)counter; } else { int sym2; mz_uint code_len; #if TINFL_USE_64BIT_BITBUF if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } #else if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } counter = sym2; bit_buf >>= code_len; num_bits -= code_len; if (counter & 256) break; #if !TINFL_USE_64BIT_BITBUF if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } bit_buf >>= code_len; num_bits -= code_len; pOut_buf_cur[0] = (mz_uint8)counter; if (sym2 & 256) { pOut_buf_cur++; counter = sym2; break; } pOut_buf_cur[1] = (mz_uint8)sym2; pOut_buf_cur += 2; } } if ((counter &= 511) == 256) break; num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) { TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); } pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) { while (counter--) { while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; } continue; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES else if ((counter >= 9) && (counter <= dist)) { const mz_uint8 *pSrc_end = pSrc + (counter & ~7); do { ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; pOut_buf_cur += 8; } while ((pSrc += 8) < pSrc_end); if ((counter &= 7) < 3) { if (counter) { pOut_buf_cur[0] = pSrc[0]; if (counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } continue; } } #endif do { pOut_buf_cur[0] = pSrc[0]; pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur[2] = pSrc[2]; pOut_buf_cur += 3; pSrc += 3; } while ((int)(counter -= 3) > 2); if ((int)counter > 0) { pOut_buf_cur[0] = pSrc[0]; if ((int)counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } } } } while (!(r->m_final & 1)); if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } } TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); TINFL_CR_FINISH common_exit: r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) { const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; } return status; } // Higher level helper functions. void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; *pOut_len = 0; tinfl_init(&decomp); for ( ; ; ) { size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } src_buf_ofs += src_buf_size; *pOut_len += dst_buf_size; if (status == TINFL_STATUS_DONE) break; new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); if (!pNew_buf) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; } return pBuf; } size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; } int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { int result = 0; tinfl_decompressor decomp; mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; if (!pDict) return TINFL_STATUS_FAILED; tinfl_init(&decomp); for ( ; ; ) { size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); in_buf_ofs += in_buf_size; if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) break; if (status != TINFL_STATUS_HAS_MORE_OUTPUT) { result = (status == TINFL_STATUS_DONE); break; } dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); } MZ_FREE(pDict); *pIn_buf_size = in_buf_ofs; return result; } // ------------------- Low-level Compression (independent from all decompression API's) // Purposely making these tables static for faster init and thread safety. static const mz_uint16 s_tdefl_len_sym[256] = { 257,258,259,260,261,262,263,264,265,265,266,266,267,267,268,268,269,269,269,269,270,270,270,270,271,271,271,271,272,272,272,272, 273,273,273,273,273,273,273,273,274,274,274,274,274,274,274,274,275,275,275,275,275,275,275,275,276,276,276,276,276,276,276,276, 277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, 279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280, 281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281, 282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282, 283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283, 284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,285 }; static const mz_uint8 s_tdefl_len_extra[256] = { 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,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, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 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, 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,0 }; static const mz_uint8 s_tdefl_small_dist_sym[512] = { 0,1,2,3,4,4,5,5,6,6,6,6,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11, 11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14, 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, 14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 }; static const mz_uint8 s_tdefl_small_dist_extra[512] = { 0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,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,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7 }; static const mz_uint8 s_tdefl_large_dist_sym[128] = { 0,0,18,19,20,20,21,21,22,22,22,22,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26, 26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, 28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 }; static const mz_uint8 s_tdefl_large_dist_extra[128] = { 0,0,8,8,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 }; // Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1) { mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist); for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) { const mz_uint32* pHist = &hist[pass << 8]; mz_uint offsets[256], cur_ofs = 0; for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; { tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } } return pCur_syms; } // tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) { int root, leaf, next, avbl, used, dpth; if (n==0) return; else if (n==1) { A[0].m_key = 1; return; } A[0].m_key += A[1].m_key; root = 0; leaf = 2; for (next=1; next < n-1; next++) { if (leaf>=n || A[root].m_key=n || (root=0; next--) A[next].m_key = A[A[next].m_key].m_key+1; avbl = 1; used = dpth = 0; root = n-2; next = n-1; while (avbl>0) { while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; } while (avbl>used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; } avbl = 2*used; dpth++; used = 0; } } // Limits canonical Huffman code table's max code size. enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) { int i; mz_uint32 total = 0; if (code_list_len <= 1) return; for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); while (total != (1UL << max_code_size)) { pNum_codes[max_code_size]--; for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } total--; } } static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) { int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes); if (static_table) { for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; } else { tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; int num_used_syms = 0; const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; } pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); for (i = 1, j = num_used_syms; i <= code_size_limit; i++) for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); } next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); for (i = 0; i < table_len; i++) { mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; } } #define TDEFL_PUT_BITS(b, l) do { \ mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \ d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \ while (d->m_bits_in >= 8) { \ if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ d->m_bit_buffer >>= 8; \ d->m_bits_in -= 8; \ } \ } MZ_MACRO_END #define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \ if (rle_repeat_count < 3) { \ d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ } else { \ d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); packed_code_sizes[num_packed_code_sizes++] = 16; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ } rle_repeat_count = 0; } } #define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \ if (rle_z_count < 3) { \ d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); while (rle_z_count--) packed_code_sizes[num_packed_code_sizes++] = 0; \ } else if (rle_z_count <= 10) { \ d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); packed_code_sizes[num_packed_code_sizes++] = 17; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ } else { \ d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); packed_code_sizes[num_packed_code_sizes++] = 18; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ } rle_z_count = 0; } } static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; static void tdefl_start_dynamic_block(tdefl_compressor *d) { int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; d->m_huff_count[0][256] = 1; tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); for (i = 0; i < total_code_sizes_to_pack; i++) { mz_uint8 code_size = code_sizes_to_pack[i]; if (!code_size) { TDEFL_RLE_PREV_CODE_SIZE(); if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } } else { TDEFL_RLE_ZERO_CODE_SIZE(); if (code_size != prev_code_size) { TDEFL_RLE_PREV_CODE_SIZE(); d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size; } else if (++rle_repeat_count == 6) { TDEFL_RLE_PREV_CODE_SIZE(); } } prev_code_size = code_size; } if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); } tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); TDEFL_PUT_BITS(2, 2); TDEFL_PUT_BITS(num_lit_codes - 257, 5); TDEFL_PUT_BITS(num_dist_codes - 1, 5); for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break; num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; ) { mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); } } static void tdefl_start_static_block(tdefl_compressor *d) { mz_uint i; mz_uint8 *p = &d->m_huff_code_sizes[0][0]; for (i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; memset(d->m_huff_code_sizes[1], 5, 32); tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); TDEFL_PUT_BITS(1, 2); } static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; mz_uint8 *pLZ_codes; mz_uint8 *pOutput_buf = d->m_pOutput_buf; mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; mz_uint64 bit_buffer = d->m_bit_buffer; mz_uint bits_in = d->m_bits_in; #define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((mz_uint64)(b)) << bits_in); bits_in += (l); } flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint s0, s1, n0, n1, sym, num_extra_bits; mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); // This sequence coaxes MSVC into using cmov's vs. jmp's. s0 = s_tdefl_small_dist_sym[match_dist & 511]; n0 = s_tdefl_small_dist_extra[match_dist & 511]; s1 = s_tdefl_large_dist_sym[match_dist >> 8]; n1 = s_tdefl_large_dist_extra[match_dist >> 8]; sym = (match_dist < 512) ? s0 : s1; num_extra_bits = (match_dist < 512) ? n0 : n1; MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } } if (pOutput_buf >= d->m_pOutput_buf_end) return MZ_FALSE; *(mz_uint64*)pOutput_buf = bit_buffer; pOutput_buf += (bits_in >> 3); bit_buffer >>= (bits_in & ~7); bits_in &= 7; } #undef TDEFL_PUT_BITS_FAST d->m_pOutput_buf = pOutput_buf; d->m_bits_in = 0; d->m_bit_buffer = 0; while (bits_in) { mz_uint32 n = MZ_MIN(bits_in, 16); TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); bit_buffer >>= n; bits_in -= n; } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #else static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; mz_uint8 *pLZ_codes; flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint sym, num_extra_bits; mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); if (match_dist < 512) { sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; } else { sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; } MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) { if (static_block) tdefl_start_static_block(d); else tdefl_start_dynamic_block(d); return tdefl_compress_lz_codes(d); } static int tdefl_flush_block(tdefl_compressor *d, int flush) { mz_uint saved_bit_buf, saved_bits_in; mz_uint8 *pSaved_output_buf; mz_bool comp_block_succeeded = MZ_FALSE; int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; d->m_pOutput_buf = pOutput_buf_start; d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; MZ_ASSERT(!d->m_output_flush_remaining); d->m_output_flush_ofs = 0; d->m_output_flush_remaining = 0; *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) { TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8); } TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; if (!use_raw_block) comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); // If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. if ( ((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size) ) { mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; TDEFL_PUT_BITS(0, 2); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) { TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); } for (i = 0; i < d->m_total_lz_bytes; ++i) { TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); } } // Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. else if (!comp_block_succeeded) { d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; tdefl_compress_block(d, MZ_TRUE); } if (flush) { if (flush == TDEFL_FINISH) { if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } } } else { mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); } } } MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++; if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) { if (d->m_pPut_buf_func) { *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); } else if (pOutput_buf_start == d->m_output_buf) { int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); d->m_out_buf_ofs += bytes_to_copy; if ((n -= bytes_to_copy) != 0) { d->m_output_flush_ofs = bytes_to_copy; d->m_output_flush_remaining = n; } } else { d->m_out_buf_ofs += n; } } return d->m_output_flush_remaining; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES #define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p) static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q; mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s); MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for ( ; ; ) { for ( ; ; ) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; q = (const mz_uint16*)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD(q) != s01) continue; p = s; probe_len = 32; do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); if (!probe_len) { *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break; } else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); } } } #else static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const mz_uint8 *s = d->m_dict + pos, *p, *q; mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for ( ; ; ) { for ( ; ; ) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break; if (probe_len > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; } } } #endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN static mz_bool tdefl_compress_fast(tdefl_compressor *d) { // Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) { const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); d->m_src_buf_left -= num_bytes_to_process; lookahead_size += num_bytes_to_process; while (num_bytes_to_process) { mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); memcpy(d->m_dict + dst_pos, d->m_pSrc, n); if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); d->m_pSrc += n; dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; num_bytes_to_process -= n; } dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; while (lookahead_size >= 4) { mz_uint cur_match_dist, cur_match_len = 1; mz_uint8 *pCur_dict = d->m_dict + cur_pos; mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; mz_uint probe_pos = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)lookahead_pos; if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) { const mz_uint16 *p = (const mz_uint16 *)pCur_dict; const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); mz_uint32 probe_len = 32; do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); if (!probe_len) cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U))) { cur_match_len = 1; *pLZ_code_buf++ = (mz_uint8)first_trigram; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); d->m_huff_count[0][(mz_uint8)first_trigram]++; } else { mz_uint32 s0, s1; cur_match_len = MZ_MIN(cur_match_len, lookahead_size); MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); cur_match_dist--; pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; pLZ_code_buf += 3; *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; } } else { *pLZ_code_buf++ = (mz_uint8)first_trigram; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); d->m_huff_count[0][(mz_uint8)first_trigram]++; } if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } total_lz_bytes += cur_match_len; lookahead_pos += cur_match_len; dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; MZ_ASSERT(lookahead_size >= cur_match_len); lookahead_size -= cur_match_len; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } while (lookahead_size) { mz_uint8 lit = d->m_dict[cur_pos]; total_lz_bytes++; *pLZ_code_buf++ = lit; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } d->m_huff_count[0][lit]++; lookahead_pos++; dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; lookahead_size--; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } } d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; return MZ_TRUE; } #endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) { d->m_total_lz_bytes++; *d->m_pLZ_code_buf++ = lit; *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } d->m_huff_count[0][lit]++; } static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) { mz_uint32 s0, s1; MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); d->m_total_lz_bytes += match_len; d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); match_dist -= 1; d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3; *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; } static mz_bool tdefl_compress_normal(tdefl_compressor *d) { const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; tdefl_flush flush = d->m_flush; while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) { mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; // Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) { mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; src_buf_left -= num_bytes_to_process; d->m_lookahead_size += num_bytes_to_process; while (pSrc != pSrc_end) { mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; } } else { while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) { mz_uint8 c = *pSrc++; mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; src_buf_left--; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) { mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); } } } d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) break; // Simple lazy/greedy parsing state machine. len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) { if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) { mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; } if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; } } else { tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); } if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) { cur_match_dist = cur_match_len = 0; } if (d->m_saved_match_len) { if (cur_match_len > d->m_saved_match_len) { tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); if (cur_match_len >= 128) { tdefl_record_match(d, cur_match_len, cur_match_dist); d->m_saved_match_len = 0; len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } } else { tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; } } else if (!cur_match_dist) tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) { tdefl_record_match(d, cur_match_len, cur_match_dist); len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } // Move the lookahead forward by len_to_move bytes. d->m_lookahead_pos += len_to_move; MZ_ASSERT(d->m_lookahead_size >= len_to_move); d->m_lookahead_size -= len_to_move; d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE); // Check if it's time to flush the current LZ codes to the internal output buffer. if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || ( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) ) { int n; d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; } } d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; return MZ_TRUE; } static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) { if (d->m_pIn_buf_size) { *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; } if (d->m_pOut_buf_size) { size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); d->m_output_flush_ofs += (mz_uint)n; d->m_output_flush_remaining -= (mz_uint)n; d->m_out_buf_ofs += n; *d->m_pOut_buf_size = d->m_out_buf_ofs; } return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; } tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) { if (!d) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return TDEFL_STATUS_BAD_PARAM; } d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; d->m_out_buf_ofs = 0; d->m_flush = flush; if ( ((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf) ) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); } d->m_wants_to_finish |= (flush == TDEFL_FINISH); if ((d->m_output_flush_remaining) || (d->m_finished)) return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) { if (!tdefl_compress_fast(d)) return d->m_prev_return_status; } else #endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN { if (!tdefl_compress_normal(d)) return d->m_prev_return_status; } if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) { if (tdefl_flush_block(d, flush) < 0) return d->m_prev_return_status; d->m_finished = (flush == TDEFL_FINISH); if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; } } return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); } tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) { MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); } tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user; d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash); d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); return TDEFL_STATUS_OKAY; } tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) { return d->m_prev_return_status; } mz_uint32 tdefl_get_adler32(tdefl_compressor *d) { return d->m_adler32; } mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); MZ_FREE(pComp); return succeeded; } typedef struct { size_t m_size, m_capacity; mz_uint8 *m_pBuf; mz_bool m_expandable; } tdefl_output_buffer; static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) { tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; size_t new_size = p->m_size + len; if (new_size > p->m_capacity) { size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE; do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity); pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE; p->m_pBuf = pNew_buf; p->m_capacity = new_capacity; } memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size; return MZ_TRUE; } void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); if (!pOut_len) return MZ_FALSE; else *pOut_len = 0; out_buf.m_expandable = MZ_TRUE; if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL; *pOut_len = out_buf.m_size; return out_buf.m_pBuf; } size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); if (!pOut_buf) return 0; out_buf.m_pBuf = (mz_uint8*)pOut_buf; out_buf.m_capacity = out_buf_len; if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0; return out_buf.m_size; } #ifndef MINIZ_NO_ZLIB_APIS static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; // level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) { mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES; else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK; else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES; return comp_flags; } #endif //MINIZ_NO_ZLIB_APIS #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable:4204) // nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) #endif // Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at // http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. // This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) { // Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0; if (!pComp) return NULL; MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57+MZ_MAX(64, (1+bpl)*h); if (NULL == (out_buf.m_pBuf = (mz_uint8*)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; } // write dummy header for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); // compress image data tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); } if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } // write real header *pLen_out = out_buf.m_size-41; { static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06}; mz_uint8 pnghdr[41]={0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52, 0,0,(mz_uint8)(w>>8),(mz_uint8)w,0,0,(mz_uint8)(h>>8),(mz_uint8)h,8,chans[num_chans],0,0,0,0,0,0,0, (mz_uint8)(*pLen_out>>24),(mz_uint8)(*pLen_out>>16),(mz_uint8)(*pLen_out>>8),(mz_uint8)*pLen_out,0x49,0x44,0x41,0x54}; c=(mz_uint32)mz_crc32(MZ_CRC32_INIT,pnghdr+12,17); for (i=0; i<4; ++i, c<<=8) ((mz_uint8*)(pnghdr+29))[i]=(mz_uint8)(c>>24); memcpy(out_buf.m_pBuf, pnghdr, 41); } // write footer (IDAT CRC-32, followed by IEND chunk) if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } c = (mz_uint32)mz_crc32(MZ_CRC32_INIT,out_buf.m_pBuf+41-4, *pLen_out+4); for (i=0; i<4; ++i, c<<=8) (out_buf.m_pBuf+out_buf.m_size-16)[i] = (mz_uint8)(c >> 24); // compute final size of file, grab compressed data buffer and return *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf; } void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) { // Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); } #ifdef _MSC_VER #pragma warning (pop) #endif // ------------------- .ZIP archive reading #ifndef MINIZ_NO_ARCHIVE_APIS #ifdef MINIZ_NO_STDIO #define MZ_FILE void * #else #include #include #if defined(_MSC_VER) || defined(__MINGW64__) static FILE *mz_fopen(const char *pFilename, const char *pMode) { FILE* pFile = NULL; fopen_s(&pFile, pFilename, pMode); return pFile; } static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) { FILE* pFile = NULL; if (freopen_s(&pFile, pPath, pMode, pStream)) return NULL; return pFile; } #ifndef MINIZ_NO_TIME #include #endif #define MZ_FILE FILE #define MZ_FOPEN mz_fopen #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 _ftelli64 #define MZ_FSEEK64 _fseeki64 #define MZ_FILE_STAT_STRUCT _stat #define MZ_FILE_STAT _stat #define MZ_FFLUSH fflush #define MZ_FREOPEN mz_freopen #define MZ_DELETE_FILE remove #elif defined(__MINGW32__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FILE FILE #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello64 #define MZ_FSEEK64 fseeko64 #define MZ_FILE_STAT_STRUCT _stat #define MZ_FILE_STAT _stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #elif defined(__TINYC__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FILE FILE #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftell #define MZ_FSEEK64 fseek #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #elif defined(__GNUC__) && _LARGEFILE64_SOURCE #ifndef MINIZ_NO_TIME #include #endif #define MZ_FILE FILE #define MZ_FOPEN(f, m) fopen64(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello64 #define MZ_FSEEK64 fseeko64 #define MZ_FILE_STAT_STRUCT stat64 #define MZ_FILE_STAT stat64 #define MZ_FFLUSH fflush #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) #define MZ_DELETE_FILE remove #else #ifndef MINIZ_NO_TIME #include #endif #define MZ_FILE FILE #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello #define MZ_FSEEK64 fseeko #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #endif // #ifdef _MSC_VER #endif // #ifdef MINIZ_NO_STDIO #define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) // Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. enum { // ZIP archive identifiers and record sizes MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, // Central directory header record offsets MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8, MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16, MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, // Local directory header offsets MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10, MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, // End of central directory offsets MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, }; typedef struct { void *m_p; size_t m_size, m_capacity; mz_uint m_element_size; } mz_zip_array; struct mz_zip_internal_state_tag { mz_zip_array m_central_dir; mz_zip_array m_central_dir_offsets; mz_zip_array m_sorted_central_dir_offsets; MZ_FILE *m_pFile; void *m_pMem; size_t m_mem_size; size_t m_mem_capacity; }; #define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) { pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); memset(pArray, 0, sizeof(mz_zip_array)); } static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) { void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE; if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; } if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE; pArray->m_p = pNew_p; pArray->m_capacity = new_capacity; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) { if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; } return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) { if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; } pArray->m_size = new_size; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) { return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); } static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) { size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); return MZ_TRUE; } #ifndef MINIZ_NO_TIME static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date) { struct tm tm; memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31; tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62; return mktime(&tm); } static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) { #ifdef _MSC_VER struct tm tm_struct; struct tm *tm = &tm_struct; errno_t err = localtime_s(tm, &time); if (err) { *pDOS_date = 0; *pDOS_time = 0; return; } #else struct tm *tm = localtime(&time); #endif *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); } #endif #ifndef MINIZ_NO_STDIO static mz_bool mz_zip_get_file_modified_time(const char *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) { #ifdef MINIZ_NO_TIME (void)pFilename; *pDOS_date = *pDOS_time = 0; #else struct MZ_FILE_STAT_STRUCT file_stat; // On Linux with x86 glibc, this call will fail on large files (>= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. if (MZ_FILE_STAT(pFilename, &file_stat) != 0) return MZ_FALSE; mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date); #endif // #ifdef MINIZ_NO_TIME return MZ_TRUE; } #ifndef MINIZ_NO_TIME static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time) { struct utimbuf t; t.actime = access_time; t.modtime = modified_time; return !utime(pFilename, &t); } #endif // #ifndef MINIZ_NO_TIME #endif // #ifndef MINIZ_NO_STDIO static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags) { (void)flags; if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) return MZ_FALSE; if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; if (!pZip->m_pFree) pZip->m_pFree = def_free_func; if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; pZip->m_zip_mode = MZ_ZIP_MODE_READING; pZip->m_archive_size = 0; pZip->m_central_directory_file_ofs = 0; pZip->m_total_files = 0; if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) return MZ_FALSE; memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) { const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); mz_uint8 l = 0, r = 0; pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pE = pL + MZ_MIN(l_len, r_len); while (pL < pE) { if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break; pL++; pR++; } return (pL == pE) ? (l_len < r_len) : (l < r); } #define MZ_SWAP_UINT32(a, b) do { mz_uint32 t = a; a = b; b = t; } MZ_MACRO_END // Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) { mz_zip_internal_state *pState = pZip->m_pState; const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; const mz_zip_array *pCentral_dir = &pState->m_central_dir; mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); const int size = pZip->m_total_files; int start = (size - 2) >> 1, end; while (start >= 0) { int child, root = start; for ( ; ; ) { if ((child = (root << 1) + 1) >= size) break; child += (((child + 1) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]))); if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) break; MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; } start--; } end = size - 1; while (end > 0) { int child, root = 0; MZ_SWAP_UINT32(pIndices[end], pIndices[0]); for ( ; ; ) { if ((child = (root << 1) + 1) >= end) break; child += (((child + 1) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])); if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) break; MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; } end--; } } static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags) { mz_uint cdir_size, num_this_disk, cdir_disk_index; mz_uint64 cdir_ofs; mz_int64 cur_file_ofs; const mz_uint8 *p; mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); // Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return MZ_FALSE; // Find the end of central directory record by scanning the file from the end towards the beginning. cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); for ( ; ; ) { int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) return MZ_FALSE; for (i = n - 4; i >= 0; --i) if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) break; if (i >= 0) { cur_file_ofs += i; break; } if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) return MZ_FALSE; cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); } // Read and verify the end of central directory record. if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return MZ_FALSE; if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) || ((pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) != MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS))) return MZ_FALSE; num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) return MZ_FALSE; if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) return MZ_FALSE; cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) return MZ_FALSE; pZip->m_central_directory_file_ofs = cdir_ofs; if (pZip->m_total_files) { mz_uint i, n; // Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and another to hold the sorted indices. if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) return MZ_FALSE; if (sort_central_dir) { if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) return MZ_FALSE; } if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) return MZ_FALSE; // Now create an index into the central directory file records, do some basic sanity checking on each record, and check for zip64 entries (which are not yet supported). p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) { mz_uint total_header_size, comp_size, decomp_size, disk_index; if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) return MZ_FALSE; MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); if (sort_central_dir) MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF)) return MZ_FALSE; disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); if ((disk_index != num_this_disk) && (disk_index != 1)) return MZ_FALSE; if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) return MZ_FALSE; if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) return MZ_FALSE; n -= total_header_size; p += total_header_size; } } if (sort_central_dir) mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); return MZ_TRUE; } mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags) { if ((!pZip) || (!pZip->m_pRead)) return MZ_FALSE; if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_archive_size = size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end(pZip); return MZ_FALSE; } return MZ_TRUE; } static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); return s; } mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags) { if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_archive_size = size; pZip->m_pRead = mz_zip_mem_read_func; pZip->m_pIO_opaque = pZip; #ifdef __cplusplus pZip->m_pState->m_pMem = const_cast(pMem); #else pZip->m_pState->m_pMem = (void *)pMem; #endif pZip->m_pState->m_mem_size = size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end(pZip); return MZ_FALSE; } return MZ_TRUE; } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) return 0; return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); } mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) { mz_uint64 file_size; MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb"); if (!pFile) return MZ_FALSE; if (MZ_FSEEK64(pFile, 0, SEEK_END)) { MZ_FCLOSE(pFile); return MZ_FALSE; } file_size = MZ_FTELL64(pFile); if (!mz_zip_reader_init_internal(pZip, flags)) { MZ_FCLOSE(pFile); return MZ_FALSE; } pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pState->m_pFile = pFile; pZip->m_archive_size = file_size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end(pZip); return MZ_FALSE; } return MZ_TRUE; } #endif // #ifndef MINIZ_NO_STDIO mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) { return pZip ? pZip->m_total_files : 0; } static MZ_FORCEINLINE const mz_uint8 *mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index) { if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return NULL; return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); } mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) { mz_uint m_bit_flag; const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); if (!p) return MZ_FALSE; m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); return (m_bit_flag & 1); } mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) { mz_uint filename_len, external_attr; const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); if (!p) return MZ_FALSE; // First see if the filename ends with a '/' character. filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); if (filename_len) { if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') return MZ_TRUE; } // Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. // Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. // FIXME: Remove this check? Is it necessary - we already check the filename. external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); if ((external_attr & 0x10) != 0) return MZ_TRUE; return MZ_FALSE; } mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) { mz_uint n; const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); if ((!p) || (!pStat)) return MZ_FALSE; // Unpack the central directory record. pStat->m_file_index = file_index; pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); #ifndef MINIZ_NO_TIME pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); #endif pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); // Copy as much of the filename and comment as possible. n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0'; n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); pStat->m_comment_size = n; memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0'; return MZ_TRUE; } mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) { mz_uint n; const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; } n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); if (filename_buf_size) { n = MZ_MIN(n, filename_buf_size - 1); memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pFilename[n] = '\0'; } return n + 1; } static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) { mz_uint i; if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) return 0 == memcmp(pA, pB, len); for (i = 0; i < len; ++i) if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) return MZ_FALSE; return MZ_TRUE; } static MZ_FORCEINLINE int mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) { const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); mz_uint8 l = 0, r = 0; pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pE = pL + MZ_MIN(l_len, r_len); while (pL < pE) { if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break; pL++; pR++; } return (pL == pE) ? (int)(l_len - r_len) : (l - r); } static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename) { mz_zip_internal_state *pState = pZip->m_pState; const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; const mz_zip_array *pCentral_dir = &pState->m_central_dir; mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); const int size = pZip->m_total_files; const mz_uint filename_len = (mz_uint)strlen(pFilename); int l = 0, h = size - 1; while (l <= h) { int m = (l + h) >> 1, file_index = pIndices[m], comp = mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); if (!comp) return file_index; else if (comp < 0) l = m + 1; else h = m - 1; } return -1; } int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) { mz_uint file_index; size_t name_len, comment_len; if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return -1; if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) return mz_zip_reader_locate_file_binary_search(pZip, pName); name_len = strlen(pName); if (name_len > 0xFFFF) return -1; comment_len = pComment ? strlen(pComment) : 0; if (comment_len > 0xFFFF) return -1; for (file_index = 0; file_index < pZip->m_total_files; file_index++) { const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; if (filename_len < name_len) continue; if (comment_len) { mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); const char *pFile_comment = pFilename + filename_len + file_extra_len; if ((file_comment_len != comment_len) || (!mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags))) continue; } if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) { int ofs = filename_len - 1; do { if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) break; } while (--ofs >= 0); ofs++; pFilename += ofs; filename_len -= ofs; } if ((filename_len == name_len) && (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags))) return file_index; } return -1; } mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { int status = TINFL_STATUS_DONE; mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; mz_zip_archive_file_stat file_stat; void *pRead_buf; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; tinfl_decompressor inflator; if ((buf_size) && (!pBuf)) return MZ_FALSE; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) if (!file_stat.m_comp_size) return MZ_TRUE; // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). // I'm torn how to handle this case - should it fail instead? if (mz_zip_reader_is_file_a_directory(pZip, file_index)) return MZ_TRUE; // Encryption and patch files are not supported. if (file_stat.m_bit_flag & (1 | 32)) return MZ_FALSE; // This function only supports stored and deflate. if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return MZ_FALSE; // Ensure supplied output buffer is large enough. needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; if (buf_size < needed_size) return MZ_FALSE; // Read and parse the local directory entry. cur_file_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return MZ_FALSE; if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return MZ_FALSE; cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) return MZ_FALSE; if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { // The file is stored or the caller has requested the compressed data. if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) return MZ_FALSE; return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32); } // Decompress the file either directly from memory or from a file input buffer. tinfl_init(&inflator); if (pZip->m_pState->m_pMem) { // Read directly from the archive in memory. pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; read_buf_size = read_buf_avail = file_stat.m_comp_size; comp_remaining = 0; } else if (pUser_read_buf) { // Use a user provided read buffer. if (!user_read_buf_size) return MZ_FALSE; pRead_buf = (mz_uint8 *)pUser_read_buf; read_buf_size = user_read_buf_size; read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } else { // Temporarily allocate a read buffer. read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); #ifdef _MSC_VER if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) #else if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) #endif return MZ_FALSE; if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) return MZ_FALSE; read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } do { size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; comp_remaining -= read_buf_avail; read_buf_ofs = 0; } in_buf_size = (size_t)read_buf_avail; status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); read_buf_avail -= in_buf_size; read_buf_ofs += in_buf_size; out_buf_ofs += out_buf_size; } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); if (status == TINFL_STATUS_DONE) { // Make sure the entire file was decompressed, and check its CRC. if ((out_buf_ofs != file_stat.m_uncomp_size) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)) status = TINFL_STATUS_FAILED; } if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return status == TINFL_STATUS_DONE; } mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); if (file_index < 0) return MZ_FALSE; return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); } mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) { return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); } mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) { return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); } void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) { mz_uint64 comp_size, uncomp_size, alloc_size; const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); void *pBuf; if (pSize) *pSize = 0; if (!p) return NULL; comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; #ifdef _MSC_VER if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) #else if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) #endif return NULL; if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) return NULL; if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return NULL; } if (pSize) *pSize = (size_t)alloc_size; return pBuf; } void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) { int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); if (file_index < 0) { if (pSize) *pSize = 0; return MZ_FALSE; } return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); } mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) { int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT; mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; mz_zip_archive_file_stat file_stat; void *pRead_buf = NULL; void *pWrite_buf = NULL; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) if (!file_stat.m_comp_size) return MZ_TRUE; // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). // I'm torn how to handle this case - should it fail instead? if (mz_zip_reader_is_file_a_directory(pZip, file_index)) return MZ_TRUE; // Encryption and patch files are not supported. if (file_stat.m_bit_flag & (1 | 32)) return MZ_FALSE; // This function only supports stored and deflate. if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return MZ_FALSE; // Read and parse the local directory entry. cur_file_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return MZ_FALSE; if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return MZ_FALSE; cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) return MZ_FALSE; // Decompress the file either directly from memory or from a file input buffer. if (pZip->m_pState->m_pMem) { pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; read_buf_size = read_buf_avail = file_stat.m_comp_size; comp_remaining = 0; } else { read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) return MZ_FALSE; read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { // The file is stored or the caller has requested the compressed data. if (pZip->m_pState->m_pMem) { #ifdef _MSC_VER if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) #else if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) #endif return MZ_FALSE; if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) status = TINFL_STATUS_FAILED; else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); cur_file_ofs += file_stat.m_comp_size; #pragma unused(cur_file_ofs) out_buf_ofs += file_stat.m_comp_size; comp_remaining = 0; #pragma unused(comp_remaining) } else { while (comp_remaining) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; break; } if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; out_buf_ofs += read_buf_avail; comp_remaining -= read_buf_avail; } } } else { tinfl_decompressor inflator; tinfl_init(&inflator); if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) status = TINFL_STATUS_FAILED; else { do { mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; comp_remaining -= read_buf_avail; read_buf_ofs = 0; } in_buf_size = (size_t)read_buf_avail; status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); read_buf_avail -= in_buf_size; read_buf_ofs += in_buf_size; if (out_buf_size) { if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) { status = TINFL_STATUS_FAILED; break; } file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) { status = TINFL_STATUS_FAILED; break; } } } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); } } if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) { // Make sure the entire file was decompressed, and check its CRC. if ((out_buf_ofs != file_stat.m_uncomp_size) || (file_crc32 != file_stat.m_crc32)) status = TINFL_STATUS_FAILED; } if (!pZip->m_pState->m_pMem) pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); if (pWrite_buf) pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); return status == TINFL_STATUS_DONE; } mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) { int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); if (file_index < 0) return MZ_FALSE; return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) { (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque); } mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) { mz_bool status; mz_zip_archive_file_stat file_stat; MZ_FILE *pFile; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; pFile = MZ_FOPEN(pDst_filename, "wb"); if (!pFile) return MZ_FALSE; status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); if (MZ_FCLOSE(pFile) == EOF) return MZ_FALSE; #ifndef MINIZ_NO_TIME if (status) mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); #endif return status; } #endif // #ifndef MINIZ_NO_STDIO mz_bool mz_zip_reader_end(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return MZ_FALSE; if (pZip->m_pState) { mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL; mz_zip_array_clear(pZip, &pState->m_central_dir); mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); #ifndef MINIZ_NO_STDIO if (pState->m_pFile) { MZ_FCLOSE(pState->m_pFile); pState->m_pFile = NULL; } #endif // #ifndef MINIZ_NO_STDIO pZip->m_pFree(pZip->m_pAlloc_opaque, pState); } pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; return MZ_TRUE; } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) { int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags); if (file_index < 0) return MZ_FALSE; return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); } #endif // ------------------- .ZIP archive writing #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); } static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); } #define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) #define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) { if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) return MZ_FALSE; if (pZip->m_file_offset_alignment) { // Ensure user specified file offset alignment is a power of 2. if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) return MZ_FALSE; } if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; if (!pZip->m_pFree) pZip->m_pFree = def_free_func; if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; pZip->m_archive_size = existing_size; pZip->m_central_directory_file_ofs = 0; pZip->m_total_files = 0; if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) return MZ_FALSE; memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); return MZ_TRUE; } static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_zip_internal_state *pState = pZip->m_pState; mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); #ifdef _MSC_VER if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) #else if ((!n) || ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) #endif return 0; if (new_size > pState->m_mem_capacity) { void *pNew_block; size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2; if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) return 0; pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity; } memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); pState->m_mem_size = (size_t)new_size; return n; } mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) { pZip->m_pWrite = mz_zip_heap_write_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) return MZ_FALSE; if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) { if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) { mz_zip_writer_end(pZip); return MZ_FALSE; } pZip->m_pState->m_mem_capacity = initial_allocation_size; } return MZ_TRUE; } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) return 0; return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); } mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) { MZ_FILE *pFile; pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) return MZ_FALSE; if (NULL == (pFile = MZ_FOPEN(pFilename, "wb"))) { mz_zip_writer_end(pZip); return MZ_FALSE; } pZip->m_pState->m_pFile = pFile; if (size_to_reserve_at_beginning) { mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf); do { size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) { mz_zip_writer_end(pZip); return MZ_FALSE; } cur_ofs += n; size_to_reserve_at_beginning -= n; } while (size_to_reserve_at_beginning); } return MZ_TRUE; } #endif // #ifndef MINIZ_NO_STDIO mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) { mz_zip_internal_state *pState; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return MZ_FALSE; // No sense in trying to write to an archive that's already at the support max size if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) return MZ_FALSE; pState = pZip->m_pState; if (pState->m_pFile) { #ifdef MINIZ_NO_STDIO pFilename; return MZ_FALSE; #else // Archive is being read from stdio - try to reopen as writable. if (pZip->m_pIO_opaque != pZip) return MZ_FALSE; if (!pFilename) return MZ_FALSE; pZip->m_pWrite = mz_zip_file_write_func; if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) { // The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. mz_zip_reader_end(pZip); return MZ_FALSE; } #endif // #ifdef MINIZ_NO_STDIO } else if (pState->m_pMem) { // Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. if (pZip->m_pIO_opaque != pZip) return MZ_FALSE; pState->m_mem_capacity = pState->m_mem_size; pZip->m_pWrite = mz_zip_heap_write_func; } // Archive is being read via a user provided read function - make sure the user has specified a write function too. else if (!pZip->m_pWrite) return MZ_FALSE; // Start writing new files at the archive's current central directory location. pZip->m_archive_size = pZip->m_central_directory_file_ofs; pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; pZip->m_central_directory_file_ofs = 0; return MZ_TRUE; } mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) { return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); } typedef struct { mz_zip_archive *m_pZip; mz_uint64 m_cur_archive_file_ofs; mz_uint64 m_comp_size; } mz_zip_writer_add_state; static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void *pUser) { mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) return MZ_FALSE; pState->m_cur_archive_file_ofs += len; pState->m_comp_size += len; return MZ_TRUE; } static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) { (void)pZip; memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); return MZ_TRUE; } static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) { (void)pZip; memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs); return MZ_TRUE; } static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) { mz_zip_internal_state *pState = pZip->m_pState; mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; size_t orig_central_dir_size = pState->m_central_dir.m_size; mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; // No zip64 support yet if ((local_header_ofs > 0xFFFFFFFF) || (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + comment_size) > 0xFFFFFFFF)) return MZ_FALSE; if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) return MZ_FALSE; if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) { // Try to push the central directory array back into its original state. mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) { // Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. if (*pArchive_name == '/') return MZ_FALSE; while (*pArchive_name) { if ((*pArchive_name == '\\') || (*pArchive_name == ':')) return MZ_FALSE; pArchive_name++; } return MZ_TRUE; } static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) { mz_uint32 n; if (!pZip->m_file_offset_alignment) return 0; n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1); } static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) { char buf[4096]; memset(buf, 0, MZ_MIN(sizeof(buf), n)); while (n) { mz_uint32 s = MZ_MIN(sizeof(buf), n); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) return MZ_FALSE; cur_file_ofs += s; n -= s; } return MZ_TRUE; } mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) { mz_uint16 method = 0, dos_time = 0, dos_date = 0; mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; size_t archive_name_size; mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; tdefl_compressor *pComp = NULL; mz_bool store_data_uncompressed; mz_zip_internal_state *pState; if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; level = level_and_flags & 0xF; store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION)) return MZ_FALSE; pState = pZip->m_pState; if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) return MZ_FALSE; // No zip64 support yet if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) return MZ_FALSE; if (!mz_zip_writer_validate_archive_name(pArchive_name)) return MZ_FALSE; #ifndef MINIZ_NO_TIME { time_t cur_time; time(&cur_time); mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date); } #endif // #ifndef MINIZ_NO_TIME archive_name_size = strlen(pArchive_name); if (archive_name_size > 0xFFFF) return MZ_FALSE; num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); // no zip64 support yet if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) return MZ_FALSE; if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) { // Set DOS Subdirectory attribute bit. ext_attributes |= 0x10; // Subdirectories cannot contain data. if ((buf_size) || (uncomp_size)) return MZ_FALSE; } // Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size)) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) return MZ_FALSE; if ((!store_data_uncompressed) && (buf_size)) { if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) return MZ_FALSE; } if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } local_dir_header_ofs += num_alignment_padding_bytes; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); MZ_CLEAR_OBJ(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } cur_archive_file_ofs += archive_name_size; if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size); uncomp_size = buf_size; if (uncomp_size <= 3) { level = 0; store_data_uncompressed = MZ_TRUE; } } if (store_data_uncompressed) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } cur_archive_file_ofs += buf_size; comp_size = buf_size; if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) method = MZ_DEFLATED; } else if (buf_size) { mz_zip_writer_add_state state; state.m_pZip = pZip; state.m_cur_archive_file_ofs = cur_archive_file_ofs; state.m_comp_size = 0; if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } comp_size = state.m_comp_size; cur_archive_file_ofs = state.m_cur_archive_file_ofs; method = MZ_DEFLATED; } pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); pComp = NULL; // no zip64 support yet if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) return MZ_FALSE; if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) return MZ_FALSE; if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return MZ_FALSE; if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) return MZ_FALSE; pZip->m_total_files++; pZip->m_archive_size = cur_archive_file_ofs; return MZ_TRUE; } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) { mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; size_t archive_name_size; mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; MZ_FILE *pSrc_file = NULL; if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; level = level_and_flags & 0xF; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) return MZ_FALSE; if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) return MZ_FALSE; if (!mz_zip_writer_validate_archive_name(pArchive_name)) return MZ_FALSE; archive_name_size = strlen(pArchive_name); if (archive_name_size > 0xFFFF) return MZ_FALSE; num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); // no zip64 support yet if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) return MZ_FALSE; if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date)) return MZ_FALSE; pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); if (!pSrc_file) return MZ_FALSE; MZ_FSEEK64(pSrc_file, 0, SEEK_END); uncomp_size = MZ_FTELL64(pSrc_file); MZ_FSEEK64(pSrc_file, 0, SEEK_SET); if (uncomp_size > 0xFFFFFFFF) { // No zip64 support yet MZ_FCLOSE(pSrc_file); return MZ_FALSE; } if (uncomp_size <= 3) level = 0; if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) { MZ_FCLOSE(pSrc_file); return MZ_FALSE; } local_dir_header_ofs += num_alignment_padding_bytes; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); MZ_CLEAR_OBJ(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { MZ_FCLOSE(pSrc_file); return MZ_FALSE; } cur_archive_file_ofs += archive_name_size; if (uncomp_size) { mz_uint64 uncomp_remaining = uncomp_size; void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); if (!pRead_buf) { MZ_FCLOSE(pSrc_file); return MZ_FALSE; } if (!level) { while (uncomp_remaining) { mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); MZ_FCLOSE(pSrc_file); return MZ_FALSE; } uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); uncomp_remaining -= n; cur_archive_file_ofs += n; } comp_size = uncomp_size; } else { mz_bool result = MZ_FALSE; mz_zip_writer_add_state state; tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); if (!pComp) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); MZ_FCLOSE(pSrc_file); return MZ_FALSE; } state.m_pZip = pZip; state.m_cur_archive_file_ofs = cur_archive_file_ofs; state.m_comp_size = 0; if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); MZ_FCLOSE(pSrc_file); return MZ_FALSE; } for ( ; ; ) { size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE); tdefl_status status; if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) break; uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); uncomp_remaining -= in_buf_size; status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH); if (status == TDEFL_STATUS_DONE) { result = MZ_TRUE; break; } else if (status != TDEFL_STATUS_OKAY) break; } pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); if (!result) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); MZ_FCLOSE(pSrc_file); return MZ_FALSE; } comp_size = state.m_comp_size; cur_archive_file_ofs = state.m_cur_archive_file_ofs; method = MZ_DEFLATED; } pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); } MZ_FCLOSE(pSrc_file); pSrc_file = NULL; // no zip64 support yet if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) return MZ_FALSE; if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) return MZ_FALSE; if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return MZ_FALSE; if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) return MZ_FALSE; pZip->m_total_files++; pZip->m_archive_size = cur_archive_file_ofs; return MZ_TRUE; } #endif // #ifndef MINIZ_NO_STDIO mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index) { mz_uint n, bit_flags, num_alignment_padding_bytes; mz_uint64 comp_bytes_remaining, local_dir_header_ofs; mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; size_t orig_central_dir_size; mz_zip_internal_state *pState; void *pBuf; const mz_uint8 *pSrc_central_header; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) return MZ_FALSE; if (NULL == (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index))) return MZ_FALSE; pState = pZip->m_pState; num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); // no zip64 support yet if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) return MZ_FALSE; cur_src_file_ofs = MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS); cur_dst_file_ofs = pZip->m_archive_size; if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return MZ_FALSE; if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return MZ_FALSE; cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) return MZ_FALSE; cur_dst_file_ofs += num_alignment_padding_bytes; local_dir_header_ofs = cur_dst_file_ofs; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return MZ_FALSE; cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); comp_bytes_remaining = n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(sizeof(mz_uint32) * 4, MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining))))) return MZ_FALSE; while (comp_bytes_remaining) { n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining); if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return MZ_FALSE; } cur_src_file_ofs += n; if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return MZ_FALSE; } cur_dst_file_ofs += n; comp_bytes_remaining -= n; } bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); if (bit_flags & 8) { // Copy data descriptor if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return MZ_FALSE; } n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return MZ_FALSE; } cur_src_file_ofs += n; #pragma unused(cur_src_file_ofs) cur_dst_file_ofs += n; } pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); // no zip64 support yet if (cur_dst_file_ofs > 0xFFFFFFFF) return MZ_FALSE; orig_central_dir_size = pState->m_central_dir.m_size; memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) return MZ_FALSE; n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n)) { mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return MZ_FALSE; } if (pState->m_central_dir.m_size > 0xFFFFFFFF) return MZ_FALSE; n = (mz_uint32)orig_central_dir_size; if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) { mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return MZ_FALSE; } pZip->m_total_files++; pZip->m_archive_size = cur_dst_file_ofs; return MZ_TRUE; } mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) { mz_zip_internal_state *pState; mz_uint64 central_dir_ofs, central_dir_size; mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE]; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) return MZ_FALSE; pState = pZip->m_pState; // no zip64 support yet if ((pZip->m_total_files > 0xFFFF) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) return MZ_FALSE; central_dir_ofs = 0; central_dir_size = 0; if (pZip->m_total_files) { // Write central directory central_dir_ofs = pZip->m_archive_size; central_dir_size = pState->m_central_dir.m_size; pZip->m_central_directory_file_ofs = central_dir_ofs; if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) return MZ_FALSE; pZip->m_archive_size += central_dir_size; } // Write end of central directory record MZ_CLEAR_OBJ(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs); if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, sizeof(hdr)) != sizeof(hdr)) return MZ_FALSE; #ifndef MINIZ_NO_STDIO if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) return MZ_FALSE; #endif // #ifndef MINIZ_NO_STDIO pZip->m_archive_size += sizeof(hdr); pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; return MZ_TRUE; } mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize) { if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize)) return MZ_FALSE; if (pZip->m_pWrite != mz_zip_heap_write_func) return MZ_FALSE; if (!mz_zip_writer_finalize_archive(pZip)) return MZ_FALSE; *pBuf = pZip->m_pState->m_pMem; *pSize = pZip->m_pState->m_mem_size; pZip->m_pState->m_pMem = NULL; pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; return MZ_TRUE; } mz_bool mz_zip_writer_end(mz_zip_archive *pZip) { mz_zip_internal_state *pState; mz_bool status = MZ_TRUE; if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) return MZ_FALSE; pState = pZip->m_pState; pZip->m_pState = NULL; mz_zip_array_clear(pZip, &pState->m_central_dir); mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); #ifndef MINIZ_NO_STDIO if (pState->m_pFile) { MZ_FCLOSE(pState->m_pFile); pState->m_pFile = NULL; } #endif // #ifndef MINIZ_NO_STDIO if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); pState->m_pMem = NULL; } pZip->m_pFree(pZip->m_pAlloc_opaque, pState); pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; return status; } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) { mz_bool status, created_new_archive = MZ_FALSE; mz_zip_archive zip_archive; struct MZ_FILE_STAT_STRUCT file_stat; MZ_CLEAR_OBJ(zip_archive); if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) return MZ_FALSE; if (!mz_zip_writer_validate_archive_name(pArchive_name)) return MZ_FALSE; if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) { // Create a new archive. if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0)) return MZ_FALSE; created_new_archive = MZ_TRUE; } else { // Append to an existing archive. if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) return MZ_FALSE; if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename)) { mz_zip_reader_end(&zip_archive); return MZ_FALSE; } } status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); // Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) if (!mz_zip_writer_finalize_archive(&zip_archive)) status = MZ_FALSE; if (!mz_zip_writer_end(&zip_archive)) status = MZ_FALSE; if ((!status) && (created_new_archive)) { // It's a new archive and something went wrong, so just delete it. int ignoredStatus = MZ_DELETE_FILE(pZip_filename); (void)ignoredStatus; } return status; } void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) { int file_index; mz_zip_archive zip_archive; void *p = NULL; if (pSize) *pSize = 0; if ((!pZip_filename) || (!pArchive_name)) return NULL; MZ_CLEAR_OBJ(zip_archive); if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) return NULL; if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, flags)) >= 0) p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); mz_zip_reader_end(&zip_archive); return p; } #endif // #ifndef MINIZ_NO_STDIO #endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS #endif // #ifndef MINIZ_NO_ARCHIVE_APIS #ifdef __cplusplus } #endif #endif // MINIZ_HEADER_FILE_ONLY /* This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to */ dcm2niix-1.0.20171215/console/nifti1.h000066400000000000000000002073671322051203000170000ustar00rootroot00000000000000/** \file nifti1.h \brief Official definition of the nifti1 header. Written by Bob Cox, SSCC, NIMH. HISTORY: 29 Nov 2007 [rickr] - added DT_RGBA32 and NIFTI_TYPE_RGBA32 - added NIFTI_INTENT codes: TIME_SERIES, NODE_INDEX, RGB_VECTOR, RGBA_VECTOR, SHAPE */ #ifndef _NIFTI_HEADER_ #define _NIFTI_HEADER_ /***************************************************************************** ** This file defines the "NIFTI-1" header format. ** ** It is derived from 2 meetings at the NIH (31 Mar 2003 and ** ** 02 Sep 2003) of the Data Format Working Group (DFWG), ** ** chartered by the NIfTI (Neuroimaging Informatics Technology ** ** Initiative) at the National Institutes of Health (NIH). ** **--------------------------------------------------------------** ** Neither the National Institutes of Health (NIH), the DFWG, ** ** nor any of the members or employees of these institutions ** ** imply any warranty of usefulness of this material for any ** ** purpose, and do not assume any liability for damages, ** ** incidental or otherwise, caused by any use of this document. ** ** If these conditions are not acceptable, do not use this! ** **--------------------------------------------------------------** ** Author: Robert W Cox (NIMH, Bethesda) ** ** Advisors: John Ashburner (FIL, London), ** ** Stephen Smith (FMRIB, Oxford), ** ** Mark Jenkinson (FMRIB, Oxford) ** ******************************************************************************/ /*---------------------------------------------------------------------------*/ /* Note that the ANALYZE 7.5 file header (dbh.h) is (c) Copyright 1986-1995 Biomedical Imaging Resource Mayo Foundation Incorporation of components of dbh.h are by permission of the Mayo Foundation. Changes from the ANALYZE 7.5 file header in this file are released to the public domain, including the functional comments and any amusing asides. -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /*! INTRODUCTION TO NIFTI-1: ------------------------ The twin (and somewhat conflicting) goals of this modified ANALYZE 7.5 format are: (a) To add information to the header that will be useful for functional neuroimaging data analysis and display. These additions include: - More basic data types. - Two affine transformations to specify voxel coordinates. - "Intent" codes and parameters to describe the meaning of the data. - Affine scaling of the stored data values to their "true" values. - Optional storage of the header and image data in one file (.nii). (b) To maintain compatibility with non-NIFTI-aware ANALYZE 7.5 compatible software (i.e., such a program should be able to do something useful with a NIFTI-1 dataset -- at least, with one stored in a traditional .img/.hdr file pair). Most of the unused fields in the ANALYZE 7.5 header have been taken, and some of the lesser-used fields have been co-opted for other purposes. Notably, most of the data_history substructure has been co-opted for other purposes, since the ANALYZE 7.5 format describes this substructure as "not required". NIFTI-1 FLAG (MAGIC STRINGS): ---------------------------- To flag such a struct as being conformant to the NIFTI-1 spec, the last 4 bytes of the header must be either the C String "ni1" or "n+1"; in hexadecimal, the 4 bytes 6E 69 31 00 or 6E 2B 31 00 (in any future version of this format, the '1' will be upgraded to '2', etc.). Normally, such a "magic number" or flag goes at the start of the file, but trying to avoid clobbering widely-used ANALYZE 7.5 fields led to putting this marker last. However, recall that "the last shall be first" (Matthew 20:16). If a NIFTI-aware program reads a header file that is NOT marked with a NIFTI magic string, then it should treat the header as an ANALYZE 7.5 structure. NIFTI-1 FILE STORAGE: -------------------- "ni1" means that the image data is stored in the ".img" file corresponding to the header file (starting at file offset 0). "n+1" means that the image data is stored in the same file as the header information. We recommend that the combined header+data filename suffix be ".nii". When the dataset is stored in one file, the first byte of image data is stored at byte location (int)vox_offset in this combined file. The minimum allowed value of vox_offset is 352; for compatibility with some software, vox_offset should be an integral multiple of 16. GRACE UNDER FIRE: ---------------- Most NIFTI-aware programs will only be able to handle a subset of the full range of datasets possible with this format. All NIFTI-aware programs should take care to check if an input dataset conforms to the program's needs and expectations (e.g., check datatype, intent_code, etc.). If the input dataset can't be handled by the program, the program should fail gracefully (e.g., print a useful warning; not crash). SAMPLE CODES: ------------ The associated files nifti1_io.h and nifti1_io.c provide a sample implementation in C of a set of functions to read, write, and manipulate NIFTI-1 files. The file nifti1_test.c is a sample program that uses the nifti1_io.c functions. -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* HEADER STRUCT DECLARATION: ------------------------- In the comments below for each field, only NIFTI-1 specific requirements or changes from the ANALYZE 7.5 format are described. For convenience, the 348 byte header is described as a single struct, rather than as the ANALYZE 7.5 group of 3 substructs. Further comments about the interpretation of various elements of this header are after the data type definition itself. Fields that are marked as ++UNUSED++ have no particular interpretation in this standard. (Also see the UNUSED FIELDS comment section, far below.) The presumption below is that the various C types have particular sizes: sizeof(int) = sizeof(float) = 4 ; sizeof(short) = 2 -----------------------------------------------------------------------------*/ /*=================*/ #ifdef __cplusplus extern "C" { #endif /*=================*/ /*! \struct nifti_1_header \brief Data structure defining the fields in the nifti1 header. This binary header should be found at the beginning of a valid NIFTI-1 header file. */ /*************************/ /************************/ struct nifti_1_header { /* NIFTI-1 usage */ /* ANALYZE 7.5 field(s) */ //typedef struct __attribute__((packed)) /*************************/ /************************/ /*--- was header_key substruct ---*/ int sizeof_hdr; /*!< MUST be 348 */ /* int sizeof_hdr; */ char data_type[10]; /*!< ++UNUSED++ */ /* char data_type[10]; */ char db_name[18]; /*!< ++UNUSED++ */ /* char db_name[18]; */ int extents; /*!< ++UNUSED++ */ /* int extents; */ short session_error; /*!< ++UNUSED++ */ /* short session_error; */ char regular; /*!< ++UNUSED++ */ /* char regular; */ char dim_info; /*!< MRI slice ordering. */ /* char hkey_un0; */ /*--- was image_dimension substruct ---*/ short dim[8]; /*!< Data array dimensions.*/ /* short dim[8]; */ float intent_p1 ; /*!< 1st intent parameter. */ /* short unused8; */ /* short unused9; */ float intent_p2 ; /*!< 2nd intent parameter. */ /* short unused10; */ /* short unused11; */ float intent_p3 ; /*!< 3rd intent parameter. */ /* short unused12; */ /* short unused13; */ short intent_code ; /*!< NIFTI_INTENT_* code. */ /* short unused14; */ short datatype; /*!< Defines data type! */ /* short datatype; */ short bitpix; /*!< Number bits/voxel. */ /* short bitpix; */ short slice_start; /*!< First slice index. */ /* short dim_un0; */ float pixdim[8]; /*!< Grid spacings. */ /* float pixdim[8]; */ float vox_offset; /*!< Offset into .nii file */ /* float vox_offset; */ float scl_slope ; /*!< Data scaling: slope. */ /* float funused1; */ float scl_inter ; /*!< Data scaling: offset. */ /* float funused2; */ short slice_end; /*!< Last slice index. */ /* float funused3; */ char slice_code ; /*!< Slice timing order. */ char xyzt_units ; /*!< Units of pixdim[1..4] */ float cal_max; /*!< Max display intensity */ /* float cal_max; */ float cal_min; /*!< Min display intensity */ /* float cal_min; */ float slice_duration;/*!< Time for 1 slice. */ /* float compressed; */ float toffset; /*!< Time axis shift. */ /* float verified; */ int glmax; /*!< ++UNUSED++ */ /* int glmax; */ int glmin; /*!< ++UNUSED++ */ /* int glmin; */ /*--- was data_history substruct ---*/ char descrip[80]; /*!< any text you like. */ /* char descrip[80]; */ char aux_file[24]; /*!< auxiliary filename. */ /* char aux_file[24]; */ short qform_code ; /*!< NIFTI_XFORM_* code. */ /*-- all ANALYZE 7.5 ---*/ short sform_code ; /*!< NIFTI_XFORM_* code. */ /* fields below here */ /* are replaced */ float quatern_b ; /*!< Quaternion b param. */ float quatern_c ; /*!< Quaternion c param. */ float quatern_d ; /*!< Quaternion d param. */ float qoffset_x ; /*!< Quaternion x shift. */ float qoffset_y ; /*!< Quaternion y shift. */ float qoffset_z ; /*!< Quaternion z shift. */ float srow_x[4] ; /*!< 1st row affine transform. */ float srow_y[4] ; /*!< 2nd row affine transform. */ float srow_z[4] ; /*!< 3rd row affine transform. */ char intent_name[16];/*!< 'name' or meaning of data. */ char magic[4] ; /*!< MUST be "ni1\0" or "n+1\0". */ } ; /**** 348 bytes total ****/ typedef struct nifti_1_header nifti_1_header ; /*---------------------------------------------------------------------------*/ /* HEADER EXTENSIONS: ----------------- After the end of the 348 byte header (e.g., after the magic field), the next 4 bytes are a char array field named "extension". By default, all 4 bytes of this array should be set to zero. In a .nii file, these 4 bytes will always be present, since the earliest start point for the image data is byte #352. In a separate .hdr file, these bytes may or may not be present. If not present (i.e., if the length of the .hdr file is 348 bytes), then a NIfTI-1 compliant program should use the default value of extension={0,0,0,0}. The first byte (extension[0]) is the only value of this array that is specified at present. The other 3 bytes are reserved for future use. If extension[0] is nonzero, it indicates that extended header information is present in the bytes following the extension array. In a .nii file, this extended header data is before the image data (and vox_offset must be set correctly to allow for this). In a .hdr file, this extended data follows extension and proceeds (potentially) to the end of the file. The format of extended header data is weakly specified. Each extension must be an integer multiple of 16 bytes long. The first 8 bytes of each extension comprise 2 integers: int esize , ecode ; These values may need to be byte-swapped, as indicated by dim[0] for the rest of the header. * esize is the number of bytes that form the extended header data + esize must be a positive integral multiple of 16 + this length includes the 8 bytes of esize and ecode themselves * ecode is a non-negative integer that indicates the format of the extended header data that follows + different ecode values are assigned to different developer groups + at present, the "registered" values for code are = 0 = unknown private format (not recommended!) = 2 = DICOM format (i.e., attribute tags and values) = 4 = AFNI group (i.e., ASCII XML-ish elements) In the interests of interoperability (a primary rationale for NIfTI), groups developing software that uses this extension mechanism are encouraged to document and publicize the format of their extensions. To this end, the NIfTI DFWG will assign even numbered codes upon request to groups submitting at least rudimentary documentation for the format of their extension; at present, the contact is mailto:rwcox@nih.gov. The assigned codes and documentation will be posted on the NIfTI website. All odd values of ecode (and 0) will remain unassigned; at least, until the even ones are used up, when we get to 2,147,483,646. Note that the other contents of the extended header data section are totally unspecified by the NIfTI-1 standard. In particular, if binary data is stored in such a section, its byte order is not necessarily the same as that given by examining dim[0]; it is incumbent on the programs dealing with such data to determine the byte order of binary extended header data. Multiple extended header sections are allowed, each starting with an esize,ecode value pair. The first esize value, as described above, is at bytes #352-355 in the .hdr or .nii file (files start at byte #0). If this value is positive, then the second (esize2) will be found starting at byte #352+esize1 , the third (esize3) at byte #352+esize1+esize2, et cetera. Of course, in a .nii file, the value of vox_offset must be compatible with these extensions. If a malformed file indicates that an extended header data section would run past vox_offset, then the entire extended header section should be ignored. In a .hdr file, if an extended header data section would run past the end-of-file, that extended header data should also be ignored. With the above scheme, a program can successively examine the esize and ecode values, and skip over each extended header section if the program doesn't know how to interpret the data within. Of course, any program can simply ignore all extended header sections simply by jumping straight to the image data using vox_offset. -----------------------------------------------------------------------------*/ /*! \struct nifti1_extender \brief This structure represents a 4-byte string that should follow the binary nifti_1_header data in a NIFTI-1 header file. If the char values are {1,0,0,0}, the file is expected to contain extensions, values of {0,0,0,0} imply the file does not contain extensions. Other sequences of values are not currently defined. */ struct nifti1_extender { char extension[4] ; } ; typedef struct nifti1_extender nifti1_extender ; /*! \struct nifti1_extension \brief Data structure defining the fields of a header extension. */ struct nifti1_extension { int esize ; /*!< size of extension, in bytes (must be multiple of 16) */ int ecode ; /*!< extension code, one of the NIFTI_ECODE_ values */ char * edata ; /*!< raw data, with no byte swapping (length is esize-8) */ } ; typedef struct nifti1_extension nifti1_extension ; /*---------------------------------------------------------------------------*/ /* DATA DIMENSIONALITY (as in ANALYZE 7.5): --------------------------------------- dim[0] = number of dimensions; - if dim[0] is outside range 1..7, then the header information needs to be byte swapped appropriately - ANALYZE supports dim[0] up to 7, but NIFTI-1 reserves dimensions 1,2,3 for space (x,y,z), 4 for time (t), and 5,6,7 for anything else needed. dim[i] = length of dimension #i, for i=1..dim[0] (must be positive) - also see the discussion of intent_code, far below pixdim[i] = voxel width along dimension #i, i=1..dim[0] (positive) - cf. ORIENTATION section below for use of pixdim[0] - the units of pixdim can be specified with the xyzt_units field (also described far below). Number of bits per voxel value is in bitpix, which MUST correspond with the datatype field. The total number of bytes in the image data is dim[1] * ... * dim[dim[0]] * bitpix / 8 In NIFTI-1 files, dimensions 1,2,3 are for space, dimension 4 is for time, and dimension 5 is for storing multiple values at each spatiotemporal voxel. Some examples: - A typical whole-brain FMRI experiment's time series: - dim[0] = 4 - dim[1] = 64 pixdim[1] = 3.75 xyzt_units = NIFTI_UNITS_MM - dim[2] = 64 pixdim[2] = 3.75 | NIFTI_UNITS_SEC - dim[3] = 20 pixdim[3] = 5.0 - dim[4] = 120 pixdim[4] = 2.0 - A typical T1-weighted anatomical volume: - dim[0] = 3 - dim[1] = 256 pixdim[1] = 1.0 xyzt_units = NIFTI_UNITS_MM - dim[2] = 256 pixdim[2] = 1.0 - dim[3] = 128 pixdim[3] = 1.1 - A single slice EPI time series: - dim[0] = 4 - dim[1] = 64 pixdim[1] = 3.75 xyzt_units = NIFTI_UNITS_MM - dim[2] = 64 pixdim[2] = 3.75 | NIFTI_UNITS_SEC - dim[3] = 1 pixdim[3] = 5.0 - dim[4] = 1200 pixdim[4] = 0.2 - A 3-vector stored at each point in a 3D volume: - dim[0] = 5 - dim[1] = 256 pixdim[1] = 1.0 xyzt_units = NIFTI_UNITS_MM - dim[2] = 256 pixdim[2] = 1.0 - dim[3] = 128 pixdim[3] = 1.1 - dim[4] = 1 pixdim[4] = 0.0 - dim[5] = 3 intent_code = NIFTI_INTENT_VECTOR - A single time series with a 3x3 matrix at each point: - dim[0] = 5 - dim[1] = 1 xyzt_units = NIFTI_UNITS_SEC - dim[2] = 1 - dim[3] = 1 - dim[4] = 1200 pixdim[4] = 0.2 - dim[5] = 9 intent_code = NIFTI_INTENT_GENMATRIX - intent_p1 = intent_p2 = 3.0 (indicates matrix dimensions) -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* DATA STORAGE: ------------ If the magic field is "n+1", then the voxel data is stored in the same file as the header. In this case, the voxel data starts at offset (int)vox_offset into the header file. Thus, vox_offset=352.0 means that the data starts immediately after the NIFTI-1 header. If vox_offset is greater than 352, the NIFTI-1 format does not say much about the contents of the dataset file between the end of the header and the start of the data. FILES: ----- If the magic field is "ni1", then the voxel data is stored in the associated ".img" file, starting at offset 0 (i.e., vox_offset is not used in this case, and should be set to 0.0). When storing NIFTI-1 datasets in pairs of files, it is customary to name the files in the pattern "name.hdr" and "name.img", as in ANALYZE 7.5. When storing in a single file ("n+1"), the file name should be in the form "name.nii" (the ".nft" and ".nif" suffixes are already taken; cf. http://www.icdatamaster.com/n.html ). BYTE ORDERING: ------------- The byte order of the data arrays is presumed to be the same as the byte order of the header (which is determined by examining dim[0]). Floating point types are presumed to be stored in IEEE-754 format. -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* DETAILS ABOUT vox_offset: ------------------------ In a .nii file, the vox_offset field value is interpreted as the start location of the image data bytes in that file. In a .hdr/.img file pair, the vox_offset field value is the start location of the image data bytes in the .img file. * If vox_offset is less than 352 in a .nii file, it is equivalent to 352 (i.e., image data never starts before byte #352 in a .nii file). * The default value for vox_offset in a .nii file is 352. * In a .hdr file, the default value for vox_offset is 0. * vox_offset should be an integer multiple of 16; otherwise, some programs may not work properly (e.g., SPM). This is to allow memory-mapped input to be properly byte-aligned. Note that since vox_offset is an IEEE-754 32 bit float (for compatibility with the ANALYZE-7.5 format), it effectively has a 24 bit mantissa. All integers from 0 to 2^24 can be represented exactly in this format, but not all larger integers are exactly storable as IEEE-754 32 bit floats. However, unless you plan to have vox_offset be potentially larger than 16 MB, this should not be an issue. (Actually, any integral multiple of 16 up to 2^27 can be represented exactly in this format, which allows for up to 128 MB of random information before the image data. If that isn't enough, then perhaps this format isn't right for you.) In a .img file (i.e., image data stored separately from the NIfTI-1 header), data bytes between #0 and #vox_offset-1 (inclusive) are completely undefined and unregulated by the NIfTI-1 standard. One potential use of having vox_offset > 0 in the .hdr/.img file pair storage method is to make the .img file be a copy of (or link to) a pre-existing image file in some other format, such as DICOM; then vox_offset would be set to the offset of the image data in this file. (It may not be possible to follow the "multiple-of-16 rule" with an arbitrary external file; using the NIfTI-1 format in such a case may lead to a file that is incompatible with software that relies on vox_offset being a multiple of 16.) In a .nii file, data bytes between #348 and #vox_offset-1 (inclusive) may be used to store user-defined extra information; similarly, in a .hdr file, any data bytes after byte #347 are available for user-defined extra information. The (very weak) regulation of this extra header data is described elsewhere. -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* DATA SCALING: ------------ If the scl_slope field is nonzero, then each voxel value in the dataset should be scaled as y = scl_slope * x + scl_inter where x = voxel value stored y = "true" voxel value Normally, we would expect this scaling to be used to store "true" floating values in a smaller integer datatype, but that is not required. That is, it is legal to use scaling even if the datatype is a float type (crazy, perhaps, but legal). - However, the scaling is to be ignored if datatype is DT_RGB24. - If datatype is a complex type, then the scaling is to be applied to both the real and imaginary parts. The cal_min and cal_max fields (if nonzero) are used for mapping (possibly scaled) dataset values to display colors: - Minimum display intensity (black) corresponds to dataset value cal_min. - Maximum display intensity (white) corresponds to dataset value cal_max. - Dataset values below cal_min should display as black also, and values above cal_max as white. - Colors "black" and "white", of course, may refer to any scalar display scheme (e.g., a color lookup table specified via aux_file). - cal_min and cal_max only make sense when applied to scalar-valued datasets (i.e., dim[0] < 5 or dim[5] = 1). -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* TYPE OF DATA (acceptable values for datatype field): --------------------------------------------------- Values of datatype smaller than 256 are ANALYZE 7.5 compatible. Larger values are NIFTI-1 additions. These are all multiples of 256, so that no bits below position 8 are set in datatype. But there is no need to use only powers-of-2, as the original ANALYZE 7.5 datatype codes do. The additional codes are intended to include a complete list of basic scalar types, including signed and unsigned integers from 8 to 64 bits, floats from 32 to 128 bits, and complex (float pairs) from 64 to 256 bits. Note that most programs will support only a few of these datatypes! A NIFTI-1 program should fail gracefully (e.g., print a warning message) when it encounters a dataset with a type it doesn't like. -----------------------------------------------------------------------------*/ //#undef DT_UNKNOWN /* defined in dirent.h on some Unix systems */ //https://www.mail-archive.com/fltk-bugs@easysw.com/msg05850.html /*! \defgroup NIFTI1_DATATYPES \brief nifti1 datatype codes @{ */ /*--- the original ANALYZE 7.5 type codes ---*/ #define DT_NONE 0 #define DT_UNKNOWN_DT 0 //3/3/2014 CR modified to avoid conflict /usr/include/dirent #define DT_BINARY 1 /* binary (1 bit/voxel) */ #define DT_UNSIGNED_CHAR 2 /* unsigned char (8 bits/voxel) */ #define DT_SIGNED_SHORT 4 /* signed short (16 bits/voxel) */ #define DT_SIGNED_INT 8 /* signed int (32 bits/voxel) */ #define DT_FLOAT 16 /* float (32 bits/voxel) */ #define DT_COMPLEX 32 /* complex (64 bits/voxel) */ #define DT_DOUBLE 64 /* double (64 bits/voxel) */ #define DT_RGB 128 /* RGB triple (24 bits/voxel) */ #define DT_ALL 255 /* not very useful (?) */ /*----- another set of names for the same ---*/ #define DT_UINT8 2 #define DT_INT16 4 #define DT_INT32 8 #define DT_FLOAT32 16 #define DT_COMPLEX64 32 #define DT_FLOAT64 64 #define DT_RGB24 128 /*------------------- new codes for NIFTI ---*/ #define DT_INT8 256 /* signed char (8 bits) */ #define DT_UINT16 512 /* unsigned short (16 bits) */ #define DT_UINT32 768 /* unsigned int (32 bits) */ #define DT_INT64 1024 /* long long (64 bits) */ #define DT_UINT64 1280 /* unsigned long long (64 bits) */ #define DT_FLOAT128 1536 /* long double (128 bits) */ #define DT_COMPLEX128 1792 /* double pair (128 bits) */ #define DT_COMPLEX256 2048 /* long double pair (256 bits) */ #define DT_RGBA32 2304 /* 4 byte RGBA (32 bits/voxel) */ /* @} */ /*------- aliases for all the above codes ---*/ /*! \defgroup NIFTI1_DATATYPE_ALIASES \brief aliases for the nifti1 datatype codes @{ */ /*! unsigned char. */ #define NIFTI_TYPE_UINT8 2 /*! signed short. */ #define NIFTI_TYPE_INT16 4 /*! signed int. */ #define NIFTI_TYPE_INT32 8 /*! 32 bit float. */ #define NIFTI_TYPE_FLOAT32 16 /*! 64 bit complex = 2 32 bit floats. */ #define NIFTI_TYPE_COMPLEX64 32 /*! 64 bit float = double. */ #define NIFTI_TYPE_FLOAT64 64 /*! 3 8 bit bytes. */ #define NIFTI_TYPE_RGB24 128 /*! signed char. */ #define NIFTI_TYPE_INT8 256 /*! unsigned short. */ #define NIFTI_TYPE_UINT16 512 /*! unsigned int. */ #define NIFTI_TYPE_UINT32 768 /*! signed long long. */ #define NIFTI_TYPE_INT64 1024 /*! unsigned long long. */ #define NIFTI_TYPE_UINT64 1280 /*! 128 bit float = long double. */ #define NIFTI_TYPE_FLOAT128 1536 /*! 128 bit complex = 2 64 bit floats. */ #define NIFTI_TYPE_COMPLEX128 1792 /*! 256 bit complex = 2 128 bit floats */ #define NIFTI_TYPE_COMPLEX256 2048 /*! 4 8 bit bytes. */ #define NIFTI_TYPE_RGBA32 2304 /* @} */ /*-------- sample typedefs for complicated types ---*/ #if 0 typedef struct { float r,i; } complex_float ; typedef struct { double r,i; } complex_double ; typedef struct { long double r,i; } complex_longdouble ; typedef struct { unsigned char r,g,b; } rgb_byte ; #endif /*---------------------------------------------------------------------------*/ /* INTERPRETATION OF VOXEL DATA: ---------------------------- The intent_code field can be used to indicate that the voxel data has some particular meaning. In particular, a large number of codes is given to indicate that the the voxel data should be interpreted as being drawn from a given probability distribution. VECTOR-VALUED DATASETS: ---------------------- The 5th dimension of the dataset, if present (i.e., dim[0]=5 and dim[5] > 1), contains multiple values (e.g., a vector) to be stored at each spatiotemporal location. For example, the header values - dim[0] = 5 - dim[1] = 64 - dim[2] = 64 - dim[3] = 20 - dim[4] = 1 (indicates no time axis) - dim[5] = 3 - datatype = DT_FLOAT - intent_code = NIFTI_INTENT_VECTOR mean that this dataset should be interpreted as a 3D volume (64x64x20), with a 3-vector of floats defined at each point in the 3D grid. A program reading a dataset with a 5th dimension may want to reformat the image data to store each voxels' set of values together in a struct or array. This programming detail, however, is beyond the scope of the NIFTI-1 file specification! Uses of dimensions 6 and 7 are also not specified here. STATISTICAL PARAMETRIC DATASETS (i.e., SPMs): -------------------------------------------- Values of intent_code from NIFTI_FIRST_STATCODE to NIFTI_LAST_STATCODE (inclusive) indicate that the numbers in the dataset should be interpreted as being drawn from a given distribution. Most such distributions have auxiliary parameters (e.g., NIFTI_INTENT_TTEST has 1 DOF parameter). If the dataset DOES NOT have a 5th dimension, then the auxiliary parameters are the same for each voxel, and are given in header fields intent_p1, intent_p2, and intent_p3. If the dataset DOES have a 5th dimension, then the auxiliary parameters are different for each voxel. For example, the header values - dim[0] = 5 - dim[1] = 128 - dim[2] = 128 - dim[3] = 1 (indicates a single slice) - dim[4] = 1 (indicates no time axis) - dim[5] = 2 - datatype = DT_FLOAT - intent_code = NIFTI_INTENT_TTEST mean that this is a 2D dataset (128x128) of t-statistics, with the t-statistic being in the first "plane" of data and the degrees-of-freedom parameter being in the second "plane" of data. If the dataset 5th dimension is used to store the voxel-wise statistical parameters, then dim[5] must be 1 plus the number of parameters required by that distribution (e.g., intent_code=NIFTI_INTENT_TTEST implies dim[5] must be 2, as in the example just above). Note: intent_code values 2..10 are compatible with AFNI 1.5x (which is why there is no code with value=1, which is obsolescent in AFNI). OTHER INTENTIONS: ---------------- The purpose of the intent_* fields is to help interpret the values stored in the dataset. Some non-statistical values for intent_code and conventions are provided for storing other complex data types. The intent_name field provides space for a 15 character (plus 0 byte) 'name' string for the type of data stored. Examples: - intent_code = NIFTI_INTENT_ESTIMATE; intent_name = "T1"; could be used to signify that the voxel values are estimates of the NMR parameter T1. - intent_code = NIFTI_INTENT_TTEST; intent_name = "House"; could be used to signify that the voxel values are t-statistics for the significance of 'activation' response to a House stimulus. - intent_code = NIFTI_INTENT_DISPVECT; intent_name = "ToMNI152"; could be used to signify that the voxel values are a displacement vector that transforms each voxel (x,y,z) location to the corresponding location in the MNI152 standard brain. - intent_code = NIFTI_INTENT_SYMMATRIX; intent_name = "DTI"; could be used to signify that the voxel values comprise a diffusion tensor image. If no data name is implied or needed, intent_name[0] should be set to 0. -----------------------------------------------------------------------------*/ /*! default: no intention is indicated in the header. */ #define NIFTI_INTENT_NONE 0 /*-------- These codes are for probability distributions ---------------*/ /* Most distributions have a number of parameters, below denoted by p1, p2, and p3, and stored in - intent_p1, intent_p2, intent_p3 if dataset doesn't have 5th dimension - image data array if dataset does have 5th dimension Functions to compute with many of the distributions below can be found in the CDF library from U Texas. Formulas for and discussions of these distributions can be found in the following books: [U] Univariate Discrete Distributions, NL Johnson, S Kotz, AW Kemp. [C1] Continuous Univariate Distributions, vol. 1, NL Johnson, S Kotz, N Balakrishnan. [C2] Continuous Univariate Distributions, vol. 2, NL Johnson, S Kotz, N Balakrishnan. */ /*----------------------------------------------------------------------*/ /*! [C2, chap 32] Correlation coefficient R (1 param): p1 = degrees of freedom R/sqrt(1-R*R) is t-distributed with p1 DOF. */ /*! \defgroup NIFTI1_INTENT_CODES \brief nifti1 intent codes, to describe intended meaning of dataset contents @{ */ #define NIFTI_INTENT_CORREL 2 /*! [C2, chap 28] Student t statistic (1 param): p1 = DOF. */ #define NIFTI_INTENT_TTEST 3 /*! [C2, chap 27] Fisher F statistic (2 params): p1 = numerator DOF, p2 = denominator DOF. */ #define NIFTI_INTENT_FTEST 4 /*! [C1, chap 13] Standard normal (0 params): Density = N(0,1). */ #define NIFTI_INTENT_ZSCORE 5 /*! [C1, chap 18] Chi-squared (1 param): p1 = DOF. Density(x) proportional to exp(-x/2) * x^(p1/2-1). */ #define NIFTI_INTENT_CHISQ 6 /*! [C2, chap 25] Beta distribution (2 params): p1=a, p2=b. Density(x) proportional to x^(a-1) * (1-x)^(b-1). */ #define NIFTI_INTENT_BETA 7 /*! [U, chap 3] Binomial distribution (2 params): p1 = number of trials, p2 = probability per trial. Prob(x) = (p1 choose x) * p2^x * (1-p2)^(p1-x), for x=0,1,...,p1. */ #define NIFTI_INTENT_BINOM 8 /*! [C1, chap 17] Gamma distribution (2 params): p1 = shape, p2 = scale. Density(x) proportional to x^(p1-1) * exp(-p2*x). */ #define NIFTI_INTENT_GAMMA 9 /*! [U, chap 4] Poisson distribution (1 param): p1 = mean. Prob(x) = exp(-p1) * p1^x / x! , for x=0,1,2,.... */ #define NIFTI_INTENT_POISSON 10 /*! [C1, chap 13] Normal distribution (2 params): p1 = mean, p2 = standard deviation. */ #define NIFTI_INTENT_NORMAL 11 /*! [C2, chap 30] Noncentral F statistic (3 params): p1 = numerator DOF, p2 = denominator DOF, p3 = numerator noncentrality parameter. */ #define NIFTI_INTENT_FTEST_NONC 12 /*! [C2, chap 29] Noncentral chi-squared statistic (2 params): p1 = DOF, p2 = noncentrality parameter. */ #define NIFTI_INTENT_CHISQ_NONC 13 /*! [C2, chap 23] Logistic distribution (2 params): p1 = location, p2 = scale. Density(x) proportional to sech^2((x-p1)/(2*p2)). */ #define NIFTI_INTENT_LOGISTIC 14 /*! [C2, chap 24] Laplace distribution (2 params): p1 = location, p2 = scale. Density(x) proportional to exp(-abs(x-p1)/p2). */ #define NIFTI_INTENT_LAPLACE 15 /*! [C2, chap 26] Uniform distribution: p1 = lower end, p2 = upper end. */ #define NIFTI_INTENT_UNIFORM 16 /*! [C2, chap 31] Noncentral t statistic (2 params): p1 = DOF, p2 = noncentrality parameter. */ #define NIFTI_INTENT_TTEST_NONC 17 /*! [C1, chap 21] Weibull distribution (3 params): p1 = location, p2 = scale, p3 = power. Density(x) proportional to ((x-p1)/p2)^(p3-1) * exp(-((x-p1)/p2)^p3) for x > p1. */ #define NIFTI_INTENT_WEIBULL 18 /*! [C1, chap 18] Chi distribution (1 param): p1 = DOF. Density(x) proportional to x^(p1-1) * exp(-x^2/2) for x > 0. p1 = 1 = 'half normal' distribution p1 = 2 = Rayleigh distribution p1 = 3 = Maxwell-Boltzmann distribution. */ #define NIFTI_INTENT_CHI 19 /*! [C1, chap 15] Inverse Gaussian (2 params): p1 = mu, p2 = lambda Density(x) proportional to exp(-p2*(x-p1)^2/(2*p1^2*x)) / x^3 for x > 0. */ #define NIFTI_INTENT_INVGAUSS 20 /*! [C2, chap 22] Extreme value type I (2 params): p1 = location, p2 = scale cdf(x) = exp(-exp(-(x-p1)/p2)). */ #define NIFTI_INTENT_EXTVAL 21 /*! Data is a 'p-value' (no params). */ #define NIFTI_INTENT_PVAL 22 /*! Data is ln(p-value) (no params). To be safe, a program should compute p = exp(-abs(this_value)). The nifti_stats.c library returns this_value as positive, so that this_value = -log(p). */ #define NIFTI_INTENT_LOGPVAL 23 /*! Data is log10(p-value) (no params). To be safe, a program should compute p = pow(10.,-abs(this_value)). The nifti_stats.c library returns this_value as positive, so that this_value = -log10(p). */ #define NIFTI_INTENT_LOG10PVAL 24 /*! Smallest intent_code that indicates a statistic. */ #define NIFTI_FIRST_STATCODE 2 /*! Largest intent_code that indicates a statistic. */ #define NIFTI_LAST_STATCODE 24 /*---------- these values for intent_code aren't for statistics ----------*/ /*! To signify that the value at each voxel is an estimate of some parameter, set intent_code = NIFTI_INTENT_ESTIMATE. The name of the parameter may be stored in intent_name. */ #define NIFTI_INTENT_ESTIMATE 1001 /*! To signify that the value at each voxel is an index into some set of labels, set intent_code = NIFTI_INTENT_LABEL. The filename with the labels may stored in aux_file. */ #define NIFTI_INTENT_LABEL 1002 /*! To signify that the value at each voxel is an index into the NeuroNames labels set, set intent_code = NIFTI_INTENT_NEURONAME. */ #define NIFTI_INTENT_NEURONAME 1003 /*! To store an M x N matrix at each voxel: - dataset must have a 5th dimension (dim[0]=5 and dim[5]>1) - intent_code must be NIFTI_INTENT_GENMATRIX - dim[5] must be M*N - intent_p1 must be M (in float format) - intent_p2 must be N (ditto) - the matrix values A[i][[j] are stored in row-order: - A[0][0] A[0][1] ... A[0][N-1] - A[1][0] A[1][1] ... A[1][N-1] - etc., until - A[M-1][0] A[M-1][1] ... A[M-1][N-1] */ #define NIFTI_INTENT_GENMATRIX 1004 /*! To store an NxN symmetric matrix at each voxel: - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_SYMMATRIX - dim[5] must be N*(N+1)/2 - intent_p1 must be N (in float format) - the matrix values A[i][[j] are stored in row-order: - A[0][0] - A[1][0] A[1][1] - A[2][0] A[2][1] A[2][2] - etc.: row-by-row */ #define NIFTI_INTENT_SYMMATRIX 1005 /*! To signify that the vector value at each voxel is to be taken as a displacement field or vector: - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_DISPVECT - dim[5] must be the dimensionality of the displacment vector (e.g., 3 for spatial displacement, 2 for in-plane) */ #define NIFTI_INTENT_DISPVECT 1006 /* specifically for displacements */ #define NIFTI_INTENT_VECTOR 1007 /* for any other type of vector */ /*! To signify that the vector value at each voxel is really a spatial coordinate (e.g., the vertices or nodes of a surface mesh): - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_POINTSET - dim[0] = 5 - dim[1] = number of points - dim[2] = dim[3] = dim[4] = 1 - dim[5] must be the dimensionality of space (e.g., 3 => 3D space). - intent_name may describe the object these points come from (e.g., "pial", "gray/white" , "EEG", "MEG"). */ #define NIFTI_INTENT_POINTSET 1008 /*! To signify that the vector value at each voxel is really a triple of indexes (e.g., forming a triangle) from a pointset dataset: - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_TRIANGLE - dim[0] = 5 - dim[1] = number of triangles - dim[2] = dim[3] = dim[4] = 1 - dim[5] = 3 - datatype should be an integer type (preferably DT_INT32) - the data values are indexes (0,1,...) into a pointset dataset. */ #define NIFTI_INTENT_TRIANGLE 1009 /*! To signify that the vector value at each voxel is a quaternion: - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_QUATERNION - dim[0] = 5 - dim[5] = 4 - datatype should be a floating point type */ #define NIFTI_INTENT_QUATERNION 1010 /*! Dimensionless value - no params - although, as in _ESTIMATE the name of the parameter may be stored in intent_name. */ #define NIFTI_INTENT_DIMLESS 1011 /*---------- these values apply to GIFTI datasets ----------*/ /*! To signify that the value at each location is from a time series. */ #define NIFTI_INTENT_TIME_SERIES 2001 /*! To signify that the value at each location is a node index, from a complete surface dataset. */ #define NIFTI_INTENT_NODE_INDEX 2002 /*! To signify that the vector value at each location is an RGB triplet, of whatever type. - dataset must have a 5th dimension - dim[0] = 5 - dim[1] = number of nodes - dim[2] = dim[3] = dim[4] = 1 - dim[5] = 3 */ #define NIFTI_INTENT_RGB_VECTOR 2003 /*! To signify that the vector value at each location is a 4 valued RGBA vector, of whatever type. - dataset must have a 5th dimension - dim[0] = 5 - dim[1] = number of nodes - dim[2] = dim[3] = dim[4] = 1 - dim[5] = 4 */ #define NIFTI_INTENT_RGBA_VECTOR 2004 /*! To signify that the value at each location is a shape value, such as the curvature. */ #define NIFTI_INTENT_SHAPE 2005 /* @} */ /*---------------------------------------------------------------------------*/ /* 3D IMAGE (VOLUME) ORIENTATION AND LOCATION IN SPACE: --------------------------------------------------- There are 3 different methods by which continuous coordinates can attached to voxels. The discussion below emphasizes 3D volumes, and the continuous coordinates are referred to as (x,y,z). The voxel index coordinates (i.e., the array indexes) are referred to as (i,j,k), with valid ranges: i = 0 .. dim[1]-1 j = 0 .. dim[2]-1 (if dim[0] >= 2) k = 0 .. dim[3]-1 (if dim[0] >= 3) The (x,y,z) coordinates refer to the CENTER of a voxel. In methods 2 and 3, the (x,y,z) axes refer to a subject-based coordinate system, with +x = Right +y = Anterior +z = Superior. This is a right-handed coordinate system. However, the exact direction these axes point with respect to the subject depends on qform_code (Method 2) and sform_code (Method 3). N.B.: The i index varies most rapidly, j index next, k index slowest. Thus, voxel (i,j,k) is stored starting at location (i + j*dim[1] + k*dim[1]*dim[2]) * (bitpix/8) into the dataset array. N.B.: The ANALYZE 7.5 coordinate system is +x = Left +y = Anterior +z = Superior which is a left-handed coordinate system. This backwardness is too difficult to tolerate, so this NIFTI-1 standard specifies the coordinate order which is most common in functional neuroimaging. N.B.: The 3 methods below all give the locations of the voxel centers in the (x,y,z) coordinate system. In many cases, programs will wish to display image data on some other grid. In such a case, the program will need to convert its desired (x,y,z) values into (i,j,k) values in order to extract (or interpolate) the image data. This operation would be done with the inverse transformation to those described below. N.B.: Method 2 uses a factor 'qfac' which is either -1 or 1; qfac is stored in the otherwise unused pixdim[0]. If pixdim[0]=0.0 (which should not occur), we take qfac=1. Of course, pixdim[0] is only used when reading a NIFTI-1 header, not when reading an ANALYZE 7.5 header. N.B.: The units of (x,y,z) can be specified using the xyzt_units field. METHOD 1 (the "old" way, used only when qform_code = 0): ------------------------------------------------------- The coordinate mapping from (i,j,k) to (x,y,z) is the ANALYZE 7.5 way. This is a simple scaling relationship: x = pixdim[1] * i y = pixdim[2] * j z = pixdim[3] * k No particular spatial orientation is attached to these (x,y,z) coordinates. (NIFTI-1 does not have the ANALYZE 7.5 orient field, which is not general and is often not set properly.) This method is not recommended, and is present mainly for compatibility with ANALYZE 7.5 files. METHOD 2 (used when qform_code > 0, which should be the "normal" case): --------------------------------------------------------------------- The (x,y,z) coordinates are given by the pixdim[] scales, a rotation matrix, and a shift. This method is intended to represent "scanner-anatomical" coordinates, which are often embedded in the image header (e.g., DICOM fields (0020,0032), (0020,0037), (0028,0030), and (0018,0050)), and represent the nominal orientation and location of the data. This method can also be used to represent "aligned" coordinates, which would typically result from some post-acquisition alignment of the volume to a standard orientation (e.g., the same subject on another day, or a rigid rotation to true anatomical orientation from the tilted position of the subject in the scanner). The formula for (x,y,z) in terms of header parameters and (i,j,k) is: [ x ] [ R11 R12 R13 ] [ pixdim[1] * i ] [ qoffset_x ] [ y ] = [ R21 R22 R23 ] [ pixdim[2] * j ] + [ qoffset_y ] [ z ] [ R31 R32 R33 ] [ qfac * pixdim[3] * k ] [ qoffset_z ] The qoffset_* shifts are in the NIFTI-1 header. Note that the center of the (i,j,k)=(0,0,0) voxel (first value in the dataset array) is just (x,y,z)=(qoffset_x,qoffset_y,qoffset_z). The rotation matrix R is calculated from the quatern_* parameters. This calculation is described below. The scaling factor qfac is either 1 or -1. The rotation matrix R defined by the quaternion parameters is "proper" (has determinant 1). This may not fit the needs of the data; for example, if the image grid is i increases from Left-to-Right j increases from Anterior-to-Posterior k increases from Inferior-to-Superior Then (i,j,k) is a left-handed triple. In this example, if qfac=1, the R matrix would have to be [ 1 0 0 ] [ 0 -1 0 ] which is "improper" (determinant = -1). [ 0 0 1 ] If we set qfac=-1, then the R matrix would be [ 1 0 0 ] [ 0 -1 0 ] which is proper. [ 0 0 -1 ] This R matrix is represented by quaternion [a,b,c,d] = [0,1,0,0] (which encodes a 180 degree rotation about the x-axis). METHOD 3 (used when sform_code > 0): ----------------------------------- The (x,y,z) coordinates are given by a general affine transformation of the (i,j,k) indexes: x = srow_x[0] * i + srow_x[1] * j + srow_x[2] * k + srow_x[3] y = srow_y[0] * i + srow_y[1] * j + srow_y[2] * k + srow_y[3] z = srow_z[0] * i + srow_z[1] * j + srow_z[2] * k + srow_z[3] The srow_* vectors are in the NIFTI_1 header. Note that no use is made of pixdim[] in this method. WHY 3 METHODS? -------------- Method 1 is provided only for backwards compatibility. The intention is that Method 2 (qform_code > 0) represents the nominal voxel locations as reported by the scanner, or as rotated to some fiducial orientation and location. Method 3, if present (sform_code > 0), is to be used to give the location of the voxels in some standard space. The sform_code indicates which standard space is present. Both methods 2 and 3 can be present, and be useful in different contexts (method 2 for displaying the data on its original grid; method 3 for displaying it on a standard grid). In this scheme, a dataset would originally be set up so that the Method 2 coordinates represent what the scanner reported. Later, a registration to some standard space can be computed and inserted in the header. Image display software can use either transform, depending on its purposes and needs. In Method 2, the origin of coordinates would generally be whatever the scanner origin is; for example, in MRI, (0,0,0) is the center of the gradient coil. In Method 3, the origin of coordinates would depend on the value of sform_code; for example, for the Talairach coordinate system, (0,0,0) corresponds to the Anterior Commissure. QUATERNION REPRESENTATION OF ROTATION MATRIX (METHOD 2) ------------------------------------------------------- The orientation of the (x,y,z) axes relative to the (i,j,k) axes in 3D space is specified using a unit quaternion [a,b,c,d], where a*a+b*b+c*c+d*d=1. The (b,c,d) values are all that is needed, since we require that a = sqrt(1.0-(b*b+c*c+d*d)) be nonnegative. The (b,c,d) values are stored in the (quatern_b,quatern_c,quatern_d) fields. The quaternion representation is chosen for its compactness in representing rotations. The (proper) 3x3 rotation matrix that corresponds to [a,b,c,d] is [ a*a+b*b-c*c-d*d 2*b*c-2*a*d 2*b*d+2*a*c ] R = [ 2*b*c+2*a*d a*a+c*c-b*b-d*d 2*c*d-2*a*b ] [ 2*b*d-2*a*c 2*c*d+2*a*b a*a+d*d-c*c-b*b ] [ R11 R12 R13 ] = [ R21 R22 R23 ] [ R31 R32 R33 ] If (p,q,r) is a unit 3-vector, then rotation of angle h about that direction is represented by the quaternion [a,b,c,d] = [cos(h/2), p*sin(h/2), q*sin(h/2), r*sin(h/2)]. Requiring a >= 0 is equivalent to requiring -Pi <= h <= Pi. (Note that [-a,-b,-c,-d] represents the same rotation as [a,b,c,d]; there are 2 quaternions that can be used to represent a given rotation matrix R.) To rotate a 3-vector (x,y,z) using quaternions, we compute the quaternion product [0,x',y',z'] = [a,b,c,d] * [0,x,y,z] * [a,-b,-c,-d] which is equivalent to the matrix-vector multiply [ x' ] [ x ] [ y' ] = R [ y ] (equivalence depends on a*a+b*b+c*c+d*d=1) [ z' ] [ z ] Multiplication of 2 quaternions is defined by the following: [a,b,c,d] = a*1 + b*I + c*J + d*K where I*I = J*J = K*K = -1 (I,J,K are square roots of -1) I*J = K J*K = I K*I = J J*I = -K K*J = -I I*K = -J (not commutative!) For example [a,b,0,0] * [0,0,0,1] = [0,0,-b,a] since this expands to (a+b*I)*(K) = (a*K+b*I*K) = (a*K-b*J). The above formula shows how to go from quaternion (b,c,d) to rotation matrix and direction cosines. Conversely, given R, we can compute the fields for the NIFTI-1 header by a = 0.5 * sqrt(1+R11+R22+R33) (not stored) b = 0.25 * (R32-R23) / a => quatern_b c = 0.25 * (R13-R31) / a => quatern_c d = 0.25 * (R21-R12) / a => quatern_d If a=0 (a 180 degree rotation), alternative formulas are needed. See the nifti1_io.c function mat44_to_quatern() for an implementation of the various cases in converting R to [a,b,c,d]. Note that R-transpose (= R-inverse) would lead to the quaternion [a,-b,-c,-d]. The choice to specify the qoffset_x (etc.) values in the final coordinate system is partly to make it easy to convert DICOM images to this format. The DICOM attribute "Image Position (Patient)" (0020,0032) stores the (Xd,Yd,Zd) coordinates of the center of the first voxel. Here, (Xd,Yd,Zd) refer to DICOM coordinates, and Xd=-x, Yd=-y, Zd=z, where (x,y,z) refers to the NIFTI coordinate system discussed above. (i.e., DICOM +Xd is Left, +Yd is Posterior, +Zd is Superior, whereas +x is Right, +y is Anterior , +z is Superior. ) Thus, if the (0020,0032) DICOM attribute is extracted into (px,py,pz), then qoffset_x = -px qoffset_y = -py qoffset_z = pz is a reasonable setting when qform_code=NIFTI_XFORM_SCANNER_ANAT. That is, DICOM's coordinate system is 180 degrees rotated about the z-axis from the neuroscience/NIFTI coordinate system. To transform between DICOM and NIFTI, you just have to negate the x- and y-coordinates. The DICOM attribute (0020,0037) "Image Orientation (Patient)" gives the orientation of the x- and y-axes of the image data in terms of 2 3-vectors. The first vector is a unit vector along the x-axis, and the second is along the y-axis. If the (0020,0037) attribute is extracted into the value (xa,xb,xc,ya,yb,yc), then the first two columns of the R matrix would be [ -xa -ya ] [ -xb -yb ] [ xc yc ] The negations are because DICOM's x- and y-axes are reversed relative to NIFTI's. The third column of the R matrix gives the direction of displacement (relative to the subject) along the slice-wise direction. This orientation is not encoded in the DICOM standard in a simple way; DICOM is mostly concerned with 2D images. The third column of R will be either the cross-product of the first 2 columns or its negative. It is possible to infer the sign of the 3rd column by examining the coordinates in DICOM attribute (0020,0032) "Image Position (Patient)" for successive slices. However, this method occasionally fails for reasons that I (RW Cox) do not understand. -----------------------------------------------------------------------------*/ /* [qs]form_code value: */ /* x,y,z coordinate system refers to: */ /*-----------------------*/ /*---------------------------------------*/ /*! \defgroup NIFTI1_XFORM_CODES \brief nifti1 xform codes to describe the "standard" coordinate system @{ */ /*! Arbitrary coordinates (Method 1). */ #define NIFTI_XFORM_UNKNOWN 0 /*! Scanner-based anatomical coordinates */ #define NIFTI_XFORM_SCANNER_ANAT 1 /*! Coordinates aligned to another file's, or to anatomical "truth". */ #define NIFTI_XFORM_ALIGNED_ANAT 2 /*! Coordinates aligned to Talairach- Tournoux Atlas; (0,0,0)=AC, etc. */ #define NIFTI_XFORM_TALAIRACH 3 /*! MNI 152 normalized coordinates. */ #define NIFTI_XFORM_MNI_152 4 /* @} */ /*---------------------------------------------------------------------------*/ /* UNITS OF SPATIAL AND TEMPORAL DIMENSIONS: ---------------------------------------- The codes below can be used in xyzt_units to indicate the units of pixdim. As noted earlier, dimensions 1,2,3 are for x,y,z; dimension 4 is for time (t). - If dim[4]=1 or dim[0] < 4, there is no time axis. - A single time series (no space) would be specified with - dim[0] = 4 (for scalar data) or dim[0] = 5 (for vector data) - dim[1] = dim[2] = dim[3] = 1 - dim[4] = number of time points - pixdim[4] = time step - xyzt_units indicates units of pixdim[4] - dim[5] = number of values stored at each time point Bits 0..2 of xyzt_units specify the units of pixdim[1..3] (e.g., spatial units are values 1..7). Bits 3..5 of xyzt_units specify the units of pixdim[4] (e.g., temporal units are multiples of 8). This compression of 2 distinct concepts into 1 byte is due to the limited space available in the 348 byte ANALYZE 7.5 header. The macros XYZT_TO_SPACE and XYZT_TO_TIME can be used to mask off the undesired bits from the xyzt_units fields, leaving "pure" space and time codes. Inversely, the macro SPACE_TIME_TO_XYZT can be used to assemble a space code (0,1,2,...,7) with a time code (0,8,16,32,...,56) into the combined value for xyzt_units. Note that codes are provided to indicate the "time" axis units are actually frequency in Hertz (_HZ), in part-per-million (_PPM) or in radians-per-second (_RADS). The toffset field can be used to indicate a nonzero start point for the time axis. That is, time point #m is at t=toffset+m*pixdim[4] for m=0..dim[4]-1. -----------------------------------------------------------------------------*/ /*! \defgroup NIFTI1_UNITS \brief nifti1 units codes to describe the unit of measurement for each dimension of the dataset @{ */ /*! NIFTI code for unspecified units. */ #define NIFTI_UNITS_UNKNOWN 0 /** Space codes are multiples of 1. **/ /*! NIFTI code for meters. */ #define NIFTI_UNITS_METER 1 /*! NIFTI code for millimeters. */ #define NIFTI_UNITS_MM 2 /*! NIFTI code for micrometers. */ #define NIFTI_UNITS_MICRON 3 /** Time codes are multiples of 8. **/ /*! NIFTI code for seconds. */ #define NIFTI_UNITS_SEC 8 /*! NIFTI code for milliseconds. */ #define NIFTI_UNITS_MSEC 16 /*! NIFTI code for microseconds. */ #define NIFTI_UNITS_USEC 24 /*** These units are for spectral data: ***/ /*! NIFTI code for Hertz. */ #define NIFTI_UNITS_HZ 32 /*! NIFTI code for ppm. */ #define NIFTI_UNITS_PPM 40 /*! NIFTI code for radians per second. */ #define NIFTI_UNITS_RADS 48 /* @} */ #undef XYZT_TO_SPACE #undef XYZT_TO_TIME #define XYZT_TO_SPACE(xyzt) ( (xyzt) & 0x07 ) #define XYZT_TO_TIME(xyzt) ( (xyzt) & 0x38 ) #undef SPACE_TIME_TO_XYZT #define SPACE_TIME_TO_XYZT(ss,tt) ( (((char)(ss)) & 0x07) \ | (((char)(tt)) & 0x38) ) /*---------------------------------------------------------------------------*/ /* MRI-SPECIFIC SPATIAL AND TEMPORAL INFORMATION: --------------------------------------------- A few fields are provided to store some extra information that is sometimes important when storing the image data from an FMRI time series experiment. (After processing such data into statistical images, these fields are not likely to be useful.) { freq_dim } = These fields encode which spatial dimension (1,2, or 3) { phase_dim } = corresponds to which acquisition dimension for MRI data. { slice_dim } = Examples: Rectangular scan multi-slice EPI: freq_dim = 1 phase_dim = 2 slice_dim = 3 (or some permutation) Spiral scan multi-slice EPI: freq_dim = phase_dim = 0 slice_dim = 3 since the concepts of frequency- and phase-encoding directions don't apply to spiral scan slice_duration = If this is positive, AND if slice_dim is nonzero, indicates the amount of time used to acquire 1 slice. slice_duration*dim[slice_dim] can be less than pixdim[4] with a clustered acquisition method, for example. slice_code = If this is nonzero, AND if slice_dim is nonzero, AND if slice_duration is positive, indicates the timing pattern of the slice acquisition. The following codes are defined: NIFTI_SLICE_SEQ_INC == sequential increasing NIFTI_SLICE_SEQ_DEC == sequential decreasing NIFTI_SLICE_ALT_INC == alternating increasing NIFTI_SLICE_ALT_DEC == alternating decreasing NIFTI_SLICE_ALT_INC2 == alternating increasing #2 NIFTI_SLICE_ALT_DEC2 == alternating decreasing #2 { slice_start } = Indicates the start and end of the slice acquisition { slice_end } = pattern, when slice_code is nonzero. These values are present to allow for the possible addition of "padded" slices at either end of the volume, which don't fit into the slice timing pattern. If there are no padding slices, then slice_start=0 and slice_end=dim[slice_dim]-1 are the correct values. For these values to be meaningful, slice_start must be non-negative and slice_end must be greater than slice_start. Otherwise, they should be ignored. The following table indicates the slice timing pattern, relative to time=0 for the first slice acquired, for some sample cases. Here, dim[slice_dim]=7 (there are 7 slices, labeled 0..6), slice_duration=0.1, and slice_start=1, slice_end=5 (1 padded slice on each end). slice index SEQ_INC SEQ_DEC ALT_INC ALT_DEC ALT_INC2 ALT_DEC2 6 : n/a n/a n/a n/a n/a n/a n/a = not applicable 5 : 0.4 0.0 0.2 0.0 0.4 0.2 (slice time offset 4 : 0.3 0.1 0.4 0.3 0.1 0.0 doesn't apply to 3 : 0.2 0.2 0.1 0.1 0.3 0.3 slices outside 2 : 0.1 0.3 0.3 0.4 0.0 0.1 the range 1 : 0.0 0.4 0.0 0.2 0.2 0.4 slice_start .. 0 : n/a n/a n/a n/a n/a n/a slice_end) The SEQ slice_codes are sequential ordering (uncommon but not unknown), either increasing in slice number or decreasing (INC or DEC), as illustrated above. The ALT slice codes are alternating ordering. The 'standard' way for these to operate (without the '2' on the end) is for the slice timing to start at the edge of the slice_start .. slice_end group (at slice_start for INC and at slice_end for DEC). For the 'ALT_*2' slice_codes, the slice timing instead starts at the first slice in from the edge (at slice_start+1 for INC2 and at slice_end-1 for DEC2). This latter acquisition scheme is found on some Siemens scanners. The fields freq_dim, phase_dim, slice_dim are all squished into the single byte field dim_info (2 bits each, since the values for each field are limited to the range 0..3). This unpleasantness is due to lack of space in the 348 byte allowance. The macros DIM_INFO_TO_FREQ_DIM, DIM_INFO_TO_PHASE_DIM, and DIM_INFO_TO_SLICE_DIM can be used to extract these values from the dim_info byte. The macro FPS_INTO_DIM_INFO can be used to put these 3 values into the dim_info byte. -----------------------------------------------------------------------------*/ #undef DIM_INFO_TO_FREQ_DIM #undef DIM_INFO_TO_PHASE_DIM #undef DIM_INFO_TO_SLICE_DIM #define DIM_INFO_TO_FREQ_DIM(di) ( ((di) ) & 0x03 ) #define DIM_INFO_TO_PHASE_DIM(di) ( ((di) >> 2) & 0x03 ) #define DIM_INFO_TO_SLICE_DIM(di) ( ((di) >> 4) & 0x03 ) #undef FPS_INTO_DIM_INFO #define FPS_INTO_DIM_INFO(fd,pd,sd) ( ( ( ((char)(fd)) & 0x03) ) | \ ( ( ((char)(pd)) & 0x03) << 2 ) | \ ( ( ((char)(sd)) & 0x03) << 4 ) ) /*! \defgroup NIFTI1_SLICE_ORDER \brief nifti1 slice order codes, describing the acquisition order of the slices @{ */ #define NIFTI_SLICE_UNKNOWN 0 #define NIFTI_SLICE_SEQ_INC 1 #define NIFTI_SLICE_SEQ_DEC 2 #define NIFTI_SLICE_ALT_INC 3 #define NIFTI_SLICE_ALT_DEC 4 #define NIFTI_SLICE_ALT_INC2 5 /* 05 May 2005: RWCox */ #define NIFTI_SLICE_ALT_DEC2 6 /* 05 May 2005: RWCox */ /* @} */ /*---------------------------------------------------------------------------*/ /* UNUSED FIELDS: ------------- Some of the ANALYZE 7.5 fields marked as ++UNUSED++ may need to be set to particular values for compatibility with other programs. The issue of interoperability of ANALYZE 7.5 files is a murky one -- not all programs require exactly the same set of fields. (Unobscuring this murkiness is a principal motivation behind NIFTI-1.) Some of the fields that may need to be set for other (non-NIFTI aware) software to be happy are: extents dbh.h says this should be 16384 regular dbh.h says this should be the character 'r' glmin, } dbh.h says these values should be the min and max voxel glmax } values for the entire dataset It is best to initialize ALL fields in the NIFTI-1 header to 0 (e.g., with calloc()), then fill in what is needed. -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* MISCELLANEOUS C MACROS -----------------------------------------------------------------------------*/ /*.................*/ /*! Given a nifti_1_header struct, check if it has a good magic number. Returns NIFTI version number (1..9) if magic is good, 0 if it is not. */ #define NIFTI_VERSION(h) \ ( ( (h).magic[0]=='n' && (h).magic[3]=='\0' && \ ( (h).magic[1]=='i' || (h).magic[1]=='+' ) && \ ( (h).magic[2]>='1' && (h).magic[2]<='9' ) ) \ ? (h).magic[2]-'0' : 0 ) /*.................*/ /*! Check if a nifti_1_header struct says if the data is stored in the same file or in a separate file. Returns 1 if the data is in the same file as the header, 0 if it is not. */ #define NIFTI_ONEFILE(h) ( (h).magic[1] == '+' ) /*.................*/ /*! Check if a nifti_1_header struct needs to be byte swapped. Returns 1 if it needs to be swapped, 0 if it does not. */ #define NIFTI_NEEDS_SWAP(h) ( (h).dim[0] < 0 || (h).dim[0] > 7 ) /*.................*/ /*! Check if a nifti_1_header struct contains a 5th (vector) dimension. Returns size of 5th dimension if > 1, returns 0 otherwise. */ #define NIFTI_5TH_DIM(h) ( ((h).dim[0]>4 && (h).dim[5]>1) ? (h).dim[5] : 0 ) /*****************************************************************************/ /*=================*/ #ifdef __cplusplus } #endif /*=================*/ #endif /* _NIFTI_HEADER_ */ dcm2niix-1.0.20171215/console/nifti1_io_core.cpp000066400000000000000000000440411322051203000210160ustar00rootroot00000000000000//This unit uses a subset of the functions from the nifti1_io available from // https://sourceforge.net/projects/niftilib/files/nifticlib/ //These functions were extended by Chris Rorden (2014) and maintain the same license /*****===================================================================*****/ /***** Sample functions to deal with NIFTI-1 and ANALYZE files *****/ /*****...................................................................*****/ /***** This code is released to the public domain. *****/ /*****...................................................................*****/ /***** Author: Robert W Cox, SSCC/DIRP/NIMH/NIH/DHHS/USA/EARTH *****/ /***** Date: August 2003 *****/ /*****...................................................................*****/ /***** Neither the National Institutes of Health (NIH), nor any of its *****/ /***** employees imply any warranty of usefulness of this software for *****/ /***** any purpose, and do not assume any liability for damages, *****/ /***** incidental or otherwise, caused by any use of this document. *****/ /*****===================================================================*****/ #include "nifti1_io_core.h" #include #include #include #include #include #include #include #include #include #ifndef _MSC_VER #include #endif #include "print.h" #ifndef HAVE_R void nifti_swap_8bytes( size_t n , void *ar ) // 4 bytes at a time { size_t ii ; unsigned char * cp0 = (unsigned char *)ar, * cp1, * cp2 ; unsigned char tval ; for( ii=0 ; ii < n ; ii++ ){ cp1 = cp0; cp2 = cp0+7; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp1++; cp2--; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp1++; cp2--; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp1++; cp2--; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp0 += 8; } return ; } void nifti_swap_4bytes( size_t n , void *ar ) // 4 bytes at a time { size_t ii ; unsigned char * cp0 = (unsigned char *)ar, * cp1, * cp2 ; unsigned char tval ; for( ii=0 ; ii < n ; ii++ ){ cp1 = cp0; cp2 = cp0+3; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp1++; cp2--; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp0 += 4; } return ; } void nifti_swap_2bytes( size_t n , void *ar ) // 2 bytes at a time { size_t ii ; unsigned char * cp1 = (unsigned char *)ar, * cp2 ; unsigned char tval; for( ii=0 ; ii < n ; ii++ ){ cp2 = cp1 + 1; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp1 += 2; } return ; } #endif int isSameFloat (float a, float b) { return (fabs (a - b) <= FLT_EPSILON); } ivec3 setiVec3(int x, int y, int z) { ivec3 v = {{x, y, z}}; return v; } vec3 setVec3(float x, float y, float z) { vec3 v = {{x, y, z}}; return v; } vec4 setVec4(float x, float y, float z) { vec4 v= {{x, y, z, 1}}; return v; } vec3 crossProduct(vec3 u, vec3 v) { return setVec3(u.v[1]*v.v[2] - v.v[1]*u.v[2], -u.v[0]*v.v[2] + v.v[0]*u.v[2], u.v[0]*v.v[1] - v.v[0]*u.v[1]); } float dotProduct(vec3 u, vec3 v) { return (u.v[0]*v.v[0] + v.v[1]*u.v[1] + v.v[2]*u.v[2]); } vec3 nifti_vect33_norm (vec3 v) { //normalize vector length vec3 vO = v; float vLen = sqrt( (v.v[0]*v.v[0]) + (v.v[1]*v.v[1]) + (v.v[2]*v.v[2])); if (vLen <= FLT_EPSILON) return vO; //avoid divide by zero for (int i = 0; i < 3; i++) vO.v[i] = v.v[i]/vLen; return vO; } vec3 nifti_vect33mat33_mul(vec3 v, mat33 m ) { //multiply vector * 3x3matrix vec3 vO; for (int i=0; i<3; i++) { //multiply Pcrs * m vO.v[i] = 0; for(int j=0; j<3; j++) vO.v[i] += m.m[i][j]*v.v[j]; } return vO; } vec4 nifti_vect44mat44_mul(vec4 v, mat44 m ) { //multiply vector * 4x4matrix vec4 vO; for (int i=0; i<4; i++) { //multiply Pcrs * m vO.v[i] = 0; for(int j=0; j<4; j++) vO.v[i] += m.m[i][j]*v.v[j]; } return vO; } mat44 nifti_dicom2mat(float orient[7], float patientPosition[4], float xyzMM[4]) { //create NIfTI header based on values from DICOM header //note orient has 6 values, indexed from 1, patient position and xyzMM have 3 values indexed from 1 mat33 Q, diagVox; Q.m[0][0] = orient[1]; Q.m[0][1] = orient[2] ; Q.m[0][2] = orient[3] ; // load Q Q.m[1][0] = orient[4]; Q.m[1][1] = orient[5] ; Q.m[1][2] = orient[6]; //printMessage("Orient %g %g %g %g %g %g\n",orient[1],orient[2],orient[3],orient[4],orient[5],orient[6] ); /* normalize row 1 */ double val = Q.m[0][0]*Q.m[0][0] + Q.m[0][1]*Q.m[0][1] + Q.m[0][2]*Q.m[0][2] ; if( val > 0.0l ){ val = 1.0l / sqrt(val) ; Q.m[0][0] *= (float)val ; Q.m[0][1] *= (float)val ; Q.m[0][2] *= (float)val ; } else { Q.m[0][0] = 1.0l ; Q.m[0][1] = 0.0l ; Q.m[0][2] = 0.0l ; } /* normalize row 2 */ val = Q.m[1][0]*Q.m[1][0] + Q.m[1][1]*Q.m[1][1] + Q.m[1][2]*Q.m[1][2] ; if( val > 0.0l ){ val = 1.0l / sqrt(val) ; Q.m[1][0] *= (float)val ; Q.m[1][1] *= (float)val ; Q.m[1][2] *= (float)val ; } else { Q.m[1][0] = 0.0l ; Q.m[1][1] = 1.0l ; Q.m[1][2] = 0.0l ; } /* row 3 is the cross product of rows 1 and 2*/ Q.m[2][0] = Q.m[0][1]*Q.m[1][2] - Q.m[0][2]*Q.m[1][1] ; /* cross */ Q.m[2][1] = Q.m[0][2]*Q.m[1][0] - Q.m[0][0]*Q.m[1][2] ; /* product */ Q.m[2][2] = Q.m[0][0]*Q.m[1][1] - Q.m[0][1]*Q.m[1][0] ; Q = nifti_mat33_transpose(Q); if (nifti_mat33_determ(Q) < 0.0) { Q.m[0][2] = -Q.m[0][2]; Q.m[1][2] = -Q.m[1][2]; Q.m[2][2] = -Q.m[2][2]; } //next scale matrix LOAD_MAT33(diagVox, xyzMM[1],0.0l,0.0l, 0.0l,xyzMM[2],0.0l, 0.0l,0.0l, xyzMM[3]); Q = nifti_mat33_mul(Q,diagVox); mat44 Q44; //4x4 matrix includes translations LOAD_MAT44(Q44, Q.m[0][0],Q.m[0][1],Q.m[0][2],patientPosition[1], Q.m[1][0],Q.m[1][1],Q.m[1][2],patientPosition[2], Q.m[2][0],Q.m[2][1],Q.m[2][2],patientPosition[3]); return Q44; } #ifndef HAVE_R float nifti_mat33_determ( mat33 R ) /* determinant of 3x3 matrix */ { double r11,r12,r13,r21,r22,r23,r31,r32,r33 ; /* INPUT MATRIX: */ r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2]; /* [ r11 r12 r13 ] */ r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2]; /* [ r21 r22 r23 ] */ r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2]; /* [ r31 r32 r33 ] */ return (float)(r11*r22*r33-r11*r32*r23-r21*r12*r33 +r21*r32*r13+r31*r12*r23-r31*r22*r13) ; } mat33 nifti_mat33_mul( mat33 A , mat33 B ) /* multiply 2 3x3 matrices */ //see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c { mat33 C ; int i,j ; for( i=0 ; i < 3 ; i++ ) for( j=0 ; j < 3 ; j++ ) C.m[i][j] = A.m[i][0] * B.m[0][j] + A.m[i][1] * B.m[1][j] + A.m[i][2] * B.m[2][j] ; return C ; } #endif mat44 nifti_mat44_mul( mat44 A , mat44 B ) /* multiply 2 3x3 matrices */ { mat44 C ; int i,j ; for( i=0 ; i < 4 ; i++ ) for( j=0 ; j < 4; j++ ) C.m[i][j] = A.m[i][0] * B.m[0][j] + A.m[i][1] * B.m[1][j] + A.m[i][2] * B.m[2][j] + A.m[i][3] * B.m[3][j]; return C ; } mat33 nifti_mat33_transpose( mat33 A ) /* transpose 3x3 matrix */ //see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c { mat33 B; int i,j ; for( i=0 ; i < 3 ; i++ ) for( j=0 ; j < 3 ; j++ ) B.m[i][j] = A.m[j][i]; return B; } #ifndef HAVE_R mat33 nifti_mat33_inverse( mat33 R ) /* inverse of 3x3 matrix */ { double r11,r12,r13,r21,r22,r23,r31,r32,r33 , deti ; mat33 Q ; // INPUT MATRIX: r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2]; // [ r11 r12 r13 ] r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2]; // [ r21 r22 r23 ] r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2]; // [ r31 r32 r33 ] deti = r11*r22*r33-r11*r32*r23-r21*r12*r33 +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; if( deti != 0.0l ) deti = 1.0l / deti ; Q.m[0][0] = deti*( r22*r33-r32*r23) ; Q.m[0][1] = deti*(-r12*r33+r32*r13) ; Q.m[0][2] = deti*( r12*r23-r22*r13) ; Q.m[1][0] = deti*(-r21*r33+r31*r23) ; Q.m[1][1] = deti*( r11*r33-r31*r13) ; Q.m[1][2] = deti*(-r11*r23+r21*r13) ; Q.m[2][0] = deti*( r21*r32-r31*r22) ; Q.m[2][1] = deti*(-r11*r32+r31*r12) ; Q.m[2][2] = deti*( r11*r22-r21*r12) ; return Q ; } float nifti_mat33_rownorm( mat33 A ) // max row norm of 3x3 matrix { float r1,r2,r3 ; r1 = fabs(A.m[0][0])+fabs(A.m[0][1])+fabs(A.m[0][2]) ; r2 = fabs(A.m[1][0])+fabs(A.m[1][1])+fabs(A.m[1][2]) ; r3 = fabs(A.m[2][0])+fabs(A.m[2][1])+fabs(A.m[2][2]) ; if( r1 < r2 ) r1 = r2 ; if( r1 < r3 ) r1 = r3 ; return r1 ; } float nifti_mat33_colnorm( mat33 A ) // max column norm of 3x3 matrix { float r1,r2,r3 ; r1 = fabs(A.m[0][0])+fabs(A.m[1][0])+fabs(A.m[2][0]) ; r2 = fabs(A.m[0][1])+fabs(A.m[1][1])+fabs(A.m[2][1]) ; r3 = fabs(A.m[0][2])+fabs(A.m[1][2])+fabs(A.m[2][2]) ; if( r1 < r2 ) r1 = r2 ; if( r1 < r3 ) r1 = r3 ; return r1 ; } mat33 nifti_mat33_polar( mat33 A ) { mat33 X , Y , Z ; float alp,bet,gam,gmi , dif=1.0 ; int k=0 ; X = A ; // force matrix to be nonsingular gam = nifti_mat33_determ(X) ; while( gam == 0.0 ){ // perturb matrix gam = 0.00001 * ( 0.001 + nifti_mat33_rownorm(X) ) ; X.m[0][0] += gam ; X.m[1][1] += gam ; X.m[2][2] += gam ; gam = nifti_mat33_determ(X) ; } while(1){ Y = nifti_mat33_inverse(X) ; if( dif > 0.3 ){ // far from convergence alp = sqrt( nifti_mat33_rownorm(X) * nifti_mat33_colnorm(X) ) ; bet = sqrt( nifti_mat33_rownorm(Y) * nifti_mat33_colnorm(Y) ) ; gam = sqrt( bet / alp ) ; gmi = 1.0 / gam ; } else gam = gmi = 1.0 ; // close to convergence Z.m[0][0] = 0.5 * ( gam*X.m[0][0] + gmi*Y.m[0][0] ) ; Z.m[0][1] = 0.5 * ( gam*X.m[0][1] + gmi*Y.m[1][0] ) ; Z.m[0][2] = 0.5 * ( gam*X.m[0][2] + gmi*Y.m[2][0] ) ; Z.m[1][0] = 0.5 * ( gam*X.m[1][0] + gmi*Y.m[0][1] ) ; Z.m[1][1] = 0.5 * ( gam*X.m[1][1] + gmi*Y.m[1][1] ) ; Z.m[1][2] = 0.5 * ( gam*X.m[1][2] + gmi*Y.m[2][1] ) ; Z.m[2][0] = 0.5 * ( gam*X.m[2][0] + gmi*Y.m[0][2] ) ; Z.m[2][1] = 0.5 * ( gam*X.m[2][1] + gmi*Y.m[1][2] ) ; Z.m[2][2] = 0.5 * ( gam*X.m[2][2] + gmi*Y.m[2][2] ) ; dif = fabs(Z.m[0][0]-X.m[0][0])+fabs(Z.m[0][1]-X.m[0][1]) +fabs(Z.m[0][2]-X.m[0][2])+fabs(Z.m[1][0]-X.m[1][0]) +fabs(Z.m[1][1]-X.m[1][1])+fabs(Z.m[1][2]-X.m[1][2]) +fabs(Z.m[2][0]-X.m[2][0])+fabs(Z.m[2][1]-X.m[2][1]) +fabs(Z.m[2][2]-X.m[2][2]) ; k = k+1 ; if( k > 100 || dif < 3.e-6 ) break ; // convergence or exhaustion X = Z ; } return Z ; } void nifti_mat44_to_quatern( mat44 R , float *qb, float *qc, float *qd, float *qx, float *qy, float *qz, float *dx, float *dy, float *dz, float *qfac ) { double r11,r12,r13 , r21,r22,r23 , r31,r32,r33 ; double xd,yd,zd , a,b,c,d ; mat33 P,Q ; // offset outputs are read write out of input matrix ASSIF(qx,R.m[0][3]) ; ASSIF(qy,R.m[1][3]) ; ASSIF(qz,R.m[2][3]) ; // load 3x3 matrix into local variables */ r11 = R.m[0][0] ; r12 = R.m[0][1] ; r13 = R.m[0][2] ; r21 = R.m[1][0] ; r22 = R.m[1][1] ; r23 = R.m[1][2] ; r31 = R.m[2][0] ; r32 = R.m[2][1] ; r33 = R.m[2][2] ; // compute lengths of each column; these determine grid spacings xd = sqrt( r11*r11 + r21*r21 + r31*r31 ) ; yd = sqrt( r12*r12 + r22*r22 + r32*r32 ) ; zd = sqrt( r13*r13 + r23*r23 + r33*r33 ) ; // if a column length is zero, patch the trouble if( xd == 0.0l ){ r11 = 1.0l ; r21 = r31 = 0.0l ; xd = 1.0l ; } if( yd == 0.0l ){ r22 = 1.0l ; r12 = r32 = 0.0l ; yd = 1.0l ; } if( zd == 0.0l ){ r33 = 1.0l ; r13 = r23 = 0.0l ; zd = 1.0l ; } // assign the output lengths */ ASSIF(dx,xd) ; ASSIF(dy,yd) ; ASSIF(dz,zd) ; // normalize the columns */ r11 /= xd ; r21 /= xd ; r31 /= xd ; r12 /= yd ; r22 /= yd ; r32 /= yd ; r13 /= zd ; r23 /= zd ; r33 /= zd ; /* At this point, the matrix has normal columns, but we have to allow for the fact that the hideous user may not have given us a matrix with orthogonal columns. So, now find the orthogonal matrix closest to the current matrix. One reason for using the polar decomposition to get this orthogonal matrix, rather than just directly orthogonalizing the columns, is so that inputting the inverse matrix to R will result in the inverse orthogonal matrix at this point. If we just orthogonalized the columns, this wouldn't necessarily hold. */ Q.m[0][0] = r11 ; Q.m[0][1] = r12 ; Q.m[0][2] = r13 ; // load Q Q.m[1][0] = r21 ; Q.m[1][1] = r22 ; Q.m[1][2] = r23 ; Q.m[2][0] = r31 ; Q.m[2][1] = r32 ; Q.m[2][2] = r33 ; P = nifti_mat33_polar(Q) ; // P is orthog matrix closest to Q r11 = P.m[0][0] ; r12 = P.m[0][1] ; r13 = P.m[0][2] ; // unload r21 = P.m[1][0] ; r22 = P.m[1][1] ; r23 = P.m[1][2] ; r31 = P.m[2][0] ; r32 = P.m[2][1] ; r33 = P.m[2][2] ; // [ r11 r12 r13 ] // at this point, the matrix [ r21 r22 r23 ] is orthogonal // [ r31 r32 r33 ] // compute the determinant to determine if it is proper zd = r11*r22*r33-r11*r32*r23-r21*r12*r33 +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; // should be -1 or 1 if( zd > 0 ){ // proper ASSIF(qfac,1.0) ; } else { // improper ==> flip 3rd column ASSIF(qfac,-1.0) ; r13 = -r13 ; r23 = -r23 ; r33 = -r33 ; } // now, compute quaternion parameters a = r11 + r22 + r33 + 1.0l ; if( a > 0.5l ){ // simplest case a = 0.5l * sqrt(a) ; b = 0.25l * (r32-r23) / a ; c = 0.25l * (r13-r31) / a ; d = 0.25l * (r21-r12) / a ; } else { // trickier case xd = 1.0 + r11 - (r22+r33) ; // 4*b*b yd = 1.0 + r22 - (r11+r33) ; // 4*c*c zd = 1.0 + r33 - (r11+r22) ; // 4*d*d if( xd > 1.0 ){ b = 0.5l * sqrt(xd) ; c = 0.25l* (r12+r21) / b ; d = 0.25l* (r13+r31) / b ; a = 0.25l* (r32-r23) / b ; } else if( yd > 1.0 ){ c = 0.5l * sqrt(yd) ; b = 0.25l* (r12+r21) / c ; d = 0.25l* (r23+r32) / c ; a = 0.25l* (r13-r31) / c ; } else { d = 0.5l * sqrt(zd) ; b = 0.25l* (r13+r31) / d ; c = 0.25l* (r23+r32) / d ; a = 0.25l* (r21-r12) / d ; } // if( a < 0.0l ){ b=-b ; c=-c ; d=-d; a=-a; } if( a < 0.0l ){ b=-b ; c=-c ; d=-d; } //a discarded... } ASSIF(qb,b) ; ASSIF(qc,c) ; ASSIF(qd,d) ; return ; } mat44 nifti_quatern_to_mat44( float qb, float qc, float qd, float qx, float qy, float qz, float dx, float dy, float dz, float qfac ) { mat44 R ; double a,b=qb,c=qc,d=qd , xd,yd,zd ; /* last row is always [ 0 0 0 1 ] */ R.m[3][0]=R.m[3][1]=R.m[3][2] = 0.0f ; R.m[3][3]= 1.0f ; /* compute a parameter from b,c,d */ a = 1.0l - (b*b + c*c + d*d) ; if( a < 1.e-7l ){ /* special case */ a = 1.0l / sqrt(b*b+c*c+d*d) ; b *= a ; c *= a ; d *= a ; /* normalize (b,c,d) vector */ a = 0.0l ; /* a = 0 ==> 180 degree rotation */ } else{ a = sqrt(a) ; /* angle = 2*arccos(a) */ } /* load rotation matrix, including scaling factors for voxel sizes */ xd = (dx > 0.0) ? dx : 1.0l ; /* make sure are positive */ yd = (dy > 0.0) ? dy : 1.0l ; zd = (dz > 0.0) ? dz : 1.0l ; if( qfac < 0.0 ) zd = -zd ; /* left handedness? */ R.m[0][0] = (float)( (a*a+b*b-c*c-d*d) * xd) ; R.m[0][1] = 2.0l * (b*c-a*d ) * yd ; R.m[0][2] = 2.0l * (b*d+a*c ) * zd ; R.m[1][0] = 2.0l * (b*c+a*d ) * xd ; R.m[1][1] = (float)( (a*a+c*c-b*b-d*d) * yd) ; R.m[1][2] = 2.0l * (c*d-a*b ) * zd ; R.m[2][0] = 2.0l * (b*d-a*c ) * xd ; R.m[2][1] = 2.0l * (c*d+a*b ) * yd ; R.m[2][2] = (float)( (a*a+d*d-c*c-b*b) * zd) ; /* load offsets */ R.m[0][3] = qx ; R.m[1][3] = qy ; R.m[2][3] = qz ; return R ; } mat44 nifti_mat44_inverse( mat44 R ) { double r11,r12,r13,r21,r22,r23,r31,r32,r33,v1,v2,v3 , deti ; mat44 Q ; /* INPUT MATRIX IS: */ r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2]; // [ r11 r12 r13 v1 ] r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2]; // [ r21 r22 r23 v2 ] r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2]; // [ r31 r32 r33 v3 ] v1 = R.m[0][3]; v2 = R.m[1][3]; v3 = R.m[2][3]; // [ 0 0 0 1 ] deti = r11*r22*r33-r11*r32*r23-r21*r12*r33 +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; if( deti != 0.0l ) deti = 1.0l / deti ; Q.m[0][0] = deti*( r22*r33-r32*r23) ; Q.m[0][1] = deti*(-r12*r33+r32*r13) ; Q.m[0][2] = deti*( r12*r23-r22*r13) ; Q.m[0][3] = deti*(-r12*r23*v3+r12*v2*r33+r22*r13*v3 -r22*v1*r33-r32*r13*v2+r32*v1*r23) ; Q.m[1][0] = deti*(-r21*r33+r31*r23) ; Q.m[1][1] = deti*( r11*r33-r31*r13) ; Q.m[1][2] = deti*(-r11*r23+r21*r13) ; Q.m[1][3] = deti*( r11*r23*v3-r11*v2*r33-r21*r13*v3 +r21*v1*r33+r31*r13*v2-r31*v1*r23) ; Q.m[2][0] = deti*( r21*r32-r31*r22) ; Q.m[2][1] = deti*(-r11*r32+r31*r12) ; Q.m[2][2] = deti*( r11*r22-r21*r12) ; Q.m[2][3] = deti*(-r11*r22*v3+r11*r32*v2+r21*r12*v3 -r21*r32*v1-r31*r12*v2+r31*r22*v1) ; Q.m[3][0] = Q.m[3][1] = Q.m[3][2] = 0.0l ; Q.m[3][3] = (deti == 0.0l) ? 0.0l : 1.0l ; // failure flag if deti == 0 return Q ; } #endif dcm2niix-1.0.20171215/console/nifti1_io_core.h000066400000000000000000000056021322051203000204630ustar00rootroot00000000000000//this minimal set of nifti routines is based on nifti1_io with the dependencies (zlib) and a few extra functions // http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.h // http://niftilib.sourceforge.net #ifndef _NIFTI_IO_CORE_HEADER_ #define _NIFTI_IO_CORE_HEADER_ #ifdef HAVE_R #define STRICT_R_HEADERS #include "RNifti.h" #endif #ifdef __cplusplus extern "C" { #endif #include #include #ifndef HAVE_R typedef struct { /** 4x4 matrix struct **/ float m[3][3] ; } mat33 ; typedef struct { /** 4x4 matrix struct **/ float m[4][4] ; } mat44 ; #endif typedef struct { /** x4 vector struct **/ float v[4] ; } vec4 ; typedef struct { /** x3 vector struct **/ float v[3] ; } vec3 ; typedef struct { /** x4 vector struct INTEGER**/ int v[3] ; } ivec3 ; #define LOAD_MAT33(AA,a11,a12,a13 ,a21,a22,a23 ,a31,a32,a33) \ ( AA.m[0][0]=a11 , AA.m[0][1]=a12 , AA.m[0][2]=a13 , \ AA.m[1][0]=a21 , AA.m[1][1]=a22 , AA.m[1][2]=a23 , \ AA.m[2][0]=a31 , AA.m[2][1]=a32 , AA.m[2][2]=a33 ) #define LOAD_MAT44(AA,a11,a12,a13,a14,a21,a22,a23,a24,a31,a32,a33,a34) \ ( AA.m[0][0]=a11 , AA.m[0][1]=a12 , AA.m[0][2]=a13 , AA.m[0][3]=a14 , \ AA.m[1][0]=a21 , AA.m[1][1]=a22 , AA.m[1][2]=a23 , AA.m[1][3]=a24 , \ AA.m[2][0]=a31 , AA.m[2][1]=a32 , AA.m[2][2]=a33 , AA.m[2][3]=a34 , \ AA.m[3][0]=AA.m[3][1]=AA.m[3][2]=0.0f , AA.m[3][3]=1.0f ) #undef ASSIF // assign v to *p, if possible #define ASSIF(p,v) if( (p)!=NULL ) *(p) = (v) float dotProduct(vec3 u, vec3 v); float nifti_mat33_determ( mat33 R ) ; int isSameFloat (float a, float b) ; mat33 nifti_mat33_inverse( mat33 R ); mat33 nifti_mat33_mul( mat33 A , mat33 B ); mat33 nifti_mat33_transpose( mat33 A ) ; mat44 nifti_dicom2mat(float orient[7], float patientPosition[4], float xyzMM[4]); mat44 nifti_mat44_inverse( mat44 R ); mat44 nifti_mat44_mul( mat44 A , mat44 B ); vec3 crossProduct(vec3 u, vec3 v); vec3 nifti_vect33_norm (vec3 v); vec3 nifti_vect33mat33_mul(vec3 v, mat33 m ); ivec3 setiVec3(int x, int y, int z); vec3 setVec3(float x, float y, float z); vec4 setVec4(float x, float y, float z); vec4 nifti_vect44mat44_mul(vec4 v, mat44 m ); void nifti_swap_2bytes( size_t n , void *ar ); // 2 bytes at a time void nifti_swap_4bytes( size_t n , void *ar ); // 4 bytes at a time void nifti_swap_8bytes( size_t n , void *ar ); // 8 bytes at a time void nifti_mat44_to_quatern( mat44 R , float *qb, float *qc, float *qd, float *qx, float *qy, float *qz, float *dx, float *dy, float *dz, float *qfac ); mat44 nifti_quatern_to_mat44( float qb, float qc, float qd, float qx, float qy, float qz, float dx, float dy, float dz, float qfac ); #ifdef __cplusplus } #endif #endifdcm2niix-1.0.20171215/console/nii_dicom.cpp000066400000000000000000005764441322051203000201000ustar00rootroot00000000000000//#define MY_DEBUG #if defined(_WIN64) || defined(_WIN32) #include //write to registry #endif #ifdef _MSC_VER #include #define getcwd _getcwd #define chdir _chrdir #include "io.h" #include //#define snprintf _snprintf //#define vsnprintf _vsnprintf #define strcasecmp _stricmp #define strncasecmp _strnicmp #else #include #endif //#include //clock() #ifndef HAVE_R #include "nifti1.h" #endif #include "print.h" #include "nii_dicom.h" #include #include // discriminate files from folders #include #include #include //toupper #include #include #include #include "jpg_0XC3.h" #include #include #include "nifti1_io_core.h" #ifdef HAVE_R #undef isnan #define isnan ISNAN #endif #ifndef myDisableClassicJPEG #ifdef myTurboJPEG #include #else #include "ujpeg.h" #endif #endif #ifdef myEnableJasper #include #endif #ifndef myDisableOpenJPEG #include "openjpeg.h" #ifdef myEnableJasper ERROR: YOU CAN NOT COMPILE WITH myEnableJasper AND NOT myDisableOpenJPEG OPTIONS SET SIMULTANEOUSLY #endif unsigned char * imagetoimg(opj_image_t * image) { int numcmpts = image->numcomps; int sgnd = image->comps[0].sgnd ; int width = image->comps[0].w; int height = image->comps[0].h; int bpp = (image->comps[0].prec + 7) >> 3; //e.g. 12 bits requires 2 bytes int imgbytes = bpp * width * height * numcmpts; bool isOK = true; if (numcmpts > 1) { for (int comp = 1; comp < numcmpts; comp++) { //check RGB data if (image->comps[0].w != image->comps[comp].w) isOK = false; if (image->comps[0].h != image->comps[comp].h) isOK = false; if (image->comps[0].dx != image->comps[comp].dx) isOK = false; if (image->comps[0].dy != image->comps[comp].dy) isOK = false; if (image->comps[0].prec != image->comps[comp].prec) isOK = false; if (image->comps[0].sgnd != image->comps[comp].sgnd) isOK = false; } if (numcmpts != 3) isOK = false; //we only handle Gray and RedGreenBlue, not GrayAlpha or RedGreenBlueAlpha if (image->comps[0].prec != 8) isOK = false; //only 8-bit for RGB data } if ((image->comps[0].prec < 1) || (image->comps[0].prec > 16)) isOK = false; //currently we only handle 1 and 2 byte data if (!isOK) { printMessage("jpeg decode failure w*h %d*%d bpp %d sgnd %d components %d OpenJPEG=%s\n", width, height, bpp, sgnd, numcmpts, opj_version()); return NULL; } #ifdef MY_DEBUG printMessage("w*h %d*%d bpp %d sgnd %d components %d OpenJPEG=%s\n", width, height, bpp, sgnd, numcmpts, opj_version()); #endif //extract the data if ((bpp < 1) || (bpp > 2) || (width < 1) || (height < 1) || (imgbytes < 1)) { printError("Catastrophic decompression error\n"); return NULL; } unsigned char *img = (unsigned char *)malloc(imgbytes); uint16_t * img16ui = (uint16_t*) img; //unsigned 16-bit int16_t * img16i = (int16_t*) img; //signed 16-bit if (sgnd) bpp = -bpp; if (bpp == -1) { free(img); printError("Signed 8-bit DICOM?\n"); return NULL; } //n.b. Analyze rgb-24 are PLANAR e.g. RRR..RGGG..GBBB..B not RGBRGBRGB...RGB int pix = 0; //ouput pixel for (int cmptno = 0; cmptno < numcmpts; ++cmptno) { int cpix = 0; //component pixel int* v = image->comps[cmptno].data; for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { switch (bpp) { case 1: img[pix] = (unsigned char) v[cpix]; break; case 2: img16ui[pix] = (uint16_t) v[cpix]; break; case -2: img16i[pix] = (int16_t) v[cpix]; break; } pix ++; cpix ++; }//for x } //for y } //for each component return img; }// imagetoimg() typedef struct bufinfo { unsigned char *buf; unsigned char *cur; size_t len; } BufInfo; static void my_stream_free (void * p_user_data) { //do nothing //BufInfo d = (BufInfo) p_user_data; //free(d.buf); } // my_stream_free() static OPJ_UINT32 opj_read_from_buffer(void * p_buffer, OPJ_UINT32 p_nb_bytes, BufInfo* p_file) { OPJ_UINT32 l_nb_read; if(p_file->cur + p_nb_bytes < p_file->buf + p_file->len ) { l_nb_read = p_nb_bytes; } else { l_nb_read = (OPJ_UINT32)(p_file->buf + p_file->len - p_file->cur); } memcpy(p_buffer, p_file->cur, l_nb_read); p_file->cur += l_nb_read; return l_nb_read ? l_nb_read : ((OPJ_UINT32)-1); } //opj_read_from_buffer() static OPJ_UINT32 opj_write_from_buffer(void * p_buffer, OPJ_UINT32 p_nb_bytes, BufInfo* p_file) { memcpy(p_file->cur,p_buffer, p_nb_bytes); p_file->cur += p_nb_bytes; p_file->len += p_nb_bytes; return p_nb_bytes; } // opj_write_from_buffer() static OPJ_SIZE_T opj_skip_from_buffer(OPJ_SIZE_T p_nb_bytes, BufInfo * p_file) { if(p_file->cur + p_nb_bytes < p_file->buf + p_file->len ) { p_file->cur += p_nb_bytes; return p_nb_bytes; } p_file->cur = p_file->buf + p_file->len; return (OPJ_SIZE_T)-1; } //opj_skip_from_buffer() //fix for https://github.com/neurolabusc/dcm_qa/issues/5 static OPJ_BOOL opj_seek_from_buffer(OPJ_SIZE_T p_nb_bytes, BufInfo * p_file) { //printf("opj_seek_from_buffer %d + %d -> %d + %d\n", p_file->cur , p_nb_bytes, p_file->buf, p_file->len); if (p_nb_bytes < p_file->len ) { p_file->cur = p_file->buf + p_nb_bytes; return OPJ_TRUE; } p_file->cur = p_file->buf + p_file->len; return OPJ_FALSE; } //opj_seek_from_buffer() /*static OPJ_BOOL opj_seek_from_buffer(OPJ_SIZE_T p_nb_bytes, BufInfo * p_file) { if((p_file->cur + p_nb_bytes) < (p_file->buf + p_file->len) ) { p_file->cur += p_nb_bytes; return OPJ_TRUE; } p_file->cur = p_file->buf + p_file->len; return OPJ_FALSE; } //opj_seek_from_buffer()*/ opj_stream_t* opj_stream_create_buffer_stream(BufInfo* p_file, OPJ_UINT32 p_size, OPJ_BOOL p_is_read_stream) { opj_stream_t* l_stream; if(! p_file) return NULL; l_stream = opj_stream_create(p_size, p_is_read_stream); if(! l_stream) return NULL; opj_stream_set_user_data(l_stream, p_file , my_stream_free); opj_stream_set_user_data_length(l_stream, p_file->len); opj_stream_set_read_function(l_stream, (opj_stream_read_fn) opj_read_from_buffer); opj_stream_set_write_function(l_stream, (opj_stream_write_fn) opj_write_from_buffer); opj_stream_set_skip_function(l_stream, (opj_stream_skip_fn) opj_skip_from_buffer); opj_stream_set_seek_function(l_stream, (opj_stream_seek_fn) opj_seek_from_buffer); return l_stream; } //opj_stream_create_buffer_stream() unsigned char * nii_loadImgCoreOpenJPEG(char* imgname, struct nifti_1_header hdr, struct TDICOMdata dcm, int compressFlag) { //OpenJPEG library is not well documented and has changed between versions //Since the JPEG is embedded in a DICOM we need to skip bytes at the start of the file // In theory we might also want to strip data that exists AFTER the image, see gdcmJPEG2000Codec.c unsigned char * ret = NULL; opj_dparameters_t params; opj_codec_t *codec; opj_image_t *jpx; opj_stream_t *stream; FILE *reader = fopen(imgname, "rb"); fseek(reader, 0, SEEK_END); long size = ftell(reader)- dcm.imageStart; if (size <= 8) return NULL; fseek(reader, dcm.imageStart, SEEK_SET); unsigned char *data = (unsigned char*) malloc(size); size_t sz = fread(data, 1, size, reader); fclose(reader); if (sz < size) return NULL; OPJ_CODEC_FORMAT format = OPJ_CODEC_JP2; //DICOM JPEG2k is SUPPOSED to start with codestream, but some vendors include a header if (data[0] == 0xFF && data[1] == 0x4F && data[2] == 0xFF && data[3] == 0x51) format = OPJ_CODEC_J2K; opj_set_default_decoder_parameters(¶ms); BufInfo dx; dx.buf = data; dx.cur = data; dx.len = size; stream = opj_stream_create_buffer_stream(&dx, (OPJ_UINT32)size, true); if (stream == NULL) return NULL; codec = opj_create_decompress(format); // setup the decoder decoding parameters using user parameters if ( !opj_setup_decoder(codec, ¶ms) ) goto cleanup2; // Read the main header of the codestream and if necessary the JP2 boxes if(! opj_read_header( stream, codec, &jpx)){ printError( "OpenJPEG failed to read the header %s (offset %d)\n", imgname, dcm.imageStart); //comment these next lines to abort: include these to create zero-padded slice #ifdef MY_ZEROFILLBROKENJPGS //fix broken slices https://github.com/scitran-apps/dcm2niix/issues/4 printError( "Zero-filled slice created\n"); int imgbytes = (hdr.bitpix/8)*hdr.dim[1]*hdr.dim[2]; ret = (unsigned char*) calloc(imgbytes,1); #endif goto cleanup2; } // Get the decoded image if ( !( opj_decode(codec, stream, jpx) && opj_end_decompress(codec,stream) ) ) { printError( "OpenJPEG j2k_to_image failed to decode %s\n",imgname); goto cleanup1; } ret = imagetoimg(jpx); cleanup1: opj_image_destroy(jpx); cleanup2: free(dx.buf); opj_stream_destroy(stream); opj_destroy_codec(codec); return ret; } #endif //if #ifndef M_PI #define M_PI 3.14159265358979323846 #endif float deFuzz(float v) { if (fabs(v) < 0.00001) return 0; else return v; } #ifdef MY_DEBUG void reportMat33(char *str, mat33 A) { printMessage("%s = [%g %g %g ; %g %g %g; %g %g %g ]\n",str, deFuzz(A.m[0][0]),deFuzz(A.m[0][1]),deFuzz(A.m[0][2]), deFuzz(A.m[1][0]),deFuzz(A.m[1][1]),deFuzz(A.m[1][2]), deFuzz(A.m[2][0]),deFuzz(A.m[2][1]),deFuzz(A.m[2][2])); } void reportMat44(char *str, mat44 A) { //example: reportMat44((char*)"out",*R); printMessage("%s = [%g %g %g %g; %g %g %g %g; %g %g %g %g; 0 0 0 1]\n",str, deFuzz(A.m[0][0]),deFuzz(A.m[0][1]),deFuzz(A.m[0][2]),deFuzz(A.m[0][3]), deFuzz(A.m[1][0]),deFuzz(A.m[1][1]),deFuzz(A.m[1][2]),deFuzz(A.m[1][3]), deFuzz(A.m[2][0]),deFuzz(A.m[2][1]),deFuzz(A.m[2][2]),deFuzz(A.m[2][3])); } #endif int verify_slice_dir (struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, mat44 *R, int isVerbose){ //returns slice direction: 1=sag,2=coronal,3=axial, -= flipped if (h->dim[3] < 2) return 0; //don't care direction for single slice int iSL = 1; //find Z-slice direction: row with highest magnitude of 3rd column if ( (fabs(R->m[1][2]) >= fabs(R->m[0][2])) && (fabs(R->m[1][2]) >= fabs(R->m[2][2]))) iSL = 2; // if ( (fabs(R->m[2][2]) >= fabs(R->m[0][2])) && (fabs(R->m[2][2]) >= fabs(R->m[1][2]))) iSL = 3; //axial acquisition float pos = NAN; if ( !isnan(d2.patientPosition[iSL]) ) { //patient position fields exist pos = d2.patientPosition[iSL]; if (isSameFloat(pos, d.patientPosition[iSL])) pos = NAN; #ifdef MY_DEBUG if (!isnan(pos)) printMessage("position determined using lastFile %f\n",pos); #endif } if (isnan(pos) &&( !isnan(d.patientPositionLast[iSL]) ) ) { //patient position fields exist pos = d.patientPositionLast[iSL]; if (isSameFloat(pos, d.patientPosition[iSL])) pos = NAN; #ifdef MY_DEBUG if (!isnan(pos)) printMessage("position determined using last (4d) %f\n",pos); #endif } if (isnan(pos) && ( !isnan(d.stackOffcentre[iSL])) ) pos = d.stackOffcentre[iSL]; if (isnan(pos) && ( !isnan(d.lastScanLoc)) ) pos = d.lastScanLoc; //if (isnan(pos)) vec4 x; x.v[0] = 0.0; x.v[1] = 0.0; x.v[2]=(float)(h->dim[3]-1.0); x.v[3] = 1.0; vec4 pos1v = nifti_vect44mat44_mul(x, *R); float pos1 = pos1v.v[iSL-1];//-1 as C indexed from 0 bool flip = false; if (!isnan(pos)) // we have real SliceLocation for last slice or volume center flip = (pos > R->m[iSL-1][3]) != (pos1 > R->m[iSL-1][3]); // same direction?, note C indices from 0 else {// we do some guess work and warn user vec3 readV = setVec3(d.orient[1],d.orient[2],d.orient[3]); vec3 phaseV = setVec3(d.orient[4],d.orient[5],d.orient[6]); //printMessage("rd %g %g %g\n",readV.v[0],readV.v[1],readV.v[2]); //printMessage("ph %g %g %g\n",phaseV.v[0],phaseV.v[1],phaseV.v[2]); vec3 sliceV = crossProduct(readV, phaseV); //order important: this is our hail mary flip = ((sliceV.v[0]+sliceV.v[1]+sliceV.v[2]) < 0); //printMessage("verify slice dir %g %g %g\n",sliceV.v[0],sliceV.v[1],sliceV.v[2]); if (isVerbose) { //1st pass only if (!d.isDerived) //do not warn user if image is derived printWarning("Unable to determine slice direction: please check whether slices are flipped\n"); else printWarning("Unable to determine slice direction: please check whether slices are flipped (derived image)\n"); } } if (flip) { for (int i = 0; i < 4; i++) R->m[i][2] = -R->m[i][2]; } if (flip) iSL = -iSL; #ifdef MY_DEBUG printMessage("verify slice dir %d %d %d\n",h->dim[1],h->dim[2],h->dim[3]); //reportMat44((char*)"Rout",*R); printMessage("flip = %d\n",flip); printMessage("sliceDir = %d\n",iSL); printMessage(" pos1 = %f\n",pos1); #endif return iSL; } //verify_slice_dir() mat44 noNaN(mat44 Q44, bool isVerbose) //simplify any headers that have NaN values { mat44 ret = Q44; bool isNaN44 = false; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) if (isnan(ret.m[i][j])) isNaN44 = true; if (isNaN44) { if (isVerbose) printWarning("Bogus spatial matrix (perhaps non-spatial image): inspect spatial orientation\n"); for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) if (i == j) ret.m[i][j] = 1; else ret.m[i][j] = 0; ret.m[1][1] = -1; } //if isNaN detected return ret; } void setQSForm(struct nifti_1_header *h, mat44 Q44i, bool isVerbose) { mat44 Q44 = noNaN(Q44i, isVerbose); h->sform_code = NIFTI_XFORM_SCANNER_ANAT; h->srow_x[0] = Q44.m[0][0]; h->srow_x[1] = Q44.m[0][1]; h->srow_x[2] = Q44.m[0][2]; h->srow_x[3] = Q44.m[0][3]; h->srow_y[0] = Q44.m[1][0]; h->srow_y[1] = Q44.m[1][1]; h->srow_y[2] = Q44.m[1][2]; h->srow_y[3] = Q44.m[1][3]; h->srow_z[0] = Q44.m[2][0]; h->srow_z[1] = Q44.m[2][1]; h->srow_z[2] = Q44.m[2][2]; h->srow_z[3] = Q44.m[2][3]; float dumdx, dumdy, dumdz; nifti_mat44_to_quatern( Q44 , &h->quatern_b, &h->quatern_c, &h->quatern_d,&h->qoffset_x, &h->qoffset_y, &h->qoffset_z, &dumdx, &dumdy, &dumdz,&h->pixdim[0]) ; h->qform_code = NIFTI_XFORM_SCANNER_ANAT; } //setQSForm() #ifdef my_unused ivec3 maxCol(mat33 R) { //return index of maximum column in 3x3 matrix, e.g. [1 0 0; 0 1 0; 0 0 1] -> 1,2,3 ivec3 ixyz; //foo is abs(R) mat33 foo; for (int i=0 ; i < 3 ; i++ ) for (int j=0 ; j < 3 ; j++ ) foo.m[i][j] = fabs(R.m[i][j]); //ixyz.v[0] : row with largest value in column 1 ixyz.v[0] = 1; if ((foo.m[1][0] > foo.m[0][0]) && (foo.m[1][0] >= foo.m[2][0])) ixyz.v[0] = 2; //2nd column largest column else if ((foo.m[2][0] > foo.m[0][0]) && (foo.m[2][0] > foo.m[1][0])) ixyz.v[0] = 3; //3rd column largest column //ixyz.v[1] : row with largest value in column 2, but not the same row as ixyz.v[1] if (ixyz.v[0] == 1) { ixyz.v[1] = 2; if (foo.m[2][1] > foo.m[1][1]) ixyz.v[1] = 3; } else if (ixyz.v[0] == 2) { ixyz.v[1] = 1; if (foo.m[2][1] > foo.m[0][1]) ixyz.v[1] = 3; } else { //ixyz.v[0] == 3 ixyz.v[1] = 1; if (foo.m[1][1] > foo.m[0][1]) ixyz.v[1] = 2; } //ixyz.v[2] : 3rd row, constrained by previous rows ixyz.v[2] = 6 - ixyz.v[1] - ixyz.v[0];//sum of 1+2+3 return ixyz; } int sign(float x) { //returns -1,0,1 depending on if X is less than, equal to or greater than zero if (x < 0) return -1; else if (x > 0) return 1; return 0; } // Subfunction: get dicom xform matrix and related info // This is a direct port of Xiangrui Li's dicm2nii function mat44 xform_mat(struct TDICOMdata d) { vec3 readV = setVec3(d.orient[1],d.orient[2],d.orient[3]); vec3 phaseV = setVec3(d.orient[4],d.orient[5],d.orient[6]); vec3 sliceV = crossProduct(readV ,phaseV); mat33 R; LOAD_MAT33(R, readV.v[0], readV.v[1], readV.v[2], phaseV.v[0], phaseV.v[1], phaseV.v[2], sliceV.v[0], sliceV.v[1], sliceV.v[2]); R = nifti_mat33_transpose(R); //reportMat33((char*)"R",R); ivec3 ixyz = maxCol(R); //printMessage("%d %d %d\n", ixyz.v[0], ixyz.v[1], ixyz.v[2]); int iSL = ixyz.v[2]; // 1/2/3 for Sag/Cor/Tra slice float cosSL = R.m[iSL-1][2]; //printMessage("cosSL\t%g\n", cosSL); //vec3 pixdim = setVec3(d.xyzMM[1], d.xyzMM[2], d.xyzMM[3]); //printMessage("%g %g %g\n", pixdim.v[0], pixdim.v[1], pixdim.v[2]); mat33 pixdim; LOAD_MAT33(pixdim, d.xyzMM[1], 0.0, 0.0, 0.0, d.xyzMM[2], 0.0, 0.0, 0.0, d.xyzMM[3]); R = nifti_mat33_mul(R, pixdim); //reportMat33((char*)"R",R); mat44 R44; LOAD_MAT44(R44, R.m[0][0], R.m[0][1], R.m[0][2], d.patientPosition[1], R.m[1][0], R.m[1][1], R.m[1][2], d.patientPosition[2], R.m[2][0], R.m[2][1], R.m[2][2], d.patientPosition[3]); //reportMat44((char*)"R",R44); //rest are former: R = verify_slice_dir(R, s, dim, iSL) if ((d.xyzDim[3]<2) && (d.CSA.mosaicSlices < 2)) return R44; //don't care direction for single slice vec3 dim = setVec3(d.xyzDim[1], d.xyzDim[2], d.xyzDim[3]); if (d.CSA.mosaicSlices > 1) { //Siemens mosaic: use dim(1) since no transpose to img float nRowCol = ceil(sqrt((double) d.CSA.mosaicSlices)); dim.v[0] = dim.v[0] / nRowCol; dim.v[1] = dim.v[1] / nRowCol; dim.v[2] = d.CSA.mosaicSlices; vec4 dim4 = setVec4((nRowCol-1)*dim.v[0]/2.0f, (nRowCol-1)*dim.v[1]/2.0f, 0); vec4 offset = nifti_vect44mat44_mul(dim4, R44 ); //printMessage("%g %g %g\n", dim.v[0], dim.v[1], dim.v[2]); //printMessage("%g %g %g\n", dim4.v[0], dim4.v[1], dim4.v[2]); //printMessage("%g %g %g %g\n", offset.v[0], offset.v[1], offset.v[2], offset.v[3]); //printMessage("nRowCol\t%g\n", nRowCol); R44.m[0][3] = offset.v[0]; R44.m[1][3] = offset.v[1]; R44.m[2][3] = offset.v[2]; //R44.m[3][3] = offset.v[3]; if (sign(d.CSA.sliceNormV[iSL]) != sign(cosSL)) { R44.m[0][2] = -R44.m[0][2]; R44.m[1][2] = -R44.m[1][2]; R44.m[2][2] = -R44.m[2][2]; R44.m[3][2] = -R44.m[3][2]; } //reportMat44((char*)"iR44",R44); return R44; } else if (true) { //SliceNormalVector TO DO printMessage("Not completed"); exit(2); return R44; } printMessage("Unable to determine spatial transform\n"); exit(1); } mat44 set_nii_header(struct TDICOMdata d) { mat44 R = xform_mat(d); //R(1:2,:) = -R(1:2,:); % dicom LPS to nifti RAS, xform matrix before reorient for (int i=0; i<2; i++) for(int j=0; j<4; j++) R.m[i][j] = -R.m[i][j]; #ifdef MY_DEBUG reportMat44((char*)"R44",R); #endif } #endif // This code predates Xiangrui Li's set_nii_header function mat44 set_nii_header_x(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, int* sliceDir, int isVerbose) { *sliceDir = 0; mat44 Q44 = nifti_dicom2mat(d.orient, d.patientPosition, d.xyzMM); if (d.isSegamiOasis == true) { //Segami reconstructions appear to disregard DICOM spatial parameters: assume center of volume is isocenter and no table tilt // Consider sample image with d.orient (0020,0037) = -1 0 0; 0 1 0: this suggests image RAI (L->R, P->A, S->I) but the vendors viewing software suggests LPS //Perhaps we should ignore 0020,0037 and 0020,0032 as they are hidden in sequence 0054,0022, but in this case no positioning is provided // http://www.cs.ucl.ac.uk/fileadmin/cmic/Documents/DavidAtkinson/DICOM.pdf // https://www.slicer.org/wiki/Coordinate_systems LOAD_MAT44(Q44, -h->pixdim[1],0,0,0, 0,-h->pixdim[2],0,0, 0,0,h->pixdim[3],0); //X and Y dimensions flipped in NIfTI (RAS) vs DICOM (LPS) vec4 originVx = setVec4( (h->dim[1]+1.0f)/2.0f, (h->dim[2]+1.0f)/2.0f, (h->dim[3]+1.0f)/2.0f); vec4 originMm = nifti_vect44mat44_mul(originVx, Q44); for (int i = 0; i < 3; i++) Q44.m[i][3] = -originMm.v[i]; //set origin to center voxel if (isVerbose) { //printMessage("origin (vx) %g %g %g\n",originVx.v[0],originVx.v[1],originVx.v[2]); //printMessage("origin (mm) %g %g %g\n",originMm.v[0],originMm.v[1],originMm.v[2]); printWarning("Segami coordinates defy DICOM convention, please check orientation\n"); } return Q44; } if (d.CSA.mosaicSlices > 1) { double nRowCol = ceil(sqrt((double) d.CSA.mosaicSlices)); double lFactorX = (d.xyzDim[1] -(d.xyzDim[1]/nRowCol) )/2.0; double lFactorY = (d.xyzDim[2] -(d.xyzDim[2]/nRowCol) )/2.0; Q44.m[0][3] =(float)((Q44.m[0][0]*lFactorX)+(Q44.m[0][1]*lFactorY)+Q44.m[0][3]); Q44.m[1][3] = (float)((Q44.m[1][0] * lFactorX) + (Q44.m[1][1] * lFactorY) + Q44.m[1][3]); Q44.m[2][3] = (float)((Q44.m[2][0] * lFactorX) + (Q44.m[2][1] * lFactorY) + Q44.m[2][3]); for (int c=0; c<2; c++) for (int r=0; r<4; r++) Q44.m[c][r] = -Q44.m[c][r]; mat33 Q; LOAD_MAT33(Q, d.orient[1], d.orient[4],d.CSA.sliceNormV[1], d.orient[2],d.orient[5],d.CSA.sliceNormV[2], d.orient[3],d.orient[6],d.CSA.sliceNormV[3]); if (nifti_mat33_determ(Q) < 0) { //Siemens sagittal are R>>L, whereas NIfTI is L>>R, we retain Siemens order on disk so ascending is still ascending, but we need to have the spatial transform reflect this. mat44 det; *sliceDir = kSliceOrientMosaicNegativeDeterminant; //we need to handle DTI vectors accordingly LOAD_MAT44(det, 1.0l,0.0l,0.0l,0.0l, 0.0l,1.0l,0.0l,0.0l, 0.0l,0.0l,-1.0l,0.0l); //patient_to_tal.m[2][3] = 1-d.CSA.MosaicSlices; Q44 = nifti_mat44_mul(Q44,det); } } else { //not a mosaic *sliceDir = verify_slice_dir(d, d2, h, &Q44, isVerbose); for (int c=0; c<4; c++)// LPS to nifti RAS, xform matrix before reorient for (int r=0; r<2; r++) //swap rows 1 & 2 Q44.m[r][c] = - Q44.m[r][c]; } #ifdef MY_DEBUG reportMat44((char*)"Q44",Q44); #endif return Q44; } int headerDcm2NiiSForm(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, int isVerbose) { //fill header s and q form //see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c //returns sliceDir: 0=unknown,1=sag,2=coro,3=axial,-=reversed slices int sliceDir = 0; if (h->dim[3] < 2) { mat44 Q44 = set_nii_header_x(d, d2, h, &sliceDir, isVerbose); setQSForm(h,Q44, isVerbose); return sliceDir; //don't care direction for single slice } h->sform_code = NIFTI_XFORM_UNKNOWN; h->qform_code = NIFTI_XFORM_UNKNOWN; bool isOK = false; for (int i = 1; i <= 6; i++) if (d.orient[i] != 0.0) isOK = true; if (!isOK) { //we will have to guess, assume axial acquisition saved in standard Siemens style? d.orient[1] = 1.0f; d.orient[2] = 0.0f; d.orient[3] = 0.0f; d.orient[1] = 0.0f; d.orient[2] = 1.0f; d.orient[3] = 0.0f; if ((d.isDerived) || ((d.bitsAllocated == 8) && (d.samplesPerPixel == 3) && (d.manufacturer == kMANUFACTURER_SIEMENS))) { printMessage("Unable to determine spatial orientation: 0020,0037 missing (probably not a problem: derived image)\n"); } else { printMessage("Unable to determine spatial orientation: 0020,0037 missing!\n"); } } mat44 Q44 = set_nii_header_x(d, d2, h, &sliceDir, isVerbose); setQSForm(h,Q44, isVerbose); return sliceDir; } //headerDcm2NiiSForm() int headerDcm2Nii2(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, int isVerbose) { //final pass after de-mosaic char txt[1024] = {""}; if (h->slice_code == NIFTI_SLICE_UNKNOWN) h->slice_code = d.CSA.sliceOrder; if (h->slice_code == NIFTI_SLICE_UNKNOWN) h->slice_code = d2.CSA.sliceOrder; //sometimes the first slice order is screwed up https://github.com/eauerbach/CMRR-MB/issues/29 sprintf(txt, "TE=%.2g;Time=%.3f", d.TE,d.acquisitionTime);// d.dateTime); if (d.CSA.phaseEncodingDirectionPositive >= 0) { char dtxt[1024] = {""}; sprintf(dtxt, ";phase=%d", d.CSA.phaseEncodingDirectionPositive); strcat(txt,dtxt); } //from dicm2nii 20151117 InPlanePhaseEncodingDirection if (d.phaseEncodingRC =='R') h->dim_info = (3 << 4) + (1 << 2) + 2; if (d.phaseEncodingRC =='C') h->dim_info = (3 << 4) + (2 << 2) + 1; if (d.CSA.multiBandFactor > 1) { char dtxt[1024] = {""}; sprintf(dtxt, ";mb=%d", d.CSA.multiBandFactor); strcat(txt,dtxt); } snprintf(h->descrip,80, "%s",txt); if (strlen(d.imageComments) > 0) snprintf(h->aux_file,24,"%s",d.imageComments); return headerDcm2NiiSForm(d,d2, h, isVerbose); } //headerDcm2Nii2() int dcmStrLen (int len, int kMaxLen) { if (len < kMaxLen) return len+1; else return kMaxLen; } //dcmStrLen() struct TDICOMdata clear_dicom_data() { struct TDICOMdata d; //d.dti4D = NULL; d.locationsInAcquisition = 0; d.modality = kMODALITY_UNKNOWN; d.effectiveEchoSpacingGE = 0; for (int i=0; i < 4; i++) { d.CSA.dtiV[i] = 0; d.patientPosition[i] = NAN; //d.patientPosition2nd[i] = NAN; //used to distinguish XYZT vs XYTZ for Philips 4D d.patientPositionLast[i] = NAN; //used to compute slice direction for Philips 4D d.stackOffcentre[i] = NAN; d.angulation[i] = 0.0f; d.xyzMM[i] = 1; } for (int i=0; i < MAX_NUMBER_OF_DIMENSIONS; ++i) d.dimensionIndexValues[i] = 0; d.CSA.sliceTiming[0] = -1.0f; //impossible value denotes not known d.CSA.numDti = 0; for (int i=0; i < 5; i++) d.xyzDim[i] = 1; for (int i = 0; i < 7; i++) d.orient[i] = 0.0f; strcpy(d.patientName, ""); strcpy(d.patientID, ""); strcpy(d.imageType,""); strcpy(d.imageComments, ""); strcpy(d.studyDate, ""); strcpy(d.studyTime, ""); strcpy(d.protocolName, ""); strcpy(d.seriesDescription, ""); strcpy(d.sequenceName, ""); strcpy(d.scanningSequence, ""); strcpy(d.sequenceVariant, ""); strcpy(d.manufacturersModelName, ""); strcpy(d.institutionalDepartmentName, ""); strcpy(d.procedureStepDescription, ""); strcpy(d.institutionName, ""); strcpy(d.referringPhysicianName, ""); strcpy(d.institutionAddress, ""); strcpy(d.deviceSerialNumber, ""); strcpy(d.softwareVersions, ""); strcpy(d.stationName, ""); strcpy(d.scanOptions, ""); //strcpy(d.mrAcquisitionType, ""); strcpy(d.seriesInstanceUID, ""); strcpy(d.studyID, ""); strcpy(d.studyInstanceUID, ""); strcpy(d.bodyPartExamined,""); d.phaseEncodingLines = 0; d.patientPositionSequentialRepeats = 0; d.isHasPhase = false; d.isHasMagnitude = false; d.sliceOrient = kSliceOrientUnknown; d.dateTime = (double)19770703150928.0; d.acquisitionTime = 0.0f; d.acquisitionDate = 0.0f; d.manufacturer = kMANUFACTURER_UNKNOWN; d.isPlanarRGB = false; d.lastScanLoc = NAN; d.TR = 0.0; d.TE = 0.0; d.TI = 0.0; d.flipAngle = 0.0; d.bandwidthPerPixelPhaseEncode = 0.0; d.fieldStrength = 0.0; d.SAR = 0.0; d.pixelBandwidth = 0.0; d.zSpacing = 0.0; d.zThick = 0.0; d.numberOfDynamicScans = 0; d.echoNum = 1; d.echoTrainLength = 0; d.phaseFieldofView = 0.0; d.dwellTime = 0; d.phaseEncodingSteps = 0; d.coilNum = 0; d.accelFactPE = 0.0; d.patientPositionNumPhilips = 0; d.imageBytes = 0; d.intenScale = 1; d.intenScalePhilips = 0; d.intenIntercept = 0; d.gantryTilt = 0.0; d.radionuclidePositronFraction = 0.0; d.radionuclideTotalDose = 0.0; d.radionuclideHalfLife = 0.0; d.doseCalibrationFactor = 0.0; d.ecat_isotope_halflife = 0.0; d.ecat_dosage = 0.0; d.seriesNum = 1; d.acquNum = 0; d.imageNum = 1; d.imageStart = 0; d.is3DAcq = false; //e.g. MP-RAGE, SPACE, TFE d.is2DAcq = false; // d.isSlicesSpatiallySequentialPhilips = true; //Philips can save slices in random order, e.g. 4,5,6,1,2,3 d.isDerived = false; //0008,0008 = DERIVED,CSAPARALLEL,POSDISP d.isSegamiOasis = false; //these images do not store spatial coordinates d.bitsAllocated = 16;//bits d.bitsStored = 0; d.samplesPerPixel = 1; d.isValid = false; d.isXRay = false; d.isMultiEcho = false; d.isSigned = false; //default is unsigned! d.isFloat = false; //default is for integers, not single or double precision d.isResampled = false; //assume data not resliced to remove gantry tilt problems d.compressionScheme = 0; //none d.isExplicitVR = true; d.isLittleEndian = true; //DICOM initially always little endian d.converted2NII = 0; d.phaseEncodingRC = '?'; d.patientSex = '?'; d.patientWeight = 0.0; strcpy(d.patientBirthDate, ""); strcpy(d.patientAge, ""); d.CSA.bandwidthPerPixelPhaseEncode = 0.0; d.CSA.mosaicSlices = 0; d.CSA.sliceNormV[1] = 1.0; d.CSA.sliceNormV[2] = 0.0; d.CSA.sliceNormV[3] = 0.0; d.CSA.sliceOrder = NIFTI_SLICE_UNKNOWN; d.CSA.slice_start = 0; d.CSA.slice_end = 0; d.CSA.protocolSliceNumber1 = 0; d.CSA.phaseEncodingDirectionPositive = -1; //unknown d.CSA.isPhaseMap = false; d.CSA.multiBandFactor = 1; d.CSA.SeriesHeader_offset = 0; d.CSA.SeriesHeader_length = 0; return d; } //clear_dicom_data() void dcmStrDigitsOnlyKey(char key, char* lStr) { //e.g. string "p2s3" returns 2 if key=="p" and 3 if key=="s" size_t len = strlen(lStr); if (len < 1) return; bool isKey = false; for (int i = 0; i < (int) len; i++) { if (!isdigit(lStr[i]) ) { isKey = (lStr[i] == key); lStr[i] = ' '; } else if (!isKey) lStr[i] = ' '; } } //dcmStrDigitsOnlyKey() void dcmStrDigitsOnly(char* lStr) { //e.g. change "H11" to " 11" size_t len = strlen(lStr); if (len < 1) return; for (int i = 0; i < (int) len; i++) if (!isdigit(lStr[i]) ) lStr[i] = ' '; } void dcmStr(int lLength, unsigned char lBuffer[], char* lOut, bool isStrLarge = false) { //char test[] = " 1 2 3 "; //lLength = (int)strlen(test); if (lLength < 1) return; //#ifdef _MSC_VER char * cString = (char *)malloc(sizeof(char) * (lLength + 1)); //#else // char cString[lLength + 1]; //#endif cString[lLength] =0; memcpy(cString, (char*)&lBuffer[0], lLength); //memcpy(cString, test, lLength); //printMessage("X%dX\n", (unsigned char)d.patientName[1]); for (int i = 0; i < lLength; i++) //assume specificCharacterSet (0008,0005) is ISO_IR 100 http://en.wikipedia.org/wiki/ISO/IEC_8859-1 if (cString[i]< 1) { unsigned char c = (unsigned char)cString[i]; if ((c >= 192) && (c <= 198)) cString[i] = 'A'; if (c == 199) cString[i] = 'C'; if ((c >= 200) && (c <= 203)) cString[i] = 'E'; if ((c >= 204) && (c <= 207)) cString[i] = 'I'; if (c == 208) cString[i] = 'D'; if (c == 209) cString[i] = 'N'; if ((c >= 210) && (c <= 214)) cString[i] = 'O'; if (c == 215) cString[i] = 'x'; if (c == 216) cString[i] = 'O'; if ((c >= 217) && (c <= 220)) cString[i] = 'O'; if (c == 221) cString[i] = 'Y'; if ((c >= 224) && (c <= 230)) cString[i] = 'a'; if (c == 231) cString[i] = 'c'; if ((c >= 232) && (c <= 235)) cString[i] = 'e'; if ((c >= 236) && (c <= 239)) cString[i] = 'i'; if (c == 240) cString[i] = 'o'; if (c == 241) cString[i] = 'n'; if ((c >= 242) && (c <= 246)) cString[i] = 'o'; if (c == 248) cString[i] = 'o'; if ((c >= 249) && (c <= 252)) cString[i] = 'u'; if (c == 253) cString[i] = 'y'; if (c == 255) cString[i] = 'y'; } for (int i = 0; i < lLength; i++) if ((cString[i]<1) || (cString[i]==' ') || (cString[i]==',') || (cString[i]=='^') || (cString[i]=='/') || (cString[i]=='\\') || (cString[i]=='%') || (cString[i]=='*') || (cString[i] == 9) || (cString[i] == 10) || (cString[i] == 11) || (cString[i] == 13)) cString[i] = '_'; int len = 1; for (int i = 1; i < lLength; i++) { //remove repeated "_" if ((cString[i-1]!='_') || (cString[i]!='_')) { cString[len] =cString[i]; len++; } } //for each item if (cString[len-1] == '_') len--; //while ((len > 0) && (cString[len]=='_')) len--; //remove trailing '_' cString[len] = 0; //null-terminate, strlcpy does this anyway int maxLen = kDICOMStr; if (isStrLarge) maxLen = kDICOMStrLarge; len = dcmStrLen(len, maxLen); if (len == maxLen) { //we need space for null-termination if (cString[len-2] == '_') len = len -2; } memcpy(lOut,cString,len-1); lOut[len-1] = 0; //#ifdef _MSC_VER free(cString); //#endif } //dcmStr() inline bool littleEndianPlatform () { uint32_t value = 1; return (*((char *) &value) == 1); } float dcmFloat(int lByteLength, unsigned char lBuffer[], bool littleEndian) {//read binary 32-bit float //http://stackoverflow.com/questions/2782725/converting-float-values-from-big-endian-to-little-endian bool swap = (littleEndian != littleEndianPlatform()); float retVal = 0; if (lByteLength < 4) return retVal; memcpy(&retVal, (char*)&lBuffer[0], 4); if (!swap) return retVal; float swapVal; char *inFloat = ( char* ) & retVal; char *outFloat = ( char* ) & swapVal; outFloat[0] = inFloat[3]; outFloat[1] = inFloat[2]; outFloat[2] = inFloat[1]; outFloat[3] = inFloat[0]; //printMessage("swapped val = %f\n",swapVal); return swapVal; } //dcmFloat() double dcmFloatDouble(const size_t lByteLength, const unsigned char lBuffer[], const bool littleEndian) {//read binary 32-bit float //http://stackoverflow.com/questions/2782725/converting-float-values-from-big-endian-to-little-endian bool swap = (littleEndian != littleEndianPlatform()); double retVal = 0.0f; if (lByteLength < 8) return retVal; memcpy(&retVal, (char*)&lBuffer[0], 8); if (!swap) return retVal; char *floatToConvert = ( char* ) & lBuffer; char *returnFloat = ( char* ) & retVal; //swap the bytes into a temporary buffer returnFloat[0] = floatToConvert[7]; returnFloat[1] = floatToConvert[6]; returnFloat[2] = floatToConvert[5]; returnFloat[3] = floatToConvert[4]; returnFloat[4] = floatToConvert[3]; returnFloat[5] = floatToConvert[2]; returnFloat[6] = floatToConvert[1]; returnFloat[7] = floatToConvert[0]; //printMessage("swapped val = %f\n",retVal); return retVal; } //dcmFloatDouble() int dcmInt (int lByteLength, unsigned char lBuffer[], bool littleEndian) { //read binary 16 or 32 bit integer if (littleEndian) { if (lByteLength <= 3) return lBuffer[0] | (lBuffer[1]<<8); //shortint vs word? return lBuffer[0]+(lBuffer[1]<<8)+(lBuffer[2]<<16)+(lBuffer[3]<<24); //shortint vs word? } if (lByteLength <= 3) return lBuffer[1] | (lBuffer[0]<<8); //shortint vs word? return lBuffer[3]+(lBuffer[2]<<8)+(lBuffer[1]<<16)+(lBuffer[0]<<24); //shortint vs word? } //dcmInt() int dcmStrInt (const int lByteLength, const unsigned char lBuffer[]) {//read float stored as a string //#ifdef _MSC_VER char * cString = (char *)malloc(sizeof(char) * (lByteLength + 1)); //#else // char cString[lByteLength + 1]; //#endif cString[lByteLength] =0; memcpy(cString, (const unsigned char*)(&lBuffer[0]), lByteLength); //printMessage(" --> *%s* %s%s\n",cString, &lBuffer[0],&lBuffer[1]); int ret = atoi(cString); //#ifdef _MSC_VER free(cString); //#endif return ret; } //dcmStrInt() int dcmStrManufacturer (const int lByteLength, unsigned char lBuffer[]) {//read float stored as a string if (lByteLength < 2) return kMANUFACTURER_UNKNOWN; //#ifdef _MSC_VER char * cString = (char *)malloc(sizeof(char) * (lByteLength + 1)); //#else // char cString[lByteLength + 1]; //#endif int ret = kMANUFACTURER_UNKNOWN; cString[lByteLength] =0; memcpy(cString, (char*)&lBuffer[0], lByteLength); //printMessage("MANU %s\n",cString); if ((toupper(cString[0])== 'S') && (toupper(cString[1])== 'I')) ret = kMANUFACTURER_SIEMENS; if ((toupper(cString[0])== 'G') && (toupper(cString[1])== 'E')) ret = kMANUFACTURER_GE; if ((toupper(cString[0])== 'P') && (toupper(cString[1])== 'H')) ret = kMANUFACTURER_PHILIPS; if ((toupper(cString[0])== 'T') && (toupper(cString[1])== 'O')) ret = kMANUFACTURER_TOSHIBA; //#ifdef _MSC_VER free(cString); //#endif return ret; } //dcmStrManufacturer float csaMultiFloat (unsigned char buff[], int nItems, float Floats[], int *ItemsOK) { //warning: lFloats indexed from 1! will fill lFloats[1]..[nFloats] //if lnItems == 1, returns first item, if lnItems > 1 returns index of final successful conversion TCSAitem itemCSA; *ItemsOK = 0; if (nItems < 1) return 0.0f; Floats[1] = 0; int lPos = 0; for (int lI = 1; lI <= nItems; lI++) { memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA)); lPos +=sizeof(itemCSA); // Storage order is always little-endian, so byte-swap required values if necessary if (!littleEndianPlatform()) nifti_swap_4bytes(1, &itemCSA.xx2_Len); if (itemCSA.xx2_Len > 0) { char * cString = (char *)malloc(sizeof(char) * (itemCSA.xx2_Len)); memcpy(cString, &buff[lPos], itemCSA.xx2_Len); //TPX memcpy(&cString, &buff[lPos], sizeof(cString)); lPos += ((itemCSA.xx2_Len +3)/4)*4; //printMessage(" %d item length %d = %s\n",lI, itemCSA.xx2_Len, cString); Floats[lI] = (float) atof(cString); *ItemsOK = lI; //some sequences have store empty items free(cString); } } //for each item return Floats[1]; } //csaMultiFloat() bool csaIsPhaseMap (unsigned char buff[], int nItems) { //returns true if the tag "ImageHistory" has an item named "CC:ComplexAdd" TCSAitem itemCSA; if (nItems < 1) return false; int lPos = 0; for (int lI = 1; lI <= nItems; lI++) { memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA)); lPos +=sizeof(itemCSA); // Storage order is always little-endian, so byte-swap required values if necessary if (!littleEndianPlatform()) nifti_swap_4bytes(1, &itemCSA.xx2_Len); if (itemCSA.xx2_Len > 0) { //#ifdef _MSC_VER char * cString = (char *)malloc(sizeof(char) * (itemCSA.xx2_Len + 1)); //#else // char cString[itemCSA.xx2_Len]; //#endif memcpy(cString, &buff[lPos], sizeof(itemCSA.xx2_Len)); //TPX memcpy(&cString, &buff[lPos], sizeof(cString)); lPos += ((itemCSA.xx2_Len +3)/4)*4; //printMessage(" %d item length %d = %s\n",lI, itemCSA.xx2_Len, cString); if (strcmp(cString, "CC:ComplexAdd") == 0) return true; //#ifdef _MSC_VER free(cString); //#endif } } //for each item return false; } //csaIsPhaseMap() //int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, int isVerbose, struct TDTI4D *dti4D) { int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, int isVerbose) { //see also http://afni.nimh.nih.gov/pub/dist/src/siemens_dicom_csa.c //printMessage("%c%c%c%c\n",buff[0],buff[1],buff[2],buff[3]); if (lLength < 36) return EXIT_FAILURE; if ((buff[0] != 'S') || (buff[1] != 'V') || (buff[2] != '1') || (buff[3] != '0') ) return EXIT_FAILURE; int lPos = 8; //skip 8 bytes of data, 'SV10' plus 2 32-bit values unused1 and unused2 int lnTag = buff[lPos]+(buff[lPos+1]<<8)+(buff[lPos+2]<<16)+(buff[lPos+3]<<24); if (buff[lPos+4] != 77) return EXIT_FAILURE; lPos += 8; //skip 8 bytes of data, 32-bit lnTag plus 77 00 00 0 TCSAtag tagCSA; TCSAitem itemCSA; int itemsOK; float lFloats[7]; for (int lT = 1; lT <= lnTag; lT++) { memcpy(&tagCSA, &buff[lPos], sizeof(tagCSA)); //read tag lPos +=sizeof(tagCSA); // Storage order is always little-endian, so byte-swap required values if necessary if (!littleEndianPlatform()) nifti_swap_4bytes(1, &tagCSA.nitems); if (isVerbose > 1) //extreme verbosity: show every CSA tag printMessage("%d CSA of %s %d\n",lPos, tagCSA.name, tagCSA.nitems); if (tagCSA.nitems > 0) { if (strcmp(tagCSA.name, "ImageHistory") == 0) CSA->isPhaseMap = csaIsPhaseMap(&buff[lPos], tagCSA.nitems); else if (strcmp(tagCSA.name, "NumberOfImagesInMosaic") == 0) CSA->mosaicSlices = (int) round(csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK)); else if (strcmp(tagCSA.name, "B_value") == 0) { CSA->dtiV[0] = csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK); if (CSA->dtiV[0] < 0.0) { printWarning("(Corrupt) CSA reports negative b-value! %g\n",CSA->dtiV[0]); CSA->dtiV[0] = 0.0; } CSA->numDti = 1; //triggered by b-value, as B0 images do not have DiffusionGradientDirection tag } else if ((strcmp(tagCSA.name, "DiffusionGradientDirection") == 0) && (tagCSA.nitems > 2)){ CSA->dtiV[1] = csaMultiFloat (&buff[lPos], 3,lFloats, &itemsOK); CSA->dtiV[2] = lFloats[2]; CSA->dtiV[3] = lFloats[3]; if (isVerbose) printMessage("DiffusionGradientDirection %f %f %f\n",lFloats[1],lFloats[2],lFloats[3]); } else if ((strcmp(tagCSA.name, "SliceNormalVector") == 0) && (tagCSA.nitems > 2)){ CSA->sliceNormV[1] = csaMultiFloat (&buff[lPos], 3,lFloats, &itemsOK); CSA->sliceNormV[2] = lFloats[2]; CSA->sliceNormV[3] = lFloats[3]; if (isVerbose) printMessage("SliceNormalVector %f %f %f\n",CSA->sliceNormV[1],CSA->sliceNormV[2],CSA->sliceNormV[3]); } else if (strcmp(tagCSA.name, "SliceMeasurementDuration") == 0) CSA->sliceMeasurementDuration = csaMultiFloat (&buff[lPos], 3,lFloats, &itemsOK); else if (strcmp(tagCSA.name, "BandwidthPerPixelPhaseEncode") == 0) CSA->bandwidthPerPixelPhaseEncode = csaMultiFloat (&buff[lPos], 3,lFloats, &itemsOK); else if ((strcmp(tagCSA.name, "MosaicRefAcqTimes") == 0) && (tagCSA.nitems > 3) ){ float * sliceTimes = (float *)malloc(sizeof(float) * (tagCSA.nitems + 1)); csaMultiFloat (&buff[lPos], tagCSA.nitems,sliceTimes, &itemsOK); float maxTimeValue, minTimeValue, timeValue1; for (int z = 0; z < kMaxEPI3D; z++) CSA->sliceTiming[z] = -1.0; if (itemsOK <= kMaxEPI3D) { for (int z = 1; z <= itemsOK; z++) CSA->sliceTiming[z-1] = sliceTimes[z]; } else printError("Please increase kMaxEPI3D and recompile\n"); CSA->multiBandFactor = 1; timeValue1 = sliceTimes[1]; int nTimeZero = 0; if (sliceTimes[1] == 0) nTimeZero++; int minTimeIndex = 1; int maxTimeIndex = minTimeIndex; minTimeValue = sliceTimes[1]; maxTimeValue = minTimeValue; if (isVerbose) printMessage("sliceTimes %g\t", sliceTimes[1]); for (int z = 2; z <= itemsOK; z++) { //find index and value of fastest time if (isVerbose) printMessage("%g\t", sliceTimes[z]); if (sliceTimes[z] == 0) nTimeZero++; if (sliceTimes[z] < minTimeValue) { minTimeValue = sliceTimes[z]; minTimeIndex = (float) z; } if (sliceTimes[z] > maxTimeValue) { maxTimeValue = sliceTimes[z]; maxTimeIndex = (float) z; } if (sliceTimes[z] == timeValue1) CSA->multiBandFactor++; } if (isVerbose) printMessage("\n"); CSA->slice_start = minTimeIndex -1; CSA->slice_end = maxTimeIndex -1; if (minTimeIndex == 2) CSA->sliceOrder = NIFTI_SLICE_ALT_INC2;// e.g. 3,1,4,2 else if (minTimeIndex == (itemsOK-1)) CSA->sliceOrder = NIFTI_SLICE_ALT_DEC2;// e.g. 2,4,1,3 or 5,2,4,1,3 else if ((minTimeIndex == 1) && (sliceTimes[2] < sliceTimes[3])) CSA->sliceOrder = NIFTI_SLICE_SEQ_INC; // e.g. 1,2,3,4 else if ((minTimeIndex == 1) && (sliceTimes[2] > sliceTimes[3])) CSA->sliceOrder = NIFTI_SLICE_ALT_INC; //e.g. 1,3,2,4 else if ((minTimeIndex == itemsOK) && (sliceTimes[itemsOK-2] > sliceTimes[itemsOK-1])) CSA->sliceOrder = NIFTI_SLICE_SEQ_DEC; //e.g. 4,3,2,1 or 5,4,3,2,1 else if ((minTimeIndex == itemsOK) && (sliceTimes[itemsOK-2] < sliceTimes[itemsOK-1])) CSA->sliceOrder = NIFTI_SLICE_ALT_DEC; //e.g. 4,2,3,1 or 3,5,2,4,1 else { /*NSMutableArray *sliceTimesNS = [NSMutableArray arrayWithCapacity:tagCSA.nitems]; for (int z = 1; z <= itemsOK; z++) [sliceTimesNS addObject:[NSNumber numberWithFloat:sliceTimes[z]]]; NSLog(@" Warning: unable to determine slice order for %lu slice mosaic: %@",(unsigned long)[sliceTimesNS count],sliceTimesNS ); */ printWarning("Unable to determine slice order from CSA tag MosaicRefAcqTimes\n"); } if ((CSA->sliceOrder != NIFTI_SLICE_UNKNOWN) && (nTimeZero > 1)) { if (isVerbose) printMessage(" Multiband x%d sequence: setting slice order as UNKNOWN (instead of %d)\n", nTimeZero, CSA->sliceOrder); CSA->sliceOrder = NIFTI_SLICE_UNKNOWN; } //#ifdef _MSC_VER free(sliceTimes); //#endif } else if (strcmp(tagCSA.name, "ProtocolSliceNumber") == 0) CSA->protocolSliceNumber1 = (int) round (csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK)); else if (strcmp(tagCSA.name, "PhaseEncodingDirectionPositive") == 0) CSA->phaseEncodingDirectionPositive = (int) round (csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK)); for (int lI = 1; lI <= tagCSA.nitems; lI++) { memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA)); lPos +=sizeof(itemCSA); // Storage order is always little-endian, so byte-swap required values if necessary if (!littleEndianPlatform()) nifti_swap_4bytes(1, &itemCSA.xx2_Len); lPos += ((itemCSA.xx2_Len +3)/4)*4; } } //if at least 1 item }// for lT 1..lnTag if (CSA->protocolSliceNumber1 > 1) CSA->sliceOrder = NIFTI_SLICE_UNKNOWN; return EXIT_SUCCESS; } // readCSAImageHeader() void dcmMultiShorts (int lByteLength, unsigned char lBuffer[], int lnShorts, uint16_t *lShorts, bool littleEndian) { //read array of unsigned shorts US http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_6.2.html if ((lnShorts < 1) || (lByteLength != (lnShorts * 2))) return; memcpy(&lShorts[0], (uint16_t *)&lBuffer[0], lByteLength); bool swap = (littleEndian != littleEndianPlatform()); if (swap) nifti_swap_2bytes(lnShorts, &lShorts[0]); } //dcmMultiShorts() void dcmMultiLongs (int lByteLength, unsigned char lBuffer[], int lnLongs, uint32_t *lLongs, bool littleEndian) { //read array of unsigned longs UL http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_6.2.html if((lnLongs < 1) || (lByteLength != (lnLongs * 4))) return; memcpy(&lLongs[0], (uint32_t *)&lBuffer[0], lByteLength); bool swap = (littleEndian != littleEndianPlatform()); if (swap) nifti_swap_4bytes(lnLongs, &lLongs[0]); } //dcmMultiLongs() void dcmMultiFloat (int lByteLength, char lBuffer[], int lnFloats, float *lFloats) { //warning: lFloats indexed from 1! will fill lFloats[1]..[nFloats] if ((lnFloats < 1) || (lByteLength < 1)) return; //#ifdef _MSC_VER char * cString = (char *)malloc(sizeof(char) * (lByteLength + 1)); //#else // char cString[lByteLength + 1]; //#endif memcpy(cString, (char*)&lBuffer[0], lByteLength); cString[lByteLength] = 0; //null terminate char *temp=( char *)malloc(lByteLength+1); int f = 0,lStart = 0; bool isOK = false; for (int i = 0; i <= lByteLength; i++) { if ((lBuffer[i] >= '0') && (lBuffer[i] <= '9')) isOK = true; if ((isOK) && ((i == (lByteLength)) || (lBuffer[i] == '/') || (lBuffer[i] == ' ') || (lBuffer[i] == '\\') )){ //x strlcpy(temp,&cString[lStart],i-lStart+1); snprintf(temp,i-lStart+1,"%s",&cString[lStart]); //printMessage("dcmMultiFloat %s\n",temp); if (f < lnFloats) { f ++; lFloats[f] = (float) atof(temp); isOK = false; //printMessage("%d == %f\n", f, atof(temp)); } //if f <= nFloats lStart = i+1; } //if isOK } //for i to length free(temp); //#ifdef _MSC_VER free(cString); //#endif } //dcmMultiFloat() float dcmStrFloat (const int lByteLength, const unsigned char lBuffer[]) { //read float stored as a string //#ifdef _MSC_VER char * cString = (char *)malloc(sizeof(char) * (lByteLength + 1)); //#else // char cString[lByteLength + 1]; //#endif memcpy(cString, (char*)&lBuffer[0], lByteLength); cString[lByteLength] = 0; //null terminate float ret = (float) atof(cString); //#ifdef _MSC_VER free(cString); //#endif return ret; } //dcmStrFloat() int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h, bool isComputeSForm) { //printMessage("bytes %dx%dx%d %d, %d\n",d.XYZdim[1],d.XYZdim[2],d.XYZdim[3], d.Allocbits_per_pixel, d.samplesPerPixel); memset(h, 0, sizeof(nifti_1_header)); //zero-fill structure so unused items are consistent for (int i = 0; i < 80; i++) h->descrip[i] = 0; for (int i = 0; i < 24; i++) h->aux_file[i] = 0; for (int i = 0; i < 18; i++) h->db_name[i] = 0; for (int i = 0; i < 10; i++) h->data_type[i] = 0; for (int i = 0; i < 16; i++) h->intent_name[i] = 0; if ((d.bitsAllocated == 8) && (d.samplesPerPixel == 3)) { h->intent_code = NIFTI_INTENT_ESTIMATE; //make sure we treat this as RGBRGB...RGB h->datatype = DT_RGB24; } else if ((d.bitsAllocated == 8) && (d.samplesPerPixel == 1)) h->datatype = DT_UINT8; else if ((d.bitsAllocated == 12) && (d.samplesPerPixel == 1)) h->datatype = DT_INT16; else if ((d.bitsAllocated == 16) && (d.samplesPerPixel == 1) && (d.isSigned)) h->datatype = DT_INT16; else if ((d.bitsAllocated == 16) && (d.samplesPerPixel == 1) && (!d.isSigned)) h->datatype = DT_UINT16; else if ((d.bitsAllocated == 32) && (d.isFloat)) h->datatype = DT_FLOAT32; else if (d.bitsAllocated == 32) h->datatype = DT_INT32; else if ((d.bitsAllocated == 64) && (d.isFloat)) h->datatype = DT_FLOAT64; else { printMessage("Unsupported DICOM bit-depth %d with %d samples per pixel\n",d.bitsAllocated,d.samplesPerPixel); return EXIT_FAILURE; } if ((h->datatype == DT_UINT16) && (d.bitsStored > 0) &&(d.bitsStored < 16)) h->datatype = DT_INT16; // DT_INT16 is more widely supported, same represenation for values 0..32767 for (int i = 0; i < 8; i++) { h->pixdim[i] = 0.0f; h->dim[i] = 0; } //next items listed as unused in NIfTI format, but zeroed for consistency across runs h->extents = 0; h->session_error = 0; h->glmin = 0; //unused, but make consistent h->glmax = 0; //unused, but make consistent h->regular = 114; //in legacy Analyze this was always 114 //these are important h->scl_inter = d.intenIntercept; h->scl_slope = d.intenScale; h->cal_max = 0; h->cal_min = 0; h->magic[0]='n'; h->magic[1]='+'; h->magic[2]='1'; h->magic[3]='\0'; h->vox_offset = (float) d.imageStart; if (d.bitsAllocated == 12) h->bitpix = 16 * d.samplesPerPixel; else h->bitpix = d.bitsAllocated * d.samplesPerPixel; h->pixdim[1] = d.xyzMM[1]; h->pixdim[2] = d.xyzMM[2]; h->pixdim[3] = d.xyzMM[3]; h->pixdim[4] = d.TR/1000; //TR reported in msec, time is in sec h->dim[1] = d.xyzDim[1]; h->dim[2] = d.xyzDim[2]; h->dim[3] = d.xyzDim[3]; h->dim[4] = d.xyzDim[4]; if (h->dim[4] < 2) h->dim[0] = 3; else h->dim[0] = 4; for (int i = 0; i <= 3; i++) { h->srow_x[i] = 0.0f; h->srow_y[i] = 0.0f; h->srow_z[i] = 0.0f; } h->slice_start = 0; h->slice_end = 0; h->srow_x[0] = -1; h->srow_y[2] = 1; h->srow_z[1] = -1; h->srow_x[3] = ((float) h->dim[1] / 2); h->srow_y[3] = -((float)h->dim[3] / 2); h->srow_z[3] = ((float)h->dim[2] / 2); h->qform_code = NIFTI_XFORM_UNKNOWN; h->sform_code = NIFTI_XFORM_SCANNER_ANAT; h->toffset = 0; h->intent_code = NIFTI_INTENT_NONE; h->dim_info = 0; //Freq, Phase and Slice all unknown h->xyzt_units = NIFTI_UNITS_MM + NIFTI_UNITS_SEC; h->slice_duration = 0; //avoid +inf/-inf, NaN h->intent_p1 = 0; //avoid +inf/-inf, NaN h->intent_p2 = 0; //avoid +inf/-inf, NaN h->intent_p3 = 0; //avoid +inf/-inf, NaN h->pixdim[0] = 1; //QFactor should be 1 or -1 h->sizeof_hdr = 348; //used to signify header does not need to be byte-swapped h->slice_code = d.CSA.sliceOrder; if (isComputeSForm) headerDcm2Nii2(d, d, h, false); return EXIT_SUCCESS; } // headerDcm2Nii() bool isFloatDiff (float a, float b) { return (fabs (a - b) > FLT_EPSILON); } //isFloatDiff() mat33 nifti_mat33_reorder_cols( mat33 m, ivec3 v ) { // matlab equivalent ret = m(:, v); where v is 1,2,3 [INDEXED FROM ONE!!!!] mat33 ret; for (int r=0; r<3; r++) { for(int c=0; c<3; c++) ret.m[r][c] = m.m[r][v.v[c]-1]; } return ret; } //nifti_mat33_reorder_cols() void changeExt (char *file_name, const char* ext) { char *p_extension; p_extension = strrchr(file_name, '.'); if (p_extension) { strcpy(++p_extension, ext); } } //changeExt() struct TDICOMdata nii_readParRec (char * parname, int isVerbose, struct TDTI4D *dti4D) { struct TDICOMdata d = clear_dicom_data(); strcpy(d.protocolName, ""); //erase dummy with empty strcpy(d.seriesDescription, ""); //erase dummy with empty strcpy(d.sequenceName, ""); //erase dummy with empty strcpy(d.scanningSequence, ""); FILE *fp = fopen(parname, "r"); if (fp == NULL) return d; #define LINESZ 2048 #define kSlice 0 #define kEcho 1 #define kDyn 2 #define kCardiac 3 #define kImageType 4 #define kSequence 5 #define kIndex 6 #define kBitsPerVoxel 7 #define kXdim 9 #define kYdim 10 #define kRI 11 #define kRS 12 #define kSS 13 #define kAngulationAPs 16 //In V4, offcentre and Angulation labeled as y z x, but actually x y z! #define kAngulationFHs 17 #define kAngulationRLs 18 #define kPositionAP 19 #define kPositionFH 20 #define kPositionRL 21 #define kThickmm 22 #define kGapmm 23 #define kSliceOrients 25 #define kXmm 28 #define kYmm 29 #define kTEcho 30 #define kDynTime 31 #define kbval 33 #define kInversionDelayMs 40 #define kGradientNumber 42 #define kv1 47 #define kv2 45 #define kv3 46 #define kASL 48 char buff[LINESZ]; int sliceNumberMrPhilipsB2[kMaxDTI4D], sliceNumberMrPhilipsVol2[kMaxDTI4D]; int patientPositionNumPhilipsB2 = 0; int patientPositionNumPhilipsVol2 = 0; //float intenScalePhilips = 0.0f; float maxBValue = 0.0f; float maxDynTime = 0.0f; float minDynTime = 999999.0f; int minDyn = 32767; int maxDyn = 0; bool ADCwarning = false; int prevDyn = -1; bool dynNotAscending = false; int parVers = 0; int maxEcho = 1; int maxCardiac = 1; int nCols = 26; int slice = 0; //int prevSliceIndex = 0; //index of prior slice: detect if images are not in order const int kMaxCols = 49; float *cols = (float *)malloc(sizeof(float) * kMaxCols); char *p = fgets (buff, LINESZ, fp); bool isIntenScaleVaries = false; bool isIndexSequential = true; for (int i = 0; i < kMaxDTI4D; i++) dti4D->S[i].V[0] = -1.0; //d.dti4D = (TDTI *)malloc(kMaxDTI4D * sizeof(TDTI)); while (p) { if (strlen(buff) < 1) continue; if (buff[0] == '#') { //comment char Comment[7][50]; sscanf(buff, "# %s %s\n", Comment[0], Comment[1]); if (strcmp(Comment[1], "TRYOUT") == 0) { sscanf(buff, "# %s %s %s %s %s %s V%s\n", Comment[0], Comment[1], Comment[2], Comment[3] ,Comment[4], Comment[5],Comment[6]); parVers = (int)round(atof(Comment[6])*10); //4.2 = 42 etc if (parVers < 40) { printMessage("This software is unable to convert ancient PAR files: please use legacy dcm2nii\n"); return d; //nCols = 26; //e.g. PAR 3.0 has 26 relevant columns } else if (parVers < 41) nCols = 32; //e.g PAR 4.0 else if (parVers < 42) nCols = kv1; //e.g. PAR 4.1 - last column is final diffusion b-value else nCols = kMaxCols; //e.g. PAR 4.2 } p = fgets (buff, LINESZ, fp);//get next line continue; } //process '#' comment if (buff[0] == '.') { //tag char Comment[8][50]; sscanf(buff, ". %s %s %s %s %s %s %s %s\n", Comment[0], Comment[1],Comment[2], Comment[3], Comment[4], Comment[5], Comment[6], Comment[7]); if ((strcmp(Comment[0], "Acquisition") == 0) && (strcmp(Comment[1], "nr") == 0)) { d.acquNum = atoi( Comment[3]); d.seriesNum = d.acquNum; } if ((strcmp(Comment[0], "Repetition") == 0) && (strcmp(Comment[1], "time") == 0)) d.TR = (float) atof(Comment[4]); if ((strcmp(Comment[0], "Patient") == 0) && (strcmp(Comment[1], "name") == 0)) { strcpy(d.patientName, Comment[3]); strcat(d.patientName, Comment[4]); strcat(d.patientName, Comment[5]); strcat(d.patientName, Comment[6]); strcat(d.patientName, Comment[7]); //printMessage("%s\n",d.patientName); } if ((strcmp(Comment[0], "Protocol") == 0) && (strcmp(Comment[1], "name") == 0)) { strcpy(d.protocolName, Comment[3]); strcat(d.protocolName, Comment[4]); strcat(d.protocolName, Comment[5]); strcat(d.protocolName, Comment[6]); strcat(d.protocolName, Comment[7]); //printMessage("%s\n",d.protocolName); } if ((strcmp(Comment[0], "Examination") == 0) && (strcmp(Comment[1], "date/time") == 0)) { strcpy(d.studyDate, Comment[3]); strcpy(d.studyTime, Comment[5]); //to do convert to traditional DICOM style date time } if ((strcmp(Comment[0], "Off") == 0) && (strcmp(Comment[1], "Centre") == 0)) { //Off Centre midslice(ap,fh,rl) [mm] d.stackOffcentre[2] = (float) atof(Comment[5]); d.stackOffcentre[3] = (float) atof(Comment[6]); d.stackOffcentre[1] = (float) atof(Comment[7]); } if ((strcmp(Comment[0], "Patient") == 0) && (strcmp(Comment[1], "position") == 0)) { //Off Centre midslice(ap,fh,rl) [mm] d.patientOrient[0] = toupper(Comment[3][0]); d.patientOrient[1] = toupper(Comment[4][0]); d.patientOrient[2] = toupper(Comment[5][0]); d.patientOrient[3] = 0; } if ((strcmp(Comment[0], "Max.") == 0) && (strcmp(Comment[3], "slices/locations") == 0)) { d.xyzDim[3] = atoi(Comment[5]); } p = fgets (buff, LINESZ, fp);//get next line continue; } //process '.' tag if (strlen(buff) < 24) { //empty line p = fgets (buff, LINESZ, fp);//get next line continue; } if (parVers < 20) { printError("PAR files should have 'CLINICAL TRYOUT' line with a version from 2.0-4.2: %s\n", parname); free (cols); return d; } for (int i = 0; i <= nCols; i++) cols[i] = strtof(p, &p); // p+1 skip comma, read a float if ((cols[kIndex]) != slice) isIndexSequential = false; //slices 0,1,2.. should have indices 0,1,2,3... slice ++; if (slice == 1) { //for (int i = 0; i < nCols; i++) // cols1[i] = cols[i]; //store first slice to see if dimensions or intensity scale varies between slices //for (int i = 0; i < nCols; i++) // printMessage("%d %g\n",i, cols[i]); //store first slice to see if dimensions or intensity scale varies between slices d.xyzDim[1] = (int) cols[kXdim]; d.xyzDim[2] = (int) cols[kYdim]; d.xyzMM[1] = cols[kXmm]; d.xyzMM[2] = cols[kYmm]; d.xyzMM[3] = cols[kThickmm] + cols[kGapmm]; d.patientPosition[1] = cols[kPositionRL]; d.patientPosition[2] = cols[kPositionAP]; d.patientPosition[3] = cols[kPositionFH]; d.angulation[1] = cols[kAngulationRLs]; d.angulation[2] = cols[kAngulationAPs]; d.angulation[3] = cols[kAngulationFHs]; d.sliceOrient = (int) cols[kSliceOrients]; d.TE = cols[kTEcho]; d.TI = cols[kInversionDelayMs]; d.bitsAllocated = (int) cols[kBitsPerVoxel]; d.bitsStored = (int) cols[kBitsPerVoxel]; d.intenIntercept = cols[kRI]; d.intenScale = cols[kRS]; d.intenScalePhilips = cols[kSS]; } else { if ((d.xyzDim[1] != cols[kXdim]) || (d.xyzDim[2] != cols[kYdim]) || (d.bitsAllocated != cols[kBitsPerVoxel]) ) { printError("Slice dimensions or bit depth varies %s\n", parname); return d; } if ((d.patientPositionSequentialRepeats == 0) && ((!isSameFloat(d.patientPosition[1],cols[kPositionRL])) || (!isSameFloat(d.patientPosition[2],cols[kPositionAP])) || (!isSameFloat(d.patientPosition[3],cols[kPositionFH])) ) )//this is the first slice with different position d.patientPositionSequentialRepeats = slice-1; if ((d.intenScale != cols[kRS]) || (d.intenIntercept != cols[kRI])) isIntenScaleVaries = true; } if (cols[kImageType] == 0) d.isHasMagnitude = true; if (cols[kImageType] != 0) d.isHasPhase = true; if (cols[kDyn] > maxDyn) maxDyn = (int) cols[kDyn]; if (cols[kDyn] < minDyn) minDyn = (int) cols[kDyn]; if (cols[kDyn] < prevDyn) dynNotAscending = true; prevDyn = cols[kDyn]; if (cols[kDynTime] > maxDynTime) maxDynTime = cols[kDynTime]; if (cols[kDynTime] < minDynTime) minDynTime = cols[kDynTime]; if (cols[kEcho] > maxEcho) maxEcho = cols[kEcho]; if (cols[kCardiac] > maxCardiac) maxCardiac = cols[kCardiac]; if ((cols[kEcho] == 1) && (cols[kDyn] == 1) && (patientPositionNumPhilipsB2 < kMaxDTI4D) && (cols[kCardiac] == 1) && (cols[kGradientNumber] == 2)) { sliceNumberMrPhilipsB2[patientPositionNumPhilipsB2] = round(cols[kSlice]); patientPositionNumPhilipsB2++; } if ((cols[kEcho] == 1) && (cols[kDyn] == 2) && (patientPositionNumPhilipsVol2 < kMaxDTI4D) && (cols[kCardiac] == 1) && (cols[kGradientNumber] == 1)) { sliceNumberMrPhilipsVol2[patientPositionNumPhilipsVol2] = round(cols[kSlice]); patientPositionNumPhilipsVol2++; } if ((cols[kEcho] == 1) && (cols[kDyn] == 1) && (cols[kCardiac] == 1) && (cols[kGradientNumber] == 1)) { if (cols[kSlice] == 1) { d.patientPosition[1] = cols[kPositionRL]; d.patientPosition[2] = cols[kPositionAP]; d.patientPosition[3] = cols[kPositionFH]; } if (d.patientPositionNumPhilips < kMaxDTI4D) { dti4D->S[d.patientPositionNumPhilips].sliceNumberMrPhilips = round(cols[kSlice]); if ((d.patientPositionNumPhilips > 0) && (dti4D->S[d.patientPositionNumPhilips].sliceNumberMrPhilips < dti4D->S[d.patientPositionNumPhilips-1].sliceNumberMrPhilips)) { d.isSlicesSpatiallySequentialPhilips = false; //printMessage("slices are not contiguous\n"); } } d.patientPositionNumPhilips++; } if (cols[kGradientNumber] > 0) { /*int dir = (int) cols[kGradientNumber]; if ((dir > 0) && (cols[kbval] > 0.0) && (cols[kv1] == 0.0) && (cols[kv1] == 0.0) && (cols[kv1] == 0.0) ) { if (dti4D->S[dir-1].V[0] >= 0) dir = dir + 1; //Philips often stores an ADC map along with B0 and weighted images, unfortunately they give it the same kGradientNumber as the B0! (seen in PAR V4.2) //the logic here is that IF the gradient was previously used we increment the gradient number. This should provide compatibility when Philips fixes this bug //it seems like the ADC is always saved as the final volume, so this solution SHOULD be foolproof. ADCwarning = true; }*/ //666: test (cols[kDyn] == 1) //(cols[kImageType] == 0) means magnitude scan if ((cols[kImageType] == 0) && (cols[kDyn] == 1) && (cols[kEcho] == 1) && (cols[kCardiac] == 1) && (cols[kSlice] == 1)) { //only first slice d.CSA.numDti++; int dir = d.CSA.numDti; if (dir <= kMaxDTI4D) { if (isVerbose ) { if (d.CSA.numDti == 1) printMessage("n\tdir\tbValue\tV1\tV2\tV3\n"); printMessage("%d\t%g\t%g\t%g\t%g\t%g\n", dir-1, cols[kGradientNumber], cols[kbval], cols[kv1], cols[kv2], cols[kv3]); } dti4D->S[dir-1].V[0] = cols[kbval]; dti4D->S[dir-1].V[1] = cols[kv1]; dti4D->S[dir-1].V[2] = cols[kv2]; dti4D->S[dir-1].V[3] = cols[kv3]; if (cols[kbval] > maxBValue) maxBValue = cols[kbval]; } //save DTI direction } } //if DTI directions //printMessage("%f %f %lu\n",cols[9],cols[kGradientNumber], strlen(buff)) p = fgets (buff, LINESZ, fp);//get next line } free (cols); fclose (fp); d.manufacturer = kMANUFACTURER_PHILIPS; d.isValid = true; d.isSigned = true; if ((patientPositionNumPhilipsB2 > 1) && (patientPositionNumPhilipsVol2 < 1)) { patientPositionNumPhilipsVol2 = patientPositionNumPhilipsB2; for (int s = 0; s < patientPositionNumPhilipsVol2; s++) sliceNumberMrPhilipsVol2[s] = sliceNumberMrPhilipsB2[s]; } if ((patientPositionNumPhilipsVol2 > 1) && (d.patientPositionNumPhilips == patientPositionNumPhilipsVol2)) { bool isSliceOrderConsistent = true; for (int s = 0; s < patientPositionNumPhilipsVol2; s++) if (sliceNumberMrPhilipsVol2[s] != dti4D->S[s].sliceNumberMrPhilips) isSliceOrderConsistent = false; if (!isSliceOrderConsistent) printError("PAR file order of slices varies between volumes (hint: use dicm2nii)\n"); d.isValid = false; } if (dynNotAscending) { printError("PAR file volumes not saved in ascending order (hint: use dicm2nii)\n"); d.isValid = false; } if ((slice % d.xyzDim[3]) != 0) { printError("Total number of slices (%d) not divisible by slices per 3D volume (%d) [acquisition aborted]. Try dicm2nii or nii_rescue_par to fix this: %s\n", slice, d.xyzDim[3], parname); d.isValid = false; } d.xyzDim[4] = slice/d.xyzDim[3]; d.locationsInAcquisition = d.xyzDim[3]; if (ADCwarning) printWarning("PAR/REC dataset includes an ADC map that could disrupt analysis. Please remove volume and ensure vectors are reported correctly\n"); if (isIntenScaleVaries) printWarning("Intensity slope/intercept varies between slices! [solution: user dcm2nii instead]\n"); if (!isIndexSequential) printWarning("Slice order not saved to disk sequentially! [solution: user dcm2nii instead]\n"); printMessage("Done reading PAR header version %.1f, with %d volumes\n", (float)parVers/10, d.CSA.numDti); //see Xiangrui Li 's dicm2nii (also BSD license) // http://www.mathworks.com/matlabcentral/fileexchange/42997-dicom-to-nifti-converter // Rotation order and signs are figured out by try and err, not 100% sure float d2r = (float) (M_PI/180.0); vec3 ca = setVec3(cos(d.angulation[1]*d2r),cos(d.angulation[2]*d2r),cos(d.angulation[3]*d2r)); vec3 sa = setVec3(sin(d.angulation[1]*d2r),sin(d.angulation[2]*d2r),sin(d.angulation[3]*d2r)); mat33 rx,ry,rz; LOAD_MAT33(rx,1.0f, 0.0f, 0.0f, 0.0f, ca.v[0], -sa.v[0], 0.0f, sa.v[0], ca.v[0]); LOAD_MAT33(ry, ca.v[1], 0.0f, sa.v[1], 0.0f, 1.0f, 0.0f, -sa.v[1], 0.0f, ca.v[1]); LOAD_MAT33(rz, ca.v[2], -sa.v[2], 0.0f, sa.v[2], ca.v[2], 0.0f, 0.0f, 0.0f, 1.0f); mat33 R = nifti_mat33_mul( rx,ry ); R = nifti_mat33_mul( R,rz); ivec3 ixyz = setiVec3(1,2,3); if (d.sliceOrient == kSliceOrientSag) { ixyz = setiVec3(2,3,1); for (int r = 0; r < 3; r++) for (int c = 0; c < 3; c++) if (c != 1) R.m[r][c] = -R.m[r][c]; //invert first and final columns }else if (d.sliceOrient == kSliceOrientCor) { ixyz = setiVec3(1,3,2); for (int r = 0; r < 3; r++) R.m[r][2] = -R.m[r][2]; //invert rows of final column } R = nifti_mat33_reorder_cols(R,ixyz); //dicom rotation matrix d.orient[1] = R.m[0][0]; d.orient[2] = R.m[1][0]; d.orient[3] = R.m[2][0]; d.orient[4] = R.m[0][1]; d.orient[5] = R.m[1][1]; d.orient[6] = R.m[2][1]; mat33 diag; LOAD_MAT33(diag, d.xyzMM[1],0.0f,0.0f, 0.0f,d.xyzMM[2],0.0f, 0.0f,0.0f, d.xyzMM[3]); R= nifti_mat33_mul( R, diag ); mat44 R44; LOAD_MAT44(R44, R.m[0][0],R.m[0][1],R.m[0][2],d.stackOffcentre[1], R.m[1][0],R.m[1][1],R.m[1][2],d.stackOffcentre[2], R.m[2][0],R.m[2][1],R.m[2][2],d.stackOffcentre[3]); vec3 x; if (parVers > 40) //guess x = setVec3(((float)d.xyzDim[1]-1)/2,((float)d.xyzDim[2]-1)/2,((float)d.xyzDim[3]-1)/2); else x = setVec3((float)d.xyzDim[1]/2,(float)d.xyzDim[2]/2,((float)d.xyzDim[3]-1)/2); mat44 eye; LOAD_MAT44(eye, 1.0f,0.0f,0.0f,x.v[0], 0.0f,1.0f,0.0f,x.v[1], 0.0f,0.0f,1.0f,x.v[2]); eye= nifti_mat44_inverse( eye ); //we wish to compute R/eye, so compute invEye and calculate R*invEye R44= nifti_mat44_mul( R44 , eye ); vec4 y; y.v[0]=0.0f; y.v[1]=0.0f; y.v[2]=(float) d.xyzDim[3]-1.0f; y.v[3]=1.0f; y= nifti_vect44mat44_mul(y, R44 ); int iOri = 2; //for axial, slices are 3rd dimenson (indexed from 0) (k) if (d.sliceOrient == kSliceOrientSag) iOri = 0; //for sagittal, slices are 1st dimension (i) if (d.sliceOrient == kSliceOrientCor) iOri = 1; //for coronal, slices are 2nd dimension (j) if (( (y.v[iOri]-R44.m[iOri][3])>0 ) == ( (y.v[iOri]-d.stackOffcentre[iOri+1])>0 ) ) { d.patientPosition[1] = R44.m[0][3]; d.patientPosition[2] = R44.m[1][3]; d.patientPosition[3] = R44.m[2][3]; d.patientPositionLast[1] = y.v[0]; d.patientPositionLast[2] = y.v[1]; d.patientPositionLast[3] = y.v[2]; }else { //d.patientPosition d.patientPosition[1] = y.v[0]; d.patientPosition[2] = y.v[1]; d.patientPosition[3] = y.v[2]; d.patientPositionLast[1] = R44.m[0][3]; d.patientPositionLast[2] = R44.m[1][3]; d.patientPositionLast[3] = R44.m[2][3]; } //finish up changeExt (parname, "REC"); #ifndef _MSC_VER //Linux is case sensitive, #include if( access( parname, F_OK ) != 0 ) changeExt (parname, "rec"); #endif d.locationsInAcquisition = d.xyzDim[3]; d.manufacturer = kMANUFACTURER_PHILIPS; d.imageStart = 0; if (d.CSA.numDti >= kMaxDTI4D) { printError("Unable to convert DTI [increase kMaxDTI4D]\n"); d.CSA.numDti = 0; }; if ((maxBValue <= 0.0f) && (maxDyn > minDyn) && (maxDynTime > minDynTime)) { //use max vs min Dyn instead of && (d.CSA.numDti > 1) int numDyn = maxDyn - minDyn; float TRms = 1000.0f * (maxDynTime - minDynTime) / (float)numDyn; //float TRms = 1000.0f * (maxDynTime - minDynTime) / (float)(d.CSA.numDti-1); if (fabs(TRms - d.TR) > 0.005f) printWarning("Reported TR=%gms, measured TR=%gms (prospect. motion corr.?)\n", d.TR, TRms); d.TR = TRms; } //check DTI makes sense if (d.CSA.numDti > 1) { bool v1varies = false; bool v2varies = false; bool v3varies = false; for (int i = 1; i < d.CSA.numDti; i++) { if (dti4D->S[0].V[1] != dti4D->S[i].V[1]) v1varies = true; if (dti4D->S[0].V[2] != dti4D->S[i].V[2]) v2varies = true; if (dti4D->S[0].V[3] != dti4D->S[i].V[3]) v3varies = true; } if ((!v1varies) || (!v2varies) || (!v3varies)) printError("Bizarre b-vectors %s\n", parname); } if ((maxEcho > 1) || (maxCardiac > 1)) printWarning("Multiple Echo (%d) or Cardiac (%d). Segment output, e.g. nii_segment4d('img.nii', %d)\n", maxEcho, maxCardiac, maxEcho*maxCardiac); return d; } //nii_readParRec() size_t nii_SliceBytes(struct nifti_1_header hdr) { //size of 2D slice size_t imgsz = hdr.bitpix/8; for (int i = 1; i < 3; i++) if (hdr.dim[i] > 1) imgsz = imgsz * hdr.dim[i]; return imgsz; } //nii_SliceBytes() size_t nii_ImgBytes(struct nifti_1_header hdr) { size_t imgsz = hdr.bitpix/8; for (int i = 1; i < 8; i++) if (hdr.dim[i] > 1) imgsz = imgsz * hdr.dim[i]; return imgsz; } //nii_ImgBytes() //unsigned char * nii_demosaic(unsigned char* inImg, struct nifti_1_header *hdr, int nMosaicSlices, int ProtocolSliceNumber1) { unsigned char * nii_demosaic(unsigned char* inImg, struct nifti_1_header *hdr, int nMosaicSlices) { //demosaic http://nipy.org/nibabel/dicom/dicom_mosaic.html if (nMosaicSlices < 2) return inImg; //Byte inImg[ [img length] ]; //[img getBytes:&inImg length:[img length]]; int nRowCol = (int) ceil(sqrt((double) nMosaicSlices)); int colBytes = hdr->dim[1]/nRowCol * hdr->bitpix/8; int lineBytes = hdr->dim[1] * hdr->bitpix/8; int rowBytes = hdr->dim[1] * hdr->dim[2]/nRowCol * hdr->bitpix/8; int col = 0; int row = 0; int lOutPos = 0; hdr->dim[1] = hdr->dim[1]/nRowCol; hdr->dim[2] = hdr->dim[2]/nRowCol; hdr->dim[3] = nMosaicSlices; size_t imgsz = nii_ImgBytes(*hdr); unsigned char *outImg = (unsigned char *)malloc(imgsz); for (int m=1; m <= nMosaicSlices; m++) { int lPos = (row * rowBytes) + (col * colBytes); for (int y = 0; y < hdr->dim[2]; y++) { memcpy(&outImg[lOutPos], &inImg[lPos], colBytes); // dest, src, bytes lPos += lineBytes; lOutPos +=colBytes; } col ++; if (col >= nRowCol) { row ++; col = 0; } //start new column } //for m = each mosaic slice /* //we now provide a warning once per series rather than once per volume (see nii_dicom_batch) if (ProtocolSliceNumber1 > 1) { printWarning("Weird CSA 'ProtocolSliceNumber': SPATIAL AND DTI TRANSFORMS UNTESTED\n"); }*/ /*if ((ProtocolSliceNumber1 > 1) && (hdr->dim[3] > 1)) { //exceptionally rare: reverse order of slices - now handled in matrix... int sliceBytes = hdr->dim[1] * hdr->dim[2] * hdr->bitpix/8; memcpy(&inImg[0], &outImg[0],sliceBytes*hdr->dim[3]); //copy data with reversed order dest, src, bytes int lOutPos = sliceBytes * (hdr->dim[3]-1); int lPos = 0; for (int m=0; m < nMosaicSlices; m++) { memcpy( &outImg[lOutPos], &inImg[lPos], sliceBytes); lPos += sliceBytes; lOutPos -= sliceBytes; } }*/ free(inImg); return outImg; } // nii_demosaic() unsigned char * nii_flipImgY(unsigned char* bImg, struct nifti_1_header *hdr){ //DICOM row order opposite from NIfTI int dim3to7 = 1; for (int i = 3; i < 8; i++) if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i]; size_t lineBytes = hdr->dim[1] * hdr->bitpix/8; if ((hdr->datatype == DT_RGB24) && (hdr->bitpix == 24) && (hdr->intent_code == NIFTI_INTENT_NONE)) { //we use the intent code to indicate planar vs triplet... lineBytes = hdr->dim[1]; dim3to7 = dim3to7 * 3; } //rgb data saved planar (RRR..RGGGG..GBBB..B //#ifdef _MSC_VER unsigned char * line = (unsigned char *)malloc(sizeof(unsigned char) * (lineBytes)); //#else // unsigned char line[lineBytes]; //#endif size_t sliceBytes = hdr->dim[2] * lineBytes; int halfY = hdr->dim[2] / 2; //note truncated toward zero, so halfY=2 regardless of 4 or 5 columns for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice size_t slBottom = (size_t)sl*sliceBytes; size_t slTop = (((size_t)sl+1)*sliceBytes)-lineBytes; for (int y = 0; y < halfY; y++) { //swap order of lines memcpy(line, &bImg[slBottom], lineBytes);//memcpy(&line, &bImg[slBottom], lineBytes); memcpy(&bImg[slBottom], &bImg[slTop], lineBytes); memcpy(&bImg[slTop], line, lineBytes);//tpx memcpy(&bImg[slTop], &line, lineBytes); slTop -= lineBytes; slBottom += lineBytes; } //for y } //for each slice //#ifdef _MSC_VER free(line); //#endif return bImg; } // nii_flipImgY() unsigned char * nii_flipImgZ(unsigned char* bImg, struct nifti_1_header *hdr){ //DICOM row order opposite from NIfTI int halfZ = hdr->dim[3] / 2; //note truncated toward zero, so halfY=2 regardless of 4 or 5 columns if (halfZ < 1) return bImg; int dim4to7 = 1; for (int i = 4; i < 8; i++) if (hdr->dim[i] > 1) dim4to7 = dim4to7 * hdr->dim[i]; int sliceBytes = hdr->dim[1] * hdr->dim[2] * hdr->bitpix/8; size_t volBytes = sliceBytes * hdr->dim[3]; //#ifdef _MSC_VER unsigned char * slice = (unsigned char *)malloc(sizeof(unsigned char) * (sliceBytes)); //#else // unsigned char slice[sliceBytes]; //#endif for (int vol = 0; vol < dim4to7; vol++) { //for each 2D slice size_t slBottom = vol*volBytes; size_t slTop = ((vol+1)*volBytes)-sliceBytes; for (int z = 0; z < halfZ; z++) { //swap order of lines memcpy(slice, &bImg[slBottom], sliceBytes); //TPX memcpy(&slice, &bImg[slBottom], sliceBytes); memcpy(&bImg[slBottom], &bImg[slTop], sliceBytes); memcpy(&bImg[slTop], slice, sliceBytes); //TPX slTop -= sliceBytes; slBottom += sliceBytes; } //for Z } //for each volume //#ifdef _MSC_VER free(slice); //#endif return bImg; } // nii_flipImgZ() unsigned char * nii_reorderSlices(unsigned char* bImg, struct nifti_1_header *h, struct TDTI4D *dti4D){ //flip slice order - Philips scanners can save data in non-contiguous order //if ((h->dim[3] < 2) || (h->dim[4] > 1)) return bImg; if (h->dim[3] < 2) return bImg; if (h->dim[3] >= kMaxDTI4D) { printWarning("Unable to reorder slices (%d > %d)\n", h->dim[3], kMaxDTI4D); return bImg; } //printMessage("<<< Slices not spatially contiguous: please check output [new feature]\n"); int dim4to7 = 1; for (int i = 4; i < 8; i++) if (h->dim[i] > 1) dim4to7 = dim4to7 * h->dim[i]; int sliceBytes = h->dim[1] * h->dim[2] * h->bitpix/8; if (sliceBytes < 0) return bImg; size_t volBytes = sliceBytes * h->dim[3]; unsigned char *srcImg = (unsigned char *)malloc(volBytes); for (int v = 0; v < dim4to7; v++) { size_t volStart = v * volBytes; memcpy(&srcImg[0], &bImg[volStart], volBytes); //dest, src, size for (int z = 0; z < h->dim[3]; z++) { //for each slice int src = dti4D->S[z].sliceNumberMrPhilips - 1; //-1 as Philips indexes slices from 1 not 0 //printMessage("Reordering volume %d slice %d\n", v, dti4D->S[z].sliceNumberMrPhilips); if ((src < 0) || (src >= h->dim[3])) continue; memcpy(&bImg[volStart+(src*sliceBytes)], &srcImg[z*sliceBytes], sliceBytes); //dest, src, size } } free(srcImg); return bImg; }// nii_reorderSlices() unsigned char * nii_flipZ(unsigned char* bImg, struct nifti_1_header *h){ //flip slice order if (h->dim[3] < 2) return bImg; mat33 s; mat44 Q44; LOAD_MAT33(s,h->srow_x[0],h->srow_x[1],h->srow_x[2], h->srow_y[0],h->srow_y[1],h->srow_y[2], h->srow_z[0],h->srow_z[1],h->srow_z[2]); LOAD_MAT44(Q44,h->srow_x[0],h->srow_x[1],h->srow_x[2],h->srow_x[3], h->srow_y[0],h->srow_y[1],h->srow_y[2],h->srow_y[3], h->srow_z[0],h->srow_z[1],h->srow_z[2],h->srow_z[3]); vec4 v= setVec4(0.0f,0.0f,(float) h->dim[3]-1.0f); v = nifti_vect44mat44_mul(v, Q44); //after flip this voxel will be the origin mat33 mFlipZ; LOAD_MAT33(mFlipZ,1.0f, 0.0f, 0.0f, 0.0f,1.0f,0.0f, 0.0f,0.0f,-1.0f); s= nifti_mat33_mul( s , mFlipZ ); LOAD_MAT44(Q44, s.m[0][0],s.m[0][1],s.m[0][2],v.v[0], s.m[1][0],s.m[1][1],s.m[1][2],v.v[1], s.m[2][0],s.m[2][1],s.m[2][2],v.v[2]); //printMessage(" ----------> %f %f %f\n",v.v[0],v.v[1],v.v[2]); setQSForm(h,Q44, true); //printMessage("nii_flipImgY dims %dx%dx%d %d \n",h->dim[1],h->dim[2], dim3to7,h->bitpix/8); return nii_flipImgZ(bImg,h); }// nii_flipZ() unsigned char * nii_flipY(unsigned char* bImg, struct nifti_1_header *h){ mat33 s; mat44 Q44; LOAD_MAT33(s,h->srow_x[0],h->srow_x[1],h->srow_x[2], h->srow_y[0],h->srow_y[1],h->srow_y[2], h->srow_z[0],h->srow_z[1],h->srow_z[2]); LOAD_MAT44(Q44,h->srow_x[0],h->srow_x[1],h->srow_x[2],h->srow_x[3], h->srow_y[0],h->srow_y[1],h->srow_y[2],h->srow_y[3], h->srow_z[0],h->srow_z[1],h->srow_z[2],h->srow_z[3]); vec4 v= setVec4(0,(float) h->dim[2]-1,0); v = nifti_vect44mat44_mul(v, Q44); //after flip this voxel will be the origin mat33 mFlipY; LOAD_MAT33(mFlipY,1.0f, 0.0f, 0.0f, 0.0f,-1.0f,0.0f, 0.0f,0.0f,1.0f); s= nifti_mat33_mul( s , mFlipY ); LOAD_MAT44(Q44, s.m[0][0],s.m[0][1],s.m[0][2],v.v[0], s.m[1][0],s.m[1][1],s.m[1][2],v.v[1], s.m[2][0],s.m[2][1],s.m[2][2],v.v[2]); setQSForm(h,Q44, true); //printMessage("nii_flipImgY dims %dx%d %d \n",h->dim[1],h->dim[2], h->bitpix/8); return nii_flipImgY(bImg,h); }// nii_flipY() /*void conv12bit16bit(unsigned char * img, struct nifti_1_header hdr) { //convert 12-bit allocated data to 16-bit // works for MR-MONO2-12-angio-an1 from http://www.barre.nom.fr/medical/samples/ // looks wrong: this sample toggles between big and little endian stores printWarning("Support for images that allocate 12 bits is experimental\n"); int nVox = nii_ImgBytes(hdr) / (hdr.bitpix/8); for (int i=(nVox-1); i >= 0; i--) { int i16 = i * 2; int i12 = floor(i * 1.5); uint16_t val; if ((i % 2) != 1) { val = (img[i12+0] << 4) + (img[i12+1] >> 4); } else { val = ((img[i12+0] & 0x0F) << 8) + img[i12+1]; } //if ((i % 2) != 1) { // val = img[i12+0] + ((img[i12+1] & 0xF0) << 4); //} else { // val = (img[i12+0] & 0x0F) + (img[i12+1] << 4); //} val = val & 0xFFF; img[i16+0] = val & 0xFF; img[i16+1] = (val >> 8) & 0xFF; } } //conv12bit16bit()*/ void conv12bit16bit(unsigned char * img, struct nifti_1_header hdr) { //convert 12-bit allocated data to 16-bit // works for MR-MONO2-12-angio-an1 from http://www.barre.nom.fr/medical/samples/ // looks wrong: this sample toggles between big and little endian stores printWarning("Support for images that allocate 12 bits is experimental\n"); int nVox = (int) nii_ImgBytes(hdr) / (hdr.bitpix/8); for (int i=(nVox-1); i >= 0; i--) { int i16 = i * 2; int i12 = floor(i * 1.5); uint16_t val; if ((i % 2) != 1) { val = img[i12+1] + (img[i12+0] << 8); val = val >> 4; } else { val = img[i12+0] + (img[i12+1] << 8); } img[i16+0] = val & 0xFF; img[i16+1] = (val >> 8) & 0xFF; } } //conv12bit16bit() unsigned char * nii_loadImgCore(char* imgname, struct nifti_1_header hdr, int bitsAllocated) { size_t imgsz = nii_ImgBytes(hdr); size_t imgszRead = imgsz; if (bitsAllocated == 12) imgszRead = round(imgsz * 0.75); FILE *file = fopen(imgname , "rb"); if (!file) { printError("Unable to open %s\n", imgname); return NULL; } fseek(file, 0, SEEK_END); long fileLen=ftell(file); if (fileLen < (imgszRead+hdr.vox_offset)) { printMessage("File not large enough to store image data: %s\n", imgname); return NULL; } fseek(file, (long) hdr.vox_offset, SEEK_SET); unsigned char *bImg = (unsigned char *)malloc(imgsz); size_t sz = fread(bImg, 1, imgszRead, file); fclose(file); if (sz < imgszRead) { printError("Only loaded %zu of %zu bytes for %s\n", sz, imgszRead, imgname); return NULL; } if (bitsAllocated == 12) conv12bit16bit(bImg, hdr); return bImg; } //nii_loadImgCore() unsigned char * nii_planar2rgb(unsigned char* bImg, struct nifti_1_header *hdr, int isPlanar) { //DICOM data saved in triples RGBRGBRGB, NIfTI RGB saved in planes RRR..RGGG..GBBBB..B if (bImg == NULL) return NULL; if (hdr->datatype != DT_RGB24) return bImg; if (isPlanar == 0) return bImg; int dim3to7 = 1; for (int i = 3; i < 8; i++) if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i]; int sliceBytes8 = hdr->dim[1]*hdr->dim[2]; int sliceBytes24 = sliceBytes8 * 3; unsigned char * slice24 = (unsigned char *)malloc(sizeof(unsigned char) * (sliceBytes24)); int sliceOffsetRGB = 0; int sliceOffsetR = 0; int sliceOffsetG = sliceOffsetR + sliceBytes8; int sliceOffsetB = sliceOffsetR + 2*sliceBytes8; //printMessage("planar->rgb %dx%dx%d\n", hdr->dim[1],hdr->dim[2], dim3to7); int i = 0; for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice memcpy(slice24, &bImg[sliceOffsetRGB], sliceBytes24); for (int rgb = 0; rgb < sliceBytes8; rgb++) { bImg[i++] =slice24[sliceOffsetR+rgb]; bImg[i++] =slice24[sliceOffsetG+rgb]; bImg[i++] =slice24[sliceOffsetB+rgb]; } sliceOffsetRGB += sliceBytes24; } //for each slice free(slice24); return bImg; } //nii_planar2rgb() unsigned char * nii_rgb2planar(unsigned char* bImg, struct nifti_1_header *hdr, int isPlanar) { //DICOM data saved in triples RGBRGBRGB, Analyze RGB saved in planes RRR..RGGG..GBBBB..B if (bImg == NULL) return NULL; if (hdr->datatype != DT_RGB24) return bImg; if (isPlanar == 1) return bImg;//return nii_bgr2rgb(bImg,hdr); int dim3to7 = 1; for (int i = 3; i < 8; i++) if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i]; int sliceBytes8 = hdr->dim[1]*hdr->dim[2]; int sliceBytes24 = sliceBytes8 * 3; unsigned char * slice24 = (unsigned char *)malloc(sizeof(unsigned char) * (sliceBytes24)); //printMessage("rgb->planar %dx%dx%d\n", hdr->dim[1],hdr->dim[2], dim3to7); int sliceOffsetR = 0; for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice memcpy(slice24, &bImg[sliceOffsetR], sliceBytes24); //TPX memcpy(&slice24, &bImg[sliceOffsetR], sliceBytes24); int sliceOffsetG = sliceOffsetR + sliceBytes8; int sliceOffsetB = sliceOffsetR + 2*sliceBytes8; int i = 0; int j = 0; for (int rgb = 0; rgb < sliceBytes8; rgb++) { bImg[sliceOffsetR+j] =slice24[i++]; bImg[sliceOffsetG+j] =slice24[i++]; bImg[sliceOffsetB+j] =slice24[i++]; j++; } sliceOffsetR += sliceBytes24; } //for each slice free(slice24); return bImg; } //nii_rgb2Planar() unsigned char * nii_iVaries(unsigned char *img, struct nifti_1_header *hdr){ //each DICOM image can have its own intesity scaling, whereas NIfTI requires the same scaling for all images in a file //WARNING: do this BEFORE nii_check16bitUnsigned!!!! //if (hdr->datatype != DT_INT16) return img; int dim3to7 = 1; for (int i = 3; i < 8; i++) if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i]; int nVox = hdr->dim[1]*hdr->dim[2]* dim3to7; if (nVox < 1) return img; float * img32=(float*)malloc(nVox*sizeof(float)); if (hdr->datatype == DT_UINT8) { uint8_t * img8i = (uint8_t*) img; for (int i=0; i < nVox; i++) img32[i] = img8i[i]; } else if (hdr->datatype == DT_UINT16) { uint16_t * img16ui = (uint16_t*) img; for (int i=0; i < nVox; i++) img32[i] = img16ui[i]; } else if (hdr->datatype == DT_INT16) { int16_t * img16i = (int16_t*) img; for (int i=0; i < nVox; i++) img32[i] = img16i[i]; } else if (hdr->datatype == DT_INT32) { int32_t * img32i = (int32_t*) img; for (int i=0; i < nVox; i++) img32[i] = (float) img32i[i]; } free (img); //release previous image for (int i=0; i < nVox; i++) img32[i] = (img32[i]* hdr->scl_slope)+hdr->scl_inter; hdr->scl_slope = 1; hdr->scl_inter = 0; hdr->datatype = DT_FLOAT; hdr->bitpix = 32; return (unsigned char*) img32; } //nii_iVaries() unsigned char * nii_XYTZ_XYZT(unsigned char* bImg, struct nifti_1_header *hdr, int seqRepeats) { //Philips can save time as 3rd dimensions, NIFTI requires time is 4th dimension int dim4to7 = 1; for (int i = 4; i < 8; i++) if (hdr->dim[i] > 1) dim4to7 = dim4to7 * hdr->dim[i]; if ((hdr->dim[3] < 2) || (dim4to7 < 2)) return bImg; printMessage("Converting XYTZ to XYZT with %d slices (Z) and %d volumes (T).\n",hdr->dim[3], dim4to7); if ((dim4to7 % seqRepeats) != 0) { printError("Patient position repeats %d times, but this does not evenly divide number of volumes (%d)\n", seqRepeats,dim4to7); seqRepeats = 1; } uint64_t typeRepeats = dim4to7 / seqRepeats; uint64_t sliceBytes = hdr->dim[1]*hdr->dim[2]*hdr->bitpix/8; uint64_t seqBytes = sliceBytes * seqRepeats; uint64_t typeBytes = seqBytes * hdr->dim[3]; uint64_t imgSz = nii_ImgBytes(*hdr); //this uses a lot of RAM, someday this could be done in place... unsigned char *outImg = (unsigned char *)malloc( imgSz); //memcpy(&tempImg[0], &bImg[0], imgSz); uint64_t origPos = 0; uint64_t Pos = 0; // for (int t = 0; t < (int)typeRepeats; t++) { //for each volume for (int s = 0; s < seqRepeats; s++) { origPos = (t*typeBytes) +s*sliceBytes; for (int z = 0; z < hdr->dim[3]; z++) { //for each slice memcpy( &outImg[Pos],&bImg[origPos], sliceBytes); Pos += sliceBytes; origPos += seqBytes; } }//for s } free(bImg); return outImg; } //nii_XYTZ_XYZT() unsigned char * nii_byteswap(unsigned char *img, struct nifti_1_header *hdr){ if (hdr->bitpix < 9) return img; uint64_t nvox = nii_ImgBytes(*hdr) / (hdr->bitpix/8); void *ar = (void*) img; if (hdr->bitpix == 16) nifti_swap_2bytes( nvox , ar ); if (hdr->bitpix == 32) nifti_swap_4bytes( nvox , ar ); if (hdr->bitpix == 64) nifti_swap_8bytes( nvox , ar ); return img; } //nii_byteswap() #ifdef myEnableJasper unsigned char * nii_loadImgCoreJasper(char* imgname, struct nifti_1_header hdr, struct TDICOMdata dcm, int compressFlag) { if (jas_init()) { return NULL; } jas_stream_t *in; jas_image_t *image; jas_setdbglevel(0); if (!(in = jas_stream_fopen(imgname, "rb"))) { printError( "Cannot open input image file %s\n", imgname); return NULL; } //int isSeekable = jas_stream_isseekable(in); jas_stream_seek(in, dcm.imageStart, 0); int infmt = jas_image_getfmt(in); if (infmt < 0) { printError( "Input image has unknown format %s offset %d bytes %d\n", imgname, dcm.imageStart, dcm.imageBytes); return NULL; } char opt[] = "\0"; char *inopts = opt; if (!(image = jas_image_decode(in, infmt, inopts))) { printError("Cannot decode image data %s offset %d bytes %d\n", imgname, dcm.imageStart, dcm.imageBytes); return NULL; } int numcmpts; int cmpts[4]; switch (jas_clrspc_fam(jas_image_clrspc(image))) { case JAS_CLRSPC_FAM_RGB: if (jas_image_clrspc(image) != JAS_CLRSPC_SRGB) printWarning("Inaccurate color\n"); numcmpts = 3; if ((cmpts[0] = jas_image_getcmptbytype(image, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R))) < 0 || (cmpts[1] = jas_image_getcmptbytype(image, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G))) < 0 || (cmpts[2] = jas_image_getcmptbytype(image, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B))) < 0) { printError("Missing color component\n"); return NULL; } break; case JAS_CLRSPC_FAM_GRAY: if (jas_image_clrspc(image) != JAS_CLRSPC_SGRAY) printWarning("Inaccurate color\n"); numcmpts = 1; if ((cmpts[0] = jas_image_getcmptbytype(image, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_GRAY_Y))) < 0) { printError("Missing color component\n"); return NULL; } break; default: printError("Unsupported color space\n"); return NULL; break; } int width = jas_image_cmptwidth(image, cmpts[0]); int height = jas_image_cmptheight(image, cmpts[0]); int prec = jas_image_cmptprec(image, cmpts[0]); int sgnd = jas_image_cmptsgnd(image, cmpts[0]); #ifdef MY_DEBUG printMessage("offset %d w*h %d*%d bpp %d sgnd %d components %d '%s' Jasper=%s\n",dcm.imageStart, width, height, prec, sgnd, numcmpts, imgname, jas_getversion()); #endif for (int cmptno = 0; cmptno < numcmpts; ++cmptno) { if (jas_image_cmptwidth(image, cmpts[cmptno]) != width || jas_image_cmptheight(image, cmpts[cmptno]) != height || jas_image_cmptprec(image, cmpts[cmptno]) != prec || jas_image_cmptsgnd(image, cmpts[cmptno]) != sgnd || jas_image_cmpthstep(image, cmpts[cmptno]) != jas_image_cmpthstep(image, 0) || jas_image_cmptvstep(image, cmpts[cmptno]) != jas_image_cmptvstep(image, 0) || jas_image_cmpttlx(image, cmpts[cmptno]) != jas_image_cmpttlx(image, 0) || jas_image_cmpttly(image, cmpts[cmptno]) != jas_image_cmpttly(image, 0)) { printMessage("The NIfTI format cannot be used to represent an image with this geometry.\n"); return NULL; } } //extract the data int bpp = (prec + 7) >> 3; //e.g. 12 bits requires 2 bytes int imgbytes = bpp * width * height * numcmpts; if ((bpp < 1) || (bpp > 2) || (width < 1) || (height < 1) || (imgbytes < 1)) { printError("Catastrophic decompression error\n"); return NULL; } jas_seqent_t v; unsigned char *img = (unsigned char *)malloc(imgbytes); uint16_t * img16ui = (uint16_t*) img; //unsigned 16-bit int16_t * img16i = (int16_t*) img; //signed 16-bit if (sgnd) bpp = -bpp; if (bpp == -1) { printError("Signed 8-bit DICOM?\n"); return NULL; } jas_matrix_t *data; jas_seqent_t *d; data = 0; int cmptno, y, x; int pix = 0; for (cmptno = 0; cmptno < numcmpts; ++cmptno) { if (!(data = jas_matrix_create(1, width))) { free(img); return NULL; } } //n.b. Analyze rgb-24 are PLANAR e.g. RRR..RGGG..GBBB..B not RGBRGBRGB...RGB for (cmptno = 0; cmptno < numcmpts; ++cmptno) { for (y = 0; y < height; ++y) { if (jas_image_readcmpt(image, cmpts[cmptno], 0, y, width, 1, data)) { free(img); return NULL; } d = jas_matrix_getref(data, 0, 0); for (x = 0; x < width; ++x) { v = *d; switch (bpp) { case 1: img[pix] = v; break; case 2: img16ui[pix] = v; break; case -2: img16i[pix] = v; break; } pix ++; ++d; }//for x } //for y } //for each component jas_matrix_destroy(data); jas_image_destroy(image); jas_image_clearfmts(); return img; } //nii_loadImgCoreJasper() #endif struct TJPEG { long offset; long size; }; TJPEG * decode_JPEG_SOF_0XC3_stack (const char *fn, int skipBytes, bool isVerbose, int frames, bool isLittleEndian) { #define abortGoto() free(lOffsetRA); return NULL; TJPEG *lOffsetRA = (TJPEG*) malloc(frames * sizeof(TJPEG)); FILE *reader = fopen(fn, "rb"); fseek(reader, 0, SEEK_END); long lRawSz = ftell(reader)- skipBytes; if (lRawSz <= 8) { printError("Unable to open %s\n", fn); abortGoto(); //read failure } fseek(reader, skipBytes, SEEK_SET); unsigned char *lRawRA = (unsigned char*) malloc(lRawSz); size_t lSz = fread(lRawRA, 1, lRawSz, reader); fclose(reader); if (lSz < (size_t)lRawSz) { printError("Unable to read %s\n", fn); abortGoto(); //read failure } long lRawPos = 0; //starting position int frame = 0; while ((frame < frames) && ((lRawPos+10) < lRawSz)) { int tag = dcmInt(4,&lRawRA[lRawPos],isLittleEndian); lRawPos += 4; //read tag int tagLength = dcmInt(4,&lRawRA[lRawPos],isLittleEndian); long tagEnd =lRawPos + tagLength + 4; if (isVerbose) printMessage("Tag %#x length %d end at %ld\n", tag, tagLength, tagEnd+skipBytes); lRawPos += 4; //read tag length if ((lRawRA[lRawPos] != 0xFF) || (lRawRA[lRawPos+1] != 0xD8) || (lRawRA[lRawPos +2] != 0xFF)) { if (isVerbose) printWarning("JPEG signature 0xFFD8FF not found at offset %d of %s\n", skipBytes, fn); } else { lOffsetRA[frame].offset = lRawPos+skipBytes; lOffsetRA[frame].size = tagLength; frame ++; } lRawPos = tagEnd; } free(lRawRA); if (frame < frames) { printMessage("Only found %d of %d JPEG fragments. Please use dcmdjpeg or gdcmconv to uncompress data.\n", frame, frames); abortGoto(); } return lOffsetRA; } unsigned char * nii_loadImgJPEGC3(char* imgname, struct nifti_1_header hdr, struct TDICOMdata dcm, bool isVerbose) { //arcane and inefficient lossless compression method popularized by dcmcjpeg, examples at http://www.osirix-viewer.com/resources/dicom-image-library/ int dimX, dimY, bits, frames; //clock_t start = clock(); // https://github.com/rii-mango/JPEGLosslessDecoderJS/blob/master/tests/data/jpeg_lossless_sel1-8bit.dcm //N.B. this current code can not extract a 2D image that is saved as multiple fragments, for example see the JPLL files at // ftp://medical.nema.org/MEDICAL/Dicom/DataSets/WG04/ //Live javascript code that can handle these is at // https://github.com/chafey/cornerstoneWADOImageLoader //I have never seen these segmented images in the wild, so we will simply warn the user if we encounter such a file //int Sz = JPEG_SOF_0XC3_sz (imgname, (dcm.imageStart - 4), dcm.isLittleEndian); //printMessage("Sz %d %d\n", Sz, dcm.imageBytes ); //This behavior is legal but appears extremely rare //ftp://medical.nema.org/medical/dicom/final/cp900_ft.pdf if (65536 == dcm.imageBytes) printError("One frame may span multiple fragments. SOFxC3 lossless JPEG. Please extract with dcmdjpeg or gdcmconv.\n"); unsigned char * ret = decode_JPEG_SOF_0XC3 (imgname, dcm.imageStart, isVerbose, &dimX, &dimY, &bits, &frames, 0); if (ret == NULL) { printMessage("Unable to decode JPEG. Please use dcmdjpeg to uncompress data.\n"); return NULL; } //printMessage("JPEG %fms\n", ((double)(clock()-start))/1000); if (hdr.dim[3] != frames) { //multi-slice image saved as multiple image fragments rather than a single image //printMessage("Unable to decode all slices (%d/%d). Please use dcmdjpeg to uncompress data.\n", frames, hdr.dim[3]); if (ret != NULL) free(ret); TJPEG * offsetRA = decode_JPEG_SOF_0XC3_stack (imgname, dcm.imageStart-8, isVerbose, hdr.dim[3], dcm.isLittleEndian); if (offsetRA == NULL) return NULL; size_t slicesz = nii_SliceBytes(hdr); size_t imgsz = slicesz * hdr.dim[3]; size_t pos = 0; unsigned char *bImg = (unsigned char *)malloc(imgsz); for (int frame = 0; frame < hdr.dim[3]; frame++) { if (isVerbose) printMessage("JPEG frame %d has %ld bytes @ %ld\n", frame, offsetRA[frame].size, offsetRA[frame].offset); unsigned char * ret = decode_JPEG_SOF_0XC3 (imgname, (int)offsetRA[frame].offset, false, &dimX, &dimY, &bits, &frames, (int)offsetRA[frame].size); if (ret == NULL) { printMessage("Unable to decode JPEG. Please use dcmdjpeg to uncompress data.\n"); free(bImg); return NULL; } memcpy(&bImg[pos], ret, slicesz); //dest, src, size free(ret); pos += slicesz; } free(offsetRA); return bImg; } return ret; } #ifndef F_OK #define F_OK 0 /* existence check */ #endif #ifndef myDisableClassicJPEG #ifdef myTurboJPEG //if turboJPEG instead of nanoJPEG for classic JPEG decompression //unsigned char * nii_loadImgJPEG50(char* imgname, struct nifti_1_header hdr, struct TDICOMdata dcm) { unsigned char * nii_loadImgJPEG50(char* imgname, struct TDICOMdata dcm) { //decode classic JPEG using nanoJPEG //printMessage("50 offset %d\n", dcm.imageStart); if ((dcm.samplesPerPixel != 1) && (dcm.samplesPerPixel != 3)) { printError("%d components (expected 1 or 3) in a JPEG image '%s'\n", dcm.samplesPerPixel, imgname); return NULL; } if( access(imgname, F_OK ) == -1 ) { printError("Unable to find '%s'\n", imgname); return NULL; } //load compressed data FILE *f = fopen(imgname, "rb"); fseek(f, 0, SEEK_END); long unsigned int _jpegSize = (long unsigned int) ftell(f); _jpegSize = _jpegSize - dcm.imageStart; if (_jpegSize < 8) { printError("File too small\n"); fclose(f); return NULL; } unsigned char* _compressedImage = (unsigned char *)malloc(_jpegSize); fseek(f, dcm.imageStart, SEEK_SET); _jpegSize = (long unsigned int) fread(_compressedImage, 1, _jpegSize, f); fclose(f); int jpegSubsamp, width, height; //printMessage("Decoding with turboJPEG\n"); tjhandle _jpegDecompressor = tjInitDecompress(); tjDecompressHeader2(_jpegDecompressor, _compressedImage, _jpegSize, &width, &height, &jpegSubsamp); int COLOR_COMPONENTS = dcm.samplesPerPixel; //printMessage("turboJPEG h*w %d*%d sampling %d components %d\n", width, height, jpegSubsamp, COLOR_COMPONENTS); if ((jpegSubsamp == TJSAMP_GRAY) && (COLOR_COMPONENTS != 1)) { printError("Grayscale jpegs should not have %d components '%s'\n", COLOR_COMPONENTS, imgname); } if ((jpegSubsamp != TJSAMP_GRAY) && (COLOR_COMPONENTS != 3)) { printError("Color jpegs should not have %d components '%s'\n", COLOR_COMPONENTS, imgname); } //unsigned char bImg[width*height*COLOR_COMPONENTS]; //!< will contain the decompressed image unsigned char *bImg = (unsigned char *)malloc(width*height*COLOR_COMPONENTS); if (COLOR_COMPONENTS == 1) //TJPF_GRAY tjDecompress2(_jpegDecompressor, _compressedImage, _jpegSize, bImg, width, 0/*pitch*/, height, TJPF_GRAY, TJFLAG_FASTDCT); else tjDecompress2(_jpegDecompressor, _compressedImage, _jpegSize, bImg, width, 0/*pitch*/, height, TJPF_RGB, TJFLAG_FASTDCT); //printMessage("turboJPEG h*w %d*%d (sampling %d)\n", width, height, jpegSubsamp); tjDestroy(_jpegDecompressor); return bImg; } #else //if turboJPEG else use nanojpeg... //unsigned char * nii_loadImgJPEG50(char* imgname, struct nifti_1_header hdr, struct TDICOMdata dcm) { unsigned char * nii_loadImgJPEG50(char* imgname, struct TDICOMdata dcm) { //decode classic JPEG using nanoJPEG //printMessage("50 offset %d\n", dcm.imageStart); if( access(imgname, F_OK ) == -1 ) { printError("Unable to find '%s'\n", imgname); return NULL; } //load compressed data FILE *f = fopen(imgname, "rb"); fseek(f, 0, SEEK_END); int size = (int) ftell(f); size = size - dcm.imageStart; if (size < 8) { printError("File too small '%s'\n", imgname); fclose(f); return NULL; } char *buf = (char *)malloc(size); fseek(f, dcm.imageStart, SEEK_SET); size = (int) fread(buf, 1, size, f); fclose(f); //decode njInit(); if (njDecode(buf, size)) { printError("Unable to decode JPEG image.\n"); return NULL; } free(buf); unsigned char *bImg = (unsigned char *)malloc(njGetImageSize()); memcpy(bImg, njGetImage(), njGetImageSize()); //dest, src, size njDone(); return bImg; } #endif #endif uint32_t rleInt(int lIndex, unsigned char lBuffer[], bool swap) {//read binary 32-bit integer uint32_t retVal = 0; memcpy(&retVal, (char*)&lBuffer[lIndex * 4], 4); if (!swap) return retVal; uint32_t swapVal; char *inInt = ( char* ) & retVal; char *outInt = ( char* ) & swapVal; outInt[0] = inInt[3]; outInt[1] = inInt[2]; outInt[2] = inInt[1]; outInt[3] = inInt[0]; return swapVal; } //rleInt() unsigned char * nii_loadImgRLE(char* imgname, struct nifti_1_header hdr, struct TDICOMdata dcm) { //decompress PackBits run-length encoding https://en.wikipedia.org/wiki/PackBits if (dcm.imageBytes < 66 ) { //64 for header+ 2 byte minimum image printError("%d is not enough bytes for RLE compression '%s'\n", dcm.imageBytes, imgname); return NULL; } FILE *file = fopen(imgname , "rb"); if (!file) { printError("Unable to open %s\n", imgname); return NULL; } fseek(file, 0, SEEK_END); long fileLen=ftell(file); if ((fileLen < 1) || (dcm.imageBytes < 1) || (fileLen < (dcm.imageBytes+dcm.imageStart))) { printMessage("File not large enough to store image data: %s\n", imgname); fclose(file); return NULL; } fseek(file, (long) dcm.imageStart, SEEK_SET); size_t imgsz = nii_ImgBytes(hdr); unsigned char *cImg = (unsigned char *)malloc(dcm.imageBytes); //compressed input size_t sz = fread(cImg, 1, dcm.imageBytes, file); fclose(file); if (sz < (size_t)dcm.imageBytes) { printError("Only loaded %zu of %d bytes for %s\n", sz, dcm.imageBytes, imgname); free(cImg); return NULL; } //read header http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_G.3.html bool swap = (dcm.isLittleEndian != littleEndianPlatform()); int bytesPerSample = dcm.samplesPerPixel * (dcm.bitsAllocated / 8); uint32_t bytesPerSampleRLE = rleInt(0, cImg, swap); if ((bytesPerSample < 0) || (bytesPerSampleRLE != (uint32_t)bytesPerSample)) { printError("RLE header corrupted %d != %d\n", bytesPerSampleRLE, bytesPerSample); free(cImg); return NULL; } unsigned char *bImg = (unsigned char *)malloc(imgsz); //binary output for (size_t i = 0; i < imgsz; i++) bImg[i] = 0; for (int i = 0; i < bytesPerSample; i++) { uint32_t offset = rleInt(i+1, cImg, swap); if ((dcm.imageBytes < 0) || (offset > (uint32_t)dcm.imageBytes)) { printError("RLE header error\n"); free(cImg); free(bImg); return NULL; } //save in platform's endian: // The first Segment is generated by stripping off the most significant byte of each Padded Composite Pixel Code... size_t vx = i; if ((dcm.samplesPerPixel == 1) && (littleEndianPlatform())) //endian, except for RGB vx = (bytesPerSample-1) - i; while (vx < imgsz) { int8_t n = cImg[offset]; offset++; //http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_G.3.html if ((n >= 0) && (n <= 127)) { //literal bytes int reps = 1 + (int)n; for (int r = 0; r < reps; r++) { int8_t v = cImg[offset]; offset++; if (vx >= imgsz) ;//printMessage("literal overflow %d %d\n", r, reps); else bImg[vx] = v; vx = vx + bytesPerSample; } } else if ((n <= -1) && (n >= -127)) { //repeated run int8_t v = cImg[offset]; offset++; int reps = -(int)n + 1; for (int r = 0; r < reps; r++) { if (vx >= imgsz) ;//printMessage("repeat overflow %d\n", reps); else bImg[vx] = v; vx = vx + bytesPerSample; } }; //n.b. we ignore -128! } //while vx < imgsz } //for i < bytesPerSample free(cImg); return bImg; } // nii_loadImgRLE() #ifdef myDisableOpenJPEG #ifndef myEnableJasper //avoid compiler warning, see https://stackoverflow.com/questions/3599160/unused-parameter-warnings-in-c #define UNUSED(x) (void)(x) #endif #endif unsigned char * nii_loadImgXL(char* imgname, struct nifti_1_header *hdr, struct TDICOMdata dcm, bool iVaries, int compressFlag, int isVerbose) { //provided with a filename (imgname) and DICOM header (dcm), creates NIfTI header (hdr) and img if (headerDcm2Nii(dcm, hdr, true) == EXIT_FAILURE) return NULL; //TOFU unsigned char * img; if (dcm.compressionScheme == kCompress50) { #ifdef myDisableClassicJPEG printMessage("Software not compiled to decompress classic JPEG DICOM images\n"); return NULL; #else //img = nii_loadImgJPEG50(imgname, *hdr, dcm); img = nii_loadImgJPEG50(imgname, dcm); if (hdr->datatype ==DT_RGB24) //convert to planar img = nii_rgb2planar(img, hdr, dcm.isPlanarRGB);//do this BEFORE Y-Flip, or RGB order can be flipped #endif } else if (dcm.compressionScheme == kCompressRLE) { img = nii_loadImgRLE(imgname, *hdr, dcm); if (hdr->datatype ==DT_RGB24) //convert to planar img = nii_rgb2planar(img, hdr, dcm.isPlanarRGB);//do this BEFORE Y-Flip, or RGB order can be flipped } else if (dcm.compressionScheme == kCompressC3) img = nii_loadImgJPEGC3(imgname, *hdr, dcm, (isVerbose > 0)); else #ifndef myDisableOpenJPEG if ( ((dcm.compressionScheme == kCompress50) || (dcm.compressionScheme == kCompressYes)) && (compressFlag != kCompressNone) ) img = nii_loadImgCoreOpenJPEG(imgname, *hdr, dcm, compressFlag); else #else #ifdef myEnableJasper if ((dcm.compressionScheme == kCompressYes) && (compressFlag != kCompressNone) ) img = nii_loadImgCoreJasper(imgname, *hdr, dcm, compressFlag); else #endif #endif if (dcm.compressionScheme == kCompressYes) { printMessage("Software not set up to decompress DICOM\n"); return NULL; } else img = nii_loadImgCore(imgname, *hdr, dcm.bitsAllocated); if (img == NULL) return img; if ((dcm.compressionScheme == kCompressNone) && (dcm.isLittleEndian != littleEndianPlatform()) && (hdr->bitpix > 8)) img = nii_byteswap(img, hdr); if ((dcm.compressionScheme == kCompressNone) && (hdr->datatype ==DT_RGB24)) img = nii_rgb2planar(img, hdr, dcm.isPlanarRGB);//do this BEFORE Y-Flip, or RGB order can be flipped dcm.isPlanarRGB = true; if (dcm.CSA.mosaicSlices > 1) { img = nii_demosaic(img, hdr, dcm.CSA.mosaicSlices); //, dcm.CSA.protocolSliceNumber1); /* we will do this in nii_dicom_batch #ifdef obsolete_mosaic_flip img = nii_flipImgY(img, hdr); #endif*/ } if ((!dcm.isFloat) && (iVaries)) img = nii_iVaries(img, hdr); int nAcq = dcm.locationsInAcquisition; if ((nAcq > 1) && (hdr->dim[0] < 4) && ((hdr->dim[3]%nAcq)==0) && (hdr->dim[3]>nAcq) ) { hdr->dim[4] = hdr->dim[3]/nAcq; hdr->dim[3] = nAcq; hdr->dim[0] = 4; } if ((hdr->dim[0] > 3) && (dcm.patientPositionSequentialRepeats > 1)) //swizzle 3rd and 4th dimension (Philips stores time as 3rd dimension) img = nii_XYTZ_XYZT(img, hdr,dcm.patientPositionSequentialRepeats ); headerDcm2NiiSForm(dcm,dcm, hdr, false); return img; } //nii_loadImgXL() int isSQ(uint32_t groupElement) { //Detect sequence VR ("SQ") for implicit tags static const int array_size = 34; uint32_t array[array_size] = {0x2005+(uint32_t(0x140F)<<16), 0x0008+(uint32_t(0x1111)<<16), 0x0008+(uint32_t(0x1115)<<16), 0x0008+(uint32_t(0x1140)<<16), 0x0008+(uint32_t(0x1199)<<16), 0x0008+(uint32_t(0x2218)<<16), 0x0008+(uint32_t(0x9092)<<16), 0x0018+(uint32_t(0x9006)<<16), 0x0018+(uint32_t(0x9042)<<16), 0x0018+(uint32_t(0x9045)<<16), 0x0018+(uint32_t(0x9049)<<16), 0x0018+(uint32_t(0x9112)<<16), 0x0018+(uint32_t(0x9114)<<16), 0x0018+(uint32_t(0x9115)<<16), 0x0018+(uint32_t(0x9119)<<16), 0x0018+(uint32_t(0x9125)<<16), 0x0018+(uint32_t(0x9152)<<16), 0x0018+(uint32_t(0x9176)<<16), 0x0018+(uint32_t(0x9226)<<16), 0x0018+(uint32_t(0x9239)<<16), 0x0020+(uint32_t(0x9071)<<16), 0x0020+(uint32_t(0x9111)<<16), 0x0020+(uint32_t(0x9113)<<16), 0x0020+(uint32_t(0x9116)<<16), 0x0020+(uint32_t(0x9221)<<16), 0x0020+(uint32_t(0x9222)<<16), 0x0028+(uint32_t(0x9110)<<16), 0x0028+(uint32_t(0x9132)<<16), 0x0028+(uint32_t(0x9145)<<16), 0x0040+(uint32_t(0x0260)<<16), 0x0040+(uint32_t(0x0555)<<16), 0x0040+(uint32_t(0xa170)<<16), 0x5200+(uint32_t(0x9229)<<16), 0x5200+(uint32_t(0x9230)<<16)}; for (int i = 0; i < array_size; i++) { //if (array[i] == groupElement) printMessage(" implicitSQ %04x,%04x\n", groupElement & 65535,groupElement>>16); if (array[i] == groupElement) return 1; } return 0; } //isSQ() int isDICOMfile(const char * fname) { //0=NotDICOM, 1=DICOM, 2=Maybe(not Part 10 compliant) FILE *fp = fopen(fname, "rb"); if (!fp) return 0; fseek(fp, 0, SEEK_END); long fileLen=ftell(fp); if (fileLen < 256) { fclose(fp); return 0; } fseek(fp, 0, SEEK_SET); unsigned char buffer[256]; size_t sz = fread(buffer, 1, 256, fp); fclose(fp); if (sz < 256) return 0; if ((buffer[128] == 'D') && (buffer[129] == 'I') && (buffer[130] == 'C') && (buffer[131] == 'M')) return 1; //valid DICOM if ((buffer[0] == 8) && (buffer[1] == 0) && (buffer[3] == 0)) return 2; //not valid Part 10 file, perhaps DICOM object return 0; } //isDICOMfile() //START RIR 12/2017 Robert I. Reid // Gathering spot for all the info needed to get the b value and direction // for a volume. struct TVolumeDiffusion { struct TDICOMdata* pdd; // The multivolume struct TDTI4D* pdti4D; // permanent records. uint8_t manufacturer; // kMANUFACTURER_UNKNOWN, kMANUFACTURER_SIEMENS, etc. //void set_manufacturer(const uint8_t m) {manufacturer = m; update();} // unnecessary // Everything after this in the structure would be private if it were a C++ // class, but it has been rewritten as a struct for C compatibility. I am // using _ as a hint of that, although _ for privacy is not really a // universal convention in C. Privacy is desired because immediately // any of these are updated _update_tvd() should be called. bool _isAtFirstPatientPosition; // Limit b vals and vecs to 1 per volume. //float bVal0018_9087; // kDiffusion_b_value, always present in Philips/Siemens. //float bVal2001_1003; // kDiffusionBFactor // float dirRL2005_10b0; // kDiffusionDirectionRL // float dirAP2005_10b1; // kDiffusionDirectionAP // float dirFH2005_10b2; // kDiffusionDirectionFH // Philips diffusion scans tend to have a "trace" (average of the diffusion // weighted volumes) volume tacked on, usually but not always at the end, // so b is > 0, but the direction is meaningless. Most software versions // explicitly set the direction to 0, but version 3 does not, making (0x18, // 0x9075) necessary. bool _isPhilipsNonDirectional; //char _directionality0018_9075[16]; // DiffusionDirectionality, not in Philips 2.6. // float _orientation0018_9089[3]; // kDiffusionOrientation, always // // present in Philips/Siemens for // // volumes with a direction. //char _seq0018_9117[64]; // MRDiffusionSequence, not in Philips 2.6. float _dtiV[4]; //uint16_t numDti; }; struct TVolumeDiffusion initTVolumeDiffusion(struct TDICOMdata* ptdd, struct TDTI4D* dti4D); void clear_volume(struct TVolumeDiffusion* ptvd); // Blank the volume-specific members or set them to impossible values. void set_directionality0018_9075(struct TVolumeDiffusion* ptvd, unsigned char* inbuf); void set_orientation0018_9089(struct TVolumeDiffusion* ptvd, int lLength, unsigned char* inbuf, bool isLittleEndian); void set_isAtFirstPatientPosition_tvd(struct TVolumeDiffusion* ptvd, bool iafpp); void set_bValGE(struct TVolumeDiffusion* ptvd, int lLength, unsigned char* inbuf); void set_diffusion_directionGE(struct TVolumeDiffusion* ptvd, int lLength, unsigned char* inbuf, int axis); void set_bVal(struct TVolumeDiffusion* ptvd, float b); void _update_tvd(struct TVolumeDiffusion* ptvd); struct TVolumeDiffusion initTVolumeDiffusion(struct TDICOMdata* ptdd, struct TDTI4D* dti4D) { struct TVolumeDiffusion tvd; tvd.pdd = ptdd; tvd.pdti4D = dti4D; clear_volume(&tvd); return tvd; } //initTVolumeDiffusion() void clear_volume(struct TVolumeDiffusion* ptvd) { ptvd->_isAtFirstPatientPosition = false; ptvd->manufacturer = kMANUFACTURER_UNKNOWN; //bVal0018_9087 = -1; //ptvd->_directionality0018_9075[0] = 0; //ptvd->seq0018_9117[0] = 0; //bVal2001_1003 = -1; // dirRL2005_10b0 = 2; // dirAP2005_10b1 = 2; // dirFH2005_10b2 = 2; ptvd->_isPhilipsNonDirectional = false; ptvd->_dtiV[0] = -1; for(int i = 1; i < 4; ++i) ptvd->_dtiV[i] = 2; //numDti = 0; }//clear_volume() void set_directionality0018_9075(struct TVolumeDiffusion* ptvd, unsigned char* inbuf) { if(strncmp(( char*)(inbuf), "DIRECTIONAL", 11) && // strncmp = 0 for ==. strncmp(( char*)(inbuf), "BMATRIX", 7)){ // Siemens XA10 ptvd->_isPhilipsNonDirectional = true; // Explicitly set the direction to 0 now, because there may // not be a 0018,9089 for this frame. for(int i = 1; i < 4; ++i) // 1-3 is intentional. ptvd->_dtiV[i] = 0.0; } else{ ptvd->_isPhilipsNonDirectional = false; // Wait for 0018,9089 to get the direction. } _update_tvd(ptvd); } //set_directionality0018_9075() void set_bValGE(struct TVolumeDiffusion* ptvd, int lLength, unsigned char* inbuf) { //int dcmStrInt (int lByteLength, unsigned char lBuffer[]) {//read float stored as a string dcmStrInt(lLength, inbuf); ptvd->_dtiV[0] = dcmStrInt(lLength, inbuf); //dd.CSA.numDti = 1; // Always true for GE. _update_tvd(ptvd); } //set_bValGE() // axis: 0 -> x, 1 -> y , 2 -> z void set_diffusion_directionGE(struct TVolumeDiffusion* ptvd, int lLength, unsigned char* inbuf, const int axis){ ptvd->_dtiV[axis + 1] = dcmStrFloat(lLength, inbuf); _update_tvd(ptvd); }//set_diffusion_directionGE() void dcmMultiFloatDouble (size_t lByteLength, unsigned char lBuffer[], size_t lnFloats, float *lFloats, bool isLittleEndian) { size_t floatlen = lByteLength / lnFloats; for(size_t i = 0; i < lnFloats; ++i) lFloats[i] = dcmFloatDouble((int)floatlen, lBuffer + i * floatlen, isLittleEndian); } //dcmMultiFloatDouble() void set_orientation0018_9089(struct TVolumeDiffusion* ptvd, int lLength, unsigned char* inbuf, bool isLittleEndian) { if(ptvd->_isPhilipsNonDirectional){ for(int i = 1; i < 4; ++i) // Deliberately ignore inbuf; it might be nonsense. ptvd->_dtiV[i] = 0.0; } else dcmMultiFloatDouble(lLength, inbuf, 3, ptvd->_dtiV + 1, isLittleEndian); _update_tvd(ptvd); }//set_orientation0018_9089() void set_bVal(struct TVolumeDiffusion* ptvd, const float b) { ptvd->_dtiV[0] = b; _update_tvd(ptvd); }//set_bVal() void set_isAtFirstPatientPosition_tvd(struct TVolumeDiffusion* ptvd, const bool iafpp) { ptvd->_isAtFirstPatientPosition = iafpp; _update_tvd(ptvd); }//set_isAtFirstPatientPosition_tvd() // Update the diffusion info in dd and *pdti4D for a volume once all the // diffusion info for that volume has been read into pvd. // // Note that depending on the scanner software the diffusion info can arrive in // different tags, in different orders (because of enclosing sequence tags), // and the values in some tags may be invalid, or may be essential, depending // on the presence of other tags. Thus it is best to gather all the diffusion // info for a volume (frame) before taking action on it. // // On the other hand, dd and *pdti4D need to be updated as soon as the // diffusion info is ready, before diffusion info for the next volume is read // in. void _update_tvd(struct TVolumeDiffusion* ptvd) { // Figure out if we have both the b value and direction (if any) for this // volume, and if isFirstPosition. // // GE (software version 27) is liable to NOT include kDiffusion_b_value for the // // slice if it is 0, but should still have kDiffusionBFactor, which comes // // after PatientPosition. // if(isAtFirstPatientPosition && manufacturer == kMANUFACTURER_GE && dtiV[0] < 0) // dtiV[0] = 0; // Implied 0. bool isReady = (ptvd->_isAtFirstPatientPosition && (ptvd->_dtiV[0] >= 0)); if(isReady){ for(int i = 1; i < 4; ++i){ if(ptvd->_dtiV[i] > 1){ isReady = false; break; } } } if(!isReady) return; // If still here, update dd and *pdti4D. ptvd->pdd->CSA.numDti++; if (ptvd->pdd->CSA.numDti == 2) { // First time we know that this is a 4D DTI dataset; for(int i = 0; i < 4; ++i) // Start *pdti4D before ptvd->pdd->CSA.dtiV ptvd->pdti4D->S[0].V[i] = ptvd->pdd->CSA.dtiV[i]; // is updated. } for(int i = 0; i < 4; ++i) // Update pdd ptvd->pdd->CSA.dtiV[i] = ptvd->_dtiV[i]; if((ptvd->pdd->CSA.numDti > 1) && (ptvd->pdd->CSA.numDti < kMaxDTI4D)){ // Update *pdti4D //d.dti4D = (TDTI *)malloc(kMaxDTI4D * sizeof(TDTI)); for(int i = 0; i < 4; ++i) ptvd->pdti4D->S[ptvd->pdd->CSA.numDti - 1].V[i] = ptvd->_dtiV[i]; } clear_volume(ptvd); // clear the slate for the next volume. }//_update_tvd() //END RIR struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, struct TDTI4D *dti4D) { struct TDICOMdata d = clear_dicom_data(); strcpy(d.protocolName, ""); //erase dummy with empty strcpy(d.protocolName, ""); //erase dummy with empty strcpy(d.seriesDescription, ""); //erase dummy with empty strcpy(d.sequenceName, ""); //erase dummy with empty //do not read folders - code specific to GCC (LLVM/Clang seems to recognize a small file size) struct TVolumeDiffusion volDiffusion = initTVolumeDiffusion(&d, dti4D); struct stat s; if( stat(fname,&s) == 0 ) { if( !(s.st_mode & S_IFREG) ){ printMessage( "DICOM read fail: not a valid file (perhaps a directory) %s\n",fname); return d; } } bool isPart10prefix = true; int isOK = isDICOMfile(fname); if (isOK == 0) return d; if (isOK == 2) { d.isExplicitVR = false; isPart10prefix = false; } FILE *file = fopen(fname, "rb"); if (!file) { printMessage("Unable to open file %s\n", fname); return d; } fseek(file, 0, SEEK_END); long fileLen=ftell(file); //Get file length if (fileLen < 256) { printMessage( "File too small to be a DICOM image %s\n", fname); return d; } //Since size of DICOM header is unknown, we will load it in 1mb segments //This uses less RAM and makes is faster for computers with slow disk access //Benefit is largest for 4D images. //To disable caching and load entire file to RAM, compile with "-dmyLoadWholeFileToReadHeader" //To implement the segments, we define these variables: // fileLen = size of file in bytes // MaxBufferSz = maximum size of buffer in bytes // Buffer = array with n elements, where n is smaller of fileLen or MaxBufferSz // lPos = position in Buffer (indexed from 0), 0..(n-1) // lFileOffset = offset of Buffer in file: true file position is lOffset+lPos (initially 0) #ifdef myLoadWholeFileToReadHeader size_t MaxBufferSz = fileLen; #else size_t MaxBufferSz = 1000000; //ideally size of DICOM header, but this varies from 2D to 4D files #endif if (MaxBufferSz > (size_t)fileLen) MaxBufferSz = fileLen; //printf("%d -> %d\n", MaxBufferSz, fileLen); long lFileOffset = 0; fseek(file, 0, SEEK_SET); //Allocate memory unsigned char *buffer=(unsigned char *)malloc(MaxBufferSz+1); if (!buffer) { printError( "Memory exhausted!"); fclose(file); return d; } //Read file contents into buffer size_t sz = fread(buffer, 1, MaxBufferSz, file); if (sz < MaxBufferSz) { printError("Only loaded %zu of %zu bytes for %s\n", sz, MaxBufferSz, fname); fclose(file); return d; } #ifdef myLoadWholeFileToReadHeader fclose(file); #endif //DEFINE DICOM TAGS #define kUnused 0x0001+(0x0001 << 16 ) #define kStart 0x0002+(0x0000 << 16 ) #define kTransferSyntax 0x0002+(0x0010 << 16) //#define kImplementationVersionName 0x0002+(0x0013 << 16) #define kSourceApplicationEntityTitle 0x0002+(0x0016 << 16 ) //#define kSpecificCharacterSet 0x0008+(0x0005 << 16 ) //someday we should handle foreign characters... #define kImageTypeTag 0x0008+(0x0008 << 16 ) #define kStudyDate 0x0008+(0x0020 << 16 ) #define kAcquisitionDate 0x0008+(0x0022 << 16 ) #define kAcquisitionDateTime 0x0008+(0x002A << 16 ) #define kStudyTime 0x0008+(0x0030 << 16 ) #define kAcquisitionTime 0x0008+(0x0032 << 16 ) #define kModality 0x0008+(0x0060 << 16 ) //CS #define kManufacturer 0x0008+(0x0070 << 16 ) #define kInstitutionName 0x0008+(0x0080 << 16 ) #define kInstitutionAddress 0x0008+(0x0081 << 16 ) #define kReferringPhysicianName 0x0008+(0x0090 << 16 ) #define kStationName 0x0008+(0x1010 << 16 ) #define kSeriesDescription 0x0008+(0x103E << 16 ) // '0008' '103E' 'LO' 'SeriesDescription' #define kInstitutionalDepartmentName 0x0008+(0x1040 << 16 ) #define kManufacturersModelName 0x0008+(0x1090 << 16 ) #define kDerivationDescription 0x0008+(0x2111 << 16 ) #define kComplexImageComponent (uint32_t) 0x0008+(0x9208 << 16 )//'0008' '9208' 'CS' 'ComplexImageComponent' #define kPatientName 0x0010+(0x0010 << 16 ) #define kPatientID 0x0010+(0x0020 << 16 ) #define kPatientBirthDate 0x0010+(0x0030 << 16 ) #define kPatientSex 0x0010+(0x0040 << 16 ) #define kPatientAge 0x0010+(0x1010 << 16 ) #define kPatientWeight 0x0010+(0x1030 << 16 ) #define kAnatomicalOrientationType 0x0010+(0x2210 << 16 ) #define kBodyPartExamined 0x0018+(0x0015 << 16) #define kScanningSequence 0x0018+(0x0020 << 16) #define kSequenceVariant 0x0018+(0x0021 << 16) #define kScanOptions 0x0018+(0x0022 << 16) #define kMRAcquisitionType 0x0018+(0x0023 << 16) #define kSequenceName 0x0018+(0x0024 << 16) #define kZThick 0x0018+(0x0050 << 16 ) #define kTR 0x0018+(0x0080 << 16 ) #define kTE 0x0018+(0x0081 << 16 ) #define kTI 0x0018+(0x0082 << 16) // Inversion time #define kEchoNum 0x0018+(0x0086 << 16 ) //IS #define kMagneticFieldStrength 0x0018+(0x0087 << 16 ) //DS #define kZSpacing 0x0018+(0x0088 << 16 ) //'DS' 'SpacingBetweenSlices' #define kPhaseEncodingSteps 0x0018+(0x0089 << 16 ) //'IS' #define kEchoTrainLength 0x0018+(0x0091 << 16 ) //IS #define kPhaseFieldofView 0x0018+(0x0094 << 16 ) //'DS' #define kPixelBandwidth 0x0018+(0x0095 << 16 ) //'DS' 'PixelBandwidth' #define kDeviceSerialNumber 0x0018+(0x1000 << 16 ) //LO #define kSoftwareVersions 0x0018+(0x1020 << 16 ) //LO #define kProtocolName 0x0018+(0x1030<< 16 ) #define kRadionuclideTotalDose 0x0018+(0x1074<< 16 ) #define kRadionuclideHalfLife 0x0018+(0x1075<< 16 ) #define kRadionuclidePositronFraction 0x0018+(0x1076<< 16 ) #define kGantryTilt 0x0018+(0x1120 << 16 ) #define kXRayExposure 0x0018+(0x1152 << 16 ) #define kAcquisitionMatrix 0x0018+(0x1310 << 16 ) //US #define kFlipAngle 0x0018+(0x1314 << 16 ) #define kInPlanePhaseEncodingDirection 0x0018+(0x1312<< 16 ) //CS #define kSAR 0x0018+(0x1316 << 16 ) //'DS' 'SAR' #define kPatientOrient 0x0018+(0x5100<< 16 ) //0018,5100. patient orientation - 'HFS' #define kDiffusionDirectionality 0x0018+uint32_t(0x9075<< 16 ) // NONE, ISOTROPIC, or DIRECTIONAL //#define kDiffusionBFactorSiemens 0x0019+(0x100C<< 16 ) // 0019;000C;SIEMENS MR HEADER ;B_value #define kDiffusion_bValue 0x0018+uint32_t(0x9087<< 16 ) // FD #define kDiffusionOrientation 0x0018+uint32_t(0x9089<< 16 ) // FD, seen in enhanced // DICOM from Philips 5.* // and Siemens XA10. #define kDwellTime 0x0019+(0x1018<< 16 ) //IS in NSec, see https://github.com/rordenlab/dcm2niix/issues/127 #define kLastScanLoc 0x0019+(0x101B<< 16 ) #define kDiffusionDirectionGEX 0x0019+(0x10BB<< 16 ) //DS #define kDiffusionDirectionGEY 0x0019+(0x10BC<< 16 ) //DS #define kDiffusionDirectionGEZ 0x0019+(0x10BD<< 16 ) //DS #define kSharedFunctionalGroupsSequence 0x5200+uint32_t(0x9229<< 16 ) // SQ #define kPerFrameFunctionalGroupsSequence 0x5200+uint32_t(0x9230<< 16 ) // SQ #define kBandwidthPerPixelPhaseEncode 0x0019+(0x1028<< 16 ) //FD #define kStudyID 0x0020+(0x0010 << 16 ) #define kSeriesNum 0x0020+(0x0011 << 16 ) #define kAcquNum 0x0020+(0x0012 << 16 ) #define kImageNum 0x0020+(0x0013 << 16 ) #define kStudyInstanceUID 0x0020+(0x000D << 16 ) #define kSeriesInstanceUID 0x0020+(0x000E << 16 ) #define kPatientPosition 0x0020+(0x0032 << 16 ) // Actually ImagePositionPatient! #define kOrientationACR 0x0020+(0x0035 << 16 ) #define kOrientation 0x0020+(0x0037 << 16 ) #define kImagesInAcquisition 0x0020+(0x1002 << 16 ) //IS #define kImageComments 0x0020+(0x4000<< 16 )// '0020' '4000' 'LT' 'ImageComments' #define kDimensionIndexValues 0x0020+uint32_t(0x9157<< 16 ) // UL n-dimensional index of frame. #define kLocationsInAcquisitionGE 0x0021+(0x104F<< 16 )// 'SS' 'LocationsInAcquisitionGE' #define kSamplesPerPixel 0x0028+(0x0002 << 16 ) #define kPhotometricInterpretation 0x0028+(0x0004 << 16 ) #define kPlanarRGB 0x0028+(0x0006 << 16 ) #define kDim3 0x0028+(0x0008 << 16 ) //number of frames - for Philips this is Dim3*Dim4 #define kDim2 0x0028+(0x0010 << 16 ) #define kDim1 0x0028+(0x0011 << 16 ) #define kXYSpacing 0x0028+(0x0030 << 16 ) //'0028' '0030' 'DS' 'PixelSpacing' #define kBitsAllocated 0x0028+(0x0100 << 16 ) #define kBitsStored 0x0028+(0x0101 << 16 )//'0028' '0101' 'US' 'BitsStored' #define kIsSigned 0x0028+(0x0103 << 16 ) #define kIntercept 0x0028+(0x1052 << 16 ) #define kSlope 0x0028+(0x1053 << 16 ) #define kGeiisFlag 0x0029+(0x0010 << 16 ) //warn user if dreaded GEIIS was used to process image #define kCSAImageHeaderInfo 0x0029+(0x1010 << 16 ) #define kCSASeriesHeaderInfo 0x0029+(0x1020 << 16 ) //#define kObjectGraphics 0x0029+(0x1210 << 16 ) //0029,1210 syngoPlatformOOGInfo Object Oriented Graphics #define kProcedureStepDescription 0x0040+(0x0254 << 16 ) #define kRealWorldIntercept 0x0040+uint32_t(0x9224 << 16 ) //IS dicm2nii's SlopInt_6_9 #define kRealWorldSlope 0x0040+uint32_t(0x9225 << 16 ) //IS dicm2nii's SlopInt_6_9 #define kEffectiveEchoSpacingGE 0x0043+(0x102C << 16 ) //SS #define kDiffusionBFactorGE 0x0043+(0x1039 << 16 ) //IS dicm2nii's SlopInt_6_9 #define kCoilSiemens 0x0051+(0x100F << 16 ) #define kImaPATModeText 0x0051+(0x1011 << 16 ) #define kLocationsInAcquisition 0x0054+(0x0081 << 16 ) //ftp://dicom.nema.org/MEDICAL/dicom/2014c/output/chtml/part03/sect_C.8.9.4.html //If ImageType is REPROJECTION we slice direction is reversed - need example to test // #define kSeriesType 0x0054+(0x1000 << 16 ) #define kDoseCalibrationFactor 0x0054+(0x1322<< 16 ) #define kIconImageSequence 0x0088+(0x0200 << 16 ) #define kDiffusionBFactor 0x2001+(0x1003 << 16 )// FL #define kSliceNumberMrPhilips 0x2001+(0x100A << 16 ) //IS Slice_Number_MR #define kNumberOfSlicesMrPhilips 0x2001+(0x1018 << 16 )//SL 0x2001, 0x1018 ), "Number_of_Slices_MR" #define kSliceOrient 0x2001+(0x100B << 16 )//2001,100B Philips slice orientation (TRANSVERSAL, AXIAL, SAGITTAL) //#define kLocationsInAcquisitionPhilips 0x2001+(0x1018 << 16 ) // //#define kStackSliceNumber 0x2001+(0x1035 << 16 )//? Potential way to determine slice order for Philips? #define kNumberOfDynamicScans 0x2001+(0x1081 << 16 )//'2001' '1081' 'IS' 'NumberOfDynamicScans' #define kMRAcquisitionTypePhilips 0x2005+(0x106F << 16) #define kAngulationAP 0x2005+(0x1071 << 16)//'2005' '1071' 'FL' 'MRStackAngulationAP' #define kAngulationFH 0x2005+(0x1072 << 16)//'2005' '1072' 'FL' 'MRStackAngulationFH' #define kAngulationRL 0x2005+(0x1073 << 16)//'2005' '1073' 'FL' 'MRStackAngulationRL' #define kMRStackOffcentreAP 0x2005+(0x1078 << 16) #define kMRStackOffcentreFH 0x2005+(0x1079 << 16) #define kMRStackOffcentreRL 0x2005+(0x107A << 16) #define kPhilipsSlope 0x2005+(0x100E << 16 ) #define kDiffusionDirectionRL 0x2005+(0x10B0 << 16) #define kDiffusionDirectionAP 0x2005+(0x10B1 << 16) #define kDiffusionDirectionFH 0x2005+(0x10B2 << 16) #define kPrivatePerFrameSq 0x2005+(0x140F << 16) #define kWaveformSq 0x5400+(0x0100 << 16) #define kImageStart 0x7FE0+(0x0010 << 16 ) #define kImageStartFloat 0x7FE0+(0x0008 << 16 ) #define kImageStartDouble 0x7FE0+(0x0009 << 16 ) uint32_t kNest = 0xFFFE +(0xE000 << 16 ); //#define kNest 0xFFFE +(0xE000 << 16 ) //Item follows SQ uint32_t kUnnest = 0xFFFE +(0xE00D << 16 ); //#define kUnnest 0xFFFE +(0xE00D << 16 ) //ItemDelimitationItem [length defined] http://www.dabsoft.ch/dicom/5/7.5/ uint32_t kUnnest2 = 0xFFFE +(0xE0DD << 16 ); //#define kUnnest2 0xFFFE +(0xE0DD << 16 )//SequenceDelimitationItem [length undefined] int nest = 0; //double zSpacing = -1.0l; //includes slice thickness plus gap int locationsInAcquisitionGE = 0; int locationsInAcquisitionPhilips = 0; int imagesInAcquisition = 0; uint32_t lLength; uint32_t groupElement; long lPos = 0; if (isPart10prefix) { //for part 10 files, skip preamble and prefix lPos = 128+4; //4-byte signature starts at 128 groupElement = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24); if (groupElement != kStart) printMessage("DICOM appears corrupt: first group:element should be 0x0002:0x0000 '%s'\n", fname); } char vr[2]; //float intenScalePhilips = 0.0; char acquisitionDateTimeTxt[kDICOMStr] = ""; bool isEncapsulatedData = false; int multiBandFactor = 0; int encapsulatedDataFragments = 0; int encapsulatedDataFragmentStart = 0; //position of first FFFE,E000 for compressed images int encapsulatedDataImageStart = 0; //position of 7FE0,0010 for compressed images (where actual image start should be start of first fragment) bool isOrient = false; bool isIconImageSequence = false; bool isSwitchToImplicitVR = false; bool isSwitchToBigEndian = false; //bool geiisBug = false; //for buggy GEIIS http://forum.dcmtk.org/viewtopic.php?p=7162&sid=3b516cc751aae51fbb5e73184abe37c2 bool is2005140FSQ = false; //for buggy Philips bool is2005140FSQwarned = false; //for buggy Philips bool isAtFirstPatientPosition = false; //for 3d and 4d files: flag is true for slices at same position as first slice bool isMosaic = false; int patientPositionNum = 0; int sqDepth = 0; float patientPosition[4] = {NAN, NAN, NAN, NAN}; //used to compute slice direction for Philips 4D float patientPositionEndPhilips[4] = {NAN, NAN, NAN, NAN}; float patientPositionStartPhilips[4] = {NAN, NAN, NAN, NAN}; while ((d.imageStart == 0) && ((lPos+8+lFileOffset) < fileLen)) { #ifndef myLoadWholeFileToReadHeader //read one segment at a time if ((size_t)(lPos + 128) > MaxBufferSz) { //avoid overreading the file lFileOffset = lFileOffset + lPos; if ((lFileOffset+MaxBufferSz) > (size_t)fileLen) MaxBufferSz = fileLen - lFileOffset; fseek(file, lFileOffset, SEEK_SET); size_t sz = fread(buffer, 1, MaxBufferSz, file); if (sz < MaxBufferSz) { printError("Only loaded %zu of %zu bytes for %s\n", sz, MaxBufferSz, fname); fclose(file); return d; } lPos = 0; } #endif if (d.isLittleEndian) groupElement = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24); else groupElement = buffer[lPos+1] | (buffer[lPos] << 8) | (buffer[lPos+3] << 16) | (buffer[lPos+2] << 24); if ((isSwitchToBigEndian) && ((groupElement & 0xFFFF) != 2)) { isSwitchToBigEndian = false; d.isLittleEndian = false; groupElement = buffer[lPos+1] | (buffer[lPos] << 8) | (buffer[lPos+3] << 16) | (buffer[lPos+2] << 24); }//transfer syntax requests switching endian after group 0002 if ((isSwitchToImplicitVR) && ((groupElement & 0xFFFF) != 2)) { isSwitchToImplicitVR = false; d.isExplicitVR = false; } //transfer syntax requests switching VR after group 0001 //uint32_t group = (groupElement & 0xFFFF); lPos += 4; if ((groupElement == kUnnest) || (groupElement == kUnnest2)) isIconImageSequence = false; if (((groupElement == kNest) || (groupElement == kUnnest) || (groupElement == kUnnest2)) && (!isEncapsulatedData)) { //if (((groupElement == kNest) || (groupElement == kUnnest) || (groupElement == kUnnest2)) ) { vr[0] = 'N'; vr[1] = 'A'; if (groupElement == kUnnest2) sqDepth--; //if (groupElement == kUnnest) geiisBug = false; //don't exit if there is a proprietary thumbnail lLength = 4; } else if (d.isExplicitVR) { vr[0] = buffer[lPos]; vr[1] = buffer[lPos+1]; if (buffer[lPos+1] < 'A') {//implicit vr with 32-bit length if (d.isLittleEndian) lLength = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24); else lLength = buffer[lPos+3] | (buffer[lPos+2] << 8) | (buffer[lPos+1] << 16) | (buffer[lPos] << 24); lPos += 4; } else if ( ((buffer[lPos] == 'U') && (buffer[lPos+1] == 'N')) || ((buffer[lPos] == 'U') && (buffer[lPos+1] == 'T')) || ((buffer[lPos] == 'O') && (buffer[lPos+1] == 'B')) || ((buffer[lPos] == 'O') && (buffer[lPos+1] == 'W')) ) { //VR= UN, OB, OW, SQ || ((buffer[lPos] == 'S') && (buffer[lPos+1] == 'Q')) lPos = lPos + 4; //skip 2 byte VR string and 2 reserved bytes = 4 bytes if (d.isLittleEndian) lLength = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24); else lLength = buffer[lPos+3] | (buffer[lPos+2] << 8) | (buffer[lPos+1] << 16) | (buffer[lPos] << 24); lPos = lPos + 4; //skip 4 byte length } else if ((buffer[lPos] == 'S') && (buffer[lPos+1] == 'Q')) { lLength = 8; //Sequence Tag //printMessage(" !!!SQ\t%04x,%04x\n", groupElement & 65535,groupElement>>16); is2005140FSQ = (groupElement == kPrivatePerFrameSq); } else { //explicit VR with 16-bit length if ((d.isLittleEndian) ) lLength = buffer[lPos+2] | (buffer[lPos+3] << 8); else lLength = buffer[lPos+3] | (buffer[lPos+2] << 8); lPos += 4; //skip 2 byte VR string and 2 length bytes = 4 bytes } } else { //implicit VR vr[0] = 'N'; vr[1] = 'A'; if (d.isLittleEndian) lLength = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24); else lLength = buffer[lPos+3] | (buffer[lPos+2] << 8) | (buffer[lPos+1] << 16) | (buffer[lPos] << 24); lPos += 4; //we have loaded the 32-bit length if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isSQ(groupElement))) { //https://github.com/rordenlab/dcm2niix/issues/144 vr[0] = 'S'; vr[1] = 'Q'; lLength = 8; //Sequence Tag is2005140FSQ = (groupElement == kPrivatePerFrameSq); //if (is2005140FSQ) printMessage(" !!!SQ\t%04x,%04x\n", groupElement & 65535,groupElement>>16); } } //if explicit else implicit VR if (lLength == 0xFFFFFFFF) { lLength = 8; //SQ (Sequences) use 0xFFFFFFFF [4294967295] to denote unknown length vr[0] = 'S'; vr[1] = 'Q'; } if ((vr[0] == 'S') && (vr[1] == 'Q')) { sqDepth++; //printMessage("SQstart %d\n", sqDepth); } if ((groupElement == kNest) || ((vr[0] == 'S') && (vr[1] == 'Q'))) nest++; //if ((groupElement == kUnnest) || (groupElement == kUnnest2)) { // <- ? if (groupElement == kUnnest) { nest--; } //next: look for required tags if ((groupElement == kNest) && (isEncapsulatedData)) { d.imageBytes = dcmInt(4,&buffer[lPos-4],d.isLittleEndian); //printMessage("compressed data %d-> %ld\n",d.imageBytes, lPos); if (d.imageBytes > 128) { encapsulatedDataFragments++; if (encapsulatedDataFragmentStart == 0) encapsulatedDataFragmentStart = (int)lPos + (int)lFileOffset; } } if ((isIconImageSequence) && ((groupElement & 0x0028) == 0x0028 )) groupElement = kUnused; //ignore icon dimensions if (true) { //(nest <= 0) { //some Philips images have different 0020,0013 //verbose reporting : // printMessage("Pos %ld GroupElement %#08x,%#08x Length %d isLittle %d\n", lPos, (groupElement & 0xFFFF), (groupElement >> 16), lLength, d.isLittleEndian); // // Debugging // int groupItem = groupElement >> 16; // int groupGroup = groupElement - (groupItem << 16); // printMessage("groupElement: (%04X, %04X)\n", groupGroup, groupItem); switch ( groupElement ) { case kTransferSyntax: { char transferSyntax[kDICOMStr]; dcmStr (lLength, &buffer[lPos], transferSyntax); //printMessage("%d transfer syntax>>> '%s'\n", compressFlag, transferSyntax); if (strcmp(transferSyntax, "1.2.840.10008.1.2.1") == 0) ; //default isExplicitVR=true; //d.isLittleEndian=true else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.50") == 0) { d.compressionScheme = kCompress50; //printMessage("Lossy JPEG: please decompress with Osirix or dcmdjpg. %s\n", transferSyntax); //d.imageStart = 1;//abort as invalid (imageStart MUST be >128) } else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.51") == 0) { d.compressionScheme = kCompress50; //printMessage("Lossy JPEG: please decompress with Osirix or dcmdjpg. %s\n", transferSyntax); //d.imageStart = 1;//abort as invalid (imageStart MUST be >128) //uJPEG does not decode these: ..53 ...55 // } else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.53") == 0) { // d.compressionScheme = kCompress50; } else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.57") == 0) { //d.isCompressed = true; //https://www.medicalconnections.co.uk/kb/Transfer_Syntax should be SOF = 0xC3 d.compressionScheme = kCompressC3; //printMessage("Ancient JPEG-lossless (SOF type 0xc3): please check conversion\n"); } else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.70") == 0) { d.compressionScheme = kCompressC3; } else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.80") == 0) { printMessage("Unsupported transfer syntax '%s' (decode with dcmdjpls or gdcmconv)\n",transferSyntax); d.imageStart = 1;//abort as invalid (imageStart MUST be >128) } else if ((compressFlag != kCompressNone) && (strcmp(transferSyntax, "1.2.840.10008.1.2.4.90") == 0)) { d.compressionScheme = kCompressYes; //printMessage("JPEG2000 Lossless support is new: please validate conversion\n"); } else if ((compressFlag != kCompressNone) && (strcmp(transferSyntax, "1.2.840.10008.1.2.4.91") == 0)) { d.compressionScheme = kCompressYes; //printMessage("JPEG2000 support is new: please validate conversion\n"); } else if (strcmp(transferSyntax, "1.2.840.10008.1.2.5") == 0) d.compressionScheme = kCompressRLE; //run length else if (strcmp(transferSyntax, "1.2.840.10008.1.2.2") == 0) isSwitchToBigEndian = true; //isExplicitVR=true; else if (strcmp(transferSyntax, "1.2.840.10008.1.2") == 0) isSwitchToImplicitVR = true; //d.isLittleEndian=true else { printMessage("Unsupported transfer syntax '%s' (see www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage)\n",transferSyntax); d.imageStart = 1;//abort as invalid (imageStart MUST be >128) } break;} //{} provide scope for variable 'transferSyntax /*case kImplementationVersionName: { char impTxt[kDICOMStr]; dcmStr (lLength, &buffer[lPos], impTxt); int slen = (int) strlen(impTxt); if((slen < 6) || (strstr(impTxt, "OSIRIX") == NULL) ) break; printError("OSIRIX Detected\n"); break; }*/ case kSourceApplicationEntityTitle: { char saeTxt[kDICOMStr]; dcmStr (lLength, &buffer[lPos], saeTxt); int slen = (int) strlen(saeTxt); if((slen < 5) || (strstr(saeTxt, "oasis") == NULL) ) break; d.isSegamiOasis = true; break; } case kImageTypeTag: dcmStr (lLength, &buffer[lPos], d.imageType); int slen; slen = (int) strlen(d.imageType); //if (strcmp(transferSyntax, "ORIGINAL_PRIMARY_M_ND_MOSAIC") == 0) if((slen > 5) && !strcmp(d.imageType + slen - 6, "MOSAIC") ) isMosaic = true; //isNonImage 0008,0008 = DERIVED,CSAPARALLEL,POSDISP // attempt to detect non-images, see https://github.com/scitran/data/blob/a516fdc39d75a6e4ac75d0e179e18f3a5fc3c0af/scitran/data/medimg/dcm/mr/siemens.py if((slen > 6) && (strstr(d.imageType, "DERIVED") != NULL) ) d.isDerived = true; //if((slen > 4) && (strstr(typestr, "DIS2D") != NULL) ) // d.isNonImage = true; break; case kAcquisitionDate: char acquisitionDateTxt[kDICOMStr]; dcmStr (lLength, &buffer[lPos], acquisitionDateTxt); d.acquisitionDate = atof(acquisitionDateTxt); break; case kAcquisitionDateTime: //char acquisitionDateTimeTxt[kDICOMStr]; dcmStr (lLength, &buffer[lPos], acquisitionDateTimeTxt); //printMessage("%s\n",acquisitionDateTimeTxt); break; case kStudyDate: dcmStr (lLength, &buffer[lPos], d.studyDate); break; case kModality: if (lLength < 2) break; if ((buffer[lPos]=='C') && (toupper(buffer[lPos+1]) == 'R')) d.modality = kMODALITY_CR; else if ((buffer[lPos]=='C') && (toupper(buffer[lPos+1]) == 'T')) d.modality = kMODALITY_CT; if ((buffer[lPos]=='M') && (toupper(buffer[lPos+1]) == 'R')) d.modality = kMODALITY_MR; if ((buffer[lPos]=='P') && (toupper(buffer[lPos+1]) == 'T')) d.modality = kMODALITY_PT; if ((buffer[lPos]=='U') && (toupper(buffer[lPos+1]) == 'S')) d.modality = kMODALITY_US; break; case kManufacturer: d.manufacturer = dcmStrManufacturer (lLength, &buffer[lPos]); volDiffusion.manufacturer = d.manufacturer; break; case kInstitutionName: dcmStr(lLength, &buffer[lPos], d.institutionName); break; case kInstitutionAddress: dcmStr(lLength, &buffer[lPos], d.institutionAddress); break; case kReferringPhysicianName: dcmStr(lLength, &buffer[lPos], d.referringPhysicianName); break; case kComplexImageComponent: if (lLength < 2) break; d.isHasPhase = (buffer[lPos]=='P') && (toupper(buffer[lPos+1]) == 'H'); d.isHasMagnitude = (buffer[lPos]=='M') && (toupper(buffer[lPos+1]) == 'A'); break; case kAcquisitionTime : char acquisitionTimeTxt[kDICOMStr]; dcmStr (lLength, &buffer[lPos], acquisitionTimeTxt); d.acquisitionTime = atof(acquisitionTimeTxt); //printMessage("%s\n",acquisitionTimeTxt); break; case kStudyTime : dcmStr (lLength, &buffer[lPos], d.studyTime); break; case kPatientName : dcmStr (lLength, &buffer[lPos], d.patientName); break; case kAnatomicalOrientationType: { char aotTxt[kDICOMStr]; //ftp://dicom.nema.org/MEDICAL/dicom/2015b/output/chtml/part03/sect_C.7.6.2.html#sect_C.7.6.2.1.1 dcmStr (lLength, &buffer[lPos], aotTxt); int slen = (int) strlen(aotTxt); if((slen < 9) || (strstr(aotTxt, "QUADRUPED") == NULL) ) break; printError("Anatomical Orientation Type (0010,2210) is QUADRUPED: rotate coordinates accordingly"); break; } case kPatientID : dcmStr (lLength, &buffer[lPos], d.patientID); break; case kPatientBirthDate : dcmStr (lLength, &buffer[lPos], d.patientBirthDate); break; case kPatientSex : d.patientSex = toupper(buffer[lPos]); //first character is either 'R'ow or 'C'ol break; case kPatientAge : dcmStr (lLength, &buffer[lPos], d.patientAge); break; case kPatientWeight : d.patientWeight = dcmStrFloat(lLength, &buffer[lPos]); break; case kStationName : dcmStr (lLength, &buffer[lPos], d.stationName); break; case kSeriesDescription: { dcmStr (lLength, &buffer[lPos], d.seriesDescription); break; } case kInstitutionalDepartmentName: dcmStr (lLength, &buffer[lPos], d.institutionalDepartmentName); break; case kManufacturersModelName : dcmStr (lLength, &buffer[lPos], d.manufacturersModelName); break; case kDerivationDescription : { //strcmp(transferSyntax, "1.2.840.10008.1.2") char derivationDescription[kDICOMStr]; dcmStr (lLength, &buffer[lPos], derivationDescription);//strcasecmp, strcmp if (strcasecmp(derivationDescription, "MEDCOM_RESAMPLED") == 0) d.isResampled = true; break; } case kDeviceSerialNumber : { dcmStr (lLength, &buffer[lPos], d.deviceSerialNumber); break; } case kSoftwareVersions : { dcmStr (lLength, &buffer[lPos], d.softwareVersions); break; } case kProtocolName : { //if ((strlen(d.protocolName) < 1) || (d.manufacturer != kMANUFACTURER_GE)) //GE uses a generic session name here: do not overwrite kProtocolNameGE dcmStr (lLength, &buffer[lPos], d.protocolName); //see also kSequenceName break; } case kPatientOrient : dcmStr (lLength, &buffer[lPos], d.patientOrient); break; case kDiffusionDirectionality : // 0018, 9075 set_directionality0018_9075(&volDiffusion, (&buffer[lPos])); break; case kDwellTime : d.dwellTime = dcmStrInt(lLength, &buffer[lPos]); break; case kLastScanLoc : d.lastScanLoc = dcmStrFloat(lLength, &buffer[lPos]); break; /*case kDiffusionBFactorSiemens : if (d.manufacturer == kMANUFACTURER_SIEMENS) printMessage("last scan location %f\n,",dcmStrFloat(lLength, &buffer[lPos])); break;*/ case kDiffusionDirectionGEX : if (d.manufacturer == kMANUFACTURER_GE) set_diffusion_directionGE(&volDiffusion, lLength, (&buffer[lPos]), 0); break; case kDiffusionDirectionGEY : if (d.manufacturer == kMANUFACTURER_GE) set_diffusion_directionGE(&volDiffusion, lLength, (&buffer[lPos]), 1); break; case kDiffusionDirectionGEZ : if (d.manufacturer == kMANUFACTURER_GE) set_diffusion_directionGE(&volDiffusion, lLength, (&buffer[lPos]), 2); break; case kBandwidthPerPixelPhaseEncode: d.bandwidthPerPixelPhaseEncode = dcmFloatDouble(lLength, &buffer[lPos],d.isLittleEndian); break; case kStudyInstanceUID : // 0020, 000D dcmStr (lLength, &buffer[lPos], d.studyInstanceUID); break; case kSeriesInstanceUID : // 0020, 000E dcmStr (lLength, &buffer[lPos], d.seriesInstanceUID); break; case kPatientPosition : // 0020, 0032, ImagePositionPatient if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (is2005140FSQ)) { if (!is2005140FSQwarned) printWarning("Philips R3.2.2 can report different positions for the same slice. Attempting patch.\n"); is2005140FSQwarned = true; } else { patientPositionNum++; isAtFirstPatientPosition = true; dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, &patientPosition[0]); //slice position if (isnan(d.patientPosition[1])) { //dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, &d.patientPosition[0]); //slice position for (int k = 0; k < 4; k++) d.patientPosition[k] = patientPosition[k]; } else { //dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, &d.patientPositionLast[0]); //slice direction for 4D for (int k = 0; k < 4; k++) d.patientPositionLast[k] = patientPosition[k]; if ((isFloatDiff(d.patientPositionLast[1],d.patientPosition[1])) || (isFloatDiff(d.patientPositionLast[2],d.patientPosition[2])) || (isFloatDiff(d.patientPositionLast[3],d.patientPosition[3])) ) { isAtFirstPatientPosition = false; //this slice is not at position of 1st slice if (d.patientPositionSequentialRepeats == 0) //this is the first slice with different position d.patientPositionSequentialRepeats = patientPositionNum-1; } //if different position from 1st slice in file } //if not first slice in file set_isAtFirstPatientPosition_tvd(&volDiffusion, isAtFirstPatientPosition); if (isVerbose > 1) printMessage(" Patient Position 0020,0032 (#,@,X,Y,Z)\t%d\t%ld\t%g\t%g\t%g\n", patientPositionNum, lPos, patientPosition[1], patientPosition[2], patientPosition[3]); } //not after 2005,140F break; case kInPlanePhaseEncodingDirection: d.phaseEncodingRC = toupper(buffer[lPos]); //first character is either 'R'ow or 'C'ol break; case kSAR: d.SAR = dcmStrFloat(lLength, &buffer[lPos]); break; case kStudyID: dcmStr (lLength, &buffer[lPos], d.studyID); break; case kSeriesNum: d.seriesNum = dcmStrInt(lLength, &buffer[lPos]); break; case kAcquNum: d.acquNum = dcmStrInt(lLength, &buffer[lPos]); break; case kImageNum: //int dx = 3; if (d.imageNum <= 1) d.imageNum = dcmStrInt(lLength, &buffer[lPos]); //Philips renames each image as image1 in 2001,9000 break; case kDimensionIndexValues: // kImageNum is not enough for 4D series from Philips 5.*. { // { necessary for initializing ndim. uint8_t ndim = lLength / 4; if(ndim > MAX_NUMBER_OF_DIMENSIONS){ // Ideally degenerate axes would be cleverly handled. // They are commonly seen in NIfTIs, but perhaps not in DICOM. printError("%d is too many dimensions. Only up to %d are supported\n", ndim, MAX_NUMBER_OF_DIMENSIONS); ndim = MAX_NUMBER_OF_DIMENSIONS; // Truncate } dcmMultiLongs(4 * ndim, &buffer[lPos], ndim, d.dimensionIndexValues, d.isLittleEndian); } break; case kPhotometricInterpretation: char interp[kDICOMStr]; dcmStr (lLength, &buffer[lPos], interp); if (strcmp(interp, "PALETTE_COLOR") == 0) printError("Photometric Interpretation 'PALETTE COLOR' not supported\n"); break; case kPlanarRGB: d.isPlanarRGB = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kDim3: d.xyzDim[3] = dcmStrInt(lLength, &buffer[lPos]); break; case kSamplesPerPixel: d.samplesPerPixel = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kDim2: d.xyzDim[2] = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kDim1: d.xyzDim[1] = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kXYSpacing: dcmMultiFloat(lLength, (char*)&buffer[lPos], 2, d.xyzMM); break; case kImageComments: dcmStr (lLength, &buffer[lPos], d.imageComments, true); break; case kLocationsInAcquisitionGE: locationsInAcquisitionGE = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kDoseCalibrationFactor : d.doseCalibrationFactor = dcmStrFloat(lLength, &buffer[lPos]); break; case kBitsAllocated : d.bitsAllocated = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kBitsStored : d.bitsStored = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kIsSigned : //http://dicomiseasy.blogspot.com/2012/08/chapter-12-pixel-data.html d.isSigned = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kTR : d.TR = dcmStrFloat(lLength, &buffer[lPos]); break; case kTE : d.TE = dcmStrFloat(lLength, &buffer[lPos]); break; case kTI : d.TI = dcmStrFloat(lLength, &buffer[lPos]); break; case kEchoNum : d.echoNum = dcmStrInt(lLength, &buffer[lPos]); break; case kMagneticFieldStrength : d.fieldStrength = dcmStrFloat(lLength, &buffer[lPos]); break; case kZSpacing : d.zSpacing = dcmStrFloat(lLength, &buffer[lPos]); break; case kPhaseEncodingSteps : d.phaseEncodingSteps = dcmStrInt(lLength, &buffer[lPos]); break; case kEchoTrainLength : d.echoTrainLength = dcmStrInt(lLength, &buffer[lPos]); break; case kPhaseFieldofView : d.phaseFieldofView = dcmStrFloat(lLength, &buffer[lPos]); break; case kPixelBandwidth : d.pixelBandwidth = dcmStrFloat(lLength, &buffer[lPos]); break; case kAcquisitionMatrix : if (lLength == 8) { uint16_t acquisitionMatrix[4]; dcmMultiShorts(lLength, &buffer[lPos], 4, &acquisitionMatrix[0],d.isLittleEndian); //slice position //phaseEncodingLines stored in either image columns or rows if (acquisitionMatrix[3] > 0) d.phaseEncodingLines = acquisitionMatrix[3]; if (acquisitionMatrix[2] > 0) d.phaseEncodingLines = acquisitionMatrix[2]; } break; case kFlipAngle : d.flipAngle = dcmStrFloat(lLength, &buffer[lPos]); break; case kRadionuclideTotalDose : d.radionuclideTotalDose = dcmStrFloat(lLength, &buffer[lPos]); break; case kRadionuclideHalfLife : d.radionuclideHalfLife = dcmStrFloat(lLength, &buffer[lPos]); break; case kRadionuclidePositronFraction : d.radionuclidePositronFraction = dcmStrFloat(lLength, &buffer[lPos]); break; case kGantryTilt : d.gantryTilt = dcmStrFloat(lLength, &buffer[lPos]); break; case kXRayExposure : //CTs do not have echo times, we use this field to detect different exposures: https://github.com/neurolabusc/dcm2niix/pull/48 if (d.TE == 0) {// for CT we will use exposure (0018,1152) whereas for MR we use echo time (0018,0081) d.isXRay = true; d.TE = dcmStrFloat(lLength, &buffer[lPos]); } break; case kSlope : d.intenScale = dcmStrFloat(lLength, &buffer[lPos]); break; case kPhilipsSlope : if ((lLength == 4) && (d.manufacturer == kMANUFACTURER_PHILIPS)) d.intenScalePhilips = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kIntercept : d.intenIntercept = dcmStrFloat(lLength, &buffer[lPos]); break; case kZThick : d.xyzMM[3] = dcmStrFloat(lLength, &buffer[lPos]); d.zThick = d.xyzMM[3]; break; case kCoilSiemens : { if (d.manufacturer == kMANUFACTURER_SIEMENS) { //see if image from single coil "H12" or an array "HEA;HEP" char coilStr[kDICOMStr]; dcmStr (lLength, &buffer[lPos], coilStr); //long coilNum = 0; char *ptr; dcmStrDigitsOnly(coilStr); d.coilNum = (int)strtol(coilStr, &ptr, 10); if (*ptr != '\0') d.coilNum = 0; } break; } case kImaPATModeText : { //e.g. Siemens iPAT x2 listed as "p2" char accelStr[kDICOMStr]; dcmStr (lLength, &buffer[lPos], accelStr); char *ptr; dcmStrDigitsOnlyKey('p', accelStr); //e.g. if "p2s4" return "2", if "s4" return "" d.accelFactPE = (float)strtof(accelStr, &ptr); if (*ptr != '\0') d.accelFactPE = 0.0; //between slice accel dcmStr (lLength, &buffer[lPos], accelStr); dcmStrDigitsOnlyKey('s', accelStr); //e.g. if "p2s4" return "4", if "p2" return "" multiBandFactor = (int)strtol(accelStr, &ptr, 10); if (*ptr != '\0') multiBandFactor = 0.0; //printMessage("p%gs%d\n", d.accelFactPE, multiBandFactor); break; } case kLocationsInAcquisition : d.locationsInAcquisition = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kIconImageSequence: isIconImageSequence = true; break; /*case kStackSliceNumber: { //https://github.com/Kevin-Mattheus-Moerman/GIBBON/blob/master/dicomDict/PMS-R32-dict.txt int stackSliceNumber = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); printMessage("StackSliceNumber %d\n",stackSliceNumber); break; }*/ case kNumberOfDynamicScans: d.numberOfDynamicScans = dcmStrInt(lLength, &buffer[lPos]); break; case kMRAcquisitionType: //detect 3D acquisition: we can reorient these without worrying about slice time correct or BVEC/BVAL orientation if (lLength > 1) d.is2DAcq = (buffer[lPos]=='2') && (toupper(buffer[lPos+1]) == 'D'); if (lLength > 1) d.is3DAcq = (buffer[lPos]=='3') && (toupper(buffer[lPos+1]) == 'D'); //dcmStr (lLength, &buffer[lPos], d.mrAcquisitionType); break; case kBodyPartExamined : { dcmStr (lLength, &buffer[lPos], d.bodyPartExamined); break; } case kScanningSequence : { dcmStr (lLength, &buffer[lPos], d.scanningSequence); break; } case kSequenceVariant : { dcmStr (lLength, &buffer[lPos], d.sequenceVariant); break; } case kScanOptions: dcmStr (lLength, &buffer[lPos], d.scanOptions); break; case kSequenceName : { //if (strlen(d.protocolName) < 1) //precedence given to kProtocolName and kProtocolNameGE dcmStr (lLength, &buffer[lPos], d.sequenceName); break; } case kMRAcquisitionTypePhilips: //kMRAcquisitionType if (lLength > 1) d.is3DAcq = (buffer[lPos]=='3') && (toupper(buffer[lPos+1]) == 'D'); break; case kAngulationRL: d.angulation[1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kAngulationAP: d.angulation[2] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kAngulationFH: d.angulation[3] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kMRStackOffcentreRL: d.stackOffcentre[1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kMRStackOffcentreAP: d.stackOffcentre[2] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kMRStackOffcentreFH: d.stackOffcentre[3] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kSliceOrient: { char orientStr[kDICOMStr]; orientStr[0] = 'X'; //avoid compiler warning: orientStr filled by dcmStr dcmStr (lLength, &buffer[lPos], orientStr); if (toupper(orientStr[0])== 'S') d.sliceOrient = kSliceOrientSag; //sagittal else if (toupper(orientStr[0])== 'C') d.sliceOrient = kSliceOrientCor; //coronal else d.sliceOrient = kSliceOrientTra; //transverse (axial) break; } // case kDiffusionBFactor: // 2001,1003 // if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition)) { // d.CSA.numDti++; //increment with BFactor: on Philips slices with B=0 have B-factor but no diffusion directions // if (d.CSA.numDti == 2) { //First time we know that this is a 4D DTI dataset // //d.dti4D = (TDTI *)malloc(kMaxDTI4D * sizeof(TDTI)); // dti4D->S[0].V[0] = d.CSA.dtiV[0]; // dti4D->S[0].V[1] = d.CSA.dtiV[1]; // dti4D->S[0].V[2] = d.CSA.dtiV[2]; // dti4D->S[0].V[3] = d.CSA.dtiV[3]; // } // d.CSA.dtiV[0] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); // if ((d.CSA.numDti > 1) && (d.CSA.numDti < kMaxDTI4D)) // dti4D->S[d.CSA.numDti-1].V[0] = d.CSA.dtiV[0]; // /*if ((d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv)) // d.CSA.dtiV[d.CSA.numDti-1][0] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);*/ // } // break; case kDiffusion_bValue: // 0018, 9087 // Note that this is ahead of kPatientPosition (0020,0032), so // isAtFirstPatientPosition is not necessarily set yet. // Philips uses this tag too, at least as of 5.1, but they also // use kDiffusionBFactor (see above), and we do not want to // double count. More importantly, with Philips this tag // (sometimes?) gets repeated in a nested sequence with the // value *unset*! // GE started using this tag in 27, and annoyingly, NOT including // the b value if it is 0 for the slice. if((d.manufacturer != kMANUFACTURER_PHILIPS) || !is2005140FSQ){ // d.CSA.numDti++; // if (d.CSA.numDti == 2) { //First time we know that this is a 4D DTI dataset // //d.dti4D = (TDTI *)malloc(kMaxDTI4D * sizeof(TDTI)); // dti4D->S[0].V[0] = d.CSA.dtiV[0]; // dti4D->S[0].V[1] = d.CSA.dtiV[1]; // dti4D->S[0].V[2] = d.CSA.dtiV[2]; // dti4D->S[0].V[3] = d.CSA.dtiV[3]; // } //d.CSA.dtiV[0] = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian); set_bVal(&volDiffusion, dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian)); // if ((d.CSA.numDti > 1) && (d.CSA.numDti < kMaxDTI4D)) // dti4D->S[d.CSA.numDti-1].V[0] = d.CSA.dtiV[0]; } break; case kDiffusionOrientation: // 0018, 9089 // Note that this is ahead of kPatientPosition (0020,0032), so // isAtFirstPatientPosition is not necessarily set yet. // Philips uses this tag too, at least as of 5.1, but they also // use kDiffusionDirectionRL, etc., and we do not want to double // count. More importantly, with Philips this tag (sometimes?) // gets repeated in a nested sequence with the value *unset*! // if (((d.manufacturer == kMANUFACTURER_SIEMENS) || // ((d.manufacturer == kMANUFACTURER_PHILIPS) && !is2005140FSQ)) && // (isAtFirstPatientPosition || isnan(d.patientPosition[1]))) if((d.manufacturer == kMANUFACTURER_SIEMENS) || ((d.manufacturer == kMANUFACTURER_PHILIPS) && !is2005140FSQ)) set_orientation0018_9089(&volDiffusion, lLength, &buffer[lPos], d.isLittleEndian); break; // case kSharedFunctionalGroupsSequence: // if ((d.manufacturer == kMANUFACTURER_SIEMENS) && isAtFirstPatientPosition) { // break; // For now - need to figure out how to get the nested // // part of buffer[lPos]. // } // break; case kSliceNumberMrPhilips : if (d.manufacturer != kMANUFACTURER_PHILIPS) break; /*if ((d.patientPositionNumPhilips >= kMaxDTI4D) || (d.patientPositionNumPhilips >= locationsInAcquisitionPhilips)) { d.patientPositionNumPhilips++; break; }*/ if ((locationsInAcquisitionPhilips > 0) && (d.patientPositionNumPhilips == locationsInAcquisitionPhilips)) break; //we have acquired all slices in volume (e.g. all volumes after 1st for XYZT storage int sliceNumber; sliceNumber = dcmStrInt(lLength, &buffer[lPos]); if ((d.patientPositionNumPhilips > 0) && (sliceNumber == dti4D->S[d.patientPositionNumPhilips-1].sliceNumberMrPhilips) ) break; //repeated spatial position (e.g. data saved XYTZ so several time points if (d.patientPositionNumPhilips >= kMaxDTI4D) { d.patientPositionNumPhilips++; //fail: out of space break; } dti4D->S[d.patientPositionNumPhilips].sliceNumberMrPhilips = sliceNumber; if ((d.patientPositionNumPhilips > 0) && (abs(dti4D->S[d.patientPositionNumPhilips].sliceNumberMrPhilips - dti4D->S[d.patientPositionNumPhilips -1].sliceNumberMrPhilips) > 1) ) d.isSlicesSpatiallySequentialPhilips = false; //slices not sequential (1,2,3,4 or 4,3,2,1) but 4,3,1,2 d.patientPositionNumPhilips++; //Philips can save 3D acquisitions in a single file with slices stored in non-sequential order. We need to know the first and final spatial position if (sliceNumber == 1) { for (int k = 0; k < 4; k++) patientPositionStartPhilips[k] = patientPosition[k]; } if (sliceNumber == locationsInAcquisitionPhilips) { for (int k = 0; k < 4; k++) patientPositionEndPhilips[k] = patientPosition[k]; } if (isVerbose > 1) printMessage("slice %d is spatial position %d\n", d.patientPositionNumPhilips, sliceNumber); break; case kNumberOfSlicesMrPhilips : if (d.manufacturer != kMANUFACTURER_PHILIPS) break; locationsInAcquisitionPhilips = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; // case kDiffusionDirectionRL: // if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition)) { // d.CSA.dtiV[1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); // if ((d.CSA.numDti > 1) && (d.CSA.numDti < kMaxDTI4D)) // dti4D->S[d.CSA.numDti-1].V[1] = d.CSA.dtiV[1]; // } // /*if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition) && (d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv)) // d.CSA.dtiV[d.CSA.numDti-1][1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);*/ // break; // case kDiffusionDirectionAP: // if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition)) { // d.CSA.dtiV[2] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); // if ((d.CSA.numDti > 1) && (d.CSA.numDti < kMaxDTI4D)) // dti4D->S[d.CSA.numDti-1].V[2] = d.CSA.dtiV[2]; // } // /*if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition) && (d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv)) // d.CSA.dtiV[d.CSA.numDti-1][2] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);*/ // break; // case kDiffusionDirectionFH: // if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition)) { // d.CSA.dtiV[3] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); // if ((d.CSA.numDti > 1) && (d.CSA.numDti < kMaxDTI4D)) // dti4D->S[d.CSA.numDti-1].V[3] = d.CSA.dtiV[3]; // //printMessage("dti XYZ %g %g %g\n",d.CSA.dtiV[1],d.CSA.dtiV[2],d.CSA.dtiV[3]); // } // /*if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition) && (d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv)) // d.CSA.dtiV[d.CSA.numDti-1][3] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);*/ // //http://www.na-mic.org/Wiki/index.php/NAMIC_Wiki:DTI:DICOM_for_DWI_and_DTI // break; case kWaveformSq: d.imageStart = 1; //abort!!! printMessage("Skipping DICOM (audio not image) '%s'\n", fname); break; case kCSAImageHeaderInfo: readCSAImageHeader(&buffer[lPos], lLength, &d.CSA, isVerbose); //, dti4D); d.isHasPhase = d.CSA.isPhaseMap; break; //case kObjectGraphics: // printMessage("---->%d,",lLength); // break; case kCSASeriesHeaderInfo: //printMessage("Series %d %d\n", lPos, lLength); if ((lPos + lLength) > fileLen) break; d.CSA.SeriesHeader_offset = (int)lPos; d.CSA.SeriesHeader_length = lLength; break; case kRealWorldIntercept: if (isSameFloat(0.0, d.intenIntercept)) //give precedence to standard value d.intenIntercept = dcmFloatDouble(lLength, &buffer[lPos],d.isLittleEndian); break; case kRealWorldSlope: if (isSameFloat(1.0, d.intenScale)) //give precedence to standard value d.intenScale = dcmFloatDouble(lLength, &buffer[lPos],d.isLittleEndian); break; case kEffectiveEchoSpacingGE: if (d.manufacturer == kMANUFACTURER_GE) d.effectiveEchoSpacingGE = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kDiffusionBFactorGE : if (d.manufacturer == kMANUFACTURER_GE) set_bValGE(&volDiffusion, lLength, &buffer[lPos]); break; case kGeiisFlag: if ((lLength > 4) && (buffer[lPos]=='G') && (buffer[lPos+1]=='E') && (buffer[lPos+2]=='I') && (buffer[lPos+3]=='I')) { //read a few digits, as bug is specific to GEIIS, while GEMS are fine printWarning("GEIIS violates the DICOM standard. Inspect results and admonish your vendor.\n"); isIconImageSequence = true; //geiisBug = true; //compressed thumbnails do not follow transfer syntax! GE should not re-use pulbic tags for these proprietary images http://sonca.kasshin.net/gdcm/Doc/GE_ImageThumbnails } break; case kProcedureStepDescription: dcmStr (lLength, &buffer[lPos], d.procedureStepDescription); break; case kOrientationACR : //use in emergency if kOrientation is not present! if (!isOrient) dcmMultiFloat(lLength, (char*)&buffer[lPos], 6, d.orient); break; case kOrientation : dcmMultiFloat(lLength, (char*)&buffer[lPos], 6, d.orient); isOrient = true; break; case kImagesInAcquisition : imagesInAcquisition = dcmStrInt(lLength, &buffer[lPos]); break; case kImageStart: //if ((!geiisBug) && (!isIconImageSequence)) //do not exit for proprietary thumbnails if ((d.compressionScheme == kCompressNone ) && (!isIconImageSequence)) //do not exit for proprietary thumbnails d.imageStart = (int)lPos + (int)lFileOffset; //geiisBug = false; //http://www.dclunie.com/medical-image-faq/html/part6.html //unlike raw data, Encapsulated data is stored as Fragments contained in Items that are the Value field of Pixel Data if ((d.compressionScheme != kCompressNone) && (!isIconImageSequence)) { lLength = 0; isEncapsulatedData = true; encapsulatedDataImageStart = (int)lPos + (int)lFileOffset; //printWarning("Encapsulated\n"); } isIconImageSequence = false; break; case kImageStartFloat: d.isFloat = true; if (!isIconImageSequence) //do not exit for proprietary thumbnails d.imageStart = (int)lPos + (int)lFileOffset; isIconImageSequence = false; break; case kImageStartDouble: printWarning("Double-precision DICOM conversion untested: please provide samples to developer\n"); d.isFloat = true; if (!isIconImageSequence) //do not exit for proprietary thumbnails d.imageStart = (int)lPos + (int)lFileOffset; isIconImageSequence = false; break; } //switch/case for groupElement } //if nest if (isVerbose > 1) { if ((lLength > 12) && (lLength < 128)) { //if length is greater than 8 bytes (+4 hdr) the data must be a string [or image data] char tagStr[kDICOMStr]; tagStr[0] = 'X'; //avoid compiler warning: orientStr filled by dcmStr dcmStr (lLength, &buffer[lPos], tagStr); if (strlen(tagStr) > 1) { for (size_t pos = 0; pos') || (tagStr[pos] == ':') || (tagStr[pos] == '"') || (tagStr[pos] == '\\') || (tagStr[pos] == '/') || (tagStr[pos] == '^') || (tagStr[pos] < 33) || (tagStr[pos] == '*') || (tagStr[pos] == '|') || (tagStr[pos] == '?')) tagStr[pos] = 'x'; } printMessage(" Tag\t%04x,%04x\tSize=%u\tOffset=%ld\t%s\n", groupElement & 65535,groupElement>>16, lLength, lFileOffset+lPos, tagStr); //printMessage(" Tag\t%04x,%04x\tSize=%u\tOffset=%ld\tnest=%d\t%s\n", groupElement & 65535,groupElement>>16, lLength, lPos, nest, tagStr); } else printMessage(" Tag\t%04x,%04x\tSize=%u\tOffset=%ld\tnest=%d\n", groupElement & 65535,groupElement>>16, lLength, lFileOffset+lPos, nest); //if (d.isExplicitVR) printMessage(" VR=%c%c\n", vr[0], vr[1]); } //printMessage(" tag=%04x,%04x length=%u pos=%ld %c%c nest=%d\n", groupElement & 65535,groupElement>>16, lLength, lPos,vr[0], vr[1], nest); lPos = lPos + (lLength); //printMessage("%d\n",d.imageStart); } //while d.imageStart == 0 free (buffer); if (encapsulatedDataFragmentStart > 0) { if (encapsulatedDataFragments > 1) printError("Compressed image stored as %d fragments: decompress with gdcmconv, Osirix, dcmdjpeg or dcmjp2k\n", encapsulatedDataFragments); else d.imageStart = encapsulatedDataFragmentStart; } else if ((isEncapsulatedData) && (d.imageStart < 128)) { //http://www.dclunie.com/medical-image-faq/html/part6.html //Uncompressed data (unencapsulated) is sent in DICOM as a series of raw bytes or words (little or big endian) in the Value field of the Pixel Data element (7FE0,0010). Encapsulated data on the other hand is sent not as raw bytes or words but as Fragments contained in Items that are the Value field of Pixel Data printWarning("DICOM violation (contact vendor): compressed image without image fragments, assuming image offset defined by 0x7FE0,x0010: %s\n", fname); d.imageStart = encapsulatedDataImageStart; } //Recent Philips images include DateTime (0008,002A) but not separate date and time (0008,0022 and 0008,0032) #define kYYYYMMDDlen 8 //how many characters to encode year,month,day in "YYYYDDMM" format if ((strlen(acquisitionDateTimeTxt) > (kYYYYMMDDlen+5)) && (!isFloatDiff(d.acquisitionTime, 0.0f)) && (!isFloatDiff(d.acquisitionDate, 0.0f)) ) { // 20161117131643.80000 -> date 20161117 time 131643.80000 //printMessage("acquisitionDateTime %s\n",acquisitionDateTimeTxt); char acquisitionDateTxt[kDICOMStr]; strncpy(acquisitionDateTxt, acquisitionDateTimeTxt, kYYYYMMDDlen); acquisitionDateTxt[kYYYYMMDDlen] = '\0'; // IMPORTANT! d.acquisitionDate = atof(acquisitionDateTxt); char acquisitionTimeTxt[kDICOMStr]; int timeLen = (int)strlen(acquisitionDateTimeTxt) - kYYYYMMDDlen; strncpy(acquisitionTimeTxt, &acquisitionDateTimeTxt[kYYYYMMDDlen], timeLen); acquisitionTimeTxt[timeLen] = '\0'; // IMPORTANT! d.acquisitionTime = atof(acquisitionTimeTxt); } d.dateTime = (atof(d.studyDate)* 1000000) + atof(d.studyTime); //printMessage("slices in Acq %d %d\n",d.locationsInAcquisition,locationsInAcquisitionPhilips); if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (d.locationsInAcquisition == 0)) d.locationsInAcquisition = locationsInAcquisitionPhilips; if ((d.manufacturer == kMANUFACTURER_GE) && (imagesInAcquisition > 0)) d.locationsInAcquisition = imagesInAcquisition; //e.g. if 72 slices acquired but interpolated as 144 if ((d.manufacturer == kMANUFACTURER_GE) && (d.locationsInAcquisition == 0)) d.locationsInAcquisition = locationsInAcquisitionGE; if (d.zSpacing > 0) d.xyzMM[3] = d.zSpacing; //use zSpacing if provided: depending on vendor, kZThick may or may not include a slice gap //printMessage("patientPositions = %d XYZT = %d slicePerVol = %d numberOfDynamicScans %d\n",patientPositionNum,d.xyzDim[3], d.locationsInAcquisition, d.numberOfDynamicScans); if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (patientPositionNum > d.xyzDim[3])) printMessage("Please check slice thicknesses: Philips R3.2.2 bug can disrupt estimation (%d positions reported for %d slices)\n",patientPositionNum, d.xyzDim[3]); //Philips reported different positions for each slice! if ((d.imageStart > 144) && (d.xyzDim[1] > 1) && (d.xyzDim[2] > 1)) d.isValid = true; if ((d.xyzMM[1] > FLT_EPSILON) && (d.xyzMM[2] < FLT_EPSILON)) { printMessage("Please check voxel size\n"); d.xyzMM[2] = d.xyzMM[1]; } if ((d.xyzMM[2] > FLT_EPSILON) && (d.xyzMM[1] < FLT_EPSILON)) { printMessage("Please check voxel size\n"); d.xyzMM[1] = d.xyzMM[2]; } if ((d.xyzMM[3] < FLT_EPSILON)) { printMessage("Unable to determine slice thickness: please check voxel size\n"); d.xyzMM[3] = 1.0; } //printMessage("Patient Position\t%g\t%g\t%g\tThick\t%g\n",d.patientPosition[1],d.patientPosition[2],d.patientPosition[3], d.xyzMM[3]); //printMessage("Patient Position\t%g\t%g\t%g\tThick\t%g\tStart\t%d\n",d.patientPosition[1],d.patientPosition[2],d.patientPosition[3], d.xyzMM[3], d.imageStart); // printMessage("ser %ld\n", d.seriesNum); //int kEchoMult = 100; //For Siemens/GE Series 1,2,3... save 2nd echo as 201, 3rd as 301, etc //if (d.seriesNum > 100) // kEchoMult = 10; //For Philips data Saved as Series 101,201,301... save 2nd echo as 111, 3rd as 121, etc //if (coilNum > 0) //segment images with multiple coils // d.seriesNum = d.seriesNum + (100*coilNum); //if (d.echoNum > 1) //segment images with multiple echoes // d.seriesNum = d.seriesNum + (kEchoMult*d.echoNum); if ((d.compressionScheme == kCompress50) && (d.bitsAllocated > 8) ) { //dcmcjpg with +ee can create .51 syntax images that are 8,12,16,24-bit: we can only decode 8/24-bit printError("Unable to decode %d-bit images with Transfer Syntax 1.2.840.10008.1.2.4.51, decompress with dcmdjpg or gdcmconv\n", d.bitsAllocated); d.isValid = false; } if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (isMosaic) && (d.CSA.mosaicSlices < 1) && (d.phaseEncodingSteps > 0) && ((d.xyzDim[1] % d.phaseEncodingSteps) == 0) && ((d.xyzDim[2] % d.phaseEncodingSteps) == 0) ) { d.CSA.mosaicSlices = (d.xyzDim[1] / d.phaseEncodingSteps) * (d.xyzDim[2] / d.phaseEncodingSteps); printWarning("Mosaic inferred without CSA header (check number of slices and spatial orientation)\n"); } if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (d.CSA.dtiV[1] < -1.0) && (d.CSA.dtiV[2] < -1.0) && (d.CSA.dtiV[3] < -1.0)) d.CSA.dtiV[0] = 0; //SiemensTrio-Syngo2004A reports B=0 images as having impossible b-vectors. if ((d.manufacturer == kMANUFACTURER_GE) && (strlen(d.seriesDescription) > 1)) //GE uses a generic session name here: do not overwrite kProtocolNameGE strcpy(d.protocolName, d.seriesDescription); if ((strlen(d.protocolName) < 1) && (strlen(d.seriesDescription) > 1)) strcpy(d.protocolName, d.seriesDescription); if ((strlen(d.protocolName) < 1) && (strlen(d.sequenceName) > 1) && (d.manufacturer != kMANUFACTURER_SIEMENS)) strcpy(d.protocolName, d.sequenceName); //protocolName (0018,1030) optional, sequence name (0018,0024) is not a good substitute for Siemens as it can vary per volume: *ep_b0 *ep_b1000#1, *ep_b1000#2, etc https://www.nitrc.org/forum/forum.php?thread_id=8771&forum_id=4703 // if (!isOrient) { // if (d.isNonImage) // printWarning("Spatial orientation ambiguous (tag 0020,0037 not found) [probably not important: derived image]: %s\n", fname); // else if (((d.manufacturer == kMANUFACTURER_SIEMENS)) && (d.samplesPerPixel != 1)) // printWarning("Spatial orientation ambiguous (tag 0020,0037 not found) [perhaps derived FA that is not required]: %s\n", fname); // else // printWarning("Spatial orientation ambiguous (tag 0020,0037 not found): %s\n", fname); // } if ((d.numberOfDynamicScans < 2) && (!d.isSlicesSpatiallySequentialPhilips) && (!isnan(patientPositionStartPhilips[1])) && (!isnan(patientPositionEndPhilips[1]))) { //to do: check for d.numberOfDynamicScans > 1 for (int k = 0; k < 4; k++) { d.patientPosition[k] = patientPositionStartPhilips[k]; d.patientPositionLast[k] = patientPositionEndPhilips[k]; } printMessage("Slices not spatially contiguous: please check output [new feature]\n"); } if (isVerbose) { printMessage("%s\n patient position (0020,0032)\t%g\t%g\t%g\n",fname, d.patientPosition[1],d.patientPosition[2],d.patientPosition[3]); printMessage("%s\n orient (0020,0037)\t%g\t%g\t%g\t%g\t%g\t%g\n",fname, d.orient[1],d.orient[2],d.orient[3], d.orient[4],d.orient[5],d.orient[6]); printMessage(" acq %d img %d ser %ld dim %dx%dx%d mm %gx%gx%g offset %d dyn %d loc %d valid %d ph %d mag %d posReps %d nDTI %d 3d %d bits %d littleEndian %d echo %d coil %d TE %g TR %g\n",d.acquNum,d.imageNum,d.seriesNum,d.xyzDim[1],d.xyzDim[2],d.xyzDim[3],d.xyzMM[1],d.xyzMM[2],d.xyzMM[3],d.imageStart, d.numberOfDynamicScans, d.locationsInAcquisition, d.isValid, d.isHasPhase, d.isHasMagnitude,d.patientPositionSequentialRepeats, d.CSA.numDti, d.is3DAcq, d.bitsAllocated, d.isLittleEndian, d.echoNum, d.coilNum, d.TE, d.TR); if (d.CSA.dtiV[0] > 0) printMessage(" DWI bxyz %g %g %g %g\n", d.CSA.dtiV[0], d.CSA.dtiV[1], d.CSA.dtiV[2], d.CSA.dtiV[3]); } if (d.patientPositionNumPhilips >= kMaxDTI4D) { printError("Too many 2D slices in a single file [recompile with increased kMaxDTI4D] detected=%d, max = %d\n", d.patientPositionNumPhilips, kMaxDTI4D); d.CSA.numDti = 0; } if (d.CSA.numDti >= kMaxDTI4D) { printError("Unable to convert DTI [recompile with increased kMaxDTI4D] detected=%d, max = %d\n", d.CSA.numDti, kMaxDTI4D); d.CSA.numDti = 0; } if (multiBandFactor > d.CSA.multiBandFactor) d.CSA.multiBandFactor = multiBandFactor; //SMS reported in 0051,1011 but not CSA header //d.isValid = false; //debug only - will not create output! #ifndef myLoadWholeFileToReadHeader fclose(file); #endif //printMessage("buffer usage %d %d %d\n",d.imageStart, lPos+lFileOffset, MaxBufferSz); return d; } // readDICOM() struct TDICOMdata readDICOM(char * fname) { TDTI4D unused; return readDICOMv(fname, false, 0, &unused); } // readDICOM() dcm2niix-1.0.20171215/console/nii_dicom.h000066400000000000000000000163451322051203000175320ustar00rootroot00000000000000#include #include #include #include "nifti1_io_core.h" #ifndef HAVE_R #include "nifti1.h" #endif #ifndef MRIpro_nii_dcm_h #define MRIpro_nii_dcm_h #ifdef __cplusplus extern "C" { #endif #define STR_HELPER(x) #x #define STR(x) STR_HELPER(x) #ifdef myEnableJasper #define kDCMsuf " (JasPer build)" #else #ifdef myDisableOpenJPEG #define kDCMsuf "" #else #define kDCMsuf " (OpenJPEG build)" #endif #endif #if defined(__ICC) || defined(__INTEL_COMPILER) #define kCCsuf " IntelCC" STR(__INTEL_COMPILER) #elif defined(_MSC_VER) #define kCCsuf " MSC" STR(_MSC_VER) #elif defined(__clang__) #define kCCsuf " Clang" STR(__clang_major__) "." STR(__clang_minor__) "." STR(__clang_patchlevel__) #elif defined(__GNUC__) || defined(__GNUG__) #define kCCsuf " GCC" STR(__GNUC__) "." STR(__GNUC_MINOR__) "." STR(__GNUC_PATCHLEVEL__) #else #define kCCsuf " CompilerNA" //unknown compiler! #endif #define kDCMvers "v1.0.20171215" kDCMsuf kCCsuf static const int kMaxEPI3D = 1024; //maximum number of EPI images in Siemens Mosaic static const int kMaxDTI4D = 4096; //maximum number of DTI directions for 4D (Philips) images, also maximum number of 3D slices for Philips 3D and 4D images #define kDICOMStr 64 #define kDICOMStrLarge 256 #define kMANUFACTURER_UNKNOWN 0 #define kMANUFACTURER_SIEMENS 1 #define kMANUFACTURER_GE 2 #define kMANUFACTURER_PHILIPS 3 #define kMANUFACTURER_TOSHIBA 4 //note: note a complete modality list, e.g. XA,PX, etc #define kMODALITY_UNKNOWN 0 #define kMODALITY_CR 1 #define kMODALITY_CT 2 #define kMODALITY_MR 3 #define kMODALITY_PT 4 #define kMODALITY_US 5 #define kEXIT_NO_VALID_FILES_FOUND 2 static const int kSliceOrientUnknown = 0; static const int kSliceOrientTra = 1; static const int kSliceOrientSag = 2; static const int kSliceOrientCor = 3; static const int kSliceOrientMosaicNegativeDeterminant = 4; static const int kCompressNone = 0; static const int kCompressYes = 1; static const int kCompressC3 = 2; //obsolete JPEG lossless static const int kCompress50 = 3; //obsolete JPEG lossy static const int kCompressRLE = 4; //run length encoding // Maximum number of dimensions for .dimensionIndexValues, i.e. possibly the // number of axes in the output .nii. static const uint8_t MAX_NUMBER_OF_DIMENSIONS = 8; struct TDTI { float V[4]; int sliceNumberMrPhilips; }; struct TDTI4D { struct TDTI S[kMaxDTI4D]; }; #ifdef _MSC_VER //Microsoft nomenclature for packed structures is different... #pragma pack(2) typedef struct { char name[64]; //null-terminated int32_t vm; char vr[4]; // possibly nul-term string int32_t syngodt;// ?? int32_t nitems;// number of items in CSA int32_t xx;// maybe == 77 or 205 } TCSAtag; //Siemens csa tag structure typedef struct { int32_t xx1, xx2_Len, xx3_77, xx4; } TCSAitem; //Siemens csa item structure #pragma pack() #else typedef struct __attribute__((packed)) { char name[64]; //null-terminated int32_t vm; char vr[4]; // possibly nul-term string int32_t syngodt;// ?? int32_t nitems;// number of items in CSA int32_t xx;// maybe == 77 or 205 } TCSAtag; //Siemens csa tag structure typedef struct __attribute__((packed)) { int32_t xx1, xx2_Len, xx3_77, xx4; } TCSAitem; //Siemens csa item structure #endif struct TCSAdata { bool isPhaseMap; float sliceTiming[kMaxEPI3D], dtiV[4], sliceNormV[4], bandwidthPerPixelPhaseEncode, sliceMeasurementDuration; int numDti, SeriesHeader_offset, SeriesHeader_length, multiBandFactor, sliceOrder, slice_start, slice_end, mosaicSlices,protocolSliceNumber1,phaseEncodingDirectionPositive; }; struct TDICOMdata { long seriesNum; int xyzDim[5]; int modality, dwellTime, effectiveEchoSpacingGE, phaseEncodingLines, phaseEncodingSteps, echoTrainLength, patientPositionNumPhilips, coilNum, echoNum, sliceOrient,numberOfDynamicScans, manufacturer, converted2NII, acquNum, imageNum, imageStart, imageBytes, bitsStored, bitsAllocated, samplesPerPixel,patientPositionSequentialRepeats,locationsInAcquisition, compressionScheme; float patientWeight, zSpacing, zThick, pixelBandwidth, SAR, phaseFieldofView, accelFactPE, flipAngle, fieldStrength, TE, TI, TR, intenScale, intenIntercept, intenScalePhilips, gantryTilt, lastScanLoc, angulation[4]; float orient[7], patientPosition[4], patientPositionLast[4], xyzMM[4], stackOffcentre[4]; float radionuclidePositronFraction, radionuclideTotalDose, radionuclideHalfLife, doseCalibrationFactor; //PET ISOTOPE MODULE ATTRIBUTES (C.8-57) float ecat_isotope_halflife, ecat_dosage; double dateTime, acquisitionTime, acquisitionDate, bandwidthPerPixelPhaseEncode; //char mrAcquisitionType[kDICOMStr] char scanOptions[kDICOMStr], stationName[kDICOMStr], softwareVersions[kDICOMStr], deviceSerialNumber[kDICOMStr], institutionAddress[kDICOMStr], institutionName[kDICOMStr], referringPhysicianName[kDICOMStr], seriesInstanceUID[kDICOMStr], studyInstanceUID[kDICOMStr], bodyPartExamined[kDICOMStr], procedureStepDescription[kDICOMStr], imageType[kDICOMStr], institutionalDepartmentName[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr],seriesDescription[kDICOMStr], studyID[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr],sequenceVariant[kDICOMStr],scanningSequence[kDICOMStr], patientBirthDate[kDICOMStr], patientAge[kDICOMStr], studyDate[kDICOMStr],studyTime[kDICOMStr]; char imageComments[kDICOMStrLarge]; uint32_t dimensionIndexValues[MAX_NUMBER_OF_DIMENSIONS]; struct TCSAdata CSA; bool isSegamiOasis, isDerived, isXRay, isMultiEcho, isSlicesSpatiallySequentialPhilips, isValid, is3DAcq, is2DAcq, isExplicitVR, isLittleEndian, isPlanarRGB, isSigned, isHasPhase,isHasMagnitude,isHasMixed, isFloat, isResampled; char phaseEncodingRC, patientSex; }; size_t nii_ImgBytes(struct nifti_1_header hdr); struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, struct TDTI4D *dti4D); struct TDICOMdata readDICOM(char * fname); struct TDICOMdata clear_dicom_data(); struct TDICOMdata nii_readParRec (char * parname, int isVerbose, struct TDTI4D *dti4D); unsigned char * nii_flipY(unsigned char* bImg, struct nifti_1_header *h); unsigned char * nii_flipZ(unsigned char* bImg, struct nifti_1_header *h); unsigned char * nii_reorderSlices(unsigned char* bImg, struct nifti_1_header *h, struct TDTI4D *dti4D); void changeExt (char *file_name, const char* ext); unsigned char * nii_planar2rgb(unsigned char* bImg, struct nifti_1_header *hdr, int isPlanar); int isDICOMfile(const char * fname); //0=not DICOM, 1=DICOM, 2=NOTSURE(not part 10 compliant) void setQSForm(struct nifti_1_header *h, mat44 Q44i, bool isVerbose); int headerDcm2Nii2(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, int isVerbose); int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h, bool isComputeSForm) ; unsigned char * nii_loadImgXL(char* imgname, struct nifti_1_header *hdr, struct TDICOMdata dcm, bool iVaries, int compressFlag, int isVerbose); #ifdef __cplusplus } #endif #endif dcm2niix-1.0.20171215/console/nii_dicom_batch.cpp000066400000000000000000004421201322051203000212200ustar00rootroot00000000000000//#define myNoSave //do not save images to disk #ifdef _MSC_VER #include #define getcwd _getcwd #define chdir _chrdir #include "io.h" //#include #define MiniZ #else #include #ifdef myDisableMiniZ #undef MiniZ #else #define MiniZ #endif #endif #if defined(__APPLE__) && defined(__MACH__) #endif #ifndef myDisableZLib #ifdef MiniZ #include "miniz.c" //single file clone of libz #else #include #endif #endif #include "tinydir.h" #include "print.h" #include "nifti1_io_core.h" #ifndef HAVE_R #include "nifti1.h" #endif #include "nii_dicom_batch.h" #include "nii_foreign.h" #include "nii_dicom.h" #include //toupper #include #include #include #include #include #include #include #include #include //#ifdef myEnableOtsu // #include "nii_ostu_ml.h" //provide better brain crop, but artificially reduces signal variability in air //#endif #include // clock_t, clock, CLOCKS_PER_SEC #include "nii_ortho.h" #if defined(_WIN64) || defined(_WIN32) #include //write to registry #endif #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #if defined(_WIN64) || defined(_WIN32) const char kPathSeparator ='\\'; const char kFileSep[2] = "\\"; #else const char kPathSeparator ='/'; const char kFileSep[2] = "/"; #endif #ifdef HAVE_R #include "ImageList.h" #undef isnan #define isnan ISNAN #endif struct TDCMsort { uint64_t indx, img; uint32_t dimensionIndexValues[MAX_NUMBER_OF_DIMENSIONS]; }; struct TSearchList { unsigned long numItems, maxItems; char **str; }; #ifndef PATH_MAX #define PATH_MAX 4096 #endif void dropFilenameFromPath(char *path) { // const char *dirPath = strrchr(path, '/'); //UNIX if (dirPath == 0) dirPath = strrchr(path, '\\'); //Windows if (dirPath == NULL) { strcpy(path,""); } else path[dirPath - path] = 0; // please make sure there is enough space in TargetDirectory if (strlen(path) == 0) { //file name did not specify path, assume relative path and return current working directory //strcat (path,"."); //relative path - use cwd <- not sure if this works on Windows! char cwd[1024]; char* ok = getcwd(cwd, sizeof(cwd)); if (ok !=NULL) strcat (path,cwd); } } void dropTrailingFileSep(char *path) { // size_t len = strlen(path) - 1; if (len <= 0) return; if (path[len] == '/') path[len] = '\0'; else if (path[len] == '\\') path[len] = '\0'; } void getFileName( char *pathParent, const char *path) //if path is c:\d1\d2 then filename is 'd2' { const char *filename = strrchr(path, '/'); //UNIX if (filename == 0) { filename = strrchr(path, '\\'); //Windows if (filename == NULL) filename = strrchr(path, ':'); //Windows } //const char *filename = strrchr(path, kPathSeparator); //x if (filename == NULL) {//no path separator strcpy(pathParent,path); return; } filename++; strcpy(pathParent,filename); } bool is_fileexists(const char * filename) { FILE * fp = NULL; if ((fp = fopen(filename, "r"))) { fclose(fp); return true; } return false; } #ifndef S_ISDIR #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #endif #ifndef S_ISREG #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) #endif bool is_fileNotDir(const char* path) { //returns false if path is a folder; requires #include struct stat buf; stat(path, &buf); return S_ISREG(buf.st_mode); } //is_file() bool is_exe(const char* path) { //requires #include struct stat buf; if (stat(path, &buf) != 0) return false; //file does not eist if (!S_ISREG(buf.st_mode)) return false; //not regular file, e.g. '..' return (buf.st_mode & 0111) ; //return (S_ISREG(buf.st_mode) && (buf.st_mode & 0111) ); } //is_exe() #if defined(_WIN64) || defined(_WIN32) //Windows does not support lstat int is_dir(const char *pathname, int follow_link) { struct stat s; if ((NULL == pathname) || (0 == strlen(pathname))) return 0; int err = stat(pathname, &s); if(-1 == err) return 0; // does not exist else { if(S_ISDIR(s.st_mode)) { return 1; // it's a dir } else { return 0;// exists but is no dir } } }// is_dir() #else //if windows else Unix int is_dir(const char *pathname, int follow_link) { struct stat s; int retval; if ((NULL == pathname) || (0 == strlen(pathname))) return 0; // does not exist retval = follow_link ? stat(pathname, &s) : lstat(pathname, &s); if ((-1 != retval) && (S_ISDIR(s.st_mode))) return 1; // it's a dir return 0; // exists but is no dir }// is_dir() #endif void geCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI *vx){ //0018,1312 phase encoding is either in row or column direction //0043,1039 (or 0043,a039). b value (as the first number in the string). //0019,10bb (or 0019,a0bb). phase diffusion direction //0019,10bc (or 0019,a0bc). frequency diffusion direction //0019,10bd (or 0019,a0bd). slice diffusion direction //These directions are relative to freq,phase,slice, so although no //transformations are required, you need to check the direction of the //phase encoding. This is in DICOM message 0018,1312. If this has value //COL then if swap the x and y value and reverse the sign on the z value. //If the phase encoding is not COL, then just reverse the sign on the x value. if (d->manufacturer != kMANUFACTURER_GE) return; if (d->CSA.numDti < 1) return; if ((toupper(d->patientOrient[0])== 'H') && (toupper(d->patientOrient[1])== 'F') && (toupper(d->patientOrient[2])== 'S')) ; //participant was head first supine else { printMessage("GE DTI directions require head first supine acquisition\n"); return; } bool col = false; if (d->phaseEncodingRC == 'C') col = true; else if (d->phaseEncodingRC != 'R') { printWarning("Unable to determine DTI gradients, 0018,1312 should be either R or C"); return; } if (abs(sliceDir) != 3) printWarning("GE DTI only tested for axial acquisitions (solution: use Xiangrui Li's dicm2nii)\n"); //GE vectors from Xiangrui Li' dicm2nii, validated with datasets from https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#Diffusion_Tensor_Imaging ivec3 flp; if (abs(sliceDir) == 1) flp = setiVec3(1, 1, 0); //SAGITTAL else if (abs(sliceDir) == 2) flp = setiVec3(0, 1, 1); //CORONAL else if (abs(sliceDir) == 3) flp = setiVec3(0, 0, 1); //AXIAL else { printMessage("Impossible GE slice orientation!"); flp = setiVec3(0, 0, 1); //AXIAL??? } if (sliceDir < 0) flp.v[2] = 1 - flp.v[2]; printMessage("Saving %d DTI gradients. GE Reorienting %s : please validate. isCol=%d sliceDir=%d flp=%d %d %d\n", d->CSA.numDti, d->protocolName, col, sliceDir, flp.v[0], flp.v[1],flp.v[2]); if (!col) printMessage(" reorienting for ROW phase-encoding untested.\n"); for (int i = 0; i < d->CSA.numDti; i++) { float vLen = sqrt( (vx[i].V[1]*vx[i].V[1]) + (vx[i].V[2]*vx[i].V[2]) + (vx[i].V[3]*vx[i].V[3])); if ((vx[i].V[0] <= FLT_EPSILON)|| (vLen <= FLT_EPSILON) ) { //bvalue=0 for (int v= 1; v < 4; v++) vx[i].V[v] = 0.0f; continue; //do not normalize or reorient 0 vectors } if (!col) { //rows need to be swizzled //see Stanford dataset Ax_DWI_Tetrahedral_7 unable to resolve between possible solutions // http://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#Diffusion_Tensor_Imaging float swap = vx[i].V[1]; vx[i].V[1] = vx[i].V[2]; vx[i].V[2] = swap; vx[i].V[2] = -vx[i].V[2]; //because of transpose? } for (int v = 0; v < 3; v++) if (flp.v[v] == 1) vx[i].V[v+1] = -vx[i].V[v+1]; vx[i].V[2] = -vx[i].V[2]; //we read out Y-direction opposite order as dicm2nii, see also opts.isFlipY } //These next lines are only so files appear identical to old versions of dcm2niix: // dicm2nii and dcm2niix generate polar opposite gradient directions. // this does not matter, since intensity is the normal of the gradient vector. for (int i = 0; i < d->CSA.numDti; i++) for (int v = 1; v < 4; v++) vx[i].V[v] = -vx[i].V[v]; //These next lines convert any "-0" values to "0" for (int i = 0; i < d->CSA.numDti; i++) for (int v = 1; v < 4; v++) if (isSameFloat(vx[i].V[v],-0)) vx[i].V[v] = 0.0f; }// geCorrectBvecs() void siemensPhilipsCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI *vx){ //see Matthew Robson's http://users.fmrib.ox.ac.uk/~robson/internal/Dicom2Nifti111.m //convert DTI vectors from scanner coordinates to image frame of reference //Uses 6 orient values from ImageOrientationPatient (0020,0037) // requires PatientPosition 0018,5100 is HFS (head first supine) if ((d->manufacturer != kMANUFACTURER_SIEMENS) && (d->manufacturer != kMANUFACTURER_PHILIPS)) return; if (d->CSA.numDti < 1) return; if ((toupper(d->patientOrient[0])== 'H') && (toupper(d->patientOrient[1])== 'F') && (toupper(d->patientOrient[2])== 'S')) ; //participant was head first supine else { printMessage("Siemens/Philips DTI directions require head first supine acquisition\n"); return; } vec3 read_vector = setVec3(d->orient[1],d->orient[2],d->orient[3]); vec3 phase_vector = setVec3(d->orient[4],d->orient[5],d->orient[6]); vec3 slice_vector = crossProduct(read_vector ,phase_vector); read_vector = nifti_vect33_norm(read_vector); phase_vector = nifti_vect33_norm(phase_vector); slice_vector = nifti_vect33_norm(slice_vector); for (int i = 0; i < d->CSA.numDti; i++) { float vLen = sqrt( (vx[i].V[1]*vx[i].V[1]) + (vx[i].V[2]*vx[i].V[2]) + (vx[i].V[3]*vx[i].V[3])); if ((vx[i].V[0] <= FLT_EPSILON)|| (vLen <= FLT_EPSILON) ) { //bvalue=0 if (vx[i].V[0] > FLT_EPSILON) printWarning("Volume %d appears to be an ADC map (non-zero b-value with zero vector length)\n", i); //for (int v= 0; v < 4; v++) // vx[i].V[v] =0.0f; continue; //do not normalize or reorient b0 vectors }//if bvalue=0 vec3 bvecs_old =setVec3(vx[i].V[1],vx[i].V[2],vx[i].V[3]); vec3 bvecs_new =setVec3(dotProduct(bvecs_old,read_vector),dotProduct(bvecs_old,phase_vector),dotProduct(bvecs_old,slice_vector) ); bvecs_new = nifti_vect33_norm(bvecs_new); vx[i].V[1] = bvecs_new.v[0]; vx[i].V[2] = -bvecs_new.v[1]; vx[i].V[3] = bvecs_new.v[2]; if (abs(sliceDir) == kSliceOrientMosaicNegativeDeterminant) vx[i].V[2] = -vx[i].V[2]; for (int v= 0; v < 4; v++) if (vx[i].V[v] == -0.0f) vx[i].V[v] = 0.0f; //remove sign from values that are virtually zero } //for each direction if (abs(sliceDir) == kSliceOrientMosaicNegativeDeterminant) { printWarning("Saving %d DTI gradients. Validate vectors (matrix had a negative determinant).\n", d->CSA.numDti); //perhaps Siemens sagittal } else if ( d->sliceOrient == kSliceOrientTra) { printMessage("Saving %d DTI gradients. Validate vectors.\n", d->CSA.numDti); } else { printWarning("Saving %d DTI gradients. Validate vectors (images are not axial slices).\n", d->CSA.numDti); } }// siemensPhilipsCorrectBvecs() bool isNanPosition(struct TDICOMdata d) { //in 2007 some Siemens RGB DICOMs did not include the PatientPosition 0020,0032 tag if (isnan(d.patientPosition[1])) return true; if (isnan(d.patientPosition[2])) return true; if (isnan(d.patientPosition[3])) return true; return false; }// isNanPosition() bool isSamePosition(struct TDICOMdata d, struct TDICOMdata d2){ if ( isNanPosition(d) || isNanPosition(d2)) return false; if (!isSameFloat(d.patientPosition[1],d2.patientPosition[1])) return false; if (!isSameFloat(d.patientPosition[2],d2.patientPosition[2])) return false; if (!isSameFloat(d.patientPosition[3],d2.patientPosition[3])) return false; return true; }// isSamePosition() void nii_SaveText(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts, struct nifti_1_header *h, char * dcmname) { if (!opts.isCreateText) return; char txtname[2048] = {""}; strcpy (txtname,pathoutname); strcat (txtname,".txt"); //printMessage("Saving text %s\n",txtname); FILE *fp = fopen(txtname, "w"); fprintf(fp, "%s\tField Strength:\t%g\tProtocolName:\t%s\tScanningSequence00180020:\t%s\tTE:\t%g\tTR:\t%g\tSeriesNum:\t%ld\tAcquNum:\t%d\tImageNum:\t%d\tImageComments:\t%s\tDateTime:\t%f\tName:\t%s\tConvVers:\t%s\tDoB:\t%s\tGender:\t%c\tAge:\t%s\tDimXYZT:\t%d\t%d\t%d\t%d\tCoil:\t%d\tEchoNum:\t%d\tOrient(6)\t%g\t%g\t%g\t%g\t%g\t%g\tbitsAllocated\t%d\tInputName\t%s\n", pathoutname, d.fieldStrength, d.protocolName, d.scanningSequence, d.TE, d.TR, d.seriesNum, d.acquNum, d.imageNum, d.imageComments, d.dateTime, d.patientName, kDCMvers, d.patientBirthDate, d.patientSex, d.patientAge, h->dim[1], h->dim[2], h->dim[3], h->dim[4], d.coilNum,d.echoNum, d.orient[1], d.orient[2], d.orient[3], d.orient[4], d.orient[5], d.orient[6], d.bitsAllocated, dcmname); fclose(fp); }// nii_SaveText() #define myReadAsciiCsa #ifdef myReadAsciiCsa //read from the ASCII portion of the Siemens CSA series header // this is not recommended: poorly documented // it is better to stick to the binary portion of the Siemens CSA image header #if defined(_WIN64) || defined(_WIN32) //https://opensource.apple.com/source/Libc/Libc-1044.1.2/string/FreeBSD/memmem.c /*- * Copyright (c) 2005 Pascal Gloor * * 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 name of the author may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ const void * memmem(const char *l, size_t l_len, const char *s, size_t s_len) { register char *cur, *last; const char *cl = (const char *)l; const char *cs = (const char *)s; /* we need something to compare */ if (l_len == 0 || s_len == 0) return NULL; /* "s" must be smaller or equal to "l" */ if (l_len < s_len) return NULL; /* special case where s_len == 1 */ if (s_len == 1) return memchr(l, (int)*cs, l_len); /* the last position where its possible to find "s" in "l" */ last = (char *)cl + l_len - s_len; for (cur = (char *)cl; cur <= last; cur++) if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0) return cur; return NULL; } //n.b. memchr returns "const void *" not "void *" for Windows C++ https://msdn.microsoft.com/en-us/library/d7zdhf37.aspx #endif //for systems without memmem int readKey(const char * key, char * buffer, int remLength) { //look for text key in binary data stream, return subsequent integer value int ret = 0; char *keyPos = (char *)memmem(buffer, remLength, key, strlen(key)); if (!keyPos) return 0; int i = (int)strlen(key); while( ( i< remLength) && (keyPos[i] != 0x0A) ) { if( keyPos[i] >= '0' && keyPos[i] <= '9' ) ret = (10 * ret) + keyPos[i] - '0'; i++; } return ret; } //readKey() float readKeyFloat(const char * key, char * buffer, int remLength) { //look for text key in binary data stream, return subsequent integer value char *keyPos = (char *)memmem(buffer, remLength, key, strlen(key)); if (!keyPos) return 0.0; char str[kDICOMStr]; strcpy(str, ""); char tmpstr[2]; tmpstr[1] = 0; int i = (int)strlen(key); while( ( i< remLength) && (keyPos[i] != 0x0A) ) { if( (keyPos[i] >= '0' && keyPos[i] <= '9') || (keyPos[i] <= '.') || (keyPos[i] <= '-') ) { tmpstr[0] = keyPos[i]; strcat (str, tmpstr); } i++; } if (strlen(str) < 1) return 0.0; return atof(str); } //readKeyFloat() void readKeyStr(const char * key, char * buffer, int remLength, char* outStr) { //if key is CoilElementID.tCoilID the string 'CoilElementID.tCoilID = ""Head_32""' returns 'Head32' strcpy(outStr, ""); char *keyPos = (char *)memmem(buffer, remLength, key, strlen(key)); if (!keyPos) return; int i = (int)strlen(key); int outLen = 0; char tmpstr[2]; tmpstr[1] = 0; bool isQuote = false; while( ( i < remLength) && (keyPos[i] != 0x0A) ) { if ((isQuote) && (keyPos[i] != '"') && (outLen < kDICOMStr)) { tmpstr[0] = keyPos[i]; strcat (outStr, tmpstr); outLen ++; } if (keyPos[i] == '"') { if (outLen > 0) break; isQuote = true; } i++; } } //readKeyStr() int phoenixOffsetCSASeriesHeader(unsigned char *buff, int lLength) { //returns offset to ASCII Phoenix data if (lLength < 36) return 0; if ((buff[0] != 'S') || (buff[1] != 'V') || (buff[2] != '1') || (buff[3] != '0') ) return EXIT_FAILURE; int lPos = 8; //skip 8 bytes of data, 'SV10' plus 2 32-bit values unused1 and unused2 int lnTag = buff[lPos]+(buff[lPos+1]<<8)+(buff[lPos+2]<<16)+(buff[lPos+3]<<24); if ((buff[lPos+4] != 77) || (lnTag < 1)) return 0; lPos += 8; //skip 8 bytes of data, 32-bit lnTag plus 77 00 00 0 TCSAtag tagCSA; TCSAitem itemCSA; for (int lT = 1; lT <= lnTag; lT++) { memcpy(&tagCSA, &buff[lPos], sizeof(tagCSA)); //read tag //if (!littleEndianPlatform()) // nifti_swap_4bytes(1, &tagCSA.nitems); //printf("%d CSA of %s %d\n",lPos, tagCSA.name, tagCSA.nitems); lPos +=sizeof(tagCSA); if (strcmp(tagCSA.name, "MrPhoenixProtocol") == 0) return lPos; for (int lI = 1; lI <= tagCSA.nitems; lI++) { memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA)); lPos +=sizeof(itemCSA); //if (!littleEndianPlatform()) // nifti_swap_4bytes(1, &itemCSA.xx2_Len); lPos += ((itemCSA.xx2_Len +3)/4)*4; } } return 0; } // phoenixOffsetCSASeriesHeader() void siemensCsaAscii(const char * filename, int csaOffset, int csaLength, float* delayTimeInTR, float* phaseOversampling, float* phaseResolution, float* txRefAmp, float* shimSetting, int* baseResolution, int* interp, int* partialFourier, int* echoSpacing, int* parallelReductionFactorInPlane, char* coilID, char* consistencyInfo, char* coilElements, char* pulseSequenceDetails, char* fmriExternalInfo, char * protocolName) { //reads ASCII portion of CSASeriesHeaderInfo and returns lEchoTrainDuration or lEchoSpacing value // returns 0 if no value found *delayTimeInTR = 0.0; *phaseOversampling = 0.0; *phaseResolution = 0.0; *txRefAmp = 0.0; *baseResolution = 0; *interp = 0; *partialFourier = 0; *echoSpacing = 0; for (int i = 0; i < 8; i++) shimSetting[i] = 0.0; strcpy(coilID, ""); strcpy(consistencyInfo, ""); strcpy(coilElements, ""); strcpy(pulseSequenceDetails, ""); strcpy(fmriExternalInfo, ""); strcpy(protocolName, ""); if ((csaOffset < 0) || (csaLength < 8)) return; FILE * pFile = fopen ( filename, "rb" ); if(pFile==NULL) return; fseek (pFile , 0 , SEEK_END); long lSize = ftell (pFile); if (lSize < (csaOffset+csaLength)) { fclose (pFile); return; } fseek(pFile, csaOffset, SEEK_SET); char * buffer = (char*) malloc (csaLength); if(buffer == NULL) return; size_t result = fread (buffer,1,csaLength,pFile); if ((int)result != csaLength) return; //next bit complicated: restrict to ASCII portion to avoid buffer overflow errors in BINARY portion int startAscii = phoenixOffsetCSASeriesHeader((unsigned char *)buffer, csaLength); int csaLengthTrim = csaLength; char * bufferTrim = buffer; if ((startAscii > 0) && (startAscii < csaLengthTrim) ) { //ignore binary data at start bufferTrim += startAscii; csaLengthTrim -= startAscii; } char keyStr[] = "### ASCCONV BEGIN"; //skip to start of ASCII often "### ASCCONV BEGIN ###" but also "### ASCCONV BEGIN object=MrProtDataImpl@MrProtocolData" char *keyPos = (char *)memmem(bufferTrim, csaLengthTrim, keyStr, strlen(keyStr)); if (keyPos) { //We could detect multi-echo MPRAGE here, e.g. "lContrasts = 4"- but ideally we want an earlier detection csaLengthTrim -= (keyPos-bufferTrim); char keyStrEnd[] = "### ASCCONV END"; char *keyPosEnd = (char *)memmem(keyPos, csaLengthTrim, keyStrEnd, strlen(keyStrEnd)); if ((keyPosEnd) && ((keyPosEnd - keyPos) < csaLengthTrim)) //ignore binary data at end csaLengthTrim = (int)(keyPosEnd - keyPos); char keyStrES[] = "sFastImaging.lEchoSpacing"; *echoSpacing = readKey(keyStrES, keyPos, csaLengthTrim); char keyStrBase[] = "sKSpace.lBaseResolution"; *baseResolution = readKey(keyStrBase, keyPos, csaLengthTrim); char keyStrInterp[] = "sKSpace.uc2DInterpolation"; *interp = readKey(keyStrInterp, keyPos, csaLengthTrim); char keyStrPF[] = "sKSpace.ucPhasePartialFourier"; *partialFourier = readKey(keyStrPF, keyPos, csaLengthTrim); //char keyStrETD[] = "sFastImaging.lEchoTrainDuration"; //*echoTrainDuration = readKey(keyStrETD, keyPos, csaLengthTrim); char keyStrAF[] = "sPat.lAccelFactPE"; *parallelReductionFactorInPlane = readKey(keyStrAF, keyPos, csaLengthTrim); //char keyStrEF[] = "sFastImaging.lEPIFactor"; //ret = readKey(keyStrEF, keyPos, csaLengthTrim); char keyStrCoil[] = "sCoilElementID.tCoilID"; readKeyStr(keyStrCoil, keyPos, csaLengthTrim, coilID); char keyStrCI[] = "sProtConsistencyInfo.tMeasuredBaselineString"; readKeyStr(keyStrCI, keyPos, csaLengthTrim, consistencyInfo); char keyStrCS[] = "sCoilSelectMeas.sCoilStringForConversion"; readKeyStr(keyStrCS, keyPos, csaLengthTrim, coilElements); char keyStrSeq[] = "tSequenceFileName"; readKeyStr(keyStrSeq, keyPos, csaLengthTrim, pulseSequenceDetails); char keyStrExt[] = "FmriExternalInfo"; readKeyStr(keyStrExt, keyPos, csaLengthTrim, fmriExternalInfo); char keyStrPn[] = "tProtocolName"; readKeyStr(keyStrPn, keyPos, csaLengthTrim, protocolName); char keyStrDelay[] = "lDelayTimeInTR"; *delayTimeInTR = readKeyFloat(keyStrDelay, keyPos, csaLengthTrim); char keyStrOver[] = "sKSpace.dPhaseOversamplingForDialog"; *phaseOversampling = readKeyFloat(keyStrOver, keyPos, csaLengthTrim); char keyStrPhase[] = "sKSpace.dPhaseResolution"; *phaseResolution = readKeyFloat(keyStrPhase, keyPos, csaLengthTrim); char keyStrAmp[] = "sTXSPEC.asNucleusInfo[0].flReferenceAmplitude"; *txRefAmp = readKeyFloat(keyStrAmp, keyPos, csaLengthTrim); //lower order shims: newer sequences char keyStrSh0[] = "sGRADSPEC.asGPAData[0].lOffsetX"; shimSetting[0] = readKeyFloat(keyStrSh0, keyPos, csaLengthTrim); char keyStrSh1[] = "sGRADSPEC.asGPAData[0].lOffsetY"; shimSetting[1] = readKeyFloat(keyStrSh1, keyPos, csaLengthTrim); char keyStrSh2[] = "sGRADSPEC.asGPAData[0].lOffsetZ"; shimSetting[2] = readKeyFloat(keyStrSh2, keyPos, csaLengthTrim); //lower order shims: older sequences char keyStrSh0s[] = "sGRADSPEC.lOffsetX"; if (shimSetting[0] == 0.0) shimSetting[0] = readKeyFloat(keyStrSh0s, keyPos, csaLengthTrim); char keyStrSh1s[] = "sGRADSPEC.lOffsetY"; if (shimSetting[1] == 0.0) shimSetting[1] = readKeyFloat(keyStrSh1s, keyPos, csaLengthTrim); char keyStrSh2s[] = "sGRADSPEC.lOffsetZ"; if (shimSetting[2] == 0.0) shimSetting[2] = readKeyFloat(keyStrSh2s, keyPos, csaLengthTrim); //higher order shims: older sequences char keyStrSh3[] = "sGRADSPEC.alShimCurrent[0]"; shimSetting[3] = readKeyFloat(keyStrSh3, keyPos, csaLengthTrim); char keyStrSh4[] = "sGRADSPEC.alShimCurrent[1]"; shimSetting[4] = readKeyFloat(keyStrSh4, keyPos, csaLengthTrim); char keyStrSh5[] = "sGRADSPEC.alShimCurrent[2]"; shimSetting[5] = readKeyFloat(keyStrSh5, keyPos, csaLengthTrim); char keyStrSh6[] = "sGRADSPEC.alShimCurrent[3]"; shimSetting[6] = readKeyFloat(keyStrSh6, keyPos, csaLengthTrim); char keyStrSh7[] = "sGRADSPEC.alShimCurrent[4]"; shimSetting[7] = readKeyFloat(keyStrSh7, keyPos, csaLengthTrim); } fclose (pFile); free (buffer); return; } // siemensCsaAscii() #endif //myReadAsciiCsa() void json_Str(FILE *fp, const char *sLabel, char *sVal) { if (strlen(sVal) < 1) return; //fprintf(fp, sLabel, sVal ); //convert \ ' " characters to _ see https://github.com/rordenlab/dcm2niix/issues/131 for (size_t pos = 0; pos < strlen(sVal); pos ++) { if ((sVal[pos] == '\'') || (sVal[pos] == '"') || (sVal[pos] == '\\')) sVal[pos] = '_'; } fprintf(fp, sLabel, sVal ); /*char outname[PATH_MAX] = {""}; char appendChar[2] = {"\\"}; char passChar[2] = {"\\"}; for (int pos = 0; pos 0.0) fprintf(fp, "\t\"MagneticFieldStrength\": %g,\n", d.fieldStrength ); switch (d.manufacturer) { case kMANUFACTURER_SIEMENS: fprintf(fp, "\t\"Manufacturer\": \"Siemens\",\n" ); break; case kMANUFACTURER_GE: fprintf(fp, "\t\"Manufacturer\": \"GE\",\n" ); break; case kMANUFACTURER_PHILIPS: fprintf(fp, "\t\"Manufacturer\": \"Philips\",\n" ); break; case kMANUFACTURER_TOSHIBA: fprintf(fp, "\t\"Manufacturer\": \"Toshiba\",\n" ); break; }; json_Str(fp, "\t\"ManufacturersModelName\": \"%s\",\n", d.manufacturersModelName); json_Str(fp, "\t\"InstitutionName\": \"%s\",\n", d.institutionName); json_Str(fp, "\t\"InstitutionalDepartmentName\": \"%s\",\n", d.institutionalDepartmentName); json_Str(fp, "\t\"InstitutionAddress\": \"%s\",\n", d.institutionAddress); json_Str(fp, "\t\"DeviceSerialNumber\": \"%s\",\n", d.deviceSerialNumber ); json_Str(fp, "\t\"StationName\": \"%s\",\n", d.stationName ); if (!opts.isAnonymizeBIDS) { json_Str(fp, "\t\"SeriesInstanceUID\": \"%s\",\n", d.seriesInstanceUID); json_Str(fp, "\t\"StudyInstanceUID\": \"%s\",\n", d.studyInstanceUID); json_Str(fp, "\t\"ReferringPhysicianName\": \"%s\",\n", d.referringPhysicianName); json_Str(fp, "\t\"StudyID\": \"%s\",\n", d.studyID); //Next lines directly reveal patient identity json_Str(fp, "\t\"PatientName\": \"%s\",\n", d.patientName); json_Str(fp, "\t\"PatientID\": \"%s\",\n", d.patientID); if (d.patientSex != '?') fprintf(fp, "\t\"PatientSex\": \"%c\",\n", d.patientSex); json_Float(fp, "\t\"PatientWeight\": %g,\n", d.patientWeight); //d.patientBirthDate //convert from DICOM YYYYMMDD to JSON //d.patientAge //4-digit Age String: nnnD, nnnW, nnnM, nnnY; } json_Str(fp, "\t\"BodyPartExamined\": \"%s\",\n", d.bodyPartExamined); json_Str(fp, "\t\"PatientPosition\": \"%s\",\n", d.patientOrient); // 0018,5100 = PatientPosition in DICOM json_Str(fp, "\t\"ProcedureStepDescription\": \"%s\",\n", d.procedureStepDescription); json_Str(fp, "\t\"SoftwareVersions\": \"%s\",\n", d.softwareVersions); //json_Str(fp, "\t\"MRAcquisitionType\": \"%s\",\n", d.mrAcquisitionType); if (d.is2DAcq) fprintf(fp, "\t\"MRAcquisitionType\": \"2D\",\n"); if (d.is3DAcq) fprintf(fp, "\t\"MRAcquisitionType\": \"3D\",\n"); json_Str(fp, "\t\"SeriesDescription\": \"%s\",\n", d.seriesDescription); json_Str(fp, "\t\"ProtocolName\": \"%s\",\n", d.protocolName); json_Str(fp, "\t\"ScanningSequence\": \"%s\",\n", d.scanningSequence); json_Str(fp, "\t\"SequenceVariant\": \"%s\",\n", d.sequenceVariant); json_Str(fp, "\t\"ScanOptions\": \"%s\",\n", d.scanOptions); json_Str(fp, "\t\"SequenceName\": \"%s\",\n", d.sequenceName); if (strlen(d.imageType) > 0) { fprintf(fp, "\t\"ImageType\": [\""); bool isSep = false; for (size_t i = 0; i < strlen(d.imageType); i++) { if (d.imageType[i] != '_') { if (isSep) fprintf(fp, "\", \""); isSep = false; fprintf(fp, "%c", d.imageType[i]); } else isSep = true; } fprintf(fp, "\"],\n"); } if (d.isDerived) //DICOM is derived image or non-spatial file (sounds, etc) fprintf(fp, "\t\"RawImage\": false,\n"); if (d.seriesNum > 0) fprintf(fp, "\t\"SeriesNumber\": %ld,\n", d.seriesNum); //Chris Gorgolewski: BIDS standard specifies ISO8601 date-time format (Example: 2016-07-06T12:49:15.679688) //Lines below directly save DICOM values if (d.acquisitionTime > 0.0 && d.acquisitionDate > 0.0){ long acquisitionDate = d.acquisitionDate; double acquisitionTime = d.acquisitionTime; char acqDateTimeBuf[64]; //snprintf(acqDateTimeBuf, sizeof acqDateTimeBuf, "%+08ld%+08f", acquisitionDate, acquisitionTime); snprintf(acqDateTimeBuf, sizeof acqDateTimeBuf, "%+08ld%+013.5f", acquisitionDate, acquisitionTime); //CR 20170404 add zero pad so 1:23am appears as +012300.00000 not +12300.00000 //printMessage("acquisitionDateTime %s\n",acqDateTimeBuf); int ayear,amonth,aday,ahour,amin; double asec; int count = 0; sscanf(acqDateTimeBuf, "%5d%2d%2d%3d%2d%lf%n", &ayear, &amonth, &aday, &ahour, &amin, &asec, &count); //CR 20170404 %lf not %f for double precision //printf("-%02d-%02dT%02d:%02d:%02.6f\",\n", amonth, aday, ahour, amin, asec); if (count) { // ISO 8601 specifies a sign must exist for distant years. //report time of the day only format, https://www.cs.tut.fi/~jkorpela/iso8601.html fprintf(fp, "\t\"AcquisitionTime\": \"%02d:%02d:%02.6f\",\n",ahour, amin, asec); //report date and time together if (!opts.isAnonymizeBIDS) { fprintf(fp, "\t\"AcquisitionDateTime\": "); fprintf(fp, (ayear >= 0 && ayear <= 9999) ? "\"%4d" : "\"%+4d", ayear); fprintf(fp, "-%02d-%02dT%02d:%02d:%02.6f\",\n", amonth, aday, ahour, amin, asec); } } //if (count) } //if acquisitionTime and acquisitionDate recorded // if (d.acquisitionTime > 0.0) fprintf(fp, "\t\"AcquisitionTime\": %f,\n", d.acquisitionTime ); // if (d.acquisitionDate > 0.0) fprintf(fp, "\t\"AcquisitionDate\": %8.0f,\n", d.acquisitionDate ); if (d.acquNum > 0) fprintf(fp, "\t\"AcquisitionNumber\": %d,\n", d.acquNum); json_Str(fp, "\t\"ImageComments\": \"%s\",\n", d.imageComments); json_Str(fp, "\t\"ConversionComments\": \"%s\",\n", opts.imageComments); //if conditionals: the following values are required for DICOM MRI, but not available for CT if ((d.intenScalePhilips != 0) || (d.manufacturer == kMANUFACTURER_PHILIPS)) { //for details, see PhilipsPrecise() fprintf(fp, "\t\"PhilipsRescaleSlope\": %g,\n", d.intenScale ); fprintf(fp, "\t\"PhilipsRescaleIntercept\": %g,\n", d.intenIntercept ); fprintf(fp, "\t\"PhilipsScaleSlope\": %g,\n", d.intenScalePhilips ); fprintf(fp, "\t\"UsePhilipsFloatNotDisplayScaling\": %d,\n", opts.isPhilipsFloatNotDisplayScaling); } //PET ISOTOPE MODULE ATTRIBUTES json_Float(fp, "\t\"RadionuclidePositronFraction\": %g,\n", d.radionuclidePositronFraction ); json_Float(fp, "\t\"RadionuclideTotalDose\": %g,\n", d.radionuclideTotalDose ); json_Float(fp, "\t\"RadionuclideHalfLife\": %g,\n", d.radionuclideHalfLife ); json_Float(fp, "\t\"DoseCalibrationFactor\": %g,\n", d.doseCalibrationFactor ); json_Float(fp, "\t\"IsotopeHalfLife\": %g,\n", d.ecat_isotope_halflife); json_Float(fp, "\t\"Dosage\": %g,\n", d.ecat_dosage); //CT parameters if ((d.TE > 0.0) && (d.isXRay)) fprintf(fp, "\t\"XRayExposure\": %g,\n", d.TE ); //MRI parameters if (!d.isXRay) { //with CT scans, slice thickness often varies //beware, not used correctly by all vendors https://public.kitware.com/pipermail/insight-users/2005-September/014711.html json_Float(fp, "\t\"SliceThickness\": %g,\n", d.zThick ); json_Float(fp, "\t\"SpacingBetweenSlices\": %g,\n", d.zSpacing); } json_Float(fp, "\t\"SAR\": %g,\n", d.SAR ); if (d.echoNum > 1) fprintf(fp, "\t\"EchoNumber\": %d,\n", d.echoNum); if ((d.TE > 0.0) && (!d.isXRay)) fprintf(fp, "\t\"EchoTime\": %g,\n", d.TE / 1000.0 ); json_Float(fp, "\t\"RepetitionTime\": %g,\n", d.TR / 1000.0 ); json_Float(fp, "\t\"InversionTime\": %g,\n", d.TI / 1000.0 ); json_Float(fp, "\t\"FlipAngle\": %g,\n", d.flipAngle ); float pf = 1.0f; //partial fourier bool interp = false; //2D interpolation float phaseOversampling = 0.0; #ifdef myReadAsciiCsa if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (d.CSA.SeriesHeader_offset > 0) && (d.CSA.SeriesHeader_length > 0)) { int baseResolution, interpInt, partialFourier, echoSpacing, parallelReductionFactorInPlane; float delayTimeInTR, phaseResolution, txRefAmp, shimSetting[8]; char protocolName[kDICOMStr], fmriExternalInfo[kDICOMStr], coilID[kDICOMStr], consistencyInfo[kDICOMStr], coilElements[kDICOMStr], pulseSequenceDetails[kDICOMStr]; siemensCsaAscii(filename, d.CSA.SeriesHeader_offset, d.CSA.SeriesHeader_length, &delayTimeInTR, &phaseOversampling, &phaseResolution, &txRefAmp, shimSetting, &baseResolution, &interpInt, &partialFourier, &echoSpacing, ¶llelReductionFactorInPlane, coilID, consistencyInfo, coilElements, pulseSequenceDetails, fmriExternalInfo, protocolName); if (partialFourier > 0) { //https://github.com/ismrmrd/siemens_to_ismrmrd/blob/master/parameter_maps/IsmrmrdParameterMap_Siemens_EPI_FLASHREF.xsl if (partialFourier == 1) pf = 0.5; // 4/8 if (partialFourier == 2) pf = 0.625; // 5/8 if (partialFourier == 4) pf = 0.75; if (partialFourier == 8) pf = 0.875; fprintf(fp, "\t\"PartialFourier\": %g,\n", pf); } if (interpInt > 0) { interp = true; fprintf(fp, "\t\"Interpolation2D\": %d,\n", interp); } if (baseResolution > 0) fprintf(fp, "\t\"BaseResolution\": %d,\n", baseResolution ); if (shimSetting[0] != 0.0) { fprintf(fp, "\t\"ShimSetting\": [\n"); for (int i = 0; i < 8; i++) { if (i != 0) fprintf(fp, ",\n"); fprintf(fp, "\t\t%g", shimSetting[i]); } fprintf(fp, "\t],\n"); } //DelayTimeInTR // https://groups.google.com/forum/#!topic/bids-discussion/nmg1BOVH1SU // https://groups.google.com/forum/#!topic/bids-discussion/seD7AtJfaFE json_Float(fp, "\t\"DelayTime\": %g,\n", delayTimeInTR/ 1000000.0); //DelayTimeInTR usec -> sec json_Float(fp, "\t\"TxRefAmp\": %g,\n", txRefAmp); json_Float(fp, "\t\"PhaseResolution\": %g,\n", phaseResolution); json_Float(fp, "\t\"PhaseOversampling\": %g,\n", phaseOversampling); //usec -> sec json_Float(fp, "\t\"VendorReportedEchoSpacing\": %g,\n", echoSpacing / 1000000.0); //usec -> sec //ETD and epiFactor not useful/reliable https://github.com/rordenlab/dcm2niix/issues/127 //if (echoTrainDuration > 0) fprintf(fp, "\t\"EchoTrainDuration\": %g,\n", echoTrainDuration / 1000000.0); //usec -> sec //if (epiFactor > 0) fprintf(fp, "\t\"EPIFactor\": %d,\n", epiFactor); json_Str(fp, "\t\"ReceiveCoilName\": \"%s\",\n", coilID); json_Str(fp, "\t\"ReceiveCoilActiveElements\": \"%s\",\n", coilElements); json_Str(fp, "\t\"PulseSequenceDetails\": \"%s\",\n", pulseSequenceDetails); json_Str(fp, "\t\"FmriExternalInfo\": \"%s\",\n", fmriExternalInfo); if (strlen(d.protocolName) < 1) //insert protocol name if it exists in CSA but not DICOM header: https://github.com/nipy/heudiconv/issues/80 json_Str(fp, "\t\"ProtocolName\": \"%s\",\n", protocolName); json_Str(fp, "\t\"ConsistencyInfo\": \"%s\",\n", consistencyInfo); if (parallelReductionFactorInPlane > 0) {//AccelFactorPE -> phase encoding if (d.accelFactPE < 1.0) { //value not found in DICOM header, but WAS found in CSA ascii d.accelFactPE = parallelReductionFactorInPlane; //value found in ASCII but not in DICOM (0051,1011) //fprintf(fp, "\t\"ParallelReductionFactorInPlane\": %g,\n", d.accelFactPE); } if (parallelReductionFactorInPlane != (int)(d.accelFactPE)) printWarning("ParallelReductionFactorInPlane reported in DICOM [0051,1011] (%d) does not match CSA series value %d\n", (int)(d.accelFactPE), parallelReductionFactorInPlane); } } #endif if (d.CSA.multiBandFactor > 1) //AccelFactorSlice fprintf(fp, "\t\"MultibandAccelerationFactor\": %d,\n", d.CSA.multiBandFactor); json_Float(fp, "\t\"PercentPhaseFOV\": %g,\n", d.phaseFieldofView); if (d.echoTrainLength > 1) //>1 as for Siemens EPI this is 1, Siemens uses EPI factor http://mriquestions.com/echo-planar-imaging.html fprintf(fp, "\t\"EchoTrainLength\": %d,\n", d.echoTrainLength); //0018,0091 Combination of partial fourier and in-plane parallel imaging if (d.phaseEncodingSteps > 0) fprintf(fp, "\t\"PhaseEncodingSteps\": %d,\n", d.phaseEncodingSteps ); if (d.phaseEncodingLines > 0) fprintf(fp, "\t\"AcquisitionMatrixPE\": %d,\n", d.phaseEncodingLines ); //Compute ReconMatrixPE // Actual size of the *reconstructed* data in the PE dimension, which does NOT match // phaseEncodingLines in the case of interpolation or phaseResolution < 100% // We'll need this for generating a value for effectiveEchoSpacing that is consistent // with the *reconstructed* data. int reconMatrixPE = d.phaseEncodingLines; if ((h->dim[2] > 0) && (h->dim[1] > 0)) { if (h->dim[2] == h->dim[2]) //phase encoding does not matter reconMatrixPE = h->dim[2]; else if (d.phaseEncodingRC =='R') reconMatrixPE = h->dim[2]; else if (d.phaseEncodingRC =='C') reconMatrixPE = h->dim[1]; } if (reconMatrixPE > 0) fprintf(fp, "\t\"ReconMatrixPE\": %d,\n", reconMatrixPE ); double bandwidthPerPixelPhaseEncode = d.bandwidthPerPixelPhaseEncode; if (bandwidthPerPixelPhaseEncode == 0.0) bandwidthPerPixelPhaseEncode = d.CSA.bandwidthPerPixelPhaseEncode; json_Float(fp, "\t\"BandwidthPerPixelPhaseEncode\": %g,\n", bandwidthPerPixelPhaseEncode ); if (d.accelFactPE > 1.0) fprintf(fp, "\t\"ParallelReductionFactorInPlane\": %g,\n", d.accelFactPE); //EffectiveEchoSpacing // Siemens bandwidthPerPixelPhaseEncode already accounts for the effects of parallel imaging, // interpolation, phaseOversampling, and phaseResolution, in the context of the size of the // *reconstructed* data in the PE dimension double effectiveEchoSpacing = 0.0; if ((reconMatrixPE > 0) && (bandwidthPerPixelPhaseEncode > 0.0)) effectiveEchoSpacing = 1.0 / (bandwidthPerPixelPhaseEncode * reconMatrixPE); if (d.effectiveEchoSpacingGE > 0.0) effectiveEchoSpacing = d.effectiveEchoSpacingGE / 1000000.0; json_Float(fp, "\t\"EffectiveEchoSpacing\": %g,\n", effectiveEchoSpacing); // Calculate true echo spacing (should match what Siemens reports on the console) // i.e., should match "echoSpacing" extracted from the ASCII CSA header, when that exists double trueESfactor = 1.0; if (d.accelFactPE > 1.0) trueESfactor /= d.accelFactPE; if (phaseOversampling > 0.0) trueESfactor *= (1.0 + phaseOversampling); float derivedEchoSpacing = 0.0; derivedEchoSpacing = bandwidthPerPixelPhaseEncode * trueESfactor * reconMatrixPE; if (derivedEchoSpacing != 0) derivedEchoSpacing = 1/derivedEchoSpacing; json_Float(fp, "\t\"DerivedVendorReportedEchoSpacing\": %g,\n", derivedEchoSpacing); //TotalReadOutTime: Really should be called "EffectiveReadOutTime", by analogy with "EffectiveEchoSpacing". // But BIDS spec calls it "TotalReadOutTime". // So, we DO NOT USE EchoTrainLength, because not trying to compute the actual (physical) readout time. // Rather, the point of computing "EffectiveEchoSpacing" properly is so that this // "Total(Effective)ReadOutTime" can be computed straightforwardly as the product of the // EffectiveEchoSpacing and the size of the *reconstructed* matrix in the PE direction. // see https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/topup/TopupUsersGuide#A--datain // FSL definition is start of first line until start of last line. // Other than the use of (n-1), the value is basically just 1.0/bandwidthPerPixelPhaseEncode. // https://github.com/rordenlab/dcm2niix/issues/130 if ((reconMatrixPE > 0) && (effectiveEchoSpacing > 0.0)) fprintf(fp, "\t\"TotalReadoutTime\": %g,\n", effectiveEchoSpacing * (reconMatrixPE - 1.0)); json_Float(fp, "\t\"PixelBandwidth\": %g,\n", d.pixelBandwidth ); if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (d.dwellTime > 0)) fprintf(fp, "\t\"DwellTime\": %g,\n", d.dwellTime * 1E-9); // Phase encoding polarity if (((d.phaseEncodingRC == 'R') || (d.phaseEncodingRC == 'C')) && (!d.is3DAcq) && ((d.CSA.phaseEncodingDirectionPositive == 1) || (d.CSA.phaseEncodingDirectionPositive == 0))) { if (d.phaseEncodingRC == 'C') //Values should be "R"ow, "C"olumn or "?"Unknown fprintf(fp, "\t\"PhaseEncodingDirection\": \"j"); else if (d.phaseEncodingRC == 'R') fprintf(fp, "\t\"PhaseEncodingDirection\": \"i"); else fprintf(fp, "\t\"PhaseEncodingDirection\": \"?"); //phaseEncodingDirectionPositive has one of three values: UNKNOWN (-1), NEGATIVE (0), POSITIVE (1) //However, DICOM and NIfTI are reversed in the j (ROW) direction //Equivalent to dicm2nii's "if flp(iPhase), phPos = ~phPos; end" //for samples see https://github.com/rordenlab/dcm2niix/issues/125 if (d.CSA.phaseEncodingDirectionPositive == -1) fprintf(fp, "?"); //unknown else if ((d.CSA.phaseEncodingDirectionPositive == 0) && (d.phaseEncodingRC != 'C')) fprintf(fp, "-"); else if ((d.phaseEncodingRC == 'C') && (d.CSA.phaseEncodingDirectionPositive == 1) && (opts.isFlipY)) fprintf(fp, "-"); else if ((d.phaseEncodingRC == 'C') && (d.CSA.phaseEncodingDirectionPositive == 0) && (!opts.isFlipY)) fprintf(fp, "-"); fprintf(fp, "\",\n"); } //only save PhaseEncodingDirection if BOTH direction and POLARITY are known // Slice Timing if (d.CSA.sliceTiming[0] >= 0.0) { fprintf(fp, "\t\"SliceTiming\": [\n"); if (d.CSA.protocolSliceNumber1 > 1) { //https://github.com/rordenlab/dcm2niix/issues/40 //equivalent to dicm2nii "s.SliceTiming = s.SliceTiming(end:-1:1);" int mx = 0; for (int i = 0; i < kMaxEPI3D; i++) { if (d.CSA.sliceTiming[i] < 0.0) break; mx++; } mx--; for (int i = mx; i >= 0; i--) { if (d.CSA.sliceTiming[i] < 0.0) break; if (i != mx) fprintf(fp, ",\n"); fprintf(fp, "\t\t%g", d.CSA.sliceTiming[i] / 1000.0 ); } } else { for (int i = 0; i < kMaxEPI3D; i++) { if (d.CSA.sliceTiming[i] < 0.0) break; if (i != 0) fprintf(fp, ",\n"); fprintf(fp, "\t\t%g", d.CSA.sliceTiming[i] / 1000.0 ); } } fprintf(fp, "\t],\n"); } //DICOM orientation and phase encoding: useful for 3D undistortion. Original DICOM values: DICOM not NIfTI space, ignores if 3D image re-oriented fprintf(fp, "\t\"ImageOrientationPatientDICOM\": [\n"); for (int i = 1; i < 7; i++) { if (i != 1) fprintf(fp, ",\n"); fprintf(fp, "\t\t%g", d.orient[i]); } fprintf(fp, "\t],\n"); if (d.phaseEncodingRC == 'C') fprintf(fp, "\t\"InPlanePhaseEncodingDirectionDICOM\": \"COL\",\n" ); if (d.phaseEncodingRC == 'R') fprintf(fp, "\t\"InPlanePhaseEncodingDirectionDICOM\": \"ROW\",\n" ); // Finish up with info on the conversion tool fprintf(fp, "\t\"ConversionSoftware\": \"dcm2niix\",\n"); fprintf(fp, "\t\"ConversionSoftwareVersion\": \"%s\"\n", kDCMvers ); //fprintf(fp, "\t\"DicomConversion\": [\"dcm2niix\", \"%s\"]\n", kDCMvers ); fprintf(fp, "}\n"); fclose(fp); }// nii_SaveBIDS() bool isADCnotDTI(TDTI bvec) { //returns true if bval!=0 but all bvecs == 0 (Philips code for derived ADC image) return ((!isSameFloat(bvec.V[0],0.0f)) && //not a B-0 image ((isSameFloat(bvec.V[1],0.0f)) && (isSameFloat(bvec.V[2],0.0f)) && (isSameFloat(bvec.V[3],0.0f)) ) ); } unsigned char * removeADC(struct nifti_1_header *hdr, unsigned char *inImg, int numADC) { //for speed we just clip the number of volumes, the realloc routine would be nice // we do not want to copy input to a new smaller array since 4D DTI datasets can be huge // and that would require almost twice as much RAM if (numADC < 1) return inImg; hdr->dim[4] = hdr->dim[4] - numADC; if (hdr->dim[4] < 2) hdr->dim[0] = 3; //e.g. 4D 2-volume DWI+ADC becomes 3D DWI if ADC is removed return inImg; } //removeADC() //#define naive_reorder_vols //for simple, fast re-ordering that consumes a lot of RAM #ifdef naive_reorder_vols unsigned char * reorderVolumes(struct nifti_1_header *hdr, unsigned char *inImg, int * volOrderIndex) { //reorder volumes to place ADC at end and (optionally) B=0 at start // volOrderIndex[0] reports location of desired first volume // naive solution creates an output buffer that doubles RAM usage (2 *numVol) int numVol = hdr->dim[4]; int numVolBytes = hdr->dim[1]*hdr->dim[2]*hdr->dim[3]*(hdr->bitpix/8); if ((!volOrderIndex) || (numVol < 1) || (numVolBytes < 1)) return inImg; unsigned char *outImg = (unsigned char *)malloc(numVolBytes * numVol); int outPos = 0; for (int i = 0; i < numVol; i++) { memcpy(&outImg[outPos], &inImg[volOrderIndex[i] * numVolBytes], numVolBytes); // dest, src, bytes outPos += numVolBytes; } //for each volume free(volOrderIndex); free(inImg); return outImg; } //reorderVolumes() #else // naive_reorder_vols unsigned char * reorderVolumes(struct nifti_1_header *hdr, unsigned char *inImg, int * volOrderIndex) { //reorder volumes to place ADC at end and (optionally) B=0 at start // volOrderIndex[0] reports location of desired first volume // complicated by fact that 4D DTI data is often huge // simple solutions would create an output buffer that would double RAM usage (2 *numVol) // here we bubble-sort volumes in place to use numVols+1 memory int numVol = hdr->dim[4]; int numVolBytes = hdr->dim[1]*hdr->dim[2]*hdr->dim[3]*(hdr->bitpix/8); int * inPos = (int *) malloc(numVol * sizeof(int)); for (int i = 0; i < numVol; i++) inPos[i] = i; unsigned char *tempVol = (unsigned char *)malloc(numVolBytes); int outPos = 0; for (int o = 0; o < numVol; o++) { int i = inPos[volOrderIndex[o]]; //input volume if (i == o) continue; //volume in correct order memcpy(&tempVol[0], &inImg[o * numVolBytes], numVolBytes); //make temp memcpy(&inImg[o * numVolBytes], &inImg[i * numVolBytes], numVolBytes); //copy volume to desire location dest, src, bytes memcpy(&inImg[i * numVolBytes], &tempVol[0], numVolBytes); //copy unsorted volume inPos[o] = i; outPos += numVolBytes; } //for each volume free(inPos); free(volOrderIndex); free(tempVol); return inImg; } //reorderVolumes() #endif // naive_reorder_vols float * bvals; //global variable for cmp_bvals int cmp_bvals(const void *a, const void *b){ int ia = *(int *)a; int ib = *(int *)b; //return bvals[ia] > bvals[ib] ? -1 : bvals[ia] < bvals[ib]; return bvals[ia] < bvals[ib] ? -1 : bvals[ia] > bvals[ib]; } // cmp_bvals() int * nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[], struct TDCMopts opts, int sliceDir, struct TDTI4D *dti4D, int * numADC) { //reports non-zero if any volumes should be excluded (e.g. philip stores an ADC maps) //to do: works with 3D mosaics and 4D files, must remove repeated volumes for 2D sequences.... *numADC = 0; if (opts.isOnlyBIDS) return NULL; uint64_t indx0 = dcmSort[0].indx; //first volume int numDti = dcmList[indx0].CSA.numDti; if (numDti < 1) return NULL; if ((numDti < 3) && (nConvert < 3)) return NULL; TDTI * vx = NULL; if (numDti > 2) { vx = (TDTI *)malloc(numDti * sizeof(TDTI)); for (int i = 0; i < numDti; i++) //for each direction for (int v = 0; v < 4; v++) //for each vector+B-value vx[i].V[v] = dti4D->S[i].V[v]; } else { //if (numDti == 1) {//extract DTI from different slices vx = (TDTI *)malloc(nConvert * sizeof(TDTI)); numDti = 0; for (int i = 0; i < nConvert; i++) { //for each image if ((dcmList[indx0].CSA.mosaicSlices > 1) || (isSamePosition(dcmList[indx0],dcmList[dcmSort[i].indx]))) { //if (numDti < kMaxDTIv) for (int v = 0; v < 4; v++) //for each vector+B-value vx[numDti].V[v] = dcmList[dcmSort[i].indx].CSA.dtiV[v]; //dcmList[indx0].CSA.dtiV[numDti][v] = dcmList[dcmSort[i].indx].CSA.dtiV[0][v]; numDti++; } //for slices with repeats }//for each file dcmList[indx0].CSA.numDti = numDti; //warning structure not changed outside scope! } bool bValueVaries = false; for (int i = 1; i < numDti; i++) //check if all bvalues match first volume if (vx[i].V[0] != vx[0].V[0]) bValueVaries = true; if (!bValueVaries) { bool bVecVaries = false; for (int i = 1; i < numDti; i++) {//check if all bvalues match first volume if (vx[i].V[1] != vx[0].V[1]) bVecVaries = true; if (vx[i].V[2] != vx[0].V[2]) bVecVaries = true; if (vx[i].V[3] != vx[0].V[3]) bVecVaries = true; } if (!bVecVaries) { free(vx); return NULL; } for (int i = 1; i < numDti; i++) printMessage("bxyz %g %g %g %g\n",vx[i].V[0],vx[i].V[1],vx[i].V[2],vx[i].V[3]); printWarning("No bvec/bval files created. Only one B-value reported for all volumes: %g\n",vx[0].V[0]); free(vx); return NULL; } int minB0idx = 0; float minB0 = vx[0].V[0]; for (int i = 0; i < numDti; i++) if (vx[i].V[0] < minB0) { minB0 = vx[i].V[0]; minB0idx = i; } float maxB0 = vx[0].V[0]; for (int i = 0; i < numDti; i++) if (vx[i].V[0] > maxB0) maxB0 = vx[i].V[0]; //for CMRR sequences unweighted volumes are not actually B=0 but they have B near zero if (minB0 > 50) printWarning("This diffusion series does not have a B0 (reference) volume\n"); if ((!opts.isSortDTIbyBVal) && (minB0idx > 0)) printMessage("Note: B0 not the first volume in the series (FSL eddy reference volume is %d)\n", minB0idx); float kADCval = maxB0 + 1; //mark as unusual *numADC = 0; bvals = (float *) malloc(numDti * sizeof(float)); for (int i = 0; i < numDti; i++) { bvals[i] = vx[i].V[0]; if (isADCnotDTI(vx[i])) { *numADC = *numADC + 1; //printMessage("Volume %d is not a normal DTI image (ADC?)\n", i+1); bvals[i] = kADCval; } bvals[i] = bvals[i] + (0.5 * i/numDti); //add a small bias so ties are kept in sequential order } if (*numADC > 0) { // DWIs (i.e. short diffusion scans with too few directions to // calculate tensors...they typically acquire b=0 + 3 b > 0 so // the isotropic trace or MD can be calculated) often come as // b=0 and trace pairs, with the b=0 and trace in either order, // and often as "ORIGINAL", even though the trace is not. // The bval file is needed for downstream processing to know // * which is the b=0 and which is the trace, and // * what b is for the trace, // so dcm2niix should *always* write the bval and bvec files, // AND include the b for the trace for DWIs. // One hackish way to accomplish that is to set *numADC = 0 // when *numADC == 1 && numDti == 2. // - Rob Reid, 2017-11-29. if ((*numADC == 1) && ((numDti - *numADC) < 2)){ *numADC = 0; printMessage("Note: this appears to be a b=0+trace DWI; ADC/trace removal has been disabled.\n"); } else{ if ((numDti - *numADC) < 2) { if (!dcmList[indx0].isDerived) //no need to warn if images are derived Trace/ND pair printWarning("No bvec/bval files created: only single value after ADC excluded\n"); *numADC = 0; free(bvals); free(vx); return NULL; } printMessage("Note: %d volumes appear to be ADC or trace images that will be removed to allow processing\n", *numADC); } } //sort ALL including ADC int * volOrderIndex = (int *) malloc(numDti * sizeof(int)); for (int i = 0; i < numDti; i++) volOrderIndex[i] = i; if (opts.isSortDTIbyBVal) qsort(volOrderIndex, numDti, sizeof(*volOrderIndex), cmp_bvals); else if (*numADC > 0) { int o = 0; for (int i = 0; i < numDti; i++) { if (bvals[i] < kADCval) { volOrderIndex[o] = i; o++; } //if not ADC } //for each volume } //if sort else if has ADC free(bvals); //save VX as sorted TDTI * vxOrig = (TDTI *)malloc(numDti * sizeof(TDTI)); for (int i = 0; i < numDti; i++) vxOrig[i] = vx[i]; //remove ADC numDti = numDti - *numADC; free(vx); vx = (TDTI *)malloc(numDti * sizeof(TDTI)); for (int i = 0; i < numDti; i++) vx[i] = vxOrig[volOrderIndex[i]]; free(vxOrig); //if no ADC or sequential, the is no need to re-order volumes bool isSequential = true; for (int i = 1; i < (numDti + *numADC); i++) if (volOrderIndex[i] <= volOrderIndex[i-1]) isSequential = false; if (isSequential) { free(volOrderIndex); volOrderIndex = NULL; } if (!isSequential) printMessage("DTI volumes re-ordered by ascending b-value\n"); dcmList[indx0].CSA.numDti = numDti; //warning structure not changed outside scope! geCorrectBvecs(&dcmList[indx0],sliceDir, vx); siemensPhilipsCorrectBvecs(&dcmList[indx0],sliceDir, vx); if (!opts.isFlipY ) { //!FLIP_Y&& (dcmList[indx0].CSA.mosaicSlices < 2) mosaics are always flipped in the Y direction for (int i = 0; i < (numDti); i++) { if (fabs(vx[i].V[2]) > FLT_EPSILON) vx[i].V[2] = -vx[i].V[2]; } //for each direction } //if not a mosaic if (opts.isVerbose) { for (int i = 0; i < (numDti); i++) { printMessage("%d\tB=\t%g\tVec=\t%g\t%g\t%g\n",i, vx[i].V[0], vx[i].V[1],vx[i].V[2],vx[i].V[3]); } //for each direction } //printMessage("%f\t%f\t%f",dcmList[indx0].CSA.dtiV[1][1],dcmList[indx0].CSA.dtiV[1][2],dcmList[indx0].CSA.dtiV[1][3]); #ifdef HAVE_R std::vector bValues(numDti); std::vector bVectors(numDti*3); for (int i = 0; i < numDti; i++) { bValues[i] = vx[i].V[0]; for (int j = 0; j < 3; j++) bVectors[i+j*numDti] = vx[i].V[j+1]; } // The image hasn't been created yet, so the attributes must be deferred ImageList *images = (ImageList *) opts.imageList; images->addDeferredAttribute("bValues", bValues); images->addDeferredAttribute("bVectors", bVectors, numDti, 3); #else char txtname[2048] = {""}; strcpy (txtname,pathoutname); strcat (txtname,".bval"); //printMessage("Saving DTI %s\n",txtname); FILE *fp = fopen(txtname, "w"); if (fp == NULL) { free(vx); return volOrderIndex; } for (int i = 0; i < (numDti-1); i++) { if (opts.isCreateBIDS) { fprintf(fp, "%g ", vx[i].V[0]); } else { fprintf(fp, "%g\t", vx[i].V[0]); } } fprintf(fp, "%g\n", vx[numDti-1].V[0]); fclose(fp); strcpy(txtname,pathoutname); strcat (txtname,".bvec"); //printMessage("Saving DTI %s\n",txtname); fp = fopen(txtname, "w"); if (fp == NULL) { free(vx); return volOrderIndex; } for (int v = 1; v < 4; v++) { for (int i = 0; i < (numDti-1); i++) { if (opts.isCreateBIDS) { fprintf(fp, "%g ", vx[i].V[v]); } else { fprintf(fp, "%g\t", vx[i].V[v]); } } fprintf(fp, "%g\n", vx[numDti-1].V[v]); } fclose(fp); #endif free(vx); return volOrderIndex; }// nii_SaveDTI() float sqr(float v){ return v*v; }// sqr() float intersliceDistance(struct TDICOMdata d1, struct TDICOMdata d2) { //some MRI scans have gaps between slices, some CT have overlapping slices. Comparing adjacent slices provides measure for dx between slices if ( isNanPosition(d1) || isNanPosition(d2)) return d1.xyzMM[3]; float tilt = 1.0; if (d1.gantryTilt != 0) tilt = (float) cos(d1.gantryTilt * M_PI/180); //for CT scans with gantry tilt, we need to compute distance between slices, not distance along bed return tilt * sqrt( sqr(d1.patientPosition[1]-d2.patientPosition[1])+ sqr(d1.patientPosition[2]-d2.patientPosition[2])+ sqr(d1.patientPosition[3]-d2.patientPosition[3])); } //intersliceDistance() void swapDim3Dim4(int d3, int d4, struct TDCMsort dcmSort[]) { //swap space and time: input A0,A1...An,B0,B1...Bn output A0,B0,A1,B1,... int nConvert = d3 * d4; //#ifdef _MSC_VER TDCMsort * dcmSortIn = (TDCMsort *)malloc(nConvert * sizeof(TDCMsort)); //#else // struct TDCMsort dcmSortIn[nConvert]; //#endif for (int i = 0; i < nConvert; i++) dcmSortIn[i] = dcmSort[i]; int i = 0; for (int b = 0; b < d3; b++) for (int a = 0; a < d4; a++) { int k = (a *d3) + b; //printMessage("%d -> %d %d ->%d\n",i,a, b, k); dcmSort[k] = dcmSortIn[i]; i++; } //#ifdef _MSC_VER free(dcmSortIn); //#endif } //swapDim3Dim4() bool intensityScaleVaries(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[]){ //detect whether some DICOM images report different intensity scaling //some Siemens PET scanners generate 16-bit images where slice has its own scaling factor. // since NIfTI provides a single scaling factor for each file, these images require special consideration if (nConvert < 2) return false; bool iVaries = false; float iScale = dcmList[dcmSort[0].indx].intenScale; float iInter = dcmList[dcmSort[0].indx].intenIntercept; for (int i = 1; i < nConvert; i++) { //stack additional images uint64_t indx = dcmSort[i].indx; if (fabs (dcmList[indx].intenScale - iScale) > FLT_EPSILON) iVaries = true; if (fabs (dcmList[indx].intenIntercept- iInter) > FLT_EPSILON) iVaries = true; } return iVaries; } //intensityScaleVaries() /*unsigned char * nii_bgr2rgb(unsigned char* bImg, struct nifti_1_header *hdr) { //DICOM planarappears to be BBB..B,GGG..G,RRR..R, NIfTI RGB saved in planes RRR..RGGG..GBBBB..B // see http://www.barre.nom.fr/medical/samples/index.html US-RGB-8-epicard if (hdr->datatype != DT_RGB24) return bImg; int dim3to7 = 1; for (int i = 3; i < 8; i++) if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i]; int sliceBytes24 = hdr->dim[1]*hdr->dim[2] * hdr->bitpix/8; int sliceBytes8 = hdr->dim[1]*hdr->dim[2]; //Byte bImg[ bSz ]; //[img getBytes:&bImg length:bSz]; unsigned char slice24[sliceBytes24]; int sliceOffsetR = 0; for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice memcpy(&slice24, &bImg[sliceOffsetR], sliceBytes24); memcpy( &bImg[sliceOffsetR], &slice24[sliceBytes8*2], sliceBytes8); sliceOffsetR += sliceBytes8; memcpy( &bImg[sliceOffsetR], &slice24[sliceBytes8], sliceBytes8); sliceOffsetR += sliceBytes8; memcpy( &bImg[sliceOffsetR], &slice24[0], sliceBytes8); sliceOffsetR += sliceBytes8; } //for each slice return bImg; } */ bool niiExists(const char*pathoutname) { char niiname[2048] = {""}; strcat (niiname,pathoutname); strcat (niiname,".nii"); if (is_fileexists(niiname)) return true; char gzname[2048] = {""}; strcat (gzname,pathoutname); strcat (gzname,".nii.gz"); if (is_fileexists(gzname)) return true; return false; } //niiExists() #ifndef W_OK #define W_OK 2 /* write mode check */ #endif int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopts opts) { char pth[1024] = {""}; if (strlen(opts.outdir) > 0) { strcpy(pth, opts.outdir); int w =access(pth,W_OK); if (w != 0) { if (getcwd(pth, sizeof(pth)) != NULL) { w =access(pth,W_OK); if (w != 0) { printError("You do not have write permissions for the directory %s\n",opts.outdir); return EXIT_FAILURE; } printWarning("%s write permission denied. Saving to working directory %s \n", opts.outdir, pth); } } } char inname[1024] = {""};//{"test%t_%av"}; //% a = acquisition, %n patient name, %t time strcpy(inname, opts.filename); char outname[1024] = {""}; char newstr[256]; if (strlen(inname) < 1) { strcpy(inname, "T%t_N%n_S%s"); } size_t start = 0; size_t pos = 0; bool isCoilReported = false; bool isEchoReported = false; bool isSeriesReported = false; while (pos < strlen(inname)) { if (inname[pos] == '%') { if (pos > start) { strncpy(&newstr[0], &inname[0] + start, pos - start); newstr[pos - start] = '\0'; strcat (outname,newstr); } pos++; //extra increment: skip both % and following character char f = 'P'; if (pos < strlen(inname)) f = toupper(inname[pos]); if ((f == 'A') && (dcm.coilNum > 0)) { isCoilReported = true; sprintf(newstr, "%02d", dcm.coilNum); strcat (outname,newstr); } if (f == 'C') strcat (outname,dcm.imageComments); if (f == 'D') strcat (outname,dcm.seriesDescription); if (f == 'E') { isEchoReported = true; sprintf(newstr, "%d", dcm.echoNum); strcat (outname,newstr); } if (f == 'F') strcat (outname,opts.indirParent); if (f == 'I') strcat (outname,dcm.patientID); if (f == 'J') strcat (outname,dcm.seriesInstanceUID); if (f == 'K') strcat (outname,dcm.studyInstanceUID); if (f == 'L') //"L"ocal Institution-generated description or classification of the Procedure Step that was performed. strcat (outname,dcm.procedureStepDescription); if (f == 'M') { if (dcm.manufacturer == kMANUFACTURER_GE) strcat (outname,"GE"); else if (dcm.manufacturer == kMANUFACTURER_TOSHIBA) strcat (outname,"To"); else if (dcm.manufacturer == kMANUFACTURER_PHILIPS) strcat (outname,"Ph"); else if (dcm.manufacturer == kMANUFACTURER_SIEMENS) strcat (outname,"Si"); else strcat (outname,"NA"); //manufacturer name not available } if (f == 'N') strcat (outname,dcm.patientName); if (f == 'P') strcat (outname,dcm.protocolName); if (f == 'Q') strcat (outname,dcm.scanningSequence); if (f == 'S') { sprintf(newstr, "%ld", dcm.seriesNum); strcat (outname,newstr); isSeriesReported = true; } if (f == 'T') { sprintf(newstr, "%0.0f", dcm.dateTime); strcat (outname,newstr); } if (f == 'U') { #ifdef mySegmentByAcq sprintf(newstr, "%d", dcm.acquNum); strcat (outname,newstr); #else printWarning("Ignoring '%%u' in output filename (recompile to segment by acquisition)\n"); #endif } if (f == 'V') { if (dcm.manufacturer == kMANUFACTURER_GE) strcat (outname,"GE"); else if (dcm.manufacturer == kMANUFACTURER_PHILIPS) strcat (outname,"Philips"); else if (dcm.manufacturer == kMANUFACTURER_SIEMENS) strcat (outname,"Siemens"); else if (dcm.manufacturer == kMANUFACTURER_TOSHIBA) strcat (outname,"Toshiba"); else strcat (outname,"NA"); } if (f == 'X') strcat (outname,dcm.studyID); if (f == 'Z') strcat (outname,dcm.sequenceName); if ((f >= '0') && (f <= '9')) { if ((pos start) { //append any trailing characters strncpy(&newstr[0], &inname[0] + start, pos - start); newstr[pos - start] = '\0'; strcat (outname,newstr); } if (!isCoilReported && (dcm.coilNum > 1)) { sprintf(newstr, "_c%d", dcm.coilNum); strcat (outname,newstr); } if ((!isEchoReported) && (dcm.isMultiEcho) && (dcm.echoNum >= 1)) { //multiple echoes saved as same series sprintf(newstr, "_e%d", dcm.echoNum); strcat (outname,newstr); isEchoReported = true; } if ((!isSeriesReported) && (!isEchoReported) && (dcm.echoNum > 1)) { //last resort: user provided no method to disambiguate echo number in filename sprintf(newstr, "_e%d", dcm.echoNum); strcat (outname,newstr); } if (dcm.isHasPhase) strcat (outname,"_ph"); //manufacturer name not available if (strlen(outname) < 1) strcpy(outname, "dcm2nii_invalidName"); if (outname[0] == '.') outname[0] = '_'; //make sure not a hidden file //eliminate illegal characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx #if defined(_WIN64) || defined(_WIN32) //https://stackoverflow.com/questions/1976007/what-characters-are-forbidden-in-windows-and-linux-directory-names for (size_t pos = 0; pos') || (outname[pos] == ':') || (outname[pos] == '"') // || (outname[pos] == '/') || (outname[pos] == '\\') || (outname[pos] == '^') || (outname[pos] == '*') || (outname[pos] == '|') || (outname[pos] == '?')) outname[pos] = '_'; for (int pos = 0; pos= 26) { printError("Too many NIFTI images with the name %s\n", baseoutname); return EXIT_FAILURE; } //printMessage("-->%s\n",pathoutname); return EXIT_SUCCESS; //printMessage("outname=%s\n", pathoutname); strcpy(niiFilename,pathoutname); return EXIT_SUCCESS; } //nii_createFilename() void nii_createDummyFilename(char * niiFilename, struct TDCMopts opts) { //generate string that illustrating sample of filename struct TDICOMdata d = clear_dicom_data(); strcpy(d.patientName, "John_Doe"); strcpy(d.patientID, "ID123"); strcpy(d.imageType,"ORIGINAL"); strcpy(d.imageComments, "imgComments"); strcpy(d.studyDate, "1/1/1977"); strcpy(d.studyTime, "11:11:11"); strcpy(d.protocolName, "MPRAGE"); strcpy(d.seriesDescription, "T1_mprage"); strcpy(d.sequenceName, "T1"); strcpy(d.scanningSequence, "tfl3d1_ns"); strcpy(d.sequenceVariant, "tfl3d1_ns"); strcpy(d.manufacturersModelName, "N/A"); strcpy(d.procedureStepDescription, ""); strcpy(d.seriesInstanceUID, ""); strcpy(d.studyInstanceUID, ""); strcpy(d.bodyPartExamined,""); strcpy(opts.indirParent,"myFolder"); char niiFilenameBase[1024] = {"/usr/myFolder/dicom.dcm"}; nii_createFilename(d, niiFilenameBase, opts) ; strcpy(niiFilename,"Example output filename: '"); strcat(niiFilename,niiFilenameBase); if (opts.isGz) strcat(niiFilename,".nii.gz'"); else strcat(niiFilename,".nii'"); }// nii_createDummyFilename() #ifndef myDisableZLib #ifndef MiniZ unsigned long mz_compressBound(unsigned long source_len) { return compressBound(source_len); } unsigned long mz_crc32(unsigned long crc, const unsigned char *ptr, size_t buf_len) { return crc32(crc, ptr, (uInt) buf_len); } #endif #ifndef MZ_UBER_COMPRESSION //defined in miniz, not defined in zlib #define MZ_UBER_COMPRESSION 9 #endif #ifndef MZ_DEFAULT_LEVEL #define MZ_DEFAULT_LEVEL 6 #endif void writeNiiGz (char * baseName, struct nifti_1_header hdr, unsigned char* src_buffer, unsigned long src_len, int gzLevel) { //create gz file in RAM, save to disk http://www.zlib.net/zlib_how.html // in general this single-threaded approach is slower than PIGZ but is useful for slow (network attached) disk drives char fname[2048] = {""}; strcpy (fname,baseName); strcat (fname,".nii.gz"); unsigned long hdrPadBytes = sizeof(hdr) + 4; //348 byte header + 4 byte pad unsigned long cmp_len = mz_compressBound(src_len+hdrPadBytes); unsigned char *pCmp = (unsigned char *)malloc(cmp_len); z_stream strm; strm.total_in = 0; strm.total_out = 0; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.next_out = pCmp; // output char array strm.avail_out = (unsigned int)cmp_len; // size of output int zLevel = MZ_DEFAULT_LEVEL;//Z_DEFAULT_COMPRESSION; if ((gzLevel > 0) && (gzLevel < 11)) zLevel = gzLevel; if (zLevel > MZ_UBER_COMPRESSION) zLevel = MZ_UBER_COMPRESSION; if (deflateInit(&strm, zLevel)!= Z_OK) { free(pCmp); return; } //add header unsigned char *pHdr = (unsigned char *)malloc(hdrPadBytes); pHdr[hdrPadBytes-1] = 0; pHdr[hdrPadBytes-2] = 0; pHdr[hdrPadBytes-3] = 0; pHdr[hdrPadBytes-4] = 0; memcpy(pHdr,&hdr, sizeof(hdr)); strm.avail_in = (unsigned int)hdrPadBytes; // size of input strm.next_in = (uint8_t *)pHdr; // input header -- TPX strm.next_in = (Bytef *)pHdr; uint32_t deflate(&strm, Z_NO_FLUSH); //add image strm.avail_in = (unsigned int)src_len; // size of input strm.next_in = (uint8_t *)src_buffer; // input image -- TPX strm.next_in = (Bytef *)src_buffer; deflate(&strm, Z_FINISH); //Z_NO_FLUSH; //finish up deflateEnd(&strm); unsigned long file_crc32 = mz_crc32(0L, Z_NULL, 0); file_crc32 = mz_crc32(file_crc32, pHdr, (unsigned int)hdrPadBytes); file_crc32 = mz_crc32(file_crc32, src_buffer, (unsigned int)src_len); cmp_len = strm.total_out; if (cmp_len <= 0) { free(pCmp); free(src_buffer); return; } FILE *fileGz = fopen(fname, "wb"); if (!fileGz) { free(pCmp); free(src_buffer); return; } //write header http://www.gzip.org/zlib/rfc-gzip.html fputc((char)0x1f, fileGz); //ID1 fputc((char)0x8b, fileGz); //ID2 fputc((char)0x08, fileGz); //CM - use deflate compression method fputc((char)0x00, fileGz); //FLG - no addition fields fputc((char)0x00, fileGz); //MTIME0 fputc((char)0x00, fileGz); //MTIME1 fputc((char)0x00, fileGz); //MTIME2 fputc((char)0x00, fileGz); //MTIME2 fputc((char)0x00, fileGz); //XFL fputc((char)0xff, fileGz); //OS //write Z-compressed data fwrite (&pCmp[2] , sizeof(char), cmp_len-6, fileGz); //-6 as LZ78 format has 2 bytes header (typically 0x789C) and 4 bytes tail (ADLER 32) //write tail: write redundancy check and uncompressed size as bytes to ensure LITTLE-ENDIAN order fputc((unsigned char)(file_crc32), fileGz); fputc((unsigned char)(file_crc32 >> 8), fileGz); fputc((unsigned char)(file_crc32 >> 16), fileGz); fputc((unsigned char)(file_crc32 >> 24), fileGz); fputc((unsigned char)(strm.total_in), fileGz); fputc((unsigned char)(strm.total_in >> 8), fileGz); fputc((unsigned char)(strm.total_in >> 16), fileGz); fputc((unsigned char)(strm.total_in >> 24), fileGz); fclose(fileGz); free(pCmp); free(pHdr); } //writeNiiGz() #endif #ifdef HAVE_R // Version of nii_saveNII() for R/divest: create nifti_image pointer and push onto stack int nii_saveNII (char *niiFilename, struct nifti_1_header hdr, unsigned char *im, struct TDCMopts opts) { hdr.vox_offset = 352; // Extract the basename from the full file path // R always uses '/' as the path separator, so this should work on all platforms char *start = niiFilename + strlen(niiFilename); while (*start != '/') start--; std::string name(++start); nifti_image *image = nifti_convert_nhdr2nim(hdr, niiFilename); if (image == NULL) return EXIT_FAILURE; image->data = (void *) im; ImageList *images = (ImageList *) opts.imageList; images->append(image, name); free(image); return EXIT_SUCCESS; } void nii_saveAttributes (struct TDICOMdata &data, struct nifti_1_header &header, struct TDCMopts &opts) { ImageList *images = (ImageList *) opts.imageList; switch (data.manufacturer) { case kMANUFACTURER_SIEMENS: images->addAttribute("manufacturer", "Siemens"); break; case kMANUFACTURER_GE: images->addAttribute("manufacturer", "GE"); break; case kMANUFACTURER_PHILIPS: images->addAttribute("manufacturer", "Philips"); break; case kMANUFACTURER_TOSHIBA: images->addAttribute("manufacturer", "Toshiba"); break; } if (strlen(data.manufacturersModelName) > 0) images->addAttribute("scannerModelName", data.manufacturersModelName); if (strlen(data.imageType) > 0) images->addAttribute("imageType", data.imageType); if (strlen(data.studyDate) >= 8 && strcmp(data.studyDate,"00000000") != 0) images->addDateAttribute("studyDate", data.studyDate); if (strlen(data.studyTime) > 0 && strncmp(data.studyTime,"000000",6) != 0) images->addAttribute("studyTime", data.studyTime); if (data.fieldStrength > 0.0) images->addAttribute("fieldStrength", data.fieldStrength); if (data.flipAngle > 0.0) images->addAttribute("flipAngle", data.flipAngle); if (data.TE > 0.0) images->addAttribute("echoTime", data.TE); if (data.TR > 0.0) images->addAttribute("repetitionTime", data.TR); if ((data.CSA.bandwidthPerPixelPhaseEncode > 0.0) && (header.dim[2] > 0) && (header.dim[1] > 0)) { if (data.phaseEncodingRC =='C') images->addAttribute("dwellTime", 1.0/data.CSA.bandwidthPerPixelPhaseEncode/header.dim[2]); else if (data.phaseEncodingRC == 'R') images->addAttribute("dwellTime", 1.0/data.CSA.bandwidthPerPixelPhaseEncode/header.dim[1]); } if (data.phaseEncodingRC == 'C') images->addAttribute("phaseEncodingDirection", "j"); else if (data.phaseEncodingRC == 'R') images->addAttribute("phaseEncodingDirection", "i"); if (data.CSA.phaseEncodingDirectionPositive != -1) images->addAttribute("phaseEncodingSign", data.CSA.phaseEncodingDirectionPositive == 0 ? -1 : 1); } #else int nii_saveNII(char * niiFilename, struct nifti_1_header hdr, unsigned char* im, struct TDCMopts opts) { if (opts.isOnlyBIDS) return EXIT_SUCCESS; hdr.vox_offset = 352; size_t imgsz = nii_ImgBytes(hdr); if (imgsz < 1) { printMessage("Error: Image size is zero bytes %s\n", niiFilename); return EXIT_FAILURE; } #ifndef myDisableGzSizeLimits //see https://github.com/rordenlab/dcm2niix/issues/124 uint64_t kMaxPigz = 4294967264; //https://stackoverflow.com/questions/5272825/detecting-64bit-compile-in-c #ifndef UINTPTR_MAX uint64_t kMaxGz = 2147483647; #elif UINTPTR_MAX == 0xffffffff uint64_t kMaxGz = 2147483647; #elif UINTPTR_MAX == 0xffffffffffffffff uint64_t kMaxGz = kMaxPigz; #else compiler error: unable to determine is 32 or 64 bit #endif #ifndef myDisableZLib if ((opts.isGz) && (strlen(opts.pigzname) < 1) && ((imgsz+hdr.vox_offset) >= kMaxGz) ) { //use internal compressor printWarning("Saving uncompressed data: internal compressor unable to process such large files.\n"); if ((imgsz+hdr.vox_offset) < kMaxPigz) printWarning(" Hint: using external compressor (pigz) should help.\n"); } else if ((opts.isGz) && (strlen(opts.pigzname) < 1) && ((imgsz+hdr.vox_offset) < kMaxGz) ) { //use internal compressor writeNiiGz (niiFilename, hdr, im, imgsz, opts.gzLevel); return EXIT_SUCCESS; } #endif #endif char fname[2048] = {""}; strcpy (fname,niiFilename); strcat (fname,".nii"); FILE *fp = fopen(fname, "wb"); if (!fp) return EXIT_FAILURE; fwrite(&hdr, sizeof(hdr), 1, fp); uint32_t pad = 0; fwrite(&pad, sizeof( pad), 1, fp); fwrite(&im[0], imgsz, 1, fp); fclose(fp); if ((opts.isGz) && (strlen(opts.pigzname) > 0) ) { #ifndef myDisableGzSizeLimits if ((imgsz+hdr.vox_offset) > kMaxPigz) { printWarning("Saving uncompressed data: image too large for pigz.\n"); return EXIT_SUCCESS; } #endif char command[768]; strcpy(command, "\"" ); strcat(command, opts.pigzname ); if ((opts.gzLevel > 0) && (opts.gzLevel < 12)) { char newstr[256]; sprintf(newstr, "\" -n -f -%d \"", opts.gzLevel); strcat(command, newstr); } else strcat(command, "\" -n -f \""); //current versions of pigz (2.3) built on Windows can hang if the filename is included, presumably because it is not finding the path characters ':\' strcat(command, fname); strcat(command, "\""); //add quotes in case spaces in filename 'pigz "c:\my dir\img.nii"' #if defined(_WIN64) || defined(_WIN32) //using CreateProcess instead of system to run in background (avoids screen flicker) DWORD exitCode; PROCESS_INFORMATION ProcessInfo = {0}; STARTUPINFO startupInfo= {0}; startupInfo.cb = sizeof(startupInfo); //StartupInfo.cb = sizeof StartupInfo ; //Only compulsory field if(CreateProcess(NULL, command, NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,NULL, NULL,&startupInfo,&ProcessInfo)) { //printMessage("compression --- %s\n",command); WaitForSingleObject(ProcessInfo.hProcess,INFINITE); CloseHandle(ProcessInfo.hThread); CloseHandle(ProcessInfo.hProcess); } else printMessage("compression failed %s\n",command); #else //if win else linux int ret = system(command); if (ret == -1) printWarning("Failed to execute: %s\n",command); #endif //else linux printMessage("compress: %s\n",command); } return EXIT_SUCCESS; }// nii_saveNII() #endif int nii_saveNII3D(char * niiFilename, struct nifti_1_header hdr, unsigned char* im, struct TDCMopts opts) { //save 4D series as sequence of 3D volumes struct nifti_1_header hdr1 = hdr; int nVol = 1; for (int i = 4; i < 8; i++) { if (hdr.dim[i] > 1) nVol = nVol * hdr.dim[i]; hdr1.dim[i] = 0; } hdr1.dim[0] = 3; //save as 3D file size_t imgsz = nii_ImgBytes(hdr1); size_t pos = 0; char fname[2048] = {""}; char zeroPad[1024] = {""}; double fnVol = nVol; int zeroPadLen = (1 + log10( fnVol)); sprintf(zeroPad,"%%s_%%0%dd",zeroPadLen); for (int i = 1; i <= nVol; i++) { sprintf(fname,zeroPad,niiFilename,i); if (nii_saveNII(fname, hdr1, (unsigned char*)&im[pos], opts) == EXIT_FAILURE) return EXIT_FAILURE; pos += imgsz; } return EXIT_SUCCESS; }// nii_saveNII3D() void nii_check16bitUnsigned(unsigned char *img, struct nifti_1_header *hdr){ //default NIfTI 16-bit is signed, set to unusual 16-bit unsigned if required... if (hdr->datatype != DT_UINT16) return; int dim3to7 = 1; for (int i = 3; i < 8; i++) if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i]; int nVox = hdr->dim[1]*hdr->dim[2]* dim3to7; if (nVox < 1) return; unsigned short * img16 = (unsigned short*) img; unsigned short max16 = img16[0]; //clock_t start = clock(); for (int i=0; i < nVox; i++) if (img16[i] > max16) max16 = img16[i]; //printMessage("max16= %d vox=%d %fms\n",max16, nVox, ((double)(clock()-start))/1000); if (max16 > 32767) { printMessage("Note: rare 16-bit UNSIGNED integer image. Older tools may require 32-bit conversion\n"); } else hdr->datatype = DT_INT16; } //nii_check16bitUnsigned() int siemensCtKludge(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[]) { //Siemens CT bug: when a user draws an open object graphics object onto a 2D slice this is appended as an additional image, //regardless of slice position. These images do not report number of positions in the volume, so we need tedious leg work to detect uint64_t indx0 = dcmSort[0].indx; if ((nConvert < 2) ||(dcmList[indx0].manufacturer != kMANUFACTURER_SIEMENS) || (!isSameFloat(dcmList[indx0].TR ,0.0f))) return nConvert; float prevDx = 0.0; for (int i = 1; i < nConvert; i++) { float dx = intersliceDistance(dcmList[indx0],dcmList[dcmSort[i].indx]); if ((!isSameFloat(dx,0.0f)) && (dx < prevDx)) { printMessage("Slices skipped: image position not sequential, admonish your vendor (Siemens OOG?)\n"); return i; } prevDx = dx; } return nConvert; //all images in sequential order }// siemensCtKludge() int isSameFloatT (float a, float b, float tolerance) { return (fabs (a - b) <= tolerance); } unsigned char * nii_saveNII3Dtilt(char * niiFilename, struct nifti_1_header * hdr, unsigned char* im, struct TDCMopts opts, float * sliceMMarray, float gantryTiltDeg, int manufacturer ) { //correct for gantry tilt - http://www.mathworks.com/matlabcentral/fileexchange/24458-dicom-gantry-tilt-correction if (opts.isOnlyBIDS) return im; if (gantryTiltDeg == 0.0) return im; struct nifti_1_header hdrIn = *hdr; int nVox2DIn = hdrIn.dim[1]*hdrIn.dim[2]; if ((nVox2DIn < 1) || (hdrIn.dim[0] != 3) || (hdrIn.dim[3] < 3)) return im; if (hdrIn.datatype != DT_INT16) { printMessage("Only able to correct gantry tilt for 16-bit integer data with at least 3 slices."); return im; } printMessage("Gantry Tilt Correction is new: please validate conversions\n"); float GNTtanPx = tan(gantryTiltDeg / (180/M_PI))/hdrIn.pixdim[2]; //tangent(degrees->radian) //unintuitive step: reverse sign for negative gantry tilt, therefore -27deg == +27deg (why @!?#) // seen in http://www.mathworks.com/matlabcentral/fileexchange/28141-gantry-detector-tilt-correction/content/gantry2.m // also validated with actual data... if (manufacturer == kMANUFACTURER_PHILIPS) //see 'Manix' example from Osirix GNTtanPx = - GNTtanPx; else if ((manufacturer == kMANUFACTURER_SIEMENS) && (gantryTiltDeg > 0.0)) GNTtanPx = - GNTtanPx; else if (manufacturer == kMANUFACTURER_GE) ; //do nothing else if (gantryTiltDeg < 0.0) GNTtanPx = - GNTtanPx; //see Toshiba examples from John Muschelli // printMessage("gantry tilt pixels per mm %g\n",GNTtanPx); short * imIn16 = ( short*) im; //create new output image: larger due to skew // compute how many pixels slice must be extended due to skew int s = hdrIn.dim[3] - 1; //top slice float maxSliceMM = fabs(s * hdrIn.pixdim[3]); if (sliceMMarray != NULL) maxSliceMM = fabs(sliceMMarray[s]); int pxOffset = ceil(fabs(GNTtanPx*maxSliceMM)); // printMessage("Tilt extends slice by %d pixels", pxOffset); hdr->dim[2] = hdr->dim[2] + pxOffset; int nVox2D = hdr->dim[1]*hdr->dim[2]; unsigned char * imOut = (unsigned char *)malloc(nVox2D * hdrIn.dim[3] * 2);// *2 as 16-bits per voxel, sizeof( short) ); short * imOut16 = ( short*) imOut; //set surrounding voxels to darkest observed value int minVoxVal = imIn16[0]; for (int v = 0; v < (nVox2DIn * hdrIn.dim[3]); v++) if (imIn16[v] < minVoxVal) minVoxVal = imIn16[v]; for (int v = 0; v < (nVox2D * hdrIn.dim[3]); v++) imOut16[v] = minVoxVal; //copy skewed voxels for (int s = 0; s < hdrIn.dim[3]; s++) { //for each slice float sliceMM = s * hdrIn.pixdim[3]; if (sliceMMarray != NULL) sliceMM = sliceMMarray[s]; //variable slice thicknesses //sliceMM -= mmMidZ; //adjust so tilt relative to middle slice if (GNTtanPx < 0) sliceMM -= maxSliceMM; float Offset = GNTtanPx*sliceMM; float fracHi = ceil(Offset) - Offset; //ceil not floor since rI=r-Offset not rI=r+Offset float fracLo = 1.0f - fracHi; for (int r = 0; r < hdr->dim[2]; r++) { //for each row of output float rI = (float)r - Offset; //input row if ((rI >= 0.0) && (rI < hdrIn.dim[2])) { int rLo = floor(rI); int rHi = rLo + 1; if (rHi >= hdrIn.dim[2]) rHi = rLo; rLo = (rLo * hdrIn.dim[1]) + (s * nVox2DIn); //offset to start of row below rHi = (rHi * hdrIn.dim[1]) + (s * nVox2DIn); //offset to start of row above int rOut = (r * hdrIn.dim[1]) + (s * nVox2D); //offset to output row for (int c = 0; c < hdrIn.dim[1]; c++) { //for each row imOut16[rOut+c] = round( ( ((float)imIn16[rLo+c])*fracLo) + ((float)imIn16[rHi+c])*fracHi); } //for c (each column) } //rI (input row) in range } //for r (each row) } //for s (each slice)*/ free(im); if (sliceMMarray != NULL) return imOut; //we will save after correcting for variable slice thicknesses char niiFilenameTilt[2048] = {""}; strcat(niiFilenameTilt,niiFilename); strcat(niiFilenameTilt,"_Tilt"); nii_saveNII3D(niiFilenameTilt, *hdr, imOut, opts); return imOut; }// nii_saveNII3Dtilt() int nii_saveNII3Deq(char * niiFilename, struct nifti_1_header hdr, unsigned char* im, struct TDCMopts opts, float * sliceMMarray ) { //convert image with unequal slice distances to equal slice distances //sliceMMarray = 0.0 3.0 6.0 12.0 22.0 <- ascending distance from first slice if (opts.isOnlyBIDS) return EXIT_SUCCESS; int nVox2D = hdr.dim[1]*hdr.dim[2]; if ((nVox2D < 1) || (hdr.dim[0] != 3) ) return EXIT_FAILURE; if ((hdr.datatype != DT_UINT8) && (hdr.datatype != DT_RGB24) && (hdr.datatype != DT_INT16)) { printMessage("Only able to make equidistant slices from 3D 8,16,24-bit volumes with at least 3 slices."); return EXIT_FAILURE; } float mn = sliceMMarray[1] - sliceMMarray[0]; for (int i = 1; i < hdr.dim[3]; i++) { float dx = sliceMMarray[i] - sliceMMarray[i-1]; //if ((dx < mn) // <- only allow consistent slice direction if ((dx < mn) && (dx > 0.0)) // <- allow slice direction to reverse mn = sliceMMarray[i] - sliceMMarray[i-1]; } if (mn <= 0.0f) { printMessage("Unable to equalize slice distances: slice number not consistent with slice position.\n"); return EXIT_FAILURE; } int slices = hdr.dim[3]; slices = (int)ceil((sliceMMarray[slices-1]-0.5*(sliceMMarray[slices-1]-sliceMMarray[slices-2]))/mn); //-0.5: fence post if (slices > (hdr.dim[3] * 2)) { slices = 2 * hdr.dim[3]; mn = (sliceMMarray[hdr.dim[3]-1]) / (slices-1); } //printMessage("-->%g mn slices %d orig %d\n", mn, slices, hdr.dim[3]); if (slices < 3) return EXIT_FAILURE; struct nifti_1_header hdrX = hdr; hdrX.dim[3] = slices; hdrX.pixdim[3] = mn; if ((hdr.pixdim[3] != 0.0) && (hdr.pixdim[3] != hdrX.pixdim[3])) { float Scale = hdrX.pixdim[3] / hdr.pixdim[3]; //to do: do I change srow_z or srow_x[2], srow_y[2], srow_z[2], hdrX.srow_z[0] = hdr.srow_z[0] * Scale; hdrX.srow_z[1] = hdr.srow_z[1] * Scale; hdrX.srow_z[2] = hdr.srow_z[2] * Scale; } unsigned char *imX; if (hdr.datatype == DT_INT16) { short * im16 = ( short*) im; imX = (unsigned char *)malloc( (nVox2D * slices) * 2);//sizeof( short) ); short * imX16 = ( short*) imX; for (int s=0; s < slices; s++) { float sliceXmm = s * mn; //distance from first slice int sliceXi = (s * nVox2D);//offset for this slice int sHi = 0; while ((sHi < (hdr.dim[3] - 1) ) && (sliceMMarray[sHi] < sliceXmm)) sHi += 1; int sLo = sHi - 1; if (sLo < 0) sLo = 0; float mmHi = sliceMMarray[sHi]; float mmLo = sliceMMarray[sLo]; sLo = sLo * nVox2D; sHi = sHi * nVox2D; if ((mmHi == mmLo) || (sliceXmm > mmHi)) { //select only from upper slice TPX //for (int v=0; v < nVox2D; v++) // imX16[sliceXi+v] = im16[sHi+v]; memcpy(&imX16[sliceXi], &im16[sHi], nVox2D* sizeof(unsigned short)); //memcpy( dest, src, bytes) } else { float fracHi = (sliceXmm-mmLo)/ (mmHi-mmLo); float fracLo = 1.0 - fracHi; //weight between two slices for (int v=0; v < nVox2D; v++) imX16[sliceXi+v] = round( ( (float)im16[sLo+v]*fracLo) + (float)im16[sHi+v]*fracHi); } } } else { if (hdr.datatype == DT_RGB24) nVox2D = nVox2D * 3; imX = (unsigned char *)malloc( (nVox2D * slices) * 2);//sizeof( short) ); for (int s=0; s < slices; s++) { float sliceXmm = s * mn; //distance from first slice int sliceXi = (s * nVox2D);//offset for this slice int sHi = 0; while ((sHi < (hdr.dim[3] - 1) ) && (sliceMMarray[sHi] < sliceXmm)) sHi += 1; int sLo = sHi - 1; if (sLo < 0) sLo = 0; float mmHi = sliceMMarray[sHi]; float mmLo = sliceMMarray[sLo]; sLo = sLo * nVox2D; sHi = sHi * nVox2D; if ((mmHi == mmLo) || (sliceXmm > mmHi)) { //select only from upper slice TPX memcpy(&imX[sliceXi], &im[sHi], nVox2D); //memcpy( dest, src, bytes) } else { float fracHi = (sliceXmm-mmLo)/ (mmHi-mmLo); float fracLo = 1.0 - fracHi; //weight between two slices for (int v=0; v < nVox2D; v++) imX[sliceXi+v] = round( ( (float)im[sLo+v]*fracLo) + (float)im[sHi+v]*fracHi); } } } char niiFilenameEq[2048] = {""}; strcat(niiFilenameEq,niiFilename); strcat(niiFilenameEq,"_Eq"); nii_saveNII3D(niiFilenameEq, hdrX, imX, opts); free(imX); return EXIT_SUCCESS; }// nii_saveNII3Deq() float PhilipsPreciseVal (float lPV, float lRS, float lRI, float lSS) { if ((lRS*lSS) == 0) //avoid divide by zero return 0.0; else return (lPV * lRS + lRI) / (lRS * lSS); } void PhilipsPrecise (struct TDICOMdata * d, bool isPhilipsFloatNotDisplayScaling, struct nifti_1_header *h) { if ((d->intenScalePhilips == 0) || (d->manufacturer != kMANUFACTURER_PHILIPS)) return; //not Philips //we will report calibrated "FP" values http://www.ncbi.nlm.nih.gov/pmc/articles/PMC3998685/ float l0 = PhilipsPreciseVal (0, d->intenScale, d->intenIntercept, d->intenScalePhilips); float l1 = PhilipsPreciseVal (1, d->intenScale, d->intenIntercept, d->intenScalePhilips); float intenScaleP = d->intenScale; float intenInterceptP = d->intenIntercept; if (l0 != l1) { intenInterceptP = l0; intenScaleP = l1-l0; } if (isSameFloat(d->intenIntercept,intenInterceptP) && isSameFloat(d->intenScale, intenScaleP)) return; //same result for both methods: nothing to do or report! printMessage("Philips Precise RS:RI:SS = %g:%g:%g (see PMC3998685)\n",d->intenScale,d->intenIntercept,d->intenScalePhilips); printMessage(" R = raw value, P = precise value, D = displayed value\n"); printMessage(" RS = rescale slope, RI = rescale intercept, SS = scale slope\n"); printMessage(" D = R * RS + RI , P = D/(RS * SS)\n"); printMessage(" D scl_slope:scl_inter = %g:%g\n", d->intenScale,d->intenIntercept); printMessage(" P scl_slope:scl_inter = %g:%g\n", intenScaleP,intenInterceptP); //#define myUsePhilipsPrecise if (isPhilipsFloatNotDisplayScaling) { printMessage(" Using P values ('-p n ' for D values)\n"); //to change DICOM: //d->intenScale = intenScaleP; //d->intenIntercept = intenInterceptP; //to change NIfTI h->scl_slope = intenScaleP; h->scl_inter = intenInterceptP; d->intenScalePhilips = 0; //so we never run this TWICE! } else printMessage(" Using D values ('-p y ' for P values)\n"); } //PhilipsPrecise() void smooth1D(int num, double * im) { if (num < 3) return; double * src = (double *) malloc(sizeof(double)*num); memcpy(&src[0], &im[0], num * sizeof(double)); //memcpy( dest, src, bytes) double frac = 0.25; for (int i = 1; i < (num-1); i++) im[i] = (src[i-1]*frac) + (src[i]*frac*2) + (src[i+1]*frac); free(src); }// smooth1D() int nii_saveCrop(char * niiFilename, struct nifti_1_header hdr, unsigned char* im, struct TDCMopts opts) { //remove excess neck slices - assumes output of nii_setOrtho() if (opts.isOnlyBIDS) return EXIT_SUCCESS; int nVox2D = hdr.dim[1]*hdr.dim[2]; if ((nVox2D < 1) || (fabs(hdr.pixdim[3]) < 0.001) || (hdr.dim[0] != 3) || (hdr.dim[3] < 128)) return EXIT_FAILURE; if ((hdr.datatype != DT_INT16) && (hdr.datatype != DT_UINT16)) { printMessage("Only able to crop 16-bit volumes."); return EXIT_FAILURE; } short * im16 = ( short*) im; unsigned short * imu16 = (unsigned short*) im; float kThresh = 0.09; //more than 9% of max brightness /*#ifdef myEnableOtsu // This code removes noise "haze" yielding better volume rendering and smaller gz compressed files // However, it may disrupt "differennce of gaussian" segmentation estimates // Therefore feature was removed from dcm2niix, which aims for lossless conversion kThresh = 0.0001; if (hdr.datatype == DT_UINT16) maskBackgroundU16 (imu16, hdr.dim[1],hdr.dim[2],hdr.dim[3], 5,2, true); else maskBackground16 (im16, hdr.dim[1],hdr.dim[2],hdr.dim[3], 5,2, true); #endif*/ int ventralCrop = 0; //find max value for each slice int slices = hdr.dim[3]; double * sliceSums = (double *) malloc(sizeof(double)*slices); double maxSliceVal = 0.0; for (int i = (slices-1); i >= 0; i--) { sliceSums[i] = 0; int sliceStart = i * nVox2D; if (hdr.datatype == DT_UINT16) for (int j = 0; j < nVox2D; j++) sliceSums[i] += imu16[j+sliceStart]; else for (int j = 0; j < nVox2D; j++) sliceSums[i] += im16[j+sliceStart]; if (sliceSums[i] > maxSliceVal) maxSliceVal = sliceSums[i]; } if (maxSliceVal <= 0) { free(sliceSums); return EXIT_FAILURE; } smooth1D(slices, sliceSums); for (int i = 0; i < slices; i++) sliceSums[i] = sliceSums[i] / maxSliceVal; //so brightest slice has value 1 //dorsal crop: eliminate slices with more than 5% brightness int dorsalCrop; for (dorsalCrop = (slices-1); dorsalCrop >= 1; dorsalCrop--) if (sliceSums[dorsalCrop-1] > kThresh) break; if (dorsalCrop <= 1) { free(sliceSums); return EXIT_FAILURE; } /* //find brightest band within 90mm of top of head int ventralMaxSlice = dorsalCrop - round(90 /fabs(hdr.pixdim[3])); //brightest stripe within 90mm of apex if (ventralMaxSlice < 0) ventralMaxSlice = 0; int maxSlice = dorsalCrop; for (int i = ventralMaxSlice; i < dorsalCrop; i++) if (sliceSums[i] > sliceSums[maxSlice]) maxSlice = i; //now find ventralMaxSlice = maxSlice - round(45 /fabs(hdr.pixdim[3])); //gap at least 60mm if (ventralMaxSlice < 0) { free(sliceSums); return EXIT_FAILURE; } int ventralMinSlice = maxSlice - round(90/fabs(hdr.pixdim[3])); //gap no more than 120mm if (ventralMinSlice < 0) ventralMinSlice = 0; for (int i = (ventralMaxSlice-1); i >= ventralMinSlice; i--) if (sliceSums[i] > sliceSums[ventralMaxSlice]) ventralMaxSlice = i; //finally: find minima between these two points... int minSlice = ventralMaxSlice; for (int i = ventralMaxSlice; i < maxSlice; i++) if (sliceSums[i] < sliceSums[minSlice]) minSlice = i; //printMessage("%d %d %d\n", ventralMaxSlice, minSlice, maxSlice); int gap = round((maxSlice-minSlice)*0.8);//add 40% for cerebellum if ((minSlice-gap) > 1) ventralCrop = minSlice-gap; free(sliceSums); if (ventralCrop > dorsalCrop) return EXIT_FAILURE; //FindDVCrop2 const double kMaxDVmm = 180.0; double sliceMM = hdr.pixdim[3] * (dorsalCrop-ventralCrop); if (sliceMM > kMaxDVmm) { //decide how many more ventral slices to remove sliceMM = sliceMM - kMaxDVmm; sliceMM = sliceMM / hdr.pixdim[3]; ventralCrop = ventralCrop + round(sliceMM); }*/ const double kMaxDVmm = 169.0; ventralCrop = dorsalCrop - round( kMaxDVmm / hdr.pixdim[3]); if (ventralCrop < 0) ventralCrop = 0; //apply crop printMessage(" Cropping from slice %d to %d (of %d)\n", ventralCrop, dorsalCrop, slices); struct nifti_1_header hdrX = hdr; slices = dorsalCrop - ventralCrop + 1; hdrX.dim[3] = slices; //translate origin to account for missing slices hdrX.srow_x[3] += hdr.srow_x[2]*ventralCrop; hdrX.srow_y[3] += hdr.srow_y[2]*ventralCrop; hdrX.srow_z[3] += hdr.srow_z[2]*ventralCrop; //convert data unsigned char *imX; imX = (unsigned char *)malloc( (nVox2D * slices) * 2);//sizeof( short) ); short * imX16 = ( short*) imX; for (int s=0; s < slices; s++) { int sIn = s+ventralCrop; int sOut = s; sOut = sOut * nVox2D; sIn = sIn * nVox2D; memcpy(&imX16[sOut], &im16[sIn], nVox2D* sizeof(unsigned short)); //memcpy( dest, src, bytes) } char niiFilenameCrop[2048] = {""}; strcat(niiFilenameCrop,niiFilename); strcat(niiFilenameCrop,"_Crop"); const int returnCode = nii_saveNII3D(niiFilenameCrop, hdrX, imX, opts); free(imX); return returnCode; }// nii_saveCrop() void checkSliceTiming(struct TDICOMdata * d, struct TDICOMdata * d1) { //detect images with slice timing errors. https://github.com/rordenlab/dcm2niix/issues/126 if ((d->TR < 0.0) || (d->CSA.sliceTiming[0] < 0.0)) return; //no slice timing float minT = d->CSA.sliceTiming[0]; float maxT = minT; for (int i = 0; i < kMaxEPI3D; i++) { if (d->CSA.sliceTiming[i] < 0.0) break; if (d->CSA.sliceTiming[i] < minT) minT = d->CSA.sliceTiming[i]; if (d->CSA.sliceTiming[i] > maxT) maxT = d->CSA.sliceTiming[i]; } if ((minT != maxT) && (maxT <= d->TR)) return; //looks fine if ((minT == maxT) && (d->is3DAcq)) return; //fine: 3D EPI if ((minT == maxT) && (d->CSA.multiBandFactor == d->CSA.mosaicSlices)) return; //fine: all slices single excitation if ((strlen(d->seriesDescription) > 0) && (strstr(d->seriesDescription, "SBRef") != NULL)) return; //fine: single-band calibration data, the slice timing WILL exceed the TR //check if 2nd image has valud slice timing float minT1 = d1->CSA.sliceTiming[0]; float maxT1 = minT1; for (int i = 0; i < kMaxEPI3D; i++) { if (d1->CSA.sliceTiming[i] < 0.0) break; if (d1->CSA.sliceTiming[i] < minT1) minT1 = d1->CSA.sliceTiming[i]; if (d1->CSA.sliceTiming[i] > maxT1) maxT1 = d1->CSA.sliceTiming[i]; } if ((minT1 == maxT1) || (maxT1 >= d->TR)) { //both first and second image corrupted printWarning("CSA slice timing appears corrupted (range %g..%g, TR=%gms)\n", minT, maxT, d->TR); return; } //1st image corrupted, but 2nd looks ok - substitute values from 2nd image for (int i = 0; i < kMaxEPI3D; i++) { d->CSA.sliceTiming[i] = d1->CSA.sliceTiming[i]; if (d1->CSA.sliceTiming[i] < 0.0) break; } d->CSA.multiBandFactor = d1->CSA.multiBandFactor; printMessage("CSA slice timing based on 2nd volume, 1st volume corrupted (CMRR bug, range %g..%g, TR=%gms)\n", minT, maxT, d->TR); }//checkSliceTiming int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[], struct TSearchList *nameList, struct TDCMopts opts, struct TDTI4D *dti4D) { bool iVaries = intensityScaleVaries(nConvert,dcmSort,dcmList); float *sliceMMarray = NULL; //only used if slices are not equidistant uint64_t indx = dcmSort[0].indx; uint64_t indx0 = dcmSort[0].indx; uint64_t indx1 = indx0; if (nConvert > 1) indx1 = dcmSort[1].indx; if (opts.isIgnoreDerivedAnd2D && dcmList[indx].isDerived) { printMessage("Ignoring derived image(s) of series %ld %s\n", dcmList[indx].seriesNum, nameList->str[indx]); return EXIT_SUCCESS; } if ((opts.isIgnoreDerivedAnd2D) && ((strcmp(dcmList[indx].sequenceName, "_fl3d1_ns")== 0) || (strcmp(dcmList[indx].sequenceName, "_fl2d1")== 0)) ) { printMessage("Ignoring localizer (sequence %s) of series %ld %s\n", dcmList[indx].sequenceName, dcmList[indx].seriesNum, nameList->str[indx]); return EXIT_SUCCESS; } if ((opts.isIgnoreDerivedAnd2D) && (nConvert < 2) && (dcmList[indx].xyzDim[3] < 2)) { printMessage("Ignoring 2D image of series %ld %s\n", dcmList[indx].seriesNum, nameList->str[indx]); return EXIT_SUCCESS; } bool saveAs3D = dcmList[indx].isHasPhase; struct nifti_1_header hdr0; unsigned char * img = nii_loadImgXL(nameList->str[indx], &hdr0,dcmList[indx], iVaries, opts.compressFlag, opts.isVerbose); if (strlen(opts.imageComments) > 0) { for (int i = 0; i < 24; i++) hdr0.aux_file[i] = 0; //remove dcm.imageComments snprintf(hdr0.aux_file,24,"%s",opts.imageComments); } if (opts.isVerbose) printMessage("Converting %s\n",nameList->str[indx]); if (img == NULL) return EXIT_FAILURE; //if (iVaries) img = nii_iVaries(img, &hdr0); size_t imgsz = nii_ImgBytes(hdr0); unsigned char *imgM = (unsigned char *)malloc(imgsz* (uint64_t)nConvert); memcpy(&imgM[0], &img[0], imgsz); free(img); //printMessage(" %d %d %d %d %lu\n", hdr0.dim[1], hdr0.dim[2], hdr0.dim[3], hdr0.dim[4], (unsigned long)[imgM length]); if (nConvert > 1) { //next: determine gantry tilt if (dcmList[indx0].gantryTilt != 0.0f) printMessage(" Warning: note these images have gantry tilt of %g degrees (manufacturer ID = %d)\n", dcmList[indx0].gantryTilt, dcmList[indx0].manufacturer); if (hdr0.dim[3] < 2) { //stack volumes with multiple acquisitions int nAcq = 1; //Next line works in theory, but fails with Siemens CT that saves pairs of slices as acquisitions, see example "testSiemensStackAcq" // nAcq = 1+abs( dcmList[dcmSort[nConvert-1].indx].acquNum-dcmList[indx0].acquNum); //therefore, the 'same position' is the most robust solution in the real world. if ((dcmList[indx0].manufacturer == kMANUFACTURER_SIEMENS) && (isSameFloat(dcmList[indx0].TR ,0.0f))) { nConvert = siemensCtKludge(nConvert, dcmSort,dcmList); } if ((nAcq == 1 ) && (dcmList[indx0].locationsInAcquisition > 0)) nAcq = nConvert/dcmList[indx0].locationsInAcquisition; if (nAcq < 2 ) { nAcq = 0; for (int i = 0; i < nConvert; i++) if (isSamePosition(dcmList[dcmSort[0].indx],dcmList[dcmSort[i].indx])) nAcq++; } /*int nImg = 1+abs( dcmList[dcmSort[nConvert-1].indx].imageNum-dcmList[dcmSort[0].indx].imageNum); if (((nConvert/nAcq) > 1) && ((nConvert%nAcq)==0) && (nImg == nConvert) && (dcmList[dcmSort[0].indx].locationsInAcquisition == 0) ) { printMessage(" stacking %d acquisitions as a single volume\n", nAcq); //some Siemens CT scans use multiple acquisitions for a single volume, perhaps also check that slice position does not repeat? hdr0.dim[3] = nConvert; } else*/ if ( (nAcq > 1) && ((nConvert/nAcq) > 1) && ((nConvert%nAcq)==0) ) { hdr0.dim[3] = nConvert/nAcq; hdr0.dim[4] = nAcq; hdr0.dim[0] = 4; } else { hdr0.dim[3] = nConvert; if (nAcq > 1) { printMessage("Slice positions repeated, but number of slices (%d) not divisible by number of repeats (%d): missing images?\n", nConvert, nAcq); } } float dx = intersliceDistance(dcmList[dcmSort[0].indx],dcmList[dcmSort[1].indx]); bool dxVaries = false; for (int i = 1; i < nConvert; i++) if (!isSameFloatT(dx,intersliceDistance(dcmList[dcmSort[i-1].indx],dcmList[dcmSort[i].indx]),0.2)) dxVaries = true; if (hdr0.dim[4] < 2) { if (dxVaries) { sliceMMarray = (float *) malloc(sizeof(float)*nConvert); sliceMMarray[0] = 0.0f; printMessage("Dims %d %d %d %d %d\n", hdr0.dim[1], hdr0.dim[2], hdr0.dim[3], hdr0.dim[4], nAcq); printWarning("Interslice distance varies in this volume (incompatible with NIfTI format).\n"); printMessage(" Distance from first slice:\n"); printMessage("dx=[0"); for (int i = 1; i < nConvert; i++) { float dx0 = intersliceDistance(dcmList[dcmSort[0].indx],dcmList[dcmSort[i].indx]); printMessage(" %g", dx0); sliceMMarray[i] = dx0; } printMessage("]\n"); } } if ((hdr0.dim[4] > 0) && (dxVaries) && (dx == 0.0) && ((dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_GE) || (dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_PHILIPS)) ) { //Niels Janssen has provided GE sequential multi-phase acquisitions that also require swizzling swapDim3Dim4(hdr0.dim[3],hdr0.dim[4],dcmSort); dx = intersliceDistance(dcmList[dcmSort[0].indx],dcmList[dcmSort[1].indx]); printMessage("swizzling 3rd and 4th dimensions (XYTZ -> XYZT), assuming interslice distance is %f\n",dx); } if ((dx == 0.0 ) && (!dxVaries)) { //all images are the same slice - 16 Dec 2014 printMessage(" Warning: all images appear to be a single slice - please check slice/vector orientation\n"); hdr0.dim[3] = 1; hdr0.dim[4] = nConvert; hdr0.dim[0] = 4; } dcmList[dcmSort[0].indx].xyzMM[3] = dx; //16Sept2014 : correct DICOM for true distance between slice centers: // e.g. MCBI Siemens ToF 0018:0088 reports 16mm SpacingBetweenSlices, but actually 0.5mm if (dx > 0) hdr0.pixdim[3] = dx; } else if (hdr0.dim[4] < 2) { hdr0.dim[4] = nConvert; hdr0.dim[0] = 4; } else { hdr0.dim[5] = nConvert; hdr0.dim[0] = 5; } /*if (nConvert > 1) { //next determine if TR is true time between volumes double startTime = dcmList[indx0].acquisitionTime; double endTime = startTime; for (int i = 1; i < nConvert; i++) { double sliceTime = dcmList[dcmSort[i].indx].acquisitionTime; if (sliceTime < startTime) startTime = sliceTime; if (sliceTime > endTime) endTime = sliceTime; } double seriesTime = (endTime - startTime); if (endTime > 0) printMessage("%g - %g = %g\n", endTime, startTime, seriesTime); }*/ //printMessage(" %d %d %d %d %lu\n", hdr0.dim[1], hdr0.dim[2], hdr0.dim[3], hdr0.dim[4], (unsigned long)[imgM length]); struct nifti_1_header hdrI; for (int i = 1; i < nConvert; i++) { //stack additional images indx = dcmSort[i].indx; //if (headerDcm2Nii(dcmList[indx], &hdrI) == EXIT_FAILURE) return EXIT_FAILURE; img = nii_loadImgXL(nameList->str[indx], &hdrI, dcmList[indx],iVaries, opts.compressFlag, opts.isVerbose); if (img == NULL) return EXIT_FAILURE; if ((hdr0.dim[1] != hdrI.dim[1]) || (hdr0.dim[2] != hdrI.dim[2]) || (hdr0.bitpix != hdrI.bitpix)) { printError("Image dimensions differ %s %s",nameList->str[dcmSort[0].indx], nameList->str[indx]); free(imgM); free(img); return EXIT_FAILURE; } memcpy(&imgM[(uint64_t)i*imgsz], &img[0], imgsz); free(img); } } char pathoutname[2048] = {""}; if (nii_createFilename(dcmList[dcmSort[0].indx], pathoutname, opts) == EXIT_FAILURE) { free(imgM); return EXIT_FAILURE; } if (strlen(pathoutname) <1) { free(imgM); return EXIT_FAILURE; } // Prevent these DICOM files from being reused. for(int i = 0; i < nConvert; ++i) dcmList[dcmSort[i].indx].converted2NII = 1; if (opts.numSeries < 0) { //report series number but do not convert printMessage("\t%d\t%s\n", dcmList[dcmSort[0].indx].seriesNum, pathoutname); printMessage(" %s\n",nameList->str[dcmSort[0].indx]); return EXIT_SUCCESS; } checkSliceTiming(&dcmList[indx0], &dcmList[indx1]); int sliceDir = 0; if (hdr0.dim[3] > 1)sliceDir = headerDcm2Nii2(dcmList[dcmSort[0].indx],dcmList[dcmSort[nConvert-1].indx] , &hdr0, true); //UNCOMMENT NEXT TWO LINES TO RE-ORDER MOSAIC WHERE CSA's protocolSliceNumber does not start with 1 if (dcmList[dcmSort[0].indx].CSA.protocolSliceNumber1 > 1) { printWarning("Weird CSA 'ProtocolSliceNumber' (System/Miscellaneous/ImageNumbering reversed): VALIDATE SLICETIMING AND BVECS\n"); //https://www.healthcare.siemens.com/siemens_hwem-hwem_ssxa_websites-context-root/wcm/idc/groups/public/@global/@imaging/@mri/documents/download/mdaz/nzmy/~edisp/mri_60_graessner-01646277.pdf //see https://github.com/neurolabusc/dcm2niix/issues/40 sliceDir = -1; //not sure how to handle negative determinants? } if (sliceDir < 0) { imgM = nii_flipZ(imgM, &hdr0); sliceDir = abs(sliceDir); //change this, we have flipped the image so GE DTI bvecs no longer need to be flipped! } // skip converting if user has specified one or more series, but has not specified this one if (opts.numSeries > 0) { int i = 0; for (; i < opts.numSeries; i++) { if (opts.seriesNumber[i] == dcmList[dcmSort[0].indx].seriesNum) { break; } } if (i == opts.numSeries) { return EXIT_SUCCESS; } } //move before headerDcm2Nii2 checkSliceTiming(&dcmList[indx0], &dcmList[indx1]); //nii_SaveBIDS(pathoutname, dcmList[dcmSort[0].indx], opts, dti4D, &hdr0, nameList->str[dcmSort[0].indx]); nii_SaveBIDS(pathoutname, dcmList[dcmSort[0].indx], opts, &hdr0, nameList->str[dcmSort[0].indx]); if (opts.isOnlyBIDS) { //note we waste time loading every image, however this ensures hdr0 matches actual output free(imgM); return EXIT_SUCCESS; } nii_SaveText(pathoutname, dcmList[dcmSort[0].indx], opts, &hdr0, nameList->str[indx]); int numADC = 0; int * volOrderIndex = nii_SaveDTI(pathoutname,nConvert, dcmSort, dcmList, opts, sliceDir, dti4D, &numADC); if ((hdr0.datatype == DT_UINT16) && (!dcmList[dcmSort[0].indx].isSigned)) nii_check16bitUnsigned(imgM, &hdr0); printMessage( "Convert %d DICOM as %s (%dx%dx%dx%d)\n", nConvert, pathoutname, hdr0.dim[1],hdr0.dim[2],hdr0.dim[3],hdr0.dim[4]); PhilipsPrecise(&dcmList[dcmSort[0].indx], opts.isPhilipsFloatNotDisplayScaling, &hdr0); if (!dcmList[dcmSort[0].indx].isSlicesSpatiallySequentialPhilips) nii_reorderSlices(imgM, &hdr0, dti4D); if (hdr0.dim[3] < 2) printWarning("Check that 2D images are not mirrored.\n"); #ifndef HAVE_R else fflush(stdout); //GUI buffers printf, display all results #endif if ((dcmList[dcmSort[0].indx].is3DAcq) && (hdr0.dim[3] > 1) && (hdr0.dim[0] < 4)) imgM = nii_setOrtho(imgM, &hdr0); //printMessage("ortho %d\n", echoInt (33)); else if (opts.isFlipY)//(FLIP_Y) //(dcmList[indx0].CSA.mosaicSlices < 2) && imgM = nii_flipY(imgM, &hdr0); else printMessage("DICOM row order preserved: may appear upside down in tools that ignore spatial transforms\n"); #ifndef myNoSave // Indicates success or failure of the (last) save int returnCode = EXIT_FAILURE; //printMessage(" x--> %d ----\n", nConvert); if (! opts.isRGBplanar) //save RGB as packed RGBRGBRGB... instead of planar RRR..RGGG..GBBB..B imgM = nii_planar2rgb(imgM, &hdr0, true); //NIfTI is packed while Analyze was planar if ((hdr0.dim[4] > 1) && (saveAs3D)) returnCode = nii_saveNII3D(pathoutname, hdr0, imgM,opts); else { if (volOrderIndex) //reorder volumes imgM = reorderVolumes(&hdr0, imgM, volOrderIndex); #ifndef HAVE_R if (numADC > 0) {//ADC maps can disrupt analysis: save a copy with the ADC map, and another without char pathoutnameADC[2048] = {""}; strcat(pathoutnameADC,pathoutname); strcat(pathoutnameADC,"_ADC"); if (opts.isSave3D) nii_saveNII3D(pathoutnameADC, hdr0, imgM, opts); else nii_saveNII(pathoutnameADC, hdr0, imgM, opts); } #endif imgM = removeADC(&hdr0, imgM, numADC); #ifndef HAVE_R if (opts.isSave3D) returnCode = nii_saveNII3D(pathoutname, hdr0, imgM, opts); else returnCode = nii_saveNII(pathoutname, hdr0, imgM, opts); #endif } #endif if (dcmList[indx0].gantryTilt != 0.0) { if (dcmList[indx0].isResampled) { printMessage("Tilt correction skipped: 0008,2111 reports RESAMPLED\n"); } else if (opts.isTiltCorrect) { imgM = nii_saveNII3Dtilt(pathoutname, &hdr0, imgM,opts, sliceMMarray, dcmList[indx0].gantryTilt, dcmList[indx0].manufacturer); strcat(pathoutname,"_Tilt"); } else printMessage("Tilt correction skipped\n"); } if (sliceMMarray != NULL) { if (dcmList[indx0].isResampled) { printMessage("Slice thickness correction skipped: 0008,2111 reports RESAMPLED\n"); } else returnCode = nii_saveNII3Deq(pathoutname, hdr0, imgM,opts, sliceMMarray); free(sliceMMarray); } if ((opts.isCrop) && (dcmList[indx0].is3DAcq) && (hdr0.dim[3] > 1) && (hdr0.dim[0] < 4))//for T1 scan: && (dcmList[indx0].TE < 25) returnCode = nii_saveCrop(pathoutname, hdr0, imgM,opts); //n.b. must be run AFTER nii_setOrtho()! #ifdef HAVE_R // Note that for R, only one image should be created per series // Hence the logical OR here if (returnCode == EXIT_SUCCESS || nii_saveNII(pathoutname,hdr0,imgM,opts) == EXIT_SUCCESS) nii_saveAttributes(dcmList[dcmSort[0].indx], hdr0, opts); #endif free(imgM); return returnCode;//EXIT_SUCCESS; }// saveDcm2Nii() void fillTDCMsort(struct TDCMsort& tdcmref, const uint64_t indx, const struct TDICOMdata& dcmdata){ // Copy the relevant parts of dcmdata to tdcmref. tdcmref.indx = indx; tdcmref.img = ((uint64_t)dcmdata.seriesNum << 32) + dcmdata.imageNum; for(int i = 0; i < MAX_NUMBER_OF_DIMENSIONS; ++i) tdcmref.dimensionIndexValues[i] = dcmdata.dimensionIndexValues[i]; } // fillTDCMsort() int compareTDCMsort(void const *item1, void const *item2) { //for quicksort http://blog.ablepear.com/2011/11/objective-c-tuesdays-sorting-arrays.html struct TDCMsort const *dcm1 = (const struct TDCMsort *)item1; struct TDCMsort const *dcm2 = (const struct TDCMsort *)item2; int retval = 0; // tie if (dcm1->img < dcm2->img) retval = -1; else if (dcm1->img > dcm2->img) retval = 1; if(retval == 0){ // Check the dimensionIndexValues (useful for enhanced DICOM 4D series). // ->img is basically behaving as a (seriesNum, imageNum) sort key // concatenated into a (large) integer for qsort. That is unwieldy when // dimensionIndexValues need to be compared, because the existence of // uint128_t, uint256_t, etc. is not guaranteed. This sorts by // (seriesNum, ImageNum, div[0], div[1], ...), or if you think of it as a // number, the dimensionIndexValues come after the decimal point. for(int i=0; i < MAX_NUMBER_OF_DIMENSIONS; ++i){ if(dcm1->dimensionIndexValues[i] < dcm2->dimensionIndexValues[i]){ retval = -1; break; } else if(dcm1->dimensionIndexValues[i] > dcm2->dimensionIndexValues[i]){ retval = 1; break; } } } return retval; } //compareTDCMsort() int isSameFloatGE (float a, float b) { //Kludge for bug in 0002,0016="DIGITAL_JACKET", 0008,0070="GE MEDICAL SYSTEMS" DICOM data: Orient field (0020:0037) can vary 0.00604261 == 0.00604273 !!! //return (a == b); //niave approach does not have any tolerance for rounding errors return (fabs (a - b) <= 0.0001); } int isSameFloatDouble (double a, double b) { //Kludge for bug in 0002,0016="DIGITAL_JACKET", 0008,0070="GE MEDICAL SYSTEMS" DICOM data: Orient field (0020:0037) can vary 0.00604261 == 0.00604273 !!! // return (a == b); //niave approach does not have any tolerance for rounding errors return (fabs (a - b) <= 0.0001); } struct TWarnings { //generate a warning only once per set bool acqNumVaries, bitDepthVaries, dateTimeVaries, echoVaries, coilVaries, nameVaries, nameEmpty, orientVaries; }; TWarnings setWarnings() { TWarnings r; r.acqNumVaries = false; r.bitDepthVaries = false; r.dateTimeVaries = false; r.echoVaries = false; r.coilVaries = false; r.nameVaries = false; r.nameEmpty = false; r.orientVaries = false; return r; } bool isSameSet (struct TDICOMdata d1, struct TDICOMdata d2, struct TDCMopts* opts, struct TWarnings* warnings, bool *isMultiEcho) { //returns true if d1 and d2 should be stacked together as a single output if (!d1.isValid) return false; if (!d2.isValid) return false; if (d1.modality != d2.modality) return false; //do not stack MR and CT data! if (d1.isDerived != d2.isDerived) return false; //do not stack raw and derived image types if (d1.manufacturer != d2.manufacturer) return false; //do not stack data from different vendors if (d1.seriesNum != d2.seriesNum) return false; #ifdef mySegmentByAcq if (d1.acquNum != d2.acquNum) return false; #else if (d1.acquNum != d2.acquNum) { if (!warnings->acqNumVaries) printMessage("slices stacked despite varying acquisition numbers (if this is not desired please recompile)\n"); warnings->acqNumVaries = true; } #endif if ((d1.bitsAllocated != d2.bitsAllocated) || (d1.xyzDim[1] != d2.xyzDim[1]) || (d1.xyzDim[2] != d2.xyzDim[2]) || (d1.xyzDim[3] != d2.xyzDim[3]) ) { if (!warnings->bitDepthVaries) printMessage("slices not stacked: dimensions or bit-depth varies\n"); warnings->bitDepthVaries = true; return false; } if (!isSameFloatDouble(d1.dateTime, d2.dateTime)) { //beware, some vendors incorrectly store Image Time (0008,0033) as Study Time (0008,0030). if (!warnings->dateTimeVaries) printMessage("slices not stacked: Study Date/Time (0008,0020 / 0008,0030) varies %12.12f ~= %12.12f\n", d1.dateTime, d2.dateTime); warnings->dateTimeVaries = true; return false; } if (opts->isForceStackSameSeries) { if ((d1.TE != d2.TE) || (d1.echoNum != d2.echoNum)) *isMultiEcho = true; return true; //we will stack these images, even if they differ in the following attributes } if ((d1.TE != d2.TE) || (d1.echoNum != d2.echoNum)) { if ((!warnings->echoVaries) && (d1.isXRay)) //for CT/XRay we check DICOM tag 0018,1152 (XRayExposure) printMessage("slices not stacked: X-Ray Exposure varies (exposure %g, %g; number %d, %d). Use 'merge 2D slices' option to force stacking\n", d1.TE, d2.TE,d1.echoNum, d2.echoNum ); if ((!warnings->echoVaries) && (!d1.isXRay)) //for MRI printMessage("slices not stacked: echo varies (TE %g, %g; echo %d, %d). Use 'merge 2D slices' option to force stacking\n", d1.TE, d2.TE,d1.echoNum, d2.echoNum ); warnings->echoVaries = true; *isMultiEcho = true; return false; } if (d1.coilNum != d2.coilNum) { if (!warnings->coilVaries) printMessage("slices not stacked: coil varies\n"); warnings->coilVaries = true; return false; } if ((strlen(d1.protocolName) < 1) && (strlen(d2.protocolName) < 1)) { if (!warnings->nameEmpty) printWarning("Empty protocol name(s) (0018,1030)\n"); warnings->nameEmpty = true; } else if ((strcmp(d1.protocolName, d2.protocolName) != 0)) { if (!warnings->nameVaries) printMessage("slices not stacked: protocol name varies '%s' != '%s'\n", d1.protocolName, d2.protocolName); warnings->nameVaries = true; return false; } if ((!isSameFloatGE(d1.orient[1], d2.orient[1]) || !isSameFloatGE(d1.orient[2], d2.orient[2]) || !isSameFloatGE(d1.orient[3], d2.orient[3]) || !isSameFloatGE(d1.orient[4], d2.orient[4]) || !isSameFloatGE(d1.orient[5], d2.orient[5]) || !isSameFloatGE(d1.orient[6], d2.orient[6]) ) ) { if (!warnings->orientVaries) printMessage("slices not stacked: orientation varies (localizer?) [%g %g %g %g %g %g] != [%g %g %g %g %g %g]\n", d1.orient[1], d1.orient[2], d1.orient[3],d1.orient[4], d1.orient[5], d1.orient[6], d2.orient[1], d2.orient[2], d2.orient[3],d2.orient[4], d2.orient[5], d2.orient[6]); warnings->orientVaries = true; return false; } return true; }// isSameSet() int singleDICOM(struct TDCMopts* opts, char *fname) { char filename[768] =""; strcat(filename, fname); if (isDICOMfile(filename) == 0) { printError("Not a DICOM image : %s\n", filename); return 0; } struct TDICOMdata *dcmList = (struct TDICOMdata *)malloc( sizeof(struct TDICOMdata)); struct TDTI4D dti4D; struct TSearchList nameList; nameList.maxItems = 1; // larger requires more memory, smaller more passes nameList.str = (char **) malloc((nameList.maxItems+1) * sizeof(char *)); //reserve one pointer (32 or 64 bits) per potential file nameList.numItems = 0; nameList.str[nameList.numItems] = (char *)malloc(strlen(filename)+1); strcpy(nameList.str[nameList.numItems],filename); nameList.numItems++; struct TDCMsort dcmSort[1]; dcmList[0].converted2NII = 1; dcmList[0] = readDICOMv(nameList.str[0], opts->isVerbose, opts->compressFlag, &dti4D); //ignore compile warning - memory only freed on first of 2 passes fillTDCMsort(dcmSort[0], 0, dcmList[0]); return saveDcm2Nii(1, dcmSort, dcmList, &nameList, *opts, &dti4D); }// singleDICOM() size_t fileBytes(const char * fname) { FILE *fp = fopen(fname, "rb"); if (!fp) return 0; fseek(fp, 0, SEEK_END); size_t fileLen = ftell(fp); fclose(fp); return fileLen; } //fileBytes() int strcicmp(char const *a, char const *b) //case insensitive compare { for (;; a++, b++) { int d = tolower(*a) - tolower(*b); if (d != 0 || !*a) return d; } }// strcicmp() void searchDirForDICOM(char *path, struct TSearchList *nameList, int maxDepth, int depth, struct TDCMopts* opts ) { tinydir_dir dir; tinydir_open(&dir, path); while (dir.has_next) { tinydir_file file; file.is_dir = 0; //avoids compiler warning: this is set by tinydir_readfile tinydir_readfile(&dir, &file); //printMessage("%s\n", file.name); char filename[768] =""; strcat(filename, path); strcat(filename,kFileSep); strcat(filename, file.name); if ((file.is_dir) && (depth < maxDepth) && (file.name[0] != '.')) searchDirForDICOM(filename, nameList, maxDepth, depth+1, opts); else if (!file.is_reg) //ignore files "." and ".." ; else if ((strlen(file.name) < 1) || (file.name[0]=='.')) ; //printMessage("skipping hidden file %s\n", file.name); else if ((strlen(file.name) == 8) && (strcicmp(file.name, "DICOMDIR") == 0)) ; //printMessage("skipping DICOMDIR\n"); else if (isDICOMfile(filename) > 0) { if (nameList->numItems < nameList->maxItems) { nameList->str[nameList->numItems] = (char *)malloc(strlen(filename)+1); strcpy(nameList->str[nameList->numItems],filename); //printMessage("OK\n"); } nameList->numItems++; //printMessage("dcm %lu %s \n",nameList->numItems, filename); } else { if (fileBytes(filename) > 2048) convert_foreign (filename, *opts); #ifdef MY_DEBUG printMessage("Not a dicom:\t%s\n", filename); #endif } tinydir_next(&dir); } tinydir_close(&dir); }// searchDirForDICOM() int removeDuplicates(int nConvert, struct TDCMsort dcmSort[]){ //done AFTER sorting, so duplicates will be sequential if (nConvert < 2) return nConvert; int nDuplicates = 0; for (int i = 1; i < nConvert; i++) { if (compareTDCMsort(&dcmSort[i], &dcmSort[i-1]) == 0) { nDuplicates ++; } else { dcmSort[i-nDuplicates].img = dcmSort[i].img; dcmSort[i-nDuplicates].indx = dcmSort[i].indx; for(int j = 0; j < MAX_NUMBER_OF_DIMENSIONS; ++j) dcmSort[i - nDuplicates].dimensionIndexValues[j] = dcmSort[i].dimensionIndexValues[j]; } } if (nDuplicates > 0) printMessage("Some images have identical time, series, acquisition and image values. DUPLICATES REMOVED.\n"); return nConvert - nDuplicates; }// removeDuplicates() int removeDuplicatesVerbose(int nConvert, struct TDCMsort dcmSort[], struct TSearchList *nameList){ //done AFTER sorting, so duplicates will be sequential if (nConvert < 2) return nConvert; int nDuplicates = 0; for (int i = 1; i < nConvert; i++) { if (compareTDCMsort(&dcmSort[i], &dcmSort[i-1]) == 0) { printMessage("\t%s\t=\t%s\n",nameList->str[dcmSort[i-1].indx],nameList->str[dcmSort[i].indx]); nDuplicates ++; } else { dcmSort[i-nDuplicates].img = dcmSort[i].img; dcmSort[i-nDuplicates].indx = dcmSort[i].indx; for(int j = 0; j < MAX_NUMBER_OF_DIMENSIONS; ++j) dcmSort[i - nDuplicates].dimensionIndexValues[j] = dcmSort[i].dimensionIndexValues[j]; } } if (nDuplicates > 0) printMessage("Some images have identical time, series, acquisition and image values. Duplicates removed.\n"); return nConvert - nDuplicates; }// removeDuplicatesVerbose() bool isExt (char *file_name, const char* ext) { char *p_extension; if((p_extension = strrchr(file_name,'.')) != NULL ) if(strcicmp(p_extension,ext) == 0) return true; //if(strcmp(p_extension,ext) == 0) return true; return false; }// isExt() int convert_parRec(struct TDCMopts opts) { //sample dataset from Ed Gronenschild struct TSearchList nameList; int ret = EXIT_FAILURE; nameList.numItems = 1; nameList.maxItems = 1; nameList.str = (char **) malloc((nameList.maxItems+1) * sizeof(char *)); //we reserve one pointer (32 or 64 bits) per potential file struct TDICOMdata *dcmList = (struct TDICOMdata *)malloc(nameList.numItems * sizeof(struct TDICOMdata)); nameList.str[0] = (char *)malloc(strlen(opts.indir)+1); strcpy(nameList.str[0],opts.indir); TDTI4D dti4D; dcmList[0] = nii_readParRec(nameList.str[0], opts.isVerbose, &dti4D); struct TDCMsort dcmSort[1]; dcmSort[0].indx = 0; if (dcmList[0].isValid) ret = saveDcm2Nii(1, dcmSort, dcmList, &nameList, opts, &dti4D); free(dcmList);//if (nConvertTotal == 0) if (nameList.numItems < 1) printMessage("No valid PAR/REC files were found\n"); if (nameList.numItems > 0) for (int i = 0; i < (int)nameList.numItems; i++) free(nameList.str[i]); free(nameList.str); return ret; }// convert_parRec() void freeNameList(struct TSearchList nameList) { if (nameList.numItems > 0) { unsigned long n = nameList.numItems; if (n > nameList.maxItems) n = nameList.maxItems; //assigned if (nameList->numItems < nameList->maxItems) for (unsigned long i = 0; i < n; i++) free(nameList.str[i]); } free(nameList.str); } int nii_loadDir(struct TDCMopts* opts) { //Identifies all the DICOM files in a folder and its subfolders if (strlen(opts->indir) < 1) { printMessage("No input\n"); return EXIT_FAILURE; } char indir[512]; strcpy(indir,opts->indir); bool isFile = is_fileNotDir(opts->indir); //bool isParRec = (isFile && ( (isExt(indir, ".par")) || (isExt(indir, ".rec"))) ); if (isFile) //if user passes ~/dicom/mr1.dcm we will look at all files in ~/dicom dropFilenameFromPath(opts->indir);//getParentFolder(opts.indir, opts.indir); dropTrailingFileSep(opts->indir); if (strlen(opts->outdir) < 1) { strcpy(opts->outdir,opts->indir); } else dropTrailingFileSep(opts->outdir); if (!is_dir(opts->outdir,true)) { #ifdef myUseInDirIfOutDirUnavailable printWarning("Output folder invalid %s will try %s\n",opts->outdir,opts->indir); strcpy(opts->outdir,opts->indir); #else printError("Output folder invalid: %s\n",opts->outdir); return EXIT_FAILURE; #endif } /*if (isFile && ((isExt(indir, ".gz")) || (isExt(indir, ".tgz"))) ) { #ifndef myDisableTarGz #ifndef myDisableZLib untargz( indir, opts->outdir); #endif #endif }*/ getFileName(opts->indirParent, opts->indir); if (isFile && ( (isExt(indir, ".v"))) ) return convert_foreign (indir, *opts); if (isFile && ( (isExt(indir, ".par")) || (isExt(indir, ".rec"))) ) { char pname[512], rname[512]; strcpy(pname,indir); strcpy(rname,indir); changeExt (pname, "PAR"); changeExt (rname, "REC"); #ifndef _MSC_VER //Linux is case sensitive, #include if( access( rname, F_OK ) != 0 ) changeExt (rname, "rec"); if( access( pname, F_OK ) != 0 ) changeExt (pname, "par"); #endif if (is_fileNotDir(rname) && is_fileNotDir(pname) ) { strcpy(opts->indir, pname); //set to original file name, not path return convert_parRec(*opts); }; } if ((isFile) && (opts->isOnlySingleFile)) return singleDICOM(opts, indir); struct TSearchList nameList; nameList.maxItems = 24000; // larger requires more memory, smaller more passes //1: find filenames of dicom files: up to two passes if we found more files than we allocated memory for (int i = 0; i < 2; i++ ) { nameList.str = (char **) malloc((nameList.maxItems+1) * sizeof(char *)); //reserve one pointer (32 or 64 bits) per potential file nameList.numItems = 0; searchDirForDICOM(opts->indir, &nameList, 5, 1, opts); if (nameList.numItems <= nameList.maxItems) break; freeNameList(nameList); nameList.maxItems = nameList.numItems+1; //printMessage("Second pass required, found %ld images\n", nameList.numItems); } if (nameList.numItems < 1) { printError("Unable to find any DICOM images in %s\n", opts->indir); free(nameList.str); //ignore compile warning - memory only freed on first of 2 passes return kEXIT_NO_VALID_FILES_FOUND; } size_t nDcm = nameList.numItems; printMessage( "Found %lu DICOM image(s)\n", nameList.numItems); // struct TDICOMdata dcmList [nameList.numItems]; //<- this exhausts the stack for large arrays struct TDICOMdata *dcmList = (struct TDICOMdata *)malloc(nameList.numItems * sizeof(struct TDICOMdata)); struct TDTI4D dti4D; int nConvertTotal = 0; bool compressionWarning = false; bool convertError = false; for (int i = 0; i < (int)nDcm; i++ ) { dcmList[i] = readDICOMv(nameList.str[i], opts->isVerbose, opts->compressFlag, &dti4D); //ignore compile warning - memory only freed on first of 2 passes if ((dcmList[i].isValid) &&((dcmList[i].patientPositionNumPhilips > 1) || (dcmList[i].CSA.numDti > 1))) { //4D dataset: dti4D arrays require huge amounts of RAM - write this immediately struct TDCMsort dcmSort[1]; fillTDCMsort(dcmSort[0], i, dcmList[i]); dcmList[i].converted2NII = 1; int ret = saveDcm2Nii(1, dcmSort, dcmList, &nameList, *opts, &dti4D); if (ret == EXIT_SUCCESS) nConvertTotal++; else convertError = true; } if ((dcmList[i].compressionScheme != kCompressNone) && (!compressionWarning) && (opts->compressFlag != kCompressNone)) { compressionWarning = true; //generate once per conversion rather than once per image printMessage("Image Decompression is new: please validate conversions\n"); } } #ifdef HAVE_R if (opts->isScanOnly) { TWarnings warnings = setWarnings(); // Create the first series from the first DICOM file TDicomSeries firstSeries; firstSeries.representativeData = dcmList[0]; firstSeries.files.push_back(nameList.str[0]); opts->series.push_back(firstSeries); // Iterate over the remaining files for (size_t i = 1; i < nDcm; i++) { bool matched = false; // If the file matches an existing series, add it to the corresponding file list for (int j = 0; j < opts->series.size(); j++) { bool isMultiEchoUnused; if (isSameSet(opts->series[j].representativeData, dcmList[i], opts, &warnings, &isMultiEchoUnused)) { opts->series[j].files.push_back(nameList.str[i]); matched = true; break; } } // If not, create a new series object if (!matched) { TDicomSeries nextSeries; nextSeries.representativeData = dcmList[i]; nextSeries.files.push_back(nameList.str[i]); opts->series.push_back(nextSeries); } } // To avoid a spurious warning below nConvertTotal = nDcm; } else { #endif //3: stack DICOMs with the same Series for (int i = 0; i < (int)nDcm; i++ ) { if ((dcmList[i].converted2NII == 0) && (dcmList[i].isValid)) { int nConvert = 0; struct TWarnings warnings = setWarnings(); bool isMultiEcho = false; for (int j = i; j < (int)nDcm; j++) if (isSameSet(dcmList[i], dcmList[j], opts, &warnings, &isMultiEcho ) ) nConvert++; if (nConvert < 1) nConvert = 1; //prevents compiler warning for next line: never executed since j=i always causes nConvert ++ TDCMsort * dcmSort = (TDCMsort *)malloc(nConvert * sizeof(TDCMsort)); nConvert = 0; isMultiEcho = false; for (int j = i; j < (int)nDcm; j++) if (isSameSet(dcmList[i], dcmList[j], opts, &warnings, &isMultiEcho)) { fillTDCMsort(dcmSort[nConvert], j, dcmList[j]); nConvert++; } else { dcmList[i].isMultiEcho = isMultiEcho; dcmList[j].isMultiEcho = isMultiEcho; } qsort(dcmSort, nConvert, sizeof(struct TDCMsort), compareTDCMsort); //sort based on series and image numbers.... //dcmList[dcmSort[0].indx].isMultiEcho = isMultiEcho; if (opts->isVerbose) nConvert = removeDuplicatesVerbose(nConvert, dcmSort, &nameList); else nConvert = removeDuplicates(nConvert, dcmSort); int ret = saveDcm2Nii(nConvert, dcmSort, dcmList, &nameList, *opts, &dti4D); if (ret == EXIT_SUCCESS) nConvertTotal += nConvert; else convertError = true; free(dcmSort); }//convert all images of this series } #ifdef HAVE_R } #endif free(dcmList); freeNameList(nameList); if (convertError) return EXIT_FAILURE; //at least one image failed to convert if (nConvertTotal == 0) { printMessage("No valid DICOM files were found\n"); return kEXIT_NO_VALID_FILES_FOUND; } return EXIT_SUCCESS; }// nii_loadDir() /* cleaner than findPigz - perhaps validate someday void findExe(char name[512], const char * argv[]) { if (is_exe(name)) return; //name exists as provided char basename[1024]; strcpy(basename, name); //basename = source //check executable folder strcpy(name,argv[0]); dropFilenameFromPath(name); char appendChar[2] = {"a"}; appendChar[0] = kPathSeparator; if (name[strlen(name)-1] != kPathSeparator) strcat (name,appendChar); strcat(name,basename); if (is_exe(name)) return; //name exists as provided //check /opt strcpy (name,"/opt/local/bin/" ); strcat (name, basename); if (is_exe(name)) return; //name exists as provided //check /usr strcpy (name,"/usr/local/bin/" ); strcat (name, basename); if (is_exe(name)) return; strcpy(name,""); //not found! }*/ #if defined(_WIN64) || defined(_WIN32) #else //UNIX int findpathof(char *pth, const char *exe) { //Find executable by searching the PATH environment variable. // http://www.linuxquestions.org/questions/programming-9/get-full-path-of-a-command-in-c-117965/ char *searchpath; char *beg, *end; int stop, found; size_t len; if (strchr(exe, '/') != NULL) { if (realpath(exe, pth) == NULL) return 0; return is_exe(pth); } searchpath = getenv("PATH"); if (searchpath == NULL) return 0; if (strlen(searchpath) <= 0) return 0; beg = searchpath; stop = 0; found = 0; do { end = strchr(beg, ':'); if (end == NULL) { len = strlen(beg); if (len == 0) return 0; strncpy(pth, beg, len); stop = 1; } else { strncpy(pth, beg, end - beg); pth[end - beg] = '\0'; len = end - beg; } if (pth[len - 1] != '/') strncat(pth, "/", 1); strncat(pth, exe, PATH_MAX - len); found = is_exe(pth); if (!stop) beg = end + 1; } while (!stop && !found); return found; } #endif void readFindPigz (struct TDCMopts *opts, const char * argv[]) { #if defined(_WIN64) || defined(_WIN32) strcpy(opts->pigzname,"pigz.exe"); if (!is_exe(opts->pigzname)) { #ifdef myDisableZLib printMessage("Compression requires %s in the same folder as the executable\n",opts->pigzname); #else //myUseZLib printMessage("Compression will be faster with %s in the same folder as the executable\n",opts->pigzname); #endif strcpy(opts->pigzname,""); } else strcpy(opts->pigzname,".\\pigz"); //drop #else char str[1024]; //possible pigz names const char * nams[] = { "pigz", "pigz_mricron", "pigz_afni", }; #define n_nam (sizeof (nams) / sizeof (const char *)) for (int n = 0; n < (int)n_nam; n++) { if (findpathof(str, nams[n])) { strcpy(opts->pigzname,str); //printMessage("Found pigz: %s\n", str); return; } } //possible pigz paths const char * pths[] = { "/usr/local/bin/", "/usr/bin/", }; #define n_pth (sizeof (pths) / sizeof (const char *)) char exepth[1024]; strcpy(exepth,argv[0]); dropFilenameFromPath(exepth);//, opts.pigzname); char appendChar[2] = {"a"}; appendChar[0] = kPathSeparator; if (exepth[strlen(exepth)-1] != kPathSeparator) strcat (exepth,appendChar); //see if pigz in any path for (int n = 0; n < (int)n_nam; n++) { //printf ("%d: %s\n", i, nams[n]); for (int p = 0; p < (int)n_pth; p++) { strcpy(str, pths[p]); strcat(str, nams[n]); if (is_exe(str)) goto pigzFound; } //p //check exepth strcpy(str, exepth); strcat(str, nams[n]); if (is_exe(str)) goto pigzFound; } //n //Failure: #ifdef myDisableZLib printMessage("Compression requires 'pigz' to be installed\n"); #else //myUseZLib printMessage("Compression will be faster with 'pigz' installed\n"); #endif return; pigzFound: //Success strcpy(opts->pigzname,str); //printMessage("Found pigz: %s\n", str); #endif } //readFindPigz() void setDefaultOpts (struct TDCMopts *opts, const char * argv[]) { //either "setDefaultOpts(opts,NULL)" or "setDefaultOpts(opts,argv)" where argv[0] is path to search strcpy(opts->pigzname,""); readFindPigz(opts, argv); #ifdef myEnableJasper opts->compressFlag = kCompressYes; //JASPER for JPEG2000 #else #ifdef myDisableOpenJPEG opts->compressFlag = kCompressNone; //no decompressor #else opts->compressFlag = kCompressYes; //OPENJPEG for JPEG2000 #endif #endif //printMessage("%d %s\n",opts->compressFlag, opts->compressname); strcpy(opts->indir,""); strcpy(opts->outdir,""); strcpy(opts->imageComments,""); opts->isOnlySingleFile = false; //convert all files in a directory, not just a single file opts->isForceStackSameSeries = false; opts->isIgnoreDerivedAnd2D = false; opts->isPhilipsFloatNotDisplayScaling = true; opts->isCrop = false; opts->isGz = false; opts->isSave3D = false; opts->gzLevel = MZ_DEFAULT_LEVEL; //-1; opts->isFlipY = true; //false: images in raw DICOM orientation, true: image rows flipped to cartesian coordinates opts->isRGBplanar = false; //false for NIfTI (RGBRGB...), true for Analyze (RRR..RGGG..GBBB..B) opts->isCreateBIDS = true; opts->isOnlyBIDS = false; opts->isSortDTIbyBVal = false; #ifdef myNoAnonymizeBIDS opts->isAnonymizeBIDS = false; #else opts->isAnonymizeBIDS = true; #endif opts->isCreateText = false; #ifdef myDebug opts->isVerbose = true; #else opts->isVerbose = false; #endif opts->isTiltCorrect = true; opts->numSeries = 0; memset(opts->seriesNumber, 0, sizeof(opts->seriesNumber)); strcpy(opts->filename,"%f_%p_%t_%s"); } // setDefaultOpts() #if defined(_WIN64) || defined(_WIN32) //windows has unusual file permissions for many users - lets save preferences to the registry void saveIniFile (struct TDCMopts opts) { HKEY hKey; if(RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\dcm2nii",0, KEY_SET_VALUE, &hKey) != ERROR_SUCCESS) { RegCloseKey(hKey); return; } DWORD dwValue = opts.isGz; //RegSetValueEx(hKey,"isGZ", 0, REG_DWORD,reinterpret_cast(&dwValue),sizeof(dwValue)); //RegSetValueExA(hKey, "isGZ", 0, REG_DWORD, (LPDWORD)&dwValue, sizeof(dwValue)); RegSetValueExA(hKey, "isGZ", 0, REG_DWORD, reinterpret_cast(&dwValue), sizeof(dwValue)); RegSetValueExA(hKey,"filename",0, REG_SZ,(LPBYTE)opts.filename, strlen(opts.filename)+1); RegCloseKey(hKey); } //saveIniFile() void readIniFile (struct TDCMopts *opts, const char * argv[]) { setDefaultOpts(opts, argv); HKEY hKey; DWORD vSize = 0; DWORD dwDataType = 0; DWORD dwValue = 0; //RegOpenKeyEx(RegOpenKeyEx, key, 0, accessRights, keyHandle); //if(RegOpenKeyEx(HKEY_CURRENT_USER,(WCHAR)"Software\\dcm2nii", 0, KEY_QUERY_VALUE,&hKey) != ERROR_SUCCESS) { if(RegOpenKeyExA(HKEY_CURRENT_USER,"Software\\dcm2nii", 0, KEY_QUERY_VALUE,&hKey) != ERROR_SUCCESS) { RegCloseKey(hKey); return; } vSize = sizeof(dwValue); //if(RegQueryValueExA(hKey,"isGZ", 0, (LPDWORD )&dwDataType, (&dwValue), &vSize) == ERROR_SUCCESS) if(RegQueryValueExA(hKey,"isGZ", 0, (LPDWORD )&dwDataType, reinterpret_cast(&dwValue), &vSize) == ERROR_SUCCESS) opts->isGz = dwValue; vSize = 512; char buffer[512]; if(RegQueryValueExA(hKey,"filename", 0,NULL,(LPBYTE)buffer,&vSize ) == ERROR_SUCCESS ) strcpy(opts->filename,buffer); RegCloseKey(hKey); } //readIniFile() #else //for Unix we will save preferences in a hidden text file in the home directory #define STATUSFILENAME "/.dcm2nii.ini" void readIniFile (struct TDCMopts *opts, const char * argv[]) { setDefaultOpts(opts, argv); sprintf(opts->optsname, "%s%s", getenv("HOME"), STATUSFILENAME); FILE *fp = fopen(opts->optsname, "r"); if (fp == NULL) return; char Setting[20],Value[255]; //while ( fscanf(fp, "%[^=]=%s\n", Setting, Value) == 2 ) { //while ( fscanf(fp, "%[^=]=%s\n", Setting, Value) == 2 ) { while ( fscanf(fp, "%[^=]=%[^\n]\n", Setting, Value) == 2 ) { //printMessage(">%s<->'%s'\n",Setting,Value); if ( strcmp(Setting,"isGZ") == 0 ) opts->isGz = atoi(Value); else if ( strcmp(Setting,"isBIDS") == 0 ) opts->isCreateBIDS = atoi(Value); else if ( strcmp(Setting,"filename") == 0 ) strcpy(opts->filename,Value); } fclose(fp); }// readIniFile() void saveIniFile (struct TDCMopts opts) { FILE *fp = fopen(opts.optsname, "w"); //printMessage("%s\n",localfilename); if (fp == NULL) return; fprintf(fp, "isGZ=%d\n", opts.isGz); fprintf(fp, "isBIDS=%d\n", opts.isCreateBIDS); fprintf(fp, "filename=%s\n", opts.filename); fclose(fp); } //saveIniFile() #endif dcm2niix-1.0.20171215/console/nii_dicom_batch.h000066400000000000000000000040121322051203000206570ustar00rootroot00000000000000// // #ifndef MRIpro_nii_batch_h #define MRIpro_nii_batch_h #ifdef __cplusplus extern "C" { #endif #include #include #ifndef HAVE_R #include "nifti1.h" #endif #include "nii_dicom.h" #ifdef HAVE_R struct TDicomSeries { TDICOMdata representativeData; std::vector files; }; #endif #define MAX_NUM_SERIES 16 struct TDCMopts { bool isSave3D,isGz, isFlipY, isCreateBIDS, isSortDTIbyBVal, isAnonymizeBIDS, isOnlyBIDS, isCreateText, isIgnoreDerivedAnd2D, isPhilipsFloatNotDisplayScaling, isTiltCorrect, isRGBplanar, isOnlySingleFile, isForceStackSameSeries, isCrop; int isVerbose, compressFlag, gzLevel; //support for compressed data 0=none, char filename[512], outdir[512], indir[512], pigzname[512], optsname[512], indirParent[512], imageComments[24]; long seriesNumber[MAX_NUM_SERIES], numSeries; #ifdef HAVE_R bool isScanOnly; void *imageList; std::vector series; #endif }; void saveIniFile (struct TDCMopts opts); void setDefaultOpts (struct TDCMopts *opts, const char * argv[]); //either "setDefaultOpts(opts,NULL)" or "setDefaultOpts(opts,argv)" where argv[0] is path to search void readIniFile (struct TDCMopts *opts, const char * argv[]); int nii_saveNII(char * niiFilename, struct nifti_1_header hdr, unsigned char* im, struct TDCMopts opts); //void readIniFile (struct TDCMopts *opts); int nii_loadDir (struct TDCMopts *opts); //void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts, struct TDTI4D *dti4D, struct nifti_1_header *h, const char * filename); void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts, struct nifti_1_header *h, const char * filename); int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopts opts); void nii_createDummyFilename(char * niiFilename, struct TDCMopts opts); //void findExe(char name[512], const char * argv[]); #ifdef __cplusplus } #endif #endif dcm2niix-1.0.20171215/console/nii_foreign.cpp000066400000000000000000000422371322051203000204220ustar00rootroot00000000000000#include "nii_foreign.h" #include "nii_dicom.h" #include "nifti1_io_core.h" #include "nii_dicom_batch.h" #include "nifti1.h" //#include "nifti1_io_core.h" #include #include #include #include #include #include #include "print.h" #ifdef _MSC_VER #include #define getcwd _getcwd #define chdir _chrdir #include "io.h" #include //#define snprintMessage _snprintMessage //#define vsnprintMessage _vsnprintMessage #define strcasecmp _stricmp #define strncasecmp _strnicmp #else #include #endif #ifndef Float32 #define Float32 float #endif #ifndef uint32 #define uint32 uint32_t #endif #ifdef __GNUC__ #define PACK(...) __VA_ARGS__ __attribute__((__packed__)) #else #define PACK(...) __pragma(pack(push, 1)) __VA_ARGS__ __pragma(pack(pop)) #endif /*nii_readEcat7 2017 by Chris Rorden, BSD license http://www.turkupetcentre.net/software/libdoc/libtpcimgio/ecat7_8h_source.html#l00060 http://www.turkupetcentre.net/software/libdoc/libtpcimgio/ecat7r_8c_source.html#l00717 http://xmedcon.sourcearchive.com/documentation/0.10.7-1build1/ecat7_8h_source.html https://github.com/BIC-MNI/minc-tools/tree/master/conversion/ecattominc https://github.com/nipy/nibabel/blob/ec4567fb09b4472c5a4bb9a13dbcc9eb0a63d875/nibabel/ecat.py */ void strClean(char * cString) { int len = (int)strlen(cString); if (len < 1) return; for (int i = 0; i < len; i++) { char c = cString[i]; if ( (c < char(32)) || (c == char(127)) || (c == char(255)) ) cString[i] = 0; if ( (c==' ') || (c==',') || (c=='^') || (c=='/') || (c=='\\') || (c=='%') || (c=='*')) cString[i] = '_'; } } unsigned char * readEcat7(const char *fname, struct TDICOMdata *dcm, struct nifti_1_header *hdr, struct TDCMopts opts, bool isWarnIfNotEcat) { //data type #define ECAT7_BYTE 1 #define ECAT7_VAXI2 2 #define ECAT7_VAXI4 3 #define ECAT7_VAXR4 4 #define ECAT7_IEEER4 5 #define ECAT7_SUNI2 6 #define ECAT7_SUNI4 7 //file types //#define ECAT7_UNKNOWN 0 #define ECAT7_2DSCAN 1 #define ECAT7_IMAGE16 2 #define ECAT7_ATTEN 3 #define ECAT7_2DNORM 4 #define ECAT7_POLARMAP 5 #define ECAT7_VOLUME8 6 #define ECAT7_VOLUME16 7 #define ECAT7_PROJ 8 #define ECAT7_PROJ16 9 #define ECAT7_IMAGE8 10 #define ECAT7_3DSCAN 11 #define ECAT7_3DSCAN8 12 #define ECAT7_3DNORM 13 #define ECAT7_3DSCANFIT 14 PACK( typedef struct { char magic[14],original_filename[32]; uint16_t sw_version, system_type, file_type; char serial_number[10]; uint32 scan_start_time; char isotope_name[8]; Float32 isotope_halflife; char radiopharmaceutical[32]; Float32 gantry_tilt, gantry_rotation, bed_elevation, intrinsic_tilt; int16_t wobble_speed, transm_source_type; Float32 distance_scanned, transaxial_fov; uint16_t angular_compression, coin_samp_mode, axial_samp_mode; Float32 ecat_calibration_factor; uint16_t calibration_unitS, calibration_units_type, compression_code; char study_type[12], patient_id[16], patient_name[32], patient_sex, patient_dexterity; Float32 patient_age, patient_height, patient_weight; uint32 patient_birth_date; char physician_name[32], operator_name[32], study_description[32]; uint16_t acquisition_type, patient_orientation; char facility_name[20]; uint16_t num_planes, num_frames, num_gates, num_bed_pos; Float32 init_bed_position; Float32 bed_position[15]; Float32 plane_separation; uint16_t lwr_sctr_thres, lwr_true_thres, upr_true_thres; char user_process_code[10]; uint16_t acquisition_mode; Float32 bin_size, branching_fraction; uint32 dose_start_time; Float32 dosage, well_counter_corr_factor; char data_units[32]; uint16_t septa_state; char fill[12]; }) ecat_main_hdr; PACK( typedef struct { int16_t data_type, num_dimensions, x_dimension, y_dimension, z_dimension; Float32 x_offset, y_offset, z_offset, recon_zoom, scale_factor; int16_t image_min, image_max; Float32 x_pixel_size, y_pixel_size, z_pixel_size; int32_t frame_duration, frame_start_time; int16_t filter_code; Float32 x_resolution, y_resolution, z_resolution, num_r_elements, num_angles, z_rotation_angle, decay_corr_fctr; int32_t processing_code, gate_duration, r_wave_offset, num_accepted_beats; Float32 filter_cutoff_frequenc, filter_resolution, filter_ramp_slope; int16_t filter_order; Float32 filter_scatter_fraction, filter_scatter_slope; char annotation[40]; Float32 mtx[9], rfilter_cutoff, rfilter_resolution; int16_t rfilter_code, rfilter_order; Float32 zfilter_cutoff, zfilter_resolution; int16_t zfilter_code, zfilter_order; Float32 mtx_1_4, mtx_2_4, mtx_3_4; int16_t scatter_type, recon_type, recon_views, fill_cti[87], fill_user[49]; }) ecat_img_hdr; PACK( typedef struct { int32_t hdr[4], r[31][4]; }) ecat_list_hdr; bool swapEndian = false; size_t n; FILE *f; ecat_main_hdr mhdr; f = fopen(fname, "rb"); if (f) n = fread(&mhdr, sizeof(mhdr), 1, f); if(!f || n!=1) { printMessage("Problem reading ECAT7 file!\n"); fclose(f); return NULL; } if ((mhdr.magic[0] != 'M') || (mhdr.magic[1] != 'A') || (mhdr.magic[2] != 'T') || (mhdr.magic[3] != 'R') || (mhdr.magic[4] != 'I') || (mhdr.magic[5] != 'X') ) { if (isWarnIfNotEcat) printMessage("Signature not 'MATRIX' (ECAT7): '%s'\n", fname); fclose(f); return NULL; } swapEndian = mhdr.file_type > 255; if (swapEndian) { nifti_swap_2bytes(2, &mhdr.sw_version); nifti_swap_2bytes(1, &mhdr.file_type); //nifti_swap_2bytes(1, &mhdr.num_frames); nifti_swap_4bytes(1, &mhdr.ecat_calibration_factor); nifti_swap_4bytes(1, &mhdr.isotope_halflife); nifti_swap_4bytes(2, &mhdr.dosage); } if ((mhdr.file_type < ECAT7_2DSCAN) || (mhdr.file_type > ECAT7_3DSCANFIT)) { printMessage("Unknown ECAT file type %d\n", mhdr.file_type); fclose(f); return NULL; } //read list matrix ecat_list_hdr lhdr; fseek(f, 512, SEEK_SET); size_t nRead = fread(&lhdr, sizeof(lhdr), 1, f); if (nRead != 1) { printMessage("Error reading ECAT file\n"); fclose(f); return NULL; } if (swapEndian) nifti_swap_4bytes(128, &lhdr.hdr[0]); //offset to first image int img_StartBytes = lhdr.r[0][1] * 512; //load image header for first image fseek(f, img_StartBytes - 512, SEEK_SET); //image header is block immediately before image ecat_img_hdr ihdr; nRead = fread(&ihdr, sizeof(ihdr), 1, f); if (nRead != 1) { printMessage("Error reading ECAT file\n"); fclose(f); return NULL; } if (swapEndian) { nifti_swap_2bytes(5, &ihdr.data_type); nifti_swap_4bytes(5, &ihdr.x_offset); nifti_swap_2bytes(2, &ihdr.image_min); nifti_swap_4bytes(5, &ihdr.x_pixel_size); nifti_swap_2bytes(1, &ihdr.filter_code); nifti_swap_4bytes(14, &ihdr.x_resolution); nifti_swap_2bytes(1, &ihdr.filter_order); nifti_swap_4bytes(2, &ihdr.filter_scatter_fraction); nifti_swap_4bytes(11, &ihdr.mtx); nifti_swap_2bytes(2, &ihdr.rfilter_code); nifti_swap_4bytes(2, &ihdr.zfilter_cutoff); nifti_swap_2bytes(2, &ihdr.zfilter_code); nifti_swap_4bytes(3, &ihdr.mtx_1_4); nifti_swap_2bytes(3, &ihdr.scatter_type); } if ((ihdr.data_type != ECAT7_BYTE) && (ihdr.data_type != ECAT7_SUNI2) && (ihdr.data_type != ECAT7_SUNI4)) { printMessage("Unknown or unsupported ECAT data type %d\n", ihdr.data_type); fclose(f); return NULL; } int bytesPerVoxel = 2; if (ihdr.data_type == ECAT7_BYTE) bytesPerVoxel = 1; if (ihdr.data_type == ECAT7_SUNI4) bytesPerVoxel = 4; //next: read offsets for each volume: data not saved sequentially (each volume preceded by its own ecat_img_hdr) int num_vol = 0; bool isAbort = false; bool isScaleFactorVaries = false; #define kMaxVols 16000 size_t * imgOffsets = (size_t *)malloc(sizeof(size_t) * (kMaxVols)); float * imgSlopes = (float *)malloc(sizeof(float) * (kMaxVols)); ecat_img_hdr ihdrN; while ((lhdr.hdr[0]+lhdr.hdr[3]) == 31) { //while valid list if (num_vol > 0) { //read the next list fseek(f, 512 * (lhdr.hdr[1] -1), SEEK_SET); nRead =fread(&lhdr, 512, 1, f); if (nRead != 1) { printMessage("Error reading ECAT file\n"); fclose(f); return NULL; } if (swapEndian) nifti_swap_4bytes(128, &lhdr.hdr[0]); } if ((lhdr.hdr[0]+lhdr.hdr[3]) != 31) break; //if valid list if (lhdr.hdr[3] < 1) break; for (int k = 0; k < lhdr.hdr[3]; k++) { //check images' ecat_img_hdr matches first fseek(f, (lhdr.r[k][1]-1) * 512, SEEK_SET); //image header is block immediately before image nRead = fread(&ihdrN, sizeof(ihdrN), 1, f); if (nRead != 1) { printMessage("Error reading ECAT file\n"); fclose(f); return NULL; } if (swapEndian) { nifti_swap_2bytes(5, &ihdrN.data_type); nifti_swap_4bytes(5, &ihdrN.x_offset); nifti_swap_2bytes(2, &ihdrN.image_min); nifti_swap_4bytes(5, &ihdrN.x_pixel_size); nifti_swap_2bytes(1, &ihdrN.filter_code); nifti_swap_4bytes(14, &ihdrN.x_resolution); nifti_swap_2bytes(1, &ihdrN.filter_order); nifti_swap_4bytes(2, &ihdrN.filter_scatter_fraction); nifti_swap_4bytes(11, &ihdrN.mtx); nifti_swap_2bytes(2, &ihdrN.rfilter_code); nifti_swap_4bytes(2, &ihdrN.zfilter_cutoff); nifti_swap_2bytes(2, &ihdrN.zfilter_code); nifti_swap_4bytes(3, &ihdrN.mtx_1_4); nifti_swap_2bytes(3, &ihdrN.scatter_type); } if (ihdr.scale_factor != ihdrN.scale_factor) isScaleFactorVaries = true; if ((ihdr.data_type != ihdrN.data_type) || (ihdr.x_dimension != ihdrN.x_dimension) || (ihdr.y_dimension != ihdrN.y_dimension) || (ihdr.z_dimension != ihdrN.z_dimension)) { printError("Error: ECAT volumes have varying image dimensions\n"); isAbort = true; } if (num_vol < kMaxVols) { imgOffsets[num_vol] = lhdr.r[k][1]; imgSlopes[num_vol] = ihdrN.scale_factor; } num_vol ++; } if ((lhdr.hdr[0] > 0) || (isAbort)) break; //this list contains empty volumes: all lists have been read } //read all image offsets //report error reading image offsets if ((num_vol < 1) || (isAbort) || (num_vol >= kMaxVols)) { printMessage("Failure to extract ECAT7 images\n"); if (num_vol >= kMaxVols) printMessage("Increase kMaxVols"); fclose(f); free (imgOffsets); free(imgSlopes); return NULL; } if ((isScaleFactorVaries) && (bytesPerVoxel != 2)) { printError("ECAT scale factor varies between volumes (check for updates) '%s'\n", fname); fclose(f); free (imgOffsets); free(imgSlopes); return NULL; } //load image data unsigned char * img = NULL; if ((isScaleFactorVaries) && (bytesPerVoxel == 2)) { //we need to convert volumes from 16-bit to 32-bit to preserve scaling factors int num_vox = ihdr.x_dimension * ihdr.y_dimension * ihdr.z_dimension; size_t bytesPerVolumeIn = num_vox * bytesPerVoxel; //bytesPerVoxel == 2 unsigned char * imgIn = (unsigned char*)malloc(bytesPerVolumeIn); int16_t * img16i = (int16_t*) imgIn; bytesPerVoxel = 4; size_t bytesPerVolume = num_vox * bytesPerVoxel; img = (unsigned char*)malloc(bytesPerVolume * num_vol); float * img32 = (float*) img; for (int v = 0; v < num_vol; v++) { fseek(f, imgOffsets[v] * 512, SEEK_SET); nRead = fread( &imgIn[0], 1, bytesPerVolumeIn, f); if (nRead != 1) { printMessage("Error reading ECAT file\n"); fclose(f); return NULL; } if (swapEndian) nifti_swap_2bytes(num_vox, imgIn); int volOffset = v * num_vox; float scale = imgSlopes[v] * mhdr.ecat_calibration_factor; for (int i = 0; i < num_vox; i++) img32[i+volOffset] = (img16i[i] * scale); } //we have applied the scale factors to the data, so eliminate them ihdr.scale_factor = 1.0; mhdr.ecat_calibration_factor = 1.0; } else { //if isScaleFactorVaries else simple conversion size_t bytesPerVolume = ihdr.x_dimension * ihdr.y_dimension * ihdr.z_dimension * bytesPerVoxel; img = (unsigned char*)malloc(bytesPerVolume * num_vol); for (int v = 0; v < num_vol; v++) { fseek(f, imgOffsets[v] * 512, SEEK_SET); size_t sz = fread( &img[v * bytesPerVolume], 1, bytesPerVolume, f); if (sz != bytesPerVolume) { free(img); return NULL; } } if ((swapEndian) && (bytesPerVoxel == 2)) nifti_swap_2bytes(ihdr.x_dimension * ihdr.y_dimension * ihdr.z_dimension * num_vol, img); if ((swapEndian) && (bytesPerVoxel == 4)) nifti_swap_4bytes(ihdr.x_dimension * ihdr.y_dimension * ihdr.z_dimension * num_vol, img); } printWarning("ECAT support VERY experimental (Spatial transforms unknown)\n"); free (imgOffsets); free(imgSlopes); fclose(f); //fill DICOM header float timeBetweenVolumes = ihdr.frame_duration; if (num_vol > 1) timeBetweenVolumes = (float)(ihdrN.frame_start_time- ihdr.frame_start_time)/(float)(num_vol-1); //copy and clean strings (ECAT can use 0x0D as a string terminator) strncpy(dcm->patientName, mhdr.patient_name, 32); strncpy(dcm->patientID, mhdr.patient_id, 16); strncpy(dcm->seriesDescription, mhdr.study_description, 32); strncpy(dcm->protocolName, mhdr.study_type, 12); strncpy(dcm->imageComments, mhdr.isotope_name, 8); strncpy(dcm->procedureStepDescription, mhdr.radiopharmaceutical, 32); strClean(dcm->patientName); strClean(dcm->patientID); strClean(dcm->seriesDescription); strClean(dcm->protocolName); strClean(dcm->imageComments); strClean(dcm->procedureStepDescription); dcm->ecat_dosage = mhdr.dosage; dcm->ecat_isotope_halflife = mhdr.isotope_halflife; if (opts.isVerbose) { printMessage("ECAT7 details for '%s'\n", fname); printMessage(" Software version %d\n", mhdr.sw_version); printMessage(" System Type %d\n", mhdr.system_type); printMessage(" Frame duration %dms\n", ihdr.frame_duration); printMessage(" Time between volumes %gms\n", timeBetweenVolumes ); printMessage(" Patient name '%s'\n", dcm->patientName); printMessage(" Patient ID '%s'\n", dcm->patientID); printMessage(" Study description '%s'\n", dcm->seriesDescription); printMessage(" Study type '%s'\n", dcm->protocolName); printMessage(" Isotope name '%s'\n", dcm->imageComments); printMessage(" Isotope halflife %gs\n", mhdr.isotope_halflife); printMessage(" Radiopharmaceutical '%s'\n", dcm->procedureStepDescription); printMessage(" Dosage %gbequerels/cc\n", mhdr.dosage); if (!isScaleFactorVaries) { printMessage(" Scale factor %12.12g\n", ihdr.scale_factor); printMessage(" ECAT calibration factor %8.12g\n", mhdr.ecat_calibration_factor); } printMessage(" NIfTI scale slope %12.12g\n",ihdr.scale_factor * mhdr.ecat_calibration_factor); } dcm->manufacturer = kMANUFACTURER_SIEMENS; //dcm->manufacturersModelName = itoa(mhdr.system_type); sprintf(dcm->manufacturersModelName, "%d", mhdr.system_type); dcm->bitsAllocated = bytesPerVoxel * 8; if (isScaleFactorVaries) dcm->isFloat = true; dcm->bitsStored = 15; //ensures 16-bit images saved as INT16 not UINT16 dcm->samplesPerPixel = 1; dcm->xyzMM[1] = ihdr.x_pixel_size * 10.0; //cm -> mm dcm->xyzMM[2] = ihdr.y_pixel_size * 10.0; //cm -> mm dcm->xyzMM[3] = ihdr.z_pixel_size * 10.0; //cm -> mm dcm->TR = timeBetweenVolumes; dcm->xyzDim[1] = ihdr.x_dimension; dcm->xyzDim[2] = ihdr.y_dimension; dcm->xyzDim[3] = ihdr.z_dimension; dcm->xyzDim[4] = num_vol; //create a NIfTI header headerDcm2Nii(*dcm, hdr, false); //here we mimic SPM's spatial starting estimate SForm mat44 m44; LOAD_MAT44(m44, -hdr->pixdim[1], 0.0f, 0.0f, ((float)dcm->xyzDim[1]-2.0)/2.0*dcm->xyzMM[1], 0.0f, -hdr->pixdim[2], 0.0f, ((float)dcm->xyzDim[2]-2.0)/2.0*dcm->xyzMM[2], 0.0f, 0.0f, -hdr->pixdim[3], ((float)dcm->xyzDim[3]-2.0)/2.0*dcm->xyzMM[3]); setQSForm(hdr, m44, false); //make sure image does not include a spatial matrix bool isMatrix = false; for (int i = 0; i < 9; i++) if (ihdr.mtx[i] != 0.0) isMatrix = true; if (isMatrix) printWarning("ECAT volume appears to store spatial transformation matrix (please check for updates)\n"); hdr->scl_slope = ihdr.scale_factor * mhdr.ecat_calibration_factor; if (mhdr.gantry_tilt != 0.0) printMessage("Warning: ECAT gantry tilt not supported %g\n", mhdr.gantry_tilt); return img; } int convert_foreign (const char *fn, struct TDCMopts opts){ struct nifti_1_header hdr; struct TDICOMdata dcm = clear_dicom_data(); unsigned char * img = NULL; img = readEcat7(fn, &dcm, &hdr, opts, false); //false: silent, do not report if file is not ECAT format if (!img) return EXIT_FAILURE; char niiFilename[1024]; int ret = nii_createFilename(dcm, niiFilename, opts); printMessage("Saving ECAT as '%s'\n", niiFilename); if (ret != EXIT_SUCCESS) return ret; //struct TDTI4D dti4D; //nii_SaveBIDS(niiFilename, dcm, opts, &dti4D, &hdr, fn); nii_SaveBIDS(niiFilename, dcm, opts, &hdr, fn); ret = nii_saveNII(niiFilename, hdr, img, opts); free(img); return ret; }// convert_foreign() dcm2niix-1.0.20171215/console/nii_foreign.h000066400000000000000000000004371322051203000200630ustar00rootroot00000000000000//Attempt to open non-DICOM image #ifndef _NII_FOREIGN_ #define _NII_FOREIGN_ #ifdef __cplusplus extern "C" { #endif #include "nii_dicom_batch.h" //int open_foreign (const char *fn); int convert_foreign (const char *fn, struct TDCMopts opts); #ifdef __cplusplus } #endif #endifdcm2niix-1.0.20171215/console/nii_ortho.cpp000066400000000000000000000313301322051203000201140ustar00rootroot00000000000000#ifndef HAVE_R #include "nifti1.h" #endif #include "nifti1_io_core.h" #include "nii_ortho.h" #include #include #include #include #include #include #include #include #include //#include #include #ifndef _MSC_VER #include #endif //#define MY_DEBUG //verbose text reporting #include "print.h" typedef struct { int v[3]; } vec3i; mat33 matDotMul33 (mat33 a, mat33 b) // in Matlab: ret = a'.*b { mat33 ret; for (int i=0; i<3; i++) { for (int j=0; j<3; j++) { ret.m[i][j] = a.m[i][j]*b.m[j][i]; } } return ret; } mat33 matMul33 (mat33 a, mat33 b) // mult = a * b { mat33 mult; for(int i=0;i<3;i++) { for(int j=0;j<3;j++) { mult.m[j][i]=0; for(int k=0;k<3;k++) mult.m[j][i]+=a.m[j][k]*b.m[k][i]; } } return mult; } float getOrthoResidual (mat33 orig, mat33 transform) { mat33 mat = matDotMul33(orig, transform); float ret = 0; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { ret = ret + (mat.m[i][j]); } } return ret; } mat33 getBestOrient(mat44 R, vec3i flipVec) //flipVec reports flip: [1 1 1]=no flips, [-1 1 1] flip X dimension { mat33 ret, newmat, orig; LOAD_MAT33(orig,R.m[0][0],R.m[0][1],R.m[0][2], R.m[1][0],R.m[1][1],R.m[1][2], R.m[2][0],R.m[2][1],R.m[2][2]); float best = 0;//FLT_MAX; float newval; for (int rot = 0; rot < 6; rot++) { //6 rotations switch (rot) { case 0: LOAD_MAT33(newmat,flipVec.v[0],0,0, 0,flipVec.v[1],0, 0,0,flipVec.v[2]); break; case 1: LOAD_MAT33(newmat,flipVec.v[0],0,0, 0,0,flipVec.v[1], 0,flipVec.v[2],0); break; case 2: LOAD_MAT33(newmat,0,flipVec.v[0],0, flipVec.v[1],0,0, 0,0,flipVec.v[2]); break; case 3: LOAD_MAT33(newmat,0,flipVec.v[0],0, 0,0,flipVec.v[1], flipVec.v[2],0,0); break; case 4: LOAD_MAT33(newmat,0,0,flipVec.v[0], flipVec.v[1],0,0, 0,flipVec.v[2],0); break; case 5: LOAD_MAT33(newmat,0,0,flipVec.v[0], 0,flipVec.v[1],0, flipVec.v[2],0,0); break; } newval = getOrthoResidual(orig, newmat); if (newval > best) { best = newval; ret = newmat; } } return ret; } bool isMat44Canonical(mat44 R) //returns true if diagonals >0 and all others =0 // no rotation is necessary - already in perfect orthogonal alignment { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if ((i == j) && (R.m[i][j] <= 0) ) return false; if ((i != j) && (R.m[i][j] != 0) ) return false; }//j }//i return true; } vec3i setOrientVec(mat33 m) // Assumes isOrthoMat NOT computed on INVERSE, hence return INVERSE of solution... //e.g. [-1,2,3] means reflect x axis, [2,1,3] means swap x and y dimensions { vec3i ret = {{0, 0, 0}}; //mat33 m = {-1,0,0, 0,1,0, 0,0,1}; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (m.m[i][j] > 0) ret.v[j] = i+1; if (m.m[i][j] < 0) ret.v[j] = -(i+1); }//j }//i return ret; } mat44 setMat44Vec(mat33 m33, vec3 Translations) //convert a 3x3 rotation matrix to a 4x4 matrix where the last column stores translations and the last row is 0 0 0 1 { mat44 m44; for (int i=0; i<3; i++) { for (int j=0; j<3; j++) { m44.m[i][j] = m33.m[i][j]; } } m44.m[0][3] = Translations.v[0]; m44.m[1][3] = Translations.v[1]; m44.m[2][3] = Translations.v[2]; m44.m[3][0] = 0; m44.m[3][1] = 0; m44.m[3][2] = 0; m44.m[3][3] = 1; return m44; } mat44 sFormMat(struct nifti_1_header *h) { mat44 s; s.m[0][0]=h->srow_x[0]; s.m[0][1]=h->srow_x[1]; s.m[0][2]=h->srow_x[2]; s.m[0][3]=h->srow_x[3]; s.m[1][0]=h->srow_y[0]; s.m[1][1]=h->srow_y[1]; s.m[1][2]=h->srow_y[2]; s.m[1][3]=h->srow_y[3]; s.m[2][0]=h->srow_z[0]; s.m[2][1]=h->srow_z[1]; s.m[2][2]=h->srow_z[2]; s.m[2][3]=h->srow_z[3]; s.m[3][0] = 0 ; s.m[3][1] = 0 ; s.m[3][2] = 0 ; s.m[3][3] = 1 ; return s; } void mat2sForm (struct nifti_1_header *h, mat44 s) { h->srow_x[0] = s.m[0][0]; h->srow_x[1] = s.m[0][1]; h->srow_x[2] = s.m[0][2]; h->srow_x[3] = s.m[0][3]; h->srow_y[0] = s.m[1][0]; h->srow_y[1] = s.m[1][1]; h->srow_y[2] = s.m[1][2]; h->srow_y[3] = s.m[1][3]; h->srow_z[0] = s.m[2][0]; h->srow_z[1] = s.m[2][1]; h->srow_z[2] = s.m[2][2]; h->srow_z[3] = s.m[2][3]; } size_t* orthoOffsetArray(int dim, int stepBytesPerVox) { //return lookup table of length dim with values incremented by stepBytesPerVox // e.g. if Dim=10 and stepBytes=2: 0,2,4..18, is stepBytes=-2 18,16,14...0 size_t *lut= (size_t *)malloc(dim*sizeof(size_t)); if (stepBytesPerVox > 0) lut[0] = 0; else lut[0] = -stepBytesPerVox *(dim-1); if (dim > 1) for (int i=1; i < dim; i++) lut[i] = lut[i-1] + (size_t)stepBytesPerVox; return lut; } //orthoOffsetArray() //void reOrientImg( unsigned char * restrict img, vec3i outDim, vec3i outInc, int bytePerVox, int nvol) { void reOrientImg( unsigned char * img, vec3i outDim, vec3i outInc, int bytePerVox, int nvol) { //reslice data to new orientation //generate look up tables size_t* xLUT =orthoOffsetArray(outDim.v[0], bytePerVox*outInc.v[0]); size_t* yLUT =orthoOffsetArray(outDim.v[1], bytePerVox*outInc.v[1]); size_t* zLUT =orthoOffsetArray(outDim.v[2], bytePerVox*outInc.v[2]); //convert data size_t bytePerVol = bytePerVox*outDim.v[0]*outDim.v[1]*outDim.v[2]; //number of voxels in spatial dimensions [1,2,3] size_t o = 0; //output address uint8_t *inbuf = (uint8_t *) malloc(bytePerVol); //we convert 1 volume at a time uint8_t *outbuf = (uint8_t *) img; //source image for (int vol= 0; vol < nvol; vol++) { memcpy(&inbuf[0], &outbuf[vol*bytePerVol], bytePerVol); //copy source volume for (int z = 0; z < outDim.v[2]; z++) for (int y = 0; y < outDim.v[1]; y++) for (int x = 0; x < outDim.v[0]; x++) { memcpy(&outbuf[o], &inbuf[xLUT[x]+yLUT[y]+zLUT[z]], bytePerVox); o = o+ bytePerVox; } //for each x } //for each volume //free arrays free(inbuf); free(xLUT); free(yLUT); free(zLUT); } //reOrientImg unsigned char * reOrient(unsigned char* img, struct nifti_1_header *h, vec3i orientVec, mat33 orient, vec3 minMM) //e.g. [-1,2,3] means reflect x axis, [2,1,3] means swap x and y dimensions { size_t nvox = h->dim[1] * h->dim[2] * h->dim[3]; if (nvox < 1) return img; vec3i outDim= {{0,0,0}}; vec3i outInc= {{0,0,0}}; for (int i = 0; i < 3; i++) { //set dimension, pixdim and outDim.v[i] = h->dim[abs(orientVec.v[i])]; if (abs(orientVec.v[i]) == 1) outInc.v[i] = 1; if (abs(orientVec.v[i]) == 2) outInc.v[i] = h->dim[1]; if (abs(orientVec.v[i]) == 3) outInc.v[i] = h->dim[1]*h->dim[2]; if (orientVec.v[i] < 0) outInc.v[i] = -outInc.v[i]; //flip } //for each dimension int nvol = 1; //convert all non-spatial volumes from source to destination for (int vol = 4; vol < 8; vol++) { if (h->dim[vol] > 1) nvol = nvol * h->dim[vol]; } reOrientImg(img, outDim, outInc, h->bitpix / 8, nvol); //now change the header.... vec3 outPix= {{h->pixdim[abs(orientVec.v[0])],h->pixdim[abs(orientVec.v[1])],h->pixdim[abs(orientVec.v[2])]}}; for (int i = 0; i < 3; i++) { h->dim[i+1] = outDim.v[i]; h->pixdim[i+1] = outPix.v[i]; } mat44 s = sFormMat(h); mat33 mat; //computer transform LOAD_MAT33(mat, s.m[0][0],s.m[0][1],s.m[0][2], s.m[1][0],s.m[1][1],s.m[1][2], s.m[2][0],s.m[2][1],s.m[2][2]); mat = matMul33( mat, orient); s = setMat44Vec(mat, minMM); //add offset mat2sForm(h,s); h->qform_code = h->sform_code; //apply to the quaternion as well float dumdx, dumdy, dumdz; nifti_mat44_to_quatern( s , &h->quatern_b, &h->quatern_c, &h->quatern_d,&h->qoffset_x, &h->qoffset_y, &h->qoffset_z, &dumdx, &dumdy, &dumdz,&h->pixdim[0]) ; return img; } //reOrient() float getDistance (vec3 v, vec3 min) //scalar distance between two 3D points - Pythagorean theorem { return sqrt(pow((v.v[0]-min.v[0]),2)+pow((v.v[1]-min.v[1]),2)+pow((v.v[2]-min.v[2]),2) ); } vec3 xyz2mm (mat44 R, vec3 v) { vec3 ret; for (int i = 0; i < 3; i++) { ret.v[i] = ( (R.m[i][0]*v.v[0])+(R.m[i][1]*v.v[1])+ (R.m[i][2]*v.v[2])+R.m[i][3] ); } return ret; } vec3 minCornerFlip (struct nifti_1_header *h, vec3i* flipVec) //orthogonal rotations and reflections applied as 3x3 matrices will cause the origin to shift // a simple solution is to first compute the most left, posterior, inferior voxel in the source image // this voxel will be at location i,j,k = 0,0,0, so we can simply use this as the offset for the final 4x4 matrix... { int i,j, minIndex; vec3i flipVecs[8]; vec3 corner[8], min; mat44 s = sFormMat(h); for (int i = 0; i < 8; i++) { if (i & 1) flipVecs[i].v[0] = -1; else flipVecs[i].v[0] = 1; if (i & 2) flipVecs[i].v[1] = -1; else flipVecs[i].v[1] = 1; if (i & 4) flipVecs[i].v[2] = -1; else flipVecs[i].v[2] = 1; corner[i] = setVec3(0,0,0); //assume no reflections if ((flipVecs[i].v[0]) < 1) corner[i].v[0] = h->dim[1]-1; //reflect X if ((flipVecs[i].v[1]) < 1) corner[i].v[1] = h->dim[2]-1; //reflect Y if ((flipVecs[i].v[2]) < 1) corner[i].v[2] = h->dim[3]-1; //reflect Z corner[i] = xyz2mm(s,corner[i]); } //find extreme edge from ALL corners.... min = corner[0]; for (i = 1; i < 8; i++) { for (j = 0; j < 3; j++) { if (corner[i].v[j]< min.v[j]) min.v[j] = corner[i].v[j]; } } float dx; //observed distance from corner float min_dx = getDistance (corner[0], min); minIndex = 0; //index of corner closest to min //see if any corner is closer to absmin than the first one... for (i = 1; i < 8; i++) { dx = getDistance (corner[i], min); if (dx < min_dx) { min_dx = dx; minIndex = i; } } min = corner[minIndex]; //this is the single corner closest to min from all *flipVec= flipVecs[minIndex]; return min; } #ifdef MY_DEBUG void reportMat44o(char *str, mat44 A) { printMessage("%s = [%g %g %g %g; %g %g %g %g; %g %g %g %g; 0 0 0 1]\n",str, A.m[0][0],A.m[0][1],A.m[0][2],A.m[0][3], A.m[1][0],A.m[1][1],A.m[1][2],A.m[1][3], A.m[2][0],A.m[2][1],A.m[2][2],A.m[2][3]); } #endif unsigned char * nii_setOrtho(unsigned char* img, struct nifti_1_header *h) { if ((h->dim[1] < 1) || (h->dim[2] < 1) || (h->dim[3] < 1)) return img; if ((h->sform_code == NIFTI_XFORM_UNKNOWN) && (h->qform_code != NIFTI_XFORM_UNKNOWN)) { //only q-form provided mat44 q = nifti_quatern_to_mat44(h->quatern_b, h->quatern_c, h->quatern_d, h->qoffset_x, h->qoffset_y, h->qoffset_z, h->pixdim[1], h->pixdim[2], h->pixdim[3],h->pixdim[0]); mat2sForm(h,q); //convert q-form to s-form h->sform_code = h->qform_code; } if (h->sform_code == NIFTI_XFORM_UNKNOWN) { #ifdef MY_DEBUG printMessage("No Q or S spatial transforms - assuming canonical orientation"); #endif return img; } mat44 s = sFormMat(h); if (isMat44Canonical( s)) { #ifdef MY_DEBUG printMessage("Image in perfect alignment: no need to reorient"); #endif return img; } vec3i flipV; vec3 minMM = minCornerFlip(h, &flipV); mat33 orient = getBestOrient(s, flipV); vec3i orientVec = setOrientVec(orient); if ((orientVec.v[0]==1) && (orientVec.v[1]==2) && (orientVec.v[2]==3) ) { #ifdef MY_DEBUG printMessage("Image already near best orthogonal alignment: no need to reorient\n"); #endif return img; } bool is24 = false; if (h->bitpix == 24 ) { //RGB stored as planar data. treat as 3 8-bit slices return img; is24 = true; h->bitpix = 8; h->dim[3] = h->dim[3] * 3; } img = reOrient(img, h,orientVec, orient, minMM); if (is24 ) { h->bitpix = 24; h->dim[3] = h->dim[3] / 3; } #ifdef MY_DEBUG printMessage("NewRotation= %d %d %d\n", orientVec.v[0],orientVec.v[1],orientVec.v[2]); printMessage("MinCorner= %.2f %.2f %.2f\n", minMM.v[0],minMM.v[1],minMM.v[2]); reportMat44o((char*)"input",s); s = sFormMat(h); reportMat44o((char*)"output",s); #endif return img; } dcm2niix-1.0.20171215/console/nii_ortho.h000066400000000000000000000005421322051203000175620ustar00rootroot00000000000000#ifndef _NIFTI_ORTHO_CORE_ #define _NIFTI_ORTHO_CORE_ #ifdef __cplusplus extern "C" { #endif #ifndef HAVE_R #include "nifti1.h" #endif void mat2sForm (struct nifti_1_header *h, mat44 s); bool isMat44Canonical(mat44 R); unsigned char * nii_setOrtho(unsigned char* img, struct nifti_1_header *h); #ifdef __cplusplus } #endif #endif dcm2niix-1.0.20171215/console/print.h000077500000000000000000000045661322051203000167410ustar00rootroot00000000000000//This unit allows us to re-direct text messages // For standard C programs send text messages to the console via "printf" // The XCode project shows how you can re-direct these messages to a NSTextView // For QT programs, we can sent text messages to the cout buffer // The QT project shows how you can re-direct these to a Qtextedit // For R programs, we can intercept these messages. #ifndef _R_PRINT_H_ #define _R_PRINT_H_ #include #ifdef HAVE_R #define R_USE_C99_IN_CXX #include #define printMessage(...) { Rprintf("[dcm2niix info] "); Rprintf(__VA_ARGS__); } #define printWarning(...) { Rprintf("[dcm2niix WARNING] "); Rprintf(__VA_ARGS__); } #define printError(...) { Rprintf("[dcm2niix ERROR] "); Rprintf(__VA_ARGS__); } #else #ifdef myUseCOut //for piping output to Qtextedit // printf and cout buffers are not the same // #define printMessage(...) ({fprintf(stdout,__VA_ARGS__);}) #include template< typename... Args > void printMessage( const char* format, Args... args ) { //std::printf( format, args... ); //fprintf(stdout,"Short read on %s: Expected 512, got %zd\n",path, bytes_read); int length = std::snprintf( nullptr, 0, format, args... ); if ( length <= 0 ) return; char* buf = new char[length + 1]; std::snprintf( buf, length + 1, format, args... ); std::cout << buf; delete[] buf; } #define printError(...) do { printMessage("Error: "); printMessage(__VA_ARGS__);} while(0) #else #include #define printMessage printf //#define printMessageError(...) fprintf (stderr, __VA_ARGS__) #ifdef myErrorStdOut //for XCode MRIcro project, pipe errors to stdout not stderr #define printError(...) do { printMessage("Error: "); printMessage(__VA_ARGS__);} while(0) #else #define printError(...) do { fprintf (stderr,"Error: "); fprintf (stderr, __VA_ARGS__);} while(0) #endif #endif //myUseCOut //n.b. use ({}) for multi-line macros http://www.geeksforgeeks.org/multiline-macros-in-c/ //these next lines work on GCC but not _MSC_VER // #define printWarning(...) ({printMessage("Warning: "); printMessage(__VA_ARGS__);}) // #define printError(...) ({ printMessage("Error: "); printMessage(__VA_ARGS__);}) #define printWarning(...) do {printMessage("Warning: "); printMessage(__VA_ARGS__);} while(0) #endif //HAVE_R #endif //_R_PRINT_H_ dcm2niix-1.0.20171215/console/tinydir.h000066400000000000000000000205411322051203000172530ustar00rootroot00000000000000/* Copyright (c) 2013-2014, Cong Xu 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. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef TINYDIR_H #define TINYDIR_H #include #include #include #ifdef _MSC_VER #define WIN32_LEAN_AND_MEAN #include #pragma warning (disable : 4996) #else #include #include #endif /* types */ #define _TINYDIR_PATH_MAX 4096 #ifdef _MSC_VER /* extra chars for the "\\*" mask */ #define _TINYDIR_PATH_EXTRA 2 #else #define _TINYDIR_PATH_EXTRA 0 #endif #define _TINYDIR_FILENAME_MAX 256 #ifdef _MSC_VER #define _TINYDIR_FUNC static __inline #else #define _TINYDIR_FUNC static __inline__ #endif typedef struct { char path[_TINYDIR_PATH_MAX]; char name[_TINYDIR_FILENAME_MAX]; int is_dir; int is_reg; #ifdef _MSC_VER #else struct stat _s; #endif } tinydir_file; typedef struct { char path[_TINYDIR_PATH_MAX]; int has_next; size_t n_files; tinydir_file *_files; #ifdef _MSC_VER HANDLE _h; WIN32_FIND_DATA _f; #else DIR *_d; struct dirent *_e; #endif } tinydir_dir; /* declarations */ _TINYDIR_FUNC int tinydir_open(tinydir_dir *dir, const char *path); _TINYDIR_FUNC int tinydir_open_sorted(tinydir_dir *dir, const char *path); _TINYDIR_FUNC void tinydir_close(tinydir_dir *dir); _TINYDIR_FUNC int tinydir_next(tinydir_dir *dir); _TINYDIR_FUNC int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file); _TINYDIR_FUNC int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i); _TINYDIR_FUNC int tinydir_open_subdir_n(tinydir_dir *dir, size_t i); _TINYDIR_FUNC int _tinydir_file_cmp(const void *a, const void *b); /* definitions*/ _TINYDIR_FUNC int tinydir_open(tinydir_dir *dir, const char *path) { if (dir == NULL || path == NULL || strlen(path) == 0) { errno = EINVAL; return -1; } if (strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) { errno = ENAMETOOLONG; return -1; } /* initialise dir */ dir->_files = NULL; #ifdef _MSC_VER dir->_h = INVALID_HANDLE_VALUE; #else dir->_d = NULL; #endif tinydir_close(dir); strcpy(dir->path, path); #ifdef _MSC_VER strcat(dir->path, "\\*"); dir->_h = FindFirstFile(dir->path, &dir->_f); dir->path[strlen(dir->path) - 2] = '\0'; if (dir->_h == INVALID_HANDLE_VALUE) #else dir->_d = opendir(path); if (dir->_d == NULL) #endif { errno = ENOENT; goto bail; } /* read first file */ dir->has_next = 1; #ifndef _MSC_VER dir->_e = readdir(dir->_d); if (dir->_e == NULL) { dir->has_next = 0; } #endif return 0; bail: tinydir_close(dir); return -1; } _TINYDIR_FUNC int tinydir_open_sorted(tinydir_dir *dir, const char *path) { /* Count the number of files first, to pre-allocate the files array */ size_t n_files = 0; if (tinydir_open(dir, path) == -1) { return -1; } while (dir->has_next) { n_files++; if (tinydir_next(dir) == -1) { goto bail; } } tinydir_close(dir); if (tinydir_open(dir, path) == -1) { return -1; } dir->n_files = 0; dir->_files = (tinydir_file *)malloc(sizeof *dir->_files * n_files); if (dir->_files == NULL) { errno = ENOMEM; goto bail; } while (dir->has_next) { tinydir_file *p_file; dir->n_files++; p_file = &dir->_files[dir->n_files - 1]; if (tinydir_readfile(dir, p_file) == -1) { goto bail; } if (tinydir_next(dir) == -1) { goto bail; } /* Just in case the number of files has changed between the first and second reads, terminate without writing into unallocated memory */ if (dir->n_files == n_files) { break; } } qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp); return 0; bail: tinydir_close(dir); return -1; } _TINYDIR_FUNC void tinydir_close(tinydir_dir *dir) { if (dir == NULL) { return; } memset(dir->path, 0, sizeof(dir->path)); dir->has_next = 0; dir->n_files = 0; if (dir->_files != NULL) { free(dir->_files); } dir->_files = NULL; #ifdef _MSC_VER if (dir->_h != INVALID_HANDLE_VALUE) { FindClose(dir->_h); } dir->_h = INVALID_HANDLE_VALUE; #else if (dir->_d) { closedir(dir->_d); } dir->_d = NULL; dir->_e = NULL; #endif } _TINYDIR_FUNC int tinydir_next(tinydir_dir *dir) { if (dir == NULL) { errno = EINVAL; return -1; } if (!dir->has_next) { errno = ENOENT; return -1; } #ifdef _MSC_VER if (FindNextFile(dir->_h, &dir->_f) == 0) #else dir->_e = readdir(dir->_d); if (dir->_e == NULL) #endif { dir->has_next = 0; #ifdef _MSC_VER if (GetLastError() != ERROR_SUCCESS && GetLastError() != ERROR_NO_MORE_FILES) { tinydir_close(dir); errno = EIO; return -1; } #endif } return 0; } _TINYDIR_FUNC int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file) { if (dir == NULL || file == NULL) { errno = EINVAL; return -1; } #ifdef _MSC_VER if (dir->_h == INVALID_HANDLE_VALUE) #else if (dir->_e == NULL) #endif { errno = ENOENT; return -1; } if (strlen(dir->path) + strlen( #ifdef _MSC_VER dir->_f.cFileName #else dir->_e->d_name #endif ) + 1 + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) { /* the path for the file will be too long */ errno = ENAMETOOLONG; return -1; } if (strlen( #ifdef _MSC_VER dir->_f.cFileName #else dir->_e->d_name #endif ) >= _TINYDIR_FILENAME_MAX) { errno = ENAMETOOLONG; return -1; } strcpy(file->path, dir->path); strcat(file->path, "/"); strcpy(file->name, #ifdef _MSC_VER dir->_f.cFileName #else dir->_e->d_name #endif ); strcat(file->path, file->name); #ifndef _MSC_VER if (stat(file->path, &file->_s) == -1) { return -1; } #endif file->is_dir = #ifdef _MSC_VER !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); #else S_ISDIR(file->_s.st_mode); #endif file->is_reg = #ifdef _MSC_VER !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) || ( !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) && !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) && #ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) && #endif #ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) && #endif !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) && !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY)); #else S_ISREG(file->_s.st_mode); #endif return 0; } _TINYDIR_FUNC int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i) { if (dir == NULL || file == NULL) { errno = EINVAL; return -1; } if (i >= dir->n_files) { errno = ENOENT; return -1; } memcpy(file, &dir->_files[i], sizeof(tinydir_file)); return 0; } _TINYDIR_FUNC int tinydir_open_subdir_n(tinydir_dir *dir, size_t i) { char path[_TINYDIR_PATH_MAX]; if (dir == NULL) { errno = EINVAL; return -1; } if (i >= dir->n_files || !dir->_files[i].is_dir) { errno = ENOENT; return -1; } strcpy(path, dir->_files[i].path); tinydir_close(dir); if (tinydir_open_sorted(dir, path) == -1) { return -1; } return 0; } _TINYDIR_FUNC int _tinydir_file_cmp(const void *a, const void *b) { const tinydir_file *fa = (const tinydir_file *)a; const tinydir_file *fb = (const tinydir_file *)b; if (fa->is_dir != fb->is_dir) { return -(fa->is_dir - fb->is_dir); } return strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX); } #endif dcm2niix-1.0.20171215/console/ucm.cmake000066400000000000000000000600361322051203000172110ustar00rootroot00000000000000# # ucm.cmake - useful cmake macros # # Copyright (c) 2016 Viktor Kirilov # # Distributed under the MIT Software License # See accompanying file LICENSE.txt or copy at # https://opensource.org/licenses/MIT # # The documentation can be found at the library's page: # https://github.com/onqtam/ucm cmake_minimum_required(VERSION 2.8.12) include(CMakeParseArguments) # optionally include cotire - the git submodule might not be inited (or the user might have already included it) if(NOT COMMAND cotire) include(${CMAKE_CURRENT_LIST_DIR}/../cotire/CMake/cotire.cmake OPTIONAL) endif() if(COMMAND cotire AND "1.7.9" VERSION_LESS "${COTIRE_CMAKE_MODULE_VERSION}") set(ucm_with_cotire 1) else() set(ucm_with_cotire 0) endif() # option(UCM_UNITY_BUILD "Enable unity build for targets registered with the ucm_add_target() macro" OFF) # option(UCM_NO_COTIRE_FOLDER "Do not use a cotire folder in the solution explorer for all unity and cotire related targets" ON) # ucm_add_flags # Adds compiler flags to CMAKE__FLAGS or to a specific config macro(ucm_add_flags) cmake_parse_arguments(ARG "C;CXX;CLEAR_OLD" "" "CONFIG" ${ARGN}) if(NOT ARG_CONFIG) set(ARG_CONFIG " ") endif() foreach(CONFIG ${ARG_CONFIG}) # determine to which flags to add if(NOT ${CONFIG} STREQUAL " ") string(TOUPPER ${CONFIG} CONFIG) set(CXX_FLAGS CMAKE_CXX_FLAGS_${CONFIG}) set(C_FLAGS CMAKE_C_FLAGS_${CONFIG}) else() set(CXX_FLAGS CMAKE_CXX_FLAGS) set(C_FLAGS CMAKE_C_FLAGS) endif() # clear the old flags if(${ARG_CLEAR_OLD}) if("${ARG_CXX}" OR NOT "${ARG_C}") set(${CXX_FLAGS} "") endif() if("${ARG_C}" OR NOT "${ARG_CXX}") set(${C_FLAGS} "") endif() endif() # add all the passed flags foreach(flag ${ARG_UNPARSED_ARGUMENTS}) if("${ARG_CXX}" OR NOT "${ARG_C}") set(${CXX_FLAGS} "${${CXX_FLAGS}} ${flag}") endif() if("${ARG_C}" OR NOT "${ARG_CXX}") set(${C_FLAGS} "${${C_FLAGS}} ${flag}") endif() endforeach() endforeach() endmacro() # ucm_set_flags # Sets the CMAKE__FLAGS compiler flags or for a specific config macro(ucm_set_flags) ucm_add_flags(CLEAR_OLD ${ARGN}) endmacro() # ucm_add_linker_flags # Adds linker flags to CMAKE__LINKER_FLAGS or to a specific config macro(ucm_add_linker_flags) cmake_parse_arguments(ARG "CLEAR_OLD;EXE;MODULE;SHARED;STATIC" "" "CONFIG" ${ARGN}) if(NOT ARG_CONFIG) set(ARG_CONFIG " ") endif() foreach(CONFIG ${ARG_CONFIG}) string(TOUPPER "${CONFIG}" CONFIG) if(NOT ${ARG_EXE} AND NOT ${ARG_MODULE} AND NOT ${ARG_SHARED} AND NOT ${ARG_STATIC}) set(ARG_EXE 1) set(ARG_MODULE 1) set(ARG_SHARED 1) set(ARG_STATIC 1) endif() set(flags_configs "") if(${ARG_EXE}) if(NOT "${CONFIG}" STREQUAL " ") list(APPEND flags_configs CMAKE_EXE_LINKER_FLAGS_${CONFIG}) else() list(APPEND flags_configs CMAKE_EXE_LINKER_FLAGS) endif() endif() if(${ARG_MODULE}) if(NOT "${CONFIG}" STREQUAL " ") list(APPEND flags_configs CMAKE_MODULE_LINKER_FLAGS_${CONFIG}) else() list(APPEND flags_configs CMAKE_MODULE_LINKER_FLAGS) endif() endif() if(${ARG_SHARED}) if(NOT "${CONFIG}" STREQUAL " ") list(APPEND flags_configs CMAKE_SHARED_LINKER_FLAGS_${CONFIG}) else() list(APPEND flags_configs CMAKE_SHARED_LINKER_FLAGS) endif() endif() if(${ARG_STATIC}) if(NOT "${CONFIG}" STREQUAL " ") list(APPEND flags_configs CMAKE_STATIC_LINKER_FLAGS_${CONFIG}) else() list(APPEND flags_configs CMAKE_STATIC_LINKER_FLAGS) endif() endif() # clear the old flags if(${ARG_CLEAR_OLD}) foreach(flags ${flags_configs}) set(${flags} "") endforeach() endif() # add all the passed flags foreach(flag ${ARG_UNPARSED_ARGUMENTS}) foreach(flags ${flags_configs}) set(${flags} "${${flags}} ${flag}") endforeach() endforeach() endforeach() endmacro() # ucm_set_linker_flags # Sets the CMAKE__LINKER_FLAGS linker flags or for a specific config macro(ucm_set_linker_flags) ucm_add_linker_flags(CLEAR_OLD ${ARGN}) endmacro() # ucm_gather_flags # Gathers all lists of flags for printing or manipulation macro(ucm_gather_flags with_linker result) set(${result} "") # add the main flags without a config list(APPEND ${result} CMAKE_C_FLAGS) list(APPEND ${result} CMAKE_CXX_FLAGS) if(${with_linker}) list(APPEND ${result} CMAKE_EXE_LINKER_FLAGS) list(APPEND ${result} CMAKE_MODULE_LINKER_FLAGS) list(APPEND ${result} CMAKE_SHARED_LINKER_FLAGS) list(APPEND ${result} CMAKE_STATIC_LINKER_FLAGS) endif() if("${CMAKE_CONFIGURATION_TYPES}" STREQUAL "" AND NOT "${CMAKE_BUILD_TYPE}" STREQUAL "") # handle single config generators - like makefiles/ninja - when CMAKE_BUILD_TYPE is set string(TOUPPER ${CMAKE_BUILD_TYPE} config) list(APPEND ${result} CMAKE_C_FLAGS_${config}) list(APPEND ${result} CMAKE_CXX_FLAGS_${config}) if(${with_linker}) list(APPEND ${result} CMAKE_EXE_LINKER_FLAGS_${config}) list(APPEND ${result} CMAKE_MODULE_LINKER_FLAGS_${config}) list(APPEND ${result} CMAKE_SHARED_LINKER_FLAGS_${config}) list(APPEND ${result} CMAKE_STATIC_LINKER_FLAGS_${config}) endif() else() # handle multi config generators (like msvc, xcode) foreach(config ${CMAKE_CONFIGURATION_TYPES}) string(TOUPPER ${config} config) list(APPEND ${result} CMAKE_C_FLAGS_${config}) list(APPEND ${result} CMAKE_CXX_FLAGS_${config}) if(${with_linker}) list(APPEND ${result} CMAKE_EXE_LINKER_FLAGS_${config}) list(APPEND ${result} CMAKE_MODULE_LINKER_FLAGS_${config}) list(APPEND ${result} CMAKE_SHARED_LINKER_FLAGS_${config}) list(APPEND ${result} CMAKE_STATIC_LINKER_FLAGS_${config}) endif() endforeach() endif() endmacro() # ucm_set_runtime # Sets the runtime (static/dynamic) for msvc/gcc macro(ucm_set_runtime) cmake_parse_arguments(ARG "STATIC;DYNAMIC" "" "" ${ARGN}) if(ARG_UNPARSED_ARGUMENTS) message(FATAL_ERROR "unrecognized arguments: ${ARG_UNPARSED_ARGUMENTS}") endif() if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" STREQUAL "") message(AUTHOR_WARNING "ucm_set_runtime() does not support clang yet!") endif() ucm_gather_flags(0 flags_configs) # add/replace the flags # note that if the user has messed with the flags directly this function might fail # - for example if with MSVC and the user has removed the flags - here we just switch/replace them if("${ARG_STATIC}") foreach(flags ${flags_configs}) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.4.7) # option "-static-libstdc++" available since GCC 4.5 if(NOT ${flags} MATCHES "-static-libstdc\\+\\+") set(${flags} "${${flags}} -static-libstdc++") endif() endif() if(NOT ${flags} MATCHES "-static-libgcc") set(${flags} "${${flags}} -static-libgcc") endif() elseif(MSVC) if(${flags} MATCHES "/MD") string(REGEX REPLACE "/MD" "/MT" ${flags} "${${flags}}") endif() endif() endforeach() elseif("${ARG_DYNAMIC}") foreach(flags ${flags_configs}) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") if(${flags} MATCHES "-static-libstdc\\+\\+") string(REGEX REPLACE "-static-libstdc\\+\\+" "" ${flags} "${${flags}}") endif() if(${flags} MATCHES "-static-libgcc") string(REGEX REPLACE "-static-libgcc" "" ${flags} "${${flags}}") endif() elseif(MSVC) if(${flags} MATCHES "/MT") string(REGEX REPLACE "/MT" "/MD" ${flags} "${${flags}}") endif() endif() endforeach() endif() endmacro() # ucm_print_flags # Prints all compiler flags for all configurations macro(ucm_print_flags) ucm_gather_flags(1 flags_configs) message("") foreach(flags ${flags_configs}) message("${flags}: ${${flags}}") endforeach() message("") endmacro() # ucm_count_sources # Counts the number of source files macro(ucm_count_sources) cmake_parse_arguments(ARG "" "RESULT" "" ${ARGN}) if(${ARG_RESULT} STREQUAL "") message(FATAL_ERROR "Need to pass RESULT and a variable name to ucm_count_sources()") endif() set(result 0) foreach(SOURCE_FILE ${ARG_UNPARSED_ARGUMENTS}) if("${SOURCE_FILE}" MATCHES \\.\(c|C|cc|cp|cpp|CPP|c\\+\\+|cxx|i|ii\)$) math(EXPR result "${result} + 1") endif() endforeach() set(${ARG_RESULT} ${result}) endmacro() # ucm_include_file_in_sources # Includes the file to the source with compiler flags macro(ucm_include_file_in_sources) cmake_parse_arguments(ARG "" "HEADER" "" ${ARGN}) if(${ARG_HEADER} STREQUAL "") message(FATAL_ERROR "Need to pass HEADER and a header file to ucm_include_file_in_sources()") endif() foreach(src ${ARG_UNPARSED_ARGUMENTS}) if(${src} MATCHES \\.\(c|C|cc|cp|cpp|CPP|c\\+\\+|cxx\)$) # get old flags get_source_file_property(old_compile_flags ${src} COMPILE_FLAGS) if(old_compile_flags STREQUAL "NOTFOUND") set(old_compile_flags "") endif() # update flags if(MSVC) set_source_files_properties(${src} PROPERTIES COMPILE_FLAGS "${old_compile_flags} /FI\"${CMAKE_CURRENT_SOURCE_DIR}/${ARG_HEADER}\"") else() set_source_files_properties(${src} PROPERTIES COMPILE_FLAGS "${old_compile_flags} -include \"${CMAKE_CURRENT_SOURCE_DIR}/${ARG_HEADER}\"") endif() endif() endforeach() endmacro() # ucm_dir_list # Returns a list of subdirectories for a given directory macro(ucm_dir_list thedir result) file(GLOB sub-dir "${thedir}/*") set(list_of_dirs "") foreach(dir ${sub-dir}) if(IS_DIRECTORY ${dir}) get_filename_component(DIRNAME ${dir} NAME) LIST(APPEND list_of_dirs ${DIRNAME}) endif() endforeach() set(${result} ${list_of_dirs}) endmacro() # ucm_trim_front_words # Trims X times the front word from a string separated with "/" and removes # the front "/" characters after that (used for filters for visual studio) macro(ucm_trim_front_words source out num_filter_trims) set(result "${source}") set(counter 0) while(${counter} LESS ${num_filter_trims}) MATH(EXPR counter "${counter} + 1") # removes everything at the front up to a "/" character string(REGEX REPLACE "^([^/]+)" "" result "${result}") # removes all consecutive "/" characters from the front string(REGEX REPLACE "^(/+)" "" result "${result}") endwhile() set(${out} ${result}) endmacro() # ucm_remove_files # Removes source files from a list of sources (path is the relative path for it to be found) macro(ucm_remove_files) cmake_parse_arguments(ARG "" "FROM" "" ${ARGN}) if("${ARG_UNPARSED_ARGUMENTS}" STREQUAL "") message(FATAL_ERROR "Need to pass some relative files to ucm_remove_files()") endif() if(${ARG_FROM} STREQUAL "") message(FATAL_ERROR "Need to pass FROM and a variable name to ucm_remove_files()") endif() foreach(cur_file ${ARG_UNPARSED_ARGUMENTS}) list(REMOVE_ITEM ${ARG_FROM} ${cur_file}) endforeach() endmacro() # ucm_remove_directories # Removes all source files from the given directories from the sources list macro(ucm_remove_directories) cmake_parse_arguments(ARG "" "FROM" "MATCHES" ${ARGN}) if("${ARG_UNPARSED_ARGUMENTS}" STREQUAL "") message(FATAL_ERROR "Need to pass some relative directories to ucm_remove_directories()") endif() if(${ARG_FROM} STREQUAL "") message(FATAL_ERROR "Need to pass FROM and a variable name to ucm_remove_directories()") endif() foreach(cur_dir ${ARG_UNPARSED_ARGUMENTS}) foreach(cur_file ${${ARG_FROM}}) string(REGEX MATCH ${cur_dir} res ${cur_file}) if(NOT "${res}" STREQUAL "") if("${ARG_MATCHES}" STREQUAL "") list(REMOVE_ITEM ${ARG_FROM} ${cur_file}) else() foreach(curr_ptrn ${ARG_MATCHES}) string(REGEX MATCH ${curr_ptrn} res ${cur_file}) if(NOT "${res}" STREQUAL "") list(REMOVE_ITEM ${ARG_FROM} ${cur_file}) break() endif() endforeach() endif() endif() endforeach() endforeach() endmacro() # ucm_add_files_impl macro(ucm_add_files_impl result trim files) foreach(cur_file ${files}) SET(${result} ${${result}} ${cur_file}) get_filename_component(FILEPATH ${cur_file} PATH) ucm_trim_front_words("${FILEPATH}" FILEPATH "${trim}") # replacing forward slashes with back slashes so filters can be generated (back slash used in parsing...) STRING(REPLACE "/" "\\" FILTERS "${FILEPATH}") SOURCE_GROUP("${FILTERS}" FILES ${cur_file}) endforeach() endmacro() # ucm_add_files # Adds files to a list of sources macro(ucm_add_files) cmake_parse_arguments(ARG "" "TO;FILTER_POP" "" ${ARGN}) if("${ARG_UNPARSED_ARGUMENTS}" STREQUAL "") message(FATAL_ERROR "Need to pass some relative files to ucm_add_files()") endif() if(${ARG_TO} STREQUAL "") message(FATAL_ERROR "Need to pass TO and a variable name to ucm_add_files()") endif() if("${ARG_FILTER_POP}" STREQUAL "") set(ARG_FILTER_POP 0) endif() ucm_add_files_impl(${ARG_TO} ${ARG_FILTER_POP} "${ARG_UNPARSED_ARGUMENTS}") endmacro() # ucm_add_dir_impl macro(ucm_add_dir_impl result rec trim dirs_in additional_ext) set(dirs "${dirs_in}") # handle the "" and "." cases if("${dirs}" STREQUAL "" OR "${dirs}" STREQUAL ".") set(dirs "./") endif() foreach(cur_dir ${dirs}) # to circumvent some linux/cmake/path issues - barely made it work... if(cur_dir STREQUAL "./") set(cur_dir "") else() set(cur_dir "${cur_dir}/") endif() # since unix is case sensitive - add these valid extensions too # we don't use "UNIX" but instead "CMAKE_HOST_UNIX" because we might be cross # compiling (for example emscripten) under windows and UNIX may be set to 1 # Also OSX is case insensitive like windows... set(additional_file_extensions "") if(CMAKE_HOST_UNIX AND NOT APPLE) set(additional_file_extensions "${cur_dir}*.CPP" "${cur_dir}*.C" "${cur_dir}*.H" "${cur_dir}*.HPP" ) endif() foreach(ext ${additional_ext}) list(APPEND additional_file_extensions "${cur_dir}*.${ext}") endforeach() # find all sources and set them as result FILE(GLOB found_sources RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" # https://gcc.gnu.org/onlinedocs/gcc-4.4.1/gcc/Overall-Options.html#index-file-name-suffix-71 # sources "${cur_dir}*.cpp" "${cur_dir}*.cxx" "${cur_dir}*.c++" "${cur_dir}*.cc" "${cur_dir}*.cp" "${cur_dir}*.c" "${cur_dir}*.i" "${cur_dir}*.ii" # headers "${cur_dir}*.h" "${cur_dir}*.h++" "${cur_dir}*.hpp" "${cur_dir}*.hxx" "${cur_dir}*.hh" "${cur_dir}*.inl" "${cur_dir}*.inc" "${cur_dir}*.ipp" "${cur_dir}*.ixx" "${cur_dir}*.txx" "${cur_dir}*.tpp" "${cur_dir}*.tcc" "${cur_dir}*.tpl" ${additional_file_extensions}) SET(${result} ${${result}} ${found_sources}) # set the proper filters ucm_trim_front_words("${cur_dir}" cur_dir "${trim}") # replacing forward slashes with back slashes so filters can be generated (back slash used in parsing...) STRING(REPLACE "/" "\\" FILTERS "${cur_dir}") SOURCE_GROUP("${FILTERS}" FILES ${found_sources}) endforeach() if(${rec}) foreach(cur_dir ${dirs}) ucm_dir_list("${cur_dir}" subdirs) foreach(subdir ${subdirs}) ucm_add_dir_impl(${result} ${rec} ${trim} "${cur_dir}/${subdir}" "${additional_ext}") endforeach() endforeach() endif() endmacro() # ucm_add_dirs # Adds all files from directories traversing them recursively to a list of sources # and generates filters according to their location (accepts relative paths only). # Also this macro trims X times the front word from the filter string for visual studio filters. macro(ucm_add_dirs) cmake_parse_arguments(ARG "RECURSIVE" "TO;FILTER_POP" "ADDITIONAL_EXT" ${ARGN}) if(${ARG_TO} STREQUAL "") message(FATAL_ERROR "Need to pass TO and a variable name to ucm_add_dirs()") endif() if("${ARG_FILTER_POP}" STREQUAL "") set(ARG_FILTER_POP 0) endif() ucm_add_dir_impl(${ARG_TO} ${ARG_RECURSIVE} ${ARG_FILTER_POP} "${ARG_UNPARSED_ARGUMENTS}" "${ARG_ADDITIONAL_EXT}") endmacro() # ucm_add_target # Adds a target eligible for cotiring - unity build and/or precompiled header macro(ucm_add_target) cmake_parse_arguments(ARG "UNITY" "NAME;TYPE;PCH_FILE;CPP_PER_UNITY" "UNITY_EXCLUDED;SOURCES" ${ARGN}) if(NOT "${ARG_UNPARSED_ARGUMENTS}" STREQUAL "") message(FATAL_ERROR "Unrecognized options passed to ucm_add_target()") endif() if("${ARG_NAME}" STREQUAL "") message(FATAL_ERROR "Need to pass NAME and a name for the target to ucm_add_target()") endif() set(valid_types EXECUTABLE STATIC SHARED MODULE) list(FIND valid_types "${ARG_TYPE}" is_type_valid) if(${is_type_valid} STREQUAL "-1") message(FATAL_ERROR "Need to pass TYPE and the type for the target [EXECUTABLE/STATIC/SHARED/MODULE] to ucm_add_target()") endif() if("${ARG_SOURCES}" STREQUAL "") message(FATAL_ERROR "Need to pass SOURCES and a list of source files to ucm_add_target()") endif() # init with the global unity flag set(do_unity ${UCM_UNITY_BUILD}) # check the UNITY argument if(NOT ARG_UNITY) set(do_unity FALSE) endif() # if target is excluded through the exclusion list list(FIND UCM_UNITY_BUILD_EXCLUDE_TARGETS ${ARG_NAME} is_target_excluded) if(NOT ${is_target_excluded} STREQUAL "-1") set(do_unity FALSE) endif() # unity build only for targets with > 1 source file (otherwise there will be an additional unnecessary target) if(do_unity) # optimization ucm_count_sources(${ARG_SOURCES} RESULT num_sources) if(${num_sources} LESS 2) set(do_unity FALSE) endif() endif() set(wanted_cotire ${do_unity}) # if cotire cannot be used if(do_unity AND NOT ucm_with_cotire) set(do_unity FALSE) endif() # inform the developer that the current target might benefit from a unity build if(NOT ARG_UNITY AND ${UCM_UNITY_BUILD}) ucm_count_sources(${ARG_SOURCES} RESULT num_sources) if(${num_sources} GREATER 1) message(AUTHOR_WARNING "Target '${ARG_NAME}' may benefit from a unity build.\nIt has ${num_sources} sources - enable with UNITY flag") endif() endif() # prepare for the unity build set(orig_target ${ARG_NAME}) if(do_unity) # the original target will be added with a different name than the requested set(orig_target ${ARG_NAME}_ORIGINAL) # exclude requested files from unity build of the current target foreach(excluded_file "${ARG_UNITY_EXCLUDED}") set_source_files_properties(${excluded_file} PROPERTIES COTIRE_EXCLUDED TRUE) endforeach() endif() # add the original target if(${ARG_TYPE} STREQUAL "EXECUTABLE") add_executable(${orig_target} ${ARG_SOURCES}) else() add_library(${orig_target} ${ARG_TYPE} ${ARG_SOURCES}) endif() if(do_unity) # set the number of unity cpp files to be used for the unity target if(NOT "${ARG_CPP_PER_UNITY}" STREQUAL "") set_property(TARGET ${orig_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "${ARG_CPP_PER_UNITY}") else() set_property(TARGET ${orig_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "100") endif() if(NOT "${ARG_PCH_FILE}" STREQUAL "") set_target_properties(${orig_target} PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "${ARG_PCH_FILE}") else() set_target_properties(${orig_target} PROPERTIES COTIRE_ENABLE_PRECOMPILED_HEADER FALSE) endif() # add a unity target for the original one with the name intended for the original set_target_properties(${orig_target} PROPERTIES COTIRE_UNITY_TARGET_NAME ${ARG_NAME}) # this is the library call that does the magic cotire(${orig_target}) set_target_properties(clean_cotire PROPERTIES FOLDER "CMakePredefinedTargets") # disable the original target and enable the unity one get_target_property(unity_target_name ${orig_target} COTIRE_UNITY_TARGET_NAME) set_target_properties(${orig_target} PROPERTIES EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_DEFAULT_BUILD 1) set_target_properties(${unity_target_name} PROPERTIES EXCLUDE_FROM_ALL 0 EXCLUDE_FROM_DEFAULT_BUILD 0) # also set the name of the target output as the original one set_target_properties(${unity_target_name} PROPERTIES OUTPUT_NAME ${ARG_NAME}) if(UCM_NO_COTIRE_FOLDER) # reset the folder property so all unity targets dont end up in a single folder in the solution explorer of VS set_target_properties(${unity_target_name} PROPERTIES FOLDER "") endif() set_target_properties(all_unity PROPERTIES FOLDER "CMakePredefinedTargets") elseif(NOT "${ARG_PCH_FILE}" STREQUAL "") set(wanted_cotire TRUE) if(ucm_with_cotire) set_target_properties(${orig_target} PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE) set_target_properties(${orig_target} PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "${ARG_PCH_FILE}") cotire(${orig_target}) set_target_properties(clean_cotire PROPERTIES FOLDER "CMakePredefinedTargets") endif() endif() # print a message if the target was requested to be cotired but it couldn't if(wanted_cotire AND NOT ucm_with_cotire) if(NOT COMMAND cotire) message(AUTHOR_WARNING "Target \"${ARG_NAME}\" not cotired because cotire isn't loaded") else() message(AUTHOR_WARNING "Target \"${ARG_NAME}\" not cotired because cotire is older than the required version") endif() endif() endmacro() dcm2niix-1.0.20171215/console/ujpeg.cpp000066400000000000000000000755511322051203000172510ustar00rootroot00000000000000// NanoJPEG -- KeyJ's Tiny Baseline JPEG Decoder // version 1.3.5 (2016-11-14) // Copyright (c) 2009-2016 Martin J. Fiedler // published under the terms of the MIT license // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. /////////////////////////////////////////////////////////////////////////////// // DOCUMENTATION SECTION // // read this if you want to know what this is all about // /////////////////////////////////////////////////////////////////////////////// // INTRODUCTION // ============ // // This is a minimal decoder for baseline JPEG images. It accepts memory dumps // of JPEG files as input and generates either 8-bit grayscale or packed 24-bit // RGB images as output. It does not parse JFIF or Exif headers; all JPEG files // are assumed to be either grayscale or YCbCr. CMYK or other color spaces are // not supported. All YCbCr subsampling schemes with power-of-two ratios are // supported, as are restart intervals. Progressive or lossless JPEG is not // supported. // Summed up, NanoJPEG should be able to decode all images from digital cameras // and most common forms of other non-progressive JPEG images. // The decoder is not optimized for speed, it's optimized for simplicity and // small code. Image quality should be at a reasonable level. A bicubic chroma // upsampling filter ensures that subsampled YCbCr images are rendered in // decent quality. The decoder is not meant to deal with broken JPEG files in // a graceful manner; if anything is wrong with the bitstream, decoding will // simply fail. // The code should work with every modern C compiler without problems and // should not emit any warnings. It uses only (at least) 32-bit integer // arithmetic and is supposed to be endianness independent and 64-bit clean. // However, it is not thread-safe. // COMPILE-TIME CONFIGURATION // ========================== // // The following aspects of NanoJPEG can be controlled with preprocessor // defines: // // _NJ_EXAMPLE_PROGRAM = Compile a main() function with an example // program. // _NJ_INCLUDE_HEADER_ONLY = Don't compile anything, just act as a header // file for NanoJPEG. Example: // #define _NJ_INCLUDE_HEADER_ONLY // #include "nanojpeg.c" // int main(void) { // njInit(); // // your code here // njDone(); // } // NJ_USE_LIBC=1 = Use the malloc(), free(), memset() and memcpy() // functions from the standard C library (default). // NJ_USE_LIBC=0 = Don't use the standard C library. In this mode, // external functions njAlloc(), njFreeMem(), // njFillMem() and njCopyMem() need to be defined // and implemented somewhere. // NJ_USE_WIN32=0 = Normal mode (default). // NJ_USE_WIN32=1 = If compiling with MSVC for Win32 and // NJ_USE_LIBC=0, NanoJPEG will use its own // implementations of the required C library // functions (default if compiling with MSVC and // NJ_USE_LIBC=0). // NJ_CHROMA_FILTER=1 = Use the bicubic chroma upsampling filter // (default). // NJ_CHROMA_FILTER=0 = Use simple pixel repetition for chroma upsampling // (bad quality, but faster and less code). // API // === // // For API documentation, read the "header section" below. // EXAMPLE // ======= // // A few pages below, you can find an example program that uses NanoJPEG to // convert JPEG files into PGM or PPM. To compile it, use something like // gcc -O3 -D_NJ_EXAMPLE_PROGRAM -o nanojpeg nanojpeg.c // You may also add -std=c99 -Wall -Wextra -pedantic -Werror, if you want :) // The only thing you might need is -Wno-shift-negative-value, because this // code relies on the target machine using two's complement arithmetic, but // the C standard does not, even though *any* practically useful machine // nowadays uses two's complement. /////////////////////////////////////////////////////////////////////////////// // HEADER SECTION // // copy and pase this into nanojpeg.h if you want // /////////////////////////////////////////////////////////////////////////////// #ifndef _NANOJPEG_H #define _NANOJPEG_H // nj_result_t: Result codes for njDecode(). typedef enum _nj_result { NJ_OK = 0, // no error, decoding successful NJ_NO_JPEG, // not a JPEG file NJ_UNSUPPORTED, // unsupported format NJ_OUT_OF_MEM, // out of memory NJ_INTERNAL_ERR, // internal error NJ_SYNTAX_ERROR, // syntax error __NJ_FINISHED, // used internally, will never be reported } nj_result_t; // njInit: Initialize NanoJPEG. // For safety reasons, this should be called at least one time before using // using any of the other NanoJPEG functions. void njInit(void); // njDecode: Decode a JPEG image. // Decodes a memory dump of a JPEG file into internal buffers. // Parameters: // jpeg = The pointer to the memory dump. // size = The size of the JPEG file. // Return value: The error code in case of failure, or NJ_OK (zero) on success. nj_result_t njDecode(const void* jpeg, const int size); // njGetWidth: Return the width (in pixels) of the most recently decoded // image. If njDecode() failed, the result of njGetWidth() is undefined. int njGetWidth(void); // njGetHeight: Return the height (in pixels) of the most recently decoded // image. If njDecode() failed, the result of njGetHeight() is undefined. int njGetHeight(void); // njIsColor: Return 1 if the most recently decoded image is a color image // (RGB) or 0 if it is a grayscale image. If njDecode() failed, the result // of njGetWidth() is undefined. int njIsColor(void); // njGetImage: Returns the decoded image data. // Returns a pointer to the most recently image. The memory layout it byte- // oriented, top-down, without any padding between lines. Pixels of color // images will be stored as three consecutive bytes for the red, green and // blue channels. This data format is thus compatible with the PGM or PPM // file formats and the OpenGL texture formats GL_LUMINANCE8 or GL_RGB8. // If njDecode() failed, the result of njGetImage() is undefined. unsigned char* njGetImage(void); // njGetImageSize: Returns the size (in bytes) of the image data returned // by njGetImage(). If njDecode() failed, the result of njGetImageSize() is // undefined. int njGetImageSize(void); // njDone: Uninitialize NanoJPEG. // Resets NanoJPEG's internal state and frees all memory that has been // allocated at run-time by NanoJPEG. It is still possible to decode another // image after a njDone() call. void njDone(void); #endif//_NANOJPEG_H /////////////////////////////////////////////////////////////////////////////// // CONFIGURATION SECTION // // adjust the default settings for the NJ_ defines here // /////////////////////////////////////////////////////////////////////////////// #ifndef NJ_USE_LIBC #define NJ_USE_LIBC 1 #endif #ifndef NJ_USE_WIN32 #ifdef _MSC_VER #define NJ_USE_WIN32 (!NJ_USE_LIBC) #else #define NJ_USE_WIN32 0 #endif #endif #ifndef NJ_CHROMA_FILTER #define NJ_CHROMA_FILTER 1 #endif /////////////////////////////////////////////////////////////////////////////// // EXAMPLE PROGRAM // // just define _NJ_EXAMPLE_PROGRAM to compile this (requires NJ_USE_LIBC) // /////////////////////////////////////////////////////////////////////////////// #ifdef _NJ_EXAMPLE_PROGRAM #include #include #include int main(int argc, char* argv[]) { int size; char *buf; FILE *f; if (argc < 2) { printf("Usage: %s []\n", argv[0]); return 2; } f = fopen(argv[1], "rb"); if (!f) { printf("Error opening the input file.\n"); return 1; } fseek(f, 0, SEEK_END); size = (int) ftell(f); buf = (char*) malloc(size); fseek(f, 0, SEEK_SET); size = (int) fread(buf, 1, size, f); fclose(f); njInit(); if (njDecode(buf, size)) { free((void*)buf); printf("Error decoding the input file.\n"); return 1; } free((void*)buf); f = fopen((argc > 2) ? argv[2] : (njIsColor() ? "nanojpeg_out.ppm" : "nanojpeg_out.pgm"), "wb"); if (!f) { printf("Error opening the output file.\n"); return 1; } fprintf(f, "P%d\n%d %d\n255\n", njIsColor() ? 6 : 5, njGetWidth(), njGetHeight()); fwrite(njGetImage(), 1, njGetImageSize(), f); fclose(f); njDone(); return 0; } #endif /////////////////////////////////////////////////////////////////////////////// // IMPLEMENTATION SECTION // // you may stop reading here // /////////////////////////////////////////////////////////////////////////////// #ifndef _NJ_INCLUDE_HEADER_ONLY #ifdef _MSC_VER #define NJ_INLINE static __inline #define NJ_FORCE_INLINE static __forceinline #else #define NJ_INLINE static inline #define NJ_FORCE_INLINE static inline #endif #if NJ_USE_LIBC #include #include #define njAllocMem malloc #define njFreeMem free #define njFillMem memset #define njCopyMem memcpy #elif NJ_USE_WIN32 #include #define njAllocMem(size) ((void*) LocalAlloc(LMEM_FIXED, (SIZE_T)(size))) #define njFreeMem(block) ((void) LocalFree((HLOCAL) block)) NJ_INLINE void njFillMem(void* block, unsigned char value, int count) { __asm { mov edi, block mov al, value mov ecx, count rep stosb } } NJ_INLINE void njCopyMem(void* dest, const void* src, int count) { __asm { mov edi, dest mov esi, src mov ecx, count rep movsb } } #else extern void* njAllocMem(int size); extern void njFreeMem(void* block); extern void njFillMem(void* block, unsigned char byte, int size); extern void njCopyMem(void* dest, const void* src, int size); #endif typedef struct _nj_code { unsigned char bits, code; } nj_vlc_code_t; typedef struct _nj_cmp { int cid; int ssx, ssy; int width, height; int stride; int qtsel; int actabsel, dctabsel; int dcpred; unsigned char *pixels; } nj_component_t; typedef struct _nj_ctx { nj_result_t error; const unsigned char *pos; int size; int length; int width, height; int mbwidth, mbheight; int mbsizex, mbsizey; int ncomp; nj_component_t comp[3]; int qtused, qtavail; unsigned char qtab[4][64]; nj_vlc_code_t vlctab[4][65536]; int buf, bufbits; int block[64]; int rstinterval; unsigned char *rgb; } nj_context_t; static nj_context_t nj; static const char njZZ[64] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 }; NJ_FORCE_INLINE unsigned char njClip(const int x) { return (x < 0) ? 0 : ((x > 0xFF) ? 0xFF : (unsigned char) x); } #define W1 2841 #define W2 2676 #define W3 2408 #define W5 1609 #define W6 1108 #define W7 565 NJ_INLINE void njRowIDCT(int* blk) { int x0, x1, x2, x3, x4, x5, x6, x7, x8; if (!((x1 = blk[4] << 11) | (x2 = blk[6]) | (x3 = blk[2]) | (x4 = blk[1]) | (x5 = blk[7]) | (x6 = blk[5]) | (x7 = blk[3]))) { blk[0] = blk[1] = blk[2] = blk[3] = blk[4] = blk[5] = blk[6] = blk[7] = blk[0] << 3; return; } x0 = (blk[0] << 11) + 128; x8 = W7 * (x4 + x5); x4 = x8 + (W1 - W7) * x4; x5 = x8 - (W1 + W7) * x5; x8 = W3 * (x6 + x7); x6 = x8 - (W3 - W5) * x6; x7 = x8 - (W3 + W5) * x7; x8 = x0 + x1; x0 -= x1; x1 = W6 * (x3 + x2); x2 = x1 - (W2 + W6) * x2; x3 = x1 + (W2 - W6) * x3; x1 = x4 + x6; x4 -= x6; x6 = x5 + x7; x5 -= x7; x7 = x8 + x3; x8 -= x3; x3 = x0 + x2; x0 -= x2; x2 = (181 * (x4 + x5) + 128) >> 8; x4 = (181 * (x4 - x5) + 128) >> 8; blk[0] = (x7 + x1) >> 8; blk[1] = (x3 + x2) >> 8; blk[2] = (x0 + x4) >> 8; blk[3] = (x8 + x6) >> 8; blk[4] = (x8 - x6) >> 8; blk[5] = (x0 - x4) >> 8; blk[6] = (x3 - x2) >> 8; blk[7] = (x7 - x1) >> 8; } NJ_INLINE void njColIDCT(const int* blk, unsigned char *out, int stride) { int x0, x1, x2, x3, x4, x5, x6, x7, x8; if (!((x1 = blk[8*4] << 8) | (x2 = blk[8*6]) | (x3 = blk[8*2]) | (x4 = blk[8*1]) | (x5 = blk[8*7]) | (x6 = blk[8*5]) | (x7 = blk[8*3]))) { x1 = njClip(((blk[0] + 32) >> 6) + 128); for (x0 = 8; x0; --x0) { *out = (unsigned char) x1; out += stride; } return; } x0 = (blk[0] << 8) + 8192; x8 = W7 * (x4 + x5) + 4; x4 = (x8 + (W1 - W7) * x4) >> 3; x5 = (x8 - (W1 + W7) * x5) >> 3; x8 = W3 * (x6 + x7) + 4; x6 = (x8 - (W3 - W5) * x6) >> 3; x7 = (x8 - (W3 + W5) * x7) >> 3; x8 = x0 + x1; x0 -= x1; x1 = W6 * (x3 + x2) + 4; x2 = (x1 - (W2 + W6) * x2) >> 3; x3 = (x1 + (W2 - W6) * x3) >> 3; x1 = x4 + x6; x4 -= x6; x6 = x5 + x7; x5 -= x7; x7 = x8 + x3; x8 -= x3; x3 = x0 + x2; x0 -= x2; x2 = (181 * (x4 + x5) + 128) >> 8; x4 = (181 * (x4 - x5) + 128) >> 8; *out = njClip(((x7 + x1) >> 14) + 128); out += stride; *out = njClip(((x3 + x2) >> 14) + 128); out += stride; *out = njClip(((x0 + x4) >> 14) + 128); out += stride; *out = njClip(((x8 + x6) >> 14) + 128); out += stride; *out = njClip(((x8 - x6) >> 14) + 128); out += stride; *out = njClip(((x0 - x4) >> 14) + 128); out += stride; *out = njClip(((x3 - x2) >> 14) + 128); out += stride; *out = njClip(((x7 - x1) >> 14) + 128); } #define njThrow(e) do { nj.error = e; return; } while (0) #define njCheckError() do { if (nj.error) return; } while (0) static int njShowBits(int bits) { unsigned char newbyte; if (!bits) return 0; while (nj.bufbits < bits) { if (nj.size <= 0) { nj.buf = (nj.buf << 8) | 0xFF; nj.bufbits += 8; continue; } newbyte = *nj.pos++; nj.size--; nj.bufbits += 8; nj.buf = (nj.buf << 8) | newbyte; if (newbyte == 0xFF) { if (nj.size) { unsigned char marker = *nj.pos++; nj.size--; switch (marker) { case 0x00: case 0xFF: break; case 0xD9: nj.size = 0; break; default: if ((marker & 0xF8) != 0xD0) nj.error = NJ_SYNTAX_ERROR; else { nj.buf = (nj.buf << 8) | marker; nj.bufbits += 8; } } } else nj.error = NJ_SYNTAX_ERROR; } } return (nj.buf >> (nj.bufbits - bits)) & ((1 << bits) - 1); } NJ_INLINE void njSkipBits(int bits) { if (nj.bufbits < bits) (void) njShowBits(bits); nj.bufbits -= bits; } NJ_INLINE int njGetBits(int bits) { int res = njShowBits(bits); njSkipBits(bits); return res; } NJ_INLINE void njByteAlign(void) { nj.bufbits &= 0xF8; } static void njSkip(int count) { nj.pos += count; nj.size -= count; nj.length -= count; if (nj.size < 0) nj.error = NJ_SYNTAX_ERROR; } NJ_INLINE unsigned short njDecode16(const unsigned char *pos) { return (pos[0] << 8) | pos[1]; } static void njDecodeLength(void) { if (nj.size < 2) njThrow(NJ_SYNTAX_ERROR); nj.length = njDecode16(nj.pos); if (nj.length > nj.size) njThrow(NJ_SYNTAX_ERROR); njSkip(2); } NJ_INLINE void njSkipMarker(void) { njDecodeLength(); njSkip(nj.length); } NJ_INLINE void njDecodeSOF(void) { int i, ssxmax = 0, ssymax = 0; nj_component_t* c; njDecodeLength(); njCheckError(); if (nj.length < 9) njThrow(NJ_SYNTAX_ERROR); if (nj.pos[0] != 8) njThrow(NJ_UNSUPPORTED); nj.height = njDecode16(nj.pos+1); nj.width = njDecode16(nj.pos+3); if (!nj.width || !nj.height) njThrow(NJ_SYNTAX_ERROR); nj.ncomp = nj.pos[5]; njSkip(6); switch (nj.ncomp) { case 1: case 3: break; default: njThrow(NJ_UNSUPPORTED); } if (nj.length < (nj.ncomp * 3)) njThrow(NJ_SYNTAX_ERROR); for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) { c->cid = nj.pos[0]; if (!(c->ssx = nj.pos[1] >> 4)) njThrow(NJ_SYNTAX_ERROR); if (c->ssx & (c->ssx - 1)) njThrow(NJ_UNSUPPORTED); // non-power of two if (!(c->ssy = nj.pos[1] & 15)) njThrow(NJ_SYNTAX_ERROR); if (c->ssy & (c->ssy - 1)) njThrow(NJ_UNSUPPORTED); // non-power of two if ((c->qtsel = nj.pos[2]) & 0xFC) njThrow(NJ_SYNTAX_ERROR); njSkip(3); nj.qtused |= 1 << c->qtsel; if (c->ssx > ssxmax) ssxmax = c->ssx; if (c->ssy > ssymax) ssymax = c->ssy; } if (nj.ncomp == 1) { c = nj.comp; c->ssx = c->ssy = ssxmax = ssymax = 1; } nj.mbsizex = ssxmax << 3; nj.mbsizey = ssymax << 3; nj.mbwidth = (nj.width + nj.mbsizex - 1) / nj.mbsizex; nj.mbheight = (nj.height + nj.mbsizey - 1) / nj.mbsizey; for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) { c->width = (nj.width * c->ssx + ssxmax - 1) / ssxmax; c->height = (nj.height * c->ssy + ssymax - 1) / ssymax; c->stride = nj.mbwidth * c->ssx << 3; if (((c->width < 3) && (c->ssx != ssxmax)) || ((c->height < 3) && (c->ssy != ssymax))) njThrow(NJ_UNSUPPORTED); if (!(c->pixels = (unsigned char*) njAllocMem(c->stride * nj.mbheight * c->ssy << 3))) njThrow(NJ_OUT_OF_MEM); } if (nj.ncomp == 3) { nj.rgb = (unsigned char*) njAllocMem(nj.width * nj.height * nj.ncomp); if (!nj.rgb) njThrow(NJ_OUT_OF_MEM); } njSkip(nj.length); } NJ_INLINE void njDecodeDHT(void) { int codelen, currcnt, remain, spread, i, j; nj_vlc_code_t *vlc; static unsigned char counts[16]; njDecodeLength(); njCheckError(); while (nj.length >= 17) { i = nj.pos[0]; if (i & 0xEC) njThrow(NJ_SYNTAX_ERROR); if (i & 0x02) njThrow(NJ_UNSUPPORTED); i = (i | (i >> 3)) & 3; // combined DC/AC + tableid value for (codelen = 1; codelen <= 16; ++codelen) counts[codelen - 1] = nj.pos[codelen]; njSkip(17); vlc = &nj.vlctab[i][0]; remain = spread = 65536; for (codelen = 1; codelen <= 16; ++codelen) { spread >>= 1; currcnt = counts[codelen - 1]; if (!currcnt) continue; if (nj.length < currcnt) njThrow(NJ_SYNTAX_ERROR); remain -= currcnt << (16 - codelen); if (remain < 0) njThrow(NJ_SYNTAX_ERROR); for (i = 0; i < currcnt; ++i) { register unsigned char code = nj.pos[i]; for (j = spread; j; --j) { vlc->bits = (unsigned char) codelen; vlc->code = code; ++vlc; } } njSkip(currcnt); } while (remain--) { vlc->bits = 0; ++vlc; } } if (nj.length) njThrow(NJ_SYNTAX_ERROR); } NJ_INLINE void njDecodeDQT(void) { int i; unsigned char *t; njDecodeLength(); njCheckError(); while (nj.length >= 65) { i = nj.pos[0]; if (i & 0xFC) njThrow(NJ_SYNTAX_ERROR); nj.qtavail |= 1 << i; t = &nj.qtab[i][0]; for (i = 0; i < 64; ++i) t[i] = nj.pos[i + 1]; njSkip(65); } if (nj.length) njThrow(NJ_SYNTAX_ERROR); } NJ_INLINE void njDecodeDRI(void) { njDecodeLength(); njCheckError(); if (nj.length < 2) njThrow(NJ_SYNTAX_ERROR); nj.rstinterval = njDecode16(nj.pos); njSkip(nj.length); } static int njGetVLC(nj_vlc_code_t* vlc, unsigned char* code) { int value = njShowBits(16); int bits = vlc[value].bits; if (!bits) { nj.error = NJ_SYNTAX_ERROR; return 0; } njSkipBits(bits); value = vlc[value].code; if (code) *code = (unsigned char) value; bits = value & 15; if (!bits) return 0; value = njGetBits(bits); if (value < (1 << (bits - 1))) value += ((-1) << bits) + 1; return value; } NJ_INLINE void njDecodeBlock(nj_component_t* c, unsigned char* out) { unsigned char code = 0; int value, coef = 0; njFillMem(nj.block, 0, sizeof(nj.block)); c->dcpred += njGetVLC(&nj.vlctab[c->dctabsel][0], NULL); nj.block[0] = (c->dcpred) * nj.qtab[c->qtsel][0]; do { value = njGetVLC(&nj.vlctab[c->actabsel][0], &code); if (!code) break; // EOB if (!(code & 0x0F) && (code != 0xF0)) njThrow(NJ_SYNTAX_ERROR); coef += (code >> 4) + 1; if (coef > 63) njThrow(NJ_SYNTAX_ERROR); nj.block[(int) njZZ[coef]] = value * nj.qtab[c->qtsel][coef]; } while (coef < 63); for (coef = 0; coef < 64; coef += 8) njRowIDCT(&nj.block[coef]); for (coef = 0; coef < 8; ++coef) njColIDCT(&nj.block[coef], &out[coef], c->stride); } NJ_INLINE void njDecodeScan(void) { int i, mbx, mby, sbx, sby; int rstcount = nj.rstinterval, nextrst = 0; nj_component_t* c; njDecodeLength(); njCheckError(); if (nj.length < (4 + 2 * nj.ncomp)) njThrow(NJ_SYNTAX_ERROR); if (nj.pos[0] != nj.ncomp) njThrow(NJ_UNSUPPORTED); njSkip(1); for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) { if (nj.pos[0] != c->cid) njThrow(NJ_SYNTAX_ERROR); if (nj.pos[1] & 0xEE) njThrow(NJ_SYNTAX_ERROR); c->dctabsel = nj.pos[1] >> 4; c->actabsel = (nj.pos[1] & 1) | 2; njSkip(2); } if (nj.pos[0] || (nj.pos[1] != 63) || nj.pos[2]) njThrow(NJ_UNSUPPORTED); njSkip(nj.length); for (mbx = mby = 0;;) { for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) for (sby = 0; sby < c->ssy; ++sby) for (sbx = 0; sbx < c->ssx; ++sbx) { njDecodeBlock(c, &c->pixels[((mby * c->ssy + sby) * c->stride + mbx * c->ssx + sbx) << 3]); njCheckError(); } if (++mbx >= nj.mbwidth) { mbx = 0; if (++mby >= nj.mbheight) break; } if (nj.rstinterval && !(--rstcount)) { njByteAlign(); i = njGetBits(16); if (((i & 0xFFF8) != 0xFFD0) || ((i & 7) != nextrst)) njThrow(NJ_SYNTAX_ERROR); nextrst = (nextrst + 1) & 7; rstcount = nj.rstinterval; for (i = 0; i < 3; ++i) nj.comp[i].dcpred = 0; } } nj.error = __NJ_FINISHED; } #if NJ_CHROMA_FILTER #define CF4A (-9) #define CF4B (111) #define CF4C (29) #define CF4D (-3) #define CF3A (28) #define CF3B (109) #define CF3C (-9) #define CF3X (104) #define CF3Y (27) #define CF3Z (-3) #define CF2A (139) #define CF2B (-11) #define CF(x) njClip(((x) + 64) >> 7) NJ_INLINE void njUpsampleH(nj_component_t* c) { const int xmax = c->width - 3; unsigned char *out, *lin, *lout; int x, y; out = (unsigned char*) njAllocMem((c->width * c->height) << 1); if (!out) njThrow(NJ_OUT_OF_MEM); lin = c->pixels; lout = out; for (y = c->height; y; --y) { lout[0] = CF(CF2A * lin[0] + CF2B * lin[1]); lout[1] = CF(CF3X * lin[0] + CF3Y * lin[1] + CF3Z * lin[2]); lout[2] = CF(CF3A * lin[0] + CF3B * lin[1] + CF3C * lin[2]); for (x = 0; x < xmax; ++x) { lout[(x << 1) + 3] = CF(CF4A * lin[x] + CF4B * lin[x + 1] + CF4C * lin[x + 2] + CF4D * lin[x + 3]); lout[(x << 1) + 4] = CF(CF4D * lin[x] + CF4C * lin[x + 1] + CF4B * lin[x + 2] + CF4A * lin[x + 3]); } lin += c->stride; lout += c->width << 1; lout[-3] = CF(CF3A * lin[-1] + CF3B * lin[-2] + CF3C * lin[-3]); lout[-2] = CF(CF3X * lin[-1] + CF3Y * lin[-2] + CF3Z * lin[-3]); lout[-1] = CF(CF2A * lin[-1] + CF2B * lin[-2]); } c->width <<= 1; c->stride = c->width; njFreeMem((void*)c->pixels); c->pixels = out; } NJ_INLINE void njUpsampleV(nj_component_t* c) { const int w = c->width, s1 = c->stride, s2 = s1 + s1; unsigned char *out, *cin, *cout; int x, y; out = (unsigned char*) njAllocMem((c->width * c->height) << 1); if (!out) njThrow(NJ_OUT_OF_MEM); for (x = 0; x < w; ++x) { cin = &c->pixels[x]; cout = &out[x]; *cout = CF(CF2A * cin[0] + CF2B * cin[s1]); cout += w; *cout = CF(CF3X * cin[0] + CF3Y * cin[s1] + CF3Z * cin[s2]); cout += w; *cout = CF(CF3A * cin[0] + CF3B * cin[s1] + CF3C * cin[s2]); cout += w; cin += s1; for (y = c->height - 3; y; --y) { *cout = CF(CF4A * cin[-s1] + CF4B * cin[0] + CF4C * cin[s1] + CF4D * cin[s2]); cout += w; *cout = CF(CF4D * cin[-s1] + CF4C * cin[0] + CF4B * cin[s1] + CF4A * cin[s2]); cout += w; cin += s1; } cin += s1; *cout = CF(CF3A * cin[0] + CF3B * cin[-s1] + CF3C * cin[-s2]); cout += w; *cout = CF(CF3X * cin[0] + CF3Y * cin[-s1] + CF3Z * cin[-s2]); cout += w; *cout = CF(CF2A * cin[0] + CF2B * cin[-s1]); } c->height <<= 1; c->stride = c->width; njFreeMem((void*) c->pixels); c->pixels = out; } #else NJ_INLINE void njUpsample(nj_component_t* c) { int x, y, xshift = 0, yshift = 0; unsigned char *out, *lin, *lout; while (c->width < nj.width) { c->width <<= 1; ++xshift; } while (c->height < nj.height) { c->height <<= 1; ++yshift; } out = (unsigned char*) njAllocMem(c->width * c->height); if (!out) njThrow(NJ_OUT_OF_MEM); lin = c->pixels; lout = out; for (y = 0; y < c->height; ++y) { lin = &c->pixels[(y >> yshift) * c->stride]; for (x = 0; x < c->width; ++x) lout[x] = lin[x >> xshift]; lout += c->width; } c->stride = c->width; njFreeMem((void*) c->pixels); c->pixels = out; } #endif NJ_INLINE void njConvert(void) { int i; nj_component_t* c; for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) { #if NJ_CHROMA_FILTER while ((c->width < nj.width) || (c->height < nj.height)) { if (c->width < nj.width) njUpsampleH(c); njCheckError(); if (c->height < nj.height) njUpsampleV(c); njCheckError(); } #else if ((c->width < nj.width) || (c->height < nj.height)) njUpsample(c); #endif if ((c->width < nj.width) || (c->height < nj.height)) njThrow(NJ_INTERNAL_ERR); } if (nj.ncomp == 3) { // convert to RGB int x, yy; unsigned char *prgb = nj.rgb; const unsigned char *py = nj.comp[0].pixels; const unsigned char *pcb = nj.comp[1].pixels; const unsigned char *pcr = nj.comp[2].pixels; for (yy = nj.height; yy; --yy) { for (x = 0; x < nj.width; ++x) { register int y = py[x] << 8; register int cb = pcb[x] - 128; register int cr = pcr[x] - 128; *prgb++ = njClip((y + 359 * cr + 128) >> 8); *prgb++ = njClip((y - 88 * cb - 183 * cr + 128) >> 8); *prgb++ = njClip((y + 454 * cb + 128) >> 8); } py += nj.comp[0].stride; pcb += nj.comp[1].stride; pcr += nj.comp[2].stride; } } else if (nj.comp[0].width != nj.comp[0].stride) { // grayscale -> only remove stride unsigned char *pin = &nj.comp[0].pixels[nj.comp[0].stride]; unsigned char *pout = &nj.comp[0].pixels[nj.comp[0].width]; int y; for (y = nj.comp[0].height - 1; y; --y) { njCopyMem(pout, pin, nj.comp[0].width); pin += nj.comp[0].stride; pout += nj.comp[0].width; } nj.comp[0].stride = nj.comp[0].width; } } void njInit(void) { njFillMem(&nj, 0, sizeof(nj_context_t)); } void njDone(void) { int i; for (i = 0; i < 3; ++i) if (nj.comp[i].pixels) njFreeMem((void*) nj.comp[i].pixels); if (nj.rgb) njFreeMem((void*) nj.rgb); njInit(); } nj_result_t njDecode(const void* jpeg, const int size) { njDone(); nj.pos = (const unsigned char*) jpeg; nj.size = size & 0x7FFFFFFF; if (nj.size < 2) return NJ_NO_JPEG; if ((nj.pos[0] ^ 0xFF) | (nj.pos[1] ^ 0xD8)) return NJ_NO_JPEG; njSkip(2); while (!nj.error) { if ((nj.size < 2) || (nj.pos[0] != 0xFF)) return NJ_SYNTAX_ERROR; njSkip(2); switch (nj.pos[-1]) { case 0xC0: njDecodeSOF(); break; case 0xC4: njDecodeDHT(); break; case 0xDB: njDecodeDQT(); break; case 0xDD: njDecodeDRI(); break; case 0xDA: njDecodeScan(); break; case 0xFE: njSkipMarker(); break; default: if ((nj.pos[-1] & 0xF0) == 0xE0) njSkipMarker(); else return NJ_UNSUPPORTED; } } if (nj.error != __NJ_FINISHED) return nj.error; nj.error = NJ_OK; njConvert(); return nj.error; } int njGetWidth(void) { return nj.width; } int njGetHeight(void) { return nj.height; } int njIsColor(void) { return (nj.ncomp != 1); } unsigned char* njGetImage(void) { return (nj.ncomp == 1) ? nj.comp[0].pixels : nj.rgb; } int njGetImageSize(void) { return nj.width * nj.height * nj.ncomp; } #endif // _NJ_INCLUDE_HEADER_ONLYdcm2niix-1.0.20171215/console/ujpeg.h000066400000000000000000000046431322051203000167100ustar00rootroot00000000000000#ifndef _NANOJPEG_H #define _NANOJPEG_H // nj_result_t: Result codes for njDecode(). typedef enum _nj_result { NJ_OK = 0, // no error, decoding successful NJ_NO_JPEG, // not a JPEG file NJ_UNSUPPORTED, // unsupported format NJ_OUT_OF_MEM, // out of memory NJ_INTERNAL_ERR, // internal error NJ_SYNTAX_ERROR, // syntax error __NJ_FINISHED, // used internally, will never be reported } nj_result_t; // njInit: Initialize NanoJPEG. // For safety reasons, this should be called at least one time before using // using any of the other NanoJPEG functions. void njInit(void); // njDecode: Decode a JPEG image. // Decodes a memory dump of a JPEG file into internal buffers. // Parameters: // jpeg = The pointer to the memory dump. // size = The size of the JPEG file. // Return value: The error code in case of failure, or NJ_OK (zero) on success. nj_result_t njDecode(const void* jpeg, const int size); // njGetWidth: Return the width (in pixels) of the most recently decoded // image. If njDecode() failed, the result of njGetWidth() is undefined. int njGetWidth(void); // njGetHeight: Return the height (in pixels) of the most recently decoded // image. If njDecode() failed, the result of njGetHeight() is undefined. int njGetHeight(void); // njIsColor: Return 1 if the most recently decoded image is a color image // (RGB) or 0 if it is a grayscale image. If njDecode() failed, the result // of njGetWidth() is undefined. int njIsColor(void); // njGetImage: Returns the decoded image data. // Returns a pointer to the most recently image. The memory layout it byte- // oriented, top-down, without any padding between lines. Pixels of color // images will be stored as three consecutive bytes for the red, green and // blue channels. This data format is thus compatible with the PGM or PPM // file formats and the OpenGL texture formats GL_LUMINANCE8 or GL_RGB8. // If njDecode() failed, the result of njGetImage() is undefined. unsigned char* njGetImage(void); // njGetImageSize: Returns the size (in bytes) of the image data returned // by njGetImage(). If njDecode() failed, the result of njGetImageSize() is // undefined. int njGetImageSize(void); // njDone: Uninitialize NanoJPEG. // Resets NanoJPEG's internal state and frees all memory that has been // allocated at run-time by NanoJPEG. It is still possible to decode another // image after a njDone() call. void njDone(void); #endif//_NANOJPEG_H dcm2niix-1.0.20171215/console/unused/000077500000000000000000000000001322051203000167215ustar00rootroot00000000000000dcm2niix-1.0.20171215/console/unused/untgz.c000066400000000000000000000726551322051203000202530ustar00rootroot00000000000000#include "untgz.h" /* external functions and related types and constants */ #include /* fprintf() */ #include /* malloc(), free() */ #include /* strerror(), strcmp(), strlen(), memcpy() */ #include /* errno */ #include /* open() */ #include /* read(), write(), close(), chown(), unlink() */ #include #include /* stat(), chmod() */ #include /* utime() */ #include "zlib.h" /* inflateBackInit(), inflateBack(), */ /* inflateBackEnd(), crc32() */ #if defined(ZLIB_CONST) && !defined(z_const) //for backwards compatibility with zlib 1.2.5 (included with OSX) # define z_const const #else # define z_const #endif /* function declaration */ #define local static /* buffer constants */ #define SIZE 32768U /* input and output buffer sizes */ #define PIECE 16384 /* limits i/o chunks for 16-bit int case */ /* structure for infback() to pass to input function in() -- it maintains the input file and a buffer of size SIZE */ struct ind { int infile; unsigned char *inbuf; }; /* Load input buffer, assumed to be empty, and return bytes loaded and a pointer to them. read() is called until the buffer is full, or until it returns end-of-file or error. Return 0 on error. */ local unsigned in(void *in_desc, z_const unsigned char **buf) { int ret; unsigned len; unsigned char *next; struct ind *me = (struct ind *)in_desc; next = me->inbuf; *buf = next; len = 0; do { ret = PIECE; if ((unsigned)ret > SIZE - len) ret = (int)(SIZE - len); ret = (int)read(me->infile, next, ret); if (ret == -1) { len = 0; break; } next += ret; len += ret; } while (ret != 0 && len < SIZE); return len; } /* structure for infback() to pass to output function out() -- it maintains the output file, a running CRC-32 check on the output and the total number of bytes output, both for checking against the gzip trailer. (The length in the gzip trailer is stored modulo 2^32, so it's ok if a long is 32 bits and the output is greater than 4 GB.) */ struct outd { int outfile; int check; /* true if checking crc and total */ unsigned long crc; unsigned long total; }; /* Write output buffer and update the CRC-32 and total bytes written. write() is called until all of the output is written or an error is encountered. On success out() returns 0. For a write failure, out() returns 1. If the output file descriptor is -1, then nothing is written. */ local int out(void *out_desc, unsigned char *buf, unsigned len) { int ret; struct outd *me = (struct outd *)out_desc; if (me->check) { me->crc = crc32(me->crc, buf, len); me->total += len; } if (me->outfile != -1) do { ret = PIECE; if ((unsigned)ret > len) ret = (int)len; ret = (int)write(me->outfile, buf, ret); if (ret == -1) return 1; buf += ret; len -= ret; } while (len != 0); return 0; } /* next input byte macro for use inside lunpipe() and gunpipe() */ #define NEXT() (have ? 0 : (have = in(indp, &next)), \ last = have ? (have--, (int)(*next++)) : -1) /* memory for gunpipe() and lunpipe() -- the first 256 entries of prefix[] and suffix[] are never used, could have offset the index, but it's faster to waste the memory */ unsigned char inbuf[SIZE]; /* input buffer */ unsigned char outbuf[SIZE]; /* output buffer */ unsigned short prefix[65536]; /* index to LZW prefix string */ unsigned char suffix[65536]; /* one-character LZW suffix */ unsigned char match[65280 + 2]; /* buffer for reversed match or gzip 32K sliding window */ /* throw out what's left in the current bits byte buffer (this is a vestigial aspect of the compressed data format derived from an implementation that made use of a special VAX machine instruction!) */ #define FLUSHCODE() \ do { \ left = 0; \ rem = 0; \ if (chunk > have) { \ chunk -= have; \ have = 0; \ if (NEXT() == -1) \ break; \ chunk--; \ if (chunk > have) { \ chunk = have = 0; \ break; \ } \ } \ have -= chunk; \ next += chunk; \ chunk = 0; \ } while (0) /* Decompress a compress (LZW) file from indp to outfile. The compress magic header (two bytes) has already been read and verified. There are have bytes of buffered input at next. strm is used for passing error information back to gunpipe(). lunpipe() will return Z_OK on success, Z_BUF_ERROR for an unexpected end of file, read error, or write error (a write error indicated by strm->next_in not equal to Z_NULL), or Z_DATA_ERROR for invalid input. */ local int lunpipe(unsigned have, z_const unsigned char *next, struct ind *indp, int outfile, z_stream *strm) { int last; /* last byte read by NEXT(), or -1 if EOF */ unsigned chunk; /* bytes left in current chunk */ int left; /* bits left in rem */ unsigned rem; /* unused bits from input */ int bits; /* current bits per code */ unsigned code; /* code, table traversal index */ unsigned mask; /* mask for current bits codes */ int max; /* maximum bits per code for this stream */ unsigned flags; /* compress flags, then block compress flag */ unsigned end; /* last valid entry in prefix/suffix tables */ unsigned temp; /* current code */ unsigned prev; /* previous code */ unsigned final; /* last character written for previous code */ unsigned stack; /* next position for reversed string */ unsigned outcnt; /* bytes in output buffer */ struct outd outd; /* output structure */ unsigned char *p; /* set up output */ outd.outfile = outfile; outd.check = 0; /* process remainder of compress header -- a flags byte */ flags = NEXT(); if (last == -1) return Z_BUF_ERROR; if (flags & 0x60) { strm->msg = (char *)"unknown lzw flags set"; return Z_DATA_ERROR; } max = flags & 0x1f; if (max < 9 || max > 16) { strm->msg = (char *)"lzw bits out of range"; return Z_DATA_ERROR; } if (max == 9) /* 9 doesn't really mean 9 */ max = 10; flags &= 0x80; /* true if block compress */ /* clear table */ bits = 9; mask = 0x1ff; end = flags ? 256 : 255; /* set up: get first 9-bit code, which is the first decompressed byte, but don't create a table entry until the next code */ if (NEXT() == -1) /* no compressed data is ok */ return Z_OK; final = prev = (unsigned)last; /* low 8 bits of code */ if (NEXT() == -1) /* missing a bit */ return Z_BUF_ERROR; if (last & 1) { /* code must be < 256 */ strm->msg = (char *)"invalid lzw code"; return Z_DATA_ERROR; } rem = (unsigned)last >> 1; /* remaining 7 bits */ left = 7; chunk = bits - 2; /* 7 bytes left in this chunk */ outbuf[0] = (unsigned char)final; /* write first decompressed byte */ outcnt = 1; /* decode codes */ stack = 0; for (;;) { /* if the table will be full after this, increment the code size */ if (end >= mask && bits < max) { FLUSHCODE(); bits++; mask <<= 1; mask++; } /* get a code of length bits */ if (chunk == 0) /* decrement chunk modulo bits */ chunk = bits; code = rem; /* low bits of code */ if (NEXT() == -1) { /* EOF is end of compressed data */ /* write remaining buffered output */ if (outcnt && out(&outd, outbuf, outcnt)) { strm->next_in = outbuf; /* signal write error */ return Z_BUF_ERROR; } return Z_OK; } code += (unsigned)last << left; /* middle (or high) bits of code */ left += 8; chunk--; if (bits > left) { /* need more bits */ if (NEXT() == -1) /* can't end in middle of code */ return Z_BUF_ERROR; code += (unsigned)last << left; /* high bits of code */ left += 8; chunk--; } code &= mask; /* mask to current code length */ left -= bits; /* number of unused bits */ rem = (unsigned)last >> (8 - left); /* unused bits from last byte */ /* process clear code (256) */ if (code == 256 && flags) { FLUSHCODE(); bits = 9; /* initialize bits and mask */ mask = 0x1ff; end = 255; /* empty table */ continue; /* get next code */ } /* special code to reuse last match */ temp = code; /* save the current code */ if (code > end) { /* Be picky on the allowed code here, and make sure that the code we drop through (prev) will be a valid index so that random input does not cause an exception. The code != end + 1 check is empirically derived, and not checked in the original uncompress code. If this ever causes a problem, that check could be safely removed. Leaving this check in greatly improves gun's ability to detect random or corrupted input after a compress header. In any case, the prev > end check must be retained. */ if (code != end + 1 || prev > end) { strm->msg = (char *)"invalid lzw code"; return Z_DATA_ERROR; } match[stack++] = (unsigned char)final; code = prev; } /* walk through linked list to generate output in reverse order */ p = match + stack; while (code >= 256) { *p++ = suffix[code]; code = prefix[code]; } stack = p - match; match[stack++] = (unsigned char)code; final = code; /* link new table entry */ if (end < mask) { end++; prefix[end] = (unsigned short)prev; suffix[end] = (unsigned char)final; } /* set previous code for next iteration */ prev = temp; /* write output in forward order */ while (stack > SIZE - outcnt) { while (outcnt < SIZE) outbuf[outcnt++] = match[--stack]; if (out(&outd, outbuf, outcnt)) { strm->next_in = outbuf; /* signal write error */ return Z_BUF_ERROR; } outcnt = 0; } p = match + stack; do { outbuf[outcnt++] = *--p; } while (p > match); stack = 0; /* loop for next code with final and prev as the last match, rem and left provide the first 0..7 bits of the next code, end is the last valid table entry */ } } /* Decompress a gzip file from infile to outfile. strm is assumed to have been successfully initialized with inflateBackInit(). The input file may consist of a series of gzip streams, in which case all of them will be decompressed to the output file. If outfile is -1, then the gzip stream(s) integrity is checked and nothing is written. The return value is a zlib error code: Z_MEM_ERROR if out of memory, Z_DATA_ERROR if the header or the compressed data is invalid, or if the trailer CRC-32 check or length doesn't match, Z_BUF_ERROR if the input ends prematurely or a write error occurs, or Z_ERRNO if junk (not a another gzip stream) follows a valid gzip stream. */ local int gunpipe(z_stream *strm, int infile, int outfile) { int ret, first, last; unsigned have, flags, len; z_const unsigned char *next = NULL; struct ind ind, *indp; struct outd outd; /* setup input buffer */ ind.infile = infile; ind.inbuf = inbuf; indp = &ind; /* decompress concatenated gzip streams */ have = 0; /* no input data read in yet */ first = 1; /* looking for first gzip header */ strm->next_in = Z_NULL; /* so Z_BUF_ERROR means EOF */ for (;;) { /* look for the two magic header bytes for a gzip stream */ if (NEXT() == -1) { ret = Z_OK; break; /* empty gzip stream is ok */ } if (last != 31 || (NEXT() != 139 && last != 157)) { strm->msg = (char *)"incorrect header check"; ret = first ? Z_DATA_ERROR : Z_ERRNO; break; /* not a gzip or compress header */ } first = 0; /* next non-header is junk */ /* process a compress (LZW) file -- can't be concatenated after this */ if (last == 157) { ret = lunpipe(have, next, indp, outfile, strm); break; } /* process remainder of gzip header */ ret = Z_BUF_ERROR; if (NEXT() != 8) { /* only deflate method allowed */ if (last == -1) break; strm->msg = (char *)"unknown compression method"; ret = Z_DATA_ERROR; break; } flags = NEXT(); /* header flags */ NEXT(); /* discard mod time, xflgs, os */ NEXT(); NEXT(); NEXT(); NEXT(); NEXT(); if (last == -1) break; if (flags & 0xe0) { strm->msg = (char *)"unknown header flags set"; ret = Z_DATA_ERROR; break; } if (flags & 4) { /* extra field */ len = NEXT(); len += (unsigned)(NEXT()) << 8; if (last == -1) break; while (len > have) { len -= have; have = 0; if (NEXT() == -1) break; len--; } if (last == -1) break; have -= len; next += len; } if (flags & 8) /* file name */ while (NEXT() != 0 && last != -1) ; if (flags & 16) /* comment */ while (NEXT() != 0 && last != -1) ; if (flags & 2) { /* header crc */ NEXT(); NEXT(); } if (last == -1) break; /* set up output */ outd.outfile = outfile; outd.check = 1; outd.crc = crc32(0L, Z_NULL, 0); outd.total = 0; /* decompress data to output */ strm->next_in = next; strm->avail_in = have; ret = inflateBack(strm, in, indp, out, &outd); if (ret != Z_STREAM_END) break; next = strm->next_in; have = strm->avail_in; strm->next_in = Z_NULL; /* so Z_BUF_ERROR means EOF */ /* check trailer */ ret = Z_BUF_ERROR; if (NEXT() != (int)(outd.crc & 0xff) || NEXT() != (int)((outd.crc >> 8) & 0xff) || NEXT() != (int)((outd.crc >> 16) & 0xff) || NEXT() != (int)((outd.crc >> 24) & 0xff)) { /* crc error */ if (last != -1) { strm->msg = (char *)"incorrect data check"; ret = Z_DATA_ERROR; } break; } if (NEXT() != (int)(outd.total & 0xff) || NEXT() != (int)((outd.total >> 8) & 0xff) || NEXT() != (int)((outd.total >> 16) & 0xff) || NEXT() != (int)((outd.total >> 24) & 0xff)) { /* length error */ if (last != -1) { strm->msg = (char *)"incorrect length check"; ret = Z_DATA_ERROR; } break; } /* go back and look for another gzip stream */ } /* clean up and return */ return ret; } #if defined(_WIN64) || defined(_WIN32) local void copymeta(char *from, char *to) { //not for windows computers } #else /* Copy file attributes, from -> to, as best we can. This is best effort, so no errors are reported. The mode bits, including suid, sgid, and the sticky bit are copied (if allowed), the owner's user id and group id are copied (again if allowed), and the access and modify times are copied. */ local void copymeta(char *from, char *to) { struct stat was; struct utimbuf when; /* get all of from's Unix meta data, return if not a regular file */ if (stat(from, &was) != 0 || (was.st_mode & S_IFMT) != S_IFREG) return; /* set to's mode bits, ignore errors */ (void)chmod(to, was.st_mode & 07777); /* copy owner's user and group, ignore errors */ (void)chown(to, was.st_uid, was.st_gid); /* copy access and modify times, ignore errors */ when.actime = was.st_atime; when.modtime = was.st_mtime; (void)utime(to, &when); } #endif /* Decompress the file inname to the file outnname, of if test is true, just decompress without writing and check the gzip trailer for integrity. If inname is NULL or an empty string, read from stdin. If outname is NULL or an empty string, write to stdout. strm is a pre-initialized inflateBack structure. When appropriate, copy the file attributes from inname to outname. gunzip() returns 1 if there is an out-of-memory error or an unexpected return code from gunpipe(). Otherwise it returns 0. */ local int gunzip(z_stream *strm, char *inname, char *outname, int test, int deletein) { int ret; int infile, outfile; /* open files */ if (inname == NULL || *inname == 0) { inname = "-"; infile = 0; /* stdin */ } else { infile = open(inname, O_RDONLY, 0); if (infile == -1) { fprintf(stderr, "gun cannot open %s\n", inname); return -1; } } if (test) outfile = -1; else if (outname == NULL || *outname == 0) { outname = "-"; outfile = 1; /* stdout */ } else { outfile = open(outname, O_CREAT | O_TRUNC | O_WRONLY, 0666); if (outfile == -1) { close(infile); fprintf(stderr, "gun cannot create %s\n", outname); return -1; } } errno = 0; /* decompress */ ret = gunpipe(strm, infile, outfile); if (outfile > 2) close(outfile); if (infile > 2) close(infile); /* interpret result */ switch (ret) { case Z_OK: case Z_ERRNO: if (infile > 2 && outfile > 2) { copymeta(inname, outname); /* copy attributes */ if (deletein) unlink(inname); } if (ret == Z_ERRNO) fprintf(stderr, "gun warning: trailing garbage ignored in %s\n", inname); break; case Z_DATA_ERROR: if (outfile > 2) unlink(outname); fprintf(stderr, "gun data error on %s: %s\n", inname, strm->msg); break; case Z_MEM_ERROR: if (outfile > 2) unlink(outname); fprintf(stderr, "gun out of memory error--aborting\n"); return 1; case Z_BUF_ERROR: if (outfile > 2) unlink(outname); if (strm->next_in != Z_NULL) { fprintf(stderr, "gun write error on %s: %s\n", outname, strerror(errno)); } else if (errno) { fprintf(stderr, "gun read error on %s: %s\n", inname, strerror(errno)); } else { fprintf(stderr, "gun unexpected end of file on '%s'\n", inname); } break; default: if (outfile > 2) unlink(outname); fprintf(stderr, "gun internal error--aborting\n"); return 1; } return ret; } /* * "untar" is an extremely simple tar extractor: * * A single C source file, so it should be easy to compile * and run on any system with a C compiler. * * Extremely portable standard C. The only non-ANSI function * used is mkdir(). * * Reads basic ustar tar archives. * * Does not require libarchive or any other special library. * * To compile: cc -o untar untar.c * * Usage: untar * * In particular, this program should be sufficient to extract the * distribution for libarchive, allowing people to bootstrap * libarchive on systems that do not already have a tar program. * * To unpack libarchive-x.y.z.tar.gz: * * gunzip libarchive-x.y.z.tar.gz * * untar libarchive-x.y.z.tar * * Written by Tim Kientzle, March 2009. * * Released into the public domain. */ /* These are all highly standard and portable headers. */ /* g++ -O3 -lz untar.c -s -o untar */ #include #include #include /* This is for mkdir(); this may need to be changed for some platforms. */ #include /* For mkdir() */ /* Parse an octal number, ignoring leading and trailing nonsense. */ static int parseoct(const char *p, size_t n) { int i = 0; while (*p < '0' || *p > '7') { ++p; --n; } while (*p >= '0' && *p <= '7' && n > 0) { i *= 8; i += *p - '0'; ++p; --n; } return (i); } /* Returns true if this is 512 zero bytes. */ static int is_end_of_archive(const char *p) { int n; for (n = 511; n >= 0; --n) if (p[n] != '\0') return (0); return (1); } #if defined(_WIN64) || defined(_WIN32) const char kPathSeparator ='\\'; //const char kFileSep[2] = "\\"; #else const char kPathSeparator ='/'; //const char kFileSep[2] = "/"; #endif void dropFilenameFromPathKeepFinalFileSep(char *path) { const char *dirPath = strrchr(path, kPathSeparator); if (dirPath == NULL) { strcpy(path,""); } else path[dirPath - path +1] = 0; // } #if defined(_WIN64) || defined(_WIN32) bool is_fileexistsWin(const char * filename) { FILE * fp = NULL; if ((fp = fopen(filename, "r"))) { fclose(fp); return true; } return false; } int mkdirWin(char *pathname) { if (is_fileexistsWin(pathname)) return 0; return mkdir(pathname); } #endif /* Create a directory, including parent directories as necessary. */ static void create_dir(char *pathname, int mode) { char *p; int r; /* Strip trailing '/' */ if (pathname[strlen(pathname) - 1] == kPathSeparator) pathname[strlen(pathname) - 1] = '\0'; /* Try creating the directory. */ #if defined(_WIN64) || defined(_WIN32) r = mkdirWin(pathname); #else r = mkdir(pathname, mode); #endif fprintf(stderr, "attempted mkdir %s got %d\n", pathname, r); if (r != 0) { /* On failure, try creating parent directory. */ p = strrchr(pathname, kPathSeparator); if (p != NULL) { *p = '\0'; create_dir(pathname, 0755); *p = kPathSeparator; #if defined(_WIN64) || defined(_WIN32) r = mkdirWin(pathname); #else r = mkdir(pathname, mode); #endif } } if (r != 0) fprintf(stderr, "Could not create directory %s\n", pathname); } /* Create a file, including parent directory as necessary. */ static FILE * create_file(char *pathname, int mode) { FILE *f; f = fopen(pathname, "w+"); if (f == NULL) { /* Try creating parent dir and then creating file. */ char *p = strrchr(pathname, kPathSeparator); if (p != NULL) { *p = '\0'; create_dir(pathname, 0755); *p = kPathSeparator; f = fopen(pathname, "w+"); } } return (f); } /* Verify the tar checksum. */ static int verify_checksum(const char *p) { int n, u = 0; for (n = 0; n < 512; ++n) { if (n < 148 || n > 155) /* Standard tar checksum adds unsigned bytes. */ u += ((unsigned char *)p)[n]; else u += 0x20; } return (u == parseoct(p + 148, 8)); } /* Extract a tar archive. */ static void untar(FILE *a, const char *path) { char buff[1024]; FILE *f = NULL; size_t bytes_read; int filesize; char pathDir[1024]; strcpy(pathDir,path); dropFilenameFromPathKeepFinalFileSep(pathDir); char newFile[1024] = {""}; for (;;) { bytes_read = fread(buff, 1, 512, a); if (bytes_read < 512) { fprintf(stderr, "Short read on %s: expected 512, got %zd\n",path, bytes_read); return; } if (is_end_of_archive(buff)) { printf("End of %s\n", path); return; } if (!verify_checksum(buff)) { fprintf(stderr, "Checksum failure\n"); return; } filesize = parseoct(buff + 124, 12); switch (buff[156]) { case '1': printf(" Ignoring hardlink %s\n", buff); break; case '2': printf(" Ignoring symlink %s\n", buff); break; case '3': printf(" Ignoring character device %s\n", buff); break; case '4': printf(" Ignoring block device %s\n", buff); break; case '5': strcpy (newFile,pathDir); strcat (newFile,buff); //printf(" Extracting dir %s\n", newFile); create_dir(buff, parseoct(buff + 100, 8)); filesize = 0; break; case '6': printf(" Ignoring FIFO %s\n", buff); break; default: strcpy (newFile,pathDir); strcat (newFile,buff); //printf(" Extracting file %s\n", newFile); f = create_file(newFile, parseoct(buff + 100, 8)); break; } while (filesize > 0) { bytes_read = fread(buff, 1, 512, a); if (bytes_read < 512) { fprintf(stderr, "Short read on %s: Expected 512, got %zd\n",path, bytes_read); return; } if (filesize < 512) bytes_read = filesize; if (f != NULL) { if (fwrite(buff, 1, bytes_read, f) != bytes_read) { fprintf(stderr, "Failed write\n"); fclose(f); f = NULL; } } filesize -= bytes_read; } if (f != NULL) { fclose(f); f = NULL; } } } void getFileNam( char *pathParent, const char *path) //if path is c:\d1\d2 then filename is 'd2' { const char *filename = strrchr(path, '/'); //UNIX if (filename == 0) filename = strrchr(path, '\\'); //Windows //const char *filename = strrchr(path, kPathSeparator); //x if (filename == NULL) {//no path separator strcpy(pathParent,path); return; } filename++; strcpy(pathParent,filename); } void changeDirOfFilename( char *filename, char *newdir) {//set path of filename to be in outdir if (strlen(newdir) < 1) return; char fname[1024]; getFileNam( fname, filename); char outname[1024]; strcpy(outname,newdir); if (outname[strlen(outname) - 1] != kPathSeparator) #if defined(_WIN64) || defined(_WIN32) strcat (outname,"\\"); #else strcat (outname,"/"); //append name #endif strcat (outname,fname); //append name strcpy(filename,outname); } //next portion by Chris Rorden, 2014 int untargz( char * fname, char * outdir){ //returns 0 if success in extracting file, -666 if not a tgz/tar.gz file, otherwise error //detect type char tardir[1024] = ""; int len = (int)strlen(fname); int ext = 0; if (len < 4) return - 1; if ((len > 7 ) && (strcmp(fname + len - 7, ".tar.gz") == 0)) { ext = 7; } else if (strcmp(fname + len - 4, ".tgz") == 0) { ext = 4; } else if (strcmp(fname + len - 3, ".gz") == 0) { return -666;//ext = 3; //this function only converts tar.gz and tgz } else return -666; len -= ext; //set filenames char outname[1024]; //strlcpy not included in all distros http://stackoverflow.com/questions/18547251/when-i-use-strlcpy-function-in-c-the-compilor-give-me-an-error strncpy(outname, fname, len);//strlcpy(outname, fname, len+1); changeDirOfFilename(outname, outdir); if (ext > 3) { //extract tar.gz files to new folder strcpy(tardir, outname); //extract ~/dir/f.tar to ~/dir/f if( access( tardir, F_OK ) != -1 ) { printf("warning folder %s already exists\n",tardir); //return -1; } else create_dir(tardir, 0777); strcat(outname, ".tar"); // file.tar.gz -> file.tar, file.tgz -> file.tar changeDirOfFilename(outname, tardir); } printf("gun will copy *%s* to *%s*\n",fname,outname); if( access( outname, F_OK ) != -1 ) { printf("file %s already exists\n",outname); return -1; } //un gzip unsigned char *window; z_stream strm; window = match; /* reuse LZW match buffer */ strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; int ret = inflateBackInit(&strm, 15, window); ret = gunzip(&strm, fname, outname, 0, 0); inflateBackEnd(&strm); if (ret != 0) return ret; //un tar if (ext > 3) { //.tgz or .tar.gz FILE *a; a = fopen(outname, "r"); untar(a, outname); fclose(a); unlink(outname); strcpy(fname,tardir); } return ret; }dcm2niix-1.0.20171215/console/unused/untgz.h000066400000000000000000000002031322051203000202340ustar00rootroot00000000000000#ifndef UNTGZ_H__ #define UNTGZ_H__ //extern const char const *one_hit_wonder; int untargz( char * fname, char * outdir); #endifdcm2niix-1.0.20171215/cpfiles.command000077500000000000000000000005231322051203000167440ustar00rootroot00000000000000#!/bin/sh cd /Users/rorden/Documents/cocoa/dcm2niix/console #cp ni*.c ../qtGui #cp ni*.cpp ../qtGui #cp ni*.h ../qtGui #cp tinydir.h ../qtGui #cp ni*.c ../wxWidgets #cp ni*.cpp ../wxWidgets #cp ni*.h ../wxWidgets #cp tinydir.h ../wxWidgets cp *.c ../xcode/dcm2/core cp *.cpp ../xcode/dcm2/core cp *.h ../xcode/dcm2/core #myDisableMiniZdcm2niix-1.0.20171215/dcm2laz/000077500000000000000000000000001322051203000153105ustar00rootroot00000000000000dcm2niix-1.0.20171215/dcm2laz/LibTar.pas000066400000000000000000001050021322051203000171700ustar00rootroot00000000000000(** =============================================================================================== Name : LibTar =============================================================================================== Subject : Handling of "tar" files =============================================================================================== Author : Stefan Heymann Eschenweg 3 72076 Tübingen GERMANY E-Mail: stefan@destructor.de Web: www.destructor.de =============================================================================================== TTarArchive Usage ----------------- - Choose a constructor - Make an instance of TTarArchive TA := TTarArchive.Create (Filename); - Scan through the archive TA.Reset; while TA.FindNext (DirRec) do begin - Evaluate the DirRec for each file ListBox.Items.Add (DirRec.Name); - Read out the current file TA.ReadFile (DestFilename); (You can ommit this if you want to read in the directory only) end; - You're done TA.Free; TTarWriter Usage ---------------- - Choose a constructor - Make an instance of TTarWriter TW := TTarWriter.Create ('my.tar'); - Add a file to the tar archive TW.AddFile ('foobar.txt'); - Add a string as a file TW.AddString (SL.Text, 'joe.txt', Now); - Destroy TarWriter instance TW.Free; - Now your tar file is ready. Source, Legals ("Licence") -------------------------- The official site to get this code is http://www.destructor.de/ Usage and Distribution of this Source Code is ruled by the "Destructor.de Source code Licence" (DSL) which comes with this file or can be downloaded at http://www.destructor.de/ IN SHORT: Usage and distribution of this source code is free. You use it completely on your own risk. =============================================================================================== !!! All parts of this code which are not finished or known to be buggy are marked with three exclamation marks =============================================================================================== Date Author Changes ----------------------------------------------------------------------------------------------- 2001-04-26 HeySt 0.0.1 Start 2001-04-28 HeySt 1.0.0 First Release 2001-06-19 HeySt 2.0.0 Finished TTarWriter 2001-09-06 HeySt 2.0.1 Bugfix in TTarArchive.FindNext: FBytesToGo must sometimes be 0 2001-10-25 HeySt 2.0.2 Introduced the ClearDirRec procedure 2001-11-13 HeySt 2.0.3 Bugfix: Take out ClearDirRec call from WriteTarHeader Bug Reported by Tony BenBrahim 2001-12-25 HeySt 2.0.4 WriteTarHeader: Fill Rec with zero bytes before filling it 2002-05-18 HeySt 2.0.5 Kylix awareness: Thanks to Kerry L. Davison for the changes 2005-09-03 HeySt 2.0.6 TTarArchive.FindNext: Don't access SourceStream.Size (for compressed streams, which don't know their .Size) 2006-03-13 HeySt 2.0.7 Bugfix in ReadFile (Buffer : POINTER) 2007-05-16 HeySt 2.0.8 Bugfix in TTarWriter.AddFile (Convertfilename in the ELSE branch) Bug Reported by Chris Rorden 2010-11-29 HeySt 2.1.0 WriteTarHeader: Mode values for ftNormal/ftLink/ftSymbolicLink/ftDirectory Thanks to Iouri Kharon for the fix. Still no support for filenames > 100 bytes. Sorry. Support for Unicode Delphi versions (2009, 2010, XE, etc.) 2011-05-23 HeySt 2.1.1 New IFDEF WIN32 in the USES clause 2014-06-23 HeySt 2.1.2 64-Bit Seek operations, CurFilePos as Int64 Thanks to Andķdy Csaba for the fixes. *) unit LibTar; interface uses (*$IFDEF LINUX*) Libc, (*$ENDIF *) {$IFDEF WIN32} {$DEFINE MSWINDOWS} // predefined for D6+/BCB6+ // because in Delphi 5 MSWINDOWS is not defined {$ENDIF} (*$IFDEF MSWINDOWS *) Windows, (*$ENDIF *) SysUtils, Classes; type (*$IFNDEF UNICODE *) RawByteString = AnsiString; (*$ENDIF *) // --- File Access Permissions TTarPermission = (tpReadByOwner, tpWriteByOwner, tpExecuteByOwner, tpReadByGroup, tpWriteByGroup, tpExecuteByGroup, tpReadByOther, tpWriteByOther, tpExecuteByOther); TTarPermissions = set of TTarPermission; // --- Type of File TFileType = (ftNormal, // Regular file ftLink, // Link to another, previously archived, file (LinkName) ftSymbolicLink, // Symbolic link to another file (LinkName) ftCharacter, // Character special files ftBlock, // Block special files ftDirectory, // Directory entry. Size is zero (unlimited) or max. number of bytes ftFifo, // FIFO special file. No data stored in the archive. ftContiguous, // Contiguous file, if supported by OS ftDumpDir, // List of files ftMultiVolume, // Multi-volume file part ftVolumeHeader); // Volume header. Can appear only as first record in the archive // --- Mode TTarMode = (tmSetUid, tmSetGid, tmSaveText); TTarModes = set of TTarMode; // --- Record for a Directory Entry // Adjust the ClearDirRec procedure when this record changes! TTarDirRec = record Name : AnsiString; // File path and name Size : INT64; // File size in Bytes DateTime : TDateTime; // Last modification date and time Permissions : TTarPermissions; // Access permissions FileType : TFileType; // Type of file LinkName : AnsiString; // Name of linked file (for ftLink, ftSymbolicLink) UID : INTEGER; // User ID GID : INTEGER; // Group ID UserName : AnsiString; // User name GroupName : AnsiString; // Group name ChecksumOK : BOOLEAN; // Checksum was OK Mode : TTarModes; // Mode Magic : AnsiString; // Contents of the "Magic" field MajorDevNo : INTEGER; // Major Device No. for ftCharacter and ftBlock MinorDevNo : INTEGER; // Minor Device No. for ftCharacter and ftBlock FilePos : INT64; // Position in TAR file end; // --- The TAR Archive CLASS TTarArchive = class protected FStream : TStream; // Internal Stream FOwnsStream : BOOLEAN; // True if FStream is owned by the TTarArchive instance FBytesToGo : INT64; // Bytes until the next Header Record public CONSTRUCTOR Create (Stream : TStream); OVERLOAD; CONSTRUCTOR Create (Filename : STRING; FileMode : WORD = fmOpenRead OR fmShareDenyWrite); OVERLOAD; DESTRUCTOR Destroy; OVERRIDE; PROCEDURE Reset; // Reset File Pointer FUNCTION FindNext (VAR DirRec : TTarDirRec) : BOOLEAN; // Reads next Directory Info Record. FALSE if EOF reached PROCEDURE ReadFile (Buffer : POINTER); OVERLOAD; // Reads file data for last Directory Record PROCEDURE ReadFile (Stream : TStream); OVERLOAD; // -;- PROCEDURE ReadFile (Filename : STRING); OVERLOAD; // -;- FUNCTION ReadFile : RawByteString; OVERLOAD; // -;- PROCEDURE GetFilePos (VAR Current, Size : INT64); // Current File Position PROCEDURE SetFilePos (NewPos : INT64); // Set new Current File Position end; // --- The TAR Archive Writer CLASS TTarWriter = class protected FStream : TStream; FOwnsStream : BOOLEAN; FFinalized : BOOLEAN; // --- Used at the next "Add" method call: --- FPermissions : TTarPermissions; // Access permissions FUID : INTEGER; // User ID FGID : INTEGER; // Group ID FUserName : AnsiString; // User name FGroupName : AnsiString; // Group name FMode : TTarModes; // Mode FMagic : AnsiString; // Contents of the "Magic" field constructor CreateEmpty; public constructor Create (TargetStream : TStream); overload; constructor Create (TargetFilename : string; Mode : integer = fmCreate); overload; destructor Destroy; override; // Writes End-Of-File Tag PROCEDURE AddFile (Filename : STRING; TarFilename : AnsiString = ''); PROCEDURE AddStream (Stream : TStream; TarFilename : AnsiString; FileDateGmt : TDateTime); PROCEDURE AddString (Contents : RawByteString; TarFilename : AnsiString; FileDateGmt : TDateTime); PROCEDURE AddDir (Dirname : AnsiString; DateGmt : TDateTime; MaxDirSize : INT64 = 0); PROCEDURE AddSymbolicLink (Filename, Linkname : AnsiString; DateGmt : TDateTime); PROCEDURE AddLink (Filename, Linkname : AnsiString; DateGmt : TDateTime); PROCEDURE AddVolumeHeader (VolumeId : AnsiString; DateGmt : TDateTime); PROCEDURE Finalize; PROPERTY Permissions : TTarPermissions READ FPermissions WRITE FPermissions; // Access permissions PROPERTY UID : INTEGER READ FUID WRITE FUID; // User ID PROPERTY GID : INTEGER READ FGID WRITE FGID; // Group ID PROPERTY UserName : AnsiString READ FUserName WRITE FUserName; // User name PROPERTY GroupName : AnsiString READ FGroupName WRITE FGroupName; // Group name PROPERTY Mode : TTarModes READ FMode WRITE FMode; // Mode PROPERTY Magic : AnsiString READ FMagic WRITE FMagic; // Contents of the "Magic" field end; // --- Some useful constants const FILETYPE_NAME : array [TFileType] of string = ('Regular', 'Link', 'Symbolic Link', 'Char File', 'Block File', 'Directory', 'FIFO File', 'Contiguous', 'Dir Dump', 'Multivol', 'Volume Header'); ALL_PERMISSIONS = [tpReadByOwner, tpWriteByOwner, tpExecuteByOwner, tpReadByGroup, tpWriteByGroup, tpExecuteByGroup, tpReadByOther, tpWriteByOther, tpExecuteByOther]; READ_PERMISSIONS = [tpReadByOwner, tpReadByGroup, tpReadByOther]; WRITE_PERMISSIONS = [tpWriteByOwner, tpWriteByGroup, tpWriteByOther]; EXECUTE_PERMISSIONS = [tpExecuteByOwner, tpExecuteByGroup, tpExecuteByOther]; function PermissionString (Permissions : TTarPermissions) : string; function ConvertFilename (Filename : string) : string; function FileTimeGMT (FileName : string) : TDateTime; overload; function FileTimeGMT (SearchRec : TSearchRec) : TDateTime; overload; procedure ClearDirRec (var DirRec : TTarDirRec); (* =============================================================================================== IMPLEMENTATION =============================================================================================== *) IMPLEMENTATION FUNCTION PermissionString (Permissions : TTarPermissions) : STRING; BEGIN Result := ''; IF tpReadByOwner IN Permissions THEN Result := Result + 'r' ELSE Result := Result + '-'; IF tpWriteByOwner IN Permissions THEN Result := Result + 'w' ELSE Result := Result + '-'; IF tpExecuteByOwner IN Permissions THEN Result := Result + 'x' ELSE Result := Result + '-'; IF tpReadByGroup IN Permissions THEN Result := Result + 'r' ELSE Result := Result + '-'; IF tpWriteByGroup IN Permissions THEN Result := Result + 'w' ELSE Result := Result + '-'; IF tpExecuteByGroup IN Permissions THEN Result := Result + 'x' ELSE Result := Result + '-'; IF tpReadByOther IN Permissions THEN Result := Result + 'r' ELSE Result := Result + '-'; IF tpWriteByOther IN Permissions THEN Result := Result + 'w' ELSE Result := Result + '-'; IF tpExecuteByOther IN Permissions THEN Result := Result + 'x' ELSE Result := Result + '-'; END; FUNCTION ConvertFilename (Filename : STRING) : STRING; // Converts the filename to Unix conventions BEGIN (*$IFDEF LINUX *) Result := Filename; (*$ELSE *) Result := StringReplace (Filename, '\', '/', [rfReplaceAll]); (*$ENDIF *) END; FUNCTION FileTimeGMT (FileName: STRING): TDateTime; // Returns the Date and Time of the last modification of the given File // The Result is zero if the file could not be found // The Result is given in UTC (GMT) time zone VAR SR : TSearchRec; BEGIN Result := 0.0; IF FindFirst (FileName, faAnyFile, SR) = 0 THEN Result := FileTimeGMT (SR); FindClose (SR); END; FUNCTION FileTimeGMT (SearchRec : TSearchRec) : TDateTime; (*$IFDEF MSWINDOWS *) VAR SystemFileTime: TSystemTime; (*$ENDIF *) (*$IFDEF LINUX *) VAR TimeVal : TTimeVal; TimeZone : TTimeZone; (*$ENDIF *) BEGIN Result := 0.0; (*$IFDEF MSWINDOWS *) (*$WARNINGS OFF *) IF (SearchRec.FindData.dwFileAttributes AND faDirectory) = 0 THEN IF FileTimeToSystemTime (SearchRec.FindData.ftLastWriteTime, SystemFileTime) THEN Result := EncodeDate (SystemFileTime.wYear, SystemFileTime.wMonth, SystemFileTime.wDay) + EncodeTime (SystemFileTime.wHour, SystemFileTime.wMinute, SystemFileTime.wSecond, SystemFileTime.wMilliseconds); (*$ENDIF *) (*$WARNINGS ON *) (*$IFDEF LINUX *) IF SearchRec.Attr AND faDirectory = 0 THEN BEGIN Result := FileDateToDateTime (SearchRec.Time); GetTimeOfDay (TimeVal, TimeZone); Result := Result + TimeZone.tz_minuteswest / (60 * 24); END; (*$ENDIF *) end; PROCEDURE ClearDirRec (VAR DirRec : TTarDirRec); // This is included because a FillChar (DirRec, SizeOf (DirRec), 0) // will destroy the long string pointers, leading to strange bugs BEGIN WITH DirRec DO BEGIN Name := ''; Size := 0; DateTime := 0.0; Permissions := []; FileType := TFileType (0); LinkName := ''; UID := 0; GID := 0; UserName := ''; GroupName := ''; ChecksumOK := FALSE; Mode := []; Magic := ''; MajorDevNo := 0; MinorDevNo := 0; FilePos := 0; END; END; (* =============================================================================================== TAR format =============================================================================================== *) CONST RECORDSIZE = 512; NAMSIZ = 100; TUNMLEN = 32; TGNMLEN = 32; CHKBLANKS = #32#32#32#32#32#32#32#32; TYPE TTarHeader = PACKED RECORD Name : ARRAY [0..NAMSIZ-1] OF AnsiChar; Mode : ARRAY [0..7] OF AnsiChar; UID : ARRAY [0..7] OF AnsiChar; GID : ARRAY [0..7] OF AnsiChar; Size : ARRAY [0..11] OF AnsiChar; MTime : ARRAY [0..11] OF AnsiChar; ChkSum : ARRAY [0..7] OF AnsiChar; LinkFlag : AnsiChar; LinkName : ARRAY [0..NAMSIZ-1] OF AnsiChar; Magic : ARRAY [0..7] OF AnsiChar; UName : ARRAY [0..TUNMLEN-1] OF AnsiChar; GName : ARRAY [0..TGNMLEN-1] OF AnsiChar; DevMajor : ARRAY [0..7] OF AnsiChar; DevMinor : ARRAY [0..7] OF AnsiChar; END; FUNCTION ExtractText (P : PAnsiChar) : AnsiString; BEGIN Result := AnsiString (P); END; FUNCTION ExtractNumber (P : PAnsiChar) : INTEGER; OVERLOAD; VAR Strg : AnsiString; BEGIN Strg := AnsiString (Trim (string (P))); P := PAnsiChar (Strg); Result := 0; WHILE (P^ <> #32) AND (P^ <> #0) DO BEGIN Result := (ORD (P^) - ORD ('0')) OR (Result SHL 3); INC (P); END; END; FUNCTION ExtractNumber64 (P : PAnsiChar) : INT64; OVERLOAD; VAR Strg : AnsiString; BEGIN Strg := AnsiString (Trim (string (P))); P := PAnsiChar (Strg); Result := 0; WHILE (P^ <> #32) AND (P^ <> #0) DO BEGIN Result := (ORD (P^) - ORD ('0')) OR (Result SHL 3); INC (P); END; END; FUNCTION ExtractNumber (P : PAnsiChar; MaxLen : INTEGER) : INTEGER; OVERLOAD; VAR S0 : ARRAY [0..255] OF AnsiChar; Strg : AnsiString; BEGIN StrLCopy (S0, P, MaxLen); Strg := AnsiString (Trim (string (S0))); P := PAnsiChar (Strg); Result := 0; WHILE (P^ <> #32) AND (P^ <> #0) DO BEGIN Result := (ORD (P^) - ORD ('0')) OR (Result SHL 3); INC (P); END; END; FUNCTION ExtractNumber64 (P : PAnsiChar; MaxLen : INTEGER) : INT64; OVERLOAD; VAR S0 : ARRAY [0..255] OF AnsiChar; Strg : AnsiString; BEGIN StrLCopy (S0, P, MaxLen); Strg := AnsiString (Trim (string (S0))); P := PAnsiChar (Strg); Result := 0; WHILE (P^ <> #32) AND (P^ <> #0) DO BEGIN Result := (ORD (P^) - ORD ('0')) OR (Result SHL 3); INC (P); END; END; FUNCTION Records (Bytes : INT64) : INT64; BEGIN Result := Bytes DIV RECORDSIZE; IF Bytes MOD RECORDSIZE > 0 THEN INC (Result); END; PROCEDURE Octal (N : INTEGER; P : PAnsiChar; Len : INTEGER); // Makes a string of octal digits // The string will always be "Len" characters long VAR I : INTEGER; BEGIN FOR I := Len-2 DOWNTO 0 DO BEGIN (P+I)^ := AnsiChar (ORD ('0') + ORD (N AND $07)); N := N SHR 3; END; FOR I := 0 TO Len-3 DO IF (P+I)^ = '0' THEN (P+I)^ := #32 ELSE BREAK; (P+Len-1)^ := #32; END; PROCEDURE Octal64 (N : INT64; P : PAnsiChar; Len : INTEGER); // Makes a string of octal digits // The string will always be "Len" characters long VAR I : INTEGER; BEGIN FOR I := Len-2 DOWNTO 0 DO BEGIN (P+I)^ := AnsiChar (ORD ('0') + ORD (N AND $07)); N := N SHR 3; END; FOR I := 0 TO Len-3 DO IF (P+I)^ = '0' THEN (P+I)^ := #32 ELSE BREAK; (P+Len-1)^ := #32; END; PROCEDURE OctalN (N : INTEGER; P : PAnsiChar; Len : INTEGER); BEGIN Octal (N, P, Len-1); (P+Len-1)^ := #0; END; PROCEDURE WriteTarHeader (Dest : TStream; DirRec : TTarDirRec); VAR Rec : ARRAY [0..RECORDSIZE-1] OF AnsiChar; TH : TTarHeader ABSOLUTE Rec; Mode : INTEGER; NullDate : TDateTime; Checksum : CARDINAL; I : INTEGER; BEGIN FillChar (Rec, RECORDSIZE, 0); StrLCopy (TH.Name, PAnsiChar (DirRec.Name), NAMSIZ); CASE DirRec.FileType OF ftNormal, ftLink : Mode := $08000; ftSymbolicLink : Mode := $0A000; ftDirectory : Mode := $04000; ELSE Mode := 0; END; IF tmSaveText IN DirRec.Mode THEN Mode := Mode OR $0200; IF tmSetGid IN DirRec.Mode THEN Mode := Mode OR $0400; IF tmSetUid IN DirRec.Mode THEN Mode := Mode OR $0800; IF tpReadByOwner IN DirRec.Permissions THEN Mode := Mode OR $0100; IF tpWriteByOwner IN DirRec.Permissions THEN Mode := Mode OR $0080; IF tpExecuteByOwner IN DirRec.Permissions THEN Mode := Mode OR $0040; IF tpReadByGroup IN DirRec.Permissions THEN Mode := Mode OR $0020; IF tpWriteByGroup IN DirRec.Permissions THEN Mode := Mode OR $0010; IF tpExecuteByGroup IN DirRec.Permissions THEN Mode := Mode OR $0008; IF tpReadByOther IN DirRec.Permissions THEN Mode := Mode OR $0004; IF tpWriteByOther IN DirRec.Permissions THEN Mode := Mode OR $0002; IF tpExecuteByOther IN DirRec.Permissions THEN Mode := Mode OR $0001; OctalN (Mode, @TH.Mode, 8); OctalN (DirRec.UID, @TH.UID, 8); OctalN (DirRec.GID, @TH.GID, 8); Octal64 (DirRec.Size, @TH.Size, 12); NullDate := EncodeDate (1970, 1, 1); IF DirRec.DateTime >= NullDate THEN Octal (Trunc ((DirRec.DateTime - NullDate) * 86400.0), @TH.MTime, 12) ELSE Octal (Trunc ( NullDate * 86400.0), @TH.MTime, 12); CASE DirRec.FileType OF ftNormal : TH.LinkFlag := '0'; ftLink : TH.LinkFlag := '1'; ftSymbolicLink : TH.LinkFlag := '2'; ftCharacter : TH.LinkFlag := '3'; ftBlock : TH.LinkFlag := '4'; ftDirectory : TH.LinkFlag := '5'; ftFifo : TH.LinkFlag := '6'; ftContiguous : TH.LinkFlag := '7'; ftDumpDir : TH.LinkFlag := 'D'; ftMultiVolume : TH.LinkFlag := 'M'; ftVolumeHeader : TH.LinkFlag := 'V'; END; StrLCopy (TH.LinkName, PAnsiChar (DirRec.LinkName), NAMSIZ); StrLCopy (TH.Magic, PAnsiChar (DirRec.Magic + #32#32#32#32#32#32#32#32), 8); StrLCopy (TH.UName, PAnsiChar (DirRec.UserName), TUNMLEN); StrLCopy (TH.GName, PAnsiChar (DirRec.GroupName), TGNMLEN); OctalN (DirRec.MajorDevNo, @TH.DevMajor, 8); OctalN (DirRec.MinorDevNo, @TH.DevMinor, 8); StrMove (TH.ChkSum, CHKBLANKS, 8); CheckSum := 0; FOR I := 0 TO SizeOf (TTarHeader)-1 DO INC (CheckSum, INTEGER (ORD (Rec [I]))); OctalN (CheckSum, @TH.ChkSum, 8); Dest.Write (TH, RECORDSIZE); END; (* =============================================================================================== TTarArchive =============================================================================================== *) CONSTRUCTOR TTarArchive.Create (Stream : TStream); BEGIN INHERITED Create; FStream := Stream; FOwnsStream := FALSE; Reset; END; CONSTRUCTOR TTarArchive.Create (Filename : STRING; FileMode : WORD); BEGIN INHERITED Create; FStream := TFileStream.Create (Filename, FileMode); FOwnsStream := TRUE; Reset; END; DESTRUCTOR TTarArchive.Destroy; BEGIN IF FOwnsStream THEN FStream.Free; INHERITED Destroy; END; PROCEDURE TTarArchive.Reset; // Reset File Pointer BEGIN FStream.Position := 0; FBytesToGo := 0; END; FUNCTION TTarArchive.FindNext (VAR DirRec : TTarDirRec) : BOOLEAN; // Reads next Directory Info Record // The Stream pointer must point to the first byte of the tar header VAR Rec : ARRAY [0..RECORDSIZE-1] OF CHAR; CurFilePos : int64; Header : TTarHeader ABSOLUTE Rec; I : INTEGER; HeaderChkSum : WORD; Checksum : CARDINAL; BEGIN // --- Scan until next pointer IF FBytesToGo > 0 THEN FStream.Seek (Records (FBytesToGo) * RECORDSIZE, soCurrent); // --- EOF reached? Result := FALSE; CurFilePos := FStream.Position; TRY FStream.ReadBuffer (Rec, RECORDSIZE); if Rec [0] = #0 THEN EXIT; // EOF reached EXCEPT EXIT; // EOF reached, too END; Result := TRUE; ClearDirRec (DirRec); DirRec.FilePos := CurFilePos; DirRec.Name := ExtractText (Header.Name); DirRec.Size := ExtractNumber64 (@Header.Size, 12); DirRec.DateTime := EncodeDate (1970, 1, 1) + (ExtractNumber (@Header.MTime, 12) / 86400.0); I := ExtractNumber (@Header.Mode); IF I AND $0100 <> 0 THEN Include (DirRec.Permissions, tpReadByOwner); IF I AND $0080 <> 0 THEN Include (DirRec.Permissions, tpWriteByOwner); IF I AND $0040 <> 0 THEN Include (DirRec.Permissions, tpExecuteByOwner); IF I AND $0020 <> 0 THEN Include (DirRec.Permissions, tpReadByGroup); IF I AND $0010 <> 0 THEN Include (DirRec.Permissions, tpWriteByGroup); IF I AND $0008 <> 0 THEN Include (DirRec.Permissions, tpExecuteByGroup); IF I AND $0004 <> 0 THEN Include (DirRec.Permissions, tpReadByOther); IF I AND $0002 <> 0 THEN Include (DirRec.Permissions, tpWriteByOther); IF I AND $0001 <> 0 THEN Include (DirRec.Permissions, tpExecuteByOther); IF I AND $0200 <> 0 THEN Include (DirRec.Mode, tmSaveText); IF I AND $0400 <> 0 THEN Include (DirRec.Mode, tmSetGid); IF I AND $0800 <> 0 THEN Include (DirRec.Mode, tmSetUid); CASE Header.LinkFlag OF #0, '0' : DirRec.FileType := ftNormal; '1' : DirRec.FileType := ftLink; '2' : DirRec.FileType := ftSymbolicLink; '3' : DirRec.FileType := ftCharacter; '4' : DirRec.FileType := ftBlock; '5' : DirRec.FileType := ftDirectory; '6' : DirRec.FileType := ftFifo; '7' : DirRec.FileType := ftContiguous; 'D' : DirRec.FileType := ftDumpDir; 'M' : DirRec.FileType := ftMultiVolume; 'V' : DirRec.FileType := ftVolumeHeader; END; DirRec.LinkName := ExtractText (Header.LinkName); DirRec.UID := ExtractNumber (@Header.UID); DirRec.GID := ExtractNumber (@Header.GID); DirRec.UserName := ExtractText (Header.UName); DirRec.GroupName := ExtractText (Header.GName); DirRec.Magic := AnsiString (Trim (string (Header.Magic))); DirRec.MajorDevNo := ExtractNumber (@Header.DevMajor); DirRec.MinorDevNo := ExtractNumber (@Header.DevMinor); HeaderChkSum := ExtractNumber (@Header.ChkSum); // Calc Checksum CheckSum := 0; StrMove (Header.ChkSum, CHKBLANKS, 8); FOR I := 0 TO SizeOf (TTarHeader)-1 DO INC (CheckSum, INTEGER (ORD (Rec [I]))); DirRec.CheckSumOK := WORD (CheckSum) = WORD (HeaderChkSum); IF DirRec.FileType in [ftLink, ftSymbolicLink, ftDirectory, ftFifo, ftVolumeHeader] THEN FBytesToGo := 0 ELSE FBytesToGo := DirRec.Size; END; PROCEDURE TTarArchive.ReadFile (Buffer : POINTER); // Reads file data for the last Directory Record. The entire file is read into the buffer. // The buffer must be large enough to take up the whole file. VAR RestBytes : INTEGER; BEGIN IF FBytesToGo = 0 THEN EXIT; RestBytes := Records (FBytesToGo) * RECORDSIZE - FBytesToGo; FStream.ReadBuffer (Buffer^, FBytesToGo); FStream.Seek (RestBytes, soCurrent); FBytesToGo := 0; END; PROCEDURE TTarArchive.ReadFile (Stream : TStream); // Reads file data for the last Directory Record. // The entire file is written out to the stream. // The stream is left at its current position prior to writing VAR RestBytes : INTEGER; BEGIN IF FBytesToGo = 0 THEN EXIT; RestBytes := Records (FBytesToGo) * RECORDSIZE - FBytesToGo; Stream.CopyFrom (FStream, FBytesToGo); FStream.Seek (RestBytes, soCurrent); FBytesToGo := 0; END; PROCEDURE TTarArchive.ReadFile (Filename : STRING); // Reads file data for the last Directory Record. // The entire file is saved in the given Filename VAR FS : TFileStream; BEGIN FS := TFileStream.Create (Filename, fmCreate); TRY ReadFile (FS); FINALLY FS.Free; END; END; FUNCTION TTarArchive.ReadFile : RawByteString; // Reads file data for the last Directory Record. The entire file is returned // as a large ANSI string. VAR RestBytes : INTEGER; BEGIN IF FBytesToGo = 0 THEN EXIT; RestBytes := Records (FBytesToGo) * RECORDSIZE - FBytesToGo; SetLength (Result, FBytesToGo); FStream.ReadBuffer (PAnsiChar (Result)^, FBytesToGo); FStream.Seek (RestBytes, soCurrent); FBytesToGo := 0; END; PROCEDURE TTarArchive.GetFilePos (VAR Current, Size : INT64); // Returns the Current Position in the TAR stream BEGIN Current := FStream.Position; Size := FStream.Size; END; PROCEDURE TTarArchive.SetFilePos (NewPos : INT64); // Set new Current File Position BEGIN IF NewPos < FStream.Size THEN FStream.Seek (NewPos, soBeginning); END; (* =============================================================================================== TTarWriter =============================================================================================== *) CONSTRUCTOR TTarWriter.CreateEmpty; VAR TP : TTarPermission; BEGIN INHERITED Create; FOwnsStream := FALSE; FFinalized := FALSE; FPermissions := []; FOR TP := Low (TP) TO High (TP) DO Include (FPermissions, TP); FUID := 0; FGID := 0; FUserName := ''; FGroupName := ''; FMode := []; FMagic := 'ustar'; END; CONSTRUCTOR TTarWriter.Create (TargetStream : TStream); BEGIN CreateEmpty; FStream := TargetStream; FOwnsStream := FALSE; END; CONSTRUCTOR TTarWriter.Create (TargetFilename : STRING; Mode : INTEGER = fmCreate); BEGIN CreateEmpty; FStream := TFileStream.Create (TargetFilename, Mode); FOwnsStream := TRUE; END; DESTRUCTOR TTarWriter.Destroy; BEGIN IF NOT FFinalized THEN BEGIN Finalize; FFinalized := TRUE; END; IF FOwnsStream THEN FStream.Free; INHERITED Destroy; END; PROCEDURE TTarWriter.AddFile (Filename : STRING; TarFilename : AnsiString = ''); VAR S : TFileStream; Date : TDateTime; BEGIN Date := FileTimeGMT (Filename); IF TarFilename = '' THEN TarFilename := AnsiString (ConvertFilename (Filename)) ELSE TarFilename := AnsiString (ConvertFilename (string (TarFilename))); S := TFileStream.Create (Filename, fmOpenRead OR fmShareDenyWrite); TRY AddStream (S, TarFilename, Date); FINALLY S.Free END; END; PROCEDURE TTarWriter.AddStream (Stream : TStream; TarFilename : AnsiString; FileDateGmt : TDateTime); VAR DirRec : TTarDirRec; Rec : ARRAY [0..RECORDSIZE-1] OF CHAR; BytesToRead : INT64; // Bytes to read from the Source Stream BlockSize : INT64; // Bytes to write out for the current record BEGIN ClearDirRec (DirRec); DirRec.Name := TarFilename; DirRec.Size := Stream.Size - Stream.Position; DirRec.DateTime := FileDateGmt; DirRec.Permissions := FPermissions; DirRec.FileType := ftNormal; DirRec.LinkName := ''; DirRec.UID := FUID; DirRec.GID := FGID; DirRec.UserName := FUserName; DirRec.GroupName := FGroupName; DirRec.ChecksumOK := TRUE; DirRec.Mode := FMode; DirRec.Magic := FMagic; DirRec.MajorDevNo := 0; DirRec.MinorDevNo := 0; WriteTarHeader (FStream, DirRec); BytesToRead := DirRec.Size; WHILE BytesToRead > 0 DO BEGIN BlockSize := BytesToRead; IF BlockSize > RECORDSIZE THEN BlockSize := RECORDSIZE; FillChar (Rec, RECORDSIZE, 0); Stream.Read (Rec, BlockSize); FStream.Write (Rec, RECORDSIZE); DEC (BytesToRead, BlockSize); END; END; PROCEDURE TTarWriter.AddString (Contents : RawByteString; TarFilename : AnsiString; FileDateGmt : TDateTime); VAR S : TStringStream; BEGIN S := TStringStream.Create (Contents); TRY AddStream (S, TarFilename, FileDateGmt); FINALLY S.Free END END; PROCEDURE TTarWriter.AddDir (Dirname : AnsiString; DateGmt : TDateTime; MaxDirSize : INT64 = 0); VAR DirRec : TTarDirRec; BEGIN ClearDirRec (DirRec); DirRec.Name := Dirname; DirRec.Size := MaxDirSize; DirRec.DateTime := DateGmt; DirRec.Permissions := FPermissions; DirRec.FileType := ftDirectory; DirRec.LinkName := ''; DirRec.UID := FUID; DirRec.GID := FGID; DirRec.UserName := FUserName; DirRec.GroupName := FGroupName; DirRec.ChecksumOK := TRUE; DirRec.Mode := FMode; DirRec.Magic := FMagic; DirRec.MajorDevNo := 0; DirRec.MinorDevNo := 0; WriteTarHeader (FStream, DirRec); END; PROCEDURE TTarWriter.AddSymbolicLink (Filename, Linkname : AnsiString; DateGmt : TDateTime); VAR DirRec : TTarDirRec; BEGIN ClearDirRec (DirRec); DirRec.Name := Filename; DirRec.Size := 0; DirRec.DateTime := DateGmt; DirRec.Permissions := FPermissions; DirRec.FileType := ftSymbolicLink; DirRec.LinkName := Linkname; DirRec.UID := FUID; DirRec.GID := FGID; DirRec.UserName := FUserName; DirRec.GroupName := FGroupName; DirRec.ChecksumOK := TRUE; DirRec.Mode := FMode; DirRec.Magic := FMagic; DirRec.MajorDevNo := 0; DirRec.MinorDevNo := 0; WriteTarHeader (FStream, DirRec); END; PROCEDURE TTarWriter.AddLink (Filename, Linkname : AnsiString; DateGmt : TDateTime); VAR DirRec : TTarDirRec; BEGIN ClearDirRec (DirRec); DirRec.Name := Filename; DirRec.Size := 0; DirRec.DateTime := DateGmt; DirRec.Permissions := FPermissions; DirRec.FileType := ftLink; DirRec.LinkName := Linkname; DirRec.UID := FUID; DirRec.GID := FGID; DirRec.UserName := FUserName; DirRec.GroupName := FGroupName; DirRec.ChecksumOK := TRUE; DirRec.Mode := FMode; DirRec.Magic := FMagic; DirRec.MajorDevNo := 0; DirRec.MinorDevNo := 0; WriteTarHeader (FStream, DirRec); END; PROCEDURE TTarWriter.AddVolumeHeader (VolumeId : AnsiString; DateGmt : TDateTime); VAR DirRec : TTarDirRec; BEGIN ClearDirRec (DirRec); DirRec.Name := VolumeId; DirRec.Size := 0; DirRec.DateTime := DateGmt; DirRec.Permissions := FPermissions; DirRec.FileType := ftVolumeHeader; DirRec.LinkName := ''; DirRec.UID := FUID; DirRec.GID := FGID; DirRec.UserName := FUserName; DirRec.GroupName := FGroupName; DirRec.ChecksumOK := TRUE; DirRec.Mode := FMode; DirRec.Magic := FMagic; DirRec.MajorDevNo := 0; DirRec.MinorDevNo := 0; WriteTarHeader (FStream, DirRec); END; PROCEDURE TTarWriter.Finalize; // Writes the End-Of-File Tag // Data after this tag will be ignored // The destructor calls this automatically if you didn't do it before VAR Rec : ARRAY [0..RECORDSIZE-1] OF CHAR; BEGIN FillChar (Rec, SizeOf (Rec), 0); FStream.Write (Rec, RECORDSIZE); FFinalized := TRUE; END; end. dcm2niix-1.0.20171215/dcm2laz/dcm2.ico000066400000000000000000000422061322051203000166350ustar00rootroot0000000000000000 ¨%F  ¨î% ˆ –6 h@(0` €%ŠŠŠ¨¨¨ššš˙˙˙ŽŽŽ‘‘‘’‘‘’’’~~€zz{{{{|||~~€€€€€€ƒƒƒƒƒƒ………†††jjjƒƒƒ­­­˛˛˛‘‘’‚‚‚‹Š“™˜ŽąœšĘĄŸÎŖ ŅĸŸĐ™—džĻvuƒmmnrrqrrrttt}}}lllxxx888€€€“““§§§kkk zzz———ÖĻĻĻØŖŖŖØŖŖŖŲĸĸĸŲŖŖŖŲĸĸĸŲĸĸĸØĻĻĻۛ››Âsss9rrp!pqm#„‚#œ›´"•“ŧœšĖĸŸĶ¤ĸÖ¤ĄÕ›™Į„ƒĻggtPPKSTG]]Ybbbiiibbb___‚‚‚‰‰‰œœœ‰‰‰ˇˇˇ¯¯¯HšššŌššš˙¨¨¨˙°°°˙ŽŽŽ˙¯¯¯˙¯¯¯˙ŽŽŽ˙ŽŽŽ˙ŽŽŽ˙ąąą˙¯¯¯˙››œũŗ˛ˇõŽŽ°ö¤¤ ö””…ö‚ƒlņwyYđrtPđstPđxzZđƒ„l𒓃𡡞đĢĢ¯đ­­˛đ¨¨ŠđĨĨŖđĨĨĨė§§§ņ“““ÅÅÅŽŽŽ———’’’‰‰‰ŠŠŠŸ˙ŠŠŠ˙———úĒĒĒũĢĢĢũĒĒĒũĢĢĢũĢĢĢũĢĢĢũĢĢĢũĒĒĒũĢĢĢũĢĢŦüšš“˙ŒŽr˙lo;˙`d˙_e˙ek˙kq˙nu˙mt˙kq˙gm˙ek˙hm˙uy=˙Žr˙§§¤˙´´ģ˙°°˛˙¯°¯˙ĨĨĨī```€€€¤¤¤ĢĢĢ•••ͨ¨¨˙§§§û¤¤¤ü˜˜˜˙­­­ūĢĢĢūĢĢĢ˙ŦŦŦ˙ŦŦŦ˙ŦŦŦ˙ŦŦŦ˙ĢĢĢūĢĢŠū°°ļ˙hj7ūX^ũu|ũˆũ‘›ũ•žũ•žũ•Ÿũ•žũ•Ÿũ•žũ’›ũŒ•ũŠũt|ũqwũ„‡SũĨĨĄũ˛˛¸úŦŦĒ˙††…p­­­ŠŠŠĒĒĢppplll"ĸĸĸøĻĻĻüĻĻĻũĄĄĄ˙šššūŽŽŽ˙ĒĒĒ˙ŦŦŦ˙ĢĢĢ˙ĢĢĢ˙ĢĢĢ˙ĢĢĢ˙ĢĢĢ˙Ģ̍˙ŦŦļ˙…Š4˙’œ˙•˙‘š˙˜˙˜˙˜˙Ž—˙—˙—˙˜˙˜˙™˙’œ˙”ž˙š˙‚‹˙x˙‘kûą°¸ūŖŖŖĶÉĘÆŠĒĨžšæƒƒƒJŖŖŖūĨĨĨũĻĻĻūžžžūœœœ˙¯¯¯˙ĢĢĢ˙ŦŦŦ˙ŽŽŽ˙¯¯¯˙¯¯¯˙ŽŽŽ˙ŦŦŦ˙ĢŦŠ˙Ŧ̎˙Œ‘;˙Ž—˙Ž—˙˜˙˜˙˜˙™˙‘™˙‘š˙‘š˙™˙˜˙˜˙Ž—˙Ž—˙—˙’›˙‘šūŠūƒ†>üĨĨŠ˙}}„I˜”¤•”ƒ‚‰sĨĨĨūĨĨĨû§§§˙˙   ˙ŽŽŽ˙ŽŽŽ˙˛˛˛˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙˛˛˛˙¯¯­˙ŽŽš˙Œ‘A˙™˙™˙™˙‘š˙™˙Œ•˙‰“˙‰“˙Š”˙—˙‘›˙“œ˙’›˙‘›˙‘›˙‘š˙™˙”ž˙‹•ûƒ‰!ū’’‰˛“ϛ̝‚‰‰‰¯¯¯“““šĻĻĻ˙¤¤¤ú§§§ū›››˙ĸĸĸ˙˛˛˛˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗą˙ĩĩŋ˙‹G˙‡‘˙Œ•˙Œ–˙‰‘˙Œ“&˙—›P˙žĄi˙ž l˙še˙”J˙ƒŠ˙‰˙ˆ˙„‹˙ƒ‰˙ƒ‰˙‚‰!˙ˆ&ū…Š,ū‚‡)ũnqFų:F?(dfZllfƒƒƒœœœƒƒƒˇˇˇ˛˛˛ņņņ›››¸ĻĻĻ˙ĨĨĨûĒĒĒ˙œœœ˙¨¨¨˙ļļļ˙˛˛˛˙ŗŗŗ˙´´´˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙˛˛˛˙ļļ¸˙ƒ˙…‡j˙ˆŠd˙„T˙‚„r˙™˜ ˙š™¤˙Ÿž¨˙ĨĨŽ˙œĻ˙—–Ą˙‰‰˙yyv˙ŽŽ’˙””™˙””š˙––ž˙——Ÿ˙˜˜Ąū˜˜ĸ˙—–ĸü™™ū‡…‡Ō’‘•ĨĻĨzzzŖŖŖÔÔÔËËËkkkúúúĐ¨¨¨˙ĢĢĢü°°°˙   ˙ĒĒĒ˙ĩĩĩ˙˛˛˛˙´´´˙ŗŗŗ˙´´´˙´´´˙ŗŗŗ˙´´ŗ˙˛˛ŗ˙ĩĩĩ˙––˜˙˜˜˙ššĄ˙œĨ˙——›˙„„ƒ˙|˙††„˙ˆˆ†˙††„˙‡‡…˙}˙——˜˙™™™˙••”˙’’˙Ž˙‹‹‰˙‡‡…˙ƒƒ‚ūŽŽŒ˙ŽŽŽü˙zzz͍v‹‹‹{ˆˆˆ}wwwNŽŽŽÄÄÄ``` (âĢĢĢ˙­­­ũ¯¯¯˙   ˙ŠŠŠ˙ĩĩĩ˙ŗŗŗ˙ŗŗŗ˙´´´˙´´´˙´´´˙´´´˙ŗŗ´˙ŗŗ˛˙ļļˇ˙–––˙””’˙›œš˙ŠŠ‡˙tts˙‚‚‚˙ŒŒŒ˙˙˙’’’˙•••˙‡‡‡˙˙yyy˙zzz˙zzz˙zzz˙~~~˙www˙„„„˙ŽŽŽūĒĒĒ˙ĒĒĒü­­­˙ŦŦŦ˙ĢĢĢ˙ŦŦŦ˙™™™ā°°°ŗŗŗeeeqqqTĸĸĸė­­­˙ŦŦŦū°°°˙ĄĄĄ˙¨¨¨˙ļļļ˙ŗŗŗ˙´´´˙´´´˙´´´˙´´´˙´´´˙´´´˙˛˛˛˙ˇˇˇ˙˜˜˜˙‘‘‘˙˜˜˜˙ƒƒƒ˙•••˙ššš˙™™™ū›››˙šššūšššū›››ūˆˆˆūzzz˙†††ū†††˙†††˙‡‡‡˙€€€˙€€€˙ĨĨ¤˙ĢŦĒ˙ĢĢŠūĢĢĒ˙ĢĢĒũĢĢĒûĢĢĒųŦŦĢ÷ĒĒĒûxxx§TTTĢĢĢ}}}ŠŠŠō­­­˙­­­ũ°°°˙ĸĸĸ˙§§§˙ļļļ˙ŗŗŗ˙´´´˙´´´˙´´´˙´´´˙´´´˙´´´˙˛˛˛˙ˇˇˇ˙šššū‘‘‘˙›››ũûšššûšššû˜˜˜ú˜˜˜ú–––ú•••ú•••û}}}ürrrū|||˙xxxūyyy˙sss˙‚‚˙ĢĢŦ˙ąąĩ˙Ž­ą˙¯¯ŗ˙¯¯ŗ˙¯Ž´˙°¯ŗ˙°°˛˙ŽŽŽ˙°°¯˙¯¯¯˙‘‘‘ž•••‚‚‚ĢĢĢöŽŽŽ˙­­­ū°°°˙ĸĸĸ˙§§§˙ļļļ˙ŗŗŗ˙´´´˙´´´˙´´´˙´´´˙´´´˙´´´˙ŗŗŗ˙ĩĩĩūĨĨĨ˙ēēēūĮĮĮ˙†††˙xxx˙€€€˙}}}˙|||˙zzz˙zzz˙{{{˙xxx˙tttūuuuūwww˙{{{˙uuu˙˜˜™˙  œ˙—™†˙š›ˆ˙™š†˙™š†˙™›„ū•–‚˙’’Œč–•™Ô––™ÖŸŸŸŌžžžã___ZŠŠŠŗĢĢĢų­­­˙ŦŦŦū¯¯¯˙ĸĸĸ˙§§§˙ĩĩĩ˙˛˛ŗ˙ŗŗ´˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙˛˛˛˙ļļļ˙¤¤¤ũŊŊŊū¤¤¤í‘‘‘´Šnnn›oooŽnnnjjjtlllgwwwUiii†¤¤¤˙ŦŦŦüŽŽŽūąąąū´´ŗ˙ššē˙§§ĸ˙€ˆ ˙‰’˙‡˙ˆ˙Š“ú‰˙ciflnurrrŒŒŒˇĢĢĢúŽŽŽ˙¯¯¯ūąąą˙ĄĄĄ˙¨¨¨˙ĩĩŗ˙˛˛°˙ŗŗą˙ŗŗą˙ŗŗą˙ŗŗą˙ŗŗą˙ŗŗ˛˙ŗŗŗ˙ļļļūĻĻĻûĩĩĩ˙ŠŠŠ^ŦŦŦĻĻψˆˆ†††wwwuuuÎÎÎÛÛÛ­­­ČÅÅÅūÂÂÂüÂÂÂ˙ÁÁÁ˙ÁÁĀ˙ŋŋÉ˙Ž”1˙š˙‘™˙‘š˙™ü‘›˙Ž—ʍŸ¨¸48DDD^^^ŒŒŒˇŦŦŦúŦŦŦ˙   ūĒĒĒ˙žžž˙ĢĢŦ˙¸ˇÁ˙ĩ´ŋ˙ļĩÁ˙ĩĩĀ˙ļĩĀ˙ĩĩĀ˙ĩ´Ā˙´´š˙°ą°˙°°ąũĨĨĨ˙¯¯¯įjjjkkk“““€€€zzzuuuyyy˛˛˛ĩĩĩ•••nŧŧŧ˙ŋŋŋûŋŋŋūŋŋŋ˙žžŧ˙ÂÁĖ˙”—R˙‹•˙˜˙Ž—˙˜ũŽ—˙“›âVkY ¨­Eĸ¨*ŠŠŠŗ­­­ų§§§˙”””ūŸŸŸ˙’’“˙šš•˙’•X˙“P˙“O˙“M˙Ž“K˙“K˙Œ‘D˙“p˙¨¨­˙ŖŖĸü˛˛˛ūŦŦŦČõõõeeevvvŸŸŸŽŽŽ‰‰‰Iģģģ˙ĀĀĀũŋŋŋũŋŋŋ˙ŋŋŊ˙ÃÂĖ˙–š]˙‹”˙˜˙˜˙™üŽ—˙’›襨š¤ĸ§*ƒƒƒœ­­­ö§§§˙ŸŸŸũ˛˛ą˙ŽŽ°˙ĢĢ ˙˜˙Œ–˙—˙—˙Œ–˙Ž—˙Š”˙‘–G˙ÃÃÎ˙ĀÁžüÄÄÄ˙ĢĢĢÎ˙˙˙ŒŒŒžžžŒŒŒNģģģūĀĀĀũŋŋŋũĀĀĀ˙žžŧ˙ÂÂĖ˙•™W˙Œ•˙™˙Ž—˙™ũ˜˙’›įą¸)ĻĸĢ*{{{yŦŦŦņŠŠŠ˙›››ū¯¯Ž˙ŦŦ­˙Ŧ­Š˙“›˙˜˙™˙™˙™˙˜˙Ž˜˙’3˙ŊŊÆ˙žžŊũÁÁÁūŗŗŗđEEE```zzzÍÍ̓ƒƒzzzÄÄÄÉÉɚššŋŋŋ˙ŋŋŋûŋŋŋūŋŋŋ˙ŋŋŊ˙ŋžÉ˙”;˙Ž˜˙˜˙˜˙˜üŽ—˙‘šā'>ĸĒ:ĸĒ*^^^J§§§ëĒĒĒ˙›››ũ¯¯¯˙­­Ģ˙ŽŽŗ˙˜ž.˙—˙™˙™˙™˙Ž—˙š˙‡ ˙ŗŗą˙ÂÂÂūĀĀŋúžžž˙–––~¸¸¸ˇˇˇÁÁÁZZZ```žžžyyyŊŊŊŋŋŋ ­­­ŪÁÁÁūŋŋŋüĀĀĀ˙žžž˙ÁÁÁ˙ĩĩˇ˙ˆ˙™˙Ž—˙™ūŽ—û‘šū›Ŗϙ ž§ ĸĢ*¤¤¤ÜŦŦŦ˙šššũ¯¯¯˙­­Š˙¯Ž¸˙ĸ§Z˙˜˙˜˙˜˙™˙˜˙™˙‰“˙œŸu˙ÃÂĘū¸¸ĩũšššüąąąõB¯¯¯¯¯¯‹‹‹ĨĨĨģģģ­­­ŒŒŒĸĩĩĩ˙´´´ûļļļ˙ģģģūžžŧ˙ÄÄË˙ŸĄ˙ˆ’˙™˙˜˙™ū–ú›¤ū¨¯4–¯ĩ7¨°-ĸĸĸÃŽŽŽ˙šššüŽŽŽ˙ŦŦĢ˙Ž­ĩ˙Ģ­Š˙–Ÿ ˙Ž—˙™˙™˙™˙˜˙™˙‡Ž˙­Ŧąū´´ŗ˙ąąąũĩĩĩūĒĒĒō¤¤¤lžžž ###žžžŠŠŠ.   ¯ˇˇˇ˙ŗŗŗüŗŗŗūŗŗŗūąąą˙ēē¸˙¸¸Ā˙‰#˙™˙˜˙™ū˜˙—ûĸĒ(˙ĸĒ-SĻ­2ŖĢ)ÆÆÆ™™™ĄŽŽŽūšššúŦŦŦū­­­˙­ŦŽ˙Ž­Ŧ˙ĸŠ8˙™˙˜˙˜˙˜˙Ž—˙™˙‹”˙‰N˙ĩ´ŋū´´ą˙ąąąũļļļ˙°°°˙¯¯¯āĻĻĻŽ———ĄœœœÃŦŦŦųļļļ˙ŗŗŗüŗŗŗ˙ŗŗŗ˙˛˛ŗūŗŗ¯˙ĩ´Á˙Ž’T˙Œ•˙™˙˜˙˜˙Ž—ü” ũĨŦ.æŊÂLĨ­% §%ĄĄĄ“““vŽŽŽū›››ûĒĒĒ˙ŽŽŽ˙Ŧ­Ģ˙­­ļ˙ĢŽx˙™ĸ˙Ž–˙™˙˜˙˜˙Ž—˙‘š˙‡˙Ž‘c˙ĩ´Āūĩĩ´˙˛˛°ūŗŗŗũ´´´˙ĩĩĩ˙ˇˇˇ˙ˇˇˇ˙ĩĩĩ˙˛˛˛üŗŗ´˙ŗŗŗū˛˛°ūĩĩŗ˙ļĩÁ˙‘”h˙ˆ‘˙‘š˙˜˙™ū™ū–û›Ŗ˙¨¯7†´ģD§¯+Ÿ§#”””………HŦŦŦ˙ũ§§§ū¯¯¯˙­­Ŧ˙­ŦŽ˙­­Ē˙§­B˙’›˙Ž—˙™˙™˙˜˙Ž—˙‘š˙‰‘˙ŒR˙ŦŦ˛ū¸ˇžūĩĩ´ūŗŗ°ü˛˛°û˛˛ąû˛˛ąû˛˛ąũŗŗąūŗŗ°ūĩĩŗūˇˇŊ˙­­ĩ˙‘X˙ˆ‘˙‘š˙˜˙™˙™˙Ž—ü’›ūĻŽ&樰0¤Ŧ- §. ¨#]]]RRRŠŠŠōĸĸĸ˙ĄĄĄũ¯¯¯ū­­­˙­­Ģ˙­­ŗ˙­ŽŽ˙ĄŠ$˙˜˙˜˙™˙™˙™˙˜˙‘š˙Œ•˙†Œ ˙˜šz˙Ŧ̰˙ĩ´ŋ˙¸ˇŋ˙¸ˇŧ˙ˇˇģ˙¸ˇŧ˙¸ˇž˙ļĩŋ˙­Ŧ˛˙˜›˙†Œ%˙‹•˙‘š˙˜˙™˙™˙˜ūŽ—û˜ ˙Œ’6f•$—ž%Ą¨,åååųųųĸĸĸ˧§§ūü°°°˙ŦŦŦ˙­Ž­˙Ŧ­Ģ˙Ž­ĩ˙̝s˙Ļ˙Ž—˙˜˙™˙˜˙™˙˜˙™˙™˙ˆ’˙†˙Ž’N˙—™u˙ŸŠ˙  ‘˙Ÿ‹˙—™w˙Ž’R˙†˙ˆ‘˙™˙™˙˜˙™˙˜˙˜ū˜ũ“˙˜Fė¯ŽMnkŖhĨŦ-ģģģÁÁÁššš”ĢĢĢū™™™úŽŽŽū­­­˙ŽŽŽ˙­­­˙­­­˙­­˛˙ĒŽh˙œ¤˙Ž—˙˜˙™˙™˙™˙˜˙˜˙‘š˙‘š˙–˙‰’˙‡˙†˙†˙ˆ‘˙Œ–˙š˙‘š˙˜˙˜˙™˙˜ū™ū˜˙—ųĄ§JüĄĄ§Ēčį¸ÕÔåty%——— ‡‡‡S¯¯¯˙ŸŸŸû­­­ū­­­ū­­­ūŽŽŽ˙­­­˙­­­˙­­˛˙ĒŽm˙Ĩ˙™˙Ž—˙™˙™˙˜˙™˙˜˙˜˙™˙‘š˙‘š˙‘›˙‘š˙‘š˙™˙˜˙˜˙™˙™ū™ū™˙Ž—ū—ûĻĢMųēšŧ˙ŒŒŽVš™¯ŸŸžsx‡‡‡yyyrrrŽŽŽ÷ĢĢĢ˙ŦŦŦøŗŗŗú­­­û­­­ü­­­ũŦ­ŦũŦ­Ŧū­­´ūĢ­‚ūĄ¨,˙“œūŽ—˙˜˙™˙™˙™˙™˙™˙˜˙˜˙˜˙˜˙˜˙™ū™ū™ū™ū˜ũ˜úŽ˜û” ˙Š­g˙ĩ´ģ˙ŦŦ­Ė’“„„~‰‰‰ˇˇˇœœœ„„„QĄĄĄ×ŖŖŖ˙ššš˙ļļļ˙ļļļ˙´´´˙˛˛˛˙°°°˙ޝ­ũŽŽļûŦ­ĄûĨĒWû™ĸû‘šûŽ—üŽ—ũŽ—ũ˜ũ˜ū˜ū™ū™ū˜ū˜ū˜ũ˜û˜û™ū‘š˙” ˙šĄ4˙Ĩ§Žæŗ˛ÂĄŅŅŅJÅÅÂęęë­­ŽŒŒŒTTTŋŋŋxxxaaaÖÖÖ ‚‚‚k˜ŖŖŖ°ĻĻĻÉĒĒĒá­­­đ°°°ü°°°˙°°Ž˙˛˛´˙´´ē˙ŗ´ž˙ǝc˙Ÿ§-˙˜Ą˙”˙’›˙‘š˙™˙˜˙˜˙™˙‘š˙˜˙“œ˙• ˙“›˙‘—"ā‹ŽW „‚œTŽēЍˇģģēŨŨŨ§§§ˆˆˆĩĩĩ|||rrrˇˇˇŽŽŽĀĀĀXXXlll-ŽŽŽBŽŽ[™™˜t˜˜–‹žžĻŖŖĸ¯¸ĨĻœČĄŖtŲ›ŸK斝/ō–!ų—Ÿü™Ą˙šĄ˙˜ ũ—Ÿō¤ĢטŸ"¯–(w~ƒ.: ˙ ˙˙˙˙ēÁŋŅ˙˙˙ÜÜÚæææ   ŽŽŽxxxĐĐЇ‡‡ąąą˙˙˙??? nnn“““ŖŖŖĩĩĩØØė&˙aU˙b`ˆopS'x|40‰Ž79‹:<~„-0y|8îđĩĀĻ­‚ˆ)]Z„˙h[ĐÅÃÕōō÷ĒĒĒwwwšššĻĻĻ´ŗ´˙˙˙¸šē˙˙˙˙˙æ5#˙H2˙_ZĢqrV„8Œ‘<”?…‹0}€9ķõŠŗĻŽ„‹#•,{)w{3{7‡$ũ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ü˙˙˙ø˙ā˙Ā˙Ā˙Ā˙€˙€˙€˙€€øøüüø€ø€đ€ā€?€?ĀĀĀ˙Ā˙Ā˙ā˙ā˙ø˙˙ø˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙( @ €vvv{{{ZZZ”””’’’’’’’’’nnnfffģģģ[[[{{{aaaŒŒŒ‹‹‹cccaa`lko“”yy|Ooq<uxE‹jĨĻ™­­­ĢĢĢŦŦŦŽŽŽ•••¤¤¤ĻĻϊŠŠĄĄĄSSSoooWWW ……….‰‰‰,‡‡‡-‡‡‡-‡‡‡,†††,\\YYYOnnj‰‰yqrPfh;kmE‚‚kĻĻŠÃÁ×ÁĀĐ´´ļ´´ŗ———žžžŸŸŸ‡‡‡O’’’Ö§§§ūĒĒĒūŠŠŠ˙ĒĒĒ˙ĒĒĒũŦŦŦ˙››Å¤¤Ģ™ŸžŖœŽ‰™yyj’ss]“wxd“……|“˜˜“ĻĻŗ“¨§°’ĄĄ žžœ“‹‹‹&™™™–––‘‘‘ŖŖŖ¤¤¤¤˙ŸŸŸ˙ŦŦŦūĢĢĢ˙ŦŦŦ˙ŦŦŦ˙ĢĢĒ˙¯¯°˙››”˙~Y˙rv*˙sx˙u| ˙v}˙v| ˙v{˙y~%˙ƒ†K˙˜™„˙ŦŦ°˙ąąļ˙œœŋŋŋģģģ——œŸŸŸ———Ф¤¤˙ŖŖŖûžžžũ­­­ūĒĒĒūĢĢĢūĢĢĢūĒŠ§ūą°¸ū‰Œ_üu}û–û’œû”û”žû”žû“œû™ûˆ‘û‡û‡%ų‰ûŦ̎ņ  ž’‘™—–ŠÉÉɝĩ§§§üĄĄĄû   ˙­­­ū­­­˙°°°˙°°°˙­­Ē˙ŽŽ¸˙ši˙‘›˙‘š˙™˙Ž—˙Œ–˙–˙˜˙‘›˙“œ˙”˙šū…Žû’–X˙––œrŖ¤žĻ§—ttrôôô   ÕĻĻĻ˙   ũŖŖŖ˙˛˛˛˙˛˛˛˙ŗŗŗ˙ŗŗŗ˙˛˛°˙ĩ´Ŋ˙šœp˙‡‘˙Œ–˙‹”˙’™(˙—œ<˙•›;˙Ž”"˙‡˙ˆ ˙ˆ˙ˆ˙Œ“ü…ū}Q×bg;lpAvvtEEE777]]].ĸĸĸ芊Š˙ŖŖŖū¨¨¨˙ĩĩĩ˙˛˛˛˙ŗŗŗ˙ŗŗŗ˙˛˛˛˙ˇˇ¸˙žž—˙‰‹l˙ŒŽe˙Ž}˙“’˜˙››¤˙š™Ŗ˙ŒŒ˙ƒƒ{˙““Ž˙“”’˙’’’ū’‘•ū”üŽŽŒ˙ut~•{zˆ ŠŠ‰ '''LLL˙˙˙………`¤¤¤ô¯¯¯˙§§§ũĒĒĒ˙´´´˙˛˛˛˙´´´˙ŗŗŗ˙ŗŗŗ˙ļļļ˙ĸĸŖ˙˜˜ž˙––ž˙‚…ū„„‚ū‰‰†ūŠūŠŠ‰ūˆˆŠūˆˆ‰˙„„„ū……„˙zzy˙ŽūŖŖŖũ  ˙¤ĨĸāĻĻĻᔔ” ÅÅÅ­­­‘ŠŠŠų¯¯¯˙§§§ūŠŠŠ˙ĩĩĩ˙ŗŗŗ˙ŗŗŗ˙´´´˙˛˛˛˙ļļļūŖŖĸ˙ūŒŒ‰û““’ûœœœû›››ûŸŸŸü——–ũyyxũ~˙€ū}}|˙‹‹‹ūŽ­°˙ą°´ū°¯ŗü¯¯ŗ˙°¯ą˙ĢĢŦ˙——™|ššš–––ēŦŦŦûŽŽŽ˙§§§˙¨¨¨˙ļļļ˙˛˛ŗ˙ŗŗŗ˙ŗŗŗ˙˛˛˛˙ĩĩĩ˙¨¨¨˙ąąą˙ššš˙………˙‡‡‡˙„„„˙{{{˙vvv˙rrr˙www˙www˙~~ūŖŖ ˙Ąĸ”ūŸ “ū Ą’ūũ›››åášš—ė~~~lšššĪ­­­ü°°°˙ŠŠŠūŠŠ¨˙ļļ´˙ŗŗą˙´´˛˙´´˛˙ŗŗ˛˙ĩĩĩūŦŦŦū¸¸¸û‰‰‰Žmmms|||giiiZļļļKļļļBĄĄĄÔ˛˛˛˙ŗŗ˛ûēēŧ˙Ŧ­Ŗ˙… ˙Š“ū‹”üƒ‹ūou Ylr rxlll›››ŌŦŦŦũ¨¨¨˙ĸĸĸ˙¨¨Ē˙ĩ´ž˙˛˛ģ˙ŗŗŧ˙ŗ˛ģ˙˛ą¸˙ąą°úĒĒĒū¨¨¨ŧÛÛÛßßß555qqqˇˇˇēē礤¤cĀĀĀ˙ÂÂÁúÂÂĀūžŊÄ˙—"˙™˙˜ú‘š˙”y“œ’œppp›››ËĒĒĒü›˙ĄĄĨūœ‡˙–7˙–6˙–4˙•1˙‘•P˙ŽŽ˛ųŗŗ˛ūĒĒǐÄÄÄÃÃÚšš~~~››››››———;ēēēūŋŋŋüŋŋŊũžžÆ˙‘—/˙—ū—û˜˙’œˆ‘š‘š˜˜˜ŦŠŠŠûŖŖ ˙˛˛š˙¨ŠŠ˙—˙Ž˜˙Ž—˙Ž˜˙Ž–˙ŊŊÂúÃÃÂ˙ŽŽ­ąčččĶĶĶŊŊŊŠŠŠ˛˛˛˛˛˛ŸŸŸVžžž˙ŋŋŋúĀĀŋūŧģÁ˙Ž•˙˜˙˜ú˜ū’›™™yŠŠŠøĄĄĄ˙ŽŽąūĢĢ ˙’› ˙Ž—˙˜˙š˙Š“˙¯° ũÃÃÆüļļĩö’’’.ĻĻĻšššxxxŅŅŅÄÄÄĢĢĢÃŧŧŧūŊŊŧûÃÃÅ˙°ą¤˙‰’˙™ūŽ—û” ˙¤Ģ%_Ą¨$ŸĻmmm=¨¨¨ėĸĸĸ˙­­Ŧũ¯Ž´˙ž¤:˙—˙™˙˜˙–˙•šM˙ˇļÂüŗŗŽ˙ĒĒĒā´´´G´´´ŊŊŊĢĢĢ#›››¨ŗŗŗ˙˛˛˛üŗŗŽ˙žŊÉū—œO˙Œ•ū™ūŽ—ūĨøŖ¨D%ą¸HĨ­,ááá §§§Øĸĸĸ˙ŦŦĒũŽ­ļ˙ĒŦ{˙’œ˙Ž—˙˜˙™˙ˆ‘ū›~˙ˇˇÁũĩĩą˙­­Ģ˙¤¤¤Õˇˇļɯ¯¯ķĩĩĩ˙˛˛°üŗŗ¯˙ˇļÂūŸ}˙Š“˙™˙Ž—û‘šūŖĢ*ŋ|‚ ĸĒ(šĨŨŨŨĨĨĨĩŖŖŖūĢĢĒü­­ŽūŽŽŽ˙ĸ¨8˙Ž—˙˜˙˜˙™ū‰’ūšœs˙ŗ˛Ŋũ¸¸ŋũ¸¸š˙ŗŗ˛˙ļļĩ˙ĩĩĩûļļŧ˙ŗŗŊūšuūŠ“˙™ū˜ūŽ—ü™ĄũŖĒ7E¯ļ8ŖĒ(ĸĢŦŦŦĸĸĸ„ŖŖŖū¨¨¨ûŽŽ­˙­­ŗ˙­Ž’˙›Ŗ˙–˙™˙˜˙™˙‹”ūŽ”7ūŸĄ€ūĒĒĻü¯¯ŗû¯Ž˛ũĒǧūŸ ū”9ū‹”˙™˙˜˙˜ûšū–(ļ”ĻŽ ¨%¤Ŧ%œœœ–––IĨĨĨū¤¤¤üŽŽ¯ū­­Ģū­­ĩū̝€˙˜Ą ˙–˙˜˙™˙‘š˙Ž˜˙Š“˙Š’ ˙‹’˙‹’˙Š‘ ˙Š“˙Ž˜ū™ū˜ū˜ũŒ–øž¤@˙››ĸ\ŠĒžĻ¨„ŖĢ0ššš’’’ŠŠŠôĒĒĒ˙°°°ûŦŦŦûŦŦĢû­ŦˇûĢŽ„ûšĸü—ũŽ—ū˜ū™ū‘šūš˙™ū™ūšū‘šū˜ü˜û˜ū™˙¤ŠK˙˛˛ŗō†…‘Ž§žœÄÁÁÁēēēĨĨĨgžžžė˛˛˛˙ŗŗŗ˙ąąą˙ąą¯˙°¯ˇ˙¯¯Ÿ˙¤ŠN˙–ž ˙™˙˜ū˜üŽ˜üŽ—üŽ—üŽ˜ũ˜˙™˙’›˙“›˙žĸdŲŦĒŧާ§˛0… ĄŽ Ÿĩ{{{———jjj–––L˜˜˜f¨¨¨ĸĸĸĒǍˇĒĒ¯ÍŽ­ļāĒŦđĄĻTú˜Ÿ(˙”œ˙“œ ˙” ˙”œ ˙• ˙•ņ˜ɇ(ˆy{Y@ljv­ĒËäãį›œ˜™™™‚‚‚ĻĻĻkkkĻĻĻ´´´˛˛˛ûüüĘĘĮwrŧ”Ō%••–3‰OCŒ’.P•œ-Y’˜+T•›-<ˆŒ:ruDy|(JUŠŠĩą×××Ü÷÷ũŠŠŠ–––ĨĨĨggg§§§¸¸¸ššš˙˙˙ČČĮ‡ƒ×œ˜Õ—–’•›E”š+¤*™ )›ĸ.–œ5sv@x{+€ˆtx-ÁŋĶÚÚŨ‘“ƒ‚†•œ5šĄ" ¨"œ¤ ™ #Š&˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙đ˙Ā€€?€?€`āđāā€Ā€€€ĀĀ?Āø˙˙ū?˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙(0 ` §§§ĨĨĨąąąŗŗŗŗŗŗĩĩĩÁÁÁˇˇˇccc›››‘‘‘   ĄĄĄĄĄĄŖŖŖ‘‘‘¤¤­ÆÄá˙………{{{LLL¸¸¸ŽŽŽŖŖŖ‚‚‚{{{$™™™HšššE™™™E›››HŠ‹Š&ĸĸĒØÕ˙,V`ÚÚØîîéŦŦŦšššĸĸĸ –––~œœœöĢĢĢ˙ĢĢĢ˙ĒĒĒ˙­­­˙ŖŖ¤÷——×‡‰hØtx;Ôqu.Ķuy<Ķ‚…]Ķ—˜ŽĶŠŠŗŅĒŠŽĶ““’1˜˜—¨¨¨œ‘ÆÆÆĢ¤¤¤˙ĸĸĸūŦŦŦüĒĒĒũĒǍũ¯¯´ü—˜ƒ˙sz˙‚Š˙‰’˙‹”˙‰’˙…˙ƒŠ˙‹A˙ŖŖ–˙ĻĨ¯ŸÄÄČģģģ‘’x“““ĖĖĖ ŖŖŖâŖŖŖüĸĸĸüŽŽŽ˙°°°ū¯¯Žūą°š˙ž zū™ũ“ũ™ũ™ũšũ“œũ‘›ũ‹–û‡ũ’–ZķŦŠÕ§ĻÉ–˜ƒ“““wwwC¤¤¤īŖŖŖū§§§ũ´´´ūŗŗŗ˙ŗŗ˛˙ˇļģ˙Ŗ¤’˙†Œ%˙А(˙’–Y˙œžv˙“–e˙„ˆ<˙ŒF˙Ž’Oū”Mü‡ŠP˙tti‹yzhƒgĄĄĄ‘‘‘¨¨¨øŠŠŠ˙ĢĢĢūĩĩĩ˙˛˛˛˙˛˛˛˙ļļĩū§§Š˙“’›ūŒ•ü‡†’ü™üšûˆ‡“ũŒ˜˙‹‹–ū‚Žū••›ūœ˙ššœąššŸ¤‡‡…———ŗŦŦŦüĒĒĒ˙ĒĒĒū´´´˙˛˛ŗ˙˛˛˛˙ĩĩļ˙ŠŠŠ˙••”˙‹‹Š˙••“˙””“˙Ž˙zzx˙vvt˙ttr˙ŠŠ‰˙¯Žē˙¯¯ģū¯Ž¸˙¯¯ĩ˙ĸĄŠÕĄĄŠ,žžžÖŽŽŽũĢĢĢ˙ĒĒĒ˙ĩĩŗ˙ŗŗą˙ŗŗ˛˙ĩĩ´ū¯¯¯˙˛˛˛ũ•••×zzz˃ƒƒÁ———ą‡‡‡č‘‘˙––™ũ¤Ĩš˙’—Lū”™IūŽ’Jú_‡ZƒwzMNĄĄĄßĢĢĢū¤¤¤˙ЍĢū´ŗģ˙ąąš˙˛ą¸˙ąąŗû­­Ŧ˙¨¨¨ŖžžžĒĒĒĀĀĀģģģĒĒĒ]ÂÂÂ˙ÆÆČüššĩū‹”ū—ũŒ–ūˆ.ˆ‘‹”ŖŖŖÖ¤¤ŖũŖĸŠ˙ž }˙–)˙‘—*˙”$˙ ĸ|û¸ˇžūĒǧnˇˇˇžžžĀ¤¤¤.ŧŧŧūŋĀŋūŧŧŋũ˜˙˜ü™ū”Q’œ“ŸŸŸ°ĨĨŖü­Ŧĩ˙§Ēū–˙˜˙‹•˙žĸVûÆÅŌ˙ąąŦąĪĪĪĖĖĖÂÂÂŧŧŧ§§§nŊŊŊ˙ÃÂÅûļļ°ūŒ•ū˜ũ‘š˙ §>šĸ˜Ąšššr¤¤¤÷ŠŠĢ˙ŽŽĻū”œ˙Ž—˙™˙Ž•ūŗ˛ļüļļ¸˙›|ŸŸŸ ¨¨¨ĒĒĒO°°¯đŗ´°ũŋžÎũĄ¤j˙‹•ũŽ—ūšŖņ™ E˛¸NĢŗ0”””+¤¤¤ę¨¨Ļ˙°¯šũŖ¨Q˙—˙™˙–˙‘–Cūŗ˛ŋūˇˇŊ˙¯¯Žčޝ­Ū°°¯˙ĩĩļ˙ˇļÅūŖ¤˙‹”˙˜û‘š˙Ą¨&ω“˜ ̞/ĶĶĶŖŖŖÍĻĻĨūŽŽ˛üޝĸūšĸ˙Œ–˙‘™ūŒ–˙•1ūϧ”ũ˛˛¸˙´´ž˙ŗŗŊū­­Ŧū›ži˙‹“ū™ū—ü“œö¤­.%ĸŠ)ŖĢ%ĻŽ%ŽŽŽĄĄĄžĨĨĨū­­Ģø­Ŧ´üŦމũ–Ÿ ũŒ–ū™ū˜ū‹”ū– ũ’˜7ü‘–0ūŒ”ūŒ•ū™üŒ–ų“œ˙ŸĸcēąģKĢą\¤Ŧ)ŽŽŽ§§§P¨¨¨˙°°°˙ޝ­˙¯¯ˇ˙ŦŽ˙šĸ$˙Ž˜ūŽ—ũ™ü˜ûŽ—ûŽ˜û™ū™˙™˙”œ˙ĒŦ‡ī­ĢÄO˛°ÄŧŊŧŖĒ(ģģģŖŖŖ‡‡‡T§§§ŖŽŽŽŊĒĒ¨ÕŽŽļé­­ŠøĻĒf˙™ (˙“› ˙‘š˙’›˙’š˙’›˙— 鎕(¯“–rb—˜‘ĨĨĢ”•ŠŧŧēžžžŧŧŧŠŠŠēēēÔÔÕy|H ŠĒĻį.ĄĄ¤A˜œNS—&b–"k’™#_“$9‚‡(E>’ļĩŋģ­˙ĒŠˇ ŸĨŊž¸ĖĖĖuuu´´´ĪĪĪBBCŽ‰˜ŦŠÕϧ›› B™Ąšĸ—Ÿ•œ#‚†)][^}„dpÃÃĮˇˇš´´ŗŧģÄްŠĄŠ'˜ ›Ŗ›¤Ÿ§˙˙˙˙˙˙˙˙˙˙˙˙Ā?€€€<><€€€€Ā?˙Ã˙˙˙˙˙˙˙˙˙˙(  @´´´×××°°° ąąą ĒĒĒÍÍÎĩļ°”•§¨ĨŠŠĒŸŸŸ˜˜˜ĄĄĄŦŦŦ–––§§¨§§ĻŠŠŽŋŊ䜚ĩhf~Šŋļ˛˙žžžŠŠŠŠŠˆ“““¤¤¤¨¨¨“““F¤¤¤d¤¤ŖdĄĄ¤T´ŗË)Ÿ°'jh~%‡„°%̧é&ŸŸŦ"~~w‚„h“^˜˜˜   Ĩĸĸĸ˙ĢĢĢ˙ŦŦŽ˙ŖŖ˙‡‹BūƒŠũ€† ü„Š ü“NúŖ¤˜ø ŸĻ8§Ĩē­­ļ˜˜˜\ĸĸĸūĻĻĻü°°¯ú˛ąˇú¤Ĩ‘ûˆ’ū‘šū“›˙Ž—˙Š”ú”˙’f ™ŸI•›GûûûĄĻĻĻúŦŦŦū´´´˙ĩĩļūŦŦĒ˙qũŒ}ü•••ū‰Š}ũ‹Œ}ū‰Šzũ‘…÷’•r{‘“oAššŖŖŖÔŦŦŦũ­­­˙´´´ū´´´˙°°°˙™™Ÿ˙‰‰Œ˙˙„˙zz~˙Žū§¨ĄūĨĨĨ˙žž­äŖĸZ§§§ëŠŠ¨ũĒĒŦ˙˛˛´˙ąąŗü°°¯ū§§ĻŸ››™AŠŠŠ1ĄĄžsĩĩ¸ū˛ŗĸû• ū‹”č{%”›ŖŖžáĻĨŽūžĄo˙—ū—%ú´´°˙ąą´UÁÁĮ´´ŗĒǧ)ÂÂÃ˙ēģŗũ—ū™ôž§ šŖ  ™­¨¨¯ü¨Ē€˙Ž˜˙‹•ũĒŦ…ūˇˇÄĪģģē(¸¸ĩŦŦϰŧģČūŠĢú‹•ū—ŸŨ‰7…Ž™™˜aĨĨĨôŽŽ°ūšĄ*ū–˙Ž–ū§Š’˙¯Ž¸÷ŗŗŊņĩ´ŋ˙§¨’ūŽ–ø˜ũšĸ…Š“ •žŖŖŖĻĻ¤æ­­˛˙Ŧ­”ũ•žüŒ–ûŒ•ø—>ũ›ŸUū•›8øŒ•ũŽ˜˙˜Ÿ-ũ¤¨€°°ŠĨŠi°°°™™™~ĒĒ¨čŽŽ´÷Ģ­—˙œĸ;˙’› ˙Ž˜˙Ž—˙˜˙˜û–œ3Ėύ‰k´ēˇ¸Ž–§˙˙˙ssw‘’†"›š­7¯ŽŊMŸŖPb‘˜"s•œy•œ^Œ’&+”;™Ą(jW=ēģt ĐĐĐIIIĄĄšĒ¨ČŗąČŸŖT•–ž”œŒ’)“Gĸ¨<}ciĨ­AģģšˇˇŧĩĩŠĸ¨E • ” ‘𠐗—ŸĨ› N˙˙˙˙ã˙€ƒÃƒ€€ü?˙˙˙˙dcm2niix-1.0.20171215/dcm2laz/dcm2.lpi000066400000000000000000000044761322051203000166560ustar00rootroot00000000000000 <ResourceType Value="res"/> <UseXPManifest Value="True"/> <Icon Value="0"/> </General> <i18n> <EnableI18N LFM="False"/> </i18n> <VersionInfo> <StringTable ProductVersion=""/> </VersionInfo> <BuildModes Count="1"> <Item1 Name="Default" Default="True"/> </BuildModes> <PublishOptions> <Version Value="2"/> <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/> <ExcludeFileFilter Value="*.(bak|ppu|o|so);*~;backup"/> </PublishOptions> <RunParams> <local> <FormatVersion Value="1"/> </local> </RunParams> <RequiredPackages Count="1"> <Item1> <PackageName Value="LCL"/> </Item1> </RequiredPackages> <Units Count="2"> <Unit0> <Filename Value="dcm2.lpr"/> <IsPartOfProject Value="True"/> <UnitName Value="dcm2"/> </Unit0> <Unit1> <Filename Value="form.pas"/> <IsPartOfProject Value="True"/> <ComponentName Value="Form1"/> <HasResources Value="True"/> <ResourceBaseClass Value="Form"/> <UnitName Value="form"/> </Unit1> </Units> </ProjectOptions> <CompilerOptions> <Version Value="11"/> <Target> <Filename Value="dcm2"/> </Target> <SearchPaths> <IncludeFiles Value="$(ProjOutDir)"/> <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/> </SearchPaths> <Linking> <Debugging> <GenerateDebugInfo Value="False"/> </Debugging> <LinkSmart Value="True"/> <Options> <Win32> <GraphicApplication Value="True"/> </Win32> </Options> </Linking> <Other> <CompilerMessages> <MsgFileName Value=""/> </CompilerMessages> <CompilerPath Value="$(CompPath)"/> </Other> </CompilerOptions> <Debugging> <Exceptions Count="3"> <Item1> <Name Value="EAbort"/> </Item1> <Item2> <Name Value="ECodetoolError"/> </Item2> <Item3> <Name Value="EFOpenError"/> </Item3> </Exceptions> </Debugging> </CONFIG> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/dcm2laz/dcm2.lpr��������������������������������������������������������������0000664�0000000�0000000�00000000544�13220512030�0016657�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������program dcm2; {$mode objfpc}{$H+} uses {$IFDEF UNIX}{$IFDEF UseCThreads} cthreads, {$ENDIF}{$ENDIF} Interfaces, // this includes the LCL widgetset Forms, form { you can add units after this }; {$R *.res} begin RequireDerivedFormResource := True; Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end. ������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/dcm2laz/dcm2.lps��������������������������������������������������������������0000664�0000000�0000000�00000014266�13220512030�0016666�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <CONFIG> <ProjectSession> <Version Value="9"/> <BuildModes Active="Default"/> <Units Count="6"> <Unit0> <Filename Value="dcm2.lpr"/> <IsPartOfProject Value="True"/> <UnitName Value="dcm2"/> <UsageCount Value="38"/> </Unit0> <Unit1> <Filename Value="form.pas"/> <IsPartOfProject Value="True"/> <ComponentName Value="Form1"/> <HasResources Value="True"/> <ResourceBaseClass Value="Form"/> <UnitName Value="form"/> <IsVisibleTab Value="True"/> <EditorIndex Value="0"/> <WindowIndex Value="0"/> <TopLine Value="61"/> <CursorPos X="34" Y="83"/> <UsageCount Value="38"/> <Loaded Value="True"/> <LoadedDesigner Value="True"/> </Unit1> <Unit2> <Filename Value="../../../lazarus/fpc/2.6.0/source/packages/fcl-registry/src/registry.pp"/> <UnitName Value="registry"/> <WindowIndex Value="0"/> <TopLine Value="427"/> <CursorPos X="40" Y="441"/> <UsageCount Value="16"/> </Unit2> <Unit3> <Filename Value="../../../lazarus/components/lazutils/fileutil.pas"/> <UnitName Value="FileUtil"/> <EditorIndex Value="3"/> <WindowIndex Value="0"/> <TopLine Value="107"/> <CursorPos X="70" Y="116"/> <UsageCount Value="14"/> <Loaded Value="True"/> </Unit3> <Unit4> <Filename Value="LibTar.pas"/> <UnitName Value="LibTar"/> <EditorIndex Value="2"/> <WindowIndex Value="0"/> <TopLine Value="136"/> <CursorPos X="18" Y="75"/> <UsageCount Value="13"/> <Loaded Value="True"/> </Unit4> <Unit5> <Filename Value="untar.pas"/> <UnitName Value="untar"/> <EditorIndex Value="1"/> <WindowIndex Value="0"/> <TopLine Value="92"/> <CursorPos X="26" Y="157"/> <UsageCount Value="13"/> <Loaded Value="True"/> </Unit5> </Units> <General> <ActiveWindowIndexAtStart Value="0"/> </General> <JumpHistory Count="30" HistoryIndex="29"> <Position1> <Filename Value="untar.pas"/> <Caret Line="187" Column="28" TopLine="177"/> </Position1> <Position2> <Filename Value="untar.pas"/> <Caret Line="189" Column="18" TopLine="176"/> </Position2> <Position3> <Filename Value="untar.pas"/> <Caret Line="5" Column="70" TopLine="1"/> </Position3> <Position4> <Filename Value="untar.pas"/> <Caret Line="25" Column="73" TopLine="16"/> </Position4> <Position5> <Filename Value="untar.pas"/> <Caret Line="32" Column="31" TopLine="16"/> </Position5> <Position6> <Filename Value="untar.pas"/> <Caret Line="48" Column="28" TopLine="25"/> </Position6> <Position7> <Filename Value="untar.pas"/> <Caret Line="78" Column="39" TopLine="68"/> </Position7> <Position8> <Filename Value="untar.pas"/> <Caret Line="24" Column="5" TopLine="7"/> </Position8> <Position9> <Filename Value="form.pas"/> <Caret Line="62" Column="15" TopLine="44"/> </Position9> <Position10> <Filename Value="form.pas"/> <Caret Line="67" Column="50" TopLine="47"/> </Position10> <Position11> <Filename Value="form.pas"/> <Caret Line="70" Column="29" TopLine="47"/> </Position11> <Position12> <Filename Value="form.pas"/> <Caret Line="69" Column="37" TopLine="56"/> </Position12> <Position13> <Filename Value="form.pas"/> <Caret Line="72" Column="27" TopLine="56"/> </Position13> <Position14> <Filename Value="form.pas"/> <Caret Line="186" Column="16" TopLine="158"/> </Position14> <Position15> <Filename Value="form.pas"/> <Caret Line="4" Column="99" TopLine="1"/> </Position15> <Position16> <Filename Value="form.pas"/> <Caret Line="47" Column="24" TopLine="20"/> </Position16> <Position17> <Filename Value="form.pas"/> <Caret Line="72" Column="27" TopLine="44"/> </Position17> <Position18> <Filename Value="form.pas"/> <Caret Line="65" Column="1" TopLine="59"/> </Position18> <Position19> <Filename Value="form.pas"/> <Caret Line="73" Column="27" TopLine="59"/> </Position19> <Position20> <Filename Value="form.pas"/> <Caret Line="149" Column="16" TopLine="121"/> </Position20> <Position21> <Filename Value="form.pas"/> <Caret Line="187" Column="16" TopLine="159"/> </Position21> <Position22> <Filename Value="form.pas"/> <Caret Line="4" Column="111" TopLine="1"/> </Position22> <Position23> <Filename Value="form.pas"/> <Caret Line="47" Column="24" TopLine="29"/> </Position23> <Position24> <Filename Value="form.pas"/> <Caret Line="76" Column="6" TopLine="60"/> </Position24> <Position25> <Filename Value="form.pas"/> <Caret Line="16" Column="37" TopLine="1"/> </Position25> <Position26> <Filename Value="form.pas"/> <Caret Line="47" Column="24" TopLine="20"/> </Position26> <Position27> <Filename Value="form.pas"/> <Caret Line="84" Column="24" TopLine="66"/> </Position27> <Position28> <Filename Value="form.pas"/> <Caret Line="147" Column="16" TopLine="119"/> </Position28> <Position29> <Filename Value="form.pas"/> <Caret Line="185" Column="16" TopLine="157"/> </Position29> <Position30> <Filename Value="form.pas"/> <Caret Line="283" Column="19" TopLine="265"/> </Position30> </JumpHistory> </ProjectSession> <EditorMacros Count="0"/> </CONFIG> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/dcm2laz/dcm2.res��������������������������������������������������������������0000664�0000000�0000000�00000044210�13220512030�0016651�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000���������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���˙˙��˙˙�������������������� ���˙˙�˙˙���������������<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="CompanyName.ProductName.YourApp" type="win32"/> <description>Your application description here.</description> <dependency> <dependentAssembly> <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/> </dependentAssembly> </dependency> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> <security> <requestedPrivileges> <requestedExecutionLevel level="asInvoker" uiAccess="false"/> </requestedPrivileges> </security> </trustInfo> </assembly>�>���0���˙˙�M�A�I�N�I�C�O�N�����������������������00��� �¨%��� ��� �¨������ �ˆ ������ �h�����¨%�� ���˙˙�˙˙���������������(���0���`���� �����€%��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ŠŠŠ����������������������������������������������������������������������������������������������������������������������������������¨¨¨�ššš����˙˙˙������������������������������������ŽŽŽ‘‘‘’‘‘’’’~~€zz{{{{|||~~€€€€€€ƒƒƒƒƒƒ………†††jjj��������������������������������������������������������ƒƒƒ�­­­�˛˛˛����������������������������������������‘‘’�‚‚‚�‹Š“�™˜Ž�ą�œšĘ�ĄŸÎ�Ŗ Ņ�ĸŸĐ�™—Ã�‡†Ļ�vuƒ�mmn�rrq�rrr�ttt�}}}�lll�xxx�888�€€€�������������������������������������“““�����§§§�kkk zzz———ÖĻĻĻØŖŖŖØŖŖŖŲĸĸĸŲŖŖŖŲĸĸĸŲĸĸĸØĻĻĻۛ››Âsss9rrp!pqm#„‚#œ›´"•“ŧœšĖĸŸĶ¤ĸÖ¤ĄÕ›™Į„ƒĻggtPPKSTG]]Ybbbiiibbb___�‚‚‚�‰‰‰����������������������������������œœœ�‰‰‰�ˇˇˇ�¯¯¯HšššŌššš˙¨¨¨˙°°°˙ŽŽŽ˙¯¯¯˙¯¯¯˙ŽŽŽ˙ŽŽŽ˙ŽŽŽ˙ąąą˙¯¯¯˙››œũŗ˛ˇõŽŽ°ö¤¤ ö””…ö‚ƒlņwyYđrtPđstPđxzZđƒ„l𒓃𡡞đĢĢ¯đ­­˛đ¨¨ŠđĨĨŖđĨĨĨė§§§ņ“““ÅÅÅ�ŽŽŽ���������������������������������———’’’�‰‰‰ŠŠŠŸ˙ŠŠŠ˙———úĒĒĒũĢĢĢũĒĒĒũĢĢĢũĢĢĢũĢĢĢũĢĢĢũĒĒĒũĢĢĢũĢĢŦüšš“˙ŒŽr˙lo;˙`d˙_e˙ek�˙kq�˙nu�˙mt�˙kq�˙gm�˙ek˙hm˙uy=˙Žr˙§§¤˙´´ģ˙°°˛˙¯°¯˙ĨĨĨī```�€€€��������������������������������¤¤¤ĢĢĢ•••ͨ¨¨˙§§§û¤¤¤ü˜˜˜˙­­­ūĢĢĢūĢĢĢ˙ŦŦŦ˙ŦŦŦ˙ŦŦŦ˙ŦŦŦ˙ĢĢĢūĢĢŠū°°ļ˙hj7ūX^�ũu|�ũˆ�ũ‘›�ũ•ž�ũ•žũ•Ÿũ•žũ•Ÿũ•ž�ũ’›�ũŒ•�ũŠ�ũt|�ũqwũ„‡SũĨĨĄũ˛˛¸úŦŦĒ˙††…p­­­�ŠŠŠĒĒĢ�����������������������������ppp�lll"ĸĸĸøĻĻĻüĻĻĻũĄĄĄ˙šššūŽŽŽ˙ĒĒĒ˙ŦŦŦ˙ĢĢĢ˙ĢĢĢ˙ĢĢĢ˙ĢĢĢ˙ĢĢĢ˙Ģ̍˙ŦŦļ˙…Š4˙’œ˙•˙‘š�˙˜�˙˜�˙˜�˙Ž—�˙—�˙—�˙˜�˙˜�˙™�˙’œ˙”ž˙š�˙‚‹�˙x˙‘kûą°¸ūŖŖŖĶÉĘÆŠĒĨžšæ������������������������������ƒƒƒJŖŖŖūĨĨĨũĻĻĻūžžžūœœœ˙¯¯¯˙ĢĢĢ˙ŦŦŦ˙ŽŽŽ˙¯¯¯˙¯¯¯˙ŽŽŽ˙ŦŦŦ˙ĢŦŠ˙Ŧ̎˙Œ‘;˙Ž—�˙Ž—˙˜�˙˜�˙˜˙™˙‘™˙‘š˙‘š˙™˙˜˙˜�˙Ž—�˙Ž—˙—˙’›˙‘š�ūŠ�ūƒ†>üĨĨŠ˙}}„I˜”¤�•”ƒ‚‰��������������������������sĨĨĨūĨĨĨû§§§˙˙   ˙ŽŽŽ˙ŽŽŽ˙˛˛˛˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙˛˛˛˙¯¯­˙ŽŽš˙Œ‘A˙™�˙™˙™˙‘š�˙™�˙Œ•�˙‰“�˙‰“�˙Š”�˙—�˙‘›�˙“œ�˙’›�˙‘›�˙‘›�˙‘š�˙™�˙”ž�˙‹•�ûƒ‰!ū’’‰˛“Ļ›�̝‚����‰‰‰���������������������¯¯¯�“““šĻĻĻ˙¤¤¤ú§§§ū›››˙ĸĸĸ˙˛˛˛˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗą˙ĩĩŋ˙‹G˙‡‘�˙Œ•�˙Œ–�˙‰‘�˙Œ“&˙—›P˙žĄi˙ž l˙še˙”J˙ƒŠ˙‰˙ˆ˙„‹˙ƒ‰˙ƒ‰˙‚‰!˙ˆ&ū…Š,ū‚‡)ũnqFų:F?(dfZ�llfƒƒƒœœœƒƒƒˇˇˇ�˛˛˛�����ņņņ�›››¸ĻĻĻ˙ĨĨĨûĒĒĒ˙œœœ˙¨¨¨˙ļļļ˙˛˛˛˙ŗŗŗ˙´´´˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙˛˛˛˙ļļ¸˙ƒ˙…‡j˙ˆŠd˙„T˙‚„r˙™˜ ˙š™¤˙Ÿž¨˙ĨĨŽ˙œĻ˙—–Ą˙‰‰˙yyv˙ŽŽ’˙””™˙””š˙––ž˙——Ÿ˙˜˜Ąū˜˜ĸ˙—–ĸü™™ū‡…‡Ō’‘•ĨĻĨ�zzz�ŖŖŖ��ÔÔÔ�ËËË�kkk�úúúĐ¨¨¨˙ĢĢĢü°°°˙   ˙ĒĒĒ˙ĩĩĩ˙˛˛˛˙´´´˙ŗŗŗ˙´´´˙´´´˙ŗŗŗ˙´´ŗ˙˛˛ŗ˙ĩĩĩ˙––˜˙˜˜˙ššĄ˙œĨ˙——›˙„„ƒ˙|˙††„˙ˆˆ†˙††„˙‡‡…˙}˙——˜˙™™™˙••”˙’’˙Ž˙‹‹‰˙‡‡…˙ƒƒ‚ūŽŽŒ˙ŽŽŽü˙zzz͍v‹‹‹{ˆˆˆ}wwwNŽŽŽ�ÄÄÄ```� (âĢĢĢ˙­­­ũ¯¯¯˙   ˙ŠŠŠ˙ĩĩĩ˙ŗŗŗ˙ŗŗŗ˙´´´˙´´´˙´´´˙´´´˙ŗŗ´˙ŗŗ˛˙ļļˇ˙–––˙””’˙›œš˙ŠŠ‡˙tts˙‚‚‚˙ŒŒŒ˙˙˙’’’˙•••˙‡‡‡˙˙yyy˙zzz˙zzz˙zzz˙~~~˙www˙„„„˙ŽŽŽūĒĒĒ˙ĒĒĒü­­­˙ŦŦŦ˙ĢĢĢ˙ŦŦŦ˙™™™ā°°°ŗŗŗeeeqqqTĸĸĸė­­­˙ŦŦŦū°°°˙ĄĄĄ˙¨¨¨˙ļļļ˙ŗŗŗ˙´´´˙´´´˙´´´˙´´´˙´´´˙´´´˙˛˛˛˙ˇˇˇ˙˜˜˜˙‘‘‘˙˜˜˜˙ƒƒƒ˙•••˙ššš˙™™™ū›››˙šššūšššū›››ūˆˆˆūzzz˙†††ū†††˙†††˙‡‡‡˙€€€˙€€€˙ĨĨ¤˙ĢŦĒ˙ĢĢŠūĢĢĒ˙ĢĢĒũĢĢĒûĢĢĒųŦŦĢ÷ĒĒĒûxxx§TTT�ĢĢĢ�}}}ŠŠŠō­­­˙­­­ũ°°°˙ĸĸĸ˙§§§˙ļļļ˙ŗŗŗ˙´´´˙´´´˙´´´˙´´´˙´´´˙´´´˙˛˛˛˙ˇˇˇ˙šššū‘‘‘˙›››ũûšššûšššû˜˜˜ú˜˜˜ú–––ú•••ú•••û}}}ürrrū|||˙xxxūyyy˙sss˙‚‚˙ĢĢŦ˙ąąĩ˙Ž­ą˙¯¯ŗ˙¯¯ŗ˙¯Ž´˙°¯ŗ˙°°˛˙ŽŽŽ˙°°¯˙¯¯¯˙‘‘‘ž•••‚‚‚ĢĢĢöŽŽŽ˙­­­ū°°°˙ĸĸĸ˙§§§˙ļļļ˙ŗŗŗ˙´´´˙´´´˙´´´˙´´´˙´´´˙´´´˙ŗŗŗ˙ĩĩĩūĨĨĨ˙ēēēūĮĮĮ˙†††˙xxx˙€€€˙}}}˙|||˙zzz˙zzz˙{{{˙xxx˙tttūuuuūwww˙{{{˙uuu˙˜˜™˙  œ˙—™†˙š›ˆ˙™š†˙™š†˙™›„ū•–‚˙’’Œč–•™Ô––™ÖŸŸŸŌžžžã___ZŠŠŠŗĢĢĢų­­­˙ŦŦŦū¯¯¯˙ĸĸĸ˙§§§˙ĩĩĩ˙˛˛ŗ˙ŗŗ´˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙˛˛˛˙ļļļ˙¤¤¤ũŊŊŊū¤¤¤í‘‘‘´Šnnn›oooŽnnnjjjtlllgwwwUiii†¤¤¤˙ŦŦŦüŽŽŽūąąąū´´ŗ˙ššē˙§§ĸ˙€ˆ ˙‰’�˙‡�˙ˆ�˙Š“�ú‰�˙cifl�nu�������rrrŒŒŒˇĢĢĢúŽŽŽ˙¯¯¯ūąąą˙ĄĄĄ˙¨¨¨˙ĩĩŗ˙˛˛°˙ŗŗą˙ŗŗą˙ŗŗą˙ŗŗą˙ŗŗą˙ŗŗ˛˙ŗŗŗ˙ļļļūĻĻĻûĩĩĩ˙ŠŠŠ^ŦŦŦ�ĻĻĻ�ˆˆˆ�†††��www�uuu�ÎÎÎ�ÛÛÛ�­­­ČÅÅÅūÂÂÂüÂÂÂ˙ÁÁÁ˙ÁÁĀ˙ŋŋÉ˙Ž”1˙š�˙‘™˙‘š�˙™�ü‘›�˙Ž—ʍŸ��¨¸��48��DDD�^^^�ŒŒŒˇŦŦŦúŦŦŦ˙   ūĒĒĒ˙žžž˙ĢĢŦ˙¸ˇÁ˙ĩ´ŋ˙ļĩÁ˙ĩĩĀ˙ļĩĀ˙ĩĩĀ˙ĩ´Ā˙´´š˙°ą°˙°°ąũĨĨĨ˙¯¯¯įjjjkkk“““€€€zzzuuuyyy˛˛˛ĩĩĩ�•••nŧŧŧ˙ŋŋŋûŋŋŋūŋŋŋ˙žžŧ˙ÂÁĖ˙”—R˙‹•�˙˜˙Ž—�˙˜�ũŽ—�˙“›âVkY ¨­E�ĸ¨*���������ŠŠŠŗ­­­ų§§§˙”””ūŸŸŸ˙’’“˙šš•˙’•X˙“P˙“O˙“M˙Ž“K˙“K˙Œ‘D˙“p˙¨¨­˙ŖŖĸü˛˛˛ūŦŦŦČ����õõõ�eee�����������������vvv�ŸŸŸŽŽŽ�‰‰‰Iģģģ˙ĀĀĀũŋŋŋũŋŋŋ˙ŋŋŊ˙ÃÂĖ˙–š]˙‹”�˙˜˙˜�˙™�üŽ—�˙’›襨š¤�ĸ§*���������ƒƒƒœ­­­ö§§§˙ŸŸŸũ˛˛ą˙ŽŽ°˙ĢĢ ˙˜˙Œ–�˙—�˙—�˙Œ–�˙Ž—�˙Š”�˙‘–G˙ÃÃÎ˙ĀÁžüÄÄÄ˙ĢĢĢÎ˙˙˙����ŒŒŒ������������������žžžŒŒŒNģģģūĀĀĀũŋŋŋũĀĀĀ˙žžŧ˙ÂÂĖ˙•™W˙Œ•�˙™˙Ž—�˙™�ũ˜�˙’›įą¸)Ļ�ĸĢ*���������{{{yŦŦŦņŠŠŠ˙›››ū¯¯Ž˙ŦŦ­˙Ŧ­Š˙“›˙˜˙™˙™˙™˙˜˙Ž˜˙’3˙ŊŊÆ˙žžŊũÁÁÁūŗŗŗđEEE```zzz�ÍÍÍ���������ƒƒƒ�zzz�ÄÄÄÉÉÉ�šššŋŋŋ˙ŋŋŋûŋŋŋūŋŋŋ˙ŋŋŊ˙ŋžÉ˙”;˙Ž˜�˙˜˙˜�˙˜�üŽ—�˙‘šā'>�ĸĒ:�ĸĒ*���������^^^J§§§ëĒĒĒ˙›››ũ¯¯¯˙­­Ģ˙ŽŽŗ˙˜ž.˙—�˙™˙™�˙™�˙Ž—�˙š�˙‡ ˙ŗŗą˙ÂÂÂūĀĀŋúžžž˙–––~¸¸¸�ˇˇˇÁÁÁZZZ�```�žžž�yyyŊŊŊŋŋŋ ­­­ŪÁÁÁūŋŋŋüĀĀĀ˙žžž˙ÁÁÁ˙ĩĩˇ˙ˆ˙™�˙Ž—�˙™�ūŽ—�û‘šū›Ŗϙ �ž§ �ĸĢ*���������¤¤¤ÜŦŦŦ˙šššũ¯¯¯˙­­Š˙¯Ž¸˙ĸ§Z˙˜�˙˜˙˜�˙™�˙˜�˙™˙‰“�˙œŸu˙ÃÂĘū¸¸ĩũšššüąąąõB¯¯¯�¯¯¯�‹‹‹���ĨĨĨģģģ�­­­�ŒŒŒĸĩĩĩ˙´´´ûļļļ˙ģģģūžžŧ˙ÄÄË˙ŸĄ˙ˆ’�˙™˙˜�˙™�ū–�ú›¤ū¨¯4–¯ĩ7�¨°-����������������ĸĸĸÃŽŽŽ˙šššüŽŽŽ˙ŦŦĢ˙Ž­ĩ˙Ģ­Š˙–Ÿ ˙Ž—�˙™�˙™�˙™�˙˜�˙™�˙‡Ž˙­Ŧąū´´ŗ˙ąąąũĩĩĩūĒĒĒō¤¤¤lžžž �###�žžž�ŠŠŠ.   ¯ˇˇˇ˙ŗŗŗüŗŗŗūŗŗŗūąąą˙ēē¸˙¸¸Ā˙‰#˙™�˙˜˙™�ū˜�˙—�ûĸĒ(˙ĸĒ-SĻ­2�ŖĢ)������������ÆÆÆ�™™™ĄŽŽŽūšššúŦŦŦū­­­˙­ŦŽ˙Ž­Ŧ˙ĸŠ8˙™�˙˜�˙˜�˙˜�˙Ž—�˙™˙‹”�˙‰N˙ĩ´ŋū´´ą˙ąąąũļļļ˙°°°˙¯¯¯āĻĻĻŽ———ĄœœœÃŦŦŦųļļļ˙ŗŗŗüŗŗŗ˙ŗŗŗ˙˛˛ŗūŗŗ¯˙ĩ´Á˙Ž’T˙Œ•�˙™˙˜�˙˜�˙Ž—�ü” ũĨŦ.æŊÂLĨ­%� §%�������������ĄĄĄ�“““vŽŽŽū›››ûĒĒĒ˙ŽŽŽ˙Ŧ­Ģ˙­­ļ˙ĢŽx˙™ĸ˙Ž–�˙™�˙˜�˙˜�˙Ž—�˙‘š˙‡�˙Ž‘c˙ĩ´Āūĩĩ´˙˛˛°ūŗŗŗũ´´´˙ĩĩĩ˙ˇˇˇ˙ˇˇˇ˙ĩĩĩ˙˛˛˛üŗŗ´˙ŗŗŗū˛˛°ūĩĩŗ˙ļĩÁ˙‘”h˙ˆ‘�˙‘š˙˜�˙™�ū™�ū–�û›Ŗ˙¨¯7†´ģD�§¯+Ÿ§#�������������”””�………HŦŦŦ˙ũ§§§ū¯¯¯˙­­Ŧ˙­ŦŽ˙­­Ē˙§­B˙’›˙Ž—�˙™�˙™�˙˜�˙Ž—�˙‘š˙‰‘�˙ŒR˙ŦŦ˛ū¸ˇžūĩĩ´ūŗŗ°ü˛˛°û˛˛ąû˛˛ąû˛˛ąũŗŗąūŗŗ°ūĩĩŗūˇˇŊ˙­­ĩ˙‘X˙ˆ‘�˙‘š˙˜�˙™�˙™�˙Ž—�ü’›ūĻŽ&樰0¤Ŧ- §.� ¨#�������������]]]�RRRŠŠŠōĸĸĸ˙ĄĄĄũ¯¯¯ū­­­˙­­Ģ˙­­ŗ˙­ŽŽ˙ĄŠ$˙˜�˙˜�˙™�˙™�˙™�˙˜�˙‘š˙Œ•�˙†Œ ˙˜šz˙Ŧ̰˙ĩ´ŋ˙¸ˇŋ˙¸ˇŧ˙ˇˇģ˙¸ˇŧ˙¸ˇž˙ļĩŋ˙­Ŧ˛˙˜›˙†Œ%˙‹•�˙‘š�˙˜�˙™�˙™�˙˜�ūŽ—�û˜ ˙Œ’6f•$�—ž%Ą¨,�����������������åååųųųĸĸĸ˧§§ūü°°°˙ŦŦŦ˙­Ž­˙Ŧ­Ģ˙Ž­ĩ˙̝s˙Ļ˙Ž—�˙˜�˙™�˙˜�˙™�˙˜�˙™˙™�˙ˆ’�˙†˙Ž’N˙—™u˙ŸŠ˙  ‘˙Ÿ‹˙—™w˙Ž’R˙†˙ˆ‘�˙™�˙™˙˜�˙™�˙˜�˙˜�ū˜ũ“˙˜Fė¯ŽMnkŖ�hĨŦ-�����������������ģģģÁÁÁ�ššš”ĢĢĢū™™™úŽŽŽū­­­˙ŽŽŽ˙­­­˙­­­˙­­˛˙ĒŽh˙œ¤˙Ž—�˙˜�˙™�˙™�˙™�˙˜�˙˜˙‘š˙‘š�˙–�˙‰’�˙‡�˙†�˙†�˙ˆ‘�˙Œ–�˙š�˙‘š˙˜˙˜�˙™�˙˜�ū™�ū˜˙—�ųĄ§JüĄĄ§Ēčį¸�ÕÔåty%���������������������——— �‡‡‡S¯¯¯˙ŸŸŸû­­­ū­­­ū­­­ūŽŽŽ˙­­­˙­­­˙­­˛˙ĒŽm˙Ĩ˙™�˙Ž—�˙™�˙™�˙˜�˙™�˙˜�˙˜˙™˙‘š˙‘š�˙‘›�˙‘š�˙‘š˙™˙˜˙˜�˙™�˙™�ū™�ū™˙Ž—�ū—�ûĻĢMųēšŧ˙ŒŒŽVš™¯�ŸŸžsx���������������������‡‡‡yyy�rrrŽŽŽ÷ĢĢĢ˙ŦŦŦøŗŗŗú­­­û­­­ü­­­ũŦ­ŦũŦ­Ŧū­­´ūĢ­‚ūĄ¨,˙“œūŽ—�˙˜˙™˙™�˙™�˙™�˙™�˙˜�˙˜�˙˜�˙˜�˙˜�˙™�ū™�ū™�ū™�ū˜�ũ˜�úŽ˜�û” ˙Š­g˙ĩ´ģ˙ŦŦ­Ė’“„„~�‰‰‰��������������������������ˇˇˇœœœ�„„„QĄĄĄ×ŖŖŖ˙ššš˙ļļļ˙ļļļ˙´´´˙˛˛˛˙°°°˙ޝ­ũŽŽļûŦ­ĄûĨĒWû™ĸû‘š�ûŽ—�üŽ—�ũŽ—�ũ˜�ũ˜�ū˜�ū™�ū™�ū˜�ū˜�ū˜�ũ˜�û˜�û™�ū‘š�˙” ˙šĄ4˙Ĩ§Žæŗ˛ÂĄŅŅŅJÅÅÂęęë�­­Ž�ŒŒŒ�������������������������TTT�ŋŋŋ�xxxaaa�ÖÖÖ ‚‚‚k˜ŖŖŖ°ĻĻĻÉĒĒĒá­­­đ°°°ü°°°˙°°Ž˙˛˛´˙´´ē˙ŗ´ž˙ǝc˙Ÿ§-˙˜Ą˙”˙’›�˙‘š�˙™�˙˜�˙˜�˙™�˙‘š�˙˜˙“œ˙• ˙“›˙‘—"ā‹ŽW „‚œTŽēЍˇ�����ģģē�ŨŨŨ�§§§�ˆˆˆ�����������������������������ĩĩĩ�|||�rrrˇˇˇ�ŽŽŽ�ĀĀĀ������������XXXlll-ŽŽŽBŽŽ[™™˜t˜˜–‹žžĻŖŖĸ¯¸ĨĻœČĄŖtŲ›ŸK斝/ō–!ų—Ÿü™Ą˙šĄ˙˜ ũ—Ÿō¤ĢטŸ"¯–(w~ƒ.: �˙ ˙˙��˙˙ē�����ÁŋŅ˙˙˙ÜÜÚ�æææ�   �������������������������������������ŽŽŽ�xxx�ĐĐĐ�‡‡‡ąąą˙˙˙???� �nnn��“““�ŖŖŖ�ĩĩĩ�����ØØė���������&˙aU˙b`ˆopS'x|40‰Ž79‹:<~„-0y|8îđĩĀ�Ļ­�‚ˆ)�]Z„���˙h[Đ����ÅÃÕ�ōō÷��������������������������������������������������������������������������������ĒĒĒwwwšššĻĻĻ´ŗ´˙˙˙¸šē˙˙˙˙˙æ�5#˙�H2˙�_ZĢ�qrV�„8�Œ‘<�”?�…‹0�}€9�ķõ�ŠŗĻŽ„‹#������������������������������������������������������������������������������������������������������������������������������������������������������•,{)w{3{7‡$���ũ˙������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������˙˙˙˙˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��ü�˙˙˙��ø���˙��ā���˙��Ā���˙��Ā���˙��Ā���˙��€���˙��€���˙��€����˙��€������€�������������������������������������ø�����ø�����ü�����ü�����ø���€�ø���€�đ���€�ā���€����?��€����?��Ā������Ā������Ā����˙��Ā����˙��Ā����˙��ā���˙��ā���˙��ø���˙��˙ø�˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��¨�� ���˙˙�˙˙���������������(��� ���@���� �����€������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vvv�{{{�ZZZ�”””’’’’’’’’’nnn��������fff�����������������������������������������������������������������������������ģģģ�[[[�{{{aaa��ŒŒŒ����‹‹‹�ccc�aa`lko“”yy|Ooq<uxE‹jĨĻ™­­­ĢĢĢŦŦŦŽŽŽ•••¤¤¤�ĻĻĻ���������������������ŠŠŠ�ĄĄĄSSSooo�WWW ……….‰‰‰,‡‡‡-‡‡‡-‡‡‡,†††,\\YYYO�nnj�‰‰y�qrP�fh;�kmE�‚‚k�ĻĻŠ�ÃÁ×�ÁĀĐ�´´ļ�´´ŗ�———�����������������������žžž�ŸŸŸ�‡‡‡O’’’Ö§§§ūĒĒĒūŠŠŠ˙ĒĒĒ˙ĒĒĒũŦŦŦ˙››Å¤¤Ģ™ŸžŖœŽ‰™yyj’ss]“wxd“……|“˜˜“ĻĻŗ“¨§°’ĄĄ žžœ“‹‹‹&�™™™�����������������–––‘‘‘ŖŖŖ¤¤¤¤˙ŸŸŸ˙ŦŦŦūĢĢĢ˙ŦŦŦ˙ŦŦŦ˙ĢĢĒ˙¯¯°˙››”˙~Y˙rv*˙sx˙u| ˙v}˙v| ˙v{˙y~%˙ƒ†K˙˜™„˙ŦŦ°˙ąąļ˙œœŋŋŋ�ģģģ——œ�����������������ŸŸŸ�———Ф¤¤˙ŖŖŖûžžžũ­­­ūĒĒĒūĢĢĢūĢĢĢūĒŠ§ūą°¸ū‰Œ_üu}�û–�û’œ�û”�û”ž�û”ž�û“œ�û™�ûˆ‘�û‡�û‡%ų‰ûŦ̎ņ  ž’‘™�—–Š�����������������ÉÉÉ�ĩ§§§üĄĄĄû   ˙­­­ū­­­˙°°°˙°°°˙­­Ē˙ŽŽ¸˙ši˙‘›˙‘š˙™�˙Ž—�˙Œ–�˙–�˙˜�˙‘›�˙“œ�˙”�˙š�ū…Ž�û’–X˙––œrŖ¤ž�ϧ—ttr�������������ôôô   ÕĻĻĻ˙   ũŖŖŖ˙˛˛˛˙˛˛˛˙ŗŗŗ˙ŗŗŗ˙˛˛°˙ĩ´Ŋ˙šœp˙‡‘�˙Œ–�˙‹”˙’™(˙—œ<˙•›;˙Ž”"˙‡˙ˆ ˙ˆ˙ˆ˙Œ“ü…ū}Q×bg;lpA�vvt�EEE�777�����]]].ĸĸĸ芊Š˙ŖŖŖū¨¨¨˙ĩĩĩ˙˛˛˛˙ŗŗŗ˙ŗŗŗ˙˛˛˛˙ˇˇ¸˙žž—˙‰‹l˙ŒŽe˙Ž}˙“’˜˙››¤˙š™Ŗ˙ŒŒ˙ƒƒ{˙““Ž˙“”’˙’’’ū’‘•ū”üŽŽŒ˙ut~•{zˆ ŠŠ‰ '''LLL˙˙˙………`¤¤¤ô¯¯¯˙§§§ũĒĒĒ˙´´´˙˛˛˛˙´´´˙ŗŗŗ˙ŗŗŗ˙ļļļ˙ĸĸŖ˙˜˜ž˙––ž˙‚…ū„„‚ū‰‰†ūŠūŠŠ‰ūˆˆŠūˆˆ‰˙„„„ū……„˙zzy˙ŽūŖŖŖũ  ˙¤ĨĸāĻĻĻᔔ” ÅÅÅ�­­­‘ŠŠŠų¯¯¯˙§§§ūŠŠŠ˙ĩĩĩ˙ŗŗŗ˙ŗŗŗ˙´´´˙˛˛˛˙ļļļūŖŖĸ˙ūŒŒ‰û““’ûœœœû›››ûŸŸŸü——–ũyyxũ~˙€ū}}|˙‹‹‹ūŽ­°˙ą°´ū°¯ŗü¯¯ŗ˙°¯ą˙ĢĢŦ˙——™|ššš–––ēŦŦŦûŽŽŽ˙§§§˙¨¨¨˙ļļļ˙˛˛ŗ˙ŗŗŗ˙ŗŗŗ˙˛˛˛˙ĩĩĩ˙¨¨¨˙ąąą˙ššš˙………˙‡‡‡˙„„„˙{{{˙vvv˙rrr˙www˙www˙~~ūŖŖ ˙Ąĸ”ūŸ “ū Ą’ūũ›››åášš—ė~~~lšššĪ­­­ü°°°˙ŠŠŠūŠŠ¨˙ļļ´˙ŗŗą˙´´˛˙´´˛˙ŗŗ˛˙ĩĩĩūŦŦŦū¸¸¸û‰‰‰Žmmms|||giiiZļļļKļļļBĄĄĄÔ˛˛˛˙ŗŗ˛ûēēŧ˙Ŧ­Ŗ˙… ˙Š“ū‹”üƒ‹ūou Ylr rxlll›››ŌŦŦŦũ¨¨¨˙ĸĸĸ˙¨¨Ē˙ĩ´ž˙˛˛ģ˙ŗŗŧ˙ŗ˛ģ˙˛ą¸˙ąą°úĒĒĒū¨¨¨ŧÛÛÛ�ßßß�555�qqq�ˇˇˇ�ēēē�¤¤¤cĀĀĀ˙ÂÂÁúÂÂĀūžŊÄ˙—"˙™�˙˜�ú‘š�˙”y“œ�’œ��ppp�›››ËĒĒĒü›˙ĄĄĨūœ‡˙–7˙–6˙–4˙•1˙‘•P˙ŽŽ˛ųŗŗ˛ūĒĒǐÄÄÄÃÃÚšš~~~››››››———;ēēēūŋŋŋüŋŋŊũžžÆ˙‘—/˙—�ū—û˜�˙’œˆ‘š��‘š�����˜˜˜ŦŠŠŠûŖŖ ˙˛˛š˙¨ŠŠ˙—�˙Ž˜�˙Ž—�˙Ž˜�˙Ž–˙ŊŊÂúÃÃÂ˙ŽŽ­ąččč�ĶĶĶŊŊŊ�ŠŠŠ�˛˛˛˛˛˛�ŸŸŸVžžž˙ŋŋŋúĀĀŋūŧģÁ˙Ž•˙˜�˙˜�ú˜�ū’›™��™�����yŠŠŠøĄĄĄ˙ŽŽąūĢĢ ˙’› ˙Ž—˙˜˙š˙Š“˙¯° ũÃÃÆüļļĩö’’’.ĻĻĻ�ššš�xxxŅŅŅ�ÄÄÄ�ĢĢĢÃŧŧŧūŊŊŧûÃÃÅ˙°ą¤˙‰’˙™�ūŽ—�û” ˙¤Ģ%_Ą¨$�ŸĻ����mmm=¨¨¨ėĸĸĸ˙­­Ŧũ¯Ž´˙ž¤:˙—�˙™˙˜˙–�˙•šM˙ˇļÂüŗŗŽ˙ĒĒĒā´´´G´´´ŊŊŊ�ĢĢĢ#›››¨ŗŗŗ˙˛˛˛üŗŗŽ˙žŊÉū—œO˙Œ•�ū™ūŽ—�ūĨøŖ¨D%ą¸H�Ĩ­,����ááá §§§Øĸĸĸ˙ŦŦĒũŽ­ļ˙ĒŦ{˙’œ˙Ž—˙˜�˙™˙ˆ‘ū›~˙ˇˇÁũĩĩą˙­­Ģ˙¤¤¤Õˇˇļɯ¯¯ķĩĩĩ˙˛˛°üŗŗ¯˙ˇļÂūŸ}˙Š“˙™˙Ž—�û‘šūŖĢ*ŋ|‚ �ĸĒ(šĨ������ŨŨŨ�ĨĨĨĩŖŖŖūĢĢĒü­­ŽūŽŽŽ˙ĸ¨8˙Ž—�˙˜˙˜�˙™ū‰’ūšœs˙ŗ˛Ŋũ¸¸ŋũ¸¸š˙ŗŗ˛˙ļļĩ˙ĩĩĩûļļŧ˙ŗŗŊūšuūŠ“˙™�ū˜�ūŽ—�ü™ĄũŖĒ7E¯ļ8�ŖĒ(ĸĢ�����ŦŦŦ�ĸĸĸ„ŖŖŖū¨¨¨ûŽŽ­˙­­ŗ˙­Ž’˙›Ŗ˙–�˙™˙˜�˙™�˙‹”�ūŽ”7ūŸĄ€ūĒĒĻü¯¯ŗû¯Ž˛ũĒǧūŸ ū”9ū‹”�˙™�˙˜�˙˜ûš�ū–(ļ”�ĻŽ ¨%�¤Ŧ%�����œœœ�–––IĨĨĨū¤¤¤üŽŽ¯ū­­Ģū­­ĩū̝€˙˜Ą ˙–�˙˜˙™˙‘š˙Ž˜�˙Š“�˙Š’ ˙‹’˙‹’˙Š‘ ˙Š“�˙Ž˜�ū™ū˜�ū˜�ũŒ–�øž¤@˙››ĸ\ŠĒž�ύ„ŖĢ0���������ššš�’’’ŠŠŠôĒĒĒ˙°°°ûŦŦŦûŦŦĢû­ŦˇûĢŽ„ûšĸü—�ũŽ—�ū˜ū™ū‘šūš�˙™�ū™�ūš�ū‘š�ū˜ü˜�û˜�ū™�˙¤ŠK˙˛˛ŗō†…‘Ž§�žœÄ�������������ÁÁÁēēē�ĨĨĨgžžžė˛˛˛˙ŗŗŗ˙ąąą˙ąą¯˙°¯ˇ˙¯¯Ÿ˙¤ŠN˙–ž ˙™�˙˜�ū˜�üŽ˜�üŽ—�üŽ—�üŽ˜�ũ˜�˙™�˙’›˙“›˙žĸdŲŦĒŧާ§˛0…� ĄŽ� Ÿĩ�����������������{{{———�jjj–––L˜˜˜f¨¨¨ĸĸĸĒǍˇĒĒ¯ÍŽ­ļāĒŦđĄĻTú˜Ÿ(˙”œ˙“œ ˙” ˙”œ ˙• ˙•ņ˜ɇ(ˆy{Y@ljv­ĒË�äãį�����›œ˜�™™™�����������������‚‚‚�ĻĻĻkkk�ĻĻĻ�´´´�˛˛˛�ûüü�ĘĘĮ������wrŧ”Ō%••–3‰OCŒ’.P•œ-Y’˜+T•›-<ˆŒ:ruD�y|(�JU��ŠŠ��ĩą×××Ü÷÷ũ�ŠŠŠ���������������������–––�ĨĨĨ�ggg§§§¸¸¸ššš˙˙˙ČČĮ��������‡ƒ×�œ˜Õ�—–’�•›E�”š+�¤*�™ )�›ĸ.�–œ5�sv@�x{+€ˆtx-�ÁŋĶ�ÚÚŨ������������������������������������������������������������������������‘“ƒ‚†•œ5šĄ" ¨"œ¤ ™ #�������Š&���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙đ��˙Ā��€��€��?€��?€��������������`�ā�đ�ā�ā€Ā€��€��€��Ā��Ā��?Ā��ø�˙˙ū?˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ˆ �� ���˙˙�˙˙���������������(������0���� �����` ����������������������������������������������������������������������������������������������������������������������§§§�ĨĨĨ����ąąąŗŗŗŗŗŗĩĩĩ���ÁÁÁ�ˇˇˇ�����������������������������������������������������ccc�›››�‘‘‘�   �ĄĄĄ�ĄĄĄ�ŖŖŖ�‘‘‘�¤¤­�ÆÄá�������������������˙�����………�{{{�LLL�¸¸¸�������������ŽŽŽ�ŖŖŖ�‚‚‚�{{{$™™™HšššE™™™E›››HŠ‹Š&ĸĸĒ�ØÕ˙�����,����������V`����������ÚÚØ�îîé�ŦŦŦ�������������šššĸĸĸ –––~œœœöĢĢĢ˙ĢĢĢ˙ĒĒĒ˙­­­˙ŖŖ¤÷——×‡‰hØtx;Ôqu.Ķuy<Ķ‚…]Ķ—˜ŽĶŠŠŗŅĒŠŽĶ““’1˜˜—�¨¨¨œ‘���������ÆÆÆ�Ģ¤¤¤˙ĸĸĸūŦŦŦüĒĒĒũĒǍũ¯¯´ü—˜ƒ˙sz˙‚Š�˙‰’�˙‹”�˙‰’�˙…�˙ƒŠ˙‹A˙ŖŖ–˙ĻĨ¯ŸÄÄČ�ģģģ‘’x�“““�����ĖĖĖ ŖŖŖâŖŖŖüĸĸĸüŽŽŽ˙°°°ū¯¯Žūą°š˙ž zū™�ũ“�ũ™�ũ™�ũš�ũ“œ�ũ‘›�ũ‹–�û‡�ũ’–ZķŦŠÕ§ĻÉ–˜ƒ“““����wwwC¤¤¤īŖŖŖū§§§ũ´´´ūŗŗŗ˙ŗŗ˛˙ˇļģ˙Ŗ¤’˙†Œ%˙А(˙’–Y˙œžv˙“–e˙„ˆ<˙ŒF˙Ž’Oū”Mü‡ŠP˙tti‹yzh�ƒg��ĄĄĄ‘‘‘¨¨¨øŠŠŠ˙ĢĢĢūĩĩĩ˙˛˛˛˙˛˛˛˙ļļĩū§§Š˙“’›ūŒ•ü‡†’ü™üšûˆ‡“ũŒ˜˙‹‹–ū‚Žū••›ūœ˙ššœąššŸ¤‡‡…�———ŗŦŦŦüĒĒĒ˙ĒĒĒū´´´˙˛˛ŗ˙˛˛˛˙ĩĩļ˙ŠŠŠ˙••”˙‹‹Š˙••“˙””“˙Ž˙zzx˙vvt˙ttr˙ŠŠ‰˙¯Žē˙¯¯ģū¯Ž¸˙¯¯ĩ˙ĸĄŠÕĄĄŠ,žžžÖŽŽŽũĢĢĢ˙ĒĒĒ˙ĩĩŗ˙ŗŗą˙ŗŗ˛˙ĩĩ´ū¯¯¯˙˛˛˛ũ•••×zzz˃ƒƒÁ———ą‡‡‡č‘‘˙––™ũ¤Ĩš˙’—Lū”™IūŽ’Jú_‡ZƒwzMNĄĄĄßĢĢĢū¤¤¤˙ЍĢū´ŗģ˙ąąš˙˛ą¸˙ąąŗû­­Ŧ˙¨¨¨Ŗžžž�ĒĒĒ�ĀĀĀ�ģģģ�ĒĒĒ]ÂÂÂ˙ÆÆČüššĩū‹”ū—�ũŒ–�ūˆ�.ˆ‘��‹”��ŖŖŖÖ¤¤ŖũŖĸŠ˙ž }˙–)˙‘—*˙”$˙ ĸ|û¸ˇžūĒǧnˇˇˇ�žžžĀ¤¤¤.ŧŧŧūŋĀŋūŧŧŋũ˜˙˜ü™ū”Q’œ�“�ŸŸŸ°ĨĨŖü­Ŧĩ˙§Ēū–�˙˜�˙‹•�˙žĸVûÆÅŌ˙ąąŦąĪĪĪ�ĖĖĖ�ÂÂÂ�ŧŧŧ�§§§nŊŊŊ˙ÃÂÅûļļ°ūŒ•ū˜�ũ‘š˙ §>šĸ�˜Ąšššr¤¤¤÷ŠŠĢ˙ŽŽĻū”œ˙Ž—˙™˙Ž•ūŗ˛ļüļļ¸˙›|ŸŸŸ ¨¨¨ĒĒĒO°°¯đŗ´°ũŋžÎũĄ¤j˙‹•�ũŽ—�ūšŖņ™ E˛¸N�Ģŗ0�”””+¤¤¤ę¨¨Ļ˙°¯šũŖ¨Q˙—�˙™˙–�˙‘–Cūŗ˛ŋūˇˇŊ˙¯¯Žčޝ­Ū°°¯˙ĩĩļ˙ˇļÅūŖ¤˙‹”˙˜�û‘š˙Ą¨&ω“�˜ ̞/�ĶĶĶ�ŖŖŖÍĻĻĨūŽŽ˛üޝĸūšĸ˙Œ–�˙‘™ūŒ–�˙•1ūϧ”ũ˛˛¸˙´´ž˙ŗŗŊū­­Ŧū›ži˙‹“ū™�ū—�ü“œö¤­.%ĸŠ)�ŖĢ%ĻŽ%�ŽŽŽ�ĄĄĄžĨĨĨū­­Ģø­Ŧ´üŦމũ–Ÿ ũŒ–�ū™ū˜�ū‹”�ū– ũ’˜7ü‘–0ūŒ”ūŒ•�ū™�üŒ–�ų“œ˙ŸĸcēąģK�Ģą\¤Ŧ)�����ŽŽŽ�§§§P¨¨¨˙°°°˙ޝ­˙¯¯ˇ˙ŦŽ˙šĸ$˙Ž˜�ūŽ—�ũ™�ü˜�ûŽ—�ûŽ˜�û™�ū™�˙™˙”œ˙ĒŦ‡ī­ĢÄO˛°ÄŧŊŧŖĒ(�����ģģģŖŖŖ‡‡‡T§§§ŖŽŽŽŊĒĒ¨ÕŽŽļé­­ŠøĻĒf˙™ (˙“› ˙‘š˙’›˙’š˙’›˙— 鎕(¯“–rb—˜‘ĨĨĢ�”•Š�ŧŧē���������žžž�ŧŧŧ�ŠŠŠ�ēēē�ÔÔÕ��y|H ŠĒĻį.ĄĄ¤A˜œNS—&b–"k’™#_“$9‚‡(E>’�ļĩŋ�ģ­˙�ĒŠˇ� ŸĨ�Ŋž¸���������ĖĖĖ�����uuu´´´ĪĪĪBBC�Ž‰�˜�ŦŠÕ�ϧ›�› B�™Ą�šĸ�—Ÿ�•œ#�‚†)�][^}„dp��ÃÃĮ�ˇˇš����������������������������������������´´ŗŧģÄްŠĄŠ'˜ ›Ŗ›¤Ÿ§�����������������������������������������������������������������������������������������������������������������������������������˙˙˙�˙˙˙�˙˙˙�˙˙˙�Ā�?�€��€��€������������<��>��<���€��€��€��€��Ā�?�˙Ã˙�˙˙˙�˙˙˙�˙˙˙�h�� ���˙˙�˙˙���������������(������ ���� �����@������������������´´´�×××����°°° ąąą ĒĒĒÍÍÎĩļ°”•§¨ĨŠŠĒ�������ŸŸŸ�˜˜˜�����ĄĄĄ�ŦŦŦ–––�§§¨�§§Ļ�ŠŠŽ�ŋŊä�œšĩ�hf~�Šŋ�ļ˛˙�žžž�ŠŠŠ�ŠŠˆ�“““�����¤¤¤�¨¨¨�“““F¤¤¤d¤¤ŖdĄĄ¤T´ŗË)Ÿ°'jh~%‡„°%̧é&ŸŸŦ"~~w�‚„h�“^�����˜˜˜   Ĩĸĸĸ˙ĢĢĢ˙ŦŦŽ˙ŖŖ˙‡‹BūƒŠũ€† ü„Š ü“NúŖ¤˜ø ŸĻ8§Ĩē­­ļ����˜˜˜\ĸĸĸūĻĻĻü°°¯ú˛ąˇú¤Ĩ‘ûˆ’�ū‘š�ū“›˙Ž—�˙Š”�ú”˙’f ™ŸI�•›G�ûûûĄĻĻĻúŦŦŦū´´´˙ĩĩļūŦŦĒ˙qũŒ}ü•••ū‰Š}ũ‹Œ}ū‰Šzũ‘…÷’•r{‘“oAšš�ŖŖŖÔŦŦŦũ­­­˙´´´ū´´´˙°°°˙™™Ÿ˙‰‰Œ˙˙„˙zz~˙Žū§¨ĄūĨĨĨ˙žž­äŖĸZ§§§ëŠŠ¨ũĒĒŦ˙˛˛´˙ąąŗü°°¯ū§§ĻŸ››™AŠŠŠ1ĄĄžsĩĩ¸ū˛ŗĸû• ū‹”č{%”›ŖŖžáĻĨŽūžĄo˙—ū—%ú´´°˙ąą´UÁÁĮ�´´ŗ�Ēǧ)ÂÂÃ˙ēģŗũ—ū™�ôž§ šŖ�  ™­¨¨¯ü¨Ē€˙Ž˜�˙‹•�ũĒŦ…ūˇˇÄĪģģē(¸¸ĩŦŦϰŧģČūŠĢú‹•�ū—ŸŨ‰7…Ž™™˜aĨĨĨôŽŽ°ūšĄ*ū–�˙Ž–ū§Š’˙¯Ž¸÷ŗŗŊņĩ´ŋ˙§¨’ūŽ–ø˜�ũšĸ…Š“ �•žŖŖŖĻĻ¤æ­­˛˙Ŧ­”ũ•žüŒ–�ûŒ•�ø—>ũ›ŸUū•›8øŒ•�ũŽ˜�˙˜Ÿ-ũ¤¨€°°Š�ĨŠi°°°�™™™~ĒĒ¨čŽŽ´÷Ģ­—˙œĸ;˙’› ˙Ž˜�˙Ž—�˙˜�˙˜û–œ3Ėύ‰k´ē�ˇ¸Ž–§��˙˙˙����ssw‘’†"›š­7¯ŽŊMŸŖPb‘˜"s•œy•œ^Œ’&+”;�™Ą(�jW=�ēģt� ��ĐĐĐ�III����ĄĄš�ǍČ�ŗąČ�ŸŖT�•�–ž�”œ�Œ’)�“G�ĸ¨<}ci�Ĩ­A����������������ģģšˇˇŧĩĩŠĸ¨E • ” ‘𠐗—Ÿ�Ĩ�› N���������˙˙��˙˙��ã˙��€������������ƒ��Ã��ƒ�����€��€��ü?��˙˙��˙˙������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/dcm2laz/dcm2niigui.ico��������������������������������������������������������0000664�0000000�0000000�00000042206�13220512030�0020042�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000����������������������������������������������������������������������������������������������������������������������������������������������������������������������������00��� �¨%��F��� ��� �¨��î%����� �ˆ ��–6����� �h��@��(���0���`���� �����€%��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ŠŠŠ����������������������������������������������������������������������������������������������������������������������������������¨¨¨�ššš����˙˙˙������������������������������������ŽŽŽ‘‘‘’‘‘’’’~~€zz{{{{|||~~€€€€€€ƒƒƒƒƒƒ………†††jjj��������������������������������������������������������ƒƒƒ�­­­�˛˛˛����������������������������������������‘‘’�‚‚‚�‹Š“�™˜Ž�ą�œšĘ�ĄŸÎ�Ŗ Ņ�ĸŸĐ�™—Ã�‡†Ļ�vuƒ�mmn�rrq�rrr�ttt�}}}�lll�xxx�888�€€€�������������������������������������“““�����§§§�kkk zzz———ÖĻĻĻØŖŖŖØŖŖŖŲĸĸĸŲŖŖŖŲĸĸĸŲĸĸĸØĻĻĻۛ››Âsss9rrp!pqm#„‚#œ›´"•“ŧœšĖĸŸĶ¤ĸÖ¤ĄÕ›™Į„ƒĻggtPPKSTG]]Ybbbiiibbb___�‚‚‚�‰‰‰����������������������������������œœœ�‰‰‰�ˇˇˇ�¯¯¯HšššŌššš˙¨¨¨˙°°°˙ŽŽŽ˙¯¯¯˙¯¯¯˙ŽŽŽ˙ŽŽŽ˙ŽŽŽ˙ąąą˙¯¯¯˙››œũŗ˛ˇõŽŽ°ö¤¤ ö””…ö‚ƒlņwyYđrtPđstPđxzZđƒ„l𒓃𡡞đĢĢ¯đ­­˛đ¨¨ŠđĨĨŖđĨĨĨė§§§ņ“““ÅÅÅ�ŽŽŽ���������������������������������———’’’�‰‰‰ŠŠŠŸ˙ŠŠŠ˙———úĒĒĒũĢĢĢũĒĒĒũĢĢĢũĢĢĢũĢĢĢũĢĢĢũĒĒĒũĢĢĢũĢĢŦüšš“˙ŒŽr˙lo;˙`d˙_e˙ek�˙kq�˙nu�˙mt�˙kq�˙gm�˙ek˙hm˙uy=˙Žr˙§§¤˙´´ģ˙°°˛˙¯°¯˙ĨĨĨī```�€€€��������������������������������¤¤¤ĢĢĢ•••ͨ¨¨˙§§§û¤¤¤ü˜˜˜˙­­­ūĢĢĢūĢĢĢ˙ŦŦŦ˙ŦŦŦ˙ŦŦŦ˙ŦŦŦ˙ĢĢĢūĢĢŠū°°ļ˙hj7ūX^�ũu|�ũˆ�ũ‘›�ũ•ž�ũ•žũ•Ÿũ•žũ•Ÿũ•ž�ũ’›�ũŒ•�ũŠ�ũt|�ũqwũ„‡SũĨĨĄũ˛˛¸úŦŦĒ˙††…p­­­�ŠŠŠĒĒĢ�����������������������������ppp�lll"ĸĸĸøĻĻĻüĻĻĻũĄĄĄ˙šššūŽŽŽ˙ĒĒĒ˙ŦŦŦ˙ĢĢĢ˙ĢĢĢ˙ĢĢĢ˙ĢĢĢ˙ĢĢĢ˙Ģ̍˙ŦŦļ˙…Š4˙’œ˙•˙‘š�˙˜�˙˜�˙˜�˙Ž—�˙—�˙—�˙˜�˙˜�˙™�˙’œ˙”ž˙š�˙‚‹�˙x˙‘kûą°¸ūŖŖŖĶÉĘÆŠĒĨžšæ������������������������������ƒƒƒJŖŖŖūĨĨĨũĻĻĻūžžžūœœœ˙¯¯¯˙ĢĢĢ˙ŦŦŦ˙ŽŽŽ˙¯¯¯˙¯¯¯˙ŽŽŽ˙ŦŦŦ˙ĢŦŠ˙Ŧ̎˙Œ‘;˙Ž—�˙Ž—˙˜�˙˜�˙˜˙™˙‘™˙‘š˙‘š˙™˙˜˙˜�˙Ž—�˙Ž—˙—˙’›˙‘š�ūŠ�ūƒ†>üĨĨŠ˙}}„I˜”¤�•”ƒ‚‰��������������������������sĨĨĨūĨĨĨû§§§˙˙   ˙ŽŽŽ˙ŽŽŽ˙˛˛˛˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙˛˛˛˙¯¯­˙ŽŽš˙Œ‘A˙™�˙™˙™˙‘š�˙™�˙Œ•�˙‰“�˙‰“�˙Š”�˙—�˙‘›�˙“œ�˙’›�˙‘›�˙‘›�˙‘š�˙™�˙”ž�˙‹•�ûƒ‰!ū’’‰˛“Ļ›�̝‚����‰‰‰���������������������¯¯¯�“““šĻĻĻ˙¤¤¤ú§§§ū›››˙ĸĸĸ˙˛˛˛˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗą˙ĩĩŋ˙‹G˙‡‘�˙Œ•�˙Œ–�˙‰‘�˙Œ“&˙—›P˙žĄi˙ž l˙še˙”J˙ƒŠ˙‰˙ˆ˙„‹˙ƒ‰˙ƒ‰˙‚‰!˙ˆ&ū…Š,ū‚‡)ũnqFų:F?(dfZ�llfƒƒƒœœœƒƒƒˇˇˇ�˛˛˛�����ņņņ�›››¸ĻĻĻ˙ĨĨĨûĒĒĒ˙œœœ˙¨¨¨˙ļļļ˙˛˛˛˙ŗŗŗ˙´´´˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙˛˛˛˙ļļ¸˙ƒ˙…‡j˙ˆŠd˙„T˙‚„r˙™˜ ˙š™¤˙Ÿž¨˙ĨĨŽ˙œĻ˙—–Ą˙‰‰˙yyv˙ŽŽ’˙””™˙””š˙––ž˙——Ÿ˙˜˜Ąū˜˜ĸ˙—–ĸü™™ū‡…‡Ō’‘•ĨĻĨ�zzz�ŖŖŖ��ÔÔÔ�ËËË�kkk�úúúĐ¨¨¨˙ĢĢĢü°°°˙   ˙ĒĒĒ˙ĩĩĩ˙˛˛˛˙´´´˙ŗŗŗ˙´´´˙´´´˙ŗŗŗ˙´´ŗ˙˛˛ŗ˙ĩĩĩ˙––˜˙˜˜˙ššĄ˙œĨ˙——›˙„„ƒ˙|˙††„˙ˆˆ†˙††„˙‡‡…˙}˙——˜˙™™™˙••”˙’’˙Ž˙‹‹‰˙‡‡…˙ƒƒ‚ūŽŽŒ˙ŽŽŽü˙zzz͍v‹‹‹{ˆˆˆ}wwwNŽŽŽ�ÄÄÄ```� (âĢĢĢ˙­­­ũ¯¯¯˙   ˙ŠŠŠ˙ĩĩĩ˙ŗŗŗ˙ŗŗŗ˙´´´˙´´´˙´´´˙´´´˙ŗŗ´˙ŗŗ˛˙ļļˇ˙–––˙””’˙›œš˙ŠŠ‡˙tts˙‚‚‚˙ŒŒŒ˙˙˙’’’˙•••˙‡‡‡˙˙yyy˙zzz˙zzz˙zzz˙~~~˙www˙„„„˙ŽŽŽūĒĒĒ˙ĒĒĒü­­­˙ŦŦŦ˙ĢĢĢ˙ŦŦŦ˙™™™ā°°°ŗŗŗeeeqqqTĸĸĸė­­­˙ŦŦŦū°°°˙ĄĄĄ˙¨¨¨˙ļļļ˙ŗŗŗ˙´´´˙´´´˙´´´˙´´´˙´´´˙´´´˙˛˛˛˙ˇˇˇ˙˜˜˜˙‘‘‘˙˜˜˜˙ƒƒƒ˙•••˙ššš˙™™™ū›››˙šššūšššū›››ūˆˆˆūzzz˙†††ū†††˙†††˙‡‡‡˙€€€˙€€€˙ĨĨ¤˙ĢŦĒ˙ĢĢŠūĢĢĒ˙ĢĢĒũĢĢĒûĢĢĒųŦŦĢ÷ĒĒĒûxxx§TTT�ĢĢĢ�}}}ŠŠŠō­­­˙­­­ũ°°°˙ĸĸĸ˙§§§˙ļļļ˙ŗŗŗ˙´´´˙´´´˙´´´˙´´´˙´´´˙´´´˙˛˛˛˙ˇˇˇ˙šššū‘‘‘˙›››ũûšššûšššû˜˜˜ú˜˜˜ú–––ú•••ú•••û}}}ürrrū|||˙xxxūyyy˙sss˙‚‚˙ĢĢŦ˙ąąĩ˙Ž­ą˙¯¯ŗ˙¯¯ŗ˙¯Ž´˙°¯ŗ˙°°˛˙ŽŽŽ˙°°¯˙¯¯¯˙‘‘‘ž•••‚‚‚ĢĢĢöŽŽŽ˙­­­ū°°°˙ĸĸĸ˙§§§˙ļļļ˙ŗŗŗ˙´´´˙´´´˙´´´˙´´´˙´´´˙´´´˙ŗŗŗ˙ĩĩĩūĨĨĨ˙ēēēūĮĮĮ˙†††˙xxx˙€€€˙}}}˙|||˙zzz˙zzz˙{{{˙xxx˙tttūuuuūwww˙{{{˙uuu˙˜˜™˙  œ˙—™†˙š›ˆ˙™š†˙™š†˙™›„ū•–‚˙’’Œč–•™Ô––™ÖŸŸŸŌžžžã___ZŠŠŠŗĢĢĢų­­­˙ŦŦŦū¯¯¯˙ĸĸĸ˙§§§˙ĩĩĩ˙˛˛ŗ˙ŗŗ´˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙ŗŗŗ˙˛˛˛˙ļļļ˙¤¤¤ũŊŊŊū¤¤¤í‘‘‘´Šnnn›oooŽnnnjjjtlllgwwwUiii†¤¤¤˙ŦŦŦüŽŽŽūąąąū´´ŗ˙ššē˙§§ĸ˙€ˆ ˙‰’�˙‡�˙ˆ�˙Š“�ú‰�˙cifl�nu�������rrrŒŒŒˇĢĢĢúŽŽŽ˙¯¯¯ūąąą˙ĄĄĄ˙¨¨¨˙ĩĩŗ˙˛˛°˙ŗŗą˙ŗŗą˙ŗŗą˙ŗŗą˙ŗŗą˙ŗŗ˛˙ŗŗŗ˙ļļļūĻĻĻûĩĩĩ˙ŠŠŠ^ŦŦŦ�ĻĻĻ�ˆˆˆ�†††��www�uuu�ÎÎÎ�ÛÛÛ�­­­ČÅÅÅūÂÂÂüÂÂÂ˙ÁÁÁ˙ÁÁĀ˙ŋŋÉ˙Ž”1˙š�˙‘™˙‘š�˙™�ü‘›�˙Ž—ʍŸ��¨¸��48��DDD�^^^�ŒŒŒˇŦŦŦúŦŦŦ˙   ūĒĒĒ˙žžž˙ĢĢŦ˙¸ˇÁ˙ĩ´ŋ˙ļĩÁ˙ĩĩĀ˙ļĩĀ˙ĩĩĀ˙ĩ´Ā˙´´š˙°ą°˙°°ąũĨĨĨ˙¯¯¯įjjjkkk“““€€€zzzuuuyyy˛˛˛ĩĩĩ�•••nŧŧŧ˙ŋŋŋûŋŋŋūŋŋŋ˙žžŧ˙ÂÁĖ˙”—R˙‹•�˙˜˙Ž—�˙˜�ũŽ—�˙“›âVkY ¨­E�ĸ¨*���������ŠŠŠŗ­­­ų§§§˙”””ūŸŸŸ˙’’“˙šš•˙’•X˙“P˙“O˙“M˙Ž“K˙“K˙Œ‘D˙“p˙¨¨­˙ŖŖĸü˛˛˛ūŦŦŦČ����õõõ�eee�����������������vvv�ŸŸŸŽŽŽ�‰‰‰Iģģģ˙ĀĀĀũŋŋŋũŋŋŋ˙ŋŋŊ˙ÃÂĖ˙–š]˙‹”�˙˜˙˜�˙™�üŽ—�˙’›襨š¤�ĸ§*���������ƒƒƒœ­­­ö§§§˙ŸŸŸũ˛˛ą˙ŽŽ°˙ĢĢ ˙˜˙Œ–�˙—�˙—�˙Œ–�˙Ž—�˙Š”�˙‘–G˙ÃÃÎ˙ĀÁžüÄÄÄ˙ĢĢĢÎ˙˙˙����ŒŒŒ������������������žžžŒŒŒNģģģūĀĀĀũŋŋŋũĀĀĀ˙žžŧ˙ÂÂĖ˙•™W˙Œ•�˙™˙Ž—�˙™�ũ˜�˙’›įą¸)Ļ�ĸĢ*���������{{{yŦŦŦņŠŠŠ˙›››ū¯¯Ž˙ŦŦ­˙Ŧ­Š˙“›˙˜˙™˙™˙™˙˜˙Ž˜˙’3˙ŊŊÆ˙žžŊũÁÁÁūŗŗŗđEEE```zzz�ÍÍÍ���������ƒƒƒ�zzz�ÄÄÄÉÉÉ�šššŋŋŋ˙ŋŋŋûŋŋŋūŋŋŋ˙ŋŋŊ˙ŋžÉ˙”;˙Ž˜�˙˜˙˜�˙˜�üŽ—�˙‘šā'>�ĸĒ:�ĸĒ*���������^^^J§§§ëĒĒĒ˙›››ũ¯¯¯˙­­Ģ˙ŽŽŗ˙˜ž.˙—�˙™˙™�˙™�˙Ž—�˙š�˙‡ ˙ŗŗą˙ÂÂÂūĀĀŋúžžž˙–––~¸¸¸�ˇˇˇÁÁÁZZZ�```�žžž�yyyŊŊŊŋŋŋ ­­­ŪÁÁÁūŋŋŋüĀĀĀ˙žžž˙ÁÁÁ˙ĩĩˇ˙ˆ˙™�˙Ž—�˙™�ūŽ—�û‘šū›Ŗϙ �ž§ �ĸĢ*���������¤¤¤ÜŦŦŦ˙šššũ¯¯¯˙­­Š˙¯Ž¸˙ĸ§Z˙˜�˙˜˙˜�˙™�˙˜�˙™˙‰“�˙œŸu˙ÃÂĘū¸¸ĩũšššüąąąõB¯¯¯�¯¯¯�‹‹‹���ĨĨĨģģģ�­­­�ŒŒŒĸĩĩĩ˙´´´ûļļļ˙ģģģūžžŧ˙ÄÄË˙ŸĄ˙ˆ’�˙™˙˜�˙™�ū–�ú›¤ū¨¯4–¯ĩ7�¨°-����������������ĸĸĸÃŽŽŽ˙šššüŽŽŽ˙ŦŦĢ˙Ž­ĩ˙Ģ­Š˙–Ÿ ˙Ž—�˙™�˙™�˙™�˙˜�˙™�˙‡Ž˙­Ŧąū´´ŗ˙ąąąũĩĩĩūĒĒĒō¤¤¤lžžž �###�žžž�ŠŠŠ.   ¯ˇˇˇ˙ŗŗŗüŗŗŗūŗŗŗūąąą˙ēē¸˙¸¸Ā˙‰#˙™�˙˜˙™�ū˜�˙—�ûĸĒ(˙ĸĒ-SĻ­2�ŖĢ)������������ÆÆÆ�™™™ĄŽŽŽūšššúŦŦŦū­­­˙­ŦŽ˙Ž­Ŧ˙ĸŠ8˙™�˙˜�˙˜�˙˜�˙Ž—�˙™˙‹”�˙‰N˙ĩ´ŋū´´ą˙ąąąũļļļ˙°°°˙¯¯¯āĻĻĻŽ———ĄœœœÃŦŦŦųļļļ˙ŗŗŗüŗŗŗ˙ŗŗŗ˙˛˛ŗūŗŗ¯˙ĩ´Á˙Ž’T˙Œ•�˙™˙˜�˙˜�˙Ž—�ü” ũĨŦ.æŊÂLĨ­%� §%�������������ĄĄĄ�“““vŽŽŽū›››ûĒĒĒ˙ŽŽŽ˙Ŧ­Ģ˙­­ļ˙ĢŽx˙™ĸ˙Ž–�˙™�˙˜�˙˜�˙Ž—�˙‘š˙‡�˙Ž‘c˙ĩ´Āūĩĩ´˙˛˛°ūŗŗŗũ´´´˙ĩĩĩ˙ˇˇˇ˙ˇˇˇ˙ĩĩĩ˙˛˛˛üŗŗ´˙ŗŗŗū˛˛°ūĩĩŗ˙ļĩÁ˙‘”h˙ˆ‘�˙‘š˙˜�˙™�ū™�ū–�û›Ŗ˙¨¯7†´ģD�§¯+Ÿ§#�������������”””�………HŦŦŦ˙ũ§§§ū¯¯¯˙­­Ŧ˙­ŦŽ˙­­Ē˙§­B˙’›˙Ž—�˙™�˙™�˙˜�˙Ž—�˙‘š˙‰‘�˙ŒR˙ŦŦ˛ū¸ˇžūĩĩ´ūŗŗ°ü˛˛°û˛˛ąû˛˛ąû˛˛ąũŗŗąūŗŗ°ūĩĩŗūˇˇŊ˙­­ĩ˙‘X˙ˆ‘�˙‘š˙˜�˙™�˙™�˙Ž—�ü’›ūĻŽ&樰0¤Ŧ- §.� ¨#�������������]]]�RRRŠŠŠōĸĸĸ˙ĄĄĄũ¯¯¯ū­­­˙­­Ģ˙­­ŗ˙­ŽŽ˙ĄŠ$˙˜�˙˜�˙™�˙™�˙™�˙˜�˙‘š˙Œ•�˙†Œ ˙˜šz˙Ŧ̰˙ĩ´ŋ˙¸ˇŋ˙¸ˇŧ˙ˇˇģ˙¸ˇŧ˙¸ˇž˙ļĩŋ˙­Ŧ˛˙˜›˙†Œ%˙‹•�˙‘š�˙˜�˙™�˙™�˙˜�ūŽ—�û˜ ˙Œ’6f•$�—ž%Ą¨,�����������������åååųųųĸĸĸ˧§§ūü°°°˙ŦŦŦ˙­Ž­˙Ŧ­Ģ˙Ž­ĩ˙̝s˙Ļ˙Ž—�˙˜�˙™�˙˜�˙™�˙˜�˙™˙™�˙ˆ’�˙†˙Ž’N˙—™u˙ŸŠ˙  ‘˙Ÿ‹˙—™w˙Ž’R˙†˙ˆ‘�˙™�˙™˙˜�˙™�˙˜�˙˜�ū˜ũ“˙˜Fė¯ŽMnkŖ�hĨŦ-�����������������ģģģÁÁÁ�ššš”ĢĢĢū™™™úŽŽŽū­­­˙ŽŽŽ˙­­­˙­­­˙­­˛˙ĒŽh˙œ¤˙Ž—�˙˜�˙™�˙™�˙™�˙˜�˙˜˙‘š˙‘š�˙–�˙‰’�˙‡�˙†�˙†�˙ˆ‘�˙Œ–�˙š�˙‘š˙˜˙˜�˙™�˙˜�ū™�ū˜˙—�ųĄ§JüĄĄ§Ēčį¸�ÕÔåty%���������������������——— �‡‡‡S¯¯¯˙ŸŸŸû­­­ū­­­ū­­­ūŽŽŽ˙­­­˙­­­˙­­˛˙ĒŽm˙Ĩ˙™�˙Ž—�˙™�˙™�˙˜�˙™�˙˜�˙˜˙™˙‘š˙‘š�˙‘›�˙‘š�˙‘š˙™˙˜˙˜�˙™�˙™�ū™�ū™˙Ž—�ū—�ûĻĢMųēšŧ˙ŒŒŽVš™¯�ŸŸžsx���������������������‡‡‡yyy�rrrŽŽŽ÷ĢĢĢ˙ŦŦŦøŗŗŗú­­­û­­­ü­­­ũŦ­ŦũŦ­Ŧū­­´ūĢ­‚ūĄ¨,˙“œūŽ—�˙˜˙™˙™�˙™�˙™�˙™�˙˜�˙˜�˙˜�˙˜�˙˜�˙™�ū™�ū™�ū™�ū˜�ũ˜�úŽ˜�û” ˙Š­g˙ĩ´ģ˙ŦŦ­Ė’“„„~�‰‰‰��������������������������ˇˇˇœœœ�„„„QĄĄĄ×ŖŖŖ˙ššš˙ļļļ˙ļļļ˙´´´˙˛˛˛˙°°°˙ޝ­ũŽŽļûŦ­ĄûĨĒWû™ĸû‘š�ûŽ—�üŽ—�ũŽ—�ũ˜�ũ˜�ū˜�ū™�ū™�ū˜�ū˜�ū˜�ũ˜�û˜�û™�ū‘š�˙” ˙šĄ4˙Ĩ§Žæŗ˛ÂĄŅŅŅJÅÅÂęęë�­­Ž�ŒŒŒ�������������������������TTT�ŋŋŋ�xxxaaa�ÖÖÖ ‚‚‚k˜ŖŖŖ°ĻĻĻÉĒĒĒá­­­đ°°°ü°°°˙°°Ž˙˛˛´˙´´ē˙ŗ´ž˙ǝc˙Ÿ§-˙˜Ą˙”˙’›�˙‘š�˙™�˙˜�˙˜�˙™�˙‘š�˙˜˙“œ˙• ˙“›˙‘—"ā‹ŽW „‚œTŽēЍˇ�����ģģē�ŨŨŨ�§§§�ˆˆˆ�����������������������������ĩĩĩ�|||�rrrˇˇˇ�ŽŽŽ�ĀĀĀ������������XXXlll-ŽŽŽBŽŽ[™™˜t˜˜–‹žžĻŖŖĸ¯¸ĨĻœČĄŖtŲ›ŸK斝/ō–!ų—Ÿü™Ą˙šĄ˙˜ ũ—Ÿō¤ĢטŸ"¯–(w~ƒ.: �˙ ˙˙��˙˙ē�����ÁŋŅ˙˙˙ÜÜÚ�æææ�   �������������������������������������ŽŽŽ�xxx�ĐĐĐ�‡‡‡ąąą˙˙˙???� �nnn��“““�ŖŖŖ�ĩĩĩ�����ØØė���������&˙aU˙b`ˆopS'x|40‰Ž79‹:<~„-0y|8îđĩĀ�Ļ­�‚ˆ)�]Z„���˙h[Đ����ÅÃÕ�ōō÷��������������������������������������������������������������������������������ĒĒĒwwwšššĻĻĻ´ŗ´˙˙˙¸šē˙˙˙˙˙æ�5#˙�H2˙�_ZĢ�qrV�„8�Œ‘<�”?�…‹0�}€9�ķõ�ŠŗĻŽ„‹#������������������������������������������������������������������������������������������������������������������������������������������������������•,{)w{3{7‡$���ũ˙������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������˙˙˙˙˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��ü�˙˙˙��ø���˙��ā���˙��Ā���˙��Ā���˙��Ā���˙��€���˙��€���˙��€����˙��€������€�������������������������������������ø�����ø�����ü�����ü�����ø���€�ø���€�đ���€�ā���€����?��€����?��Ā������Ā������Ā����˙��Ā����˙��Ā����˙��ā���˙��ā���˙��ø���˙��˙ø�˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��˙˙˙˙˙˙��(��� ���@���� �����€������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vvv�{{{�ZZZ�”””’’’’’’’’’nnn��������fff�����������������������������������������������������������������������������ģģģ�[[[�{{{aaa��ŒŒŒ����‹‹‹�ccc�aa`lko“”yy|Ooq<uxE‹jĨĻ™­­­ĢĢĢŦŦŦŽŽŽ•••¤¤¤�ĻĻĻ���������������������ŠŠŠ�ĄĄĄSSSooo�WWW ……….‰‰‰,‡‡‡-‡‡‡-‡‡‡,†††,\\YYYO�nnj�‰‰y�qrP�fh;�kmE�‚‚k�ĻĻŠ�ÃÁ×�ÁĀĐ�´´ļ�´´ŗ�———�����������������������žžž�ŸŸŸ�‡‡‡O’’’Ö§§§ūĒĒĒūŠŠŠ˙ĒĒĒ˙ĒĒĒũŦŦŦ˙››Å¤¤Ģ™ŸžŖœŽ‰™yyj’ss]“wxd“……|“˜˜“ĻĻŗ“¨§°’ĄĄ žžœ“‹‹‹&�™™™�����������������–––‘‘‘ŖŖŖ¤¤¤¤˙ŸŸŸ˙ŦŦŦūĢĢĢ˙ŦŦŦ˙ŦŦŦ˙ĢĢĒ˙¯¯°˙››”˙~Y˙rv*˙sx˙u| ˙v}˙v| ˙v{˙y~%˙ƒ†K˙˜™„˙ŦŦ°˙ąąļ˙œœŋŋŋ�ģģģ——œ�����������������ŸŸŸ�———Ф¤¤˙ŖŖŖûžžžũ­­­ūĒĒĒūĢĢĢūĢĢĢūĒŠ§ūą°¸ū‰Œ_üu}�û–�û’œ�û”�û”ž�û”ž�û“œ�û™�ûˆ‘�û‡�û‡%ų‰ûŦ̎ņ  ž’‘™�—–Š�����������������ÉÉÉ�ĩ§§§üĄĄĄû   ˙­­­ū­­­˙°°°˙°°°˙­­Ē˙ŽŽ¸˙ši˙‘›˙‘š˙™�˙Ž—�˙Œ–�˙–�˙˜�˙‘›�˙“œ�˙”�˙š�ū…Ž�û’–X˙––œrŖ¤ž�ϧ—ttr�������������ôôô   ÕĻĻĻ˙   ũŖŖŖ˙˛˛˛˙˛˛˛˙ŗŗŗ˙ŗŗŗ˙˛˛°˙ĩ´Ŋ˙šœp˙‡‘�˙Œ–�˙‹”˙’™(˙—œ<˙•›;˙Ž”"˙‡˙ˆ ˙ˆ˙ˆ˙Œ“ü…ū}Q×bg;lpA�vvt�EEE�777�����]]].ĸĸĸ芊Š˙ŖŖŖū¨¨¨˙ĩĩĩ˙˛˛˛˙ŗŗŗ˙ŗŗŗ˙˛˛˛˙ˇˇ¸˙žž—˙‰‹l˙ŒŽe˙Ž}˙“’˜˙››¤˙š™Ŗ˙ŒŒ˙ƒƒ{˙““Ž˙“”’˙’’’ū’‘•ū”üŽŽŒ˙ut~•{zˆ ŠŠ‰ '''LLL˙˙˙………`¤¤¤ô¯¯¯˙§§§ũĒĒĒ˙´´´˙˛˛˛˙´´´˙ŗŗŗ˙ŗŗŗ˙ļļļ˙ĸĸŖ˙˜˜ž˙––ž˙‚…ū„„‚ū‰‰†ūŠūŠŠ‰ūˆˆŠūˆˆ‰˙„„„ū……„˙zzy˙ŽūŖŖŖũ  ˙¤ĨĸāĻĻĻᔔ” ÅÅÅ�­­­‘ŠŠŠų¯¯¯˙§§§ūŠŠŠ˙ĩĩĩ˙ŗŗŗ˙ŗŗŗ˙´´´˙˛˛˛˙ļļļūŖŖĸ˙ūŒŒ‰û““’ûœœœû›››ûŸŸŸü——–ũyyxũ~˙€ū}}|˙‹‹‹ūŽ­°˙ą°´ū°¯ŗü¯¯ŗ˙°¯ą˙ĢĢŦ˙——™|ššš–––ēŦŦŦûŽŽŽ˙§§§˙¨¨¨˙ļļļ˙˛˛ŗ˙ŗŗŗ˙ŗŗŗ˙˛˛˛˙ĩĩĩ˙¨¨¨˙ąąą˙ššš˙………˙‡‡‡˙„„„˙{{{˙vvv˙rrr˙www˙www˙~~ūŖŖ ˙Ąĸ”ūŸ “ū Ą’ūũ›››åášš—ė~~~lšššĪ­­­ü°°°˙ŠŠŠūŠŠ¨˙ļļ´˙ŗŗą˙´´˛˙´´˛˙ŗŗ˛˙ĩĩĩūŦŦŦū¸¸¸û‰‰‰Žmmms|||giiiZļļļKļļļBĄĄĄÔ˛˛˛˙ŗŗ˛ûēēŧ˙Ŧ­Ŗ˙… ˙Š“ū‹”üƒ‹ūou Ylr rxlll›››ŌŦŦŦũ¨¨¨˙ĸĸĸ˙¨¨Ē˙ĩ´ž˙˛˛ģ˙ŗŗŧ˙ŗ˛ģ˙˛ą¸˙ąą°úĒĒĒū¨¨¨ŧÛÛÛ�ßßß�555�qqq�ˇˇˇ�ēēē�¤¤¤cĀĀĀ˙ÂÂÁúÂÂĀūžŊÄ˙—"˙™�˙˜�ú‘š�˙”y“œ�’œ��ppp�›››ËĒĒĒü›˙ĄĄĨūœ‡˙–7˙–6˙–4˙•1˙‘•P˙ŽŽ˛ųŗŗ˛ūĒĒǐÄÄÄÃÃÚšš~~~››››››———;ēēēūŋŋŋüŋŋŊũžžÆ˙‘—/˙—�ū—û˜�˙’œˆ‘š��‘š�����˜˜˜ŦŠŠŠûŖŖ ˙˛˛š˙¨ŠŠ˙—�˙Ž˜�˙Ž—�˙Ž˜�˙Ž–˙ŊŊÂúÃÃÂ˙ŽŽ­ąččč�ĶĶĶŊŊŊ�ŠŠŠ�˛˛˛˛˛˛�ŸŸŸVžžž˙ŋŋŋúĀĀŋūŧģÁ˙Ž•˙˜�˙˜�ú˜�ū’›™��™�����yŠŠŠøĄĄĄ˙ŽŽąūĢĢ ˙’› ˙Ž—˙˜˙š˙Š“˙¯° ũÃÃÆüļļĩö’’’.ĻĻĻ�ššš�xxxŅŅŅ�ÄÄÄ�ĢĢĢÃŧŧŧūŊŊŧûÃÃÅ˙°ą¤˙‰’˙™�ūŽ—�û” ˙¤Ģ%_Ą¨$�ŸĻ����mmm=¨¨¨ėĸĸĸ˙­­Ŧũ¯Ž´˙ž¤:˙—�˙™˙˜˙–�˙•šM˙ˇļÂüŗŗŽ˙ĒĒĒā´´´G´´´ŊŊŊ�ĢĢĢ#›››¨ŗŗŗ˙˛˛˛üŗŗŽ˙žŊÉū—œO˙Œ•�ū™ūŽ—�ūĨøŖ¨D%ą¸H�Ĩ­,����ááá §§§Øĸĸĸ˙ŦŦĒũŽ­ļ˙ĒŦ{˙’œ˙Ž—˙˜�˙™˙ˆ‘ū›~˙ˇˇÁũĩĩą˙­­Ģ˙¤¤¤Õˇˇļɯ¯¯ķĩĩĩ˙˛˛°üŗŗ¯˙ˇļÂūŸ}˙Š“˙™˙Ž—�û‘šūŖĢ*ŋ|‚ �ĸĒ(šĨ������ŨŨŨ�ĨĨĨĩŖŖŖūĢĢĒü­­ŽūŽŽŽ˙ĸ¨8˙Ž—�˙˜˙˜�˙™ū‰’ūšœs˙ŗ˛Ŋũ¸¸ŋũ¸¸š˙ŗŗ˛˙ļļĩ˙ĩĩĩûļļŧ˙ŗŗŊūšuūŠ“˙™�ū˜�ūŽ—�ü™ĄũŖĒ7E¯ļ8�ŖĒ(ĸĢ�����ŦŦŦ�ĸĸĸ„ŖŖŖū¨¨¨ûŽŽ­˙­­ŗ˙­Ž’˙›Ŗ˙–�˙™˙˜�˙™�˙‹”�ūŽ”7ūŸĄ€ūĒĒĻü¯¯ŗû¯Ž˛ũĒǧūŸ ū”9ū‹”�˙™�˙˜�˙˜ûš�ū–(ļ”�ĻŽ ¨%�¤Ŧ%�����œœœ�–––IĨĨĨū¤¤¤üŽŽ¯ū­­Ģū­­ĩū̝€˙˜Ą ˙–�˙˜˙™˙‘š˙Ž˜�˙Š“�˙Š’ ˙‹’˙‹’˙Š‘ ˙Š“�˙Ž˜�ū™ū˜�ū˜�ũŒ–�øž¤@˙››ĸ\ŠĒž�ύ„ŖĢ0���������ššš�’’’ŠŠŠôĒĒĒ˙°°°ûŦŦŦûŦŦĢû­ŦˇûĢŽ„ûšĸü—�ũŽ—�ū˜ū™ū‘šūš�˙™�ū™�ūš�ū‘š�ū˜ü˜�û˜�ū™�˙¤ŠK˙˛˛ŗō†…‘Ž§�žœÄ�������������ÁÁÁēēē�ĨĨĨgžžžė˛˛˛˙ŗŗŗ˙ąąą˙ąą¯˙°¯ˇ˙¯¯Ÿ˙¤ŠN˙–ž ˙™�˙˜�ū˜�üŽ˜�üŽ—�üŽ—�üŽ˜�ũ˜�˙™�˙’›˙“›˙žĸdŲŦĒŧާ§˛0…� ĄŽ� Ÿĩ�����������������{{{———�jjj–––L˜˜˜f¨¨¨ĸĸĸĒǍˇĒĒ¯ÍŽ­ļāĒŦđĄĻTú˜Ÿ(˙”œ˙“œ ˙” ˙”œ ˙• ˙•ņ˜ɇ(ˆy{Y@ljv­ĒË�äãį�����›œ˜�™™™�����������������‚‚‚�ĻĻĻkkk�ĻĻĻ�´´´�˛˛˛�ûüü�ĘĘĮ������wrŧ”Ō%••–3‰OCŒ’.P•œ-Y’˜+T•›-<ˆŒ:ruD�y|(�JU��ŠŠ��ĩą×××Ü÷÷ũ�ŠŠŠ���������������������–––�ĨĨĨ�ggg§§§¸¸¸ššš˙˙˙ČČĮ��������‡ƒ×�œ˜Õ�—–’�•›E�”š+�¤*�™ )�›ĸ.�–œ5�sv@�x{+€ˆtx-�ÁŋĶ�ÚÚŨ������������������������������������������������������������������������‘“ƒ‚†•œ5šĄ" ¨"œ¤ ™ #�������Š&���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙đ��˙Ā��€��€��?€��?€��������������`�ā�đ�ā�ā€Ā€��€��€��Ā��Ā��?Ā��ø�˙˙ū?˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙(������0���� �����` ����������������������������������������������������������������������������������������������������������������������§§§�ĨĨĨ����ąąąŗŗŗŗŗŗĩĩĩ���ÁÁÁ�ˇˇˇ�����������������������������������������������������ccc�›››�‘‘‘�   �ĄĄĄ�ĄĄĄ�ŖŖŖ�‘‘‘�¤¤­�ÆÄá�������������������˙�����………�{{{�LLL�¸¸¸�������������ŽŽŽ�ŖŖŖ�‚‚‚�{{{$™™™HšššE™™™E›››HŠ‹Š&ĸĸĒ�ØÕ˙�����,����������V`����������ÚÚØ�îîé�ŦŦŦ�������������šššĸĸĸ –––~œœœöĢĢĢ˙ĢĢĢ˙ĒĒĒ˙­­­˙ŖŖ¤÷——×‡‰hØtx;Ôqu.Ķuy<Ķ‚…]Ķ—˜ŽĶŠŠŗŅĒŠŽĶ““’1˜˜—�¨¨¨œ‘���������ÆÆÆ�Ģ¤¤¤˙ĸĸĸūŦŦŦüĒĒĒũĒǍũ¯¯´ü—˜ƒ˙sz˙‚Š�˙‰’�˙‹”�˙‰’�˙…�˙ƒŠ˙‹A˙ŖŖ–˙ĻĨ¯ŸÄÄČ�ģģģ‘’x�“““�����ĖĖĖ ŖŖŖâŖŖŖüĸĸĸüŽŽŽ˙°°°ū¯¯Žūą°š˙ž zū™�ũ“�ũ™�ũ™�ũš�ũ“œ�ũ‘›�ũ‹–�û‡�ũ’–ZķŦŠÕ§ĻÉ–˜ƒ“““����wwwC¤¤¤īŖŖŖū§§§ũ´´´ūŗŗŗ˙ŗŗ˛˙ˇļģ˙Ŗ¤’˙†Œ%˙А(˙’–Y˙œžv˙“–e˙„ˆ<˙ŒF˙Ž’Oū”Mü‡ŠP˙tti‹yzh�ƒg��ĄĄĄ‘‘‘¨¨¨øŠŠŠ˙ĢĢĢūĩĩĩ˙˛˛˛˙˛˛˛˙ļļĩū§§Š˙“’›ūŒ•ü‡†’ü™üšûˆ‡“ũŒ˜˙‹‹–ū‚Žū••›ūœ˙ššœąššŸ¤‡‡…�———ŗŦŦŦüĒĒĒ˙ĒĒĒū´´´˙˛˛ŗ˙˛˛˛˙ĩĩļ˙ŠŠŠ˙••”˙‹‹Š˙••“˙””“˙Ž˙zzx˙vvt˙ttr˙ŠŠ‰˙¯Žē˙¯¯ģū¯Ž¸˙¯¯ĩ˙ĸĄŠÕĄĄŠ,žžžÖŽŽŽũĢĢĢ˙ĒĒĒ˙ĩĩŗ˙ŗŗą˙ŗŗ˛˙ĩĩ´ū¯¯¯˙˛˛˛ũ•••×zzz˃ƒƒÁ———ą‡‡‡č‘‘˙––™ũ¤Ĩš˙’—Lū”™IūŽ’Jú_‡ZƒwzMNĄĄĄßĢĢĢū¤¤¤˙ЍĢū´ŗģ˙ąąš˙˛ą¸˙ąąŗû­­Ŧ˙¨¨¨Ŗžžž�ĒĒĒ�ĀĀĀ�ģģģ�ĒĒĒ]ÂÂÂ˙ÆÆČüššĩū‹”ū—�ũŒ–�ūˆ�.ˆ‘��‹”��ŖŖŖÖ¤¤ŖũŖĸŠ˙ž }˙–)˙‘—*˙”$˙ ĸ|û¸ˇžūĒǧnˇˇˇ�žžžĀ¤¤¤.ŧŧŧūŋĀŋūŧŧŋũ˜˙˜ü™ū”Q’œ�“�ŸŸŸ°ĨĨŖü­Ŧĩ˙§Ēū–�˙˜�˙‹•�˙žĸVûÆÅŌ˙ąąŦąĪĪĪ�ĖĖĖ�ÂÂÂ�ŧŧŧ�§§§nŊŊŊ˙ÃÂÅûļļ°ūŒ•ū˜�ũ‘š˙ §>šĸ�˜Ąšššr¤¤¤÷ŠŠĢ˙ŽŽĻū”œ˙Ž—˙™˙Ž•ūŗ˛ļüļļ¸˙›|ŸŸŸ ¨¨¨ĒĒĒO°°¯đŗ´°ũŋžÎũĄ¤j˙‹•�ũŽ—�ūšŖņ™ E˛¸N�Ģŗ0�”””+¤¤¤ę¨¨Ļ˙°¯šũŖ¨Q˙—�˙™˙–�˙‘–Cūŗ˛ŋūˇˇŊ˙¯¯Žčޝ­Ū°°¯˙ĩĩļ˙ˇļÅūŖ¤˙‹”˙˜�û‘š˙Ą¨&ω“�˜ ̞/�ĶĶĶ�ŖŖŖÍĻĻĨūŽŽ˛üޝĸūšĸ˙Œ–�˙‘™ūŒ–�˙•1ūϧ”ũ˛˛¸˙´´ž˙ŗŗŊū­­Ŧū›ži˙‹“ū™�ū—�ü“œö¤­.%ĸŠ)�ŖĢ%ĻŽ%�ŽŽŽ�ĄĄĄžĨĨĨū­­Ģø­Ŧ´üŦމũ–Ÿ ũŒ–�ū™ū˜�ū‹”�ū– ũ’˜7ü‘–0ūŒ”ūŒ•�ū™�üŒ–�ų“œ˙ŸĸcēąģK�Ģą\¤Ŧ)�����ŽŽŽ�§§§P¨¨¨˙°°°˙ޝ­˙¯¯ˇ˙ŦŽ˙šĸ$˙Ž˜�ūŽ—�ũ™�ü˜�ûŽ—�ûŽ˜�û™�ū™�˙™˙”œ˙ĒŦ‡ī­ĢÄO˛°ÄŧŊŧŖĒ(�����ģģģŖŖŖ‡‡‡T§§§ŖŽŽŽŊĒĒ¨ÕŽŽļé­­ŠøĻĒf˙™ (˙“› ˙‘š˙’›˙’š˙’›˙— 鎕(¯“–rb—˜‘ĨĨĢ�”•Š�ŧŧē���������žžž�ŧŧŧ�ŠŠŠ�ēēē�ÔÔÕ��y|H ŠĒĻį.ĄĄ¤A˜œNS—&b–"k’™#_“$9‚‡(E>’�ļĩŋ�ģ­˙�ĒŠˇ� ŸĨ�Ŋž¸���������ĖĖĖ�����uuu´´´ĪĪĪBBC�Ž‰�˜�ŦŠÕ�ϧ›�› B�™Ą�šĸ�—Ÿ�•œ#�‚†)�][^}„dp��ÃÃĮ�ˇˇš����������������������������������������´´ŗŧģÄްŠĄŠ'˜ ›Ŗ›¤Ÿ§�����������������������������������������������������������������������������������������������������������������������������������˙˙˙�˙˙˙�˙˙˙�˙˙˙�Ā�?�€��€��€������������<��>��<���€��€��€��€��Ā�?�˙Ã˙�˙˙˙�˙˙˙�˙˙˙�(������ ���� �����@������������������´´´�×××����°°° ąąą ĒĒĒÍÍÎĩļ°”•§¨ĨŠŠĒ�������ŸŸŸ�˜˜˜�����ĄĄĄ�ŦŦŦ–––�§§¨�§§Ļ�ŠŠŽ�ŋŊä�œšĩ�hf~�Šŋ�ļ˛˙�žžž�ŠŠŠ�ŠŠˆ�“““�����¤¤¤�¨¨¨�“““F¤¤¤d¤¤ŖdĄĄ¤T´ŗË)Ÿ°'jh~%‡„°%̧é&ŸŸŦ"~~w�‚„h�“^�����˜˜˜   Ĩĸĸĸ˙ĢĢĢ˙ŦŦŽ˙ŖŖ˙‡‹BūƒŠũ€† ü„Š ü“NúŖ¤˜ø ŸĻ8§Ĩē­­ļ����˜˜˜\ĸĸĸūĻĻĻü°°¯ú˛ąˇú¤Ĩ‘ûˆ’�ū‘š�ū“›˙Ž—�˙Š”�ú”˙’f ™ŸI�•›G�ûûûĄĻĻĻúŦŦŦū´´´˙ĩĩļūŦŦĒ˙qũŒ}ü•••ū‰Š}ũ‹Œ}ū‰Šzũ‘…÷’•r{‘“oAšš�ŖŖŖÔŦŦŦũ­­­˙´´´ū´´´˙°°°˙™™Ÿ˙‰‰Œ˙˙„˙zz~˙Žū§¨ĄūĨĨĨ˙žž­äŖĸZ§§§ëŠŠ¨ũĒĒŦ˙˛˛´˙ąąŗü°°¯ū§§ĻŸ››™AŠŠŠ1ĄĄžsĩĩ¸ū˛ŗĸû• ū‹”č{%”›ŖŖžáĻĨŽūžĄo˙—ū—%ú´´°˙ąą´UÁÁĮ�´´ŗ�Ēǧ)ÂÂÃ˙ēģŗũ—ū™�ôž§ šŖ�  ™­¨¨¯ü¨Ē€˙Ž˜�˙‹•�ũĒŦ…ūˇˇÄĪģģē(¸¸ĩŦŦϰŧģČūŠĢú‹•�ū—ŸŨ‰7…Ž™™˜aĨĨĨôŽŽ°ūšĄ*ū–�˙Ž–ū§Š’˙¯Ž¸÷ŗŗŊņĩ´ŋ˙§¨’ūŽ–ø˜�ũšĸ…Š“ �•žŖŖŖĻĻ¤æ­­˛˙Ŧ­”ũ•žüŒ–�ûŒ•�ø—>ũ›ŸUū•›8øŒ•�ũŽ˜�˙˜Ÿ-ũ¤¨€°°Š�ĨŠi°°°�™™™~ĒĒ¨čŽŽ´÷Ģ­—˙œĸ;˙’› ˙Ž˜�˙Ž—�˙˜�˙˜û–œ3Ėύ‰k´ē�ˇ¸Ž–§��˙˙˙����ssw‘’†"›š­7¯ŽŊMŸŖPb‘˜"s•œy•œ^Œ’&+”;�™Ą(�jW=�ēģt� ��ĐĐĐ�III����ĄĄš�ǍČ�ŗąČ�ŸŖT�•�–ž�”œ�Œ’)�“G�ĸ¨<}ci�Ĩ­A����������������ģģšˇˇŧĩĩŠĸ¨E • ” ‘𠐗—Ÿ�Ĩ�› N���������˙˙��˙˙��ã˙��€������������ƒ��Ã��ƒ�����€��€��ü?��˙˙��˙˙��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/dcm2laz/form.lfm��������������������������������������������������������������0000664�0000000�0000000�00000006222�13220512030�0016755�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������object Form1: TForm1 Left = 126 Height = 480 Top = 178 Width = 760 AllowDropFiles = True Caption = 'dcm2' ClientHeight = 461 ClientWidth = 760 Constraints.MinHeight = 120 Constraints.MinWidth = 640 Menu = MainMenu1 OnClose = FormClose OnCreate = FormCreate OnDropFiles = FormDropFiles OnResize = FormResize Position = poScreenCenter LCLVersion = '1.2.4.0' Visible = True object Panel1: TPanel Left = 0 Height = 34 Top = 0 Width = 760 Align = alTop BevelOuter = bvNone ClientHeight = 34 ClientWidth = 760 TabOrder = 0 object compressCheck: TCheckBox Left = 80 Height = 17 Top = 9 Width = 20 Checked = True OnClick = compressCheckClick ParentBidiMode = False State = cbChecked TabOrder = 0 end object outnameLabel: TLabel Left = 104 Height = 13 Top = 9 Width = 64 Caption = 'Output Name' ParentColor = False ParentShowHint = False ShowHint = True end object outnameEdit: TEdit Left = 192 Height = 21 Hint = 'Name for NIfTI images. Special characters are %f (Folder name) %i (ID) %n (patient Name) %p (Protocol name) %s (Series number) %t (Time)' Top = 8 Width = 232 OnKeyUp = outnameEditKeyUp ParentShowHint = False ShowHint = True TabOrder = 1 Text = 'outnameEdit' end object Label2: TLabel Left = 8 Height = 13 Hint = 'Set whether NIfTI images are compressed (.nii.gz) or not (.nii)' Top = 9 Width = 47 Caption = 'Compress' ParentColor = False end object outputFolderLabel: TLabel Left = 432 Height = 13 Top = 10 Width = 65 Caption = 'Output folder' ParentColor = False end object outputFolderName: TButton Left = 520 Height = 25 Hint = 'NIfTI files will be saved to this folder. Press this button and click Cancel if you want files NIfTI images saved to same folder as DICOM input' Top = 5 Width = 240 Caption = 'input folder' OnClick = outputFolderNameClick ParentShowHint = False ShowHint = True TabOrder = 2 end end object Memo1: TMemo Left = 0 Height = 427 Top = 34 Width = 760 Align = alClient ScrollBars = ssAutoBoth TabOrder = 1 end object MainMenu1: TMainMenu left = 24 top = 48 object FileMenu: TMenuItem Caption = 'File' object DicomMenu: TMenuItem Caption = 'DICOM to NIfTI...' OnClick = DicomMenuClick end object ParRecMenu: TMenuItem Caption = 'PAR/REC to NIfTI...' OnClick = ParRecMenuClick end object ResetMenu: TMenuItem Caption = 'Reset defaults' OnClick = ResetMenuClick end end object EditMenu: TMenuItem Caption = 'Edit' object CopyMenu: TMenuItem Caption = 'Copy' OnClick = CopyMenuClick end end end object OpenDialog1: TOpenDialog Filter = 'Philips research (*.par)|*.PAR;*.par' Options = [ofAllowMultiSelect, ofEnableSizing, ofViewDetail] left = 96 top = 48 end end ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/dcm2laz/form.pas��������������������������������������������������������������0000664�0000000�0000000�00000025245�13220512030�0016770�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������unit form; {$IFDEF FPC} {$mode delphi}{$H+} {$ENDIF} interface uses {$IFNDEF UNIX} Registry, {$ENDIF} Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls, Menus, Process, untar; type { TForm1 } TForm1 = class(TForm) MainMenu1: TMainMenu; FileMenu: TMenuItem; EditMenu: TMenuItem; CopyMenu: TMenuItem; DicomMenu: TMenuItem; ResetMenu: TMenuItem; OpenDialog1: TOpenDialog; ParRecMenu: TMenuItem; outputFolderName: TButton; compressCheck: TCheckBox; Label2: TLabel; outputFolderLabel: TLabel; outnameEdit: TEdit; outnameLabel: TLabel; Memo1: TMemo; Panel1: TPanel; procedure compressCheckClick(Sender: TObject); procedure DicomMenuClick(Sender: TObject); procedure FormResize(Sender: TObject); function getOutputFolder: string; procedure outnameEditKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); procedure ParRecMenuClick(Sender: TObject); procedure ProcessFile(infilename: string); procedure FormClose(Sender: TObject; var CloseAction: TCloseAction); procedure FormCreate(Sender: TObject); procedure FormDropFiles(Sender: TObject; const FileNames: array of String); procedure CopyMenuClick(Sender: TObject); procedure outputFolderNameClick(Sender: TObject); procedure ResetMenuClick(Sender: TObject); procedure RunCmd (lCmd: string); function getExeName : string; //return path for command line tool procedure readIni (ForceReset: boolean); //load preferences procedure writeIni; //save preferences private { private declarations } public { public declarations } end; var Form1: TForm1; implementation {$R *.lfm} const kExeName = 'dcm2niix' ; var isAppDoneInitializing : boolean = false; function FindDefaultExecutablePathX(const Executable: string): string; begin result := FindDefaultExecutablePath(kExeName); if result = '' then result := FindDefaultExecutablePath(ExtractFilePath (paramstr(0)) +kExeName); end; function TForm1.getExeName : string; var lF: string; begin result := FindDefaultExecutablePathX(kExeName); if not fileexists(result) then begin lF := ExtractFilePath (paramstr(0)); result := lF+kExeName; if not fileexists(result) then begin Memo1.Lines.Clear; memo1.Lines.Add('Error: unable to find executable '+kExeName+' in path'); memo1.Lines.Add(' Solution: copy '+kExeName+' to '+lF); result := ''; end; //not in same folder as GUI end; //not in path {$IFNDEF UNIX} //strip .exe for Windows result := ChangeFileExt(result, ''); {$ENDIF} end; //exeName() {$IFDEF UNIX} function iniName : string; begin result := GetEnvironmentVariable ('HOME')+PathDelim+'.dcm2nii.ini'; end; procedure TForm1.writeIni; var iniFile : TextFile; begin AssignFile(iniFile, iniName); ReWrite(iniFile); if (compressCheck.checked) then WriteLn(iniFile, 'isGZ=1') else WriteLn(iniFile, 'isGZ=0'); WriteLn(iniFile, 'filename='+outnameEdit.caption); CloseFile(iniFile); end; //writeIni procedure TForm1.readIni (ForceReset: boolean); var fileData, rowData : TStringList; row, i: integer; opts_isGz: boolean; opts_filename: string; begin opts_isGz := true; //opts_outdir := ''; opts_filename := '%t_%p_%s'; if FileExists( iniName) and (not (ForceReset )) then begin fileData := TStringList.Create; fileData.LoadFromFile(iniName); // Load from Testing.txt file if (fileData.Count > 0) then begin rowData := TStringList.Create; rowData.Delimiter := '='; for row := 0 to (fileData.Count-1) do begin //for each row of file rowData.DelimitedText:=fileData[row]; if ((rowData.Count > 1) and (CompareText(rowData[0] ,'isGZ')= 0)) then opts_isGz := (CompareText(rowData[1],'1') = 0); if ((rowData.Count > 1) and (CompareText(rowData[0] ,'filename')= 0)) then begin opts_filename := ''; if (rowData.Count > 2) then for i := 1 to (rowData.Count-2) do opts_filename := opts_filename+ rowData[i]+' '; opts_filename := opts_filename+ rowData[rowData.Count-1]; end; end; rowData.Free; end; fileData.Free; end else memo1.Lines.Add('Using default settings'); compressCheck.Checked := opts_isGz; outnameEdit.Caption := opts_filename; getExeName; end; //readIni() {$ELSE} //For Windows we save preferences in the registry to ensure user has write access procedure TForm1.writeIni; var ARegistry: TRegistry; begin ARegistry := TRegistry.Create; ARegistry.RootKey := HKEY_CURRENT_USER;//HKEY_LOCAL_MACHINE; if ARegistry.OpenKey ('\Software\dcm2nii',true) then begin ARegistry.WriteBool('isGZ', compressCheck.Checked ); ARegistry.WriteString('filename', outnameEdit.Caption ); end; ARegistry.Free; end; //writeIni() procedure TForm1.readIni (ForceReset: boolean); var ARegistry: TRegistry; opts_isGz: boolean; opts_filename: string; begin //showmessage(inttostr(SizeOf(Integer))); opts_isGz := true; opts_filename := '%t_%p_%s'; if not ForceReset then begin ARegistry := TRegistry.Create; ARegistry.RootKey := HKEY_CURRENT_USER;//HKEY_LOCAL_MACHINE; if ARegistry.OpenKey ('\Software\dcm2nii',true) then begin if ARegistry.ValueExists( 'isGZ' ) then opts_isGz := ARegistry.ReadBool( 'isGZ' ); if ARegistry.ValueExists( 'isGZ' ) then opts_filename := ARegistry.ReadString( 'filename' ); end; ARegistry.Free; end; compressCheck.Checked := opts_isGz; outnameEdit.Caption := opts_filename; getExeName; end; //readIni() {$ENDIF} procedure TForm1.RunCmd (lCmd: string); //http://wiki.freepascal.org/Executing_External_Programs var OutputLines: TStringList; MemStream: TMemoryStream; OurProcess: TProcess; NumBytes: LongInt; BytesRead: LongInt; const READ_BYTES = 2048; begin if (not isAppDoneInitializing) then exit; if (getExeName = '') then exit; Memo1.Lines.Clear; Memo1.Lines.Add(lCmd); Form1.refresh; Memo1.refresh; Memo1.invalidate; MemStream := TMemoryStream.Create; BytesRead := 0; OurProcess := TProcess.Create(nil); {$IFDEF UNIX} OurProcess.Environment.Add(GetEnvironmentVariable('PATH')); {$ENDIF} OurProcess.CommandLine := lCmd; // We cannot use poWaitOnExit here since we don't // know the size of the output. On Linux the size of the // output pipe is 2 kB; if the output data is more, we // need to read the data. This isn't possible since we are // waiting. So we get a deadlock here if we use poWaitOnExit. OurProcess.Options := [poUsePipes, poNoConsole]; OurProcess.Execute; while True do begin // make sure we have room MemStream.SetSize(BytesRead + READ_BYTES); // try reading it NumBytes := OurProcess.Output.Read((MemStream.Memory + BytesRead)^, READ_BYTES); if NumBytes > 0 // All read() calls will block, except the final one. then begin Inc(BytesRead, NumBytes); end else BREAK // Program has finished execution. end; MemStream.SetSize(BytesRead); OutputLines := TStringList.Create; OutputLines.LoadFromStream(MemStream); Memo1.Lines.AddStrings(OutputLines); OutputLines.Free; OurProcess.Free; MemStream.Free; end; function TForm1.getOutputFolder: string; begin if (outputFolderName.Tag > 0) then result := outputFolderName.Caption else result := ''; end; //getOutputFolder procedure TForm1.ProcessFile(infilename: string); var cmd, outputFolder, inFolder: string; begin inFolder := infilename; if isTGZ(inFolder) then begin infolder := deTGZ(infolder); if infolder = '' then exit; //error end; cmd := getExeName +' '; if compressCheck.checked then cmd := cmd + '-z y ' else cmd := cmd + '-z n '; outputFolder := getOutputFolder; if length(outputFolder) > 0 then cmd := cmd + '-o '+outputFolder+' '; cmd := cmd + '-f "'+outnameEdit.Text+'" '; if length(inFolder) > 0 then cmd := cmd +'"'+inFolder+'"'; RunCmd(cmd); end; //ProcessFile() procedure TForm1.outnameEditKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); begin ProcessFile(''); end; //outnameEditKeyUp() procedure TForm1.ParRecMenuClick(Sender: TObject); var lI: integer; begin if not OpenDialog1.execute then exit; //ProcessFile(OpenDialog1.filename); if OpenDialog1.Files.count < 1 then exit; for lI := 0 to (OpenDialog1.Files.count-1) do ProcessFile(OpenDialog1.Files[lI]); end; //ParRecMenuClick() function getDirPrompt (lDefault: string): string; begin result := lDefault; // Set the starting directory chdir(result); //start search from default dir... if SelectDirectory(result, [sdAllowCreate,sdPerformCreate,sdPrompt], 0) then chdir(result) else result := ''; end; //getDirPrompt() procedure TForm1.DicomMenuClick(Sender: TObject); var dir: string; begin dir := getDirPrompt(''); ProcessFile( dir); end; //DicomMenuClick() procedure TForm1.compressCheckClick(Sender: TObject); begin ProcessFile(''); end; procedure TForm1.FormResize(Sender: TObject); begin outputFolderName.width := Form1.Width-outputFolderName.left-2; end; //FormResize() procedure TForm1.FormDropFiles(Sender: TObject; const FileNames: array of String); begin ProcessFile( FileNames[0]); end; //FormDropFiles() procedure TForm1.CopyMenuClick(Sender: TObject); begin Memo1.SelectAll; Memo1.CopyToClipboard; end; //CopyMenuClick() procedure TForm1.outputFolderNameClick(Sender: TObject); var lDir : string; begin if (outputFolderName.Tag > 0) then //start search from prior location lDir := outputFolderName.Caption else lDir := ''; lDir := getDirPrompt(lDir); outputFolderName.Tag := length(lDir); if length(lDir) > 0 then outputFolderName.Caption := lDir else outputFolderName.Caption := 'input folder'; end; //outputFolderNameClick() procedure TForm1.ResetMenuClick(Sender: TObject); begin isAppDoneInitializing := false; readIni(true); isAppDoneInitializing := true; ProcessFile(''); end; procedure TForm1.FormCreate(Sender: TObject); begin readIni(false); //memo1.lines.add('for details see http://www.mccauslandcenter.sc.edu/CRNL/tools/dcm2niix'); application.ShowButtonGlyphs:= sbgNever; isAppDoneInitializing := true; ProcessFile(''); end; //FormCreate() procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction); begin writeIni; end; //FormClose() end. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/dcm2laz/untar.pas�������������������������������������������������������������0000664�0000000�0000000�00000014001�13220512030�0017142�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������unit untar; interface {$IFDEF FPC}{$mode delphi}{$H+}{$ENDIF} uses sysutils, LibTar, classes, dialogs, zstream; function deTGZ (lFilename: string): string; function isTGZ (var lStr: string): boolean; implementation function isTGZ (var lStr: string): boolean; var lExt: string; begin lExt := ExtractFileExt(lStr); lExt := UpperCase(lExt); if (lExt='.TGZ') then Result := true else Result := false; end; procedure Extract (var lTarFile: string; lOverwrite: boolean); //extract target VAR TA : TTarArchive; DirRec : TTarDirRec; lPos,lLen,lnumFilesTotal,lnumFilesCompleted,lPct: longint; lStr,lOutDir,lLocalDir,lFileName,lNewDir,lTarName : String; begin lOutDir := extractfiledir(lTarFile); //next Count files for progress bar.... lnumFilesTotal := 0; TA := TTarArchive.Create (lTarFile); TRY TA.Reset; TA.SetFilePos (0); TA.FindNext (DirRec); repeat inc(lnumFilesTotal); until not TA.FindNext (DirRec); FINALLY TA.Free; END; //finished counting files //next: extract files... lnumFilesCompleted := 0; //FProgress := 0; TA := TTarArchive.Create (lTarFile); TRY TA.Reset; TA.SetFilePos (0); TA.FindNext (DirRec); repeat inc(lNumFilesCompleted); {lPct := round(lNumFilesCOmpleted/lNumfilesTotal*100); if lPct > FProgress then begin //only update progress bar 100 times: do not waste time updating screen FProgress := lPct; DoOnProgress; end;} if DirRec.Name <> '' then begin //Screen.Cursor := crHourGlass; TRY //filename change '/' to '\' lTarName := ''; lLen := length(DirRec.name); for lPos := 1 to lLen do begin if (DirRec.Name[lPos]='/') or (DirRec.Name[lPos]='\') then lTarName := lTarName + pathdelim//'\' else if (DirRec.Name[lPos]=':') then else lTarName := lTarName + DirRec.Name[lPos]; end; lFilename := lOutDir+pathdelim+lTarName; lLocalDir := extractfiledir(lFileName); if (DirectoryExists(lLocalDir)) then begin if lOverwrite{(lProceed = mrYes) or (lProceed = mrYesToAll)} then begin if (length(lFilename)>2) and (lFilename[length(lFilename)] = pathdelim) then begin lLen := length(lFilename)-1; lStr := lFilename; lFilename := ''; for lPos := 1 to lLen do lFilename := lFilename+lStr[lPos]; if not DirectoryExists(lFilename) then begin mkdir (lFilename); end; end else TA.ReadFile (lFileName); end; //proceed end else begin lLen := length(lTarName); lPos := 1; if (lLen >= 1) and (lTarName[1] = pathdelim) then inc(lPos); lNewDir := lOutDir+pathdelim; while lPos <= lLen do begin if (lTarName[lPos] = pathdelim) then begin //showmessage('creating directory:'+lNewDir); if not DirectoryExists(lNewDir) then mkdir(lNewDir); lNewDir := lNewDir + pathdelim; end else lNewDir := lNewDir + lTarName[lPos]; inc(lPos); end; if (lFileName[length(lFileName)] <> pathdelim) and (DirectoryExists(lLocalDir)) and (not Fileexists(lFileName)) then begin TA.ReadFile (lFileName) end; end; FINALLY //Screen.Cursor := crDefault; END; end; until not TA.FindNext (DirRec); FINALLY TA.Free; END; end; function UnGZipFile(inname, outname:string):string; var ArchStream : TGZFileStream; FileStream : TFileStream; bytescopied: integer; buf: array[0..65535] of byte; chunksize : integer; begin chunksize := SizeOf(buf); ArchStream := TGZFileStream.Create(inname,gzOpenRead); FileStream := TFileStream.Create(outname,fmOpenWrite or fmCreate ); repeat bytescopied := ArchStream.read(buf,chunksize); FileStream.Write(buf,bytescopied) ; until bytescopied < chunksize; FileStream.Free; ArchStream.Free; end; function DeTGZ (lFilename: string): string; var lPath,lName,lExt,lOutPath,lTempDir,lTarName,lDicomName: string; begin result := ''; if (not fileexists(lFilename)) or (not isTGZ(lFilename)) then exit; lPath := ExtractFilePath(lFilename); lExt := ExtractFileExt(lFilename); lName := ChangeFileExt (ExtractFileName (lFilename), ''); lOutPath := lPath+lName; if DirectoryExists(lOutPath) then begin if IsConsole then writeln( 'Unable to extract TGZ file - folder exists '+lOutpath) else ShowMessage('Unable to extract TGZ file - folder exists '+lOutpath); exit; end; MkDir(lOutPath); lTempDir := lOutPath+pathdelim+'temp'; MkDir(lTempDir); lTarName := lTempDir+Pathdelim+lName+'.tar'; UnGZipFile( lFilename,lTarName); Extract (lTarName,true); deletefile(lTarName); result := lOutPath; end; end. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/dcm_qa/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�13220512030�0015200�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/docs/�������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13220512030�0014704�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/docs/CMakeLists.txt�����������������������������������������������������������0000664�0000000�0000000�00000002336�13220512030�0017450�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright 2016, The dcm2niix contributors # # This program is free software: you can redistribute it and/or modify it under # the terms of the new BSD license. See the license.txt file or visit # https://opensource.org/licenses/BSD-3-Clause for details. find_program(SPHINX_EXECUTABLE NAMES sphinx-build HINTS $ENV{SPHINX_DIR} PATH_SUFFIXES bin DOC "Sphinx documentation generator") if (NOT SPHINX_EXECUTABLE) message(FATAL_ERROR "sphinx-build executable not found") endif () set(SPHINX_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/source") set(SPHINX_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/build") set(SPHINX_CACHE_DIR "${CMAKE_CURRENT_BINARY_DIR}/doctrees") set(SPHINX_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/man") configure_file("${SPHINX_SOURCE_DIR}/conf.py" "${SPHINX_BUILD_DIR}/conf.py" COPYONLY) add_custom_target(build-man ALL COMMAND ${SPHINX_EXECUTABLE} -b man -c "${SPHINX_BUILD_DIR}" -d "${SPHINX_CACHE_DIR}" "${SPHINX_SOURCE_DIR}" "${SPHINX_OUTPUT_DIR}" COMMENT "Generating man pages with Sphinx") install(DIRECTORY "${SPHINX_OUTPUT_DIR}/" DESTINATION "share/man/man1") ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/docs/source/������������������������������������������������������������������0000775�0000000�0000000�00000000000�13220512030�0016204�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/docs/source/conf.py�����������������������������������������������������������0000664�0000000�0000000�00000023441�13220512030�0017507�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # # dcm2niix documentation build configuration file, created by # sphinx-quickstart on Tue Dec 20 16:22:24 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # # import os # import sys # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The encoding of source files. # # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'dcm2niix' copyright = u'2016 The dcm2niix contributors' author = u'The dcm2niix contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = u'1.0' # The full version, including alpha/beta/rc tags. release = u'1.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # # today = '' # # Else, today_fmt is used as the format for a strftime call. # # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. # # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. # "<project> v<release> documentation" by default. # # html_title = u'dcm2niix v1.0' # A shorter title for the navigation bar. Default is the same as html_title. # # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # # html_logo = None # The name of an image file (relative to this directory) to use as a favicon of # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # # html_extra_path = [] # If not None, a 'Last updated on:' timestamp is inserted at every page # bottom, using the given strftime format. # The empty string is equivalent to '%b %d, %Y'. # # html_last_updated_fmt = None # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # # html_additional_pages = {} # If false, no module index is generated. # # html_domain_indices = True # If false, no index is generated. # # html_use_index = True # If true, the index is split into individual pages for each letter. # # html_split_index = False # If true, links to the reST sources are added to the pages. # # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a <link> tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh' # # html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # 'ja' uses this config value. # 'zh' user can custom change `jieba` dictionary path. # # html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. # # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'dcm2niixdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'dcm2niix.tex', u'dcm2niix Documentation', author, 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # # latex_use_parts = False # If true, show page references after internal links. # # latex_show_pagerefs = False # If true, show URL addresses after external links. # # latex_show_urls = False # Documents to append as an appendix to all manuals. # # latex_appendices = [] # It false, will not define \strong, \code, itleref, \crossref ... but only # \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added # packages. # # latex_keep_old_macro_names = True # If false, no module index is generated. # # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('dcm2niix', 'dcm2niix', u'DICOM to NifTI converter', ['This manual was developed and is maintained by Ghislain Antony Vaillant <ghisvail@gmail.com>'], 1), ('dcm2niibatch', 'dcm2niibatch', u'DICOM to NifTI batch converter', ['This manual was developed and is maintained by Benjamin Irving <mail@birving.com>'], 1) ] # If true, show URL addresses after external links. # # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'dcm2niix', u'dcm2niix Documentation', author, 'dcm2niix', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # # texinfo_appendices = [] # If false, no module index is generated. # # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # # texinfo_no_detailmenu = False �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/docs/source/dcm2niibatch.rst��������������������������������������������������0000664�0000000�0000000�00000004541�13220512030�0021271�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������:orphan: dcm2niibatch manual =================== Synopsis -------- **dcm2niixbatch** <*configuration-file*> Description ----------- Most medical imaging devices save images in some variation of the popular DICOM format. However, most scientific tools expect medical images to be stored in the comparatively simpler NIfTI format. **dcm2niix** is designed to perform such conversion from DICOM to NIfTI with a simple command-line interface. **dcm2niibatch** acts as a wrapper around **dcm2niix** and allows the processing of multiple DICOM sequences by specifying the list of files and settings in a yaml text file. The makes processing a dataset of DICOM scans simpler and more easily repeatable. In addition, yaml files are designed to be both human and machine readable and a script can easily be written in many programming languages to automatically create a yaml file based on an existing folder structure. Please be advised that **dcm2niix** and **dcm2niibatch** have been developed for research purposes only and should not be considered a clinical tool. Configuration file format ------------------------- Perform a batch conversion of multiple dicoms using **dcm2niibatch**, which is run by passing a configuration file e.g *dcm2niibatch batch_config.yml* The configuration file should be in yaml format as shown in example *batch_config.yaml* .. code-block:: yaml Options: isGz: false isFlipY: false isVerbose: false isCreateBIDS: false isOnlySingleFile: false Files: - in_dir: /path/to/first/folder out_dir: /path/to/output/folder filename: dcemri - in_dir: /path/to/second/folder out_dir: /path/to/output/folder filename: fa3 You can add as many files as you want to convert as long as this structure stays consistent. Note that a dash must separate each file. in_dir Path to the dicom files out_dir Path to save the nifti file filename File name of nifti file to save See also -------- :manpage:`dcm2niix(1)` Licensing --------- Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without any warranty. ���������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/docs/source/dcm2niix.rst������������������������������������������������������0000664�0000000�0000000�00000004355�13220512030�0020462�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������:orphan: dcm2niix manual =============== Synopsis -------- **dcm2niix** [*options*] <*sourcedir*> Description ----------- Most medical imaging devices save images in some variation of the popular DICOM format. However, most scientific tools expect medical images to be stored in the comparatively simpler NIfTI format. **dcm2niix** is designed to perform such conversion from DICOM to NIfTI with a simple command-line interface. Please be advised that **dcm2niix** has been developed for research purposes only and should not be considered a clinical tool. Options ------- -b <y/n> Save additional BIDS metadata to a side-car .json file. -f <format> Format string for the output filename(s). The following specifiers are supported: - %a, antenna (coil) number - %c, comments - %d, description - %e, echo number - %f, folder name - %i, patient ID - %m, manufacturer - %n, patient name - %p, protocol - %s, series number - %t, time - %u, acquisition number - %z, sequence name. The default format string is "%p_%e_%4s". -m <y/n> Merge slices from the same series regardless of study time, echo, coil, orientation, etc... -o <path> Output directory where the converted files should be saved. If unspecified, the files are saved within the specified source directory. -s <y/n> Convert a single file only. -t <y/n> Save patient details. -v <h/y/n> Enable verbose output. "n" for succinct, "y" for verbose, "h" for high verbosity -x <y/n> Crop images. -z <y/i/n> Desired compression method. The "y"es option uses the external program pigz if available. The "i" option compresses the image using the slower built-in compression routines. Licensing --------- Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without any warranty. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/docs/source/index.rst���������������������������������������������������������0000664�0000000�0000000�00000000655�13220512030�0020053�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������.. dcm2niix documentation master file, created by sphinx-quickstart on Tue Dec 20 16:22:24 2016. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to dcm2niix's documentation! ==================================== Contents: .. toctree:: :maxdepth: 2 Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` �����������������������������������������������������������������������������������dcm2niix-1.0.20171215/license.txt�������������������������������������������������������������������0000664�0000000�0000000�00000003710�13220512030�0016140�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������The following files are from different authors and have their own licenses nifti.h, nifti1_io.h/nifti1_io_core.cpp is public domain http://niftilib.sourceforge.net http://sourceforge.net/projects/niftilib/files/latest/download ujpeg.h/ujpeg.cpp uses the MIT license (see file for license text) http://keyj.emphy.de/nanojpeg/ miniz.c is public domain (http://unlicense.org) https://code.google.com/p/miniz/ --- The Software has been developed for research purposes only and is not a clinical tool Copyright (c) 2014-2016 Chris Rorden. 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. Neither the name of the copyright owner nor the name of this project (dcm2niix) may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT OWNER ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.��������������������������������������������������������dcm2niix-1.0.20171215/qtGui/������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13220512030�0015045�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/qtGui/Q_DebugStream.h���������������������������������������������������������0000664�0000000�0000000�00000002476�13220512030�0017711�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������//http://stackoverflow.com/questions/10308425/redirect-stdcout-to-a-qtextedit #ifndef QDEBUGSTREAM_H #define QDEBUGSTREAM_H #include <iostream> #include <streambuf> #include <string> #include "qtextedit.h" class QDebugStream : public std::basic_streambuf<char> { public: QDebugStream(std::ostream &stream, QTextEdit* text_edit) : m_stream(stream) { log_window = text_edit; m_old_buf = stream.rdbuf(); stream.rdbuf(this); } ~QDebugStream() { // output anything that is left if (!m_string.empty()) log_window->append(m_string.c_str()); m_stream.rdbuf(m_old_buf); } protected: virtual int_type overflow(int_type v) { if (v == '\n') { log_window->append(m_string.c_str()); m_string.erase(m_string.begin(), m_string.end()); } else m_string += v; return v; } virtual std::streamsize xsputn(const char *p, std::streamsize n) { m_string.append(p, p + n); int pos = 0; while (pos != std::string::npos) { pos = m_string.find('\n'); if (pos != std::string::npos) { std::string tmp(m_string.begin(), m_string.begin() + pos); log_window->append(tmp.c_str()); m_string.erase(m_string.begin(), m_string.begin() + pos + 1); } } return n; } private: std::ostream &m_stream; std::streambuf *m_old_buf; std::string m_string; QTextEdit* log_window; }; #endif��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/qtGui/Q_DebugStream_new.h�����������������������������������������������������0000664�0000000�0000000�00000004754�13220512030�0020563�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������//http://www.qtforum.org/article/678/redirecting-cout-cerr-to-qdebug.html #ifndef Q_DEBUGSTREAM_H #define Q_DEBUGSTREAM_H #include <iostream> #include <streambuf> #include <string> #include "QTextEdit.h" class Q_DebugStream : public std::basic_streambuf<char> { public: Q_DebugStream(std::ostream &stream, QTextEdit* text_edit) : m_stream(stream) { log_window = text_edit; m_old_buf = stream.rdbuf(); stream.rdbuf(this); } ~Q_DebugStream() { m_stream.rdbuf(m_old_buf); } static void registerQDebugMessageHandler(){ qInstallMessageHandler(myQDebugMessageHandler); } private: static void myQDebugMessageHandler(QtMsgType, const QMessageLogContext &, const QString &msg) { std::cout << msg.toStdString().c_str(); } protected: //This is called when a std::endl has been inserted into the stream virtual int_type overflow(int_type v) { if (v == '\n') { log_window->append(""); } return v; } /*virtual std::streamsize xsputn(const char *p, std::streamsize n) { std::string m_string; m_string.append(p, p + n); int pos = 0; while (pos != std::string::npos) { pos = m_string.find('\n'); if (pos != std::string::npos) { std::string tmp(m_string.begin(), m_string.begin() + pos); log_window->append(tmp.c_str()); m_string.erase(m_string.begin(), m_string.begin() + pos + 1); } } return n; }*/ virtual std::streamsize xsputn(const char *p, std::streamsize n) { QString str(p); //str.remove(QRegExp("[^a-zA-Z/0-9. \\d\\s]")); //QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); //str.remove(QRegExp(QString::fromUtf8("[-`~!@#$%^&*()_—+=|:;<>ÂĢÂģ,.?/{}\'\"\\\[\\\]\\\\]"))); if (str.contains("\n")){ QStringList strSplitted = str.split("\n"); log_window->moveCursor (QTextCursor::End); log_window->insertPlainText (strSplitted.at(0)); //Index 0 is still on the same old line for(int i = 1; i < strSplitted.size(); i++){ log_window->append(strSplitted.at(i)); } }else{ log_window->moveCursor (QTextCursor::End); log_window->insertPlainText (str); //log_window->append(str); } return n; } private: std::ostream &m_stream; std::streambuf *m_old_buf; QTextEdit* log_window; }; #endif // Q_DEBUGSTREAM_H ��������������������dcm2niix-1.0.20171215/qtGui/dcm2.pro����������������������������������������������������������������0000664�0000000�0000000�00000002030�13220512030�0016407�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#------------------------------------------------- # # Project created by QtCreator 2014-04-22T13:13:08 # #------------------------------------------------- QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets DEFINES += UNICODE #link zlib for Windows OS see https://gitorious.org/tiled/stefanbellers-tiled-qt/commit/2f9cb77ae082223ef2324965cd8f45a2f245098c #INCLUDEPATH += $$[QT_INSTALL_PREFIX]/src/3rdparty/zlib #INCLUDEPATH += ../zlib LIBS += -lz TARGET = dcm2 TEMPLATE = app #our .c files have c++ code, but old versions of xcode will complain if we use .cpp instead of .c QMAKE_CFLAGS += -x c++ QMAKE_CXXFLAGS += -x c++ #we need to redirect the std:cout instead of printf QMAKE_CFLAGS += -DmyUseCOut SOURCES += main.cpp\ mainwindow.cpp \ nifti1_io_core.c \ nii_dicom_batch.c \ nii_dicom.c \ nii_ortho.c \ HEADERS += mainwindow.h \ nifti1_io_core.h \ nifti1.h \ nii_dicom_batch.h \ nii_dicom.h \ nii_ortho.h \ tinydir.h \ Q_DebugStream.h FORMS += mainwindow.ui ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/qtGui/dcm2.pro.user�����������������������������������������������������������0000664�0000000�0000000�00000043740�13220512030�0017401�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE QtCreatorProject> <!-- Written by QtCreator 3.1.0, 2014-05-09T16:07:16. --> <qtcreator> <data> <variable>ProjectExplorer.Project.ActiveTarget</variable> <value type="int">0</value> </data> <data> <variable>ProjectExplorer.Project.EditorSettings</variable> <valuemap type="QVariantMap"> <value type="bool" key="EditorConfiguration.AutoIndent">true</value> <value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value> <value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value> <valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0"> <value type="QString" key="language">Cpp</value> <valuemap type="QVariantMap" key="value"> <value type="QByteArray" key="CurrentPreferences">CppGlobal</value> </valuemap> </valuemap> <valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1"> <value type="QString" key="language">QmlJS</value> <valuemap type="QVariantMap" key="value"> <value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value> </valuemap> </valuemap> <value type="int" key="EditorConfiguration.CodeStyle.Count">2</value> <value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value> <value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value> <value type="int" key="EditorConfiguration.IndentSize">4</value> <value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value> <value type="int" key="EditorConfiguration.MarginColumn">80</value> <value type="bool" key="EditorConfiguration.MouseHiding">true</value> <value type="bool" key="EditorConfiguration.MouseNavigation">true</value> <value type="int" key="EditorConfiguration.PaddingMode">1</value> <value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value> <value type="bool" key="EditorConfiguration.ShowMargin">false</value> <value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value> <value type="bool" key="EditorConfiguration.SpacesForTabs">true</value> <value type="int" key="EditorConfiguration.TabKeyBehavior">0</value> <value type="int" key="EditorConfiguration.TabSize">8</value> <value type="bool" key="EditorConfiguration.UseGlobal">true</value> <value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value> <value type="bool" key="EditorConfiguration.addFinalNewLine">true</value> <value type="bool" key="EditorConfiguration.cleanIndentation">true</value> <value type="bool" key="EditorConfiguration.cleanWhitespace">true</value> <value type="bool" key="EditorConfiguration.inEntireDocument">false</value> </valuemap> </data> <data> <variable>ProjectExplorer.Project.PluginSettings</variable> <valuemap type="QVariantMap"/> </data> <data> <variable>ProjectExplorer.Project.Target.0</variable> <valuemap type="QVariantMap"> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop Qt 5.2.1 clang 64bit</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop Qt 5.2.1 clang 64bit</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">qt.521.clang_64.essentials_kit</value> <value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">1</value> <value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value> <value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value> <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0"> <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Users/rorden/Documents/cocoa/dcm2/build-dcm2-Desktop_Qt_5_2_1_clang_64bit-Debug</value> <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0"> <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0"> <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value> <value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">false</value> <value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibraryAuto">true</value> <value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value> <value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value> </valuemap> <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1"> <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value> <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments"> <value type="QString">-w</value> <value type="QString">-r</value> </valuelist> <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value> <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value> <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value> </valuemap> <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value> </valuemap> <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1"> <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0"> <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value> <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments"> <value type="QString">-w</value> <value type="QString">-r</value> </valuelist> <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value> <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value> <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value> </valuemap> <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value> </valuemap> <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value> <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value> <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Debug</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value> <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value> <value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value> </valuemap> <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1"> <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Users/rorden/Documents/cocoa/dcm2/build-dcm2-Desktop_Qt_5_2_1_clang_64bit-Release</value> <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0"> <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0"> <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value> <value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">false</value> <value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibraryAuto">true</value> <value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value> <value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value> </valuemap> <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1"> <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value> <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments"> <value type="QString">-w</value> <value type="QString">-r</value> </valuelist> <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value> <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value> <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value> </valuemap> <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value> </valuemap> <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1"> <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0"> <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value> <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments"> <value type="QString">-w</value> <value type="QString">-r</value> </valuelist> <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value> <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value> <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value> </valuemap> <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value> </valuemap> <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value> <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value> <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Release</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value> <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value> <value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value> </valuemap> <value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">2</value> <valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0"> <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0"> <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value> </valuemap> <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy locally</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value> </valuemap> <value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value> <valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/> <valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0"> <valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/> <value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value> <value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value> <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value> <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value> <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value> <value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value> <value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value> <value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value> <value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value> <value type="int" key="Analyzer.Valgrind.NumCallers">25</value> <valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/> <value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value> <value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value> <value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value> <value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value> <value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value> <valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds"> <value type="int">0</value> <value type="int">1</value> <value type="int">2</value> <value type="int">3</value> <value type="int">4</value> <value type="int">5</value> <value type="int">6</value> <value type="int">7</value> <value type="int">8</value> <value type="int">9</value> <value type="int">10</value> <value type="int">11</value> <value type="int">12</value> <value type="int">13</value> <value type="int">14</value> </valuelist> <value type="int" key="PE.EnvironmentAspect.Base">2</value> <valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">dcm2</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:/Users/rorden/Documents/cocoa/dcm2/qtGui/dcm2.pro</value> <value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments"></value> <value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.ProFile">dcm2.pro</value> <value type="bool" key="Qt4ProjectManager.Qt4RunConfiguration.UseDyldImageSuffix">false</value> <value type="bool" key="Qt4ProjectManager.Qt4RunConfiguration.UseTerminal">false</value> <value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory"></value> <value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value> <value type="bool" key="RunConfiguration.UseCppDebugger">false</value> <value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value> <value type="bool" key="RunConfiguration.UseMultiProcess">false</value> <value type="bool" key="RunConfiguration.UseQmlDebugger">false</value> <value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value> </valuemap> <value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value> </valuemap> </data> <data> <variable>ProjectExplorer.Project.TargetCount</variable> <value type="int">1</value> </data> <data> <variable>ProjectExplorer.Project.Updater.EnvironmentId</variable> <value type="QByteArray">{6365facd-1c54-4b35-8fef-327797cb09f7}</value> </data> <data> <variable>ProjectExplorer.Project.Updater.FileVersion</variable> <value type="int">15</value> </data> </qtcreator> ��������������������������������dcm2niix-1.0.20171215/qtGui/main.cpp����������������������������������������������������������������0000664�0000000�0000000�00000000255�13220512030�0016477�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/qtGui/mainwindow.cpp����������������������������������������������������������0000664�0000000�0000000�00000007465�13220512030�0017741�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "mainwindow.h" #include "ui_mainwindow.h" #include <QFileDialog> #include <QDragEnterEvent> #include <QClipboard> #include "nii_dicom_batch.h" #include "Q_DebugStream.h" #include <time.h> // clock_t, clock, CLOCKS_PER_SEC #if defined(_WIN64) || defined(_WIN32) #include <windows.h> //write to registry #endif void MainWindow::showPrefs(bool doClear) { ui->compressCheck->setChecked(opts.isGz); ui->outputFilenameEdit->setText(opts.filename); size_t kLen = 40; if (strlen(opts.outdir) < 1) ui->folderButton->setText("input folder"); else if (strlen(opts.outdir) > kLen) { char cString[kLen+1]; memcpy(cString,opts.outdir,kLen-1); cString[kLen-1] = 0; ui->folderButton->setText(cString); } else ui->folderButton->setText(opts.outdir); this->showExampleFilename( doClear); } void MainWindow::processFile(const QString &arg1) { //convert folder (DICOM) or file (PAR/REC) to NIFTI format ui->theTextView->clear(); struct TDCMopts optsTemp; optsTemp = opts; //conversion may change values like the outdir (if not specified) strcpy(optsTemp.indir, arg1.toStdString().c_str()); printf("processing %s\n", arg1.toStdString().c_str()); clock_t start = clock(); nii_loadDir (&(optsTemp)); std::cout<< "Required "<<((float)(clock()-start))/CLOCKS_PER_SEC<<"seconds."<<std::endl; } void MainWindow::showExampleFilename(bool doClear) { char niiFilename[1024]; nii_createDummyFilename(niiFilename, opts); if (doClear) ui->theTextView->clear(); std::cout<<niiFilename<<std::endl; std::cout<< "Version "<<kDCMvers<<std::endl; } void MainWindow::on_outputFilenameEdit_textEdited(const QString &arg1) { strcpy(opts.filename, arg1.toStdString().c_str()); this->showExampleFilename(true); } MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); ui->theTextView->setReadOnly(true); setAcceptDrops(true); new QDebugStream(std::cout, ui->theTextView); //Redirect Console output to QTextEdit //new Q_DebugStream(std::cout, ui->theTextView); //Redirect Console output to QTextEdit //Q_DebugStream::registerQDebugMessageHandler(); //Redirect qDebug() output to QTextEdit const char *appPath =QCoreApplication::applicationFilePath().toStdString().c_str(); readIniFile (&opts, &appPath); this->showPrefs(false); } MainWindow::~MainWindow() { saveIniFile (opts); //save preferences delete ui; } void MainWindow::on_compressCheck_clicked() { opts.isGz = ui->compressCheck->isChecked(); this->showExampleFilename(true); } void MainWindow::on_actionCopy_triggered() { QString newText = ui->theTextView->toPlainText(); QApplication::clipboard()->setText(newText); } void MainWindow::on_actionPAR_REC_to_NIfTI_triggered() { QString fileName = QFileDialog::getOpenFileName(this, tr("Open Philips PAR image"),directory.path(), tr("PAR Files (*.par *.PAR)")); if ( fileName.isNull() ) return; processFile(fileName); } void MainWindow::on_actionDICOM_to_NIfTI_triggered() { QString path = QFileDialog::getExistingDirectory (this, tr("Directory"), directory.path()); if ( path.isNull() ) return; directory.setPath(path); processFile(path); } void MainWindow::dragEnterEvent(QDragEnterEvent *ev) { ev->accept(); } void MainWindow::dropEvent(QDropEvent *event) { QList<QUrl> urls = event->mimeData()->urls(); event->acceptProposedAction(); processFile(urls[0].toLocalFile()); } void MainWindow::on_folderButton_clicked() { QString path = QFileDialog::getExistingDirectory (this, tr("Select output folder (cancel to use input folder)"), directory.path()); if ( path.isNull() ) path = ""; strcpy(opts.outdir, path.toStdString().c_str()); directory.setPath(path); this->showPrefs(true); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/qtGui/mainwindow.h������������������������������������������������������������0000664�0000000�0000000�00000002300�13220512030�0017365�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QDir> #include <QDropEvent> #include <QUrl> #include <QDebug> #include <QMimeData> //#include <QProcess> #include "nii_dicom_batch.h" namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); //QProcess *myProcess; //Ui::MainWindow *ui; struct TDCMopts opts; private slots: //void on_pushButton_clicked(); void on_compressCheck_clicked(); void on_actionCopy_triggered(); void on_actionPAR_REC_to_NIfTI_triggered(); void on_actionDICOM_to_NIfTI_triggered(); //void on_lineEdit_textEdited(const QString &arg1); //void updateText(); //void readData(); void processFile(const QString &arg1); void on_folderButton_clicked(); void on_outputFilenameEdit_textEdited(const QString &arg1); private: Ui::MainWindow *ui; QDir directory; protected: void showExampleFilename(bool doClear) ; void showPrefs(bool doClear); void dropEvent(QDropEvent *ev); void dragEnterEvent(QDragEnterEvent *ev); //StdoutRedirector *redirector; }; #endif // MAINWINDOW_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/qtGui/mainwindow.ui�����������������������������������������������������������0000664�0000000�0000000�00000012362�13220512030�0017564�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>MainWindow</class> <widget class="QMainWindow" name="MainWindow"> <property name="enabled"> <bool>true</bool> </property> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>760</width> <height>480</height> </rect> </property> <property name="minimumSize"> <size> <width>720</width> <height>120</height> </size> </property> <property name="windowTitle"> <string>dcm2</string> </property> <widget class="QWidget" name="centralWidget"> <layout class="QGridLayout" name="gridLayout"> <property name="leftMargin"> <number>4</number> </property> <property name="topMargin"> <number>4</number> </property> <property name="rightMargin"> <number>4</number> </property> <property name="bottomMargin"> <number>4</number> </property> <item row="0" column="0"> <widget class="QCheckBox" name="compressCheck"> <property name="maximumSize"> <size> <width>89</width> <height>16777215</height> </size> </property> <property name="toolTip"> <string>If checked, images will be stored as compressed .nii.gz</string> </property> <property name="layoutDirection"> <enum>Qt::RightToLeft</enum> </property> <property name="text"> <string>Compress</string> </property> </widget> </item> <item row="0" column="1"> <widget class="QLabel" name="label"> <property name="minimumSize"> <size> <width>87</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> <width>87</width> <height>16777215</height> </size> </property> <property name="text"> <string> Output name</string> </property> </widget> </item> <item row="0" column="2"> <widget class="QLineEdit" name="outputFilenameEdit"> <property name="minimumSize"> <size> <width>231</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> <width>231</width> <height>16777215</height> </size> </property> <property name="toolTip"> <string>Name of NIfTI images (%c= comments, %f folder name, %n name, %p protocol, %s series, %t time)</string> </property> </widget> </item> <item row="0" column="3"> <widget class="QLabel" name="label_2"> <property name="minimumSize"> <size> <width>96</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> <width>96</width> <height>16777215</height> </size> </property> <property name="text"> <string> Output folder</string> </property> </widget> </item> <item row="0" column="4"> <widget class="QPushButton" name="folderButton"> <property name="minimumSize"> <size> <width>200</width> <height>0</height> </size> </property> <property name="toolTip"> <string>Set location for converted images (or choose to have files saved to the same location as the input images)</string> </property> <property name="text"> <string>Output Folder</string> </property> </widget> </item> <item row="1" column="0" colspan="5"> <widget class="QTextEdit" name="theTextView"> <property name="maximumSize"> <size> <width>16777215</width> <height>16777215</height> </size> </property> <property name="frameShadow"> <enum>QFrame::Sunken</enum> </property> <property name="lineWidth"> <number>1</number> </property> <property name="autoFormatting"> <set>QTextEdit::AutoBulletList</set> </property> </widget> </item> </layout> </widget> <widget class="QMenuBar" name="menuBar"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>760</width> <height>22</height> </rect> </property> <widget class="QMenu" name="menuDcm2"> <property name="title"> <string>File</string> </property> <addaction name="actionDICOM_to_NIfTI"/> <addaction name="actionPAR_REC_to_NIfTI"/> </widget> <widget class="QMenu" name="menuEdit"> <property name="title"> <string>Edit</string> </property> <addaction name="actionCopy"/> </widget> <addaction name="menuDcm2"/> <addaction name="menuEdit"/> </widget> <action name="actionDICOM_to_NIfTI"> <property name="text"> <string>DICOM to NIfTI...</string> </property> <property name="shortcut"> <string>Ctrl+D</string> </property> </action> <action name="actionPAR_REC_to_NIfTI"> <property name="text"> <string>PAR/REC to NIfTI...</string> </property> <property name="shortcut"> <string>Ctrl+P</string> </property> </action> <action name="actionCopy"> <property name="text"> <string>Copy</string> </property> <property name="shortcut"> <string>Ctrl+C</string> </property> </action> </widget> <layoutdefault spacing="6" margin="11"/> <resources/> <connections/> </ui> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/qtGui/nifti1_io_core.c��������������������������������������������������������0000664�0000000�0000000�00000041300�13220512030�0020100�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "nifti1_io_core.h" #include <math.h> #include <stdlib.h> #include <sys/stat.h> #include <stdbool.h> #include <ctype.h> #include <string.h> #include <stddef.h> #include <float.h> #include <unistd.h> #include <stdio.h> void nifti_swap_8bytes( size_t n , void *ar ) // 4 bytes at a time { register size_t ii ; unsigned char * cp0 = (unsigned char *)ar, * cp1, * cp2 ; register unsigned char tval ; for( ii=0 ; ii < n ; ii++ ){ cp1 = cp0; cp2 = cp0+7; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp1++; cp2--; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp1++; cp2--; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp1++; cp2--; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp0 += 8; } return ; } void nifti_swap_4bytes( size_t n , void *ar ) // 4 bytes at a time { register size_t ii ; unsigned char * cp0 = (unsigned char *)ar, * cp1, * cp2 ; register unsigned char tval ; for( ii=0 ; ii < n ; ii++ ){ cp1 = cp0; cp2 = cp0+3; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp1++; cp2--; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp0 += 4; } return ; } void nifti_swap_2bytes( size_t n , void *ar ) // 2 bytes at a time { register size_t ii ; unsigned char * cp1 = (unsigned char *)ar, * cp2 ; unsigned char tval; for( ii=0 ; ii < n ; ii++ ){ cp2 = cp1 + 1; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp1 += 2; } return ; } int isSameFloat (float a, float b) { return (fabs (a - b) <= FLT_EPSILON); } vec3 setVec3(float x, float y, float z) { vec3 v = {x, y, z}; return v; } vec4 setVec4(float x, float y, float z) { vec4 v= {x, y, z, 1}; return v; } vec3 crossProduct(vec3 u, vec3 v) { return setVec3(u.v[1]*v.v[2] - v.v[1]*u.v[2], -u.v[0]*v.v[2] + v.v[0]*u.v[2], u.v[0]*v.v[1] - v.v[0]*u.v[1]); } float dotProduct(vec3 u, vec3 v) { return (u.v[0]*v.v[0] + v.v[1]*u.v[1] + v.v[2]*u.v[2]); } vec3 nifti_vect33_norm (vec3 v) { //normalize vector length vec3 vO = v; float vLen = sqrt( (v.v[0]*v.v[0]) + (v.v[1]*v.v[1]) + (v.v[2]*v.v[2])); if (vLen <= FLT_EPSILON) return vO; //avoid divide by zero for (int i = 0; i < 3; i++) vO.v[i] = v.v[i]/vLen; return vO; } vec3 nifti_vect33mat33_mul(vec3 v, mat33 m ) { //multiply vector * 3x3matrix vec3 vO; for (int i=0; i<3; i++) { //multiply Pcrs * m vO.v[i] = 0; for(int j=0; j<3; j++) vO.v[i] += m.m[i][j]*v.v[j]; } return vO; } vec4 nifti_vect44mat44_mul(vec4 v, mat44 m ) { //multiply vector * 4x4matrix vec4 vO; for (int i=0; i<4; i++) { //multiply Pcrs * m vO.v[i] = 0; for(int j=0; j<4; j++) vO.v[i] += m.m[i][j]*v.v[j]; } return vO; } mat44 nifti_dicom2mat(float orient[7], float patientPosition[4], float xyzMM[4]) { //create NIfTI header based on values from DICOM header //note orient has 6 values, indexed from 1, patient position and xyzMM have 3 values indexed from 1 mat33 Q, diagVox; Q.m[0][0] = orient[1]; Q.m[0][1] = orient[2] ; Q.m[0][2] = orient[3] ; // load Q Q.m[1][0] = orient[4]; Q.m[1][1] = orient[5] ; Q.m[1][2] = orient[6]; //printf("Orient %g %g %g %g %g %g\n",orient[1],orient[2],orient[3],orient[4],orient[5],orient[6] ); /* normalize row 1 */ double val = Q.m[0][0]*Q.m[0][0] + Q.m[0][1]*Q.m[0][1] + Q.m[0][2]*Q.m[0][2] ; if( val > 0.0l ){ val = 1.0l / sqrt(val) ; Q.m[0][0] *= (float)val ; Q.m[0][1] *= (float)val ; Q.m[0][2] *= (float)val ; } else { Q.m[0][0] = 1.0l ; Q.m[0][1] = 0.0l ; Q.m[0][2] = 0.0l ; } /* normalize row 2 */ val = Q.m[1][0]*Q.m[1][0] + Q.m[1][1]*Q.m[1][1] + Q.m[1][2]*Q.m[1][2] ; if( val > 0.0l ){ val = 1.0l / sqrt(val) ; Q.m[1][0] *= (float)val ; Q.m[1][1] *= (float)val ; Q.m[1][2] *= (float)val ; } else { Q.m[1][0] = 0.0l ; Q.m[1][1] = 1.0l ; Q.m[1][2] = 0.0l ; } /* row 3 is the cross product of rows 1 and 2*/ Q.m[2][0] = Q.m[0][1]*Q.m[1][2] - Q.m[0][2]*Q.m[1][1] ; /* cross */ Q.m[2][1] = Q.m[0][2]*Q.m[1][0] - Q.m[0][0]*Q.m[1][2] ; /* product */ Q.m[2][2] = Q.m[0][0]*Q.m[1][1] - Q.m[0][1]*Q.m[1][0] ; Q = nifti_mat33_transpose(Q); if (nifti_mat33_determ(Q) < 0.0) { Q.m[0][2] = -Q.m[0][2]; Q.m[1][2] = -Q.m[1][2]; Q.m[2][2] = -Q.m[2][2]; } //next scale matrix LOAD_MAT33(diagVox, xyzMM[1],0.0l,0.0l, 0.0l,xyzMM[2],0.0l, 0.0l,0.0l, xyzMM[3]); Q = nifti_mat33_mul(Q,diagVox); mat44 Q44; //4x4 matrix includes translations LOAD_MAT44(Q44, Q.m[0][0],Q.m[0][1],Q.m[0][2],patientPosition[1], Q.m[1][0],Q.m[1][1],Q.m[1][2],patientPosition[2], Q.m[2][0],Q.m[2][1],Q.m[2][2],patientPosition[3]); return Q44; } float nifti_mat33_determ( mat33 R ) /* determinant of 3x3 matrix */ { double r11,r12,r13,r21,r22,r23,r31,r32,r33 ; /* INPUT MATRIX: */ r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2]; /* [ r11 r12 r13 ] */ r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2]; /* [ r21 r22 r23 ] */ r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2]; /* [ r31 r32 r33 ] */ return (float)(r11*r22*r33-r11*r32*r23-r21*r12*r33 +r21*r32*r13+r31*r12*r23-r31*r22*r13) ; } mat33 nifti_mat33_mul( mat33 A , mat33 B ) /* multiply 2 3x3 matrices */ //see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c { mat33 C ; int i,j ; for( i=0 ; i < 3 ; i++ ) for( j=0 ; j < 3 ; j++ ) C.m[i][j] = A.m[i][0] * B.m[0][j] + A.m[i][1] * B.m[1][j] + A.m[i][2] * B.m[2][j] ; return C ; } mat44 nifti_mat44_mul( mat44 A , mat44 B ) /* multiply 2 3x3 matrices */ { mat44 C ; int i,j ; for( i=0 ; i < 4 ; i++ ) for( j=0 ; j < 4; j++ ) C.m[i][j] = A.m[i][0] * B.m[0][j] + A.m[i][1] * B.m[1][j] + A.m[i][2] * B.m[2][j] + A.m[i][3] * B.m[3][j]; return C ; } mat33 nifti_mat33_transpose( mat33 A ) /* transpose 3x3 matrix */ //see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c { mat33 B; int i,j ; for( i=0 ; i < 3 ; i++ ) for( j=0 ; j < 3 ; j++ ) B.m[i][j] = A.m[j][i]; return B; } mat33 nifti_mat33_inverse( mat33 R ) /* inverse of 3x3 matrix */ { double r11,r12,r13,r21,r22,r23,r31,r32,r33 , deti ; mat33 Q ; // INPUT MATRIX: r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2]; // [ r11 r12 r13 ] r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2]; // [ r21 r22 r23 ] r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2]; // [ r31 r32 r33 ] deti = r11*r22*r33-r11*r32*r23-r21*r12*r33 +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; if( deti != 0.0l ) deti = 1.0l / deti ; Q.m[0][0] = deti*( r22*r33-r32*r23) ; Q.m[0][1] = deti*(-r12*r33+r32*r13) ; Q.m[0][2] = deti*( r12*r23-r22*r13) ; Q.m[1][0] = deti*(-r21*r33+r31*r23) ; Q.m[1][1] = deti*( r11*r33-r31*r13) ; Q.m[1][2] = deti*(-r11*r23+r21*r13) ; Q.m[2][0] = deti*( r21*r32-r31*r22) ; Q.m[2][1] = deti*(-r11*r32+r31*r12) ; Q.m[2][2] = deti*( r11*r22-r21*r12) ; return Q ; } float nifti_mat33_rownorm( mat33 A ) // max row norm of 3x3 matrix { float r1,r2,r3 ; r1 = fabs(A.m[0][0])+fabs(A.m[0][1])+fabs(A.m[0][2]) ; r2 = fabs(A.m[1][0])+fabs(A.m[1][1])+fabs(A.m[1][2]) ; r3 = fabs(A.m[2][0])+fabs(A.m[2][1])+fabs(A.m[2][2]) ; if( r1 < r2 ) r1 = r2 ; if( r1 < r3 ) r1 = r3 ; return r1 ; } float nifti_mat33_colnorm( mat33 A ) // max column norm of 3x3 matrix { float r1,r2,r3 ; r1 = fabs(A.m[0][0])+fabs(A.m[1][0])+fabs(A.m[2][0]) ; r2 = fabs(A.m[0][1])+fabs(A.m[1][1])+fabs(A.m[2][1]) ; r3 = fabs(A.m[0][2])+fabs(A.m[1][2])+fabs(A.m[2][2]) ; if( r1 < r2 ) r1 = r2 ; if( r1 < r3 ) r1 = r3 ; return r1 ; } mat33 nifti_mat33_polar( mat33 A ) { mat33 X , Y , Z ; float alp,bet,gam,gmi , dif=1.0 ; int k=0 ; X = A ; // force matrix to be nonsingular gam = nifti_mat33_determ(X) ; while( gam == 0.0 ){ // perturb matrix gam = 0.00001 * ( 0.001 + nifti_mat33_rownorm(X) ) ; X.m[0][0] += gam ; X.m[1][1] += gam ; X.m[2][2] += gam ; gam = nifti_mat33_determ(X) ; } while(1){ Y = nifti_mat33_inverse(X) ; if( dif > 0.3 ){ // far from convergence alp = sqrt( nifti_mat33_rownorm(X) * nifti_mat33_colnorm(X) ) ; bet = sqrt( nifti_mat33_rownorm(Y) * nifti_mat33_colnorm(Y) ) ; gam = sqrt( bet / alp ) ; gmi = 1.0 / gam ; } else gam = gmi = 1.0 ; // close to convergence Z.m[0][0] = 0.5 * ( gam*X.m[0][0] + gmi*Y.m[0][0] ) ; Z.m[0][1] = 0.5 * ( gam*X.m[0][1] + gmi*Y.m[1][0] ) ; Z.m[0][2] = 0.5 * ( gam*X.m[0][2] + gmi*Y.m[2][0] ) ; Z.m[1][0] = 0.5 * ( gam*X.m[1][0] + gmi*Y.m[0][1] ) ; Z.m[1][1] = 0.5 * ( gam*X.m[1][1] + gmi*Y.m[1][1] ) ; Z.m[1][2] = 0.5 * ( gam*X.m[1][2] + gmi*Y.m[2][1] ) ; Z.m[2][0] = 0.5 * ( gam*X.m[2][0] + gmi*Y.m[0][2] ) ; Z.m[2][1] = 0.5 * ( gam*X.m[2][1] + gmi*Y.m[1][2] ) ; Z.m[2][2] = 0.5 * ( gam*X.m[2][2] + gmi*Y.m[2][2] ) ; dif = fabs(Z.m[0][0]-X.m[0][0])+fabs(Z.m[0][1]-X.m[0][1]) +fabs(Z.m[0][2]-X.m[0][2])+fabs(Z.m[1][0]-X.m[1][0]) +fabs(Z.m[1][1]-X.m[1][1])+fabs(Z.m[1][2]-X.m[1][2]) +fabs(Z.m[2][0]-X.m[2][0])+fabs(Z.m[2][1]-X.m[2][1]) +fabs(Z.m[2][2]-X.m[2][2]) ; k = k+1 ; if( k > 100 || dif < 3.e-6 ) break ; // convergence or exhaustion X = Z ; } return Z ; } void nifti_mat44_to_quatern( mat44 R , float *qb, float *qc, float *qd, float *qx, float *qy, float *qz, float *dx, float *dy, float *dz, float *qfac ) { double r11,r12,r13 , r21,r22,r23 , r31,r32,r33 ; double xd,yd,zd , a,b,c,d ; mat33 P,Q ; // offset outputs are read write out of input matrix ASSIF(qx,R.m[0][3]) ; ASSIF(qy,R.m[1][3]) ; ASSIF(qz,R.m[2][3]) ; // load 3x3 matrix into local variables */ r11 = R.m[0][0] ; r12 = R.m[0][1] ; r13 = R.m[0][2] ; r21 = R.m[1][0] ; r22 = R.m[1][1] ; r23 = R.m[1][2] ; r31 = R.m[2][0] ; r32 = R.m[2][1] ; r33 = R.m[2][2] ; // compute lengths of each column; these determine grid spacings xd = sqrt( r11*r11 + r21*r21 + r31*r31 ) ; yd = sqrt( r12*r12 + r22*r22 + r32*r32 ) ; zd = sqrt( r13*r13 + r23*r23 + r33*r33 ) ; // if a column length is zero, patch the trouble if( xd == 0.0l ){ r11 = 1.0l ; r21 = r31 = 0.0l ; xd = 1.0l ; } if( yd == 0.0l ){ r22 = 1.0l ; r12 = r32 = 0.0l ; yd = 1.0l ; } if( zd == 0.0l ){ r33 = 1.0l ; r13 = r23 = 0.0l ; zd = 1.0l ; } // assign the output lengths */ ASSIF(dx,xd) ; ASSIF(dy,yd) ; ASSIF(dz,zd) ; // normalize the columns */ r11 /= xd ; r21 /= xd ; r31 /= xd ; r12 /= yd ; r22 /= yd ; r32 /= yd ; r13 /= zd ; r23 /= zd ; r33 /= zd ; /* At this point, the matrix has normal columns, but we have to allow for the fact that the hideous user may not have given us a matrix with orthogonal columns. So, now find the orthogonal matrix closest to the current matrix. One reason for using the polar decomposition to get this orthogonal matrix, rather than just directly orthogonalizing the columns, is so that inputting the inverse matrix to R will result in the inverse orthogonal matrix at this point. If we just orthogonalized the columns, this wouldn't necessarily hold. */ Q.m[0][0] = r11 ; Q.m[0][1] = r12 ; Q.m[0][2] = r13 ; // load Q Q.m[1][0] = r21 ; Q.m[1][1] = r22 ; Q.m[1][2] = r23 ; Q.m[2][0] = r31 ; Q.m[2][1] = r32 ; Q.m[2][2] = r33 ; P = nifti_mat33_polar(Q) ; // P is orthog matrix closest to Q r11 = P.m[0][0] ; r12 = P.m[0][1] ; r13 = P.m[0][2] ; // unload r21 = P.m[1][0] ; r22 = P.m[1][1] ; r23 = P.m[1][2] ; r31 = P.m[2][0] ; r32 = P.m[2][1] ; r33 = P.m[2][2] ; // [ r11 r12 r13 ] // at this point, the matrix [ r21 r22 r23 ] is orthogonal // [ r31 r32 r33 ] // compute the determinant to determine if it is proper zd = r11*r22*r33-r11*r32*r23-r21*r12*r33 +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; // should be -1 or 1 if( zd > 0 ){ // proper ASSIF(qfac,1.0) ; } else { // improper ==> flip 3rd column ASSIF(qfac,-1.0) ; r13 = -r13 ; r23 = -r23 ; r33 = -r33 ; } // now, compute quaternion parameters a = r11 + r22 + r33 + 1.0l ; if( a > 0.5l ){ // simplest case a = 0.5l * sqrt(a) ; b = 0.25l * (r32-r23) / a ; c = 0.25l * (r13-r31) / a ; d = 0.25l * (r21-r12) / a ; } else { // trickier case xd = 1.0 + r11 - (r22+r33) ; // 4*b*b yd = 1.0 + r22 - (r11+r33) ; // 4*c*c zd = 1.0 + r33 - (r11+r22) ; // 4*d*d if( xd > 1.0 ){ b = 0.5l * sqrt(xd) ; c = 0.25l* (r12+r21) / b ; d = 0.25l* (r13+r31) / b ; a = 0.25l* (r32-r23) / b ; } else if( yd > 1.0 ){ c = 0.5l * sqrt(yd) ; b = 0.25l* (r12+r21) / c ; d = 0.25l* (r23+r32) / c ; a = 0.25l* (r13-r31) / c ; } else { d = 0.5l * sqrt(zd) ; b = 0.25l* (r13+r31) / d ; c = 0.25l* (r23+r32) / d ; a = 0.25l* (r21-r12) / d ; } // if( a < 0.0l ){ b=-b ; c=-c ; d=-d; a=-a; } if( a < 0.0l ){ b=-b ; c=-c ; d=-d; } //a discarded... } ASSIF(qb,b) ; ASSIF(qc,c) ; ASSIF(qd,d) ; return ; } mat44 nifti_quatern_to_mat44( float qb, float qc, float qd, float qx, float qy, float qz, float dx, float dy, float dz, float qfac ) { mat44 R ; double a,b=qb,c=qc,d=qd , xd,yd,zd ; /* last row is always [ 0 0 0 1 ] */ R.m[3][0]=R.m[3][1]=R.m[3][2] = 0.0f ; R.m[3][3]= 1.0f ; /* compute a parameter from b,c,d */ a = 1.0l - (b*b + c*c + d*d) ; if( a < 1.e-7l ){ /* special case */ a = 1.0l / sqrt(b*b+c*c+d*d) ; b *= a ; c *= a ; d *= a ; /* normalize (b,c,d) vector */ a = 0.0l ; /* a = 0 ==> 180 degree rotation */ } else{ a = sqrt(a) ; /* angle = 2*arccos(a) */ } /* load rotation matrix, including scaling factors for voxel sizes */ xd = (dx > 0.0) ? dx : 1.0l ; /* make sure are positive */ yd = (dy > 0.0) ? dy : 1.0l ; zd = (dz > 0.0) ? dz : 1.0l ; if( qfac < 0.0 ) zd = -zd ; /* left handedness? */ R.m[0][0] = (float)( (a*a+b*b-c*c-d*d) * xd) ; R.m[0][1] = 2.0l * (b*c-a*d ) * yd ; R.m[0][2] = 2.0l * (b*d+a*c ) * zd ; R.m[1][0] = 2.0l * (b*c+a*d ) * xd ; R.m[1][1] = (float)( (a*a+c*c-b*b-d*d) * yd) ; R.m[1][2] = 2.0l * (c*d-a*b ) * zd ; R.m[2][0] = 2.0l * (b*d-a*c ) * xd ; R.m[2][1] = 2.0l * (c*d+a*b ) * yd ; R.m[2][2] = (float)( (a*a+d*d-c*c-b*b) * zd) ; /* load offsets */ R.m[0][3] = qx ; R.m[1][3] = qy ; R.m[2][3] = qz ; return R ; } mat44 nifti_mat44_inverse( mat44 R ) { double r11,r12,r13,r21,r22,r23,r31,r32,r33,v1,v2,v3 , deti ; mat44 Q ; /* INPUT MATRIX IS: */ r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2]; // [ r11 r12 r13 v1 ] r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2]; // [ r21 r22 r23 v2 ] r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2]; // [ r31 r32 r33 v3 ] v1 = R.m[0][3]; v2 = R.m[1][3]; v3 = R.m[2][3]; // [ 0 0 0 1 ] deti = r11*r22*r33-r11*r32*r23-r21*r12*r33 +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; if( deti != 0.0l ) deti = 1.0l / deti ; Q.m[0][0] = deti*( r22*r33-r32*r23) ; Q.m[0][1] = deti*(-r12*r33+r32*r13) ; Q.m[0][2] = deti*( r12*r23-r22*r13) ; Q.m[0][3] = deti*(-r12*r23*v3+r12*v2*r33+r22*r13*v3 -r22*v1*r33-r32*r13*v2+r32*v1*r23) ; Q.m[1][0] = deti*(-r21*r33+r31*r23) ; Q.m[1][1] = deti*( r11*r33-r31*r13) ; Q.m[1][2] = deti*(-r11*r23+r21*r13) ; Q.m[1][3] = deti*( r11*r23*v3-r11*v2*r33-r21*r13*v3 +r21*v1*r33+r31*r13*v2-r31*v1*r23) ; Q.m[2][0] = deti*( r21*r32-r31*r22) ; Q.m[2][1] = deti*(-r11*r32+r31*r12) ; Q.m[2][2] = deti*( r11*r22-r21*r12) ; Q.m[2][3] = deti*(-r11*r22*v3+r11*r32*v2+r21*r12*v3 -r21*r32*v1-r31*r12*v2+r31*r22*v1) ; Q.m[3][0] = Q.m[3][1] = Q.m[3][2] = 0.0l ; Q.m[3][3] = (deti == 0.0l) ? 0.0l : 1.0l ; // failure flag if deti == 0 return Q ; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/qtGui/nii_dicom.c�������������������������������������������������������������0000664�0000000�0000000�00000246643�13220512030�0017162�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������//#define MY_DEBUG #include "nifti1.h" #include "nii_dicom.h" #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <ctype.h> //toupper #include <math.h> #include <string.h> #include <stddef.h> #include <unistd.h> #include <float.h> #include <stdint.h> #include "nifti1_io_core.h" #ifdef myUseCOut #include <iostream> #endif #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifdef MY_DEBUG float deFuzz(float v) { if (fabs(v) < 0.00001) return 0; else return v; } void reportMat33(char *str, mat33 A) { printf("%s = [%g %g %g ; %g %g %g; %g %g %g ]\n",str, deFuzz(A.m[0][0]),deFuzz(A.m[0][1]),deFuzz(A.m[0][2]), deFuzz(A.m[1][0]),deFuzz(A.m[1][1]),deFuzz(A.m[1][2]), deFuzz(A.m[2][0]),deFuzz(A.m[2][1]),deFuzz(A.m[2][2])); } void reportMat44(char *str, mat44 A) { printf("%s = [%g %g %g %g; %g %g %g %g; %g %g %g %g; 0 0 0 1]\n",str, deFuzz(A.m[0][0]),deFuzz(A.m[0][1]),deFuzz(A.m[0][2]),deFuzz(A.m[0][3]), deFuzz(A.m[1][0]),deFuzz(A.m[1][1]),deFuzz(A.m[1][2]),deFuzz(A.m[1][3]), deFuzz(A.m[2][0]),deFuzz(A.m[2][1]),deFuzz(A.m[2][2]),deFuzz(A.m[2][3])); } #endif int verify_slice_dir (struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, mat44 *R){ //returns slice direction: 1=sag,2=coronal,3=axial, -= flipped if (h->dim[3] < 2) return 0; //don't care direction for single slice int iSL = 1; //find Z-slice direction: row with highest magnitude of 3rd column if ( (fabs(R->m[1][2]) >= fabs(R->m[0][2])) && (fabs(R->m[1][2]) >= fabs(R->m[2][2]))) iSL = 2; // if ( (fabs(R->m[2][2]) >= fabs(R->m[0][2])) && (fabs(R->m[2][2]) >= fabs(R->m[1][2]))) iSL = 3; //axial acquisition float pos = NAN; if ( !isnan(d2.patientPosition[iSL]) ) { //patient position fields exist pos = d2.patientPosition[iSL]; if (isSameFloat(pos, d.patientPosition[iSL])) pos = NAN; #ifdef MY_DEBUG if (!isnan(pos)) printf("position determined using lastFile %f\n",pos); #endif } if (isnan(pos) &&( !isnan(d.patientPositionLast[iSL]) ) ) { //patient position fields exist pos = d.patientPositionLast[iSL]; if (isSameFloat(pos, d.patientPosition[iSL])) pos = NAN; #ifdef MY_DEBUG if (!isnan(pos)) printf("position determined using last (4d) %f\n",pos); #endif } if (isnan(pos) && ( !isnan(d.stackOffcentre[iSL])) ) pos = d.stackOffcentre[iSL]; if (isnan(pos) && ( !isnan(d.lastScanLoc)) ) pos = d.lastScanLoc; vec4 x; x.v[0] = 0; x.v[1] = 0; x.v[2]=h->dim[3]-1; x.v[3] = 1; vec4 pos1v = nifti_vect44mat44_mul(x, *R); float pos1 = pos1v.v[iSL-1];//-1 as C indexed from 0 bool flip = false; if (!isnan(pos)) // we have real SliceLocation for last slice or volume center flip = (pos > R->m[iSL-1][3]) != (pos1 > R->m[iSL-1][3]); // same direction?, note C indices from 0 else {// we do some guess work and warn user #ifdef myUseCOut std::cout<<"WARNING: Unable to determine slice direction: please check whether slices are flipped" <<std::endl; #else printf("WARNING: Unable to determine slice direction: please check whether slices are flipped\n"); #endif } if (flip) { for (int i = 0; i < 4; i++) R->m[i][2] = -R->m[i][2]; } #ifdef MY_DEBUG printf("verify slice dir %d %d %d\n",h->dim[1],h->dim[2],h->dim[3]); reportMat44("Rout",*R); printf("iSL = %d\n",iSL); printf(" pos1 = %f\n",pos1); #endif if (flip) return -iSL; else return iSL; } //verify_slice_dir() void setQSForm(struct nifti_1_header *h, mat44 Q44) { h->sform_code = NIFTI_XFORM_SCANNER_ANAT; h->srow_x[0] = Q44.m[0][0]; h->srow_x[1] = Q44.m[0][1]; h->srow_x[2] = Q44.m[0][2]; h->srow_x[3] = Q44.m[0][3]; h->srow_y[0] = Q44.m[1][0]; h->srow_y[1] = Q44.m[1][1]; h->srow_y[2] = Q44.m[1][2]; h->srow_y[3] = Q44.m[1][3]; h->srow_z[0] = Q44.m[2][0]; h->srow_z[1] = Q44.m[2][1]; h->srow_z[2] = Q44.m[2][2]; h->srow_z[3] = Q44.m[2][3]; float dumdx, dumdy, dumdz; nifti_mat44_to_quatern( Q44 , &h->quatern_b, &h->quatern_c, &h->quatern_d,&h->qoffset_x, &h->qoffset_y, &h->qoffset_z, &dumdx, &dumdy, &dumdz,&h->pixdim[0]) ; h->qform_code = NIFTI_XFORM_SCANNER_ANAT; } //setQSForm() int headerDcm2NiiSForm(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h) { //fill header s and q form //see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c //returns sliceDirection: 0=unknown,1=sag,2=coro,3=axial,-=reversed slices // int sliceDir = 0; if (h->dim[3] < 2) return sliceDir; //don't care direction for single slice h->sform_code = NIFTI_XFORM_UNKNOWN; h->qform_code = NIFTI_XFORM_UNKNOWN; bool isOK = false; for (int i = 1; i <= 6; i++) if (d.orient[i] != 0.0) isOK = true; if (!isOK) return sliceDir; mat44 Q44 = nifti_dicom2mat(d.orient, d.patientPosition, d.xyzMM); if (d.CSA.mosaicSlices > 1) { double nRowCol = ceil(sqrt(d.CSA.mosaicSlices)); double lFactorX = (d.xyzDim[1] -(d.xyzDim[1]/nRowCol) )/2.0; double lFactorY = (d.xyzDim[2] -(d.xyzDim[2]/nRowCol) )/2.0; Q44.m[0][3] =(Q44.m[0][0]*lFactorX)+(Q44.m[0][1]*lFactorY)+Q44.m[0][3]; Q44.m[1][3] =(Q44.m[1][0]*lFactorX)+(Q44.m[1][1]*lFactorY)+Q44.m[1][3]; Q44.m[2][3] =(Q44.m[2][0]*lFactorX)+(Q44.m[2][1]*lFactorY)+Q44.m[2][3]; /* #ifdef obsolete_mosaic_flip double val = d.xyzDim[2]/nRowCol; //obsolete!!! //Q44 now equals 'dicom_to_patient' in spm_dicom_convert mat44 patient_to_tal, analyze_to_dicom; LOAD_MAT44(patient_to_tal, -1.0l,0.0l,0.0l,0.0l, 0.0l,-1.0l,0.0l,0.0l, 0.0l,0.0l,1.0l,0.0l); LOAD_MAT44(analyze_to_dicom, 1.0l,0.0l,0.0l,-1.0l, 0.0l,-1.0l,0.0l,val, 0.0l,0.0l,1.0l,-1.0l); Q44 = nifti_mat44_mul(patient_to_tal,Q44); Q44 = nifti_mat44_mul(Q44,analyze_to_dicom); //Q44 now equals 'mat' in spm_dicom_convert //subasgn.m in SPM5 translates by one voxel... LOAD_MAT44(analyze_to_dicom, 1.0l,0.0l,0.0l,1.0l, 0.0l,1.0l,0.0l,1.0l, 0.0l,0.0l,1.0l,1.0l); Q44 = nifti_mat44_mul(Q44,analyze_to_dicom); #else */ for (int c=0; c<2; c++) for (int r=0; r<4; r++) Q44.m[c][r] = -Q44.m[c][r]; // #endif mat33 Q; LOAD_MAT33(Q, d.orient[1], d.orient[4],d.CSA.sliceNormV[1], d.orient[2],d.orient[5],d.CSA.sliceNormV[2], d.orient[3],d.orient[6],d.CSA.sliceNormV[3]); if (nifti_mat33_determ(Q) < 0) { //Siemens sagittal are R>>L, whereas NIfTI is L>>R, we retain Siemens order on disk so ascending is still ascending, but we need to have the spatial transform reflect this. mat44 det; sliceDir = kSliceOrientMosaicNegativeDeterminant; //we need to handle DTI vectors accordingly LOAD_MAT44(det, 1.0l,0.0l,0.0l,0.0l, 0.0l,1.0l,0.0l,0.0l, 0.0l,0.0l,-1.0l,0.0l); //patient_to_tal.m[2][3] = 1-d.CSA.MosaicSlices; Q44 = nifti_mat44_mul(Q44,det); } } else { //not a mosaic sliceDir = verify_slice_dir(d, d2, h, &Q44); for (int c=0; c<4; c++)// LPS to nifti RAS, xform matrix before reorient for (int r=0; r<2; r++) //swap rows 1 & 2 Q44.m[r][c] = - Q44.m[r][c]; #ifdef MY_DEBUG reportMat44("final",Q44); #endif } setQSForm(h,Q44); return sliceDir; } //headerDcm2NiiSForm() int headerDcm2Nii2(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h) { //final pass after de-mosaic char txt[1024] = {""}; if (h->slice_code == NIFTI_SLICE_UNKNOWN) h->slice_code = d.CSA.sliceOrder; if (h->slice_code == NIFTI_SLICE_UNKNOWN) h->slice_code = d2.CSA.sliceOrder; //sometimes the first slice order is screwed up https://github.com/eauerbach/CMRR-MB/issues/29 sprintf(txt, "TE=%.2g;Time=%.3f", d.TE,d.acquisitionTime);// d.dateTime); if (d.CSA.phaseEncodingDirectionPositive > 0) { char dtxt[1024] = {""}; sprintf(dtxt, ";phase+=%d", d.CSA.phaseEncodingDirectionPositive); strcat(txt,dtxt); } if ((d.CSA.bandwidthPerPixelPhaseEncode > 0) && ((d.phaseEncodingRC =='C') || (d.phaseEncodingRC =='R'))) { float dwellTime = 0; if (d.phaseEncodingRC =='C') dwellTime = 1000/d.CSA.bandwidthPerPixelPhaseEncode/h->dim[2]; else dwellTime = 1000/d.CSA.bandwidthPerPixelPhaseEncode/h->dim[1]; char dtxt[1024] = {""}; sprintf(dtxt, ";dwell=%.3f", dwellTime); strcat(txt,dtxt); } //printf(" Description= '%s' [length=%lu]\n",txt, strlen(txt)); //x strlcpy(h->descrip,txt,80); snprintf(h->descrip,80, "%s",txt); if (strlen(d.imageComments) > 0) snprintf(h->aux_file,24,"%s",d.imageComments); return headerDcm2NiiSForm(d,d2, h); } //headerDcm2Nii2() int dcmStrLen (int len) { if (len < kDICOMStr) return len+1; else return kDICOMStr; } //dcmStrLen() struct TDICOMdata clear_dicom_data() { struct TDICOMdata d; d.locationsInAcquisition = 0; for (int i=0; i < 4; i++) { for (int n=0; n < kMaxDTIv; n++) d.CSA.dtiV[n][i] = 0; d.patientPosition[i] = NAN; //d.patientPosition2nd[i] = NAN; //used to distinguish XYZT vs XYTZ for Philips 4D d.patientPositionLast[i] = NAN; //used to compute slice direction for Philips 4D d.stackOffcentre[i] = NAN; d.angulation[i] = 0.0f; d.xyzMM[i] = 1; } d.CSA.numDti = 0; for (int i=0; i < 5; i++) d.xyzDim[i] = 1; for (int i = 0; i < 7; i++) d.orient[i] = 0.0f; d.patientPositionSequentialRepeats = 0;//d.isHasMixed = false; d.isHasPhase = false; d.isHasMagnitude = false; d.sliceOrient = kSliceOrientUnknown; //strcpy(d.sliceOrient,"\n"); strcpy(d.patientName, "John_Doe"); strcpy(d.patientID, "ID123"); strcpy(d.imageComments, "imgComments"); strcpy(d.studyDate, "1/1/1977"); strcpy(d.studyTime, "11:11:11"); d.dateTime = (double)19770703150928.0; d.acquisitionTime = 0.0f; strcpy(d.protocolName, "MPRAGE"); d.manufacturer = kMANUFACTURER_UNKNOWN; d.isPlanarRGB = false; d.lastScanLoc = NAN; d.TR = 0; d.TE = 0; //d.locationsInAcquisition = 0; d.numberOfDynamicScans = 0; d.imageNum = 0; d.intenScale = 1; d.intenIntercept = 0; d.seriesNum = 1; d.acquNum = 0; d.imageNum = 1; d.imageStart = 0; d.is3DAcq = false; //e.g. MP-RAGE, SPACE, TFE d.bitsAllocated = 16;//bits d.bitsStored = 0; d.samplesPerPixel = 1; d.isValid = false; d.isSigned = false; //default is unsigned! d.isExplicitVR = true; d.isLittleEndian = true; //DICOM initially always little endian d.converted2NII = 0; d.phaseEncodingRC = '?'; d.CSA.bandwidthPerPixelPhaseEncode = 0.0; d.CSA.mosaicSlices = 0; d.CSA.sliceOrder = NIFTI_SLICE_UNKNOWN; d.CSA.protocolSliceNumber1 = 0; d.CSA.phaseEncodingDirectionPositive = -1; //unknown return d; } //clear_dicom_data() void dcmStrDigitsOnly(char* lStr) { //e.g. change "H11" to " 11" size_t len = strlen(lStr); if (len < 1) return; for (int i = 0; i < len; i++) if (!isdigit(lStr[i]) ) lStr[i] = ' '; } void dcmStr(int lLength, unsigned char lBuffer[], char* lOut) { //char test[] = " 1 2 3 "; //lLength = (int)strlen(test); if (lLength < 1) return; char cString[lLength+1]; cString[lLength] =0; memcpy(cString, (char*)&lBuffer[0], lLength); //memcpy(cString, test, lLength); for (int i = 0; i < lLength; i++) if (cString[i]==' ') cString[i] = '_'; int len = 1; for (int i = 1; i < lLength; i++) { //remove repeated "_" if ((cString[i-1]!='_') || (cString[i]!='_')) { cString[len] =cString[i]; len++; } } //for each item if (cString[len-1] == '_') len--; //while ((len > 0) && (cString[len]=='_')) len--; //remove trailing '_' cString[len] = 0; //null-terminate, strlcpy does this anyway len = dcmStrLen(len); //strlcpy(lOut,cString,len); memcpy(lOut,cString,len-1); lOut[len-1] = 0; //printf(">>%s<<>>%s<<\n",cString,lOut); //lOut[len-1]='z'; //printf(">>%s<<>>%s<<\n",cString,lOut); //strlcpy(lOut,cString,len); } //dcmStr() float dcmFloat(int lByteLength, unsigned char lBuffer[], bool littleEndian) {//read binary 32-bit float //http://stackoverflow.com/questions/2782725/converting-float-values-from-big-endian-to-little-endian #ifdef __BIG_ENDIAN__ bool swap = littleEndian; #else bool swap = !littleEndian; #endif float retVal; memcpy(&retVal, (char*)&lBuffer[0], 4); if (!swap) return retVal; char *floatToConvert = ( char* ) & lBuffer; char *returnFloat = ( char* ) & retVal; //swap the bytes into a temporary buffer returnFloat[0] = floatToConvert[3]; returnFloat[1] = floatToConvert[2]; returnFloat[2] = floatToConvert[1]; returnFloat[3] = floatToConvert[0]; //printf("swapped val = %f\n",retVal); return retVal; } //dcmFloat() int dcmInt (int lByteLength, unsigned char lBuffer[], bool littleEndian) { //read binary 16 or 32 bit integer if (littleEndian) { if (lByteLength <= 3) return lBuffer[0] | (lBuffer[1]<<8); //shortint vs word? return lBuffer[0]+(lBuffer[1]<<8)+(lBuffer[2]<<16)+(lBuffer[3]<<24); //shortint vs word? } if (lByteLength <= 3) return lBuffer[1] | (lBuffer[0]<<8); //shortint vs word? return lBuffer[3]+(lBuffer[2]<<8)+(lBuffer[1]<<16)+(lBuffer[0]<<24); //shortint vs word? } //dcmInt() int dcmStrInt (int lByteLength, unsigned char lBuffer[]) {//read float stored as a string char cString[lByteLength+1]; cString[lByteLength] =0; memcpy(cString, (char*)&lBuffer[0], lByteLength); //printf(" --> *%s* %s%s\n",cString, &lBuffer[0],&lBuffer[1]); return atoi(cString); } //dcmStrInt() int dcmStrManufacturer (int lByteLength, unsigned char lBuffer[]) {//read float stored as a string if (lByteLength < 2) return kMANUFACTURER_UNKNOWN; char cString[lByteLength+1]; cString[lByteLength] =0; memcpy(cString, (char*)&lBuffer[0], lByteLength); //printf("MANU %s\n",cString); if ((toupper(cString[0])== 'S') && (toupper(cString[1])== 'I')) return kMANUFACTURER_SIEMENS; if ((toupper(cString[0])== 'G') && (toupper(cString[1])== 'E')) return kMANUFACTURER_GE; if ((toupper(cString[0])== 'P') && (toupper(cString[1])== 'H')) return kMANUFACTURER_PHILIPS; return kMANUFACTURER_UNKNOWN; } //dcmStrManufacturer typedef struct __attribute__((packed)) { char name[64]; //null-terminated int32_t vm; char vr[4]; // possibly nul-term string int32_t syngodt;// ?? int32_t nitems;// number of items in CSA int32_t xx;// maybe == 77 or 205 } TCSAtag; //Siemens csa tag structure typedef struct __attribute__((packed)) { int32_t xx1, xx2_Len, xx3_77, xx4; } TCSAitem; //Siemens csa item structure float csaMultiFloat (unsigned char buff[], int nItems, float Floats[], int *ItemsOK) { //warning: lFloats indexed from 1! will fill lFloats[1]..[nFloats] //if lnItems == 1, returns first item, if lnItems > 1 returns index of final successful conversion TCSAitem itemCSA; *ItemsOK = 0; if (nItems < 1) return 0.0f; int lPos = 0; for (int lI = 1; lI <= nItems; lI++) { memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA)); lPos +=sizeof(itemCSA); if (itemCSA.xx2_Len > 0) { char cString[itemCSA.xx2_Len]; memcpy(&cString, &buff[lPos], sizeof(cString)); lPos += ((itemCSA.xx2_Len +3)/4)*4; //printf(" %d item length %d = %s\n",lI, itemCSA.xx2_Len, cString); Floats[lI] = atof(cString); *ItemsOK = lI; //some sequences have store empty items } } //for each item return Floats[1]; } //csaMultiFloat() int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, bool isVerbose) { //see also http://afni.nimh.nih.gov/pub/dist/src/siemens_dicom_csa.c //printf("%c%c%c%c\n",buff[0],buff[1],buff[2],buff[3]); if (lLength < 36) return EXIT_FAILURE; if ((buff[0] != 'S') || (buff[1] != 'V') || (buff[2] != '1') || (buff[3] != '0') ) return EXIT_FAILURE; int lPos = 8; //skip 8 bytes of data, 'SV10' plus 2 32-bit values unused1 and unused2 int lnTag = buff[lPos]+(buff[lPos+1]<<8)+(buff[lPos+2]<<16)+(buff[lPos+3]<<24); if (buff[lPos+4] != 77) return EXIT_FAILURE; lPos += 8; //skip 8 bytes of data, 32-bit lnTag plus 77 00 00 0 TCSAtag tagCSA; TCSAitem itemCSA; int itemsOK; float lFloats[7]; for (int lT = 1; lT <= lnTag; lT++) { memcpy(&tagCSA, &buff[lPos], sizeof(tagCSA)); //read tag lPos +=sizeof(tagCSA); //printf("%d CSA of %s %d\n",lPos, tagCSA.name, tagCSA.nitems); if (tagCSA.nitems > 0) { if (strcmp(tagCSA.name, "NumberOfImagesInMosaic") == 0) CSA->mosaicSlices = round(csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK)); else if (strcmp(tagCSA.name, "B_value") == 0) { CSA->dtiV[0][0] = csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK); CSA->numDti = 1; //triggered by b-value, as B0 images do not have DiffusionGradientDirection tag } else if ((strcmp(tagCSA.name, "DiffusionGradientDirection") == 0) && (tagCSA.nitems > 2)){ CSA->dtiV[0][1] = csaMultiFloat (&buff[lPos], 3,lFloats, &itemsOK); CSA->dtiV[0][2] = lFloats[2]; CSA->dtiV[0][3] = lFloats[3]; if (isVerbose) printf("DiffusionGradientDirection %f %f %f\n",lFloats[1],lFloats[2],lFloats[3]); } else if ((strcmp(tagCSA.name, "SliceNormalVector") == 0) && (tagCSA.nitems > 2)){ CSA->sliceNormV[1] = csaMultiFloat (&buff[lPos], 3,lFloats, &itemsOK); CSA->sliceNormV[2] = lFloats[2]; CSA->sliceNormV[3] = lFloats[3]; if (isVerbose) printf("SliceNormalVector %f %f %f\n",CSA->sliceNormV[1],CSA->sliceNormV[2],CSA->sliceNormV[3]); } else if (strcmp(tagCSA.name, "SliceMeasurementDuration") == 0) CSA->sliceMeasurementDuration = csaMultiFloat (&buff[lPos], 3,lFloats, &itemsOK); else if (strcmp(tagCSA.name, "BandwidthPerPixelPhaseEncode") == 0) CSA->bandwidthPerPixelPhaseEncode = csaMultiFloat (&buff[lPos], 3,lFloats, &itemsOK); else if ((strcmp(tagCSA.name, "MosaicRefAcqTimes") == 0) && (tagCSA.nitems > 3) ){ float sliceTimes[tagCSA.nitems+1]; csaMultiFloat (&buff[lPos], tagCSA.nitems,sliceTimes, &itemsOK); float minTimeIndex, minTimeValue, timeValue1; CSA->multiBandFactor = 1; timeValue1 = sliceTimes[1]; minTimeIndex = 1; minTimeValue= sliceTimes[1]; for (int z = 2; z <= itemsOK; z++) { //find index and value of fastest time if (sliceTimes[z] < minTimeValue) { minTimeValue = sliceTimes[z]; minTimeIndex = z; } if (sliceTimes[z] == timeValue1) CSA->multiBandFactor++; } if (minTimeIndex == 2) CSA->sliceOrder = NIFTI_SLICE_ALT_INC2;// e.g. 3,1,4,2 else if (minTimeIndex == (itemsOK-1)) CSA->sliceOrder = NIFTI_SLICE_ALT_DEC2;// e.g. 4,3,2,1 else if ((minTimeIndex == 1) && (sliceTimes[2] < sliceTimes[3])) CSA->sliceOrder = NIFTI_SLICE_SEQ_INC; else if ((minTimeIndex == 1) && (sliceTimes[2] > sliceTimes[3])) CSA->sliceOrder = NIFTI_SLICE_ALT_INC; else if ((minTimeIndex == itemsOK) && (sliceTimes[itemsOK-1] < sliceTimes[itemsOK])) CSA->sliceOrder = NIFTI_SLICE_SEQ_DEC; else if ((minTimeIndex == itemsOK) && (sliceTimes[itemsOK-1] > sliceTimes[itemsOK-2])) CSA->sliceOrder = NIFTI_SLICE_ALT_DEC; else { /*NSMutableArray *sliceTimesNS = [NSMutableArray arrayWithCapacity:tagCSA.nitems]; for (int z = 1; z <= itemsOK; z++) [sliceTimesNS addObject:[NSNumber numberWithFloat:sliceTimes[z]]]; NSLog(@" Warning: unable to determine slice order for %lu slice mosaic: %@",(unsigned long)[sliceTimesNS count],sliceTimesNS ); */ printf("Warning: unable to determine slice order from CSA tag MosaicRefAcqTimes\n"); } } else if (strcmp(tagCSA.name, "ProtocolSliceNumber") == 0) CSA->protocolSliceNumber1 = round (csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK)); else if (strcmp(tagCSA.name, "PhaseEncodingDirectionPositive") == 0) CSA->phaseEncodingDirectionPositive = round (csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK)); /*if (strcmp(tagCSA.name, "SlicePosition_PCS") == 0) { float f = (csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK)); for (int k = 1; k <= tagCSA.nitems; k++) printf("PCS %d = %f\n",k, lFloats[k]); } else { printf("unused CSA tag %s with %d items\n",tagCSA.name, tagCSA.nitems); }*/ for (int lI = 1; lI <= tagCSA.nitems; lI++) { memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA)); lPos +=sizeof(itemCSA); lPos += ((itemCSA.xx2_Len +3)/4)*4; } } //if at least 1 item }// for lT 1..lnTag return EXIT_SUCCESS; } // readCSAImageHeader() void dcmMultiFloat (int lByteLength, char lBuffer[], int lnFloats, float *lFloats) { //warning: lFloats indexed from 1! will fill lFloats[1]..[nFloats] if ((lnFloats < 1) || (lByteLength < 1)) return; char cString[lByteLength+1]; memcpy(cString, (char*)&lBuffer[0], lByteLength); cString[lByteLength] = 0; //null terminate char *temp=( char *)malloc(lByteLength+1); int f = 0,lStart = 0; bool isOK = false; for (int i = 0; i <= lByteLength; i++) { if ((lBuffer[i] >= '0') && (lBuffer[i] <= '9')) isOK = true; if ((isOK) && ((i == (lByteLength)) || (lBuffer[i] == '/') || (lBuffer[i] == ' ') || (lBuffer[i] == '\\') )){ //x strlcpy(temp,&cString[lStart],i-lStart+1); snprintf(temp,i-lStart+1,"%s",&cString[lStart]); //printf("dcmMultiFloat %s\n",temp); if (f < lnFloats) { f ++; lFloats[f] = atof(temp); //printf("%d == %f\n", f, atof(temp)); } //if f <= nFloats lStart = i+1; } //if isOK } //for i to length free(temp); } //dcmMultiFloat() float dcmStrFloat (int lByteLength, unsigned char lBuffer[]) { //read float stored as a string char cString[lByteLength+1]; memcpy(cString, (char*)&lBuffer[0], lByteLength); cString[lByteLength] = 0; //null terminate return atof(cString); } //dcmStrFloat() /* void dcmStrReport (int lByteLength, char lBuffer[]) {//print value at location char cString[lByteLength+1]; memcpy(cString, (char*)&lBuffer[0], lByteLength); cString[lByteLength] = 0; //null terminate printf("%d dcmStrReport '%s'\n",lByteLength, cString); } //dcmStrReport */ int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h) { //printf("bytes %dx%dx%d %d, %d\n",d.XYZdim[1],d.XYZdim[2],d.XYZdim[3], d.Allocbits_per_pixel, d.samplesPerPixel); for (int i = 0; i < 80; i++) h->descrip[i] = 0; for (int i = 0; i < 24; i++) h->aux_file[i] = 0; for (int i = 0; i < 18; i++) h->db_name[i] = 0; for (int i = 0; i < 10; i++) h->data_type[i] = 0; for (int i = 0; i < 16; i++) h->intent_name[i] = 0; if ((d.bitsAllocated == 8) && (d.samplesPerPixel == 3)) h->datatype = DT_RGB24; else if ((d.bitsAllocated == 8) && (d.samplesPerPixel == 1)) h->datatype = DT_UINT8; else if ((d.bitsAllocated == 16) && (d.samplesPerPixel == 1) && (d.isSigned)) h->datatype = DT_INT16; else if ((d.bitsAllocated == 16) && (d.samplesPerPixel == 1) && (!d.isSigned)) h->datatype = DT_UINT16; else if (d.bitsAllocated == 32) h->datatype = DT_INT32; else { #ifdef myUseCOut std::cout<<"Unsupported DICOM bit-depth " <<d.bitsAllocated << " with " << d.samplesPerPixel << "samples per pixel" <<std::endl; #else printf("Unsupported DICOM bit-depth %d with %d samples per pixel\n",d.bitsAllocated,d.samplesPerPixel); #endif return EXIT_FAILURE; } if ((h->datatype == DT_UINT16) && (d.bitsStored > 0) &&(d.bitsStored < 16)) h->datatype = DT_INT16; // DT_INT16 is more widely supported, same represenation for values 0..32767 for (int i = 0; i < 8; i++) { h->pixdim[i] = 0.0f; h->dim[i] = 0; } h->regular = 114; h->scl_inter = d.intenIntercept; h->scl_slope = d.intenScale; h->cal_max = 0; h->cal_min = 0; h->magic[0]='n'; h->magic[1]='+'; h->magic[2]='1'; h->magic[3]='\0'; h->vox_offset = d.imageStart; h->bitpix = d.bitsAllocated * d.samplesPerPixel; h->pixdim[1] = d.xyzMM[1]; h->pixdim[2] = d.xyzMM[2]; h->pixdim[3] = d.xyzMM[3]; h->pixdim[4] = d.TR/1000; //TR reported in msec, time is in sec h->dim[1] = d.xyzDim[1]; h->dim[2] = d.xyzDim[2]; h->dim[3] = d.xyzDim[3]; h->dim[4] = d.xyzDim[4]; if (h->dim[4] < 2) h->dim[0] = 3; else h->dim[0] = 4; for (int i = 0; i <= 3; i++) { h->srow_x[i] = 0.0f; h->srow_y[i] = 0.0f; h->srow_z[i] = 0.0f; } h->srow_x[0] = -1; h->srow_y[2] = 1; h->srow_z[1] = -1; h->srow_x[3] = (h->dim[1] /2); h->srow_y[3] = -(h->dim[3] /2); h->srow_z[3] = (h->dim[2] /2); h->qform_code = NIFTI_XFORM_UNKNOWN; h->sform_code = NIFTI_XFORM_SCANNER_ANAT; h->toffset = 0; h->intent_code = NIFTI_INTENT_NONE; h->dim_info = 0; //Freq, Phase and Slice all unknown h->xyzt_units = NIFTI_UNITS_UNKNOWN; h->slice_duration = 0; //avoid +inf/-inf, NaN h->intent_p1 = 0; //avoid +inf/-inf, NaN h->intent_p2 = 0; //avoid +inf/-inf, NaN h->intent_p3 = 0; //avoid +inf/-inf, NaN h->pixdim[0] = 1; //QFactor should be 1 or -1 h->sizeof_hdr = 348; //used to signify header does not need to be byte-swapped h->slice_code = d.CSA.sliceOrder; headerDcm2Nii2(d, d, h); return EXIT_SUCCESS; } // headerDcm2Nii() bool isFloatDiff (float a, float b) { return (fabs (a - b) > FLT_EPSILON); } //isFloatDiff() ivec3 setVec3i(int x, int y, int z) { ivec3 v = {x, y, z}; return v; } //setVec3i() mat33 nifti_mat33_reorder_cols( mat33 m, ivec3 v ) { // matlab equivalent ret = m(:, v); where v is 1,2,3 [INDEXED FROM ONE!!!!] mat33 ret; for (int r=0; r<3; r++) { for(int c=0; c<3; c++) ret.m[r][c] = m.m[r][v.v[c]-1]; } return ret; } //nifti_mat33_reorder_cols() /*bool isExt (char *file_name, const char* ext) { char *p_extension; if((p_extension = strrchr(file_name,'.')) != NULL ) if(strcmp(p_extension,ext) == 0) return true; return false; }*/ void changeExt (char *file_name, const char* ext) { char *p_extension; p_extension = strrchr(file_name, '.'); if (p_extension) { strcpy(++p_extension, ext); } } //changeExt() struct TDICOMdata nii_readParRec (char * parname) { struct TDICOMdata d = clear_dicom_data(); FILE *fp = fopen(parname, "r"); if (fp == NULL) return d; #define LINESZ 2048 #define kSlice 0 #define kEcho 1 #define kDyn 2 #define kCardiac 3 #define kImageType 4 #define kSequence 5 #define kIndex 6 #define kBitsPerVoxel 7 #define kXdim 9 #define kYdim 10 #define kRI 11 #define kRS 12 #define kSS 13 #define kAngulationAPs 16 //In V4, offcentre and Angulation labeled as y z x, but actually x y z! #define kAngulationFHs 17 #define kAngulationRLs 18 #define kPositionAP 19 #define kPositionFH 20 #define kPositionRL 21 #define kThickmm 22 #define kGapmm 23 #define kSliceOrients 25 #define kXmm 28 #define kYmm 29 #define kTEcho 30 #define kDynTime 31 #define kGradientNumber 42 #define kbval 33 #define kv1 47 #define kv2 45 #define kv3 46 #define kASL 48 char buff[LINESZ]; bool ADCwarning = false; for (int n=0; kMaxDTIv < 4; n++) d.CSA.dtiV[n][0] = -1; //set to impossible value to detect re-usage by ADC map int parVers = 0; int nCols = 26; int slice = 0; //int prevSliceIndex = 0; //index of prior slice: detect if images are not in order const int kMaxCols = 49; float *cols = (float *)malloc(sizeof(float) * kMaxCols); char *p = fgets (buff, LINESZ, fp); bool isIntenScaleVaries = false; bool isIndexSequential = true; while (p) { if (strlen(buff) < 1) continue; if (buff[0] == '#') { //comment char Comment[7][50]; sscanf(buff, "# %s %s\n", Comment[0], Comment[1]); if (strcmp(Comment[1], "TRYOUT") == 0) { sscanf(buff, "# %s %s %s %s %s %s V%s\n", Comment[0], Comment[1], Comment[2], Comment[3] ,Comment[4], Comment[5],Comment[6]); parVers = (atof(Comment[6])*10); //4.2 = 42 etc if (parVers < 40) { #ifdef myUseCOut std::cout<<"This software is unable to convert ancient PAR files: please use legacy dcm2nii" <<std::endl; #else printf("This software is unable to convert ancient PAR files: please use legacy dcm2nii\n"); #endif return d; //nCols = 26; //e.g. PAR 3.0 has 26 relevant columns } else if (parVers < 41) nCols = 32; //e.g PAR 4.0 else if (parVers < 42) nCols = 47; //e.g. PAR 4.1 else nCols = kMaxCols; //e.g. PAR 4.2 } p = fgets (buff, LINESZ, fp);//get next line continue; } //process '#' comment if (buff[0] == '.') { //tag char Comment[8][50]; sscanf(buff, ". %s %s %s %s %s %s %s %s\n", Comment[0], Comment[1],Comment[2], Comment[3], Comment[4], Comment[5], Comment[6], Comment[7]); if ((strcmp(Comment[0], "Acquisition") == 0) && (strcmp(Comment[1], "nr") == 0)) { d.acquNum = atoi( Comment[3]); d.seriesNum = d.acquNum; } if ((strcmp(Comment[0], "Repetition") == 0) && (strcmp(Comment[1], "time") == 0)) d.TR = atof(Comment[4]); if ((strcmp(Comment[0], "Patient") == 0) && (strcmp(Comment[1], "name") == 0)) { strcpy(d.patientName, Comment[3]); strcat(d.patientName, Comment[4]); strcat(d.patientName, Comment[5]); strcat(d.patientName, Comment[6]); strcat(d.patientName, Comment[7]); //printf("%s\n",d.patientName); } if ((strcmp(Comment[0], "Protocol") == 0) && (strcmp(Comment[1], "name") == 0)) { strcpy(d.protocolName, Comment[3]); strcat(d.protocolName, Comment[4]); strcat(d.protocolName, Comment[5]); strcat(d.protocolName, Comment[6]); strcat(d.protocolName, Comment[7]); //printf("%s\n",d.protocolName); } if ((strcmp(Comment[0], "Examination") == 0) && (strcmp(Comment[1], "date/time") == 0)) { strcpy(d.studyDate, Comment[3]); strcpy(d.studyTime, Comment[5]); //to do convert to traditional DICOM style date time } if ((strcmp(Comment[0], "Off") == 0) && (strcmp(Comment[1], "Centre") == 0)) { //Off Centre midslice(ap,fh,rl) [mm] d.stackOffcentre[2] = atof(Comment[5]); d.stackOffcentre[3] = atof(Comment[6]); d.stackOffcentre[1] = atof(Comment[7]); } if ((strcmp(Comment[0], "Patient") == 0) && (strcmp(Comment[1], "position") == 0)) { //Off Centre midslice(ap,fh,rl) [mm] d.patientOrient[0] = toupper(Comment[3][0]); d.patientOrient[1] = toupper(Comment[4][0]); d.patientOrient[2] = toupper(Comment[5][0]); d.patientOrient[3] = 0; } if ((strcmp(Comment[0], "Max.") == 0) && (strcmp(Comment[3], "slices/locations") == 0)) { d.xyzDim[3] = atoi(Comment[5]); } p = fgets (buff, LINESZ, fp);//get next line continue; } //process '.' tag if (strlen(buff) < 24) { //empty line p = fgets (buff, LINESZ, fp);//get next line continue; } if (parVers < 20) { #ifdef myUseCOut std::cout<<"Error: PAR files should have 'CLINICAL TRYOUT' line with a version from 2.0-4.2: " << parname<<std::endl; #else printf("Error: PAR files should have 'CLINICAL TRYOUT' line with a version from 2.0-4.2: %s\n", parname); #endif return d; } for (int i = 0; i < nCols; i++) cols[i] = strtof(p, &p); // p+1 skip comma, read a float if ((cols[kIndex]) != slice) isIndexSequential = false; //slices 0,1,2.. should have indices 0,1,2,3... slice ++; if (slice == 1) { //for (int i = 0; i < nCols; i++) // cols1[i] = cols[i]; //store first slice to see if dimensions or intensity scale varies between slices d.xyzDim[1] = cols[kXdim]; d.xyzDim[2] = cols[kYdim]; d.xyzMM[1] = cols[kXmm]; d.xyzMM[2] = cols[kYmm]; d.xyzMM[3] = cols[kThickmm] + cols[kGapmm]; d.patientPosition[1] = cols[kPositionRL]; d.patientPosition[2] = cols[kPositionAP]; d.patientPosition[3] = cols[kPositionFH]; d.angulation[1] = cols[kAngulationRLs]; d.angulation[2] = cols[kAngulationAPs]; d.angulation[3] = cols[kAngulationFHs]; d.sliceOrient = cols[kSliceOrients]; d.TE = cols[kTEcho]; d.bitsAllocated = cols[kBitsPerVoxel]; d.bitsStored = cols[kBitsPerVoxel]; d.intenIntercept = cols[kRI]; d.intenScale = cols[kRS]; } else { if ((d.xyzDim[1] != cols[kXdim]) || (d.xyzDim[2] != cols[kYdim]) || (d.bitsAllocated != cols[kBitsPerVoxel]) ) { #ifdef myUseCOut std::cout<<"Error: slice dimensions or bit depth varies "<< parname <<std::endl; #else printf("Error: slice dimensions or bit depth varies %s\n", parname); #endif return d; } if ((d.patientPositionSequentialRepeats == 0) && ((!isSameFloat(d.patientPosition[1],cols[kPositionRL])) || (!isSameFloat(d.patientPosition[2],cols[kPositionAP])) || (!isSameFloat(d.patientPosition[3],cols[kPositionFH])) ) )//this is the first slice with different position d.patientPositionSequentialRepeats = slice-1; if ((d.intenScale != cols[kRS]) || (d.intenIntercept != cols[kRI])) isIntenScaleVaries = true; } if (cols[kImageType] == 0) d.isHasMagnitude = true; if (cols[kImageType] != 0) d.isHasPhase = true; if (cols[kGradientNumber] > 0) { int dir = cols[kGradientNumber]; if ((cols[kbval] > 0.0) && (cols[kv1] == 0.0) && (cols[kv1] == 0.0) && (cols[kv1] == 0.0) ) { if (d.CSA.dtiV[dir-1][0] >= 0) dir = dir + 1; //Philips often stores an ADC map along with B0 and weighted images, unfortunately they give it the same kGradientNumber as the B0! (seen in PAR V4.2) //the logic here is that IF the gradient was previously used we increment the gradient number. This should provide compatibility when Philips fixes this bug //it seems like the ADC is always saved as the final volume, so this solution SHOULD be foolproof. ADCwarning = true; } if (dir > d.CSA.numDti) d.CSA.numDti = dir; if (cols[dir] <= kMaxDTIv) { d.CSA.dtiV[dir-1][0] = cols[kbval]; d.CSA.dtiV[dir-1][1] = cols[kv1]; d.CSA.dtiV[dir-1][2] = cols[kv2]; d.CSA.dtiV[dir-1][3] = cols[kv3]; } //save DTI direction } //if DTI directions //printf("%f %f %lu\n",cols[9],cols[kGradientNumber], strlen(buff)) p = fgets (buff, LINESZ, fp);//get next line } free (cols); fclose (fp); d.isValid = true; d.isSigned = true; d.xyzDim[4] = slice/d.xyzDim[3]; d.locationsInAcquisition = d.xyzDim[3]; #ifdef myUseCOut if (isIntenScaleVaries) std::cout<<"Warning: intensity slope/intercept varies between slices! [solution: user dcm2nii instead]" <<std::endl; if (!isIndexSequential) std::cout<<"Warning: slice order not saved to disk sequentially! [solution: user dcm2nii instead]" <<std::endl; printf("Warning: slice order not saved to disk sequentially! [solution: user dcm2nii instead]\n"); std::cout<<"Done reading PAR header version "<< (float)parVers/10<<" with "<< d.CSA.numDti << "DTI directions"<<std::endl; #else if (ADCwarning) printf("Warning: PAR/REC dataset includes an ADC map that could disrupt analysis. Please remove volume and ensure vectors are reported correctly\n"); if (isIntenScaleVaries) printf("Warning: intensity slope/intercept varies between slices! [solution: user dcm2nii instead]\n"); if (!isIndexSequential) printf("Warning: slice order not saved to disk sequentially! [solution: user dcm2nii instead]\n"); printf("Done reading PAR header version %.1f, with %d DTI directions\n", (float)parVers/10, d.CSA.numDti); #endif //see Xiangrui Li 's dicm2nii (also BSD license) // http://www.mathworks.com/matlabcentral/fileexchange/42997-dicom-to-nifti-converter // Rotation order and signs are figured out by try and err, not 100% sure float d2r = M_PI/180; vec3 ca = setVec3(cos(d.angulation[1]*d2r),cos(d.angulation[2]*d2r),cos(d.angulation[3]*d2r)); vec3 sa = setVec3(sin(d.angulation[1]*d2r),sin(d.angulation[2]*d2r),sin(d.angulation[3]*d2r)); mat33 rx,ry,rz; LOAD_MAT33(rx,1.0f, 0.0f, 0.0f, 0.0f, ca.v[0], -sa.v[0], 0.0f, sa.v[0], ca.v[0]); LOAD_MAT33(ry, ca.v[1], 0.0f, sa.v[1], 0.0f, 1.0f, 0.0f, -sa.v[1], 0.0f, ca.v[1]); LOAD_MAT33(rz, ca.v[2], -sa.v[2], 0.0f, sa.v[2], ca.v[2], 0.0f, 0.0f, 0.0f, 1.0f); mat33 R = nifti_mat33_mul( rx,ry ); R = nifti_mat33_mul( R,rz); ivec3 ixyz = setVec3i(1,2,3); if (d.sliceOrient == kSliceOrientSag) { ixyz = setVec3i(2,3,1); for (int r = 0; r < 3; r++) for (int c = 0; c < 3; c++) if (c != 1) R.m[r][c] = -R.m[r][c]; //invert first and final columns }else if (d.sliceOrient == kSliceOrientCor) { ixyz = setVec3i(1,3,2); for (int r = 0; r < 3; r++) R.m[r][2] = -R.m[r][2]; //invert rows of final column } R = nifti_mat33_reorder_cols(R,ixyz); //dicom rotation matrix d.orient[1] = R.m[0][0]; d.orient[2] = R.m[1][0]; d.orient[3] = R.m[2][0]; d.orient[4] = R.m[0][1]; d.orient[5] = R.m[1][1]; d.orient[6] = R.m[2][1]; mat33 diag; LOAD_MAT33(diag, d.xyzMM[1],0.0f,0.0f, 0.0f,d.xyzMM[2],0.0f, 0.0f,0.0f, d.xyzMM[3]); R= nifti_mat33_mul( R, diag ); mat44 R44; LOAD_MAT44(R44, R.m[0][0],R.m[0][1],R.m[0][2],d.stackOffcentre[1], R.m[1][0],R.m[1][1],R.m[1][2],d.stackOffcentre[2], R.m[2][0],R.m[2][1],R.m[2][2],d.stackOffcentre[3]); vec3 x; if (parVers > 40) //guess x = setVec3(((float)d.xyzDim[1]-1)/2,((float)d.xyzDim[2]-1)/2,((float)d.xyzDim[3]-1)/2); else x = setVec3((float)d.xyzDim[1]/2,(float)d.xyzDim[2]/2,((float)d.xyzDim[3]-1)/2); mat44 eye; LOAD_MAT44(eye, 1.0f,0.0f,0.0f,x.v[0], 0.0f,1.0f,0.0f,x.v[1], 0.0f,0.0f,1.0f,x.v[2]); eye= nifti_mat44_inverse( eye ); //we wish to compute R/eye, so compute invEye and calculate R*invEye R44= nifti_mat44_mul( R44 , eye ); vec4 y; y.v[0]=0.0f; y.v[1]=0.0f; y.v[2]=d.xyzDim[3]-1; y.v[3]=1.0f; y= nifti_vect44mat44_mul(y, R44 ); int iOri = 2; //for axial, slices are 3rd dimenson (indexed from 0) (k) if (d.sliceOrient == kSliceOrientSag) iOri = 0; //for sagittal, slices are 1st dimension (i) if (d.sliceOrient == kSliceOrientCor) iOri = 1; //for coronal, slices are 2nd dimension (j) if (( (y.v[iOri]-R44.m[iOri][3])>0 ) == ( (y.v[iOri]-d.stackOffcentre[iOri+1])>0 ) ) { d.patientPosition[1] = R44.m[0][3]; d.patientPosition[2] = R44.m[1][3]; d.patientPosition[3] = R44.m[2][3]; d.patientPositionLast[1] = y.v[0]; d.patientPositionLast[2] = y.v[1]; d.patientPositionLast[3] = y.v[2]; }else { //d.patientPosition d.patientPosition[1] = y.v[0]; d.patientPosition[2] = y.v[1]; d.patientPosition[3] = y.v[2]; d.patientPositionLast[1] = R44.m[0][3]; d.patientPositionLast[2] = R44.m[1][3]; d.patientPositionLast[3] = R44.m[2][3]; } //finish up changeExt (parname, "REC"); d.locationsInAcquisition = d.xyzDim[3]; d.manufacturer = kMANUFACTURER_PHILIPS; d.imageStart = 0; return d; } //nii_readParRec() size_t nii_ImgBytes(struct nifti_1_header hdr) { //unsigned long imgsz = nii_ImgBytes(hdr); size_t imgsz = hdr.bitpix/8; for (int i = 1; i < 8; i++) if (hdr.dim[i] > 1) imgsz = imgsz * hdr.dim[i]; return imgsz; } //nii_ImgBytes() unsigned char * nii_demosaic(unsigned char* inImg, struct nifti_1_header *hdr, int nMosaicSlices, int ProtocolSliceNumber1) { //demosaic http://nipy.org/nibabel/dicom/dicom_mosaic.html if (nMosaicSlices < 2) return inImg; //Byte inImg[ [img length] ]; //[img getBytes:&inImg length:[img length]]; int nRowCol = ceil(sqrt(nMosaicSlices)); int colBytes = hdr->dim[1]/nRowCol * hdr->bitpix/8; int lineBytes = hdr->dim[1] * hdr->bitpix/8; int rowBytes = hdr->dim[1] * hdr->dim[2]/nRowCol * hdr->bitpix/8; int col = 0; int row = 0; int lOutPos = 0; hdr->dim[1] = hdr->dim[1]/nRowCol; hdr->dim[2] = hdr->dim[2]/nRowCol; hdr->dim[3] = nMosaicSlices; size_t imgsz = nii_ImgBytes(*hdr); unsigned char *outImg = (unsigned char *)malloc(imgsz); for (int m=1; m <= nMosaicSlices; m++) { int lPos = (row * rowBytes) + (col * colBytes); for (int y = 0; y < hdr->dim[2]; y++) { memcpy(&outImg[lOutPos], &inImg[lPos], colBytes); // dest, src, bytes lPos += lineBytes; lOutPos +=colBytes; } col ++; if (col >= nRowCol) { row ++; col = 0; } //start new column } //for m = each mosaic slice if (ProtocolSliceNumber1 > 1) { #ifdef myUseCOut std::cout<<"WARNING: CSA 'ProtocolSliceNumber' SUGGESTS REVERSED SLICE ORDER: SPATIAL AND DTI COORDINATES UNTESTED" <<std::endl; #else printf("WARNING: CSA 'ProtocolSliceNumber' SUGGESTS REVERSED SLICE ORDER: SPATIAL AND DTI COORDINATES UNTESTED\n"); #endif } /*if ((ProtocolSliceNumber1 > 1) && (hdr->dim[3] > 1)) { //exceptionally rare: reverse order of slices - now handled in matrix... int sliceBytes = hdr->dim[1] * hdr->dim[2] * hdr->bitpix/8; memcpy(&inImg[0], &outImg[0],sliceBytes*hdr->dim[3]); //copy data with reversed order dest, src, bytes int lOutPos = sliceBytes * (hdr->dim[3]-1); int lPos = 0; for (int m=0; m < nMosaicSlices; m++) { memcpy( &outImg[lOutPos], &inImg[lPos], sliceBytes); lPos += sliceBytes; lOutPos -= sliceBytes; } }*/ free(inImg); return outImg; //return [NSData dataWithBytes:&outImg length:outlen]; } //nii_demosaic() unsigned char * nii_flipImgY(unsigned char* bImg, struct nifti_1_header *hdr){ //DICOM row order opposite from NIfTI int dim3to7 = 1; for (int i = 3; i < 8; i++) if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i]; size_t lineBytes = hdr->dim[1] * hdr->bitpix/8; if ((hdr->datatype == DT_RGB24) && (hdr->bitpix == 24)) { lineBytes = hdr->dim[1]; dim3to7 = dim3to7 * 3; } //rgb data saved planar (RRR..RGGGG..GBBB..B //printf("smoking gun %d %d\n",hdr->dim[3],hdr->dim[4]); return bImg; unsigned char line[lineBytes]; size_t sliceBytes = hdr->dim[2] * lineBytes; int halfY = hdr->dim[2] / 2; //note truncated toward zero, so halfY=2 regardless of 4 or 5 columns for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice size_t slBottom = (size_t)sl*sliceBytes; size_t slTop = (((size_t)sl+1)*sliceBytes)-lineBytes; for (int y = 0; y < halfY; y++) { //swap order of lines memcpy(&line, &bImg[slBottom], lineBytes); memcpy(&bImg[slBottom], &bImg[slTop], lineBytes); memcpy(&bImg[slTop], &line, lineBytes); slTop -= lineBytes; slBottom += lineBytes; } //for y } //for each slice return bImg; } //nii_flipImgY() unsigned char * nii_flipImgZ(unsigned char* bImg, struct nifti_1_header *hdr){ //DICOM row order opposite from NIfTI int halfZ = hdr->dim[3] / 2; //note truncated toward zero, so halfY=2 regardless of 4 or 5 columns if (halfZ < 1) return bImg; int dim4to7 = 1; for (int i = 4; i < 8; i++) if (hdr->dim[i] > 1) dim4to7 = dim4to7 * hdr->dim[i]; int sliceBytes = hdr->dim[1] * hdr->dim[2] * hdr->bitpix/8; long long volBytes = sliceBytes * hdr->dim[3]; unsigned char slice[sliceBytes]; for (int vol = 0; vol < dim4to7; vol++) { //for each 2D slice long long slBottom = vol*volBytes; long long slTop = ((vol+1)*volBytes)-sliceBytes; for (int z = 0; z < halfZ; z++) { //swap order of lines memcpy(&slice, &bImg[slBottom], sliceBytes); memcpy(&bImg[slBottom], &bImg[slTop], sliceBytes); memcpy(&bImg[slTop], &slice, sliceBytes); slTop -= sliceBytes; slBottom += sliceBytes; } //for Z } //for each volume return bImg; } //nii_flipImgZ() unsigned char * nii_flipZ(unsigned char* bImg, struct nifti_1_header *h){ //flip slice order if (h->dim[3] < 2) return bImg; mat33 s; mat44 Q44; LOAD_MAT33(s,h->srow_x[0],h->srow_x[1],h->srow_x[2], h->srow_y[0],h->srow_y[1],h->srow_y[2], h->srow_z[0],h->srow_z[1],h->srow_z[2]); LOAD_MAT44(Q44,h->srow_x[0],h->srow_x[1],h->srow_x[2],h->srow_x[3], h->srow_y[0],h->srow_y[1],h->srow_y[2],h->srow_y[3], h->srow_z[0],h->srow_z[1],h->srow_z[2],h->srow_z[3]); vec4 v= setVec4(0,0,h->dim[3]-1); v = nifti_vect44mat44_mul(v, Q44); //after flip this voxel will be the origin mat33 mFlipZ; LOAD_MAT33(mFlipZ,1.0f, 0.0f, 0.0f, 0.0f,1.0f,0.0f, 0.0f,0.0f,-1.0f); s= nifti_mat33_mul( s , mFlipZ ); LOAD_MAT44(Q44, s.m[0][0],s.m[0][1],s.m[0][2],v.v[0], s.m[1][0],s.m[1][1],s.m[1][2],v.v[1], s.m[2][0],s.m[2][1],s.m[2][2],v.v[2]); //printf(" ----------> %f %f %f\n",v.v[0],v.v[1],v.v[2]); setQSForm(h,Q44); //printf("nii_flipImgY dims %dx%dx%d %d \n",h->dim[1],h->dim[2], dim3to7,h->bitpix/8); return nii_flipImgZ(bImg,h); }//nii_flipZ() unsigned char * nii_flipY(unsigned char* bImg, struct nifti_1_header *h){ mat33 s; mat44 Q44; LOAD_MAT33(s,h->srow_x[0],h->srow_x[1],h->srow_x[2], h->srow_y[0],h->srow_y[1],h->srow_y[2], h->srow_z[0],h->srow_z[1],h->srow_z[2]); LOAD_MAT44(Q44,h->srow_x[0],h->srow_x[1],h->srow_x[2],h->srow_x[3], h->srow_y[0],h->srow_y[1],h->srow_y[2],h->srow_y[3], h->srow_z[0],h->srow_z[1],h->srow_z[2],h->srow_z[3]); vec4 v= setVec4(0,h->dim[2]-1,0); v = nifti_vect44mat44_mul(v, Q44); //after flip this voxel will be the origin mat33 mFlipY; LOAD_MAT33(mFlipY,1.0f, 0.0f, 0.0f, 0.0f,-1.0f,0.0f, 0.0f,0.0f,1.0f); s= nifti_mat33_mul( s , mFlipY ); LOAD_MAT44(Q44, s.m[0][0],s.m[0][1],s.m[0][2],v.v[0], s.m[1][0],s.m[1][1],s.m[1][2],v.v[1], s.m[2][0],s.m[2][1],s.m[2][2],v.v[2]); setQSForm(h,Q44); //printf("nii_flipImgY dims %dx%dx%d %d \n",h->dim[1],h->dim[2], dim3to7,h->bitpix/8); return nii_flipImgY(bImg,h); }//nii_flipY() unsigned char * nii_loadImgCore(char* imgname, struct nifti_1_header hdr) { size_t imgsz = nii_ImgBytes(hdr); FILE *file = fopen(imgname , "rb"); if (!file) { printf("Error: unable to open %s\n", imgname); return NULL; } fseek(file, 0, SEEK_END); long long fileLen=ftell(file); if (fileLen < (imgsz+hdr.vox_offset)) return NULL; fseek(file, hdr.vox_offset, SEEK_SET); unsigned char *bImg = (unsigned char *)malloc(imgsz); fread(bImg, imgsz, 1, file); fclose(file); return bImg; } //nii_loadImg() unsigned char * nii_rgb2Planar(unsigned char* bImg, struct nifti_1_header *hdr, int isPlanar) { //DICOM data saved in triples RGBRGBRGB, NIfTI RGB saved in planes RRR..RGGG..GBBBB..B if (hdr->datatype != DT_RGB24) return bImg; if (isPlanar == 1) return bImg;//return nii_bgr2rgb(bImg,hdr); int dim3to7 = 1; for (int i = 3; i < 8; i++) if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i]; int sliceBytes24 = hdr->dim[1]*hdr->dim[2] * hdr->bitpix/8; int sliceBytes8 = hdr->dim[1]*hdr->dim[2]; unsigned char slice24[ sliceBytes24 ]; int sliceOffsetR = 0; for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice memcpy(&slice24, &bImg[sliceOffsetR], sliceBytes24); int sliceOffsetG = sliceOffsetR + sliceBytes8; int sliceOffsetB = sliceOffsetR + 2*sliceBytes8; int i = 0; int j = 0; for (int rgb = 0; rgb < sliceBytes8; rgb++) { bImg[sliceOffsetR+j] =slice24[i]; i++; bImg[sliceOffsetG+j] =slice24[i]; i++; bImg[sliceOffsetB+j] =slice24[i]; i++; j++; } sliceOffsetR += sliceBytes24; } //for each slice return bImg; } //nii_rgb2Planar() unsigned char * nii_iVaries(unsigned char *img, struct nifti_1_header *hdr){ //each DICOM image can have its own intesity scaling, whereas NIfTI requires the same scaling for all images in a file //WARNING: do this BEFORE nii_check16bitUnsigned!!!! //if (hdr->datatype != DT_INT16) return img; int dim3to7 = 1; for (int i = 3; i < 8; i++) if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i]; int nVox = hdr->dim[1]*hdr->dim[2]* dim3to7; if (nVox < 1) return img; float * img32=(float*)malloc(nVox*sizeof(float)); if (hdr->datatype == DT_UINT8) { uint8_t * img8i = (uint8_t*) img; for (int i=0; i < nVox; i++) img32[i] = img8i[i]; } else if (hdr->datatype == DT_UINT16) { uint16_t * img16ui = (uint16_t*) img; for (int i=0; i < nVox; i++) img32[i] = img16ui[i]; } else if (hdr->datatype == DT_INT16) { int16_t * img16i = (int16_t*) img; for (int i=0; i < nVox; i++) img32[i] = img16i[i]; } else if (hdr->datatype == DT_INT32) { int32_t * img32i = (int32_t*) img; for (int i=0; i < nVox; i++) img32[i] = img32i[i]; } free (img); //release previous image for (int i=0; i < nVox; i++) img32[i] = (img32[i]* hdr->scl_slope)+hdr->scl_inter; hdr->scl_slope = 1; hdr->scl_inter = 0; hdr->datatype = DT_FLOAT; hdr->bitpix = 32; return (unsigned char*) img32; } //nii_iVaries() unsigned char * nii_XYTZ_XYZT(unsigned char* bImg, struct nifti_1_header *hdr, int seqRepeats) { //Philips can save time as 3rd dimensions, NIFTI requires time is 4th dimension int dim4to7 = 1; for (int i = 4; i < 8; i++) if (hdr->dim[i] > 1) dim4to7 = dim4to7 * hdr->dim[i]; if ((hdr->dim[3] < 2) || (dim4to7 < 2)) return bImg; #ifdef myUseCOut std::cout<<"Converting XYTZ to XYZT with "<<hdr->dim[3]<<" slices (Z) and "<< dim4to7<< "volumes" <<std::endl; if ((dim4to7 % seqRepeats) != 0) { std::cout<<"Error: patient position repeats "<<seqRepeats<<" times, but this does not evenly divide number of volumes: "<< dim4to7 <<std::endl; seqRepeats = 1; } #else printf("Converting XYTZ to XYZT with %d slices (Z) and %d volumes (T).\n",hdr->dim[3], dim4to7); if ((dim4to7 % seqRepeats) != 0) { printf("Error: patient position repeats %d times, but this does not evenly divide number of volumes (%d)", seqRepeats,dim4to7); seqRepeats = 1; } #endif uint64_t typeRepeats = dim4to7 / seqRepeats; uint64_t sliceBytes = hdr->dim[1]*hdr->dim[2]*hdr->bitpix/8; uint64_t seqBytes = sliceBytes * seqRepeats; uint64_t typeBytes = seqBytes * hdr->dim[3]; uint64_t imgSz = nii_ImgBytes(*hdr); //this uses a lot of RAM, someday this could be done in place... unsigned char *outImg = (unsigned char *)malloc(imgSz); //memcpy(&tempImg[0], &bImg[0], imgSz); uint64_t origPos = 0; uint64_t Pos = 0; // for (int t = 0; t < typeRepeats; t++) { //for each volume for (int s = 0; s < seqRepeats; s++) { origPos = (t*typeBytes) +s*sliceBytes; for (int z = 0; z < hdr->dim[3]; z++) { //for each slice memcpy( &outImg[Pos],&bImg[origPos], sliceBytes); Pos += sliceBytes; origPos += seqBytes; } }//for s } free(bImg); return outImg; } //nii_ImgBytes() unsigned char * nii_byteswap(unsigned char *img, struct nifti_1_header *hdr){ if (hdr->bitpix < 9) return img; uint64_t nvox = nii_ImgBytes(*hdr) / (hdr->bitpix/8); void *ar = (void*) img; if (hdr->bitpix == 16) nifti_swap_2bytes( nvox , ar ); if (hdr->bitpix == 32) nifti_swap_4bytes( nvox , ar ); if (hdr->bitpix == 64) nifti_swap_8bytes( nvox , ar ); return img; } //nii_byteswap() unsigned char * nii_loadImgX(char* imgname, struct nifti_1_header *hdr, struct TDICOMdata dcm, bool iVaries) { //provided with a filename (imgname) and DICOM header (dcm), creates NIfTI header (hdr) and img if (headerDcm2Nii(dcm, hdr) == EXIT_FAILURE) return NULL; unsigned char * img = nii_loadImgCore(imgname, *hdr); if (img == NULL) return img; #ifdef __BIG_ENDIAN__ if ((dcm.isLittleEndian) && (hdr->bitpix > 8)) img = nii_byteswap(img, hdr); #else if ((!dcm.isLittleEndian) && (hdr->bitpix > 8)) img = nii_byteswap(img, hdr); #endif if (hdr->datatype ==DT_RGB24) img = nii_rgb2Planar(img, hdr, dcm.isPlanarRGB);//do this BEFORE Y-Flip, or RGB order can be flipped if (dcm.CSA.mosaicSlices > 1) { img = nii_demosaic(img, hdr, dcm.CSA.mosaicSlices, dcm.CSA.protocolSliceNumber1); /* we will do this in nii_dicom_batch #ifdef obsolete_mosaic_flip img = nii_flipImgY(img, hdr); #endif*/ } if (iVaries) img = nii_iVaries(img, hdr); int nAcq = dcm.locationsInAcquisition; if ((nAcq > 1) && (hdr->dim[0] < 4) && ((hdr->dim[3]%nAcq)==0) && (hdr->dim[3]>nAcq) ) { hdr->dim[4] = hdr->dim[3]/nAcq; hdr->dim[3] = nAcq; hdr->dim[0] = 4; } if ((hdr->dim[0] > 3) && (dcm.patientPositionSequentialRepeats > 1)) //swizzle 3rd and 4th dimension (Philips stores time as 3rd dimension) img = nii_XYTZ_XYZT(img, hdr,dcm.patientPositionSequentialRepeats ); headerDcm2NiiSForm(dcm,dcm, hdr); return img; } //nii_loadImgX() struct TDICOMdata readDICOMv(char * fname, bool isVerbose) { struct TDICOMdata d = clear_dicom_data(); strcpy(d.protocolName, ""); //fill dummy with empty space so we can detect kProtocolNameGE FILE *file = fopen(fname, "rb"); if (!file) { #ifdef myUseCOut std::cout<<"Unable to open file "<< fname <<std::endl; #else printf( "Unable to open file %s\n", fname); #endif return d; } fseek(file, 0, SEEK_END); long long fileLen=ftell(file); //Get file length if (fileLen < 256) { #ifdef myUseCOut std::cout<<"File too small to be a DICOM image "<< fname <<std::endl; #else printf( "File too small to be a DICOM image %s\n", fname); #endif return d; } fseek(file, 0, SEEK_SET); //Allocate memory unsigned char *buffer=(unsigned char *)malloc(fileLen+1); if (!buffer) { printf( "Memory error!"); fclose(file); return d; } //Read file contents into buffer fread(buffer, fileLen, 1, file); fclose(file); if ((buffer[128] != 'D') || (buffer[129] != 'I') || (buffer[130] != 'C') || (buffer[131] != 'M')) { free (buffer); return d; } //DEFINE DICOM TAGS #define kUnused 0x0001+(0x0001 << 16 ) #define kStart 0x0002+(0x0000 << 16 ) #define kTransferSyntax 0x0002+(0x0010 << 16) #define kStudyDate 0x0008+(0x0020 << 16 ) #define kStudyTime 0x0008+(0x0030 << 16 ) #define kAcquisitionTime 0x0008+(0x0032 << 16 ) #define kManufacturer 0x0008+(0x0070 << 16 ) #define kProtocolNameGE 0x0008+(0x103E << 16 ) #define kComplexImageComponent 0x0008+(0x9208 << 16 )//'0008' '9208' 'CS' 'ComplexImageComponent' #define kPatientName 0x0010+(0x0010 << 16 ) #define kPatientID 0x0010+(0x0020 << 16 ) #define kMRAcquisitionType 0x0018+(0x0023 << 16) #define kZThick 0x0018+(0x0050 << 16 ) #define kTR 0x0018+(0x0080 << 16 ) #define kTE 0x0018+(0x0081 << 16 ) #define kEchoNum 0x0018+(0x0086 << 16 ) //IS #define kZSpacing 0x0018+(0x0088 << 16 ) //'DS' 'SpacingBetweenSlices' #define kProtocolName 0x0018+(0x1030<< 16 ) #define kInPlanePhaseEncodingDirection 0x0018+(0x1312<< 16 ) //CS #define kPatientOrient 0x0018+(0x5100<< 16 ) //0018,5100. patient orientation - 'HFS' //#define kDiffusionBFactorSiemens 0x0019+(0x100C<< 16 ) // 0019;000C;SIEMENS MR HEADER ;B_value ;1;IS;1 #define kLastScanLoc 0x0019+(0x101B<< 16 ) #define kDiffusionDirectionGEX 0x0019+(0x10BB<< 16 ) //DS #define kDiffusionDirectionGEY 0x0019+(0x10BC<< 16 ) //DS #define kDiffusionDirectionGEZ 0x0019+(0x10BD<< 16 ) //DS #define kPatientPosition 0x0020+(0x0032 << 16 ) #define kSeriesNum 0x0020+(0x0011 << 16 ) #define kAcquNum 0x0020+(0x0012 << 16 ) #define kImageNum 0x0020+(0x0013 << 16 ) #define kOrientation 0x0020+(0x0037 << 16 ) #define kImageComments 0x0020+(0x4000<< 16 )// '0020' '4000' 'LT' 'ImageComments' #define kLocationsInAcquisitionGE 0x0021+(0x104F<< 16 )// 'SS' 'LocationsInAcquisitionGE' #define kSamplesPerPixel 0x0028+(0x0002 << 16 ) #define kPlanarRGB 0x0028+(0x0006 << 16 ) #define kDim3 0x0028+(0x0008 << 16 ) //number of frames - for Philips this is Dim3*Dim4 #define kDim2 0x0028+(0x0010 << 16 ) #define kDim1 0x0028+(0x0011 << 16 ) #define kXYSpacing 0x0028+(0x0030 << 16 ) //'0028' '0030' 'DS' 'PixelSpacing' #define kBitsAllocated 0x0028+(0x0100 << 16 ) #define kBitsStored 0x0028+(0x0101 << 16 )//'0028' '0101' 'US' 'BitsStored' #define kIsSigned 0x0028+(0x0103 << 16 ) #define kIntercept 0x0028+(0x1052 << 16 ) #define kSlope 0x0028+(0x1053 << 16 ) #define kGeiisFlag 0x0029+(0x0010 << 16 ) //warn user if dreaded GEIIS was used to process image #define kCSAImageHeaderInfo 0x0029+(0x1010 << 16 ) //#define kObjectGraphics 0x0029+(0x1210 << 16 ) //0029,1210 syngoPlatformOOGInfo Object Oriented Graphics #define kDiffusionBFactorGE 0x0043+(0x1039 << 16 ) //IS dicm2nii's SlopInt_6_9 #define kCoilSiemens 0x0051+(0x100F << 16 ) #define kLocationsInAcquisition 0x0054+(0x0081 << 16 ) #define kIconImageSequence 0x0088+(0x0200 << 16 ) #define kDiffusionBFactor 0x2001+(0x1003 << 16 )// FL #define kSliceOrient 0x2001+(0x100B << 16 )//2001,100B Philips slice orientation (TRANSVERSAL, AXIAL, SAGITTAL) #define kLocationsInAcquisitionPhilips 0x2001+(0x1018 << 16 ) #define kNumberOfDynamicScans 0x2001+(0x1081 << 16 )//'2001' '1081' 'IS' 'NumberOfDynamicScans' #define kMRAcquisitionTypePhilips 0x2005+(0x106F << 16) #define kAngulationAP 0x2005+(0x1071 << 16)//'2005' '1071' 'FL' 'MRStackAngulationAP' #define kAngulationFH 0x2005+(0x1072 << 16)//'2005' '1072' 'FL' 'MRStackAngulationFH' #define kAngulationRL 0x2005+(0x1073 << 16)//'2005' '1073' 'FL' 'MRStackAngulationRL' #define kMRStackOffcentreAP 0x2005+(0x1078 << 16) #define kMRStackOffcentreFH 0x2005+(0x1079 << 16) #define kMRStackOffcentreRL 0x2005+(0x107A << 16) #define kDiffusionDirectionRL 0x2005+(0x10B0 << 16) #define kDiffusionDirectionAP 0x2005+(0x10B1 << 16) #define kDiffusionDirectionFH 0x2005+(0x10B2 << 16) #define k2005140F 0x2005+(0x140F << 16) #define kWaveformSq 0x5400+(0x0100 << 16) #define kImageStart 0x7FE0+(0x0010 << 16 ) #define kNest 0xFFFE +(0xE000 << 16 ) //Item follows SQ #define kUnnest 0xFFFE +(0xE00D << 16 ) //ItemDelimitationItem [length defined] http://www.dabsoft.ch/dicom/5/7.5/ #define kUnnest2 0xFFFE +(0xE0DD << 16 )//SequenceDelimitationItem [length undefined] double zSpacing = -1.0l; //includes slice thickness plus gap int locationsInAcquisitionGE = 0; int locationsInAcquisitionPhilips = 0; long lPos = 128+4; //4-byte signature starts at 128 uint32_t lLength; uint32_t groupElement = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24); if (groupElement != kStart) #ifdef myUseCOut std::cout<<"DICOM appears corrupt: first group:element should be 0x0002:0x0000" <<std::endl; #else printf("DICOM appears corrupt: first group:element should be 0x0002:0x0000\n"); #endif char vr[2]; bool isIconImageSequence = false; bool isSwitchToImplicitVR = false; bool isSwitchToBigEndian = false; //bool geiisBug = false; //for buggy GEIIS http://forum.dcmtk.org/viewtopic.php?p=7162&sid=3b516cc751aae51fbb5e73184abe37c2 bool is2005140FSQ = false; //for buggy Philips bool is2005140FSQwarned = false; //for buggy Philips bool isAtFirstPatientPosition = false; //for 3d and 4d files: flag is true for slices at same position as first slice int patientPositionCount = 0; long coilNum = 0; //Siemens can save one image per coil (H12,H13,etc) or one combined image for array (HEA;HEP) long echoNum = 0; while ((d.imageStart == 0) && ((lPos+8) < fileLen)) { if (d.isLittleEndian) groupElement = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24); else groupElement = buffer[lPos+1] | (buffer[lPos] << 8) | (buffer[lPos+3] << 16) | (buffer[lPos+2] << 24); if ((isSwitchToBigEndian) && ((groupElement & 0xFFFF) != 2)) { isSwitchToBigEndian = false; d.isLittleEndian = false; groupElement = buffer[lPos+1] | (buffer[lPos] << 8) | (buffer[lPos+3] << 16) | (buffer[lPos+2] << 24); }//transfer syntax requests switching endian after group 0002 if ((isSwitchToImplicitVR) && ((groupElement & 0xFFFF) != 2)) { isSwitchToImplicitVR = false; d.isExplicitVR = false; } //transfer syntax requests switching VR after group 0001 //uint32_t group = (groupElement & 0xFFFF); lPos += 4; if ((groupElement == kNest) || (groupElement == kUnnest) || (groupElement == kUnnest2)) { vr[0] = 'N'; vr[1] = 'A'; //if (groupElement == kUnnest) geiisBug = false; //don't exit if there is a proprietary thumbnail //printf("xxx"); lLength = 4; } else if (d.isExplicitVR) { vr[0] = buffer[lPos]; vr[1] = buffer[lPos+1]; if (buffer[lPos+1] < 'A') {//implicit vr with 32-bit length if (d.isLittleEndian) lLength = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24); else lLength = buffer[lPos+3] | (buffer[lPos+2] << 8) | (buffer[lPos+1] << 16) | (buffer[lPos] << 24); lPos += 4; } else if ( ((buffer[lPos] == 'U') && (buffer[lPos+1] == 'N')) || ((buffer[lPos] == 'O') && (buffer[lPos+1] == 'B')) || ((buffer[lPos] == 'O') && (buffer[lPos+1] == 'W')) ) { //VR= UN, OB, OW, SQ || ((buffer[lPos] == 'S') && (buffer[lPos+1] == 'Q')) lPos = lPos + 4; //skip 2 byte VR string and 2 reserved bytes = 4 bytes if (d.isLittleEndian) lLength = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24); else lLength = buffer[lPos+3] | (buffer[lPos+2] << 8) | (buffer[lPos+1] << 16) | (buffer[lPos] << 24); lPos = lPos + 4; //skip 4 byte length } else if ((buffer[lPos] == 'S') && (buffer[lPos+1] == 'Q')) { lLength = 8; //Sequence Tag is2005140FSQ = (groupElement == k2005140F); } else { //explicit VR with 16-bit length if ((d.isLittleEndian) ) lLength = buffer[lPos+2] | (buffer[lPos+3] << 8); else lLength = buffer[lPos+3] | (buffer[lPos+2] << 8); lPos += 4; //skip 2 byte VR string and 2 length bytes = 4 bytes } } else { //implicit VR if (d.isLittleEndian) lLength = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24); else lLength = buffer[lPos+3] | (buffer[lPos+2] << 8) | (buffer[lPos+1] << 16) | (buffer[lPos] << 24); lPos += 4; //we have loaded the 32-bit length } //if explicit else implicit VR if (lLength == 0xFFFFFFFF) lLength = 8; //SQ (Sequences) use 0xFFFFFFFF [4294967295] to denote unknown length //next: look for required tags if ((isIconImageSequence) && ((groupElement & 0x0028) == 0x0028 )) groupElement = kUnused; //ignore icon dimensions switch ( groupElement ) { case kTransferSyntax: { char transferSyntax[kDICOMStr]; dcmStr (lLength, &buffer[lPos], transferSyntax); //printf("transfer syntax '%s'\n", transferSyntax); if (strcmp(transferSyntax, "1.2.840.10008.1.2.1") == 0) ; //default isExplicitVR=true; //d.isLittleEndian=true else if (strcmp(transferSyntax, "1.2.840.10008.1.2.2") == 0) isSwitchToBigEndian = true; //isExplicitVR=true; else if (strcmp(transferSyntax, "1.2.840.10008.1.2") == 0) isSwitchToImplicitVR = true; //d.isLittleEndian=true else { #ifdef myUseCOut std::cout<<"Unsupported transfer syntax "<< transferSyntax<<std::endl; #else printf("Unsupported transfer syntax '%s'\n",transferSyntax); #endif d.imageStart = 1;//abort as invalid (imageStart MUST be >128) } break;} //{} provide scope for variable 'transferSyntax case kStudyDate: dcmStr (lLength, &buffer[lPos], d.studyDate); break; case kManufacturer: d.manufacturer = dcmStrManufacturer (lLength, &buffer[lPos]); break; case kComplexImageComponent: d.isHasPhase = (buffer[lPos]=='P') && (toupper(buffer[lPos+1]) == 'H'); d.isHasMagnitude = (buffer[lPos]=='M') && (toupper(buffer[lPos+1]) == 'A'); break; case kAcquisitionTime : { char acquisitionTimeTxt[kDICOMStr]; dcmStr (lLength, &buffer[lPos], acquisitionTimeTxt); d.acquisitionTime = atof(acquisitionTimeTxt); break; } case kStudyTime : dcmStr (lLength, &buffer[lPos], d.studyTime); break; case kPatientName : dcmStr (lLength, &buffer[lPos], d.patientName); break; case kPatientID : dcmStr (lLength, &buffer[lPos], d.patientID); break; case kProtocolNameGE: { if (d.manufacturer == kMANUFACTURER_GE) dcmStr (lLength, &buffer[lPos], d.protocolName); break; } case kProtocolName : { if (strlen(d.protocolName) < 1) //GE uses a generic session name here: do not overwrite kProtocolNameGE dcmStr (lLength, &buffer[lPos], d.protocolName); break; } case kPatientOrient : dcmStr (lLength, &buffer[lPos], d.patientOrient); break; case kLastScanLoc : d.lastScanLoc = dcmStrFloat(lLength, &buffer[lPos]); break; /*case kDiffusionBFactorSiemens : if (d.manufacturer == kMANUFACTURER_SIEMENS) printf(">>>>%f\n,",dcmStrFloat(lLength, &buffer[lPos])); break;*/ case kDiffusionDirectionGEX : if (d.manufacturer == kMANUFACTURER_GE) d.CSA.dtiV[0][1] = dcmStrFloat(lLength, &buffer[lPos]); break; case kDiffusionDirectionGEY : if (d.manufacturer == kMANUFACTURER_GE) d.CSA.dtiV[0][2] = dcmStrFloat(lLength, &buffer[lPos]); break; case kDiffusionDirectionGEZ : if (d.manufacturer == kMANUFACTURER_GE) { d.CSA.dtiV[0][3] = dcmStrFloat(lLength, &buffer[lPos]); d.CSA.numDti = 1; } break; case kPatientPosition : if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (is2005140FSQ)) { #ifdef myUseCOut if (!is2005140FSQwarned) std::cout<<"Warning: Philips R3.2.2 can report different positions for the same slice. Attempting patch." <<std::endl; #else if (!is2005140FSQwarned) printf("Warning: Philips R3.2.2 can report different positions for the same slice. Attempting patch.\n"); #endif is2005140FSQwarned = true; } else { patientPositionCount++; isAtFirstPatientPosition = true; if (isnan(d.patientPosition[1])) dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, &d.patientPosition[0]); //slice position else { dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, &d.patientPositionLast[0]); //slice direction for 4D if ((isFloatDiff(d.patientPositionLast[1],d.patientPosition[1])) || (isFloatDiff(d.patientPositionLast[2],d.patientPosition[2])) || (isFloatDiff(d.patientPositionLast[3],d.patientPosition[3])) ) { isAtFirstPatientPosition = false; //this slice is not at position of 1st slice if (d.patientPositionSequentialRepeats == 0) //this is the first slice with different position d.patientPositionSequentialRepeats = patientPositionCount-1; } //if different position from 1st slice in file } //if not first slice in file } //not after 2005,140F break; case kInPlanePhaseEncodingDirection: d.phaseEncodingRC = toupper(buffer[lPos]); //first character is either 'R'ow or 'C'ol break; case kSeriesNum: d.seriesNum = dcmStrInt(lLength, &buffer[lPos]); break; case kAcquNum: d.acquNum = dcmStrInt(lLength, &buffer[lPos]); break; case kImageNum: d.imageNum = dcmStrInt(lLength, &buffer[lPos]); break; case kPlanarRGB: d.isPlanarRGB = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kDim3: d.xyzDim[3] = dcmStrInt(lLength, &buffer[lPos]); break; case kSamplesPerPixel: d.samplesPerPixel = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kDim2: d.xyzDim[2] = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kDim1: d.xyzDim[1] = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kXYSpacing: dcmMultiFloat(lLength, (char*)&buffer[lPos], 2, d.xyzMM); break; case kImageComments: dcmStr (lLength, &buffer[lPos], d.imageComments); break; case kLocationsInAcquisitionGE: locationsInAcquisitionGE = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kBitsAllocated : d.bitsAllocated = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kBitsStored : d.bitsStored = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kIsSigned : //http://dicomiseasy.blogspot.com/2012/08/chapter-12-pixel-data.html d.isSigned = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kTR : d.TR = dcmStrFloat(lLength, &buffer[lPos]); break; case kTE : d.TE = dcmStrFloat(lLength, &buffer[lPos]); break; case kEchoNum : echoNum = dcmStrInt(lLength, &buffer[lPos]); break; case kZSpacing : zSpacing = dcmStrFloat(lLength, &buffer[lPos]); break; case kSlope : d.intenScale = dcmStrFloat(lLength, &buffer[lPos]); break; case kIntercept : d.intenIntercept = dcmStrFloat(lLength, &buffer[lPos]); break; case kZThick : d.xyzMM[3] = dcmStrFloat(lLength, &buffer[lPos]); break; case kCoilSiemens : { if (d.manufacturer == kMANUFACTURER_SIEMENS) { //see if image from single coil "H12" or an array "HEA;HEP" char coilStr[kDICOMStr]; dcmStr (lLength, &buffer[lPos], coilStr); //long coilNum = 0; char *ptr; dcmStrDigitsOnly(coilStr); coilNum = strtol(coilStr, &ptr, 10); if (*ptr != '\0') coilNum = 0; } break; } case kLocationsInAcquisition : d.locationsInAcquisition = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kLocationsInAcquisitionPhilips: locationsInAcquisitionPhilips = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kIconImageSequence: isIconImageSequence = true; break; case kNumberOfDynamicScans: d.numberOfDynamicScans = dcmStrInt(lLength, &buffer[lPos]); break; case kMRAcquisitionType: if (lLength > 1) d.is3DAcq = (buffer[lPos]=='3') && (toupper(buffer[lPos+1]) == 'D'); break; case kMRAcquisitionTypePhilips: //kMRAcquisitionType if (lLength > 1) d.is3DAcq = (buffer[lPos]=='3') && (toupper(buffer[lPos+1]) == 'D'); break; case kAngulationRL: d.angulation[1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kAngulationAP: d.angulation[2] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kAngulationFH: d.angulation[3] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kMRStackOffcentreRL: d.stackOffcentre[1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kMRStackOffcentreAP: d.stackOffcentre[2] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kMRStackOffcentreFH: d.stackOffcentre[3] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kSliceOrient: { char orientStr[kDICOMStr]; dcmStr (lLength, &buffer[lPos], orientStr); if (toupper(orientStr[0])== 'S') d.sliceOrient = kSliceOrientSag; //sagittal else if (toupper(orientStr[0])== 'C') d.sliceOrient = kSliceOrientCor; //coronal else d.sliceOrient = kSliceOrientTra; //transverse (axial) break; } case kDiffusionBFactor: if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition)) { d.CSA.numDti++; //increment with BFactor: on Philips slices with B=0 have B-factor but no diffusion directions if ((d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv)) d.CSA.dtiV[d.CSA.numDti-1][0] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); } break; case kDiffusionDirectionRL: if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition) && (d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv)) d.CSA.dtiV[d.CSA.numDti-1][1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kDiffusionDirectionAP: if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition) && (d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv)) d.CSA.dtiV[d.CSA.numDti-1][2] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kDiffusionDirectionFH: if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition) && (d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv)) d.CSA.dtiV[d.CSA.numDti-1][3] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); //http://www.na-mic.org/Wiki/index.php/NAMIC_Wiki:DTI:DICOM_for_DWI_and_DTI break; case kWaveformSq: d.imageStart = 1; //abort!!! #ifdef myUseCOut std::cout<<"Warning: Unable to extract sound wave forms" <<std::endl; #else printf("Warning: Unable to extract sound wave forms\n"); #endif break; case kCSAImageHeaderInfo: readCSAImageHeader(&buffer[lPos], lLength, &d.CSA, isVerbose); break; //case kObjectGraphics: // printf("---->%d,",lLength); // break; case kDiffusionBFactorGE : if (d.manufacturer == kMANUFACTURER_GE) d.CSA.dtiV[0][0] = dcmStrInt(lLength, &buffer[lPos]); break; case kGeiisFlag: if ((lLength > 4) && (buffer[lPos]=='G') && (buffer[lPos+1]=='E') && (buffer[lPos+2]=='I') && (buffer[lPos+3]=='I')) { //read a few digits, as bug is specific to GEIIS, while GEMS are fine #ifdef myUseCOut std::cout<<"Warning: GEIIS violates the DICOM standard. Inspect results and admonish your vendor." <<std::endl; #else printf("Warning: GEIIS violates the DICOM standard. Inspect results and admonish your vendor.\n"); #endif isIconImageSequence = true; //geiisBug = true; //compressed thumbnails do not follow transfer syntax! GE should not re-use pulbic tags for these proprietary images http://sonca.kasshin.net/gdcm/Doc/GE_ImageThumbnails } break; case kOrientation : dcmMultiFloat(lLength, (char*)&buffer[lPos], 6, d.orient); break; case kImageStart: //if ((!geiisBug) && (!isIconImageSequence)) //do not exit for proprietary thumbnails if (!isIconImageSequence) //do not exit for proprietary thumbnails d.imageStart = (int)lPos; //geiisBug = false; isIconImageSequence = false; break; } //switch/case for groupElement //printf("VR=%c%c tag=%04x,%04x length=%lu, pos=%ld x=%d\n",vr[0],vr[1],groupElement & 65535,groupElement>>16, lLength, lPos, d.xyzDim[1]); lPos = lPos + (lLength); } free (buffer); d.dateTime = (atof(d.studyDate)* 1000000) + atof(d.studyTime); //printf("slices in Acq %d %d\n",d.locationsInAcquisition,locationsInAcquisitionPhilips); if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (d.locationsInAcquisition == 0)) d.locationsInAcquisition = locationsInAcquisitionPhilips; if ((d.manufacturer == kMANUFACTURER_GE) && (d.locationsInAcquisition == 0)) d.locationsInAcquisition = locationsInAcquisitionGE; if (zSpacing > 0) d.xyzMM[3] = zSpacing; //use zSpacing if provided: depending on vendor, kZThick may or may not include a slice gap //printf("patientPositions = %d XYZT = %d slicePerVol = %d numberOfDynamicScans %d\n",patientPositionCount,d.xyzDim[3], d.locationsInAcquisition, d.numberOfDynamicScans); if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (patientPositionCount > d.xyzDim[3])) printf("Please check slice thicknesses: Philips R3.2.2 bug can disrupt estimation (%d positions reported for %d slices)\n",patientPositionCount, d.xyzDim[3]); //Philips reported different positions for each slice! if ((d.imageStart > 144) && (d.xyzDim[1] > 1) && (d.xyzDim[2] > 1)) d.isValid = true; if ((d.xyzMM[1] > FLT_EPSILON) && (d.xyzMM[2] < FLT_EPSILON)) { printf("Please check voxel size\n"); d.xyzMM[2] = d.xyzMM[1]; } if ((d.xyzMM[2] > FLT_EPSILON) && (d.xyzMM[1] < FLT_EPSILON)) { printf("Please check voxel size\n"); d.xyzMM[1] = d.xyzMM[2]; } if ((d.xyzMM[3] < FLT_EPSILON)) { printf("Unable to determine slice thickness: please check voxel size\n"); d.xyzMM[3] = 1.0; } if (coilNum > 0) //segment images with multiple coils d.seriesNum = d.seriesNum + (100*coilNum); if (echoNum > 2) //segment images with multiple echoes d.seriesNum = d.seriesNum + (100*echoNum); if (isVerbose) { printf("Patient Position %f %f %f\n",d.patientPosition[1],d.patientPosition[2],d.patientPosition[3]); printf("DICOM acq %d img %d ser %ld dim %dx%dx%d mm %gx%gx%g offset %d dyn %d loc %d valid %d ph %d mag %d posReps %d nDTI %d 3d %d\n",d.acquNum,d.imageNum,d.seriesNum,d.xyzDim[1],d.xyzDim[2],d.xyzDim[3],d.xyzMM[1],d.xyzMM[2],d.xyzMM[3],d.imageStart, d.numberOfDynamicScans, d.locationsInAcquisition, d.isValid, d.isHasPhase, d.isHasMagnitude,d.patientPositionSequentialRepeats, d.CSA.numDti, d.is3DAcq); } return d; } // readDICOM() struct TDICOMdata readDICOM(char * fname) { return readDICOMv(fname, false); } ���������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/qtGui/nii_dicom_batch.c�������������������������������������������������������0000664�0000000�0000000�00000173334�13220512030�0020317�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������//#define myNoSave //do not save images to disk #ifndef myDisableZLib #include <zlib.h> #ifndef myDisableTarGz // #include "untgz.h" #endif #endif #ifdef myUseCOut #include <iostream> #endif #include "nifti1_io_core.h" #include "nifti1.h" #include "nii_dicom_batch.h" #include "nii_dicom.h" #include "tinydir.h" #include <ctype.h> //toupper #include <float.h> #include <math.h> #include <stdbool.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> #include <time.h> // clock_t, clock, CLOCKS_PER_SEC #include "nii_ortho.h" #if defined(_WIN64) || defined(_WIN32) #include <windows.h> //write to registry #endif #ifndef M_PI #define M_PI 3.14159265358979323846 #endif //gcc -O3 -o main main.c nii_dicom.c #if defined(_WIN64) || defined(_WIN32) const char kPathSeparator ='\\'; const char kFileSep[2] = "\\"; #else const char kPathSeparator ='/'; const char kFileSep[2] = "/"; #endif struct TDCMsort { uint64_t indx, img; }; struct TSearchList { unsigned long numItems, maxItems; char **str; }; void dropFilenameFromPath(char *path) { // const char *dirPath = strrchr(path, '/'); //UNIX if (dirPath == 0) dirPath = strrchr(path, '\\'); //Windows if (dirPath == NULL) { strcpy(path,""); } else path[dirPath - path] = 0; // please make sure there is enough space in TargetDirectory } void getFileName( char *pathParent, const char *path) //if path is c:\d1\d2 then filename is 'd2' { const char *filename = strrchr(path, '/'); //UNIX if (filename == 0) filename = strrchr(path, '\\'); //Windows //const char *filename = strrchr(path, kPathSeparator); //x if (filename == NULL) {//no path separator strcpy(pathParent,path); return; } filename++; strcpy(pathParent,filename); } bool is_fileexists(const char * filename) { FILE * fp = NULL; if ((fp = fopen(filename, "r"))) { fclose(fp); return true; } return false; } bool is_fileNotDir(const char* path) { //returns false if path is a folder; requires #include <sys/stat.h> struct stat buf; stat(path, &buf); return S_ISREG(buf.st_mode); } //is_file() bool is_exe(const char* path) { //requires #include <sys/stat.h> struct stat buf; stat(path, &buf); return (S_ISREG(buf.st_mode) && (buf.st_mode & 0111) ); } //is_exe() int is_dir(const char *pathname, int follow_link) { struct stat s; if ((NULL == pathname) || (0 == strlen(pathname))) return 0; int err = stat(pathname, &s); if(-1 == err) { return 0; /* does not exist */ } else { if(S_ISDIR(s.st_mode)) { return 1; /* it's a dir */ } else { return 0;/* exists but is no dir */ } } } //is_dir /*int is_dir(const char *pathname, int follow_link) { //http://sources.gentoo.org/cgi-bin/viewvc.cgi/path-sandbox/trunk/libsbutil/get_tmp_dir.c?revision=260 struct stat buf; int retval; if ((NULL == pathname) || (0 == strlen(pathname))) return 0; retval = follow_link ? stat(pathname, &buf) : lstat(pathname, &buf); if ((-1 != retval) && (S_ISDIR(buf.st_mode))) return 1; if ((-1 == retval) && (ENOENT != errno)) // Some or other error occurred return -1; return 0; } //is_dir() */ bool isDICOMfile(const char * fname) { FILE *fp = fopen(fname, "rb"); if (!fp) return false; fseek(fp, 0, SEEK_END); long long fileLen=ftell(fp); if (fileLen < 256) return false; fseek(fp, 0, SEEK_SET); unsigned char buffer[256]; fread(buffer, 256, 1, fp); fclose(fp); if ((buffer[128] != 'D') || (buffer[129] != 'I') || (buffer[130] != 'C') || (buffer[131] != 'M')) return false; return true; } //isDICOMfile() void geCorrectBvecs(struct TDICOMdata *d, int sliceDir){ //0018,1312 phase encoding is either in row or column direction //0043,1039 (or 0043,a039). b value (as the first number in the string). //0019,10bb (or 0019,a0bb). phase diffusion direction //0019,10bc (or 0019,a0bc). frequency diffusion direction //0019,10bd (or 0019,a0bd). slice diffusion direction //These directions are relative to freq,phase,slice, so although no //transformations are required, you need to check the direction of the //phase encoding. This is in DICOM message 0018,1312. If this has value //COL then if swap the x and y value and reverse the sign on the z value. //If the phase encoding is not COL, then just reverse the sign on the x value. if (d->manufacturer != kMANUFACTURER_GE) return; if (d->CSA.numDti < 1) return; if ((toupper(d->patientOrient[0])== 'H') && (toupper(d->patientOrient[1])== 'F') && (toupper(d->patientOrient[2])== 'S')) ; //participant was head first supine else { #ifdef myUseCOut std::cout<<"GE DTI directions require head first supine acquisition" <<std::endl; #else printf("GE DTI directions require head first supine acquisition\n"); #endif return; } bool col = false; if (d->phaseEncodingRC== 'C') col = true; else if (d->phaseEncodingRC!= 'R') { #ifdef myUseCOut std::cout<<"Error: Unable to determine DTI gradients, 0018,1312 should be either R or C" <<std::endl; #else printf("Error: Unable to determine DTI gradients, 0018,1312 should be either R or C"); #endif return; } if (abs(sliceDir) != 3) #ifdef myUseCOut std::cout<<"GE DTI gradients only tested for axial acquisitions" <<std::endl; #else printf("GE DTI gradients only tested for axial acquisitions"); #endif //printf("GE row(0) or column(1) = %d",col); #ifdef myUseCOut std::cout<<"Reorienting "<< d->CSA.numDti << "GE DTI gradients. Please validate if you are conducting DTI analyses. isCol="<<col <<std::endl; #else printf("Reorienting %d GE DTI gradients. Please validate if you are conducting DTI analyses. isCol=%d\n", d->CSA.numDti, col); #endif for (int i = 0; i < d->CSA.numDti; i++) { float vLen = sqrt( (d->CSA.dtiV[i][1]*d->CSA.dtiV[i][1]) + (d->CSA.dtiV[i][2]*d->CSA.dtiV[i][2]) + (d->CSA.dtiV[i][3]*d->CSA.dtiV[i][3])); if ((d->CSA.dtiV[i][0] <= FLT_EPSILON)|| (vLen <= FLT_EPSILON) ) { //bvalue=0 for (int v= 0; v < 4; v++) d->CSA.dtiV[i][v] =0.0f; continue; //do not normalize or reorient b0 vectors } d->CSA.dtiV[i][1] = -d->CSA.dtiV[i][1]; if (!col) { //rows need to be swizzled float swap = d->CSA.dtiV[i][1]; d->CSA.dtiV[i][1] = -d->CSA.dtiV[i][2]; d->CSA.dtiV[i][2] = swap; } if (sliceDir < 0) d->CSA.dtiV[i][3] = -d->CSA.dtiV[i][3]; if (isSameFloat(d->CSA.dtiV[i][1],-0)) d->CSA.dtiV[i][1] = 0.0f; if (isSameFloat(d->CSA.dtiV[i][2],-0)) d->CSA.dtiV[i][2] = 0.0f; if (isSameFloat(d->CSA.dtiV[i][3],-0)) d->CSA.dtiV[i][3] = 0.0f; } } //geCorrectBvecs() /*void philipsCorrectBvecs(struct TDICOMdata *d){ //Philips DICOM data stored in patient (LPH) space, regardless of settings in Philips user interface //algorithm from PARtoNRRD/CATNAP (with July 20, 2007 patch) http://godzilla.kennedykrieger.org/~jfarrell/software_web.htm //0018,5100. patient orientation - 'HFS' //2001,100B Philips slice orientation (TRANSVERSAL, AXIAL, SAGITTAL) //2005,1071 MRStackAngulationAP //2005,1072 MRStackAngulationFH //2005,1073 MRStackAngulationRL if (d->manufacturer != kMANUFACTURER_PHILIPS) return; if (d->CSA.numDti < 1) return; mat33 tpp,tpo; if ((toupper(d->patientOrient[0])== 'F') && (toupper(d->patientOrient[1])== 'F')) LOAD_MAT33(tpp, 0,-1,0, -1,0,0, 0,0,1); //feet first else if ((toupper(d->patientOrient[0])== 'H') && (toupper(d->patientOrient[1])== 'F')) LOAD_MAT33(tpp, 0,1,0,-1,0,0, 0,0,-1); //head first else { printf("Unable to correct Philips DTI vectors: patient position must be head or feet first\n"); return; } //unused? mat33 rev_tpp =nifti_mat33_transpose(tpp); if (toupper(d->patientOrient[2])== 'S')//supine LOAD_MAT33 (tpo, 1,0,0, 0,1,0, 0,0,1); else if (toupper(d->patientOrient[2])== 'P')//prone LOAD_MAT33 (tpo,-1,0,0, 0,-1,0, 0,0,1); else if ((toupper(d->patientOrient[2])== 'D') && (toupper(d->patientOrient[3])== 'R')) //DR LOAD_MAT33 (tpo,0,-1,0, 1,0,0, 0,0,1); else if ((toupper(d->patientOrient[2])== 'D') && (toupper(d->patientOrient[3])== 'L'))//DL LOAD_MAT33 (tpo,0,1,0, -1,0,0, 0,0,1); else { printf("DTI vector error: Position is not HFS,HFP,HFDR,HFDL,FFS,FFP,FFDR, or FFDL: %s\n",d->patientOrient); return; } //unused? mat33 rev_tpo =nifti_mat33_transpose(tpo); //unused? mat33 tpom = nifti_mat33_mul( tpo, tpp); //unused? mat33 rev_tpom = nifti_mat33_mul( rev_tpp,rev_tpo ); printf("Reorienting %d Philip DTI gradients with angulations %f %f %f. Please validate if you are conducting DTI analyses.\n", d->CSA.numDti, d->angulation[1], d->angulation[2], d->angulation[3]); float rl = d->angulation[1] * M_PI /180; //as radian float ap = d->angulation[2] * M_PI /180; float fh = d->angulation[3] * M_PI /180; //printf(" %f %f %f \n",ap,fh,rl); mat33 trl, tap, tfh, rev_tsom, dtiextra; LOAD_MAT33 (trl,1,0,0, 0, cos(rl),- sin(rl), 0, sin(rl),cos(rl)); LOAD_MAT33 (tap, cos(ap),0, sin(ap), 0,1,0, - sin(ap),0, cos(ap)); LOAD_MAT33 (tfh,cos(fh),- sin(fh),0, sin(fh), cos(fh),0, 0,0,1); mat33 rev_trl =nifti_mat33_transpose(trl); mat33 rev_tap =nifti_mat33_transpose(tap); mat33 rev_tfh =nifti_mat33_transpose(tfh); mat33 mtemp1 = nifti_mat33_mul( trl, tap); //unused? mat33 tang = nifti_mat33_mul( mtemp1, tfh); mtemp1 = nifti_mat33_mul( rev_tfh, rev_tap ); mat33 rev_tang = nifti_mat33_mul( mtemp1, rev_trl); //kSliceOrientSag if (d->sliceOrient == kSliceOrientSag)//SAGITTAL LOAD_MAT33 (rev_tsom, 0,0,1, 0,-1,0, -1,0,0 ); else if (d->sliceOrient == 2)//CORONAL LOAD_MAT33 (rev_tsom, 0,0,1, -1,0,0, 0,1,0 ); else LOAD_MAT33 (rev_tsom, 0,-1,0, -1,0,0, 0,0,1 ); LOAD_MAT33 (dtiextra, 0,-1,0, -1,0,0, 0,0,1 ); mat33 mtemp2 = nifti_mat33_mul( dtiextra, rev_tsom); mtemp1 = nifti_mat33_mul( mtemp2, rev_tang); for (int i = 0; i < d->CSA.numDti; i++) { //printf("%d\tvin=[\t%f\t%f\t%f\t]; bval=\t%f\n",i,d->CSA.dtiV[i][1],d->CSA.dtiV[i][2],d->CSA.dtiV[i][3],d->CSA.dtiV[i][0]); float vLen = sqrt( (d->CSA.dtiV[i][1]*d->CSA.dtiV[i][1]) + (d->CSA.dtiV[i][2]*d->CSA.dtiV[i][2]) + (d->CSA.dtiV[i][3]*d->CSA.dtiV[i][3])); if ((d->CSA.dtiV[i][0] <= FLT_EPSILON)|| (vLen <= FLT_EPSILON) ) { //bvalue=0 for (int v= 0; v < 4; v++) d->CSA.dtiV[i][v] =0.0f; continue; //do not normalize or reorient b0 vectors } vec3 v3; for (int v= 1; v < 4; v++) //normalize and reverse vector directions v3.v[v-1] =-d->CSA.dtiV[i][v]/vLen; v3 = nifti_vect33mat33_mul(v3,mtemp1); v3 = nifti_vect33_norm(v3); d->CSA.dtiV[i][1] = v3.v[0]; d->CSA.dtiV[i][2] = v3.v[1]; //NIfTI Y reversed relative to DICOM d->CSA.dtiV[i][3] = v3.v[2]; //printf("%d\tvoutt=[\t%f\t%f\t%f\t]; bval=\t%f\n",i,d->CSA.dtiV[i][1],d->CSA.dtiV[i][2],d->CSA.dtiV[i][3],d->CSA.dtiV[i][0]); } if ( d->sliceOrient != kSliceOrientTra) printf("Warning: Philips DTI gradients only evaluated for axial (transverse) acquisitions. Please verify sign and direction\n"); } //philipsCorrectBvecs() */ void siemensPhilipsCorrectBvecs(struct TDICOMdata *d, int sliceDir){ //see Matthew Robson's http://users.fmrib.ox.ac.uk/~robson/internal/Dicom2Nifti111.m //convert DTI vectors from scanner coordinates to image frame of reference //Uses 6 orient values from ImageOrientationPatient (0020,0037) // requires PatientPosition 0018,5100 is HFS (head first supine) if ((d->manufacturer != kMANUFACTURER_SIEMENS) && (d->manufacturer != kMANUFACTURER_PHILIPS)) return; if (d->CSA.numDti < 1) return; if ((toupper(d->patientOrient[0])== 'H') && (toupper(d->patientOrient[1])== 'F') && (toupper(d->patientOrient[2])== 'S')) ; //participant was head first supine else { #ifdef myUseCOut std::cout<<"Siemens/Philips DTI directions require head first supine acquisition"<<std::endl; #else printf("Siemens/Philips DTI directions require head first supine acquisition\n"); #endif return; } vec3 read_vector = setVec3(d->orient[1],d->orient[2],d->orient[3]); vec3 phase_vector = setVec3(d->orient[4],d->orient[5],d->orient[6]); vec3 slice_vector = crossProduct(read_vector ,phase_vector); read_vector = nifti_vect33_norm(read_vector); phase_vector = nifti_vect33_norm(phase_vector); slice_vector = nifti_vect33_norm(slice_vector); for (int i = 0; i < d->CSA.numDti; i++) { float vLen = sqrt( (d->CSA.dtiV[i][1]*d->CSA.dtiV[i][1]) + (d->CSA.dtiV[i][2]*d->CSA.dtiV[i][2]) + (d->CSA.dtiV[i][3]*d->CSA.dtiV[i][3])); if ((d->CSA.dtiV[i][0] <= FLT_EPSILON)|| (vLen <= FLT_EPSILON) ) { //bvalue=0 for (int v= 0; v < 4; v++) d->CSA.dtiV[i][v] =0.0f; continue; //do not normalize or reorient b0 vectors }//if bvalue=0 vec3 bvecs_old =setVec3(d->CSA.dtiV[i][1],d->CSA.dtiV[i][2],d->CSA.dtiV[i][3]); vec3 bvecs_new =setVec3(dotProduct(bvecs_old,read_vector),dotProduct(bvecs_old,phase_vector),dotProduct(bvecs_old,slice_vector) ); bvecs_new = nifti_vect33_norm(bvecs_new); d->CSA.dtiV[i][1] = bvecs_new.v[0]; d->CSA.dtiV[i][2] = -bvecs_new.v[1]; d->CSA.dtiV[i][3] = bvecs_new.v[2]; if (sliceDir == kSliceOrientMosaicNegativeDeterminant) d->CSA.dtiV[i][2] = -d->CSA.dtiV[i][2]; for (int v= 0; v < 4; v++) if (d->CSA.dtiV[i][v] == -0.0f) d->CSA.dtiV[i][v] = 0.0f; //remove sign from values that are virtually zero } //for each direction #ifdef myUseCOut if (sliceDir == kSliceOrientMosaicNegativeDeterminant) std::cout<<"WARNING: please validate DTI vectors (matrix had a negative determinant, perhaps Siemens sagittal)."<<std::endl; else if ( d->sliceOrient == kSliceOrientTra) std::cout<<"Saving "<<d->CSA.numDti<<" DTI gradients. Please validate if you are conducting DTI analyses."<<std::endl; else std::cout<<"WARNING: DTI gradient directions only tested for axial (transverse) acquisitions. Please validate bvec files."<<std::endl; #else if (sliceDir == kSliceOrientMosaicNegativeDeterminant) printf("WARNING: please validate DTI vectors (matrix had a negative determinant, perhaps Siemens sagittal).\n"); else if ( d->sliceOrient == kSliceOrientTra) printf("Saving %d DTI gradients. Please validate if you are conducting DTI analyses.\n", d->CSA.numDti); else printf("WARNING: DTI gradient directions only tested for axial (transverse) acquisitions. Please validate bvec files.\n"); #endif } //siemensPhilipsCorrectBvecs() bool isSamePosition (struct TDICOMdata d, struct TDICOMdata d2){ if (!isSameFloat(d.patientPosition[1],d2.patientPosition[1])) return false; if (!isSameFloat(d.patientPosition[2],d2.patientPosition[2])) return false; if (!isSameFloat(d.patientPosition[3],d2.patientPosition[3])) return false; return true; } //isSamePosition() bool nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[], struct TDCMopts opts, int sliceDir) { //reports true if last volume is excluded (e.g. philip stores an ADC map) //to do: works with 3D mosaics and 4D files, must remove repeated volumes for 2D sequences.... uint64_t indx0 = dcmSort[0].indx; //first volume int numDti = dcmList[indx0].CSA.numDti; if (numDti < 1) return false; if ((numDti < 3) && (nConvert < 3)) return false; if (numDti == 1) {//extract DTI from different slices numDti = 0; for (int i = 0; i < nConvert; i++) { //for each image if ((dcmList[indx0].CSA.mosaicSlices > 1) || (isSamePosition(dcmList[indx0],dcmList[dcmSort[i].indx]))) { if (numDti < kMaxDTIv) for (int v = 0; v < 4; v++) //for each vector+B-value dcmList[indx0].CSA.dtiV[numDti][v] = dcmList[dcmSort[i].indx].CSA.dtiV[0][v]; numDti++; } //for slices with repeats }//for each file dcmList[indx0].CSA.numDti = numDti; } if (numDti < 3) return false; if (numDti > kMaxDTIv) { #ifdef myUseCOut std::cout<<"Error: more than "<<kMaxDTIv<<" DTI directions detected (check for a new software)"<<std::endl; #else printf("Error: more than %d DTI directions detected (check for a new software)", kMaxDTIv); #endif return false; } bool bValueVaries = false; for (int i = 1; i < numDti; i++) //check if all bvalues match first volume if (dcmList[indx0].CSA.dtiV[i][0] != dcmList[indx0].CSA.dtiV[0][0]) bValueVaries = true; if (!bValueVaries) { for (int i = 1; i < numDti; i++) printf("bxyz %g %g %g %g\n",dcmList[indx0].CSA.dtiV[i][0],dcmList[indx0].CSA.dtiV[i][1],dcmList[indx0].CSA.dtiV[i][2],dcmList[indx0].CSA.dtiV[i][3]); printf("Error: only one B-value reported for all volumes: %g\n",dcmList[indx0].CSA.dtiV[0][0]); return false; } int firstB0 = -1; for (int i = 0; i < numDti; i++) //check if all bvalues match first volume if (isSameFloat(dcmList[indx0].CSA.dtiV[i][0],0) ) { firstB0 = i; break; } //printf("2015ALPHA %d -> %d\n",numDti, nConvert); #ifdef myUseCOut if (firstB0 < 0) std::cout<<"Warning: this diffusion series does not have a B0 (reference) volume"<<std::endl; if (firstB0 > 0) std::cout<<"Note: B0 not the first volume in the series (FSL eddy reference volume is "<<firstB0<<")"<<std::endl; #else if (firstB0 < 0) printf("Warning: this diffusion series does not have a B0 (reference) volume\n"); if (firstB0 > 0) printf("Note: B0 not the first volume in the series (FSL eddy reference volume is %d)\n", firstB0); #endif bool isFinalADC = false; /*if ((dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_PHILIPS) && (!isSameFloat(dcmList[indx0].CSA.dtiV[numDti-1][0],0.0f)) ) printf("xxx-->%f\n", dcmList[indx0].CSA.dtiV[numDti-1][3]);*/ if ((dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_PHILIPS) && (!isSameFloat(dcmList[indx0].CSA.dtiV[numDti-1][0],0.0f)) //not a B-0 image && ((isSameFloat(dcmList[indx0].CSA.dtiV[numDti-1][1],0.0f)) || (isSameFloat(dcmList[indx0].CSA.dtiV[numDti-1][2],0.0f)) || (isSameFloat(dcmList[indx0].CSA.dtiV[numDti-1][3],0.0f)) )) {//yet all vectors are zero!!!! must be ADC isFinalADC = true; //final volume is ADC map numDti --; //remove final volume - it is a computed ADC map! dcmList[indx0].CSA.numDti = numDti; } // philipsCorrectBvecs(&dcmList[indx0]); //<- replaced by unified siemensPhilips solution geCorrectBvecs(&dcmList[indx0],sliceDir); siemensPhilipsCorrectBvecs(&dcmList[indx0],sliceDir); if (opts.isVerbose) { for (int i = 0; i < (numDti-1); i++) { #ifdef myUseCOut std::cout<<i<<"\tB=\t"<<dcmList[indx0].CSA.dtiV[i][0]<<"\tVec=\t"<< dcmList[indx0].CSA.dtiV[i][1]<<"\t"<<dcmList[indx0].CSA.dtiV[i][2] <<"\t"<<dcmList[indx0].CSA.dtiV[i][3]<<std::endl; #else printf("%d\tB=\t%g\tVec=\t%g\t%g\t%g\n",i, dcmList[indx0].CSA.dtiV[i][0], dcmList[indx0].CSA.dtiV[i][1],dcmList[indx0].CSA.dtiV[i][2],dcmList[indx0].CSA.dtiV[i][3]); #endif } //for each direction } if (!opts.isFlipY ) { //!FLIP_Y&& (dcmList[indx0].CSA.mosaicSlices < 2) mosaics are always flipped in the Y direction for (int i = 0; i < (numDti-1); i++) { if (fabs(dcmList[indx0].CSA.dtiV[i][2]) > FLT_EPSILON) dcmList[indx0].CSA.dtiV[i][2] = -dcmList[indx0].CSA.dtiV[i][2]; } //for each direction } //if not a mosaic //printf("%f\t%f\t%f",dcmList[indx0].CSA.dtiV[1][1],dcmList[indx0].CSA.dtiV[1][2],dcmList[indx0].CSA.dtiV[1][3]); char txtname[2048] = {""}; strcpy (txtname,pathoutname); strcat (txtname,".bval"); //printf("Saving DTI %s\n",txtname); FILE *fp = fopen(txtname, "w"); if (fp == NULL) return isFinalADC; for (int i = 0; i < (numDti-1); i++) fprintf(fp, "%g\t", dcmList[indx0].CSA.dtiV[i][0]); fprintf(fp, "%g\n", dcmList[indx0].CSA.dtiV[numDti-1][0]); fclose(fp); strcpy(txtname,pathoutname); strcat (txtname,".bvec"); //printf("Saving DTI %s\n",txtname); fp = fopen(txtname, "w"); if (fp == NULL) return isFinalADC; for (int v = 1; v < 4; v++) { for (int i = 0; i < (numDti-1); i++) fprintf(fp, "%g\t", dcmList[indx0].CSA.dtiV[i][v]); fprintf(fp, "%g\n", dcmList[indx0].CSA.dtiV[numDti-1][v]); } fclose(fp); return isFinalADC; } //nii_SaveDTI() float sqr(float v){ return v*v; } //sqr() float intersliceDistance(struct TDICOMdata d1, struct TDICOMdata d2) { //some MRI scans have gaps between slices, some CT have overlapping slices. Comparing adjacent slices provides measure for dx between slices return sqrt( sqr(d1.patientPosition[1]-d2.patientPosition[1])+ sqr(d1.patientPosition[2]-d2.patientPosition[2])+ sqr(d1.patientPosition[3]-d2.patientPosition[3])); } //intersliceDistance() void swapDim3Dim4(int d3, int d4, struct TDCMsort dcmSort[]) { //swap space and time: input A0,A1...An,B0,B1...Bn output A0,B0,A1,B1,... int nConvert = d3 * d4; struct TDCMsort dcmSortIn[nConvert]; for (int i = 0; i < nConvert; i++) dcmSortIn[i] = dcmSort[i]; int i = 0; for (int b = 0; b < d3; b++) for (int a = 0; a < d4; a++) { int k = (a *d3) + b; //printf("%d -> %d %d ->%d\n",i,a, b, k); dcmSort[k] = dcmSortIn[i]; i++; } } //swapDim3Dim4() bool intensityScaleVaries(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[]){ //detect whether some DICOM images report different intensity scaling //some Siemens PET scanners generate 16-bit images where slice has its own scaling factor. // since NIfTI provides a single scaling factor for each file, these images require special consideration if (nConvert < 2) return false; bool iVaries = false; float iScale = dcmList[dcmSort[0].indx].intenScale; float iInter = dcmList[dcmSort[0].indx].intenIntercept; for (int i = 1; i < nConvert; i++) { //stack additional images uint64_t indx = dcmSort[i].indx; if (fabs (dcmList[indx].intenScale - iScale) > FLT_EPSILON) iVaries = true; if (fabs (dcmList[indx].intenIntercept- iInter) > FLT_EPSILON) iVaries = true; } return iVaries; } //intensityScaleVaries() /*unsigned char * nii_bgr2rgb(unsigned char* bImg, struct nifti_1_header *hdr) { //DICOM planarappears to be BBB..B,GGG..G,RRR..R, NIfTI RGB saved in planes RRR..RGGG..GBBBB..B // see http://www.barre.nom.fr/medical/samples/index.html US-RGB-8-epicard if (hdr->datatype != DT_RGB24) return bImg; int dim3to7 = 1; for (int i = 3; i < 8; i++) if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i]; int sliceBytes24 = hdr->dim[1]*hdr->dim[2] * hdr->bitpix/8; int sliceBytes8 = hdr->dim[1]*hdr->dim[2]; //Byte bImg[ bSz ]; //[img getBytes:&bImg length:bSz]; unsigned char slice24[sliceBytes24]; int sliceOffsetR = 0; for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice memcpy(&slice24, &bImg[sliceOffsetR], sliceBytes24); memcpy( &bImg[sliceOffsetR], &slice24[sliceBytes8*2], sliceBytes8); sliceOffsetR += sliceBytes8; memcpy( &bImg[sliceOffsetR], &slice24[sliceBytes8], sliceBytes8); sliceOffsetR += sliceBytes8; memcpy( &bImg[sliceOffsetR], &slice24[0], sliceBytes8); sliceOffsetR += sliceBytes8; } //for each slice return bImg; } //nii_ImgBytes()*/ bool niiExists(const char*pathoutname) { char niiname[2048] = {""}; strcat (niiname,pathoutname); strcat (niiname,".nii"); if (is_fileexists(niiname)) return true; char gzname[2048] = {""}; strcat (gzname,pathoutname); strcat (gzname,".nii.gz"); if (is_fileexists(gzname)) return true; return false; } //niiExists() int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopts opts) { char pth[1024] = {""}; if (strlen(opts.outdir) > 0) { strcpy(pth, opts.outdir); int w =access(pth,W_OK); if (w != 0) { if (getcwd(pth, sizeof(pth)) != NULL) { w =access(pth,W_OK); if (w != 0) { #ifdef myUseCOut std::cout<<"Error: you do not have write permissions for the directory "<<opts.outdir<<std::endl; #else printf("Error: you do not have write permissions for the directory %s\n",opts.outdir); #endif return EXIT_FAILURE; } #ifdef myUseCOut std::cout<<"Warning: "<<opts.outdir<<" write permission denied. Saving to working directory "<<pth<<std::endl; #else printf("Warning: %s write permission denied. Saving to working directory %s \n", opts.outdir, pth); #endif } } } char inname[1024] = {""};//{"test%t_%av"}; //% a = acquisition, %n patient name, %t time strcpy(inname, opts.filename); char outname[1024] = {""}; char newstr[256]; if (strlen(inname) < 1) { strcpy(inname, "T%t_N%n_S%s"); } int start = 0; int pos = 0; while (pos<strlen(inname)) { if (inname[pos] == '%') { if (pos > start) { strncpy(&newstr[0], &inname[0] + start, pos - start); newstr[pos - start] = '\0'; strcat (outname,newstr); } pos++; //extra increment: skip both % and following character char f = 'P'; if (pos<strlen(inname)) f = toupper(inname[pos]); if (f == 'C') strcat (outname,dcm.imageComments); if (f == 'F') strcat (outname,opts.indirParent); if (f == 'I') strcat (outname,dcm.patientID); if (f == 'N') strcat (outname,dcm.patientName); if (f == 'P') strcat (outname,dcm.protocolName); if ((f >= '0') && (f <= '9')) { if ((pos<strlen(inname)) && (toupper(inname[pos+1]) == 'S')) { char zeroPad[12] = {""}; sprintf(zeroPad,"%%0%dd",atoi(&f)); sprintf(newstr, zeroPad, dcm.seriesNum); strcat (outname,newstr); pos++; // e.g. %3f requires extra increment: skip both number and following character } } if (f == 'S') { sprintf(newstr, "%ld", dcm.seriesNum); strcat (outname,newstr); } if (f == 'T') { sprintf(newstr, "%0.0f", dcm.dateTime); strcat (outname,newstr); } start = pos + 1; } //found a % character pos++; } //for each character in input if (pos > start) { //append any trailing characters strncpy(&newstr[0], &inname[0] + start, pos - start); newstr[pos - start] = '\0'; strcat (outname,newstr); } if (strlen(outname) < 1) strcpy(outname, "dcm2nii_invalidName"); if (outname[0] == '.') outname[0] = '_'; //make sure not a hidden file //eliminate illegal characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx for (int pos = 0; pos<strlen(outname); pos ++) if ((outname[pos] == '<') || (outname[pos] == '>') || (outname[pos] == ':') || (outname[pos] == '"') || (outname[pos] == '\\') || (outname[pos] == '/') || (outname[pos] == '*') || (outname[pos] == '|') || (outname[pos] == '?')) outname[pos] = '_'; //printf("name=*%s* %d %d\n", outname, pos,start); char baseoutname[2048] = {""}; strcat (baseoutname,pth); char appendChar[2] = {"a"}; appendChar[0] = kPathSeparator; if (pth[strlen(pth)-1] != kPathSeparator) strcat (baseoutname,appendChar); strcat (baseoutname,outname); char pathoutname[2048] = {""}; strcat (pathoutname,baseoutname); int i = 0; while (niiExists(pathoutname) && (i < 26)) { strcpy(pathoutname,baseoutname); appendChar[0] = 'a'+i; strcat (pathoutname,appendChar); i++; } if (i >= 26) { #ifdef myUseCOut std::cout<<"Error: too many NIFTI images with the name "<<baseoutname<<std::endl; #else printf("Error: too many NIFTI images with the name %s\n", baseoutname); #endif return EXIT_FAILURE; } //printf("-->%s\n",pathoutname); return EXIT_SUCCESS; //printf("outname=%s\n", pathoutname); strcpy(niiFilename,pathoutname); return EXIT_SUCCESS; } //nii_createFilename() void nii_createDummyFilename(char * niiFilename, struct TDCMopts opts) { //generate string that illustrating sample of filename struct TDICOMdata dcm = clear_dicom_data(); strcpy(opts.indirParent,"myFolder"); char niiFilenameBase[1024] = {"/usr/myFolder/dicom.dcm"}; nii_createFilename(dcm, niiFilenameBase, opts) ; strcpy(niiFilename,"Example output filename: '"); strcat(niiFilename,niiFilenameBase); if (opts.isGz) strcat(niiFilename,".nii.gz'"); else strcat(niiFilename,".nii'"); } //nii_createDummyFilename() #ifndef myDisableZLib void writeNiiGz (char * baseName, struct nifti_1_header hdr, unsigned char* src_buffer, unsigned long src_len) { //create gz file in RAM, save to disk http://www.zlib.net/zlib_how.html // in general this single-threaded approach is slower than PIGZ but is useful for slow (network attached) disk drives char fname[2048] = {""}; strcpy (fname,baseName); strcat (fname,".nii.gz"); unsigned long hdrPadBytes = sizeof(hdr) + 4; //348 byte header + 4 byte pad unsigned long cmp_len = compressBound(src_len+hdrPadBytes); unsigned char *pCmp = (unsigned char *)malloc(cmp_len); z_stream strm; uLong file_crc32 = crc32(0L, Z_NULL, 0); //strm.adler = crc32(0L, Z_NULL, 0); strm.total_in = 0; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.next_out = pCmp; // output char array strm.avail_out = (unsigned int)cmp_len; // size of output //if ( deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15, 8, Z_DEFAULT_STRATEGY)!= Z_OK) return; if (deflateInit(&strm, Z_DEFAULT_COMPRESSION)!= Z_OK) return; //add header unsigned char *pHdr = (unsigned char *)malloc(hdrPadBytes); memcpy(pHdr,&hdr, sizeof(hdr)); strm.avail_in = (unsigned int)hdrPadBytes; // size of input strm.next_in = (Bytef *)pHdr; // input header deflate(&strm, Z_NO_FLUSH); file_crc32 = crc32(file_crc32, pHdr, (unsigned int)hdrPadBytes); //add image strm.avail_in = (unsigned int)src_len; // size of input strm.next_in = (Bytef *)src_buffer; // input image deflate(&strm, Z_FINISH); //Z_NO_FLUSH; //finish up deflateEnd(&strm); file_crc32 = crc32(file_crc32, src_buffer, (unsigned int)src_len); cmp_len = strm.total_out; if (cmp_len <= 0) return; FILE *fileGz = fopen(fname, "wb"); if (!fileGz) return; //write header http://www.gzip.org/zlib/rfc-gzip.html fputc((char)0x1f, fileGz); //ID1 fputc((char)0x8b, fileGz); //ID2 fputc((char)0x08, fileGz); //CM - use deflate compression method fputc((char)0x00, fileGz); //FLG - no addition fields fputc((char)0x00, fileGz); //MTIME0 fputc((char)0x00, fileGz); //MTIME1 fputc((char)0x00, fileGz); //MTIME2 fputc((char)0x00, fileGz); //MTIME2 fputc((char)0x00, fileGz); //XFL fputc((char)0xff, fileGz); //OS //write Z-compressed data fwrite (&pCmp[2] , sizeof(char), cmp_len-6, fileGz); //-6 as LZ78 format has 2 bytes header and 4 bytes tail //write tail: write redundancy check and uncompressed size as bytes to ensure LITTLE-ENDIAN order fputc((unsigned char)(file_crc32), fileGz); fputc((unsigned char)(file_crc32 >> 8), fileGz); fputc((unsigned char)(file_crc32 >> 16), fileGz); fputc((unsigned char)(file_crc32 >> 24), fileGz); fputc((unsigned char)(strm.total_in), fileGz); fputc((unsigned char)(strm.total_in >> 8), fileGz); fputc((unsigned char)(strm.total_in >> 16), fileGz); fputc((unsigned char)(strm.total_in >> 24), fileGz); fclose(fileGz); free(pCmp); } //writeNiiGz() #endif int nii_saveNII(char * niiFilename, struct nifti_1_header hdr, unsigned char* im, struct TDCMopts opts) { hdr.vox_offset = 352; size_t imgsz = nii_ImgBytes(hdr); #ifndef myDisableZLib if ((opts.isGz) && (strlen(opts.pigzname) < 1) && ((imgsz+hdr.vox_offset) < 2147483647) ) { //use internal compressor writeNiiGz (niiFilename, hdr, im,imgsz); return EXIT_SUCCESS; } #endif char fname[2048] = {""}; strcpy (fname,niiFilename); strcat (fname,".nii"); FILE *fp = fopen(fname, "wb"); if (!fp) return EXIT_FAILURE; fwrite(&hdr, sizeof(hdr), 1, fp); uint32_t pad = 0; fwrite(&pad, sizeof( pad), 1, fp); fwrite(&im[0], imgsz, 1, fp); fclose(fp); if ((opts.isGz) && (strlen(opts.pigzname) > 0) ) { char command[768]; strcpy(command, "\"" ); strcat(command, opts.pigzname ); strcat(command, "\" \""); strcat(command, fname); strcat(command, "\""); //add quotes in case spaces in filename 'pigz "c:\my dir\img.nii"' #if defined(_WIN64) || defined(_WIN32) //using CreateProcess instead of system to run in background (avoids screen flicker) PROCESS_INFORMATION ProcessInfo; //This is what we get as an [out] parameter STARTUPINFO StartupInfo; //This is an [in] parameter ZeroMemory(&StartupInfo, sizeof(StartupInfo)); StartupInfo.cb = sizeof StartupInfo ; //Only compulsory field if(CreateProcess(NULL, command, NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL, NULL,&StartupInfo,&ProcessInfo)) { WaitForSingleObject(ProcessInfo.hProcess,INFINITE); CloseHandle(ProcessInfo.hThread); CloseHandle(ProcessInfo.hProcess); } else #ifdef myUseCOut std::cout<<"compression failed "<<command<<std::endl; #else printf("compression failed %s\n",command); #endif #else //if win else linux system(command); #endif //else linux #ifdef myUseCOut std::cout<<"compress: "<<command<<std::endl; #else printf("compress: %s\n",command); #endif } return EXIT_SUCCESS; } //nii_saveNII() int nii_saveNII3D(char * niiFilename, struct nifti_1_header hdr, unsigned char* im, struct TDCMopts opts) { //save 4D series as sequence of 3D volumes struct nifti_1_header hdr1 = hdr; int nVol = 1; for (int i = 4; i < 8; i++) { if (hdr.dim[i] > 1) nVol = nVol * hdr.dim[i]; hdr1.dim[i] = 0; } hdr1.dim[0] = 3; //save as 3D file size_t imgsz = nii_ImgBytes(hdr1); size_t pos = 0; char fname[2048] = {""}; char zeroPad[1024] = {""}; int zeroPadLen = (1 + log10(nVol)); sprintf(zeroPad,"%%s_%%0%dd",zeroPadLen); for (int i = 1; i <= nVol; i++) { sprintf(fname,zeroPad,niiFilename,i); if (nii_saveNII(fname, hdr1, (unsigned char*)&im[pos], opts) == EXIT_FAILURE) return EXIT_FAILURE; pos += imgsz; } return EXIT_SUCCESS; } //nii_saveNII3D void nii_check16bitUnsigned(unsigned char *img, struct nifti_1_header *hdr){ //default NIfTI 16-bit is signed, set to unusual 16-bit unsigned if required... if (hdr->datatype != DT_UINT16) return; int dim3to7 = 1; for (int i = 3; i < 8; i++) if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i]; int nVox = hdr->dim[1]*hdr->dim[2]* dim3to7; if (nVox < 1) return; unsigned short * img16 = (unsigned short*) img; unsigned short max16 = img16[0]; //clock_t start = clock(); for (int i=0; i < nVox; i++) if (img16[i] > max16) max16 = img16[i]; //printf("max16= %d vox=%d %fms\n",max16, nVox, ((double)(clock()-start))/1000); if (max16 > 32767) #ifdef myUseCOut std::cout<<"Note: intensity range requires saving as rare 16-bit UNSIGNED integer. Subsequent tools may require 32-bit conversion"<<std::endl; #else printf("Note: intensity range requires saving as rare 16-bit UNSIGNED integer. Subsequent tools may require 32-bit conversion\n"); #endif else hdr->datatype = DT_INT16; } //nii_check16bitUnsigned() int siemensCtKludge(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[]) { //Siemens CT bug: when a user draws an open object graphics object onto a 2D slice this is appended as an additional image, //regardless of slice position. These images do not report number of positions in the volume, so we need tedious leg work to detect uint64_t indx0 = dcmSort[0].indx; if ((nConvert < 2) ||(dcmList[indx0].manufacturer != kMANUFACTURER_SIEMENS) || (!isSameFloat(dcmList[indx0].TR ,0.0f))) return nConvert; float prevDx = 0.0; for (int i = 1; i < nConvert; i++) { float dx = intersliceDistance(dcmList[indx0],dcmList[dcmSort[i].indx]); if ((!isSameFloat(dx,0.0f)) && (dx < prevDx)) { #ifdef myUseCOut std::cout<<"Slices skipped: image position not sequential, admonish your vendor (Siemens OOG?)"<<std::endl; #else printf("Slices skipped: image position not sequential, admonish your vendor (Siemens OOG?)\n"); #endif return i; } prevDx = dx; } return nConvert; //all images in sequential order } int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[], struct TSearchList *nameList, struct TDCMopts opts) { bool iVaries = intensityScaleVaries(nConvert,dcmSort,dcmList); uint64_t indx = dcmSort[0].indx; uint64_t indx0 = dcmSort[0].indx; bool saveAs3D = dcmList[indx].isHasPhase; struct nifti_1_header hdr0; unsigned char * img = nii_loadImgX(nameList->str[indx], &hdr0,dcmList[indx], iVaries); if (opts.isVerbose) #ifdef myUseCOut std::cout<<"Converting "<<nameList->str[indx]<<std::endl; #else printf("Converting %s\n",nameList->str[indx]); #endif if (img == NULL) return EXIT_FAILURE; //if (iVaries) img = nii_iVaries(img, &hdr0); size_t imgsz = nii_ImgBytes(hdr0); unsigned char *imgM = (unsigned char *)malloc(imgsz* (uint64_t)nConvert); memcpy(&imgM[0], &img[0], imgsz); free(img); /*if ((nConvert < 2) && (dcmList[indx].locationsInAcquisition > 0)) { //stack philips 4D file int nAcq = dcmList[indx].locationsInAcquisition; if ((hdr0.dim[0] < 4) && ((hdr0.dim[3]%nAcq)==0)) { hdr0.dim[4] = hdr0.dim[3]/nAcq; hdr0.dim[3] = nAcq; hdr0.dim[0] = 4; } if (hdr0.dim[0] > 3) { if (dcmList[indx].patientPositionSequentialRepeats > 1) //swizzle 3rd and 4th dimension (Philips stores time as 3rd dimension) imgM = nii_XYTZ_XYZT(imgM, &hdr0,dcmList[indx].patientPositionSequentialRepeats ); } }*/ //printf(" %d %d %d %d %lu\n", hdr0.dim[1], hdr0.dim[2], hdr0.dim[3], hdr0.dim[4], (unsigned long)[imgM length]); if (nConvert > 1) { if (hdr0.dim[3] < 2) { //stack volumes with multiple acquisitions int nAcq = 1; //Next line works in theory, but fails with Siemens CT that saves pairs of slices as acquisitions, see example "testSiemensStackAcq" // nAcq = 1+abs( dcmList[dcmSort[nConvert-1].indx].acquNum-dcmList[indx0].acquNum); //therefore, the 'same position' is the most robust solution in the real world. if ((dcmList[indx0].manufacturer == kMANUFACTURER_SIEMENS) && (isSameFloat(dcmList[indx0].TR ,0.0f))) { nConvert = siemensCtKludge(nConvert, dcmSort,dcmList); } if ((nAcq == 1 ) && (dcmList[indx0].locationsInAcquisition > 0)) nAcq = nConvert/dcmList[indx0].locationsInAcquisition; if (nAcq < 2 ) { nAcq = 0; for (int i = 0; i < nConvert; i++) if (isSamePosition(dcmList[dcmSort[0].indx],dcmList[dcmSort[i].indx])) nAcq++; } /*int nImg = 1+abs( dcmList[dcmSort[nConvert-1].indx].imageNum-dcmList[dcmSort[0].indx].imageNum); if (((nConvert/nAcq) > 1) && ((nConvert%nAcq)==0) && (nImg == nConvert) && (dcmList[dcmSort[0].indx].locationsInAcquisition == 0) ) { printf(" stacking %d acquisitions as a single volume\n", nAcq); //some Siemens CT scans use multiple acquisitions for a single volume, perhaps also check that slice position does not repeat? hdr0.dim[3] = nConvert; } else*/ if ( (nAcq > 1) && ((nConvert/nAcq) > 1) && ((nConvert%nAcq)==0) ) { hdr0.dim[3] = nConvert/nAcq; hdr0.dim[4] = nAcq; hdr0.dim[0] = 4; } else hdr0.dim[3] = nConvert; float dx = intersliceDistance(dcmList[dcmSort[0].indx],dcmList[dcmSort[1].indx]); if ((hdr0.dim[4] > 0) && (dx ==0) && (dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_PHILIPS)) { swapDim3Dim4(hdr0.dim[3],hdr0.dim[4],dcmSort); dx = intersliceDistance(dcmList[dcmSort[0].indx],dcmList[dcmSort[1].indx]); //printf("swizzling 3rd and 4th dimensions (XYTZ -> XYZT), assuming interslice distance is %f\n",dx); } dcmList[dcmSort[0].indx].xyzMM[3] = dx; //16Sept2014 : correct DICOM for true distance between slice centers: // e.g. MCBI Siemens ToF 0018:0088 reports 16mm SpacingBetweenSlices, but actually 0.5mm if (dx > 0) hdr0.pixdim[3] = dx; } else if (hdr0.dim[4] < 2) { hdr0.dim[4] = nConvert; hdr0.dim[0] = 4; } else { hdr0.dim[5] = nConvert; hdr0.dim[0] = 5; } //printf(" %d %d %d %d %lu\n", hdr0.dim[1], hdr0.dim[2], hdr0.dim[3], hdr0.dim[4], (unsigned long)[imgM length]); struct nifti_1_header hdrI; for (int i = 1; i < nConvert; i++) { //stack additional images indx = dcmSort[i].indx; //if (headerDcm2Nii(dcmList[indx], &hdrI) == EXIT_FAILURE) return EXIT_FAILURE; img = nii_loadImgX(nameList->str[indx], &hdrI, dcmList[indx],iVaries); if ((hdr0.dim[1] != hdrI.dim[1]) || (hdr0.dim[2] != hdrI.dim[2]) || (hdr0.bitpix != hdrI.bitpix)) { #ifdef myUseCOut std::cout<<"Error: image dimensions differ "<<nameList->str[dcmSort[0].indx]<<" "<<nameList->str[indx]<<std::endl; #else printf("Error: image dimensions differ %s %s",nameList->str[dcmSort[0].indx], nameList->str[indx]); #endif return EXIT_FAILURE; } memcpy(&imgM[(uint64_t)i*imgsz], &img[0], imgsz); free(img); } } //printf("Mango %zd\n", (imgsz* nConvert)); return 0; char pathoutname[2048] = {""}; if (nii_createFilename(dcmList[dcmSort[0].indx], pathoutname, opts) == EXIT_FAILURE) return EXIT_FAILURE; if (strlen(pathoutname) <1) return EXIT_FAILURE; int sliceDir = 0; if (hdr0.dim[3] > 1) sliceDir =headerDcm2Nii2(dcmList[dcmSort[0].indx],dcmList[dcmSort[nConvert-1].indx] , &hdr0); if (sliceDir < 0) { imgM = nii_flipZ(imgM, &hdr0); sliceDir = abs(sliceDir); } bool isFinalADC = nii_SaveDTI(pathoutname,nConvert, dcmSort, dcmList, opts, sliceDir); isFinalADC =isFinalADC; //simply to silence compiler warning when myNoSave defined if ((hdr0.datatype == DT_UINT16) && (!dcmList[dcmSort[0].indx].isSigned)) nii_check16bitUnsigned(imgM, &hdr0); #ifdef myUseCOut std::cout<<"Convert "<<nConvert<<" DICOM as "<<pathoutname<< " ("<<hdr0.dim[1]<<"x"<<hdr0.dim[2]<<"x"<<hdr0.dim[3]<<"x"<<hdr0.dim[4]<<")" <<std::endl; #else printf( "Convert %d DICOM as %s (%dx%dx%dx%d)\n", nConvert, pathoutname, hdr0.dim[1],hdr0.dim[2],hdr0.dim[3],hdr0.dim[4]); #endif if (hdr0.dim[3] < 2) #ifdef myUseCOut std::cout<<"WARNING: check that 2D images are not mirrored"<<std::endl; #else printf("WARNING: check that 2D images are not mirrored.\n"); #endif else fflush(stdout); //GUI buffers printf, display all results if ((dcmList[dcmSort[0].indx].is3DAcq) && (hdr0.dim[3] > 1) && (hdr0.dim[0] < 4)) imgM = nii_setOrtho(imgM, &hdr0); //printf("ortho %d\n", echoInt (33)); else if (opts.isFlipY)//(FLIP_Y) //(dcmList[indx0].CSA.mosaicSlices < 2) && imgM = nii_flipY(imgM, &hdr0); else #ifdef myUseCOut std::cout<<"DICOM row order preserved: may appear upside down in tools that ignore spatial transforms"<<std::endl; #else printf("DICOM row order preserved: may appear upside down in tools that ignore spatial transforms\n"); #endif #ifndef myNoSave if ((hdr0.dim[4] > 1) && (saveAs3D)) nii_saveNII3D(pathoutname, hdr0, imgM,opts); else { if ((isFinalADC) && (hdr0.dim[4] > 2)) { //ADC maps can disrupt analysis: save a copy with the ADC map, and another without char pathoutnameADC[2048] = {""}; strcat(pathoutnameADC,pathoutname); strcat(pathoutnameADC,"_ADC"); nii_saveNII(pathoutnameADC, hdr0, imgM, opts); hdr0.dim[4] = hdr0.dim[4]-1; }; nii_saveNII(pathoutname, hdr0, imgM, opts); } #endif free(imgM); return EXIT_SUCCESS; } //saveDcm2Nii() int compareTDCMsort(void const *item1, void const *item2) { //for quicksort http://blog.ablepear.com/2011/11/objective-c-tuesdays-sorting-arrays.html struct TDCMsort const *dcm1 = (const struct TDCMsort *)item1; struct TDCMsort const *dcm2 = (const struct TDCMsort *)item2; if (dcm1->img < dcm2->img) return -1; else if (dcm1->img > dcm2->img) return 1; return 0; //tie } //compareTDCMsort() bool isSameSet (struct TDICOMdata d1, struct TDICOMdata d2) { //returns true if d1 and d2 should be stacked together as a signle output if (!d1.isValid) return false; if (!d2.isValid) return false; if ((d1.dateTime == d2.dateTime) && (d1.seriesNum == d2.seriesNum) && (d1.bitsAllocated == d2.bitsAllocated) && (d1.xyzDim[1] == d2.xyzDim[1]) && (d1.xyzDim[2] == d2.xyzDim[2]) && (d1.xyzDim[3] == d2.xyzDim[3]) ) return true; return false; } //isSameSet() void searchDirForDICOM(char *path, struct TSearchList *nameList, int maxDepth, int depth) { tinydir_dir dir; tinydir_open(&dir, path); while (dir.has_next) { tinydir_file file; tinydir_readfile(&dir, &file); //printf("%s\n", file.name); char filename[768] =""; strcat(filename, path); strcat(filename,kFileSep); strcat(filename, file.name); if ((file.is_dir) && (depth < maxDepth) && (file.name[0] != '.')) searchDirForDICOM(filename, nameList, maxDepth, depth+1); else if (isDICOMfile(filename)) { if (nameList->numItems < nameList->maxItems) { nameList->str[nameList->numItems] = (char *)malloc(strlen(filename)+1); strcpy(nameList->str[nameList->numItems],filename); //printf("OK\n"); } nameList->numItems++; //printf("dcm %lu %s \n",nameList->numItems, filename); } else { #ifdef MY_DEBUG #ifdef myUseCOut std::cout<<"Not a dicom"<< filename <<std::endl; #else printf("Not a dicom:\t%s\n", filename); #endif #endif } tinydir_next(&dir); } tinydir_close(&dir); } //searchDirForDICOM() int removeDuplicates(int nConvert, struct TDCMsort dcmSort[]){ //done AFTER sorting, so duplicates will be sequential if (nConvert < 2) return nConvert; int nDuplicates = 0; for (int i = 1; i < nConvert; i++) { if (dcmSort[i].img == dcmSort[i-1].img) nDuplicates ++; else { dcmSort[i-nDuplicates].img = dcmSort[i].img; dcmSort[i-nDuplicates].indx = dcmSort[i].indx; } } if (nDuplicates > 0) #ifdef myUseCOut std::cout<<"Some images have identical time, series, acquisition and image values. Duplicates removed."<<std::endl; #else printf("Some images have identical time, series, acquisition and image values. Duplicates removed.\n"); #endif return nConvert - nDuplicates; } //removeDuplicates() int removeDuplicatesVerbose(int nConvert, struct TDCMsort dcmSort[], struct TSearchList *nameList){ //done AFTER sorting, so duplicates will be sequential if (nConvert < 2) return nConvert; int nDuplicates = 0; for (int i = 1; i < nConvert; i++) { if (dcmSort[i].img == dcmSort[i-1].img) { #ifdef myUseCOut std::cout<<"\t"<<nameList->str[dcmSort[i-1].indx]<<"\t"<<nameList->str[dcmSort[i].indx] <<std::endl; #else printf("\t%s\t%s\n",nameList->str[dcmSort[i-1].indx],nameList->str[dcmSort[i].indx]); #endif nDuplicates ++; }else { dcmSort[i-nDuplicates].img = dcmSort[i].img; dcmSort[i-nDuplicates].indx = dcmSort[i].indx; } } if (nDuplicates > 0) #ifdef myUseCOut std::cout<<"Some images have identical time, series, acquisition and image values. Duplicates removed."<<std::endl; #else printf("Some images have identical time, series, acquisition and image values. Duplicates removed.\n"); #endif return nConvert - nDuplicates; } //removeDuplicates() int strcicmp(char const *a, char const *b) //case insensitive compare { for (;; a++, b++) { int d = tolower(*a) - tolower(*b); if (d != 0 || !*a) return d; } } //strcicmp() bool isExt (char *file_name, const char* ext) { char *p_extension; if((p_extension = strrchr(file_name,'.')) != NULL ) if(strcicmp(p_extension,ext) == 0) return true; //if(strcmp(p_extension,ext) == 0) return true; return false; } //isExt() int convert_parRec(struct TDCMopts opts) { //sample dataset from Ed Gronenschild <ed.gronenschild@maastrichtuniversity.nl> struct TSearchList nameList; nameList.numItems = 1; nameList.maxItems = 1; nameList.str = (char **) malloc((nameList.maxItems+1) * sizeof(char *)); //we reserve one pointer (32 or 64 bits) per potential file struct TDICOMdata *dcmList = (struct TDICOMdata *)malloc(nameList.numItems * sizeof(struct TDICOMdata)); nameList.str[0] = (char *)malloc(strlen(opts.indir)+1); strcpy(nameList.str[0],opts.indir); dcmList[0] = nii_readParRec(nameList.str[0]); struct TDCMsort dcmSort[1]; dcmSort[0].indx = 0; saveDcm2Nii(1, dcmSort, dcmList, &nameList, opts); free(dcmList);//if (nConvertTotal == 0) if (nameList.numItems < 1) { #ifdef myUseCOut std::cout<<"No valid PAR/REC files were found"<<std::endl; #else printf("No valid PAR/REC files were found\n"); #endif } if (nameList.numItems > 0) for (int i = 0; i < nameList.numItems; i++) free(nameList.str[i]); free(nameList.str); return EXIT_SUCCESS; } //convert_parRec() int nii_loadDir (struct TDCMopts* opts) { //printf("-->%s",opts->filename); //return EXIT_FAILURE; if (strlen(opts->indir) < 1) { #ifdef myUseCOut std::cout<<"No input"<<std::endl; #else printf("No input\n"); #endif return EXIT_FAILURE; } #ifdef myUseCOut std::cout << "Version " <<kDCMvers <<std::endl; #else printf("Version %s\n",kDCMvers); #endif char indir[512]; strcpy(indir,opts->indir); bool isFile = is_fileNotDir(opts->indir); if (isFile) {//if user passes ~/dicom/mr1.dcm we will look at all files in ~/dicom dropFilenameFromPath(opts->indir);//getParentFolder(opts.indir, opts.indir); } if (strlen(opts->outdir) < 1) strcpy(opts->outdir,opts->indir); else if (!is_dir(opts->outdir,true)) { #ifdef myUseCOut std::cout << "Warning: output folder invalid "<< opts->outdir<<" will try %s\n"<< opts->indir <<std::endl; #else printf("Warning: output folder invalid %s will try %s\n",opts->outdir,opts->indir); #endif strcpy(opts->outdir,opts->indir); } /*if (isFile && ((isExt(indir, ".gz")) || (isExt(indir, ".tgz"))) ) { #ifndef myDisableTarGz #ifndef myDisableZLib untargz( indir, opts->outdir); #endif #endif }*/ if (isFile && ((isExt(indir, ".par")) || (isExt(indir, ".rec"))) ) { strcpy(opts->indir, indir); //set to original file name, not path return convert_parRec(*opts); } getFileName(opts->indirParent, opts->indir); struct TSearchList nameList; nameList.numItems = 0; nameList.maxItems = 64000-1; nameList.str = (char **) malloc((nameList.maxItems+1) * sizeof(char *)); //we reserve one pointer (32 or 64 bits) per potential file //1: find filenames of dicom files searchDirForDICOM(opts->indir, &nameList, 5,1); if (nameList.numItems < 1) { #ifdef myUseCOut std::cout << "Error: unable to find any DICOM images in "<< opts->indir <<std::endl; #else printf("Error: unable to find any DICOM images in %s\n", opts->indir); #endif free(nameList.str); return EXIT_FAILURE; } if (nameList.numItems < 1) { #ifdef myUseCOut std::cout << "Overwhelmed: found more than "<<nameList.maxItems<<" DICOM images in " << opts->indir <<std::endl; #else printf("Overwhelmed: found more than %lu DICOM images in %s\n",nameList.maxItems, opts->indir); #endif //goto freeMem; return EXIT_FAILURE; } long long nDcm = nameList.numItems; #ifdef myUseCOut std::cout << "Found "<< nameList.numItems <<" DICOM images" <<std::endl; #else printf( "Found %lu DICOM images\n", nameList.numItems); #endif // struct TDICOMdata dcmList [nameList.numItems]; //<- this exhausts the stack for large arrays struct TDICOMdata *dcmList = (struct TDICOMdata *)malloc(nameList.numItems * sizeof(struct TDICOMdata)); for (int i = 0; i < nameList.numItems; i++ ) dcmList[i] = readDICOMv(nameList.str[i], opts->isVerbose); //3: stack DICOMs with the same Series int nConvertTotal = 0; for (int i = 0; i < nDcm; i++ ) { if ((dcmList[i].converted2NII == 0) && (dcmList[i].isValid)) { int nConvert = 0; for (int j = i; j < nDcm; j++) if (isSameSet(dcmList[i],dcmList[j]) ) nConvert ++; struct TDCMsort dcmSort[nConvert]; nConvert = 0; for (int j = i; j < nDcm; j++) if (isSameSet(dcmList[i],dcmList[j]) ) { dcmSort[nConvert].indx = j; dcmSort[nConvert].img = ((uint64_t)dcmList[j].seriesNum << 32)+ dcmList[j].imageNum; dcmList[j].converted2NII = 1; nConvert ++; } qsort(dcmSort, nConvert, sizeof(struct TDCMsort), compareTDCMsort); //sort based on series and image numbers.... if (opts->isVerbose) nConvert = removeDuplicatesVerbose(nConvert, dcmSort, &nameList); else nConvert = removeDuplicates(nConvert, dcmSort); nConvertTotal += nConvert; saveDcm2Nii(nConvert, dcmSort, dcmList, &nameList, *opts); }//convert all images of this series } free(dcmList); if (nConvertTotal == 0) #ifdef myUseCOut std::cout << "No valid DICOM files were found\n" <<std::endl; #else printf("No valid DICOM files were found\n"); #endif if (nameList.numItems > 0) for (int i = 0; i < nameList.numItems; i++) free(nameList.str[i]); free(nameList.str); return EXIT_SUCCESS; } //nii_loadDir() void readFindPigz (struct TDCMopts *opts, const char * argv[]) { #if defined(_WIN64) || defined(_WIN32) strcpy(opts->pigzname,"pigz.exe"); if (!is_exe(opts->pigzname)) { #ifdef myUseCOut #ifdef myDisableZLib std::cout << "Compression requires "<<opts->pigzname<<" in the same folder as the executable"<<std::endl; #else //myUseZLib std::cout << "Compression will be faster with "<<opts->pigzname<<" in the same folder as the executable "<<std::endl; #endif #else #ifdef myDisableZLib printf("Compression requires %s in the same folder as the executable\n",opts->pigzname); #else //myUseZLib printf("Compression will be faster with %s in the same folder as the executable\n",opts->pigzname); #endif #endif strcpy(opts->pigzname,""); } else strcpy(opts->pigzname,".\\pigz"); //drop #else strcpy(opts->pigzname,"/usr/local/bin/pigz"); char pigz[1024]; strcpy(pigz, opts->pigzname); if (!is_exe(opts->pigzname)) { strcpy(opts->pigzname,"/usr/bin/pigz"); if (!is_exe(opts->pigzname)) { strcpy(opts->pigzname,"/usr/local/bin/pigz_mricron"); if (!is_exe(opts->pigzname)) { strcpy(opts->pigzname,argv[0]); dropFilenameFromPath(opts->pigzname);//, opts.pigzname); char appendChar[2] = {"a"}; appendChar[0] = kPathSeparator; if (opts->pigzname[strlen(opts->pigzname)-1] != kPathSeparator) strcat (opts->pigzname,appendChar); strcat(opts->pigzname,"pigz_mricron"); #if defined(_WIN64) || defined(_WIN32) strcat(opts->pigzname,".exe"); #endif if (!is_exe(opts->pigzname)) { #ifdef myUseCOut #ifdef myDisableZLib std::cout << "Compression requires "<<pigz<<std::endl; #else //myUseZLib std::cout << "Compression will be faster with "<<pigz<<std::endl; #endif #else #ifdef myDisableZLib printf("Compression requires %s\n",pigz); #else //myUseZLib printf("Compression will be faster with %s\n",pigz); #endif #endif strcpy(opts->pigzname,""); } //no pigz_mricron in exe's folder } //no /usr/local/pigz_mricron }//no /usr/bin/pigz } //no /usr/local/pigz #endif } //readFindPigz() #if defined(_WIN64) || defined(_WIN32) //windows has unusual file permissions for many users - lets save preferences to the registry void saveIniFile (struct TDCMopts opts) { HKEY hKey; if(RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\dcm2nii",0, KEY_SET_VALUE, &hKey) != ERROR_SUCCESS) { RegCloseKey(hKey); return; } DWORD dwValue = opts.isGz; //RegSetValueEx(hKey,"isGZ", 0, REG_DWORD,reinterpret_cast<BYTE *>(&dwValue),sizeof(dwValue)); //RegSetValueExA(hKey, "isGZ", 0, REG_DWORD, (LPDWORD)&dwValue, sizeof(dwValue)); RegSetValueExA(hKey, "isGZ", 0, REG_DWORD, reinterpret_cast<BYTE *>(&dwValue), sizeof(dwValue)); RegSetValueExA(hKey,"filename",0, REG_SZ,(LPBYTE)opts.filename, strlen(opts.filename)+1); RegCloseKey(hKey); } //saveIniFile() void readIniFile (struct TDCMopts *opts, const char * argv[]) { readFindPigz(opts, argv); strcpy(opts->indir,""); strcpy(opts->outdir,""); opts->isGz = false; opts->isFlipY = true; #ifdef myDebug opts->isVerbose = true; #else opts->isVerbose = false; #endif strcpy(opts->filename,"%f_%p_%t_%s"); HKEY hKey; DWORD vSize = 0; DWORD dwDataType = 0; DWORD dwValue = 0; //RegOpenKeyEx(RegOpenKeyEx, key, 0, accessRights, keyHandle); //if(RegOpenKeyEx(HKEY_CURRENT_USER,(WCHAR)"Software\\dcm2nii", 0, KEY_QUERY_VALUE,&hKey) != ERROR_SUCCESS) { if(RegOpenKeyExA(HKEY_CURRENT_USER,"Software\\dcm2nii", 0, KEY_QUERY_VALUE,&hKey) != ERROR_SUCCESS) { RegCloseKey(hKey); return; } vSize = sizeof(dwValue); //if(RegQueryValueExA(hKey,"isGZ", 0, (LPDWORD )&dwDataType, (&dwValue), &vSize) == ERROR_SUCCESS) if(RegQueryValueExA(hKey,"isGZ", 0, (LPDWORD )&dwDataType, reinterpret_cast<BYTE *>(&dwValue), &vSize) == ERROR_SUCCESS) opts->isGz = dwValue; vSize = 512; char buffer[512]; if(RegQueryValueExA(hKey,"filename", 0,NULL,(LPBYTE)buffer,&vSize ) == ERROR_SUCCESS ) strcpy(opts->filename,buffer); RegCloseKey(hKey); } //readIniFile() #else //for Unix we will save preferences in a hidden text file in the home directory #define STATUSFILENAME "/.dcm2nii.ini" void readIniFile (struct TDCMopts *opts, const char * argv[]) { readFindPigz(opts, argv); sprintf(opts->optsname, "%s%s", getenv("HOME"), STATUSFILENAME); strcpy(opts->indir,""); strcpy(opts->outdir,""); opts->isGz = false; opts->isFlipY = true; //false: images in raw DICOM orientation, true: image rows flipped to cartesian coordinates #ifdef myDebug opts->isVerbose = true; #else opts->isVerbose = false; #endif strcpy(opts->filename,"%f_%p_%t_%s"); FILE *fp = fopen(opts->optsname, "r"); if (fp == NULL) return; char Setting[20],Value[255]; //while ( fscanf(fp, "%[^=]=%s\n", Setting, Value) == 2 ) { //while ( fscanf(fp, "%[^=]=%s\n", Setting, Value) == 2 ) { while ( fscanf(fp, "%[^=]=%[^\n]\n", Setting, Value) == 2 ) { //printf(">%s<->'%s'\n",Setting,Value); if ( strcmp(Setting,"isGZ") == 0 ) opts->isGz = atoi(Value); else if ( strcmp(Setting,"filename") == 0 ) strcpy(opts->filename,Value); } fclose(fp); } //readIniFile() void saveIniFile (struct TDCMopts opts) { FILE *fp = fopen(opts.optsname, "w"); //printf("%s\n",localfilename); if (fp == NULL) return; fprintf(fp, "isGZ=%d\n", opts.isGz); fprintf(fp, "filename=%s\n", opts.filename); fclose(fp); } //saveIniFile() #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/qtGui/nii_ortho.c�������������������������������������������������������������0000664�0000000�0000000�00000031151�13220512030�0017204�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "nifti1.h" #include "nifti1_io_core.h" #include "nii_ortho.h" #include <math.h> #include <stdint.h> #include <stdlib.h> #include <sys/stat.h> #include <stdbool.h> #include <ctype.h> #include <string.h> #include <stddef.h> #include <float.h> #include <unistd.h> #include <stdio.h> //#define MY_DEBUG //verbose text reporting typedef struct { int v[3]; } vec3i; mat33 matDotMul33 (mat33 a, mat33 b) // in Matlab: ret = a'.*b { mat33 ret; for (int i=0; i<3; i++) { for (int j=0; j<3; j++) { ret.m[i][j] = a.m[i][j]*b.m[j][i]; } } return ret; } mat33 matMul33 (mat33 a, mat33 b) // mult = a * b { mat33 mult; for(int i=0;i<3;i++) { for(int j=0;j<3;j++) { mult.m[j][i]=0; for(int k=0;k<3;k++) mult.m[j][i]+=a.m[j][k]*b.m[k][i]; } } return mult; } float getOrthoResidual (mat33 orig, mat33 transform) { mat33 mat = matDotMul33(orig, transform); float ret = 0; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { ret = ret + (mat.m[i][j]); } } return ret; } mat33 getBestOrient(mat44 R, vec3i flipVec) //flipVec reports flip: [1 1 1]=no flips, [-1 1 1] flip X dimension { mat33 ret, newmat, orig; LOAD_MAT33(orig,R.m[0][0],R.m[0][1],R.m[0][2], R.m[1][0],R.m[1][1],R.m[1][2], R.m[2][0],R.m[2][1],R.m[2][2]); float best = 0;//FLT_MAX; float newval; for (int rot = 0; rot < 6; rot++) { //6 rotations switch (rot) { case 0: LOAD_MAT33(newmat,flipVec.v[0],0,0, 0,flipVec.v[1],0, 0,0,flipVec.v[2]); break; case 1: LOAD_MAT33(newmat,flipVec.v[0],0,0, 0,0,flipVec.v[1], 0,flipVec.v[2],0); break; case 2: LOAD_MAT33(newmat,0,flipVec.v[0],0, flipVec.v[1],0,0, 0,0,flipVec.v[2]); break; case 3: LOAD_MAT33(newmat,0,flipVec.v[0],0, 0,0,flipVec.v[1], flipVec.v[2],0,0); break; case 4: LOAD_MAT33(newmat,0,0,flipVec.v[0], flipVec.v[1],0,0, 0,flipVec.v[2],0); break; case 5: LOAD_MAT33(newmat,0,0,flipVec.v[0], 0,flipVec.v[1],0, flipVec.v[2],0,0); break; } newval = getOrthoResidual(orig, newmat); if (newval > best) { best = newval; ret = newmat; } } return ret; } bool isMat44Canonical(mat44 R) //returns true if diagonals >0 and all others =0 // no rotation is necessary - already in perfect orthogonal alignment { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if ((i == j) && (R.m[i][j] <= 0) ) return false; if ((i != j) && (R.m[i][j] != 0) ) return false; }//j }//i return true; } vec3i setOrientVec(mat33 m) // Assumes isOrthoMat NOT computed on INVERSE, hence return INVERSE of solution... //e.g. [-1,2,3] means reflect x axis, [2,1,3] means swap x and y dimensions { vec3i ret = {0, 0, 0}; //mat33 m = {-1,0,0, 0,1,0, 0,0,1}; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (m.m[i][j] > 0) ret.v[j] = i+1; if (m.m[i][j] < 0) ret.v[j] = -(i+1); }//j }//i return ret; } mat44 setMat44Vec(mat33 m33, vec3 Translations) //convert a 3x3 rotation matrix to a 4x4 matrix where the last column stores translations and the last row is 0 0 0 1 { mat44 m44; for (int i=0; i<3; i++) { for (int j=0; j<3; j++) { m44.m[i][j] = m33.m[i][j]; } } m44.m[0][3] = Translations.v[0]; m44.m[1][3] = Translations.v[1]; m44.m[2][3] = Translations.v[2]; m44.m[3][0] = 0; m44.m[3][1] = 0; m44.m[3][2] = 0; m44.m[3][3] = 1; return m44; } mat44 sFormMat(struct nifti_1_header *h) { mat44 s; s.m[0][0]=h->srow_x[0]; s.m[0][1]=h->srow_x[1]; s.m[0][2]=h->srow_x[2]; s.m[0][3]=h->srow_x[3]; s.m[1][0]=h->srow_y[0]; s.m[1][1]=h->srow_y[1]; s.m[1][2]=h->srow_y[2]; s.m[1][3]=h->srow_y[3]; s.m[2][0]=h->srow_z[0]; s.m[2][1]=h->srow_z[1]; s.m[2][2]=h->srow_z[2]; s.m[2][3]=h->srow_z[3]; s.m[3][0] = 0 ; s.m[3][1] = 0 ; s.m[3][2] = 0 ; s.m[3][3] = 1 ; return s; } void mat2sForm (struct nifti_1_header *h, mat44 s) { h->srow_x[0] = s.m[0][0]; h->srow_x[1] = s.m[0][1]; h->srow_x[2] = s.m[0][2]; h->srow_x[3] = s.m[0][3]; h->srow_y[0] = s.m[1][0]; h->srow_y[1] = s.m[1][1]; h->srow_y[2] = s.m[1][2]; h->srow_y[3] = s.m[1][3]; h->srow_z[0] = s.m[2][0]; h->srow_z[1] = s.m[2][1]; h->srow_z[2] = s.m[2][2]; h->srow_z[3] = s.m[2][3]; } size_t* orthoOffsetArray(int dim, int stepBytesPerVox) { //return lookup table of length dim with values incremented by stepBytesPerVox // e.g. if Dim=10 and stepBytes=2: 0,2,4..18, is stepBytes=-2 18,16,14...0 size_t *lut= (size_t *)malloc(dim*sizeof(size_t)); if (stepBytesPerVox > 0) lut[0] = 0; else lut[0] = -stepBytesPerVox *(dim-1); if (dim > 1) for (int i=1; i < dim; i++) lut[i] = lut[i-1] + (size_t)stepBytesPerVox; return lut; } //orthoOffsetArray() //void reOrientImg( unsigned char * restrict img, vec3i outDim, vec3i outInc, int bytePerVox, int nvol) { void reOrientImg( unsigned char * img, vec3i outDim, vec3i outInc, int bytePerVox, int nvol) { //reslice data to new orientation //generate look up tables size_t* xLUT =orthoOffsetArray(outDim.v[0], bytePerVox*outInc.v[0]); size_t* yLUT =orthoOffsetArray(outDim.v[1], bytePerVox*outInc.v[1]); size_t* zLUT =orthoOffsetArray(outDim.v[2], bytePerVox*outInc.v[2]); //convert data size_t bytePerVol = bytePerVox*outDim.v[0]*outDim.v[1]*outDim.v[2]; //number of voxels in spatial dimensions [1,2,3] size_t o = 0; //output address uint8_t *inbuf = (uint8_t *) malloc(bytePerVol); //we convert 1 volume at a time uint8_t *outbuf = (uint8_t *) img; //source image for (int vol= 0; vol < nvol; vol++) { memcpy(&inbuf[0], &outbuf[vol*bytePerVol], bytePerVol); //copy source volume for (int z = 0; z < outDim.v[2]; z++) for (int y = 0; y < outDim.v[1]; y++) for (int x = 0; x < outDim.v[0]; x++) { memcpy(&outbuf[o], &inbuf[xLUT[x]+yLUT[y]+zLUT[z]], bytePerVox); o = o+ bytePerVox; } //for each x } //for each volume //free arrays free(inbuf); free(xLUT); free(yLUT); free(zLUT); } //reOrientImg unsigned char * reOrient(unsigned char* img, struct nifti_1_header *h, vec3i orientVec, mat33 orient, vec3 minMM) //e.g. [-1,2,3] means reflect x axis, [2,1,3] means swap x and y dimensions { size_t nvox = h->dim[1] * h->dim[2] * h->dim[3]; if (nvox < 1) return img; vec3i outDim= {0,0,0}; vec3i outInc= {0,0,0}; for (int i = 0; i < 3; i++) { //set dimension, pixdim and outDim.v[i] = h->dim[abs(orientVec.v[i])]; if (abs(orientVec.v[i]) == 1) outInc.v[i] = 1; if (abs(orientVec.v[i]) == 2) outInc.v[i] = h->dim[1]; if (abs(orientVec.v[i]) == 3) outInc.v[i] = h->dim[1]*h->dim[2]; if (orientVec.v[i] < 0) outInc.v[i] = -outInc.v[i]; //flip } //for each dimension int nvol = 1; //convert all non-spatial volumes from source to destination for (int vol = 4; vol < 8; vol++) { if (h->dim[vol] > 1) nvol = nvol * h->dim[vol]; } reOrientImg(img, outDim, outInc, h->bitpix / 8, nvol); //now change the header.... vec3 outPix= {h->pixdim[abs(orientVec.v[0])],h->pixdim[abs(orientVec.v[1])],h->pixdim[abs(orientVec.v[2])]}; for (int i = 0; i < 3; i++) { h->dim[i+1] = outDim.v[i]; h->pixdim[i+1] = outPix.v[i]; } mat44 s = sFormMat(h); mat33 mat; //computer transform LOAD_MAT33(mat, s.m[0][0],s.m[0][1],s.m[0][2], s.m[1][0],s.m[1][1],s.m[1][2], s.m[2][0],s.m[2][1],s.m[2][2]); mat = matMul33( mat, orient); s = setMat44Vec(mat, minMM); //add offset mat2sForm(h,s); h->qform_code = h->sform_code; //apply to the quaternion as well float dumdx, dumdy, dumdz; nifti_mat44_to_quatern( s , &h->quatern_b, &h->quatern_c, &h->quatern_d,&h->qoffset_x, &h->qoffset_y, &h->qoffset_z, &dumdx, &dumdy, &dumdz,&h->pixdim[0]) ; return img; } //reOrient() float getDistance (vec3 v, vec3 min) //scalar distance between two 3D points - Pythagorean theorem { return sqrt(pow((v.v[0]-min.v[0]),2)+pow((v.v[1]-min.v[1]),2)+pow((v.v[2]-min.v[2]),2) ); } vec3 xyz2mm (mat44 R, vec3 v) { vec3 ret; for (int i = 0; i < 3; i++) { ret.v[i] = ( (R.m[i][0]*v.v[0])+(R.m[i][1]*v.v[1])+ (R.m[i][2]*v.v[2])+R.m[i][3] ); } return ret; } vec3 minCornerFlip (struct nifti_1_header *h, vec3i* flipVec) //orthogonal rotations and reflections applied as 3x3 matrices will cause the origin to shift // a simple solution is to first compute the most left, posterior, inferior voxel in the source image // this voxel will be at location i,j,k = 0,0,0, so we can simply use this as the offset for the final 4x4 matrix... { int i,j, minIndex; vec3i flipVecs[8]; vec3 corner[8], min; mat44 s = sFormMat(h); for (int i = 0; i < 8; i++) { if (i & 1) flipVecs[i].v[0] = -1; else flipVecs[i].v[0] = 1; if (i & 2) flipVecs[i].v[1] = -1; else flipVecs[i].v[1] = 1; if (i & 4) flipVecs[i].v[2] = -1; else flipVecs[i].v[2] = 1; corner[i] = setVec3(0,0,0); //assume no reflections if ((flipVecs[i].v[0]) < 1) corner[i].v[0] = h->dim[1]-1; //reflect X if ((flipVecs[i].v[1]) < 1) corner[i].v[1] = h->dim[2]-1; //reflect Y if ((flipVecs[i].v[2]) < 1) corner[i].v[2] = h->dim[3]-1; //reflect Z corner[i] = xyz2mm(s,corner[i]); } //find extreme edge from ALL corners.... min = corner[0]; for (i = 1; i < 8; i++) { for (j = 0; j < 3; j++) { if (corner[i].v[j]< min.v[j]) min.v[j] = corner[i].v[j]; } } float dx; //observed distance from corner float min_dx = getDistance (corner[0], min); minIndex = 0; //index of corner closest to min //see if any corner is closer to absmin than the first one... for (i = 1; i < 8; i++) { dx = getDistance (corner[i], min); if (dx < min_dx) { min_dx = dx; minIndex = i; } } min = corner[minIndex]; //this is the single corner closest to min from all *flipVec= flipVecs[minIndex]; return min; } #ifdef MY_DEBUG void reportMat44o(char *str, mat44 A) { printf("%s = [%g %g %g %g; %g %g %g %g; %g %g %g %g; 0 0 0 1]\n",str, A.m[0][0],A.m[0][1],A.m[0][2],A.m[0][3], A.m[1][0],A.m[1][1],A.m[1][2],A.m[1][3], A.m[2][0],A.m[2][1],A.m[2][2],A.m[2][3]); } #endif unsigned char * nii_setOrtho(unsigned char* img, struct nifti_1_header *h) { if ((h->dim[0] != 3) || (h->dim[1] < 1) || (h->dim[2] < 1) || (h->dim[3] < 1)) return img; if ((h->sform_code == NIFTI_XFORM_UNKNOWN) && (h->qform_code != NIFTI_XFORM_UNKNOWN)) { //only q-form provided mat44 q = nifti_quatern_to_mat44(h->quatern_b, h->quatern_c, h->quatern_d, h->qoffset_x, h->qoffset_y, h->qoffset_z, h->pixdim[1], h->pixdim[2], h->pixdim[3],h->pixdim[0]); mat2sForm(h,q); //convert q-form to s-form h->sform_code = h->qform_code; } if (h->sform_code == NIFTI_XFORM_UNKNOWN) { #ifdef MY_DEBUG printf("No Q or S spatial transforms - assuming canonical orientation"); #endif return img; } mat44 s = sFormMat(h); if (isMat44Canonical( s)) { #ifdef MY_DEBUG printf("Image in perfect alignment: no need to reorient"); #endif return img; } vec3i flipV; vec3 minMM = minCornerFlip(h, &flipV); mat33 orient = getBestOrient(s, flipV); vec3i orientVec = setOrientVec(orient); if ((orientVec.v[0]==1) && (orientVec.v[1]==2) && (orientVec.v[2]==3) ) { #ifdef MY_DEBUG printf("Image already near best orthogonal alignment: no need to reorient\n"); #endif return img; } bool is24 = false; if (h->bitpix == 24 ) { //RGB stored as planar data. treat as 3 8-bit slices return img; is24 = true; h->bitpix = 8; h->dim[3] = h->dim[3] * 3; } img = reOrient(img, h,orientVec, orient, minMM); if (is24 ) { h->bitpix = 24; h->dim[3] = h->dim[3] / 3; } #ifdef MY_DEBUG printf("NewRotation= %d %d %d\n", orientVec.v[0],orientVec.v[1],orientVec.v[2]); printf("MinCorner= %.2f %.2f %.2f\n", minMM.v[0],minMM.v[1],minMM.v[2]); reportMat44o("input",s); s = sFormMat(h); reportMat44o("output",s); #endif return img; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/qtGui/notes.txt���������������������������������������������������������������0000664�0000000�0000000�00000000122�13220512030�0016731�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/Users/rorden/QT/5.2.1/clang_64/bin/macdeployqt /Users/rorden/dcm2/deploy/dcm2.app����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/rmfiles.command���������������������������������������������������������������0000775�0000000�0000000�00000000476�13220512030�0016767�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh #remove duplicates cd /Users/rorden/Documents/cocoa/dcm2niix rm ./qtGui/ni*.cpp rm ./qtGui/ni*.h rm ./qtGui/tinydir.h rm ./wxWidgets/ni*.cpp rm ./wxWidgets/ni*.h rm ./wxWidgets/tinydir.h rm ./xcode/dcm2/core/*.c rm ./xcode/dcm2/core/*.cpp rm ./xcode/dcm2/core/*.h rm ./xcode/dcm2/core/tinydir.h ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/wxWidgets/��������������������������������������������������������������������0000775�0000000�0000000�00000000000�13220512030�0015741�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/wxWidgets/clipboard.cpp�������������������������������������������������������0000664�0000000�0000000�00000022024�13220512030�0020404�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������///////////////////////////////////////////////////////////////////////////// // Name: dcm.cpp // Purpose: wxWidgets wrapper for dcm2niix // Author: Chris Rorden // Copyright: (c) Chris Rorden // Licence: BSD licence ///////////////////////////////////////////////////////////////////////////// #include "wx/wxprec.h" #include <wx/stdpaths.h> #include "nii_dicom_batch.h" #include "wx/clipbrd.h" #include <iostream> #ifdef __BORLANDC__ #pragma hdrstop #endif // for all others, include the necessary headers (this file is usually all you // need because it includes almost all "standard" wxWidgets headers) #ifndef WX_PRECOMP #include "wx/wx.h" #endif #ifndef wxHAS_IMAGES_IN_RESOURCES #include "../sample.xpm" #endif class MyApp : public wxApp { public: virtual bool OnInit(); }; class MyFrame : public wxFrame { public: MyFrame(const wxString& title); void OnQuit(wxCommandEvent&event); void OnAbout(wxCommandEvent&event); void OnSetOutputFolder(wxCommandEvent&event); void OnDicom(wxCommandEvent&event); void OnParRec(wxCommandEvent&event); void OnCompressCheck(wxCommandEvent&event); void OnCopy(wxCommandEvent&event); void OnFileName(wxCommandEvent&event); void OnShowPrefs(); void OnShowExampleFilename(); void OnDropFiles(wxDropFilesEvent&event); void OnClose(wxCloseEvent& event); //void OnProcessFile: (char * fname); void OnProcessFile (char * fname); private: wxTextCtrl *m_textctrl; wxCheckBox *m_compressCheck; wxTextCtrl *m_fileName; wxButton *m_outputFolder; struct TDCMopts opts; bool applicationBusy; DECLARE_EVENT_TABLE() }; enum { ID_Quit = wxID_EXIT, ID_About = wxID_ABOUT, ID_OutputFolder = 100, ID_Text = 101, ID_CompressCheck = 102, ID_DicomMenu = 103, ID_ParRecMenu = 104, ID_CopyMenu = 105, ID_FileName = 106 }; BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(ID_Quit, MyFrame::OnQuit) EVT_MENU(ID_About, MyFrame::OnAbout) EVT_MENU(ID_DicomMenu, MyFrame::OnDicom) EVT_MENU(ID_ParRecMenu, MyFrame::OnParRec) EVT_MENU(ID_CopyMenu, MyFrame::OnCopy) EVT_BUTTON(ID_OutputFolder, MyFrame::OnSetOutputFolder) EVT_CHECKBOX(ID_CompressCheck, MyFrame::OnCompressCheck) EVT_TEXT(ID_FileName, MyFrame::OnFileName) EVT_CLOSE(MyFrame::OnClose) END_EVENT_TABLE() IMPLEMENT_APP(MyApp) void MyFrame::OnClose(wxCloseEvent& event) { saveIniFile (opts); //save preferences Destroy(); } bool MyApp::OnInit() { if ( !wxApp::OnInit() ) return false; MyFrame *frame = new MyFrame("dcm2"); frame->SetClientSize(720, 440); frame->SetMinSize(wxSize(640, 340)); frame->Show(true); return true; } MyFrame::MyFrame(const wxString& title) : wxFrame(NULL, wxID_ANY, title) { // set the frame icon this->applicationBusy = true; SetIcon(wxICON(sample)); //&Open\tCtrl+O wxMenu *fileMenu = new wxMenu; fileMenu->Append(ID_DicomMenu, wxT("&DICOM to NIfTI...\tCtrl-D")); fileMenu->Append(ID_ParRecMenu, wxT("&PAR/REC to NIfTI...\tCtrl-P")); fileMenu->Append(ID_Quit, "E&xit\tAlt-X", "Quit this program"); wxMenu *editMenu = new wxMenu; editMenu->Append(ID_CopyMenu, "&Copy\tCtrl-C", "Copy text to clipboard"); wxMenuBar *menuBar = new wxMenuBar(); #ifdef __WXMAC__ #define kTextLabelBorder 2 editMenu->Append(ID_About, "&About", "Show about dialog"); #else #define kTextLabelBorder 6 wxMenu *helpMenu = new wxMenu; helpMenu->Append(ID_About, "&About", "Show about dialog"); menuBar->Append(helpMenu, "Help"); #endif menuBar->Append(fileMenu, "File"); menuBar->Append(editMenu, " Edit"); SetMenuBar(menuBar); wxPanel *panel = new wxPanel( this, -1 ); wxBoxSizer *main_sizer = new wxBoxSizer( wxVERTICAL ); wxBoxSizer *hbox1 = new wxBoxSizer(wxHORIZONTAL); hbox1->Add( new wxStaticText( panel, wxID_ANY, "Compress" ), 0, wxEXPAND | wxTOP, kTextLabelBorder ); m_compressCheck = new wxCheckBox( panel, ID_CompressCheck, "" ); hbox1->Add( m_compressCheck, 0, wxBottom, 20); hbox1->Add( new wxStaticText( panel, wxID_ANY, " Output name" ), 0, wxEXPAND| wxTOP, kTextLabelBorder ); m_fileName = new wxTextCtrl( panel, ID_FileName, "%f%s" ); hbox1->Add( m_fileName, 1, wxEXPAND, 5 ); hbox1->Add( new wxStaticText( panel, wxID_ANY, " Output folder" ), 0, wxEXPAND| wxTOP, kTextLabelBorder ); m_outputFolder = new wxButton( panel, ID_OutputFolder, " Input folder" ); hbox1->Add(m_outputFolder , 1, wxEXPAND, 0); main_sizer->Add(hbox1, 0, wxEXPAND| wxALL, 4); m_textctrl = new wxTextCtrl( panel, ID_Text, "", wxDefaultPosition,wxDefaultSize, wxTE_READONLY | wxTE_MULTILINE ); main_sizer->Add( m_textctrl, 1, wxGROW ); panel->SetSizer( main_sizer ); wxString appPath = wxStandardPaths::Get().GetExecutablePath(); const char *appPathC = appPath.mb_str(wxConvUTF8); readIniFile (&opts, &appPathC); //redirect http://docs.wxwidgets.org/trunk/classwx_text_ctrl.html //std::streambuf *sbOld = std::cout.rdbuf(); std::cout.rdbuf(m_textctrl); //redirect until... std::cout.rdbuf(sbOld); m_textctrl->DragAcceptFiles(true); m_textctrl->Connect(wxEVT_DROP_FILES, wxDropFilesEventHandler(MyFrame::OnDropFiles), NULL, this); this->OnShowPrefs(); this->applicationBusy = false; } void MyFrame::OnProcessFile (char * fname) { m_textctrl->Clear(); struct TDCMopts optsTemp; optsTemp = opts; //conversion may change values like the outdir (if not specified) strcpy(optsTemp.indir, fname); clock_t start = clock(); nii_loadDir (&(optsTemp)); //printf("required %fms\n", ((double)(clock()-start))/1000); std::cout << "required " <<((double)(clock()-start))/1000 <<"ms" <<std::endl; fflush(stdout); //GUI buffers printf, display all results } void MyFrame::OnDropFiles(wxDropFilesEvent& event) { if (event.GetNumberOfFiles() < 1) return; wxString* dropped = event.GetFiles(); wxString name =dropped[0]; char fname[1024]; strncpy(fname, (const char*)name.mb_str(wxConvUTF8), 1023); OnProcessFile (fname); } void MyFrame::OnShowExampleFilename() { char niiFilename[1024]; nii_createDummyFilename(niiFilename, opts); m_textctrl->Clear(); wxString mystring = wxString::Format(wxT("%s\nVersion %s\n"),niiFilename,kDCMvers); m_textctrl->AppendText(mystring); } void MyFrame::OnShowPrefs() { applicationBusy = true; m_compressCheck->SetValue(opts.isGz) ; m_fileName->Clear(); m_fileName->AppendText(opts.filename); //char cstring[1024]; //strcpy(cstring,opts.outdir); //wxString outdir = wxString::FromUTF8(cstring); wxString outdir = wxString::FromUTF8(opts.outdir); if (outdir.length() < 1) m_outputFolder->SetLabel("Input folder"); else { if (outdir.Length() > 40) { outdir.Truncate(40); outdir.Append("..."); } m_outputFolder->SetLabel(outdir);; } this->OnShowExampleFilename(); applicationBusy = false; } void MyFrame::OnFileName(wxCommandEvent& WXUNUSED(event)) { //only respond to inputs from the user, http://wxwidgets.10942.n7.nabble.com/wxTextCtrl-constructor-generates-EVT-TEXT-td24562.html if (applicationBusy) return; strncpy(opts.filename, (const char*)m_fileName->GetValue().mb_str(wxConvUTF8), 1023); this->OnShowExampleFilename(); } void MyFrame::OnCompressCheck(wxCommandEvent& WXUNUSED(event)) { opts.isGz = m_compressCheck->GetValue(); this->OnShowExampleFilename(); } void MyFrame::OnParRec(wxCommandEvent& WXUNUSED(event)) { wxFileDialog* fileDialog = new wxFileDialog( this, _("Choose a file to covert"), wxEmptyString, wxEmptyString, _("PAR/REC Image (*.par)||*.par;*.PAR"), wxFD_OPEN, wxDefaultPosition); if (fileDialog->ShowModal() == wxID_OK) { //wxString file = fileDialog->GetPath(); //wxMessageBox(file); char fname[1024]; strncpy(fname, (const char*)fileDialog->GetPath().mb_str(wxConvUTF8), 1023); OnProcessFile (fname); } fileDialog->Destroy(); } void MyFrame::OnDicom(wxCommandEvent& WXUNUSED(event)) { wxString pth = wxT(""); wxDirDialog dirDialog(this, wxT("Select the DICOM folder"),pth, wxDD_NEW_DIR_BUTTON); if (dirDialog.ShowModal() != wxID_OK) return; //wxString dir = dirDialog.GetPath(); char fname[1024]; strncpy(fname, (const char*)dirDialog.GetPath().mb_str(wxConvUTF8), 1023); OnProcessFile (fname); //wxMessageBox(dir); } void MyFrame::OnCopy(wxCommandEvent& WXUNUSED(event)) { m_textctrl->SelectAll(); m_textctrl->Copy(); m_textctrl->SetSelection(m_textctrl->XYToPosition(0,0),m_textctrl->XYToPosition(0,0)); } void MyFrame::OnSetOutputFolder(wxCommandEvent& WXUNUSED(event)) { wxString pth = wxT(""); wxDirDialog dialog(this, wxT("Select out folder (or cancel to use input folder)"),pth, wxDD_NEW_DIR_BUTTON); if (dialog.ShowModal() != wxID_OK) return; wxString dir = dialog.GetPath(); strncpy(opts.outdir, (const char*)dialog.GetPath().mb_str(wxConvUTF8), 1023); this->OnShowPrefs(); } void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event)) { Close(true); } void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event)) { wxMessageBox("Chris Rorden :: www.mricro.com","dcm2nii", wxOK, this); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/wxWidgets/nifti1_io_core.c����������������������������������������������������0000664�0000000�0000000�00000041300�13220512030�0020774�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "nifti1_io_core.h" #include <math.h> #include <stdlib.h> #include <sys/stat.h> #include <stdbool.h> #include <ctype.h> #include <string.h> #include <stddef.h> #include <float.h> #include <unistd.h> #include <stdio.h> void nifti_swap_8bytes( size_t n , void *ar ) // 4 bytes at a time { register size_t ii ; unsigned char * cp0 = (unsigned char *)ar, * cp1, * cp2 ; register unsigned char tval ; for( ii=0 ; ii < n ; ii++ ){ cp1 = cp0; cp2 = cp0+7; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp1++; cp2--; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp1++; cp2--; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp1++; cp2--; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp0 += 8; } return ; } void nifti_swap_4bytes( size_t n , void *ar ) // 4 bytes at a time { register size_t ii ; unsigned char * cp0 = (unsigned char *)ar, * cp1, * cp2 ; register unsigned char tval ; for( ii=0 ; ii < n ; ii++ ){ cp1 = cp0; cp2 = cp0+3; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp1++; cp2--; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp0 += 4; } return ; } void nifti_swap_2bytes( size_t n , void *ar ) // 2 bytes at a time { register size_t ii ; unsigned char * cp1 = (unsigned char *)ar, * cp2 ; unsigned char tval; for( ii=0 ; ii < n ; ii++ ){ cp2 = cp1 + 1; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp1 += 2; } return ; } int isSameFloat (float a, float b) { return (fabs (a - b) <= FLT_EPSILON); } vec3 setVec3(float x, float y, float z) { vec3 v = {x, y, z}; return v; } vec4 setVec4(float x, float y, float z) { vec4 v= {x, y, z, 1}; return v; } vec3 crossProduct(vec3 u, vec3 v) { return setVec3(u.v[1]*v.v[2] - v.v[1]*u.v[2], -u.v[0]*v.v[2] + v.v[0]*u.v[2], u.v[0]*v.v[1] - v.v[0]*u.v[1]); } float dotProduct(vec3 u, vec3 v) { return (u.v[0]*v.v[0] + v.v[1]*u.v[1] + v.v[2]*u.v[2]); } vec3 nifti_vect33_norm (vec3 v) { //normalize vector length vec3 vO = v; float vLen = sqrt( (v.v[0]*v.v[0]) + (v.v[1]*v.v[1]) + (v.v[2]*v.v[2])); if (vLen <= FLT_EPSILON) return vO; //avoid divide by zero for (int i = 0; i < 3; i++) vO.v[i] = v.v[i]/vLen; return vO; } vec3 nifti_vect33mat33_mul(vec3 v, mat33 m ) { //multiply vector * 3x3matrix vec3 vO; for (int i=0; i<3; i++) { //multiply Pcrs * m vO.v[i] = 0; for(int j=0; j<3; j++) vO.v[i] += m.m[i][j]*v.v[j]; } return vO; } vec4 nifti_vect44mat44_mul(vec4 v, mat44 m ) { //multiply vector * 4x4matrix vec4 vO; for (int i=0; i<4; i++) { //multiply Pcrs * m vO.v[i] = 0; for(int j=0; j<4; j++) vO.v[i] += m.m[i][j]*v.v[j]; } return vO; } mat44 nifti_dicom2mat(float orient[7], float patientPosition[4], float xyzMM[4]) { //create NIfTI header based on values from DICOM header //note orient has 6 values, indexed from 1, patient position and xyzMM have 3 values indexed from 1 mat33 Q, diagVox; Q.m[0][0] = orient[1]; Q.m[0][1] = orient[2] ; Q.m[0][2] = orient[3] ; // load Q Q.m[1][0] = orient[4]; Q.m[1][1] = orient[5] ; Q.m[1][2] = orient[6]; //printf("Orient %g %g %g %g %g %g\n",orient[1],orient[2],orient[3],orient[4],orient[5],orient[6] ); /* normalize row 1 */ double val = Q.m[0][0]*Q.m[0][0] + Q.m[0][1]*Q.m[0][1] + Q.m[0][2]*Q.m[0][2] ; if( val > 0.0l ){ val = 1.0l / sqrt(val) ; Q.m[0][0] *= (float)val ; Q.m[0][1] *= (float)val ; Q.m[0][2] *= (float)val ; } else { Q.m[0][0] = 1.0l ; Q.m[0][1] = 0.0l ; Q.m[0][2] = 0.0l ; } /* normalize row 2 */ val = Q.m[1][0]*Q.m[1][0] + Q.m[1][1]*Q.m[1][1] + Q.m[1][2]*Q.m[1][2] ; if( val > 0.0l ){ val = 1.0l / sqrt(val) ; Q.m[1][0] *= (float)val ; Q.m[1][1] *= (float)val ; Q.m[1][2] *= (float)val ; } else { Q.m[1][0] = 0.0l ; Q.m[1][1] = 1.0l ; Q.m[1][2] = 0.0l ; } /* row 3 is the cross product of rows 1 and 2*/ Q.m[2][0] = Q.m[0][1]*Q.m[1][2] - Q.m[0][2]*Q.m[1][1] ; /* cross */ Q.m[2][1] = Q.m[0][2]*Q.m[1][0] - Q.m[0][0]*Q.m[1][2] ; /* product */ Q.m[2][2] = Q.m[0][0]*Q.m[1][1] - Q.m[0][1]*Q.m[1][0] ; Q = nifti_mat33_transpose(Q); if (nifti_mat33_determ(Q) < 0.0) { Q.m[0][2] = -Q.m[0][2]; Q.m[1][2] = -Q.m[1][2]; Q.m[2][2] = -Q.m[2][2]; } //next scale matrix LOAD_MAT33(diagVox, xyzMM[1],0.0l,0.0l, 0.0l,xyzMM[2],0.0l, 0.0l,0.0l, xyzMM[3]); Q = nifti_mat33_mul(Q,diagVox); mat44 Q44; //4x4 matrix includes translations LOAD_MAT44(Q44, Q.m[0][0],Q.m[0][1],Q.m[0][2],patientPosition[1], Q.m[1][0],Q.m[1][1],Q.m[1][2],patientPosition[2], Q.m[2][0],Q.m[2][1],Q.m[2][2],patientPosition[3]); return Q44; } float nifti_mat33_determ( mat33 R ) /* determinant of 3x3 matrix */ { double r11,r12,r13,r21,r22,r23,r31,r32,r33 ; /* INPUT MATRIX: */ r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2]; /* [ r11 r12 r13 ] */ r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2]; /* [ r21 r22 r23 ] */ r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2]; /* [ r31 r32 r33 ] */ return (float)(r11*r22*r33-r11*r32*r23-r21*r12*r33 +r21*r32*r13+r31*r12*r23-r31*r22*r13) ; } mat33 nifti_mat33_mul( mat33 A , mat33 B ) /* multiply 2 3x3 matrices */ //see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c { mat33 C ; int i,j ; for( i=0 ; i < 3 ; i++ ) for( j=0 ; j < 3 ; j++ ) C.m[i][j] = A.m[i][0] * B.m[0][j] + A.m[i][1] * B.m[1][j] + A.m[i][2] * B.m[2][j] ; return C ; } mat44 nifti_mat44_mul( mat44 A , mat44 B ) /* multiply 2 3x3 matrices */ { mat44 C ; int i,j ; for( i=0 ; i < 4 ; i++ ) for( j=0 ; j < 4; j++ ) C.m[i][j] = A.m[i][0] * B.m[0][j] + A.m[i][1] * B.m[1][j] + A.m[i][2] * B.m[2][j] + A.m[i][3] * B.m[3][j]; return C ; } mat33 nifti_mat33_transpose( mat33 A ) /* transpose 3x3 matrix */ //see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c { mat33 B; int i,j ; for( i=0 ; i < 3 ; i++ ) for( j=0 ; j < 3 ; j++ ) B.m[i][j] = A.m[j][i]; return B; } mat33 nifti_mat33_inverse( mat33 R ) /* inverse of 3x3 matrix */ { double r11,r12,r13,r21,r22,r23,r31,r32,r33 , deti ; mat33 Q ; // INPUT MATRIX: r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2]; // [ r11 r12 r13 ] r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2]; // [ r21 r22 r23 ] r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2]; // [ r31 r32 r33 ] deti = r11*r22*r33-r11*r32*r23-r21*r12*r33 +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; if( deti != 0.0l ) deti = 1.0l / deti ; Q.m[0][0] = deti*( r22*r33-r32*r23) ; Q.m[0][1] = deti*(-r12*r33+r32*r13) ; Q.m[0][2] = deti*( r12*r23-r22*r13) ; Q.m[1][0] = deti*(-r21*r33+r31*r23) ; Q.m[1][1] = deti*( r11*r33-r31*r13) ; Q.m[1][2] = deti*(-r11*r23+r21*r13) ; Q.m[2][0] = deti*( r21*r32-r31*r22) ; Q.m[2][1] = deti*(-r11*r32+r31*r12) ; Q.m[2][2] = deti*( r11*r22-r21*r12) ; return Q ; } float nifti_mat33_rownorm( mat33 A ) // max row norm of 3x3 matrix { float r1,r2,r3 ; r1 = fabs(A.m[0][0])+fabs(A.m[0][1])+fabs(A.m[0][2]) ; r2 = fabs(A.m[1][0])+fabs(A.m[1][1])+fabs(A.m[1][2]) ; r3 = fabs(A.m[2][0])+fabs(A.m[2][1])+fabs(A.m[2][2]) ; if( r1 < r2 ) r1 = r2 ; if( r1 < r3 ) r1 = r3 ; return r1 ; } float nifti_mat33_colnorm( mat33 A ) // max column norm of 3x3 matrix { float r1,r2,r3 ; r1 = fabs(A.m[0][0])+fabs(A.m[1][0])+fabs(A.m[2][0]) ; r2 = fabs(A.m[0][1])+fabs(A.m[1][1])+fabs(A.m[2][1]) ; r3 = fabs(A.m[0][2])+fabs(A.m[1][2])+fabs(A.m[2][2]) ; if( r1 < r2 ) r1 = r2 ; if( r1 < r3 ) r1 = r3 ; return r1 ; } mat33 nifti_mat33_polar( mat33 A ) { mat33 X , Y , Z ; float alp,bet,gam,gmi , dif=1.0 ; int k=0 ; X = A ; // force matrix to be nonsingular gam = nifti_mat33_determ(X) ; while( gam == 0.0 ){ // perturb matrix gam = 0.00001 * ( 0.001 + nifti_mat33_rownorm(X) ) ; X.m[0][0] += gam ; X.m[1][1] += gam ; X.m[2][2] += gam ; gam = nifti_mat33_determ(X) ; } while(1){ Y = nifti_mat33_inverse(X) ; if( dif > 0.3 ){ // far from convergence alp = sqrt( nifti_mat33_rownorm(X) * nifti_mat33_colnorm(X) ) ; bet = sqrt( nifti_mat33_rownorm(Y) * nifti_mat33_colnorm(Y) ) ; gam = sqrt( bet / alp ) ; gmi = 1.0 / gam ; } else gam = gmi = 1.0 ; // close to convergence Z.m[0][0] = 0.5 * ( gam*X.m[0][0] + gmi*Y.m[0][0] ) ; Z.m[0][1] = 0.5 * ( gam*X.m[0][1] + gmi*Y.m[1][0] ) ; Z.m[0][2] = 0.5 * ( gam*X.m[0][2] + gmi*Y.m[2][0] ) ; Z.m[1][0] = 0.5 * ( gam*X.m[1][0] + gmi*Y.m[0][1] ) ; Z.m[1][1] = 0.5 * ( gam*X.m[1][1] + gmi*Y.m[1][1] ) ; Z.m[1][2] = 0.5 * ( gam*X.m[1][2] + gmi*Y.m[2][1] ) ; Z.m[2][0] = 0.5 * ( gam*X.m[2][0] + gmi*Y.m[0][2] ) ; Z.m[2][1] = 0.5 * ( gam*X.m[2][1] + gmi*Y.m[1][2] ) ; Z.m[2][2] = 0.5 * ( gam*X.m[2][2] + gmi*Y.m[2][2] ) ; dif = fabs(Z.m[0][0]-X.m[0][0])+fabs(Z.m[0][1]-X.m[0][1]) +fabs(Z.m[0][2]-X.m[0][2])+fabs(Z.m[1][0]-X.m[1][0]) +fabs(Z.m[1][1]-X.m[1][1])+fabs(Z.m[1][2]-X.m[1][2]) +fabs(Z.m[2][0]-X.m[2][0])+fabs(Z.m[2][1]-X.m[2][1]) +fabs(Z.m[2][2]-X.m[2][2]) ; k = k+1 ; if( k > 100 || dif < 3.e-6 ) break ; // convergence or exhaustion X = Z ; } return Z ; } void nifti_mat44_to_quatern( mat44 R , float *qb, float *qc, float *qd, float *qx, float *qy, float *qz, float *dx, float *dy, float *dz, float *qfac ) { double r11,r12,r13 , r21,r22,r23 , r31,r32,r33 ; double xd,yd,zd , a,b,c,d ; mat33 P,Q ; // offset outputs are read write out of input matrix ASSIF(qx,R.m[0][3]) ; ASSIF(qy,R.m[1][3]) ; ASSIF(qz,R.m[2][3]) ; // load 3x3 matrix into local variables */ r11 = R.m[0][0] ; r12 = R.m[0][1] ; r13 = R.m[0][2] ; r21 = R.m[1][0] ; r22 = R.m[1][1] ; r23 = R.m[1][2] ; r31 = R.m[2][0] ; r32 = R.m[2][1] ; r33 = R.m[2][2] ; // compute lengths of each column; these determine grid spacings xd = sqrt( r11*r11 + r21*r21 + r31*r31 ) ; yd = sqrt( r12*r12 + r22*r22 + r32*r32 ) ; zd = sqrt( r13*r13 + r23*r23 + r33*r33 ) ; // if a column length is zero, patch the trouble if( xd == 0.0l ){ r11 = 1.0l ; r21 = r31 = 0.0l ; xd = 1.0l ; } if( yd == 0.0l ){ r22 = 1.0l ; r12 = r32 = 0.0l ; yd = 1.0l ; } if( zd == 0.0l ){ r33 = 1.0l ; r13 = r23 = 0.0l ; zd = 1.0l ; } // assign the output lengths */ ASSIF(dx,xd) ; ASSIF(dy,yd) ; ASSIF(dz,zd) ; // normalize the columns */ r11 /= xd ; r21 /= xd ; r31 /= xd ; r12 /= yd ; r22 /= yd ; r32 /= yd ; r13 /= zd ; r23 /= zd ; r33 /= zd ; /* At this point, the matrix has normal columns, but we have to allow for the fact that the hideous user may not have given us a matrix with orthogonal columns. So, now find the orthogonal matrix closest to the current matrix. One reason for using the polar decomposition to get this orthogonal matrix, rather than just directly orthogonalizing the columns, is so that inputting the inverse matrix to R will result in the inverse orthogonal matrix at this point. If we just orthogonalized the columns, this wouldn't necessarily hold. */ Q.m[0][0] = r11 ; Q.m[0][1] = r12 ; Q.m[0][2] = r13 ; // load Q Q.m[1][0] = r21 ; Q.m[1][1] = r22 ; Q.m[1][2] = r23 ; Q.m[2][0] = r31 ; Q.m[2][1] = r32 ; Q.m[2][2] = r33 ; P = nifti_mat33_polar(Q) ; // P is orthog matrix closest to Q r11 = P.m[0][0] ; r12 = P.m[0][1] ; r13 = P.m[0][2] ; // unload r21 = P.m[1][0] ; r22 = P.m[1][1] ; r23 = P.m[1][2] ; r31 = P.m[2][0] ; r32 = P.m[2][1] ; r33 = P.m[2][2] ; // [ r11 r12 r13 ] // at this point, the matrix [ r21 r22 r23 ] is orthogonal // [ r31 r32 r33 ] // compute the determinant to determine if it is proper zd = r11*r22*r33-r11*r32*r23-r21*r12*r33 +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; // should be -1 or 1 if( zd > 0 ){ // proper ASSIF(qfac,1.0) ; } else { // improper ==> flip 3rd column ASSIF(qfac,-1.0) ; r13 = -r13 ; r23 = -r23 ; r33 = -r33 ; } // now, compute quaternion parameters a = r11 + r22 + r33 + 1.0l ; if( a > 0.5l ){ // simplest case a = 0.5l * sqrt(a) ; b = 0.25l * (r32-r23) / a ; c = 0.25l * (r13-r31) / a ; d = 0.25l * (r21-r12) / a ; } else { // trickier case xd = 1.0 + r11 - (r22+r33) ; // 4*b*b yd = 1.0 + r22 - (r11+r33) ; // 4*c*c zd = 1.0 + r33 - (r11+r22) ; // 4*d*d if( xd > 1.0 ){ b = 0.5l * sqrt(xd) ; c = 0.25l* (r12+r21) / b ; d = 0.25l* (r13+r31) / b ; a = 0.25l* (r32-r23) / b ; } else if( yd > 1.0 ){ c = 0.5l * sqrt(yd) ; b = 0.25l* (r12+r21) / c ; d = 0.25l* (r23+r32) / c ; a = 0.25l* (r13-r31) / c ; } else { d = 0.5l * sqrt(zd) ; b = 0.25l* (r13+r31) / d ; c = 0.25l* (r23+r32) / d ; a = 0.25l* (r21-r12) / d ; } // if( a < 0.0l ){ b=-b ; c=-c ; d=-d; a=-a; } if( a < 0.0l ){ b=-b ; c=-c ; d=-d; } //a discarded... } ASSIF(qb,b) ; ASSIF(qc,c) ; ASSIF(qd,d) ; return ; } mat44 nifti_quatern_to_mat44( float qb, float qc, float qd, float qx, float qy, float qz, float dx, float dy, float dz, float qfac ) { mat44 R ; double a,b=qb,c=qc,d=qd , xd,yd,zd ; /* last row is always [ 0 0 0 1 ] */ R.m[3][0]=R.m[3][1]=R.m[3][2] = 0.0f ; R.m[3][3]= 1.0f ; /* compute a parameter from b,c,d */ a = 1.0l - (b*b + c*c + d*d) ; if( a < 1.e-7l ){ /* special case */ a = 1.0l / sqrt(b*b+c*c+d*d) ; b *= a ; c *= a ; d *= a ; /* normalize (b,c,d) vector */ a = 0.0l ; /* a = 0 ==> 180 degree rotation */ } else{ a = sqrt(a) ; /* angle = 2*arccos(a) */ } /* load rotation matrix, including scaling factors for voxel sizes */ xd = (dx > 0.0) ? dx : 1.0l ; /* make sure are positive */ yd = (dy > 0.0) ? dy : 1.0l ; zd = (dz > 0.0) ? dz : 1.0l ; if( qfac < 0.0 ) zd = -zd ; /* left handedness? */ R.m[0][0] = (float)( (a*a+b*b-c*c-d*d) * xd) ; R.m[0][1] = 2.0l * (b*c-a*d ) * yd ; R.m[0][2] = 2.0l * (b*d+a*c ) * zd ; R.m[1][0] = 2.0l * (b*c+a*d ) * xd ; R.m[1][1] = (float)( (a*a+c*c-b*b-d*d) * yd) ; R.m[1][2] = 2.0l * (c*d-a*b ) * zd ; R.m[2][0] = 2.0l * (b*d-a*c ) * xd ; R.m[2][1] = 2.0l * (c*d+a*b ) * yd ; R.m[2][2] = (float)( (a*a+d*d-c*c-b*b) * zd) ; /* load offsets */ R.m[0][3] = qx ; R.m[1][3] = qy ; R.m[2][3] = qz ; return R ; } mat44 nifti_mat44_inverse( mat44 R ) { double r11,r12,r13,r21,r22,r23,r31,r32,r33,v1,v2,v3 , deti ; mat44 Q ; /* INPUT MATRIX IS: */ r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2]; // [ r11 r12 r13 v1 ] r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2]; // [ r21 r22 r23 v2 ] r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2]; // [ r31 r32 r33 v3 ] v1 = R.m[0][3]; v2 = R.m[1][3]; v3 = R.m[2][3]; // [ 0 0 0 1 ] deti = r11*r22*r33-r11*r32*r23-r21*r12*r33 +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; if( deti != 0.0l ) deti = 1.0l / deti ; Q.m[0][0] = deti*( r22*r33-r32*r23) ; Q.m[0][1] = deti*(-r12*r33+r32*r13) ; Q.m[0][2] = deti*( r12*r23-r22*r13) ; Q.m[0][3] = deti*(-r12*r23*v3+r12*v2*r33+r22*r13*v3 -r22*v1*r33-r32*r13*v2+r32*v1*r23) ; Q.m[1][0] = deti*(-r21*r33+r31*r23) ; Q.m[1][1] = deti*( r11*r33-r31*r13) ; Q.m[1][2] = deti*(-r11*r23+r21*r13) ; Q.m[1][3] = deti*( r11*r23*v3-r11*v2*r33-r21*r13*v3 +r21*v1*r33+r31*r13*v2-r31*v1*r23) ; Q.m[2][0] = deti*( r21*r32-r31*r22) ; Q.m[2][1] = deti*(-r11*r32+r31*r12) ; Q.m[2][2] = deti*( r11*r22-r21*r12) ; Q.m[2][3] = deti*(-r11*r22*v3+r11*r32*v2+r21*r12*v3 -r21*r32*v1-r31*r12*v2+r31*r22*v1) ; Q.m[3][0] = Q.m[3][1] = Q.m[3][2] = 0.0l ; Q.m[3][3] = (deti == 0.0l) ? 0.0l : 1.0l ; // failure flag if deti == 0 return Q ; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/wxWidgets/nii_dicom.c���������������������������������������������������������0000664�0000000�0000000�00000246643�13220512030�0020056�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������//#define MY_DEBUG #include "nifti1.h" #include "nii_dicom.h" #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <ctype.h> //toupper #include <math.h> #include <string.h> #include <stddef.h> #include <unistd.h> #include <float.h> #include <stdint.h> #include "nifti1_io_core.h" #ifdef myUseCOut #include <iostream> #endif #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifdef MY_DEBUG float deFuzz(float v) { if (fabs(v) < 0.00001) return 0; else return v; } void reportMat33(char *str, mat33 A) { printf("%s = [%g %g %g ; %g %g %g; %g %g %g ]\n",str, deFuzz(A.m[0][0]),deFuzz(A.m[0][1]),deFuzz(A.m[0][2]), deFuzz(A.m[1][0]),deFuzz(A.m[1][1]),deFuzz(A.m[1][2]), deFuzz(A.m[2][0]),deFuzz(A.m[2][1]),deFuzz(A.m[2][2])); } void reportMat44(char *str, mat44 A) { printf("%s = [%g %g %g %g; %g %g %g %g; %g %g %g %g; 0 0 0 1]\n",str, deFuzz(A.m[0][0]),deFuzz(A.m[0][1]),deFuzz(A.m[0][2]),deFuzz(A.m[0][3]), deFuzz(A.m[1][0]),deFuzz(A.m[1][1]),deFuzz(A.m[1][2]),deFuzz(A.m[1][3]), deFuzz(A.m[2][0]),deFuzz(A.m[2][1]),deFuzz(A.m[2][2]),deFuzz(A.m[2][3])); } #endif int verify_slice_dir (struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, mat44 *R){ //returns slice direction: 1=sag,2=coronal,3=axial, -= flipped if (h->dim[3] < 2) return 0; //don't care direction for single slice int iSL = 1; //find Z-slice direction: row with highest magnitude of 3rd column if ( (fabs(R->m[1][2]) >= fabs(R->m[0][2])) && (fabs(R->m[1][2]) >= fabs(R->m[2][2]))) iSL = 2; // if ( (fabs(R->m[2][2]) >= fabs(R->m[0][2])) && (fabs(R->m[2][2]) >= fabs(R->m[1][2]))) iSL = 3; //axial acquisition float pos = NAN; if ( !isnan(d2.patientPosition[iSL]) ) { //patient position fields exist pos = d2.patientPosition[iSL]; if (isSameFloat(pos, d.patientPosition[iSL])) pos = NAN; #ifdef MY_DEBUG if (!isnan(pos)) printf("position determined using lastFile %f\n",pos); #endif } if (isnan(pos) &&( !isnan(d.patientPositionLast[iSL]) ) ) { //patient position fields exist pos = d.patientPositionLast[iSL]; if (isSameFloat(pos, d.patientPosition[iSL])) pos = NAN; #ifdef MY_DEBUG if (!isnan(pos)) printf("position determined using last (4d) %f\n",pos); #endif } if (isnan(pos) && ( !isnan(d.stackOffcentre[iSL])) ) pos = d.stackOffcentre[iSL]; if (isnan(pos) && ( !isnan(d.lastScanLoc)) ) pos = d.lastScanLoc; vec4 x; x.v[0] = 0; x.v[1] = 0; x.v[2]=h->dim[3]-1; x.v[3] = 1; vec4 pos1v = nifti_vect44mat44_mul(x, *R); float pos1 = pos1v.v[iSL-1];//-1 as C indexed from 0 bool flip = false; if (!isnan(pos)) // we have real SliceLocation for last slice or volume center flip = (pos > R->m[iSL-1][3]) != (pos1 > R->m[iSL-1][3]); // same direction?, note C indices from 0 else {// we do some guess work and warn user #ifdef myUseCOut std::cout<<"WARNING: Unable to determine slice direction: please check whether slices are flipped" <<std::endl; #else printf("WARNING: Unable to determine slice direction: please check whether slices are flipped\n"); #endif } if (flip) { for (int i = 0; i < 4; i++) R->m[i][2] = -R->m[i][2]; } #ifdef MY_DEBUG printf("verify slice dir %d %d %d\n",h->dim[1],h->dim[2],h->dim[3]); reportMat44("Rout",*R); printf("iSL = %d\n",iSL); printf(" pos1 = %f\n",pos1); #endif if (flip) return -iSL; else return iSL; } //verify_slice_dir() void setQSForm(struct nifti_1_header *h, mat44 Q44) { h->sform_code = NIFTI_XFORM_SCANNER_ANAT; h->srow_x[0] = Q44.m[0][0]; h->srow_x[1] = Q44.m[0][1]; h->srow_x[2] = Q44.m[0][2]; h->srow_x[3] = Q44.m[0][3]; h->srow_y[0] = Q44.m[1][0]; h->srow_y[1] = Q44.m[1][1]; h->srow_y[2] = Q44.m[1][2]; h->srow_y[3] = Q44.m[1][3]; h->srow_z[0] = Q44.m[2][0]; h->srow_z[1] = Q44.m[2][1]; h->srow_z[2] = Q44.m[2][2]; h->srow_z[3] = Q44.m[2][3]; float dumdx, dumdy, dumdz; nifti_mat44_to_quatern( Q44 , &h->quatern_b, &h->quatern_c, &h->quatern_d,&h->qoffset_x, &h->qoffset_y, &h->qoffset_z, &dumdx, &dumdy, &dumdz,&h->pixdim[0]) ; h->qform_code = NIFTI_XFORM_SCANNER_ANAT; } //setQSForm() int headerDcm2NiiSForm(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h) { //fill header s and q form //see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c //returns sliceDirection: 0=unknown,1=sag,2=coro,3=axial,-=reversed slices // int sliceDir = 0; if (h->dim[3] < 2) return sliceDir; //don't care direction for single slice h->sform_code = NIFTI_XFORM_UNKNOWN; h->qform_code = NIFTI_XFORM_UNKNOWN; bool isOK = false; for (int i = 1; i <= 6; i++) if (d.orient[i] != 0.0) isOK = true; if (!isOK) return sliceDir; mat44 Q44 = nifti_dicom2mat(d.orient, d.patientPosition, d.xyzMM); if (d.CSA.mosaicSlices > 1) { double nRowCol = ceil(sqrt(d.CSA.mosaicSlices)); double lFactorX = (d.xyzDim[1] -(d.xyzDim[1]/nRowCol) )/2.0; double lFactorY = (d.xyzDim[2] -(d.xyzDim[2]/nRowCol) )/2.0; Q44.m[0][3] =(Q44.m[0][0]*lFactorX)+(Q44.m[0][1]*lFactorY)+Q44.m[0][3]; Q44.m[1][3] =(Q44.m[1][0]*lFactorX)+(Q44.m[1][1]*lFactorY)+Q44.m[1][3]; Q44.m[2][3] =(Q44.m[2][0]*lFactorX)+(Q44.m[2][1]*lFactorY)+Q44.m[2][3]; /* #ifdef obsolete_mosaic_flip double val = d.xyzDim[2]/nRowCol; //obsolete!!! //Q44 now equals 'dicom_to_patient' in spm_dicom_convert mat44 patient_to_tal, analyze_to_dicom; LOAD_MAT44(patient_to_tal, -1.0l,0.0l,0.0l,0.0l, 0.0l,-1.0l,0.0l,0.0l, 0.0l,0.0l,1.0l,0.0l); LOAD_MAT44(analyze_to_dicom, 1.0l,0.0l,0.0l,-1.0l, 0.0l,-1.0l,0.0l,val, 0.0l,0.0l,1.0l,-1.0l); Q44 = nifti_mat44_mul(patient_to_tal,Q44); Q44 = nifti_mat44_mul(Q44,analyze_to_dicom); //Q44 now equals 'mat' in spm_dicom_convert //subasgn.m in SPM5 translates by one voxel... LOAD_MAT44(analyze_to_dicom, 1.0l,0.0l,0.0l,1.0l, 0.0l,1.0l,0.0l,1.0l, 0.0l,0.0l,1.0l,1.0l); Q44 = nifti_mat44_mul(Q44,analyze_to_dicom); #else */ for (int c=0; c<2; c++) for (int r=0; r<4; r++) Q44.m[c][r] = -Q44.m[c][r]; // #endif mat33 Q; LOAD_MAT33(Q, d.orient[1], d.orient[4],d.CSA.sliceNormV[1], d.orient[2],d.orient[5],d.CSA.sliceNormV[2], d.orient[3],d.orient[6],d.CSA.sliceNormV[3]); if (nifti_mat33_determ(Q) < 0) { //Siemens sagittal are R>>L, whereas NIfTI is L>>R, we retain Siemens order on disk so ascending is still ascending, but we need to have the spatial transform reflect this. mat44 det; sliceDir = kSliceOrientMosaicNegativeDeterminant; //we need to handle DTI vectors accordingly LOAD_MAT44(det, 1.0l,0.0l,0.0l,0.0l, 0.0l,1.0l,0.0l,0.0l, 0.0l,0.0l,-1.0l,0.0l); //patient_to_tal.m[2][3] = 1-d.CSA.MosaicSlices; Q44 = nifti_mat44_mul(Q44,det); } } else { //not a mosaic sliceDir = verify_slice_dir(d, d2, h, &Q44); for (int c=0; c<4; c++)// LPS to nifti RAS, xform matrix before reorient for (int r=0; r<2; r++) //swap rows 1 & 2 Q44.m[r][c] = - Q44.m[r][c]; #ifdef MY_DEBUG reportMat44("final",Q44); #endif } setQSForm(h,Q44); return sliceDir; } //headerDcm2NiiSForm() int headerDcm2Nii2(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h) { //final pass after de-mosaic char txt[1024] = {""}; if (h->slice_code == NIFTI_SLICE_UNKNOWN) h->slice_code = d.CSA.sliceOrder; if (h->slice_code == NIFTI_SLICE_UNKNOWN) h->slice_code = d2.CSA.sliceOrder; //sometimes the first slice order is screwed up https://github.com/eauerbach/CMRR-MB/issues/29 sprintf(txt, "TE=%.2g;Time=%.3f", d.TE,d.acquisitionTime);// d.dateTime); if (d.CSA.phaseEncodingDirectionPositive > 0) { char dtxt[1024] = {""}; sprintf(dtxt, ";phase+=%d", d.CSA.phaseEncodingDirectionPositive); strcat(txt,dtxt); } if ((d.CSA.bandwidthPerPixelPhaseEncode > 0) && ((d.phaseEncodingRC =='C') || (d.phaseEncodingRC =='R'))) { float dwellTime = 0; if (d.phaseEncodingRC =='C') dwellTime = 1000/d.CSA.bandwidthPerPixelPhaseEncode/h->dim[2]; else dwellTime = 1000/d.CSA.bandwidthPerPixelPhaseEncode/h->dim[1]; char dtxt[1024] = {""}; sprintf(dtxt, ";dwell=%.3f", dwellTime); strcat(txt,dtxt); } //printf(" Description= '%s' [length=%lu]\n",txt, strlen(txt)); //x strlcpy(h->descrip,txt,80); snprintf(h->descrip,80, "%s",txt); if (strlen(d.imageComments) > 0) snprintf(h->aux_file,24,"%s",d.imageComments); return headerDcm2NiiSForm(d,d2, h); } //headerDcm2Nii2() int dcmStrLen (int len) { if (len < kDICOMStr) return len+1; else return kDICOMStr; } //dcmStrLen() struct TDICOMdata clear_dicom_data() { struct TDICOMdata d; d.locationsInAcquisition = 0; for (int i=0; i < 4; i++) { for (int n=0; n < kMaxDTIv; n++) d.CSA.dtiV[n][i] = 0; d.patientPosition[i] = NAN; //d.patientPosition2nd[i] = NAN; //used to distinguish XYZT vs XYTZ for Philips 4D d.patientPositionLast[i] = NAN; //used to compute slice direction for Philips 4D d.stackOffcentre[i] = NAN; d.angulation[i] = 0.0f; d.xyzMM[i] = 1; } d.CSA.numDti = 0; for (int i=0; i < 5; i++) d.xyzDim[i] = 1; for (int i = 0; i < 7; i++) d.orient[i] = 0.0f; d.patientPositionSequentialRepeats = 0;//d.isHasMixed = false; d.isHasPhase = false; d.isHasMagnitude = false; d.sliceOrient = kSliceOrientUnknown; //strcpy(d.sliceOrient,"\n"); strcpy(d.patientName, "John_Doe"); strcpy(d.patientID, "ID123"); strcpy(d.imageComments, "imgComments"); strcpy(d.studyDate, "1/1/1977"); strcpy(d.studyTime, "11:11:11"); d.dateTime = (double)19770703150928.0; d.acquisitionTime = 0.0f; strcpy(d.protocolName, "MPRAGE"); d.manufacturer = kMANUFACTURER_UNKNOWN; d.isPlanarRGB = false; d.lastScanLoc = NAN; d.TR = 0; d.TE = 0; //d.locationsInAcquisition = 0; d.numberOfDynamicScans = 0; d.imageNum = 0; d.intenScale = 1; d.intenIntercept = 0; d.seriesNum = 1; d.acquNum = 0; d.imageNum = 1; d.imageStart = 0; d.is3DAcq = false; //e.g. MP-RAGE, SPACE, TFE d.bitsAllocated = 16;//bits d.bitsStored = 0; d.samplesPerPixel = 1; d.isValid = false; d.isSigned = false; //default is unsigned! d.isExplicitVR = true; d.isLittleEndian = true; //DICOM initially always little endian d.converted2NII = 0; d.phaseEncodingRC = '?'; d.CSA.bandwidthPerPixelPhaseEncode = 0.0; d.CSA.mosaicSlices = 0; d.CSA.sliceOrder = NIFTI_SLICE_UNKNOWN; d.CSA.protocolSliceNumber1 = 0; d.CSA.phaseEncodingDirectionPositive = -1; //unknown return d; } //clear_dicom_data() void dcmStrDigitsOnly(char* lStr) { //e.g. change "H11" to " 11" size_t len = strlen(lStr); if (len < 1) return; for (int i = 0; i < len; i++) if (!isdigit(lStr[i]) ) lStr[i] = ' '; } void dcmStr(int lLength, unsigned char lBuffer[], char* lOut) { //char test[] = " 1 2 3 "; //lLength = (int)strlen(test); if (lLength < 1) return; char cString[lLength+1]; cString[lLength] =0; memcpy(cString, (char*)&lBuffer[0], lLength); //memcpy(cString, test, lLength); for (int i = 0; i < lLength; i++) if (cString[i]==' ') cString[i] = '_'; int len = 1; for (int i = 1; i < lLength; i++) { //remove repeated "_" if ((cString[i-1]!='_') || (cString[i]!='_')) { cString[len] =cString[i]; len++; } } //for each item if (cString[len-1] == '_') len--; //while ((len > 0) && (cString[len]=='_')) len--; //remove trailing '_' cString[len] = 0; //null-terminate, strlcpy does this anyway len = dcmStrLen(len); //strlcpy(lOut,cString,len); memcpy(lOut,cString,len-1); lOut[len-1] = 0; //printf(">>%s<<>>%s<<\n",cString,lOut); //lOut[len-1]='z'; //printf(">>%s<<>>%s<<\n",cString,lOut); //strlcpy(lOut,cString,len); } //dcmStr() float dcmFloat(int lByteLength, unsigned char lBuffer[], bool littleEndian) {//read binary 32-bit float //http://stackoverflow.com/questions/2782725/converting-float-values-from-big-endian-to-little-endian #ifdef __BIG_ENDIAN__ bool swap = littleEndian; #else bool swap = !littleEndian; #endif float retVal; memcpy(&retVal, (char*)&lBuffer[0], 4); if (!swap) return retVal; char *floatToConvert = ( char* ) & lBuffer; char *returnFloat = ( char* ) & retVal; //swap the bytes into a temporary buffer returnFloat[0] = floatToConvert[3]; returnFloat[1] = floatToConvert[2]; returnFloat[2] = floatToConvert[1]; returnFloat[3] = floatToConvert[0]; //printf("swapped val = %f\n",retVal); return retVal; } //dcmFloat() int dcmInt (int lByteLength, unsigned char lBuffer[], bool littleEndian) { //read binary 16 or 32 bit integer if (littleEndian) { if (lByteLength <= 3) return lBuffer[0] | (lBuffer[1]<<8); //shortint vs word? return lBuffer[0]+(lBuffer[1]<<8)+(lBuffer[2]<<16)+(lBuffer[3]<<24); //shortint vs word? } if (lByteLength <= 3) return lBuffer[1] | (lBuffer[0]<<8); //shortint vs word? return lBuffer[3]+(lBuffer[2]<<8)+(lBuffer[1]<<16)+(lBuffer[0]<<24); //shortint vs word? } //dcmInt() int dcmStrInt (int lByteLength, unsigned char lBuffer[]) {//read float stored as a string char cString[lByteLength+1]; cString[lByteLength] =0; memcpy(cString, (char*)&lBuffer[0], lByteLength); //printf(" --> *%s* %s%s\n",cString, &lBuffer[0],&lBuffer[1]); return atoi(cString); } //dcmStrInt() int dcmStrManufacturer (int lByteLength, unsigned char lBuffer[]) {//read float stored as a string if (lByteLength < 2) return kMANUFACTURER_UNKNOWN; char cString[lByteLength+1]; cString[lByteLength] =0; memcpy(cString, (char*)&lBuffer[0], lByteLength); //printf("MANU %s\n",cString); if ((toupper(cString[0])== 'S') && (toupper(cString[1])== 'I')) return kMANUFACTURER_SIEMENS; if ((toupper(cString[0])== 'G') && (toupper(cString[1])== 'E')) return kMANUFACTURER_GE; if ((toupper(cString[0])== 'P') && (toupper(cString[1])== 'H')) return kMANUFACTURER_PHILIPS; return kMANUFACTURER_UNKNOWN; } //dcmStrManufacturer typedef struct __attribute__((packed)) { char name[64]; //null-terminated int32_t vm; char vr[4]; // possibly nul-term string int32_t syngodt;// ?? int32_t nitems;// number of items in CSA int32_t xx;// maybe == 77 or 205 } TCSAtag; //Siemens csa tag structure typedef struct __attribute__((packed)) { int32_t xx1, xx2_Len, xx3_77, xx4; } TCSAitem; //Siemens csa item structure float csaMultiFloat (unsigned char buff[], int nItems, float Floats[], int *ItemsOK) { //warning: lFloats indexed from 1! will fill lFloats[1]..[nFloats] //if lnItems == 1, returns first item, if lnItems > 1 returns index of final successful conversion TCSAitem itemCSA; *ItemsOK = 0; if (nItems < 1) return 0.0f; int lPos = 0; for (int lI = 1; lI <= nItems; lI++) { memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA)); lPos +=sizeof(itemCSA); if (itemCSA.xx2_Len > 0) { char cString[itemCSA.xx2_Len]; memcpy(&cString, &buff[lPos], sizeof(cString)); lPos += ((itemCSA.xx2_Len +3)/4)*4; //printf(" %d item length %d = %s\n",lI, itemCSA.xx2_Len, cString); Floats[lI] = atof(cString); *ItemsOK = lI; //some sequences have store empty items } } //for each item return Floats[1]; } //csaMultiFloat() int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, bool isVerbose) { //see also http://afni.nimh.nih.gov/pub/dist/src/siemens_dicom_csa.c //printf("%c%c%c%c\n",buff[0],buff[1],buff[2],buff[3]); if (lLength < 36) return EXIT_FAILURE; if ((buff[0] != 'S') || (buff[1] != 'V') || (buff[2] != '1') || (buff[3] != '0') ) return EXIT_FAILURE; int lPos = 8; //skip 8 bytes of data, 'SV10' plus 2 32-bit values unused1 and unused2 int lnTag = buff[lPos]+(buff[lPos+1]<<8)+(buff[lPos+2]<<16)+(buff[lPos+3]<<24); if (buff[lPos+4] != 77) return EXIT_FAILURE; lPos += 8; //skip 8 bytes of data, 32-bit lnTag plus 77 00 00 0 TCSAtag tagCSA; TCSAitem itemCSA; int itemsOK; float lFloats[7]; for (int lT = 1; lT <= lnTag; lT++) { memcpy(&tagCSA, &buff[lPos], sizeof(tagCSA)); //read tag lPos +=sizeof(tagCSA); //printf("%d CSA of %s %d\n",lPos, tagCSA.name, tagCSA.nitems); if (tagCSA.nitems > 0) { if (strcmp(tagCSA.name, "NumberOfImagesInMosaic") == 0) CSA->mosaicSlices = round(csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK)); else if (strcmp(tagCSA.name, "B_value") == 0) { CSA->dtiV[0][0] = csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK); CSA->numDti = 1; //triggered by b-value, as B0 images do not have DiffusionGradientDirection tag } else if ((strcmp(tagCSA.name, "DiffusionGradientDirection") == 0) && (tagCSA.nitems > 2)){ CSA->dtiV[0][1] = csaMultiFloat (&buff[lPos], 3,lFloats, &itemsOK); CSA->dtiV[0][2] = lFloats[2]; CSA->dtiV[0][3] = lFloats[3]; if (isVerbose) printf("DiffusionGradientDirection %f %f %f\n",lFloats[1],lFloats[2],lFloats[3]); } else if ((strcmp(tagCSA.name, "SliceNormalVector") == 0) && (tagCSA.nitems > 2)){ CSA->sliceNormV[1] = csaMultiFloat (&buff[lPos], 3,lFloats, &itemsOK); CSA->sliceNormV[2] = lFloats[2]; CSA->sliceNormV[3] = lFloats[3]; if (isVerbose) printf("SliceNormalVector %f %f %f\n",CSA->sliceNormV[1],CSA->sliceNormV[2],CSA->sliceNormV[3]); } else if (strcmp(tagCSA.name, "SliceMeasurementDuration") == 0) CSA->sliceMeasurementDuration = csaMultiFloat (&buff[lPos], 3,lFloats, &itemsOK); else if (strcmp(tagCSA.name, "BandwidthPerPixelPhaseEncode") == 0) CSA->bandwidthPerPixelPhaseEncode = csaMultiFloat (&buff[lPos], 3,lFloats, &itemsOK); else if ((strcmp(tagCSA.name, "MosaicRefAcqTimes") == 0) && (tagCSA.nitems > 3) ){ float sliceTimes[tagCSA.nitems+1]; csaMultiFloat (&buff[lPos], tagCSA.nitems,sliceTimes, &itemsOK); float minTimeIndex, minTimeValue, timeValue1; CSA->multiBandFactor = 1; timeValue1 = sliceTimes[1]; minTimeIndex = 1; minTimeValue= sliceTimes[1]; for (int z = 2; z <= itemsOK; z++) { //find index and value of fastest time if (sliceTimes[z] < minTimeValue) { minTimeValue = sliceTimes[z]; minTimeIndex = z; } if (sliceTimes[z] == timeValue1) CSA->multiBandFactor++; } if (minTimeIndex == 2) CSA->sliceOrder = NIFTI_SLICE_ALT_INC2;// e.g. 3,1,4,2 else if (minTimeIndex == (itemsOK-1)) CSA->sliceOrder = NIFTI_SLICE_ALT_DEC2;// e.g. 4,3,2,1 else if ((minTimeIndex == 1) && (sliceTimes[2] < sliceTimes[3])) CSA->sliceOrder = NIFTI_SLICE_SEQ_INC; else if ((minTimeIndex == 1) && (sliceTimes[2] > sliceTimes[3])) CSA->sliceOrder = NIFTI_SLICE_ALT_INC; else if ((minTimeIndex == itemsOK) && (sliceTimes[itemsOK-1] < sliceTimes[itemsOK])) CSA->sliceOrder = NIFTI_SLICE_SEQ_DEC; else if ((minTimeIndex == itemsOK) && (sliceTimes[itemsOK-1] > sliceTimes[itemsOK-2])) CSA->sliceOrder = NIFTI_SLICE_ALT_DEC; else { /*NSMutableArray *sliceTimesNS = [NSMutableArray arrayWithCapacity:tagCSA.nitems]; for (int z = 1; z <= itemsOK; z++) [sliceTimesNS addObject:[NSNumber numberWithFloat:sliceTimes[z]]]; NSLog(@" Warning: unable to determine slice order for %lu slice mosaic: %@",(unsigned long)[sliceTimesNS count],sliceTimesNS ); */ printf("Warning: unable to determine slice order from CSA tag MosaicRefAcqTimes\n"); } } else if (strcmp(tagCSA.name, "ProtocolSliceNumber") == 0) CSA->protocolSliceNumber1 = round (csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK)); else if (strcmp(tagCSA.name, "PhaseEncodingDirectionPositive") == 0) CSA->phaseEncodingDirectionPositive = round (csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK)); /*if (strcmp(tagCSA.name, "SlicePosition_PCS") == 0) { float f = (csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK)); for (int k = 1; k <= tagCSA.nitems; k++) printf("PCS %d = %f\n",k, lFloats[k]); } else { printf("unused CSA tag %s with %d items\n",tagCSA.name, tagCSA.nitems); }*/ for (int lI = 1; lI <= tagCSA.nitems; lI++) { memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA)); lPos +=sizeof(itemCSA); lPos += ((itemCSA.xx2_Len +3)/4)*4; } } //if at least 1 item }// for lT 1..lnTag return EXIT_SUCCESS; } // readCSAImageHeader() void dcmMultiFloat (int lByteLength, char lBuffer[], int lnFloats, float *lFloats) { //warning: lFloats indexed from 1! will fill lFloats[1]..[nFloats] if ((lnFloats < 1) || (lByteLength < 1)) return; char cString[lByteLength+1]; memcpy(cString, (char*)&lBuffer[0], lByteLength); cString[lByteLength] = 0; //null terminate char *temp=( char *)malloc(lByteLength+1); int f = 0,lStart = 0; bool isOK = false; for (int i = 0; i <= lByteLength; i++) { if ((lBuffer[i] >= '0') && (lBuffer[i] <= '9')) isOK = true; if ((isOK) && ((i == (lByteLength)) || (lBuffer[i] == '/') || (lBuffer[i] == ' ') || (lBuffer[i] == '\\') )){ //x strlcpy(temp,&cString[lStart],i-lStart+1); snprintf(temp,i-lStart+1,"%s",&cString[lStart]); //printf("dcmMultiFloat %s\n",temp); if (f < lnFloats) { f ++; lFloats[f] = atof(temp); //printf("%d == %f\n", f, atof(temp)); } //if f <= nFloats lStart = i+1; } //if isOK } //for i to length free(temp); } //dcmMultiFloat() float dcmStrFloat (int lByteLength, unsigned char lBuffer[]) { //read float stored as a string char cString[lByteLength+1]; memcpy(cString, (char*)&lBuffer[0], lByteLength); cString[lByteLength] = 0; //null terminate return atof(cString); } //dcmStrFloat() /* void dcmStrReport (int lByteLength, char lBuffer[]) {//print value at location char cString[lByteLength+1]; memcpy(cString, (char*)&lBuffer[0], lByteLength); cString[lByteLength] = 0; //null terminate printf("%d dcmStrReport '%s'\n",lByteLength, cString); } //dcmStrReport */ int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h) { //printf("bytes %dx%dx%d %d, %d\n",d.XYZdim[1],d.XYZdim[2],d.XYZdim[3], d.Allocbits_per_pixel, d.samplesPerPixel); for (int i = 0; i < 80; i++) h->descrip[i] = 0; for (int i = 0; i < 24; i++) h->aux_file[i] = 0; for (int i = 0; i < 18; i++) h->db_name[i] = 0; for (int i = 0; i < 10; i++) h->data_type[i] = 0; for (int i = 0; i < 16; i++) h->intent_name[i] = 0; if ((d.bitsAllocated == 8) && (d.samplesPerPixel == 3)) h->datatype = DT_RGB24; else if ((d.bitsAllocated == 8) && (d.samplesPerPixel == 1)) h->datatype = DT_UINT8; else if ((d.bitsAllocated == 16) && (d.samplesPerPixel == 1) && (d.isSigned)) h->datatype = DT_INT16; else if ((d.bitsAllocated == 16) && (d.samplesPerPixel == 1) && (!d.isSigned)) h->datatype = DT_UINT16; else if (d.bitsAllocated == 32) h->datatype = DT_INT32; else { #ifdef myUseCOut std::cout<<"Unsupported DICOM bit-depth " <<d.bitsAllocated << " with " << d.samplesPerPixel << "samples per pixel" <<std::endl; #else printf("Unsupported DICOM bit-depth %d with %d samples per pixel\n",d.bitsAllocated,d.samplesPerPixel); #endif return EXIT_FAILURE; } if ((h->datatype == DT_UINT16) && (d.bitsStored > 0) &&(d.bitsStored < 16)) h->datatype = DT_INT16; // DT_INT16 is more widely supported, same represenation for values 0..32767 for (int i = 0; i < 8; i++) { h->pixdim[i] = 0.0f; h->dim[i] = 0; } h->regular = 114; h->scl_inter = d.intenIntercept; h->scl_slope = d.intenScale; h->cal_max = 0; h->cal_min = 0; h->magic[0]='n'; h->magic[1]='+'; h->magic[2]='1'; h->magic[3]='\0'; h->vox_offset = d.imageStart; h->bitpix = d.bitsAllocated * d.samplesPerPixel; h->pixdim[1] = d.xyzMM[1]; h->pixdim[2] = d.xyzMM[2]; h->pixdim[3] = d.xyzMM[3]; h->pixdim[4] = d.TR/1000; //TR reported in msec, time is in sec h->dim[1] = d.xyzDim[1]; h->dim[2] = d.xyzDim[2]; h->dim[3] = d.xyzDim[3]; h->dim[4] = d.xyzDim[4]; if (h->dim[4] < 2) h->dim[0] = 3; else h->dim[0] = 4; for (int i = 0; i <= 3; i++) { h->srow_x[i] = 0.0f; h->srow_y[i] = 0.0f; h->srow_z[i] = 0.0f; } h->srow_x[0] = -1; h->srow_y[2] = 1; h->srow_z[1] = -1; h->srow_x[3] = (h->dim[1] /2); h->srow_y[3] = -(h->dim[3] /2); h->srow_z[3] = (h->dim[2] /2); h->qform_code = NIFTI_XFORM_UNKNOWN; h->sform_code = NIFTI_XFORM_SCANNER_ANAT; h->toffset = 0; h->intent_code = NIFTI_INTENT_NONE; h->dim_info = 0; //Freq, Phase and Slice all unknown h->xyzt_units = NIFTI_UNITS_UNKNOWN; h->slice_duration = 0; //avoid +inf/-inf, NaN h->intent_p1 = 0; //avoid +inf/-inf, NaN h->intent_p2 = 0; //avoid +inf/-inf, NaN h->intent_p3 = 0; //avoid +inf/-inf, NaN h->pixdim[0] = 1; //QFactor should be 1 or -1 h->sizeof_hdr = 348; //used to signify header does not need to be byte-swapped h->slice_code = d.CSA.sliceOrder; headerDcm2Nii2(d, d, h); return EXIT_SUCCESS; } // headerDcm2Nii() bool isFloatDiff (float a, float b) { return (fabs (a - b) > FLT_EPSILON); } //isFloatDiff() ivec3 setVec3i(int x, int y, int z) { ivec3 v = {x, y, z}; return v; } //setVec3i() mat33 nifti_mat33_reorder_cols( mat33 m, ivec3 v ) { // matlab equivalent ret = m(:, v); where v is 1,2,3 [INDEXED FROM ONE!!!!] mat33 ret; for (int r=0; r<3; r++) { for(int c=0; c<3; c++) ret.m[r][c] = m.m[r][v.v[c]-1]; } return ret; } //nifti_mat33_reorder_cols() /*bool isExt (char *file_name, const char* ext) { char *p_extension; if((p_extension = strrchr(file_name,'.')) != NULL ) if(strcmp(p_extension,ext) == 0) return true; return false; }*/ void changeExt (char *file_name, const char* ext) { char *p_extension; p_extension = strrchr(file_name, '.'); if (p_extension) { strcpy(++p_extension, ext); } } //changeExt() struct TDICOMdata nii_readParRec (char * parname) { struct TDICOMdata d = clear_dicom_data(); FILE *fp = fopen(parname, "r"); if (fp == NULL) return d; #define LINESZ 2048 #define kSlice 0 #define kEcho 1 #define kDyn 2 #define kCardiac 3 #define kImageType 4 #define kSequence 5 #define kIndex 6 #define kBitsPerVoxel 7 #define kXdim 9 #define kYdim 10 #define kRI 11 #define kRS 12 #define kSS 13 #define kAngulationAPs 16 //In V4, offcentre and Angulation labeled as y z x, but actually x y z! #define kAngulationFHs 17 #define kAngulationRLs 18 #define kPositionAP 19 #define kPositionFH 20 #define kPositionRL 21 #define kThickmm 22 #define kGapmm 23 #define kSliceOrients 25 #define kXmm 28 #define kYmm 29 #define kTEcho 30 #define kDynTime 31 #define kGradientNumber 42 #define kbval 33 #define kv1 47 #define kv2 45 #define kv3 46 #define kASL 48 char buff[LINESZ]; bool ADCwarning = false; for (int n=0; kMaxDTIv < 4; n++) d.CSA.dtiV[n][0] = -1; //set to impossible value to detect re-usage by ADC map int parVers = 0; int nCols = 26; int slice = 0; //int prevSliceIndex = 0; //index of prior slice: detect if images are not in order const int kMaxCols = 49; float *cols = (float *)malloc(sizeof(float) * kMaxCols); char *p = fgets (buff, LINESZ, fp); bool isIntenScaleVaries = false; bool isIndexSequential = true; while (p) { if (strlen(buff) < 1) continue; if (buff[0] == '#') { //comment char Comment[7][50]; sscanf(buff, "# %s %s\n", Comment[0], Comment[1]); if (strcmp(Comment[1], "TRYOUT") == 0) { sscanf(buff, "# %s %s %s %s %s %s V%s\n", Comment[0], Comment[1], Comment[2], Comment[3] ,Comment[4], Comment[5],Comment[6]); parVers = (atof(Comment[6])*10); //4.2 = 42 etc if (parVers < 40) { #ifdef myUseCOut std::cout<<"This software is unable to convert ancient PAR files: please use legacy dcm2nii" <<std::endl; #else printf("This software is unable to convert ancient PAR files: please use legacy dcm2nii\n"); #endif return d; //nCols = 26; //e.g. PAR 3.0 has 26 relevant columns } else if (parVers < 41) nCols = 32; //e.g PAR 4.0 else if (parVers < 42) nCols = 47; //e.g. PAR 4.1 else nCols = kMaxCols; //e.g. PAR 4.2 } p = fgets (buff, LINESZ, fp);//get next line continue; } //process '#' comment if (buff[0] == '.') { //tag char Comment[8][50]; sscanf(buff, ". %s %s %s %s %s %s %s %s\n", Comment[0], Comment[1],Comment[2], Comment[3], Comment[4], Comment[5], Comment[6], Comment[7]); if ((strcmp(Comment[0], "Acquisition") == 0) && (strcmp(Comment[1], "nr") == 0)) { d.acquNum = atoi( Comment[3]); d.seriesNum = d.acquNum; } if ((strcmp(Comment[0], "Repetition") == 0) && (strcmp(Comment[1], "time") == 0)) d.TR = atof(Comment[4]); if ((strcmp(Comment[0], "Patient") == 0) && (strcmp(Comment[1], "name") == 0)) { strcpy(d.patientName, Comment[3]); strcat(d.patientName, Comment[4]); strcat(d.patientName, Comment[5]); strcat(d.patientName, Comment[6]); strcat(d.patientName, Comment[7]); //printf("%s\n",d.patientName); } if ((strcmp(Comment[0], "Protocol") == 0) && (strcmp(Comment[1], "name") == 0)) { strcpy(d.protocolName, Comment[3]); strcat(d.protocolName, Comment[4]); strcat(d.protocolName, Comment[5]); strcat(d.protocolName, Comment[6]); strcat(d.protocolName, Comment[7]); //printf("%s\n",d.protocolName); } if ((strcmp(Comment[0], "Examination") == 0) && (strcmp(Comment[1], "date/time") == 0)) { strcpy(d.studyDate, Comment[3]); strcpy(d.studyTime, Comment[5]); //to do convert to traditional DICOM style date time } if ((strcmp(Comment[0], "Off") == 0) && (strcmp(Comment[1], "Centre") == 0)) { //Off Centre midslice(ap,fh,rl) [mm] d.stackOffcentre[2] = atof(Comment[5]); d.stackOffcentre[3] = atof(Comment[6]); d.stackOffcentre[1] = atof(Comment[7]); } if ((strcmp(Comment[0], "Patient") == 0) && (strcmp(Comment[1], "position") == 0)) { //Off Centre midslice(ap,fh,rl) [mm] d.patientOrient[0] = toupper(Comment[3][0]); d.patientOrient[1] = toupper(Comment[4][0]); d.patientOrient[2] = toupper(Comment[5][0]); d.patientOrient[3] = 0; } if ((strcmp(Comment[0], "Max.") == 0) && (strcmp(Comment[3], "slices/locations") == 0)) { d.xyzDim[3] = atoi(Comment[5]); } p = fgets (buff, LINESZ, fp);//get next line continue; } //process '.' tag if (strlen(buff) < 24) { //empty line p = fgets (buff, LINESZ, fp);//get next line continue; } if (parVers < 20) { #ifdef myUseCOut std::cout<<"Error: PAR files should have 'CLINICAL TRYOUT' line with a version from 2.0-4.2: " << parname<<std::endl; #else printf("Error: PAR files should have 'CLINICAL TRYOUT' line with a version from 2.0-4.2: %s\n", parname); #endif return d; } for (int i = 0; i < nCols; i++) cols[i] = strtof(p, &p); // p+1 skip comma, read a float if ((cols[kIndex]) != slice) isIndexSequential = false; //slices 0,1,2.. should have indices 0,1,2,3... slice ++; if (slice == 1) { //for (int i = 0; i < nCols; i++) // cols1[i] = cols[i]; //store first slice to see if dimensions or intensity scale varies between slices d.xyzDim[1] = cols[kXdim]; d.xyzDim[2] = cols[kYdim]; d.xyzMM[1] = cols[kXmm]; d.xyzMM[2] = cols[kYmm]; d.xyzMM[3] = cols[kThickmm] + cols[kGapmm]; d.patientPosition[1] = cols[kPositionRL]; d.patientPosition[2] = cols[kPositionAP]; d.patientPosition[3] = cols[kPositionFH]; d.angulation[1] = cols[kAngulationRLs]; d.angulation[2] = cols[kAngulationAPs]; d.angulation[3] = cols[kAngulationFHs]; d.sliceOrient = cols[kSliceOrients]; d.TE = cols[kTEcho]; d.bitsAllocated = cols[kBitsPerVoxel]; d.bitsStored = cols[kBitsPerVoxel]; d.intenIntercept = cols[kRI]; d.intenScale = cols[kRS]; } else { if ((d.xyzDim[1] != cols[kXdim]) || (d.xyzDim[2] != cols[kYdim]) || (d.bitsAllocated != cols[kBitsPerVoxel]) ) { #ifdef myUseCOut std::cout<<"Error: slice dimensions or bit depth varies "<< parname <<std::endl; #else printf("Error: slice dimensions or bit depth varies %s\n", parname); #endif return d; } if ((d.patientPositionSequentialRepeats == 0) && ((!isSameFloat(d.patientPosition[1],cols[kPositionRL])) || (!isSameFloat(d.patientPosition[2],cols[kPositionAP])) || (!isSameFloat(d.patientPosition[3],cols[kPositionFH])) ) )//this is the first slice with different position d.patientPositionSequentialRepeats = slice-1; if ((d.intenScale != cols[kRS]) || (d.intenIntercept != cols[kRI])) isIntenScaleVaries = true; } if (cols[kImageType] == 0) d.isHasMagnitude = true; if (cols[kImageType] != 0) d.isHasPhase = true; if (cols[kGradientNumber] > 0) { int dir = cols[kGradientNumber]; if ((cols[kbval] > 0.0) && (cols[kv1] == 0.0) && (cols[kv1] == 0.0) && (cols[kv1] == 0.0) ) { if (d.CSA.dtiV[dir-1][0] >= 0) dir = dir + 1; //Philips often stores an ADC map along with B0 and weighted images, unfortunately they give it the same kGradientNumber as the B0! (seen in PAR V4.2) //the logic here is that IF the gradient was previously used we increment the gradient number. This should provide compatibility when Philips fixes this bug //it seems like the ADC is always saved as the final volume, so this solution SHOULD be foolproof. ADCwarning = true; } if (dir > d.CSA.numDti) d.CSA.numDti = dir; if (cols[dir] <= kMaxDTIv) { d.CSA.dtiV[dir-1][0] = cols[kbval]; d.CSA.dtiV[dir-1][1] = cols[kv1]; d.CSA.dtiV[dir-1][2] = cols[kv2]; d.CSA.dtiV[dir-1][3] = cols[kv3]; } //save DTI direction } //if DTI directions //printf("%f %f %lu\n",cols[9],cols[kGradientNumber], strlen(buff)) p = fgets (buff, LINESZ, fp);//get next line } free (cols); fclose (fp); d.isValid = true; d.isSigned = true; d.xyzDim[4] = slice/d.xyzDim[3]; d.locationsInAcquisition = d.xyzDim[3]; #ifdef myUseCOut if (isIntenScaleVaries) std::cout<<"Warning: intensity slope/intercept varies between slices! [solution: user dcm2nii instead]" <<std::endl; if (!isIndexSequential) std::cout<<"Warning: slice order not saved to disk sequentially! [solution: user dcm2nii instead]" <<std::endl; printf("Warning: slice order not saved to disk sequentially! [solution: user dcm2nii instead]\n"); std::cout<<"Done reading PAR header version "<< (float)parVers/10<<" with "<< d.CSA.numDti << "DTI directions"<<std::endl; #else if (ADCwarning) printf("Warning: PAR/REC dataset includes an ADC map that could disrupt analysis. Please remove volume and ensure vectors are reported correctly\n"); if (isIntenScaleVaries) printf("Warning: intensity slope/intercept varies between slices! [solution: user dcm2nii instead]\n"); if (!isIndexSequential) printf("Warning: slice order not saved to disk sequentially! [solution: user dcm2nii instead]\n"); printf("Done reading PAR header version %.1f, with %d DTI directions\n", (float)parVers/10, d.CSA.numDti); #endif //see Xiangrui Li 's dicm2nii (also BSD license) // http://www.mathworks.com/matlabcentral/fileexchange/42997-dicom-to-nifti-converter // Rotation order and signs are figured out by try and err, not 100% sure float d2r = M_PI/180; vec3 ca = setVec3(cos(d.angulation[1]*d2r),cos(d.angulation[2]*d2r),cos(d.angulation[3]*d2r)); vec3 sa = setVec3(sin(d.angulation[1]*d2r),sin(d.angulation[2]*d2r),sin(d.angulation[3]*d2r)); mat33 rx,ry,rz; LOAD_MAT33(rx,1.0f, 0.0f, 0.0f, 0.0f, ca.v[0], -sa.v[0], 0.0f, sa.v[0], ca.v[0]); LOAD_MAT33(ry, ca.v[1], 0.0f, sa.v[1], 0.0f, 1.0f, 0.0f, -sa.v[1], 0.0f, ca.v[1]); LOAD_MAT33(rz, ca.v[2], -sa.v[2], 0.0f, sa.v[2], ca.v[2], 0.0f, 0.0f, 0.0f, 1.0f); mat33 R = nifti_mat33_mul( rx,ry ); R = nifti_mat33_mul( R,rz); ivec3 ixyz = setVec3i(1,2,3); if (d.sliceOrient == kSliceOrientSag) { ixyz = setVec3i(2,3,1); for (int r = 0; r < 3; r++) for (int c = 0; c < 3; c++) if (c != 1) R.m[r][c] = -R.m[r][c]; //invert first and final columns }else if (d.sliceOrient == kSliceOrientCor) { ixyz = setVec3i(1,3,2); for (int r = 0; r < 3; r++) R.m[r][2] = -R.m[r][2]; //invert rows of final column } R = nifti_mat33_reorder_cols(R,ixyz); //dicom rotation matrix d.orient[1] = R.m[0][0]; d.orient[2] = R.m[1][0]; d.orient[3] = R.m[2][0]; d.orient[4] = R.m[0][1]; d.orient[5] = R.m[1][1]; d.orient[6] = R.m[2][1]; mat33 diag; LOAD_MAT33(diag, d.xyzMM[1],0.0f,0.0f, 0.0f,d.xyzMM[2],0.0f, 0.0f,0.0f, d.xyzMM[3]); R= nifti_mat33_mul( R, diag ); mat44 R44; LOAD_MAT44(R44, R.m[0][0],R.m[0][1],R.m[0][2],d.stackOffcentre[1], R.m[1][0],R.m[1][1],R.m[1][2],d.stackOffcentre[2], R.m[2][0],R.m[2][1],R.m[2][2],d.stackOffcentre[3]); vec3 x; if (parVers > 40) //guess x = setVec3(((float)d.xyzDim[1]-1)/2,((float)d.xyzDim[2]-1)/2,((float)d.xyzDim[3]-1)/2); else x = setVec3((float)d.xyzDim[1]/2,(float)d.xyzDim[2]/2,((float)d.xyzDim[3]-1)/2); mat44 eye; LOAD_MAT44(eye, 1.0f,0.0f,0.0f,x.v[0], 0.0f,1.0f,0.0f,x.v[1], 0.0f,0.0f,1.0f,x.v[2]); eye= nifti_mat44_inverse( eye ); //we wish to compute R/eye, so compute invEye and calculate R*invEye R44= nifti_mat44_mul( R44 , eye ); vec4 y; y.v[0]=0.0f; y.v[1]=0.0f; y.v[2]=d.xyzDim[3]-1; y.v[3]=1.0f; y= nifti_vect44mat44_mul(y, R44 ); int iOri = 2; //for axial, slices are 3rd dimenson (indexed from 0) (k) if (d.sliceOrient == kSliceOrientSag) iOri = 0; //for sagittal, slices are 1st dimension (i) if (d.sliceOrient == kSliceOrientCor) iOri = 1; //for coronal, slices are 2nd dimension (j) if (( (y.v[iOri]-R44.m[iOri][3])>0 ) == ( (y.v[iOri]-d.stackOffcentre[iOri+1])>0 ) ) { d.patientPosition[1] = R44.m[0][3]; d.patientPosition[2] = R44.m[1][3]; d.patientPosition[3] = R44.m[2][3]; d.patientPositionLast[1] = y.v[0]; d.patientPositionLast[2] = y.v[1]; d.patientPositionLast[3] = y.v[2]; }else { //d.patientPosition d.patientPosition[1] = y.v[0]; d.patientPosition[2] = y.v[1]; d.patientPosition[3] = y.v[2]; d.patientPositionLast[1] = R44.m[0][3]; d.patientPositionLast[2] = R44.m[1][3]; d.patientPositionLast[3] = R44.m[2][3]; } //finish up changeExt (parname, "REC"); d.locationsInAcquisition = d.xyzDim[3]; d.manufacturer = kMANUFACTURER_PHILIPS; d.imageStart = 0; return d; } //nii_readParRec() size_t nii_ImgBytes(struct nifti_1_header hdr) { //unsigned long imgsz = nii_ImgBytes(hdr); size_t imgsz = hdr.bitpix/8; for (int i = 1; i < 8; i++) if (hdr.dim[i] > 1) imgsz = imgsz * hdr.dim[i]; return imgsz; } //nii_ImgBytes() unsigned char * nii_demosaic(unsigned char* inImg, struct nifti_1_header *hdr, int nMosaicSlices, int ProtocolSliceNumber1) { //demosaic http://nipy.org/nibabel/dicom/dicom_mosaic.html if (nMosaicSlices < 2) return inImg; //Byte inImg[ [img length] ]; //[img getBytes:&inImg length:[img length]]; int nRowCol = ceil(sqrt(nMosaicSlices)); int colBytes = hdr->dim[1]/nRowCol * hdr->bitpix/8; int lineBytes = hdr->dim[1] * hdr->bitpix/8; int rowBytes = hdr->dim[1] * hdr->dim[2]/nRowCol * hdr->bitpix/8; int col = 0; int row = 0; int lOutPos = 0; hdr->dim[1] = hdr->dim[1]/nRowCol; hdr->dim[2] = hdr->dim[2]/nRowCol; hdr->dim[3] = nMosaicSlices; size_t imgsz = nii_ImgBytes(*hdr); unsigned char *outImg = (unsigned char *)malloc(imgsz); for (int m=1; m <= nMosaicSlices; m++) { int lPos = (row * rowBytes) + (col * colBytes); for (int y = 0; y < hdr->dim[2]; y++) { memcpy(&outImg[lOutPos], &inImg[lPos], colBytes); // dest, src, bytes lPos += lineBytes; lOutPos +=colBytes; } col ++; if (col >= nRowCol) { row ++; col = 0; } //start new column } //for m = each mosaic slice if (ProtocolSliceNumber1 > 1) { #ifdef myUseCOut std::cout<<"WARNING: CSA 'ProtocolSliceNumber' SUGGESTS REVERSED SLICE ORDER: SPATIAL AND DTI COORDINATES UNTESTED" <<std::endl; #else printf("WARNING: CSA 'ProtocolSliceNumber' SUGGESTS REVERSED SLICE ORDER: SPATIAL AND DTI COORDINATES UNTESTED\n"); #endif } /*if ((ProtocolSliceNumber1 > 1) && (hdr->dim[3] > 1)) { //exceptionally rare: reverse order of slices - now handled in matrix... int sliceBytes = hdr->dim[1] * hdr->dim[2] * hdr->bitpix/8; memcpy(&inImg[0], &outImg[0],sliceBytes*hdr->dim[3]); //copy data with reversed order dest, src, bytes int lOutPos = sliceBytes * (hdr->dim[3]-1); int lPos = 0; for (int m=0; m < nMosaicSlices; m++) { memcpy( &outImg[lOutPos], &inImg[lPos], sliceBytes); lPos += sliceBytes; lOutPos -= sliceBytes; } }*/ free(inImg); return outImg; //return [NSData dataWithBytes:&outImg length:outlen]; } //nii_demosaic() unsigned char * nii_flipImgY(unsigned char* bImg, struct nifti_1_header *hdr){ //DICOM row order opposite from NIfTI int dim3to7 = 1; for (int i = 3; i < 8; i++) if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i]; size_t lineBytes = hdr->dim[1] * hdr->bitpix/8; if ((hdr->datatype == DT_RGB24) && (hdr->bitpix == 24)) { lineBytes = hdr->dim[1]; dim3to7 = dim3to7 * 3; } //rgb data saved planar (RRR..RGGGG..GBBB..B //printf("smoking gun %d %d\n",hdr->dim[3],hdr->dim[4]); return bImg; unsigned char line[lineBytes]; size_t sliceBytes = hdr->dim[2] * lineBytes; int halfY = hdr->dim[2] / 2; //note truncated toward zero, so halfY=2 regardless of 4 or 5 columns for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice size_t slBottom = (size_t)sl*sliceBytes; size_t slTop = (((size_t)sl+1)*sliceBytes)-lineBytes; for (int y = 0; y < halfY; y++) { //swap order of lines memcpy(&line, &bImg[slBottom], lineBytes); memcpy(&bImg[slBottom], &bImg[slTop], lineBytes); memcpy(&bImg[slTop], &line, lineBytes); slTop -= lineBytes; slBottom += lineBytes; } //for y } //for each slice return bImg; } //nii_flipImgY() unsigned char * nii_flipImgZ(unsigned char* bImg, struct nifti_1_header *hdr){ //DICOM row order opposite from NIfTI int halfZ = hdr->dim[3] / 2; //note truncated toward zero, so halfY=2 regardless of 4 or 5 columns if (halfZ < 1) return bImg; int dim4to7 = 1; for (int i = 4; i < 8; i++) if (hdr->dim[i] > 1) dim4to7 = dim4to7 * hdr->dim[i]; int sliceBytes = hdr->dim[1] * hdr->dim[2] * hdr->bitpix/8; long long volBytes = sliceBytes * hdr->dim[3]; unsigned char slice[sliceBytes]; for (int vol = 0; vol < dim4to7; vol++) { //for each 2D slice long long slBottom = vol*volBytes; long long slTop = ((vol+1)*volBytes)-sliceBytes; for (int z = 0; z < halfZ; z++) { //swap order of lines memcpy(&slice, &bImg[slBottom], sliceBytes); memcpy(&bImg[slBottom], &bImg[slTop], sliceBytes); memcpy(&bImg[slTop], &slice, sliceBytes); slTop -= sliceBytes; slBottom += sliceBytes; } //for Z } //for each volume return bImg; } //nii_flipImgZ() unsigned char * nii_flipZ(unsigned char* bImg, struct nifti_1_header *h){ //flip slice order if (h->dim[3] < 2) return bImg; mat33 s; mat44 Q44; LOAD_MAT33(s,h->srow_x[0],h->srow_x[1],h->srow_x[2], h->srow_y[0],h->srow_y[1],h->srow_y[2], h->srow_z[0],h->srow_z[1],h->srow_z[2]); LOAD_MAT44(Q44,h->srow_x[0],h->srow_x[1],h->srow_x[2],h->srow_x[3], h->srow_y[0],h->srow_y[1],h->srow_y[2],h->srow_y[3], h->srow_z[0],h->srow_z[1],h->srow_z[2],h->srow_z[3]); vec4 v= setVec4(0,0,h->dim[3]-1); v = nifti_vect44mat44_mul(v, Q44); //after flip this voxel will be the origin mat33 mFlipZ; LOAD_MAT33(mFlipZ,1.0f, 0.0f, 0.0f, 0.0f,1.0f,0.0f, 0.0f,0.0f,-1.0f); s= nifti_mat33_mul( s , mFlipZ ); LOAD_MAT44(Q44, s.m[0][0],s.m[0][1],s.m[0][2],v.v[0], s.m[1][0],s.m[1][1],s.m[1][2],v.v[1], s.m[2][0],s.m[2][1],s.m[2][2],v.v[2]); //printf(" ----------> %f %f %f\n",v.v[0],v.v[1],v.v[2]); setQSForm(h,Q44); //printf("nii_flipImgY dims %dx%dx%d %d \n",h->dim[1],h->dim[2], dim3to7,h->bitpix/8); return nii_flipImgZ(bImg,h); }//nii_flipZ() unsigned char * nii_flipY(unsigned char* bImg, struct nifti_1_header *h){ mat33 s; mat44 Q44; LOAD_MAT33(s,h->srow_x[0],h->srow_x[1],h->srow_x[2], h->srow_y[0],h->srow_y[1],h->srow_y[2], h->srow_z[0],h->srow_z[1],h->srow_z[2]); LOAD_MAT44(Q44,h->srow_x[0],h->srow_x[1],h->srow_x[2],h->srow_x[3], h->srow_y[0],h->srow_y[1],h->srow_y[2],h->srow_y[3], h->srow_z[0],h->srow_z[1],h->srow_z[2],h->srow_z[3]); vec4 v= setVec4(0,h->dim[2]-1,0); v = nifti_vect44mat44_mul(v, Q44); //after flip this voxel will be the origin mat33 mFlipY; LOAD_MAT33(mFlipY,1.0f, 0.0f, 0.0f, 0.0f,-1.0f,0.0f, 0.0f,0.0f,1.0f); s= nifti_mat33_mul( s , mFlipY ); LOAD_MAT44(Q44, s.m[0][0],s.m[0][1],s.m[0][2],v.v[0], s.m[1][0],s.m[1][1],s.m[1][2],v.v[1], s.m[2][0],s.m[2][1],s.m[2][2],v.v[2]); setQSForm(h,Q44); //printf("nii_flipImgY dims %dx%dx%d %d \n",h->dim[1],h->dim[2], dim3to7,h->bitpix/8); return nii_flipImgY(bImg,h); }//nii_flipY() unsigned char * nii_loadImgCore(char* imgname, struct nifti_1_header hdr) { size_t imgsz = nii_ImgBytes(hdr); FILE *file = fopen(imgname , "rb"); if (!file) { printf("Error: unable to open %s\n", imgname); return NULL; } fseek(file, 0, SEEK_END); long long fileLen=ftell(file); if (fileLen < (imgsz+hdr.vox_offset)) return NULL; fseek(file, hdr.vox_offset, SEEK_SET); unsigned char *bImg = (unsigned char *)malloc(imgsz); fread(bImg, imgsz, 1, file); fclose(file); return bImg; } //nii_loadImg() unsigned char * nii_rgb2Planar(unsigned char* bImg, struct nifti_1_header *hdr, int isPlanar) { //DICOM data saved in triples RGBRGBRGB, NIfTI RGB saved in planes RRR..RGGG..GBBBB..B if (hdr->datatype != DT_RGB24) return bImg; if (isPlanar == 1) return bImg;//return nii_bgr2rgb(bImg,hdr); int dim3to7 = 1; for (int i = 3; i < 8; i++) if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i]; int sliceBytes24 = hdr->dim[1]*hdr->dim[2] * hdr->bitpix/8; int sliceBytes8 = hdr->dim[1]*hdr->dim[2]; unsigned char slice24[ sliceBytes24 ]; int sliceOffsetR = 0; for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice memcpy(&slice24, &bImg[sliceOffsetR], sliceBytes24); int sliceOffsetG = sliceOffsetR + sliceBytes8; int sliceOffsetB = sliceOffsetR + 2*sliceBytes8; int i = 0; int j = 0; for (int rgb = 0; rgb < sliceBytes8; rgb++) { bImg[sliceOffsetR+j] =slice24[i]; i++; bImg[sliceOffsetG+j] =slice24[i]; i++; bImg[sliceOffsetB+j] =slice24[i]; i++; j++; } sliceOffsetR += sliceBytes24; } //for each slice return bImg; } //nii_rgb2Planar() unsigned char * nii_iVaries(unsigned char *img, struct nifti_1_header *hdr){ //each DICOM image can have its own intesity scaling, whereas NIfTI requires the same scaling for all images in a file //WARNING: do this BEFORE nii_check16bitUnsigned!!!! //if (hdr->datatype != DT_INT16) return img; int dim3to7 = 1; for (int i = 3; i < 8; i++) if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i]; int nVox = hdr->dim[1]*hdr->dim[2]* dim3to7; if (nVox < 1) return img; float * img32=(float*)malloc(nVox*sizeof(float)); if (hdr->datatype == DT_UINT8) { uint8_t * img8i = (uint8_t*) img; for (int i=0; i < nVox; i++) img32[i] = img8i[i]; } else if (hdr->datatype == DT_UINT16) { uint16_t * img16ui = (uint16_t*) img; for (int i=0; i < nVox; i++) img32[i] = img16ui[i]; } else if (hdr->datatype == DT_INT16) { int16_t * img16i = (int16_t*) img; for (int i=0; i < nVox; i++) img32[i] = img16i[i]; } else if (hdr->datatype == DT_INT32) { int32_t * img32i = (int32_t*) img; for (int i=0; i < nVox; i++) img32[i] = img32i[i]; } free (img); //release previous image for (int i=0; i < nVox; i++) img32[i] = (img32[i]* hdr->scl_slope)+hdr->scl_inter; hdr->scl_slope = 1; hdr->scl_inter = 0; hdr->datatype = DT_FLOAT; hdr->bitpix = 32; return (unsigned char*) img32; } //nii_iVaries() unsigned char * nii_XYTZ_XYZT(unsigned char* bImg, struct nifti_1_header *hdr, int seqRepeats) { //Philips can save time as 3rd dimensions, NIFTI requires time is 4th dimension int dim4to7 = 1; for (int i = 4; i < 8; i++) if (hdr->dim[i] > 1) dim4to7 = dim4to7 * hdr->dim[i]; if ((hdr->dim[3] < 2) || (dim4to7 < 2)) return bImg; #ifdef myUseCOut std::cout<<"Converting XYTZ to XYZT with "<<hdr->dim[3]<<" slices (Z) and "<< dim4to7<< "volumes" <<std::endl; if ((dim4to7 % seqRepeats) != 0) { std::cout<<"Error: patient position repeats "<<seqRepeats<<" times, but this does not evenly divide number of volumes: "<< dim4to7 <<std::endl; seqRepeats = 1; } #else printf("Converting XYTZ to XYZT with %d slices (Z) and %d volumes (T).\n",hdr->dim[3], dim4to7); if ((dim4to7 % seqRepeats) != 0) { printf("Error: patient position repeats %d times, but this does not evenly divide number of volumes (%d)", seqRepeats,dim4to7); seqRepeats = 1; } #endif uint64_t typeRepeats = dim4to7 / seqRepeats; uint64_t sliceBytes = hdr->dim[1]*hdr->dim[2]*hdr->bitpix/8; uint64_t seqBytes = sliceBytes * seqRepeats; uint64_t typeBytes = seqBytes * hdr->dim[3]; uint64_t imgSz = nii_ImgBytes(*hdr); //this uses a lot of RAM, someday this could be done in place... unsigned char *outImg = (unsigned char *)malloc(imgSz); //memcpy(&tempImg[0], &bImg[0], imgSz); uint64_t origPos = 0; uint64_t Pos = 0; // for (int t = 0; t < typeRepeats; t++) { //for each volume for (int s = 0; s < seqRepeats; s++) { origPos = (t*typeBytes) +s*sliceBytes; for (int z = 0; z < hdr->dim[3]; z++) { //for each slice memcpy( &outImg[Pos],&bImg[origPos], sliceBytes); Pos += sliceBytes; origPos += seqBytes; } }//for s } free(bImg); return outImg; } //nii_ImgBytes() unsigned char * nii_byteswap(unsigned char *img, struct nifti_1_header *hdr){ if (hdr->bitpix < 9) return img; uint64_t nvox = nii_ImgBytes(*hdr) / (hdr->bitpix/8); void *ar = (void*) img; if (hdr->bitpix == 16) nifti_swap_2bytes( nvox , ar ); if (hdr->bitpix == 32) nifti_swap_4bytes( nvox , ar ); if (hdr->bitpix == 64) nifti_swap_8bytes( nvox , ar ); return img; } //nii_byteswap() unsigned char * nii_loadImgX(char* imgname, struct nifti_1_header *hdr, struct TDICOMdata dcm, bool iVaries) { //provided with a filename (imgname) and DICOM header (dcm), creates NIfTI header (hdr) and img if (headerDcm2Nii(dcm, hdr) == EXIT_FAILURE) return NULL; unsigned char * img = nii_loadImgCore(imgname, *hdr); if (img == NULL) return img; #ifdef __BIG_ENDIAN__ if ((dcm.isLittleEndian) && (hdr->bitpix > 8)) img = nii_byteswap(img, hdr); #else if ((!dcm.isLittleEndian) && (hdr->bitpix > 8)) img = nii_byteswap(img, hdr); #endif if (hdr->datatype ==DT_RGB24) img = nii_rgb2Planar(img, hdr, dcm.isPlanarRGB);//do this BEFORE Y-Flip, or RGB order can be flipped if (dcm.CSA.mosaicSlices > 1) { img = nii_demosaic(img, hdr, dcm.CSA.mosaicSlices, dcm.CSA.protocolSliceNumber1); /* we will do this in nii_dicom_batch #ifdef obsolete_mosaic_flip img = nii_flipImgY(img, hdr); #endif*/ } if (iVaries) img = nii_iVaries(img, hdr); int nAcq = dcm.locationsInAcquisition; if ((nAcq > 1) && (hdr->dim[0] < 4) && ((hdr->dim[3]%nAcq)==0) && (hdr->dim[3]>nAcq) ) { hdr->dim[4] = hdr->dim[3]/nAcq; hdr->dim[3] = nAcq; hdr->dim[0] = 4; } if ((hdr->dim[0] > 3) && (dcm.patientPositionSequentialRepeats > 1)) //swizzle 3rd and 4th dimension (Philips stores time as 3rd dimension) img = nii_XYTZ_XYZT(img, hdr,dcm.patientPositionSequentialRepeats ); headerDcm2NiiSForm(dcm,dcm, hdr); return img; } //nii_loadImgX() struct TDICOMdata readDICOMv(char * fname, bool isVerbose) { struct TDICOMdata d = clear_dicom_data(); strcpy(d.protocolName, ""); //fill dummy with empty space so we can detect kProtocolNameGE FILE *file = fopen(fname, "rb"); if (!file) { #ifdef myUseCOut std::cout<<"Unable to open file "<< fname <<std::endl; #else printf( "Unable to open file %s\n", fname); #endif return d; } fseek(file, 0, SEEK_END); long long fileLen=ftell(file); //Get file length if (fileLen < 256) { #ifdef myUseCOut std::cout<<"File too small to be a DICOM image "<< fname <<std::endl; #else printf( "File too small to be a DICOM image %s\n", fname); #endif return d; } fseek(file, 0, SEEK_SET); //Allocate memory unsigned char *buffer=(unsigned char *)malloc(fileLen+1); if (!buffer) { printf( "Memory error!"); fclose(file); return d; } //Read file contents into buffer fread(buffer, fileLen, 1, file); fclose(file); if ((buffer[128] != 'D') || (buffer[129] != 'I') || (buffer[130] != 'C') || (buffer[131] != 'M')) { free (buffer); return d; } //DEFINE DICOM TAGS #define kUnused 0x0001+(0x0001 << 16 ) #define kStart 0x0002+(0x0000 << 16 ) #define kTransferSyntax 0x0002+(0x0010 << 16) #define kStudyDate 0x0008+(0x0020 << 16 ) #define kStudyTime 0x0008+(0x0030 << 16 ) #define kAcquisitionTime 0x0008+(0x0032 << 16 ) #define kManufacturer 0x0008+(0x0070 << 16 ) #define kProtocolNameGE 0x0008+(0x103E << 16 ) #define kComplexImageComponent 0x0008+(0x9208 << 16 )//'0008' '9208' 'CS' 'ComplexImageComponent' #define kPatientName 0x0010+(0x0010 << 16 ) #define kPatientID 0x0010+(0x0020 << 16 ) #define kMRAcquisitionType 0x0018+(0x0023 << 16) #define kZThick 0x0018+(0x0050 << 16 ) #define kTR 0x0018+(0x0080 << 16 ) #define kTE 0x0018+(0x0081 << 16 ) #define kEchoNum 0x0018+(0x0086 << 16 ) //IS #define kZSpacing 0x0018+(0x0088 << 16 ) //'DS' 'SpacingBetweenSlices' #define kProtocolName 0x0018+(0x1030<< 16 ) #define kInPlanePhaseEncodingDirection 0x0018+(0x1312<< 16 ) //CS #define kPatientOrient 0x0018+(0x5100<< 16 ) //0018,5100. patient orientation - 'HFS' //#define kDiffusionBFactorSiemens 0x0019+(0x100C<< 16 ) // 0019;000C;SIEMENS MR HEADER ;B_value ;1;IS;1 #define kLastScanLoc 0x0019+(0x101B<< 16 ) #define kDiffusionDirectionGEX 0x0019+(0x10BB<< 16 ) //DS #define kDiffusionDirectionGEY 0x0019+(0x10BC<< 16 ) //DS #define kDiffusionDirectionGEZ 0x0019+(0x10BD<< 16 ) //DS #define kPatientPosition 0x0020+(0x0032 << 16 ) #define kSeriesNum 0x0020+(0x0011 << 16 ) #define kAcquNum 0x0020+(0x0012 << 16 ) #define kImageNum 0x0020+(0x0013 << 16 ) #define kOrientation 0x0020+(0x0037 << 16 ) #define kImageComments 0x0020+(0x4000<< 16 )// '0020' '4000' 'LT' 'ImageComments' #define kLocationsInAcquisitionGE 0x0021+(0x104F<< 16 )// 'SS' 'LocationsInAcquisitionGE' #define kSamplesPerPixel 0x0028+(0x0002 << 16 ) #define kPlanarRGB 0x0028+(0x0006 << 16 ) #define kDim3 0x0028+(0x0008 << 16 ) //number of frames - for Philips this is Dim3*Dim4 #define kDim2 0x0028+(0x0010 << 16 ) #define kDim1 0x0028+(0x0011 << 16 ) #define kXYSpacing 0x0028+(0x0030 << 16 ) //'0028' '0030' 'DS' 'PixelSpacing' #define kBitsAllocated 0x0028+(0x0100 << 16 ) #define kBitsStored 0x0028+(0x0101 << 16 )//'0028' '0101' 'US' 'BitsStored' #define kIsSigned 0x0028+(0x0103 << 16 ) #define kIntercept 0x0028+(0x1052 << 16 ) #define kSlope 0x0028+(0x1053 << 16 ) #define kGeiisFlag 0x0029+(0x0010 << 16 ) //warn user if dreaded GEIIS was used to process image #define kCSAImageHeaderInfo 0x0029+(0x1010 << 16 ) //#define kObjectGraphics 0x0029+(0x1210 << 16 ) //0029,1210 syngoPlatformOOGInfo Object Oriented Graphics #define kDiffusionBFactorGE 0x0043+(0x1039 << 16 ) //IS dicm2nii's SlopInt_6_9 #define kCoilSiemens 0x0051+(0x100F << 16 ) #define kLocationsInAcquisition 0x0054+(0x0081 << 16 ) #define kIconImageSequence 0x0088+(0x0200 << 16 ) #define kDiffusionBFactor 0x2001+(0x1003 << 16 )// FL #define kSliceOrient 0x2001+(0x100B << 16 )//2001,100B Philips slice orientation (TRANSVERSAL, AXIAL, SAGITTAL) #define kLocationsInAcquisitionPhilips 0x2001+(0x1018 << 16 ) #define kNumberOfDynamicScans 0x2001+(0x1081 << 16 )//'2001' '1081' 'IS' 'NumberOfDynamicScans' #define kMRAcquisitionTypePhilips 0x2005+(0x106F << 16) #define kAngulationAP 0x2005+(0x1071 << 16)//'2005' '1071' 'FL' 'MRStackAngulationAP' #define kAngulationFH 0x2005+(0x1072 << 16)//'2005' '1072' 'FL' 'MRStackAngulationFH' #define kAngulationRL 0x2005+(0x1073 << 16)//'2005' '1073' 'FL' 'MRStackAngulationRL' #define kMRStackOffcentreAP 0x2005+(0x1078 << 16) #define kMRStackOffcentreFH 0x2005+(0x1079 << 16) #define kMRStackOffcentreRL 0x2005+(0x107A << 16) #define kDiffusionDirectionRL 0x2005+(0x10B0 << 16) #define kDiffusionDirectionAP 0x2005+(0x10B1 << 16) #define kDiffusionDirectionFH 0x2005+(0x10B2 << 16) #define k2005140F 0x2005+(0x140F << 16) #define kWaveformSq 0x5400+(0x0100 << 16) #define kImageStart 0x7FE0+(0x0010 << 16 ) #define kNest 0xFFFE +(0xE000 << 16 ) //Item follows SQ #define kUnnest 0xFFFE +(0xE00D << 16 ) //ItemDelimitationItem [length defined] http://www.dabsoft.ch/dicom/5/7.5/ #define kUnnest2 0xFFFE +(0xE0DD << 16 )//SequenceDelimitationItem [length undefined] double zSpacing = -1.0l; //includes slice thickness plus gap int locationsInAcquisitionGE = 0; int locationsInAcquisitionPhilips = 0; long lPos = 128+4; //4-byte signature starts at 128 uint32_t lLength; uint32_t groupElement = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24); if (groupElement != kStart) #ifdef myUseCOut std::cout<<"DICOM appears corrupt: first group:element should be 0x0002:0x0000" <<std::endl; #else printf("DICOM appears corrupt: first group:element should be 0x0002:0x0000\n"); #endif char vr[2]; bool isIconImageSequence = false; bool isSwitchToImplicitVR = false; bool isSwitchToBigEndian = false; //bool geiisBug = false; //for buggy GEIIS http://forum.dcmtk.org/viewtopic.php?p=7162&sid=3b516cc751aae51fbb5e73184abe37c2 bool is2005140FSQ = false; //for buggy Philips bool is2005140FSQwarned = false; //for buggy Philips bool isAtFirstPatientPosition = false; //for 3d and 4d files: flag is true for slices at same position as first slice int patientPositionCount = 0; long coilNum = 0; //Siemens can save one image per coil (H12,H13,etc) or one combined image for array (HEA;HEP) long echoNum = 0; while ((d.imageStart == 0) && ((lPos+8) < fileLen)) { if (d.isLittleEndian) groupElement = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24); else groupElement = buffer[lPos+1] | (buffer[lPos] << 8) | (buffer[lPos+3] << 16) | (buffer[lPos+2] << 24); if ((isSwitchToBigEndian) && ((groupElement & 0xFFFF) != 2)) { isSwitchToBigEndian = false; d.isLittleEndian = false; groupElement = buffer[lPos+1] | (buffer[lPos] << 8) | (buffer[lPos+3] << 16) | (buffer[lPos+2] << 24); }//transfer syntax requests switching endian after group 0002 if ((isSwitchToImplicitVR) && ((groupElement & 0xFFFF) != 2)) { isSwitchToImplicitVR = false; d.isExplicitVR = false; } //transfer syntax requests switching VR after group 0001 //uint32_t group = (groupElement & 0xFFFF); lPos += 4; if ((groupElement == kNest) || (groupElement == kUnnest) || (groupElement == kUnnest2)) { vr[0] = 'N'; vr[1] = 'A'; //if (groupElement == kUnnest) geiisBug = false; //don't exit if there is a proprietary thumbnail //printf("xxx"); lLength = 4; } else if (d.isExplicitVR) { vr[0] = buffer[lPos]; vr[1] = buffer[lPos+1]; if (buffer[lPos+1] < 'A') {//implicit vr with 32-bit length if (d.isLittleEndian) lLength = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24); else lLength = buffer[lPos+3] | (buffer[lPos+2] << 8) | (buffer[lPos+1] << 16) | (buffer[lPos] << 24); lPos += 4; } else if ( ((buffer[lPos] == 'U') && (buffer[lPos+1] == 'N')) || ((buffer[lPos] == 'O') && (buffer[lPos+1] == 'B')) || ((buffer[lPos] == 'O') && (buffer[lPos+1] == 'W')) ) { //VR= UN, OB, OW, SQ || ((buffer[lPos] == 'S') && (buffer[lPos+1] == 'Q')) lPos = lPos + 4; //skip 2 byte VR string and 2 reserved bytes = 4 bytes if (d.isLittleEndian) lLength = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24); else lLength = buffer[lPos+3] | (buffer[lPos+2] << 8) | (buffer[lPos+1] << 16) | (buffer[lPos] << 24); lPos = lPos + 4; //skip 4 byte length } else if ((buffer[lPos] == 'S') && (buffer[lPos+1] == 'Q')) { lLength = 8; //Sequence Tag is2005140FSQ = (groupElement == k2005140F); } else { //explicit VR with 16-bit length if ((d.isLittleEndian) ) lLength = buffer[lPos+2] | (buffer[lPos+3] << 8); else lLength = buffer[lPos+3] | (buffer[lPos+2] << 8); lPos += 4; //skip 2 byte VR string and 2 length bytes = 4 bytes } } else { //implicit VR if (d.isLittleEndian) lLength = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24); else lLength = buffer[lPos+3] | (buffer[lPos+2] << 8) | (buffer[lPos+1] << 16) | (buffer[lPos] << 24); lPos += 4; //we have loaded the 32-bit length } //if explicit else implicit VR if (lLength == 0xFFFFFFFF) lLength = 8; //SQ (Sequences) use 0xFFFFFFFF [4294967295] to denote unknown length //next: look for required tags if ((isIconImageSequence) && ((groupElement & 0x0028) == 0x0028 )) groupElement = kUnused; //ignore icon dimensions switch ( groupElement ) { case kTransferSyntax: { char transferSyntax[kDICOMStr]; dcmStr (lLength, &buffer[lPos], transferSyntax); //printf("transfer syntax '%s'\n", transferSyntax); if (strcmp(transferSyntax, "1.2.840.10008.1.2.1") == 0) ; //default isExplicitVR=true; //d.isLittleEndian=true else if (strcmp(transferSyntax, "1.2.840.10008.1.2.2") == 0) isSwitchToBigEndian = true; //isExplicitVR=true; else if (strcmp(transferSyntax, "1.2.840.10008.1.2") == 0) isSwitchToImplicitVR = true; //d.isLittleEndian=true else { #ifdef myUseCOut std::cout<<"Unsupported transfer syntax "<< transferSyntax<<std::endl; #else printf("Unsupported transfer syntax '%s'\n",transferSyntax); #endif d.imageStart = 1;//abort as invalid (imageStart MUST be >128) } break;} //{} provide scope for variable 'transferSyntax case kStudyDate: dcmStr (lLength, &buffer[lPos], d.studyDate); break; case kManufacturer: d.manufacturer = dcmStrManufacturer (lLength, &buffer[lPos]); break; case kComplexImageComponent: d.isHasPhase = (buffer[lPos]=='P') && (toupper(buffer[lPos+1]) == 'H'); d.isHasMagnitude = (buffer[lPos]=='M') && (toupper(buffer[lPos+1]) == 'A'); break; case kAcquisitionTime : { char acquisitionTimeTxt[kDICOMStr]; dcmStr (lLength, &buffer[lPos], acquisitionTimeTxt); d.acquisitionTime = atof(acquisitionTimeTxt); break; } case kStudyTime : dcmStr (lLength, &buffer[lPos], d.studyTime); break; case kPatientName : dcmStr (lLength, &buffer[lPos], d.patientName); break; case kPatientID : dcmStr (lLength, &buffer[lPos], d.patientID); break; case kProtocolNameGE: { if (d.manufacturer == kMANUFACTURER_GE) dcmStr (lLength, &buffer[lPos], d.protocolName); break; } case kProtocolName : { if (strlen(d.protocolName) < 1) //GE uses a generic session name here: do not overwrite kProtocolNameGE dcmStr (lLength, &buffer[lPos], d.protocolName); break; } case kPatientOrient : dcmStr (lLength, &buffer[lPos], d.patientOrient); break; case kLastScanLoc : d.lastScanLoc = dcmStrFloat(lLength, &buffer[lPos]); break; /*case kDiffusionBFactorSiemens : if (d.manufacturer == kMANUFACTURER_SIEMENS) printf(">>>>%f\n,",dcmStrFloat(lLength, &buffer[lPos])); break;*/ case kDiffusionDirectionGEX : if (d.manufacturer == kMANUFACTURER_GE) d.CSA.dtiV[0][1] = dcmStrFloat(lLength, &buffer[lPos]); break; case kDiffusionDirectionGEY : if (d.manufacturer == kMANUFACTURER_GE) d.CSA.dtiV[0][2] = dcmStrFloat(lLength, &buffer[lPos]); break; case kDiffusionDirectionGEZ : if (d.manufacturer == kMANUFACTURER_GE) { d.CSA.dtiV[0][3] = dcmStrFloat(lLength, &buffer[lPos]); d.CSA.numDti = 1; } break; case kPatientPosition : if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (is2005140FSQ)) { #ifdef myUseCOut if (!is2005140FSQwarned) std::cout<<"Warning: Philips R3.2.2 can report different positions for the same slice. Attempting patch." <<std::endl; #else if (!is2005140FSQwarned) printf("Warning: Philips R3.2.2 can report different positions for the same slice. Attempting patch.\n"); #endif is2005140FSQwarned = true; } else { patientPositionCount++; isAtFirstPatientPosition = true; if (isnan(d.patientPosition[1])) dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, &d.patientPosition[0]); //slice position else { dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, &d.patientPositionLast[0]); //slice direction for 4D if ((isFloatDiff(d.patientPositionLast[1],d.patientPosition[1])) || (isFloatDiff(d.patientPositionLast[2],d.patientPosition[2])) || (isFloatDiff(d.patientPositionLast[3],d.patientPosition[3])) ) { isAtFirstPatientPosition = false; //this slice is not at position of 1st slice if (d.patientPositionSequentialRepeats == 0) //this is the first slice with different position d.patientPositionSequentialRepeats = patientPositionCount-1; } //if different position from 1st slice in file } //if not first slice in file } //not after 2005,140F break; case kInPlanePhaseEncodingDirection: d.phaseEncodingRC = toupper(buffer[lPos]); //first character is either 'R'ow or 'C'ol break; case kSeriesNum: d.seriesNum = dcmStrInt(lLength, &buffer[lPos]); break; case kAcquNum: d.acquNum = dcmStrInt(lLength, &buffer[lPos]); break; case kImageNum: d.imageNum = dcmStrInt(lLength, &buffer[lPos]); break; case kPlanarRGB: d.isPlanarRGB = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kDim3: d.xyzDim[3] = dcmStrInt(lLength, &buffer[lPos]); break; case kSamplesPerPixel: d.samplesPerPixel = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kDim2: d.xyzDim[2] = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kDim1: d.xyzDim[1] = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kXYSpacing: dcmMultiFloat(lLength, (char*)&buffer[lPos], 2, d.xyzMM); break; case kImageComments: dcmStr (lLength, &buffer[lPos], d.imageComments); break; case kLocationsInAcquisitionGE: locationsInAcquisitionGE = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kBitsAllocated : d.bitsAllocated = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kBitsStored : d.bitsStored = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kIsSigned : //http://dicomiseasy.blogspot.com/2012/08/chapter-12-pixel-data.html d.isSigned = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kTR : d.TR = dcmStrFloat(lLength, &buffer[lPos]); break; case kTE : d.TE = dcmStrFloat(lLength, &buffer[lPos]); break; case kEchoNum : echoNum = dcmStrInt(lLength, &buffer[lPos]); break; case kZSpacing : zSpacing = dcmStrFloat(lLength, &buffer[lPos]); break; case kSlope : d.intenScale = dcmStrFloat(lLength, &buffer[lPos]); break; case kIntercept : d.intenIntercept = dcmStrFloat(lLength, &buffer[lPos]); break; case kZThick : d.xyzMM[3] = dcmStrFloat(lLength, &buffer[lPos]); break; case kCoilSiemens : { if (d.manufacturer == kMANUFACTURER_SIEMENS) { //see if image from single coil "H12" or an array "HEA;HEP" char coilStr[kDICOMStr]; dcmStr (lLength, &buffer[lPos], coilStr); //long coilNum = 0; char *ptr; dcmStrDigitsOnly(coilStr); coilNum = strtol(coilStr, &ptr, 10); if (*ptr != '\0') coilNum = 0; } break; } case kLocationsInAcquisition : d.locationsInAcquisition = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kLocationsInAcquisitionPhilips: locationsInAcquisitionPhilips = dcmInt(lLength,&buffer[lPos],d.isLittleEndian); break; case kIconImageSequence: isIconImageSequence = true; break; case kNumberOfDynamicScans: d.numberOfDynamicScans = dcmStrInt(lLength, &buffer[lPos]); break; case kMRAcquisitionType: if (lLength > 1) d.is3DAcq = (buffer[lPos]=='3') && (toupper(buffer[lPos+1]) == 'D'); break; case kMRAcquisitionTypePhilips: //kMRAcquisitionType if (lLength > 1) d.is3DAcq = (buffer[lPos]=='3') && (toupper(buffer[lPos+1]) == 'D'); break; case kAngulationRL: d.angulation[1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kAngulationAP: d.angulation[2] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kAngulationFH: d.angulation[3] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kMRStackOffcentreRL: d.stackOffcentre[1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kMRStackOffcentreAP: d.stackOffcentre[2] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kMRStackOffcentreFH: d.stackOffcentre[3] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kSliceOrient: { char orientStr[kDICOMStr]; dcmStr (lLength, &buffer[lPos], orientStr); if (toupper(orientStr[0])== 'S') d.sliceOrient = kSliceOrientSag; //sagittal else if (toupper(orientStr[0])== 'C') d.sliceOrient = kSliceOrientCor; //coronal else d.sliceOrient = kSliceOrientTra; //transverse (axial) break; } case kDiffusionBFactor: if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition)) { d.CSA.numDti++; //increment with BFactor: on Philips slices with B=0 have B-factor but no diffusion directions if ((d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv)) d.CSA.dtiV[d.CSA.numDti-1][0] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); } break; case kDiffusionDirectionRL: if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition) && (d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv)) d.CSA.dtiV[d.CSA.numDti-1][1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kDiffusionDirectionAP: if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition) && (d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv)) d.CSA.dtiV[d.CSA.numDti-1][2] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); break; case kDiffusionDirectionFH: if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition) && (d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv)) d.CSA.dtiV[d.CSA.numDti-1][3] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian); //http://www.na-mic.org/Wiki/index.php/NAMIC_Wiki:DTI:DICOM_for_DWI_and_DTI break; case kWaveformSq: d.imageStart = 1; //abort!!! #ifdef myUseCOut std::cout<<"Warning: Unable to extract sound wave forms" <<std::endl; #else printf("Warning: Unable to extract sound wave forms\n"); #endif break; case kCSAImageHeaderInfo: readCSAImageHeader(&buffer[lPos], lLength, &d.CSA, isVerbose); break; //case kObjectGraphics: // printf("---->%d,",lLength); // break; case kDiffusionBFactorGE : if (d.manufacturer == kMANUFACTURER_GE) d.CSA.dtiV[0][0] = dcmStrInt(lLength, &buffer[lPos]); break; case kGeiisFlag: if ((lLength > 4) && (buffer[lPos]=='G') && (buffer[lPos+1]=='E') && (buffer[lPos+2]=='I') && (buffer[lPos+3]=='I')) { //read a few digits, as bug is specific to GEIIS, while GEMS are fine #ifdef myUseCOut std::cout<<"Warning: GEIIS violates the DICOM standard. Inspect results and admonish your vendor." <<std::endl; #else printf("Warning: GEIIS violates the DICOM standard. Inspect results and admonish your vendor.\n"); #endif isIconImageSequence = true; //geiisBug = true; //compressed thumbnails do not follow transfer syntax! GE should not re-use pulbic tags for these proprietary images http://sonca.kasshin.net/gdcm/Doc/GE_ImageThumbnails } break; case kOrientation : dcmMultiFloat(lLength, (char*)&buffer[lPos], 6, d.orient); break; case kImageStart: //if ((!geiisBug) && (!isIconImageSequence)) //do not exit for proprietary thumbnails if (!isIconImageSequence) //do not exit for proprietary thumbnails d.imageStart = (int)lPos; //geiisBug = false; isIconImageSequence = false; break; } //switch/case for groupElement //printf("VR=%c%c tag=%04x,%04x length=%lu, pos=%ld x=%d\n",vr[0],vr[1],groupElement & 65535,groupElement>>16, lLength, lPos, d.xyzDim[1]); lPos = lPos + (lLength); } free (buffer); d.dateTime = (atof(d.studyDate)* 1000000) + atof(d.studyTime); //printf("slices in Acq %d %d\n",d.locationsInAcquisition,locationsInAcquisitionPhilips); if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (d.locationsInAcquisition == 0)) d.locationsInAcquisition = locationsInAcquisitionPhilips; if ((d.manufacturer == kMANUFACTURER_GE) && (d.locationsInAcquisition == 0)) d.locationsInAcquisition = locationsInAcquisitionGE; if (zSpacing > 0) d.xyzMM[3] = zSpacing; //use zSpacing if provided: depending on vendor, kZThick may or may not include a slice gap //printf("patientPositions = %d XYZT = %d slicePerVol = %d numberOfDynamicScans %d\n",patientPositionCount,d.xyzDim[3], d.locationsInAcquisition, d.numberOfDynamicScans); if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (patientPositionCount > d.xyzDim[3])) printf("Please check slice thicknesses: Philips R3.2.2 bug can disrupt estimation (%d positions reported for %d slices)\n",patientPositionCount, d.xyzDim[3]); //Philips reported different positions for each slice! if ((d.imageStart > 144) && (d.xyzDim[1] > 1) && (d.xyzDim[2] > 1)) d.isValid = true; if ((d.xyzMM[1] > FLT_EPSILON) && (d.xyzMM[2] < FLT_EPSILON)) { printf("Please check voxel size\n"); d.xyzMM[2] = d.xyzMM[1]; } if ((d.xyzMM[2] > FLT_EPSILON) && (d.xyzMM[1] < FLT_EPSILON)) { printf("Please check voxel size\n"); d.xyzMM[1] = d.xyzMM[2]; } if ((d.xyzMM[3] < FLT_EPSILON)) { printf("Unable to determine slice thickness: please check voxel size\n"); d.xyzMM[3] = 1.0; } if (coilNum > 0) //segment images with multiple coils d.seriesNum = d.seriesNum + (100*coilNum); if (echoNum > 2) //segment images with multiple echoes d.seriesNum = d.seriesNum + (100*echoNum); if (isVerbose) { printf("Patient Position %f %f %f\n",d.patientPosition[1],d.patientPosition[2],d.patientPosition[3]); printf("DICOM acq %d img %d ser %ld dim %dx%dx%d mm %gx%gx%g offset %d dyn %d loc %d valid %d ph %d mag %d posReps %d nDTI %d 3d %d\n",d.acquNum,d.imageNum,d.seriesNum,d.xyzDim[1],d.xyzDim[2],d.xyzDim[3],d.xyzMM[1],d.xyzMM[2],d.xyzMM[3],d.imageStart, d.numberOfDynamicScans, d.locationsInAcquisition, d.isValid, d.isHasPhase, d.isHasMagnitude,d.patientPositionSequentialRepeats, d.CSA.numDti, d.is3DAcq); } return d; } // readDICOM() struct TDICOMdata readDICOM(char * fname) { return readDICOMv(fname, false); } ���������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/wxWidgets/nii_dicom_batch.c���������������������������������������������������0000664�0000000�0000000�00000173334�13220512030�0021213�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������//#define myNoSave //do not save images to disk #ifndef myDisableZLib #include <zlib.h> #ifndef myDisableTarGz // #include "untgz.h" #endif #endif #ifdef myUseCOut #include <iostream> #endif #include "nifti1_io_core.h" #include "nifti1.h" #include "nii_dicom_batch.h" #include "nii_dicom.h" #include "tinydir.h" #include <ctype.h> //toupper #include <float.h> #include <math.h> #include <stdbool.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> #include <time.h> // clock_t, clock, CLOCKS_PER_SEC #include "nii_ortho.h" #if defined(_WIN64) || defined(_WIN32) #include <windows.h> //write to registry #endif #ifndef M_PI #define M_PI 3.14159265358979323846 #endif //gcc -O3 -o main main.c nii_dicom.c #if defined(_WIN64) || defined(_WIN32) const char kPathSeparator ='\\'; const char kFileSep[2] = "\\"; #else const char kPathSeparator ='/'; const char kFileSep[2] = "/"; #endif struct TDCMsort { uint64_t indx, img; }; struct TSearchList { unsigned long numItems, maxItems; char **str; }; void dropFilenameFromPath(char *path) { // const char *dirPath = strrchr(path, '/'); //UNIX if (dirPath == 0) dirPath = strrchr(path, '\\'); //Windows if (dirPath == NULL) { strcpy(path,""); } else path[dirPath - path] = 0; // please make sure there is enough space in TargetDirectory } void getFileName( char *pathParent, const char *path) //if path is c:\d1\d2 then filename is 'd2' { const char *filename = strrchr(path, '/'); //UNIX if (filename == 0) filename = strrchr(path, '\\'); //Windows //const char *filename = strrchr(path, kPathSeparator); //x if (filename == NULL) {//no path separator strcpy(pathParent,path); return; } filename++; strcpy(pathParent,filename); } bool is_fileexists(const char * filename) { FILE * fp = NULL; if ((fp = fopen(filename, "r"))) { fclose(fp); return true; } return false; } bool is_fileNotDir(const char* path) { //returns false if path is a folder; requires #include <sys/stat.h> struct stat buf; stat(path, &buf); return S_ISREG(buf.st_mode); } //is_file() bool is_exe(const char* path) { //requires #include <sys/stat.h> struct stat buf; stat(path, &buf); return (S_ISREG(buf.st_mode) && (buf.st_mode & 0111) ); } //is_exe() int is_dir(const char *pathname, int follow_link) { struct stat s; if ((NULL == pathname) || (0 == strlen(pathname))) return 0; int err = stat(pathname, &s); if(-1 == err) { return 0; /* does not exist */ } else { if(S_ISDIR(s.st_mode)) { return 1; /* it's a dir */ } else { return 0;/* exists but is no dir */ } } } //is_dir /*int is_dir(const char *pathname, int follow_link) { //http://sources.gentoo.org/cgi-bin/viewvc.cgi/path-sandbox/trunk/libsbutil/get_tmp_dir.c?revision=260 struct stat buf; int retval; if ((NULL == pathname) || (0 == strlen(pathname))) return 0; retval = follow_link ? stat(pathname, &buf) : lstat(pathname, &buf); if ((-1 != retval) && (S_ISDIR(buf.st_mode))) return 1; if ((-1 == retval) && (ENOENT != errno)) // Some or other error occurred return -1; return 0; } //is_dir() */ bool isDICOMfile(const char * fname) { FILE *fp = fopen(fname, "rb"); if (!fp) return false; fseek(fp, 0, SEEK_END); long long fileLen=ftell(fp); if (fileLen < 256) return false; fseek(fp, 0, SEEK_SET); unsigned char buffer[256]; fread(buffer, 256, 1, fp); fclose(fp); if ((buffer[128] != 'D') || (buffer[129] != 'I') || (buffer[130] != 'C') || (buffer[131] != 'M')) return false; return true; } //isDICOMfile() void geCorrectBvecs(struct TDICOMdata *d, int sliceDir){ //0018,1312 phase encoding is either in row or column direction //0043,1039 (or 0043,a039). b value (as the first number in the string). //0019,10bb (or 0019,a0bb). phase diffusion direction //0019,10bc (or 0019,a0bc). frequency diffusion direction //0019,10bd (or 0019,a0bd). slice diffusion direction //These directions are relative to freq,phase,slice, so although no //transformations are required, you need to check the direction of the //phase encoding. This is in DICOM message 0018,1312. If this has value //COL then if swap the x and y value and reverse the sign on the z value. //If the phase encoding is not COL, then just reverse the sign on the x value. if (d->manufacturer != kMANUFACTURER_GE) return; if (d->CSA.numDti < 1) return; if ((toupper(d->patientOrient[0])== 'H') && (toupper(d->patientOrient[1])== 'F') && (toupper(d->patientOrient[2])== 'S')) ; //participant was head first supine else { #ifdef myUseCOut std::cout<<"GE DTI directions require head first supine acquisition" <<std::endl; #else printf("GE DTI directions require head first supine acquisition\n"); #endif return; } bool col = false; if (d->phaseEncodingRC== 'C') col = true; else if (d->phaseEncodingRC!= 'R') { #ifdef myUseCOut std::cout<<"Error: Unable to determine DTI gradients, 0018,1312 should be either R or C" <<std::endl; #else printf("Error: Unable to determine DTI gradients, 0018,1312 should be either R or C"); #endif return; } if (abs(sliceDir) != 3) #ifdef myUseCOut std::cout<<"GE DTI gradients only tested for axial acquisitions" <<std::endl; #else printf("GE DTI gradients only tested for axial acquisitions"); #endif //printf("GE row(0) or column(1) = %d",col); #ifdef myUseCOut std::cout<<"Reorienting "<< d->CSA.numDti << "GE DTI gradients. Please validate if you are conducting DTI analyses. isCol="<<col <<std::endl; #else printf("Reorienting %d GE DTI gradients. Please validate if you are conducting DTI analyses. isCol=%d\n", d->CSA.numDti, col); #endif for (int i = 0; i < d->CSA.numDti; i++) { float vLen = sqrt( (d->CSA.dtiV[i][1]*d->CSA.dtiV[i][1]) + (d->CSA.dtiV[i][2]*d->CSA.dtiV[i][2]) + (d->CSA.dtiV[i][3]*d->CSA.dtiV[i][3])); if ((d->CSA.dtiV[i][0] <= FLT_EPSILON)|| (vLen <= FLT_EPSILON) ) { //bvalue=0 for (int v= 0; v < 4; v++) d->CSA.dtiV[i][v] =0.0f; continue; //do not normalize or reorient b0 vectors } d->CSA.dtiV[i][1] = -d->CSA.dtiV[i][1]; if (!col) { //rows need to be swizzled float swap = d->CSA.dtiV[i][1]; d->CSA.dtiV[i][1] = -d->CSA.dtiV[i][2]; d->CSA.dtiV[i][2] = swap; } if (sliceDir < 0) d->CSA.dtiV[i][3] = -d->CSA.dtiV[i][3]; if (isSameFloat(d->CSA.dtiV[i][1],-0)) d->CSA.dtiV[i][1] = 0.0f; if (isSameFloat(d->CSA.dtiV[i][2],-0)) d->CSA.dtiV[i][2] = 0.0f; if (isSameFloat(d->CSA.dtiV[i][3],-0)) d->CSA.dtiV[i][3] = 0.0f; } } //geCorrectBvecs() /*void philipsCorrectBvecs(struct TDICOMdata *d){ //Philips DICOM data stored in patient (LPH) space, regardless of settings in Philips user interface //algorithm from PARtoNRRD/CATNAP (with July 20, 2007 patch) http://godzilla.kennedykrieger.org/~jfarrell/software_web.htm //0018,5100. patient orientation - 'HFS' //2001,100B Philips slice orientation (TRANSVERSAL, AXIAL, SAGITTAL) //2005,1071 MRStackAngulationAP //2005,1072 MRStackAngulationFH //2005,1073 MRStackAngulationRL if (d->manufacturer != kMANUFACTURER_PHILIPS) return; if (d->CSA.numDti < 1) return; mat33 tpp,tpo; if ((toupper(d->patientOrient[0])== 'F') && (toupper(d->patientOrient[1])== 'F')) LOAD_MAT33(tpp, 0,-1,0, -1,0,0, 0,0,1); //feet first else if ((toupper(d->patientOrient[0])== 'H') && (toupper(d->patientOrient[1])== 'F')) LOAD_MAT33(tpp, 0,1,0,-1,0,0, 0,0,-1); //head first else { printf("Unable to correct Philips DTI vectors: patient position must be head or feet first\n"); return; } //unused? mat33 rev_tpp =nifti_mat33_transpose(tpp); if (toupper(d->patientOrient[2])== 'S')//supine LOAD_MAT33 (tpo, 1,0,0, 0,1,0, 0,0,1); else if (toupper(d->patientOrient[2])== 'P')//prone LOAD_MAT33 (tpo,-1,0,0, 0,-1,0, 0,0,1); else if ((toupper(d->patientOrient[2])== 'D') && (toupper(d->patientOrient[3])== 'R')) //DR LOAD_MAT33 (tpo,0,-1,0, 1,0,0, 0,0,1); else if ((toupper(d->patientOrient[2])== 'D') && (toupper(d->patientOrient[3])== 'L'))//DL LOAD_MAT33 (tpo,0,1,0, -1,0,0, 0,0,1); else { printf("DTI vector error: Position is not HFS,HFP,HFDR,HFDL,FFS,FFP,FFDR, or FFDL: %s\n",d->patientOrient); return; } //unused? mat33 rev_tpo =nifti_mat33_transpose(tpo); //unused? mat33 tpom = nifti_mat33_mul( tpo, tpp); //unused? mat33 rev_tpom = nifti_mat33_mul( rev_tpp,rev_tpo ); printf("Reorienting %d Philip DTI gradients with angulations %f %f %f. Please validate if you are conducting DTI analyses.\n", d->CSA.numDti, d->angulation[1], d->angulation[2], d->angulation[3]); float rl = d->angulation[1] * M_PI /180; //as radian float ap = d->angulation[2] * M_PI /180; float fh = d->angulation[3] * M_PI /180; //printf(" %f %f %f \n",ap,fh,rl); mat33 trl, tap, tfh, rev_tsom, dtiextra; LOAD_MAT33 (trl,1,0,0, 0, cos(rl),- sin(rl), 0, sin(rl),cos(rl)); LOAD_MAT33 (tap, cos(ap),0, sin(ap), 0,1,0, - sin(ap),0, cos(ap)); LOAD_MAT33 (tfh,cos(fh),- sin(fh),0, sin(fh), cos(fh),0, 0,0,1); mat33 rev_trl =nifti_mat33_transpose(trl); mat33 rev_tap =nifti_mat33_transpose(tap); mat33 rev_tfh =nifti_mat33_transpose(tfh); mat33 mtemp1 = nifti_mat33_mul( trl, tap); //unused? mat33 tang = nifti_mat33_mul( mtemp1, tfh); mtemp1 = nifti_mat33_mul( rev_tfh, rev_tap ); mat33 rev_tang = nifti_mat33_mul( mtemp1, rev_trl); //kSliceOrientSag if (d->sliceOrient == kSliceOrientSag)//SAGITTAL LOAD_MAT33 (rev_tsom, 0,0,1, 0,-1,0, -1,0,0 ); else if (d->sliceOrient == 2)//CORONAL LOAD_MAT33 (rev_tsom, 0,0,1, -1,0,0, 0,1,0 ); else LOAD_MAT33 (rev_tsom, 0,-1,0, -1,0,0, 0,0,1 ); LOAD_MAT33 (dtiextra, 0,-1,0, -1,0,0, 0,0,1 ); mat33 mtemp2 = nifti_mat33_mul( dtiextra, rev_tsom); mtemp1 = nifti_mat33_mul( mtemp2, rev_tang); for (int i = 0; i < d->CSA.numDti; i++) { //printf("%d\tvin=[\t%f\t%f\t%f\t]; bval=\t%f\n",i,d->CSA.dtiV[i][1],d->CSA.dtiV[i][2],d->CSA.dtiV[i][3],d->CSA.dtiV[i][0]); float vLen = sqrt( (d->CSA.dtiV[i][1]*d->CSA.dtiV[i][1]) + (d->CSA.dtiV[i][2]*d->CSA.dtiV[i][2]) + (d->CSA.dtiV[i][3]*d->CSA.dtiV[i][3])); if ((d->CSA.dtiV[i][0] <= FLT_EPSILON)|| (vLen <= FLT_EPSILON) ) { //bvalue=0 for (int v= 0; v < 4; v++) d->CSA.dtiV[i][v] =0.0f; continue; //do not normalize or reorient b0 vectors } vec3 v3; for (int v= 1; v < 4; v++) //normalize and reverse vector directions v3.v[v-1] =-d->CSA.dtiV[i][v]/vLen; v3 = nifti_vect33mat33_mul(v3,mtemp1); v3 = nifti_vect33_norm(v3); d->CSA.dtiV[i][1] = v3.v[0]; d->CSA.dtiV[i][2] = v3.v[1]; //NIfTI Y reversed relative to DICOM d->CSA.dtiV[i][3] = v3.v[2]; //printf("%d\tvoutt=[\t%f\t%f\t%f\t]; bval=\t%f\n",i,d->CSA.dtiV[i][1],d->CSA.dtiV[i][2],d->CSA.dtiV[i][3],d->CSA.dtiV[i][0]); } if ( d->sliceOrient != kSliceOrientTra) printf("Warning: Philips DTI gradients only evaluated for axial (transverse) acquisitions. Please verify sign and direction\n"); } //philipsCorrectBvecs() */ void siemensPhilipsCorrectBvecs(struct TDICOMdata *d, int sliceDir){ //see Matthew Robson's http://users.fmrib.ox.ac.uk/~robson/internal/Dicom2Nifti111.m //convert DTI vectors from scanner coordinates to image frame of reference //Uses 6 orient values from ImageOrientationPatient (0020,0037) // requires PatientPosition 0018,5100 is HFS (head first supine) if ((d->manufacturer != kMANUFACTURER_SIEMENS) && (d->manufacturer != kMANUFACTURER_PHILIPS)) return; if (d->CSA.numDti < 1) return; if ((toupper(d->patientOrient[0])== 'H') && (toupper(d->patientOrient[1])== 'F') && (toupper(d->patientOrient[2])== 'S')) ; //participant was head first supine else { #ifdef myUseCOut std::cout<<"Siemens/Philips DTI directions require head first supine acquisition"<<std::endl; #else printf("Siemens/Philips DTI directions require head first supine acquisition\n"); #endif return; } vec3 read_vector = setVec3(d->orient[1],d->orient[2],d->orient[3]); vec3 phase_vector = setVec3(d->orient[4],d->orient[5],d->orient[6]); vec3 slice_vector = crossProduct(read_vector ,phase_vector); read_vector = nifti_vect33_norm(read_vector); phase_vector = nifti_vect33_norm(phase_vector); slice_vector = nifti_vect33_norm(slice_vector); for (int i = 0; i < d->CSA.numDti; i++) { float vLen = sqrt( (d->CSA.dtiV[i][1]*d->CSA.dtiV[i][1]) + (d->CSA.dtiV[i][2]*d->CSA.dtiV[i][2]) + (d->CSA.dtiV[i][3]*d->CSA.dtiV[i][3])); if ((d->CSA.dtiV[i][0] <= FLT_EPSILON)|| (vLen <= FLT_EPSILON) ) { //bvalue=0 for (int v= 0; v < 4; v++) d->CSA.dtiV[i][v] =0.0f; continue; //do not normalize or reorient b0 vectors }//if bvalue=0 vec3 bvecs_old =setVec3(d->CSA.dtiV[i][1],d->CSA.dtiV[i][2],d->CSA.dtiV[i][3]); vec3 bvecs_new =setVec3(dotProduct(bvecs_old,read_vector),dotProduct(bvecs_old,phase_vector),dotProduct(bvecs_old,slice_vector) ); bvecs_new = nifti_vect33_norm(bvecs_new); d->CSA.dtiV[i][1] = bvecs_new.v[0]; d->CSA.dtiV[i][2] = -bvecs_new.v[1]; d->CSA.dtiV[i][3] = bvecs_new.v[2]; if (sliceDir == kSliceOrientMosaicNegativeDeterminant) d->CSA.dtiV[i][2] = -d->CSA.dtiV[i][2]; for (int v= 0; v < 4; v++) if (d->CSA.dtiV[i][v] == -0.0f) d->CSA.dtiV[i][v] = 0.0f; //remove sign from values that are virtually zero } //for each direction #ifdef myUseCOut if (sliceDir == kSliceOrientMosaicNegativeDeterminant) std::cout<<"WARNING: please validate DTI vectors (matrix had a negative determinant, perhaps Siemens sagittal)."<<std::endl; else if ( d->sliceOrient == kSliceOrientTra) std::cout<<"Saving "<<d->CSA.numDti<<" DTI gradients. Please validate if you are conducting DTI analyses."<<std::endl; else std::cout<<"WARNING: DTI gradient directions only tested for axial (transverse) acquisitions. Please validate bvec files."<<std::endl; #else if (sliceDir == kSliceOrientMosaicNegativeDeterminant) printf("WARNING: please validate DTI vectors (matrix had a negative determinant, perhaps Siemens sagittal).\n"); else if ( d->sliceOrient == kSliceOrientTra) printf("Saving %d DTI gradients. Please validate if you are conducting DTI analyses.\n", d->CSA.numDti); else printf("WARNING: DTI gradient directions only tested for axial (transverse) acquisitions. Please validate bvec files.\n"); #endif } //siemensPhilipsCorrectBvecs() bool isSamePosition (struct TDICOMdata d, struct TDICOMdata d2){ if (!isSameFloat(d.patientPosition[1],d2.patientPosition[1])) return false; if (!isSameFloat(d.patientPosition[2],d2.patientPosition[2])) return false; if (!isSameFloat(d.patientPosition[3],d2.patientPosition[3])) return false; return true; } //isSamePosition() bool nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[], struct TDCMopts opts, int sliceDir) { //reports true if last volume is excluded (e.g. philip stores an ADC map) //to do: works with 3D mosaics and 4D files, must remove repeated volumes for 2D sequences.... uint64_t indx0 = dcmSort[0].indx; //first volume int numDti = dcmList[indx0].CSA.numDti; if (numDti < 1) return false; if ((numDti < 3) && (nConvert < 3)) return false; if (numDti == 1) {//extract DTI from different slices numDti = 0; for (int i = 0; i < nConvert; i++) { //for each image if ((dcmList[indx0].CSA.mosaicSlices > 1) || (isSamePosition(dcmList[indx0],dcmList[dcmSort[i].indx]))) { if (numDti < kMaxDTIv) for (int v = 0; v < 4; v++) //for each vector+B-value dcmList[indx0].CSA.dtiV[numDti][v] = dcmList[dcmSort[i].indx].CSA.dtiV[0][v]; numDti++; } //for slices with repeats }//for each file dcmList[indx0].CSA.numDti = numDti; } if (numDti < 3) return false; if (numDti > kMaxDTIv) { #ifdef myUseCOut std::cout<<"Error: more than "<<kMaxDTIv<<" DTI directions detected (check for a new software)"<<std::endl; #else printf("Error: more than %d DTI directions detected (check for a new software)", kMaxDTIv); #endif return false; } bool bValueVaries = false; for (int i = 1; i < numDti; i++) //check if all bvalues match first volume if (dcmList[indx0].CSA.dtiV[i][0] != dcmList[indx0].CSA.dtiV[0][0]) bValueVaries = true; if (!bValueVaries) { for (int i = 1; i < numDti; i++) printf("bxyz %g %g %g %g\n",dcmList[indx0].CSA.dtiV[i][0],dcmList[indx0].CSA.dtiV[i][1],dcmList[indx0].CSA.dtiV[i][2],dcmList[indx0].CSA.dtiV[i][3]); printf("Error: only one B-value reported for all volumes: %g\n",dcmList[indx0].CSA.dtiV[0][0]); return false; } int firstB0 = -1; for (int i = 0; i < numDti; i++) //check if all bvalues match first volume if (isSameFloat(dcmList[indx0].CSA.dtiV[i][0],0) ) { firstB0 = i; break; } //printf("2015ALPHA %d -> %d\n",numDti, nConvert); #ifdef myUseCOut if (firstB0 < 0) std::cout<<"Warning: this diffusion series does not have a B0 (reference) volume"<<std::endl; if (firstB0 > 0) std::cout<<"Note: B0 not the first volume in the series (FSL eddy reference volume is "<<firstB0<<")"<<std::endl; #else if (firstB0 < 0) printf("Warning: this diffusion series does not have a B0 (reference) volume\n"); if (firstB0 > 0) printf("Note: B0 not the first volume in the series (FSL eddy reference volume is %d)\n", firstB0); #endif bool isFinalADC = false; /*if ((dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_PHILIPS) && (!isSameFloat(dcmList[indx0].CSA.dtiV[numDti-1][0],0.0f)) ) printf("xxx-->%f\n", dcmList[indx0].CSA.dtiV[numDti-1][3]);*/ if ((dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_PHILIPS) && (!isSameFloat(dcmList[indx0].CSA.dtiV[numDti-1][0],0.0f)) //not a B-0 image && ((isSameFloat(dcmList[indx0].CSA.dtiV[numDti-1][1],0.0f)) || (isSameFloat(dcmList[indx0].CSA.dtiV[numDti-1][2],0.0f)) || (isSameFloat(dcmList[indx0].CSA.dtiV[numDti-1][3],0.0f)) )) {//yet all vectors are zero!!!! must be ADC isFinalADC = true; //final volume is ADC map numDti --; //remove final volume - it is a computed ADC map! dcmList[indx0].CSA.numDti = numDti; } // philipsCorrectBvecs(&dcmList[indx0]); //<- replaced by unified siemensPhilips solution geCorrectBvecs(&dcmList[indx0],sliceDir); siemensPhilipsCorrectBvecs(&dcmList[indx0],sliceDir); if (opts.isVerbose) { for (int i = 0; i < (numDti-1); i++) { #ifdef myUseCOut std::cout<<i<<"\tB=\t"<<dcmList[indx0].CSA.dtiV[i][0]<<"\tVec=\t"<< dcmList[indx0].CSA.dtiV[i][1]<<"\t"<<dcmList[indx0].CSA.dtiV[i][2] <<"\t"<<dcmList[indx0].CSA.dtiV[i][3]<<std::endl; #else printf("%d\tB=\t%g\tVec=\t%g\t%g\t%g\n",i, dcmList[indx0].CSA.dtiV[i][0], dcmList[indx0].CSA.dtiV[i][1],dcmList[indx0].CSA.dtiV[i][2],dcmList[indx0].CSA.dtiV[i][3]); #endif } //for each direction } if (!opts.isFlipY ) { //!FLIP_Y&& (dcmList[indx0].CSA.mosaicSlices < 2) mosaics are always flipped in the Y direction for (int i = 0; i < (numDti-1); i++) { if (fabs(dcmList[indx0].CSA.dtiV[i][2]) > FLT_EPSILON) dcmList[indx0].CSA.dtiV[i][2] = -dcmList[indx0].CSA.dtiV[i][2]; } //for each direction } //if not a mosaic //printf("%f\t%f\t%f",dcmList[indx0].CSA.dtiV[1][1],dcmList[indx0].CSA.dtiV[1][2],dcmList[indx0].CSA.dtiV[1][3]); char txtname[2048] = {""}; strcpy (txtname,pathoutname); strcat (txtname,".bval"); //printf("Saving DTI %s\n",txtname); FILE *fp = fopen(txtname, "w"); if (fp == NULL) return isFinalADC; for (int i = 0; i < (numDti-1); i++) fprintf(fp, "%g\t", dcmList[indx0].CSA.dtiV[i][0]); fprintf(fp, "%g\n", dcmList[indx0].CSA.dtiV[numDti-1][0]); fclose(fp); strcpy(txtname,pathoutname); strcat (txtname,".bvec"); //printf("Saving DTI %s\n",txtname); fp = fopen(txtname, "w"); if (fp == NULL) return isFinalADC; for (int v = 1; v < 4; v++) { for (int i = 0; i < (numDti-1); i++) fprintf(fp, "%g\t", dcmList[indx0].CSA.dtiV[i][v]); fprintf(fp, "%g\n", dcmList[indx0].CSA.dtiV[numDti-1][v]); } fclose(fp); return isFinalADC; } //nii_SaveDTI() float sqr(float v){ return v*v; } //sqr() float intersliceDistance(struct TDICOMdata d1, struct TDICOMdata d2) { //some MRI scans have gaps between slices, some CT have overlapping slices. Comparing adjacent slices provides measure for dx between slices return sqrt( sqr(d1.patientPosition[1]-d2.patientPosition[1])+ sqr(d1.patientPosition[2]-d2.patientPosition[2])+ sqr(d1.patientPosition[3]-d2.patientPosition[3])); } //intersliceDistance() void swapDim3Dim4(int d3, int d4, struct TDCMsort dcmSort[]) { //swap space and time: input A0,A1...An,B0,B1...Bn output A0,B0,A1,B1,... int nConvert = d3 * d4; struct TDCMsort dcmSortIn[nConvert]; for (int i = 0; i < nConvert; i++) dcmSortIn[i] = dcmSort[i]; int i = 0; for (int b = 0; b < d3; b++) for (int a = 0; a < d4; a++) { int k = (a *d3) + b; //printf("%d -> %d %d ->%d\n",i,a, b, k); dcmSort[k] = dcmSortIn[i]; i++; } } //swapDim3Dim4() bool intensityScaleVaries(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[]){ //detect whether some DICOM images report different intensity scaling //some Siemens PET scanners generate 16-bit images where slice has its own scaling factor. // since NIfTI provides a single scaling factor for each file, these images require special consideration if (nConvert < 2) return false; bool iVaries = false; float iScale = dcmList[dcmSort[0].indx].intenScale; float iInter = dcmList[dcmSort[0].indx].intenIntercept; for (int i = 1; i < nConvert; i++) { //stack additional images uint64_t indx = dcmSort[i].indx; if (fabs (dcmList[indx].intenScale - iScale) > FLT_EPSILON) iVaries = true; if (fabs (dcmList[indx].intenIntercept- iInter) > FLT_EPSILON) iVaries = true; } return iVaries; } //intensityScaleVaries() /*unsigned char * nii_bgr2rgb(unsigned char* bImg, struct nifti_1_header *hdr) { //DICOM planarappears to be BBB..B,GGG..G,RRR..R, NIfTI RGB saved in planes RRR..RGGG..GBBBB..B // see http://www.barre.nom.fr/medical/samples/index.html US-RGB-8-epicard if (hdr->datatype != DT_RGB24) return bImg; int dim3to7 = 1; for (int i = 3; i < 8; i++) if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i]; int sliceBytes24 = hdr->dim[1]*hdr->dim[2] * hdr->bitpix/8; int sliceBytes8 = hdr->dim[1]*hdr->dim[2]; //Byte bImg[ bSz ]; //[img getBytes:&bImg length:bSz]; unsigned char slice24[sliceBytes24]; int sliceOffsetR = 0; for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice memcpy(&slice24, &bImg[sliceOffsetR], sliceBytes24); memcpy( &bImg[sliceOffsetR], &slice24[sliceBytes8*2], sliceBytes8); sliceOffsetR += sliceBytes8; memcpy( &bImg[sliceOffsetR], &slice24[sliceBytes8], sliceBytes8); sliceOffsetR += sliceBytes8; memcpy( &bImg[sliceOffsetR], &slice24[0], sliceBytes8); sliceOffsetR += sliceBytes8; } //for each slice return bImg; } //nii_ImgBytes()*/ bool niiExists(const char*pathoutname) { char niiname[2048] = {""}; strcat (niiname,pathoutname); strcat (niiname,".nii"); if (is_fileexists(niiname)) return true; char gzname[2048] = {""}; strcat (gzname,pathoutname); strcat (gzname,".nii.gz"); if (is_fileexists(gzname)) return true; return false; } //niiExists() int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopts opts) { char pth[1024] = {""}; if (strlen(opts.outdir) > 0) { strcpy(pth, opts.outdir); int w =access(pth,W_OK); if (w != 0) { if (getcwd(pth, sizeof(pth)) != NULL) { w =access(pth,W_OK); if (w != 0) { #ifdef myUseCOut std::cout<<"Error: you do not have write permissions for the directory "<<opts.outdir<<std::endl; #else printf("Error: you do not have write permissions for the directory %s\n",opts.outdir); #endif return EXIT_FAILURE; } #ifdef myUseCOut std::cout<<"Warning: "<<opts.outdir<<" write permission denied. Saving to working directory "<<pth<<std::endl; #else printf("Warning: %s write permission denied. Saving to working directory %s \n", opts.outdir, pth); #endif } } } char inname[1024] = {""};//{"test%t_%av"}; //% a = acquisition, %n patient name, %t time strcpy(inname, opts.filename); char outname[1024] = {""}; char newstr[256]; if (strlen(inname) < 1) { strcpy(inname, "T%t_N%n_S%s"); } int start = 0; int pos = 0; while (pos<strlen(inname)) { if (inname[pos] == '%') { if (pos > start) { strncpy(&newstr[0], &inname[0] + start, pos - start); newstr[pos - start] = '\0'; strcat (outname,newstr); } pos++; //extra increment: skip both % and following character char f = 'P'; if (pos<strlen(inname)) f = toupper(inname[pos]); if (f == 'C') strcat (outname,dcm.imageComments); if (f == 'F') strcat (outname,opts.indirParent); if (f == 'I') strcat (outname,dcm.patientID); if (f == 'N') strcat (outname,dcm.patientName); if (f == 'P') strcat (outname,dcm.protocolName); if ((f >= '0') && (f <= '9')) { if ((pos<strlen(inname)) && (toupper(inname[pos+1]) == 'S')) { char zeroPad[12] = {""}; sprintf(zeroPad,"%%0%dd",atoi(&f)); sprintf(newstr, zeroPad, dcm.seriesNum); strcat (outname,newstr); pos++; // e.g. %3f requires extra increment: skip both number and following character } } if (f == 'S') { sprintf(newstr, "%ld", dcm.seriesNum); strcat (outname,newstr); } if (f == 'T') { sprintf(newstr, "%0.0f", dcm.dateTime); strcat (outname,newstr); } start = pos + 1; } //found a % character pos++; } //for each character in input if (pos > start) { //append any trailing characters strncpy(&newstr[0], &inname[0] + start, pos - start); newstr[pos - start] = '\0'; strcat (outname,newstr); } if (strlen(outname) < 1) strcpy(outname, "dcm2nii_invalidName"); if (outname[0] == '.') outname[0] = '_'; //make sure not a hidden file //eliminate illegal characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx for (int pos = 0; pos<strlen(outname); pos ++) if ((outname[pos] == '<') || (outname[pos] == '>') || (outname[pos] == ':') || (outname[pos] == '"') || (outname[pos] == '\\') || (outname[pos] == '/') || (outname[pos] == '*') || (outname[pos] == '|') || (outname[pos] == '?')) outname[pos] = '_'; //printf("name=*%s* %d %d\n", outname, pos,start); char baseoutname[2048] = {""}; strcat (baseoutname,pth); char appendChar[2] = {"a"}; appendChar[0] = kPathSeparator; if (pth[strlen(pth)-1] != kPathSeparator) strcat (baseoutname,appendChar); strcat (baseoutname,outname); char pathoutname[2048] = {""}; strcat (pathoutname,baseoutname); int i = 0; while (niiExists(pathoutname) && (i < 26)) { strcpy(pathoutname,baseoutname); appendChar[0] = 'a'+i; strcat (pathoutname,appendChar); i++; } if (i >= 26) { #ifdef myUseCOut std::cout<<"Error: too many NIFTI images with the name "<<baseoutname<<std::endl; #else printf("Error: too many NIFTI images with the name %s\n", baseoutname); #endif return EXIT_FAILURE; } //printf("-->%s\n",pathoutname); return EXIT_SUCCESS; //printf("outname=%s\n", pathoutname); strcpy(niiFilename,pathoutname); return EXIT_SUCCESS; } //nii_createFilename() void nii_createDummyFilename(char * niiFilename, struct TDCMopts opts) { //generate string that illustrating sample of filename struct TDICOMdata dcm = clear_dicom_data(); strcpy(opts.indirParent,"myFolder"); char niiFilenameBase[1024] = {"/usr/myFolder/dicom.dcm"}; nii_createFilename(dcm, niiFilenameBase, opts) ; strcpy(niiFilename,"Example output filename: '"); strcat(niiFilename,niiFilenameBase); if (opts.isGz) strcat(niiFilename,".nii.gz'"); else strcat(niiFilename,".nii'"); } //nii_createDummyFilename() #ifndef myDisableZLib void writeNiiGz (char * baseName, struct nifti_1_header hdr, unsigned char* src_buffer, unsigned long src_len) { //create gz file in RAM, save to disk http://www.zlib.net/zlib_how.html // in general this single-threaded approach is slower than PIGZ but is useful for slow (network attached) disk drives char fname[2048] = {""}; strcpy (fname,baseName); strcat (fname,".nii.gz"); unsigned long hdrPadBytes = sizeof(hdr) + 4; //348 byte header + 4 byte pad unsigned long cmp_len = compressBound(src_len+hdrPadBytes); unsigned char *pCmp = (unsigned char *)malloc(cmp_len); z_stream strm; uLong file_crc32 = crc32(0L, Z_NULL, 0); //strm.adler = crc32(0L, Z_NULL, 0); strm.total_in = 0; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.next_out = pCmp; // output char array strm.avail_out = (unsigned int)cmp_len; // size of output //if ( deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15, 8, Z_DEFAULT_STRATEGY)!= Z_OK) return; if (deflateInit(&strm, Z_DEFAULT_COMPRESSION)!= Z_OK) return; //add header unsigned char *pHdr = (unsigned char *)malloc(hdrPadBytes); memcpy(pHdr,&hdr, sizeof(hdr)); strm.avail_in = (unsigned int)hdrPadBytes; // size of input strm.next_in = (Bytef *)pHdr; // input header deflate(&strm, Z_NO_FLUSH); file_crc32 = crc32(file_crc32, pHdr, (unsigned int)hdrPadBytes); //add image strm.avail_in = (unsigned int)src_len; // size of input strm.next_in = (Bytef *)src_buffer; // input image deflate(&strm, Z_FINISH); //Z_NO_FLUSH; //finish up deflateEnd(&strm); file_crc32 = crc32(file_crc32, src_buffer, (unsigned int)src_len); cmp_len = strm.total_out; if (cmp_len <= 0) return; FILE *fileGz = fopen(fname, "wb"); if (!fileGz) return; //write header http://www.gzip.org/zlib/rfc-gzip.html fputc((char)0x1f, fileGz); //ID1 fputc((char)0x8b, fileGz); //ID2 fputc((char)0x08, fileGz); //CM - use deflate compression method fputc((char)0x00, fileGz); //FLG - no addition fields fputc((char)0x00, fileGz); //MTIME0 fputc((char)0x00, fileGz); //MTIME1 fputc((char)0x00, fileGz); //MTIME2 fputc((char)0x00, fileGz); //MTIME2 fputc((char)0x00, fileGz); //XFL fputc((char)0xff, fileGz); //OS //write Z-compressed data fwrite (&pCmp[2] , sizeof(char), cmp_len-6, fileGz); //-6 as LZ78 format has 2 bytes header and 4 bytes tail //write tail: write redundancy check and uncompressed size as bytes to ensure LITTLE-ENDIAN order fputc((unsigned char)(file_crc32), fileGz); fputc((unsigned char)(file_crc32 >> 8), fileGz); fputc((unsigned char)(file_crc32 >> 16), fileGz); fputc((unsigned char)(file_crc32 >> 24), fileGz); fputc((unsigned char)(strm.total_in), fileGz); fputc((unsigned char)(strm.total_in >> 8), fileGz); fputc((unsigned char)(strm.total_in >> 16), fileGz); fputc((unsigned char)(strm.total_in >> 24), fileGz); fclose(fileGz); free(pCmp); } //writeNiiGz() #endif int nii_saveNII(char * niiFilename, struct nifti_1_header hdr, unsigned char* im, struct TDCMopts opts) { hdr.vox_offset = 352; size_t imgsz = nii_ImgBytes(hdr); #ifndef myDisableZLib if ((opts.isGz) && (strlen(opts.pigzname) < 1) && ((imgsz+hdr.vox_offset) < 2147483647) ) { //use internal compressor writeNiiGz (niiFilename, hdr, im,imgsz); return EXIT_SUCCESS; } #endif char fname[2048] = {""}; strcpy (fname,niiFilename); strcat (fname,".nii"); FILE *fp = fopen(fname, "wb"); if (!fp) return EXIT_FAILURE; fwrite(&hdr, sizeof(hdr), 1, fp); uint32_t pad = 0; fwrite(&pad, sizeof( pad), 1, fp); fwrite(&im[0], imgsz, 1, fp); fclose(fp); if ((opts.isGz) && (strlen(opts.pigzname) > 0) ) { char command[768]; strcpy(command, "\"" ); strcat(command, opts.pigzname ); strcat(command, "\" \""); strcat(command, fname); strcat(command, "\""); //add quotes in case spaces in filename 'pigz "c:\my dir\img.nii"' #if defined(_WIN64) || defined(_WIN32) //using CreateProcess instead of system to run in background (avoids screen flicker) PROCESS_INFORMATION ProcessInfo; //This is what we get as an [out] parameter STARTUPINFO StartupInfo; //This is an [in] parameter ZeroMemory(&StartupInfo, sizeof(StartupInfo)); StartupInfo.cb = sizeof StartupInfo ; //Only compulsory field if(CreateProcess(NULL, command, NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL, NULL,&StartupInfo,&ProcessInfo)) { WaitForSingleObject(ProcessInfo.hProcess,INFINITE); CloseHandle(ProcessInfo.hThread); CloseHandle(ProcessInfo.hProcess); } else #ifdef myUseCOut std::cout<<"compression failed "<<command<<std::endl; #else printf("compression failed %s\n",command); #endif #else //if win else linux system(command); #endif //else linux #ifdef myUseCOut std::cout<<"compress: "<<command<<std::endl; #else printf("compress: %s\n",command); #endif } return EXIT_SUCCESS; } //nii_saveNII() int nii_saveNII3D(char * niiFilename, struct nifti_1_header hdr, unsigned char* im, struct TDCMopts opts) { //save 4D series as sequence of 3D volumes struct nifti_1_header hdr1 = hdr; int nVol = 1; for (int i = 4; i < 8; i++) { if (hdr.dim[i] > 1) nVol = nVol * hdr.dim[i]; hdr1.dim[i] = 0; } hdr1.dim[0] = 3; //save as 3D file size_t imgsz = nii_ImgBytes(hdr1); size_t pos = 0; char fname[2048] = {""}; char zeroPad[1024] = {""}; int zeroPadLen = (1 + log10(nVol)); sprintf(zeroPad,"%%s_%%0%dd",zeroPadLen); for (int i = 1; i <= nVol; i++) { sprintf(fname,zeroPad,niiFilename,i); if (nii_saveNII(fname, hdr1, (unsigned char*)&im[pos], opts) == EXIT_FAILURE) return EXIT_FAILURE; pos += imgsz; } return EXIT_SUCCESS; } //nii_saveNII3D void nii_check16bitUnsigned(unsigned char *img, struct nifti_1_header *hdr){ //default NIfTI 16-bit is signed, set to unusual 16-bit unsigned if required... if (hdr->datatype != DT_UINT16) return; int dim3to7 = 1; for (int i = 3; i < 8; i++) if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i]; int nVox = hdr->dim[1]*hdr->dim[2]* dim3to7; if (nVox < 1) return; unsigned short * img16 = (unsigned short*) img; unsigned short max16 = img16[0]; //clock_t start = clock(); for (int i=0; i < nVox; i++) if (img16[i] > max16) max16 = img16[i]; //printf("max16= %d vox=%d %fms\n",max16, nVox, ((double)(clock()-start))/1000); if (max16 > 32767) #ifdef myUseCOut std::cout<<"Note: intensity range requires saving as rare 16-bit UNSIGNED integer. Subsequent tools may require 32-bit conversion"<<std::endl; #else printf("Note: intensity range requires saving as rare 16-bit UNSIGNED integer. Subsequent tools may require 32-bit conversion\n"); #endif else hdr->datatype = DT_INT16; } //nii_check16bitUnsigned() int siemensCtKludge(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[]) { //Siemens CT bug: when a user draws an open object graphics object onto a 2D slice this is appended as an additional image, //regardless of slice position. These images do not report number of positions in the volume, so we need tedious leg work to detect uint64_t indx0 = dcmSort[0].indx; if ((nConvert < 2) ||(dcmList[indx0].manufacturer != kMANUFACTURER_SIEMENS) || (!isSameFloat(dcmList[indx0].TR ,0.0f))) return nConvert; float prevDx = 0.0; for (int i = 1; i < nConvert; i++) { float dx = intersliceDistance(dcmList[indx0],dcmList[dcmSort[i].indx]); if ((!isSameFloat(dx,0.0f)) && (dx < prevDx)) { #ifdef myUseCOut std::cout<<"Slices skipped: image position not sequential, admonish your vendor (Siemens OOG?)"<<std::endl; #else printf("Slices skipped: image position not sequential, admonish your vendor (Siemens OOG?)\n"); #endif return i; } prevDx = dx; } return nConvert; //all images in sequential order } int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[], struct TSearchList *nameList, struct TDCMopts opts) { bool iVaries = intensityScaleVaries(nConvert,dcmSort,dcmList); uint64_t indx = dcmSort[0].indx; uint64_t indx0 = dcmSort[0].indx; bool saveAs3D = dcmList[indx].isHasPhase; struct nifti_1_header hdr0; unsigned char * img = nii_loadImgX(nameList->str[indx], &hdr0,dcmList[indx], iVaries); if (opts.isVerbose) #ifdef myUseCOut std::cout<<"Converting "<<nameList->str[indx]<<std::endl; #else printf("Converting %s\n",nameList->str[indx]); #endif if (img == NULL) return EXIT_FAILURE; //if (iVaries) img = nii_iVaries(img, &hdr0); size_t imgsz = nii_ImgBytes(hdr0); unsigned char *imgM = (unsigned char *)malloc(imgsz* (uint64_t)nConvert); memcpy(&imgM[0], &img[0], imgsz); free(img); /*if ((nConvert < 2) && (dcmList[indx].locationsInAcquisition > 0)) { //stack philips 4D file int nAcq = dcmList[indx].locationsInAcquisition; if ((hdr0.dim[0] < 4) && ((hdr0.dim[3]%nAcq)==0)) { hdr0.dim[4] = hdr0.dim[3]/nAcq; hdr0.dim[3] = nAcq; hdr0.dim[0] = 4; } if (hdr0.dim[0] > 3) { if (dcmList[indx].patientPositionSequentialRepeats > 1) //swizzle 3rd and 4th dimension (Philips stores time as 3rd dimension) imgM = nii_XYTZ_XYZT(imgM, &hdr0,dcmList[indx].patientPositionSequentialRepeats ); } }*/ //printf(" %d %d %d %d %lu\n", hdr0.dim[1], hdr0.dim[2], hdr0.dim[3], hdr0.dim[4], (unsigned long)[imgM length]); if (nConvert > 1) { if (hdr0.dim[3] < 2) { //stack volumes with multiple acquisitions int nAcq = 1; //Next line works in theory, but fails with Siemens CT that saves pairs of slices as acquisitions, see example "testSiemensStackAcq" // nAcq = 1+abs( dcmList[dcmSort[nConvert-1].indx].acquNum-dcmList[indx0].acquNum); //therefore, the 'same position' is the most robust solution in the real world. if ((dcmList[indx0].manufacturer == kMANUFACTURER_SIEMENS) && (isSameFloat(dcmList[indx0].TR ,0.0f))) { nConvert = siemensCtKludge(nConvert, dcmSort,dcmList); } if ((nAcq == 1 ) && (dcmList[indx0].locationsInAcquisition > 0)) nAcq = nConvert/dcmList[indx0].locationsInAcquisition; if (nAcq < 2 ) { nAcq = 0; for (int i = 0; i < nConvert; i++) if (isSamePosition(dcmList[dcmSort[0].indx],dcmList[dcmSort[i].indx])) nAcq++; } /*int nImg = 1+abs( dcmList[dcmSort[nConvert-1].indx].imageNum-dcmList[dcmSort[0].indx].imageNum); if (((nConvert/nAcq) > 1) && ((nConvert%nAcq)==0) && (nImg == nConvert) && (dcmList[dcmSort[0].indx].locationsInAcquisition == 0) ) { printf(" stacking %d acquisitions as a single volume\n", nAcq); //some Siemens CT scans use multiple acquisitions for a single volume, perhaps also check that slice position does not repeat? hdr0.dim[3] = nConvert; } else*/ if ( (nAcq > 1) && ((nConvert/nAcq) > 1) && ((nConvert%nAcq)==0) ) { hdr0.dim[3] = nConvert/nAcq; hdr0.dim[4] = nAcq; hdr0.dim[0] = 4; } else hdr0.dim[3] = nConvert; float dx = intersliceDistance(dcmList[dcmSort[0].indx],dcmList[dcmSort[1].indx]); if ((hdr0.dim[4] > 0) && (dx ==0) && (dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_PHILIPS)) { swapDim3Dim4(hdr0.dim[3],hdr0.dim[4],dcmSort); dx = intersliceDistance(dcmList[dcmSort[0].indx],dcmList[dcmSort[1].indx]); //printf("swizzling 3rd and 4th dimensions (XYTZ -> XYZT), assuming interslice distance is %f\n",dx); } dcmList[dcmSort[0].indx].xyzMM[3] = dx; //16Sept2014 : correct DICOM for true distance between slice centers: // e.g. MCBI Siemens ToF 0018:0088 reports 16mm SpacingBetweenSlices, but actually 0.5mm if (dx > 0) hdr0.pixdim[3] = dx; } else if (hdr0.dim[4] < 2) { hdr0.dim[4] = nConvert; hdr0.dim[0] = 4; } else { hdr0.dim[5] = nConvert; hdr0.dim[0] = 5; } //printf(" %d %d %d %d %lu\n", hdr0.dim[1], hdr0.dim[2], hdr0.dim[3], hdr0.dim[4], (unsigned long)[imgM length]); struct nifti_1_header hdrI; for (int i = 1; i < nConvert; i++) { //stack additional images indx = dcmSort[i].indx; //if (headerDcm2Nii(dcmList[indx], &hdrI) == EXIT_FAILURE) return EXIT_FAILURE; img = nii_loadImgX(nameList->str[indx], &hdrI, dcmList[indx],iVaries); if ((hdr0.dim[1] != hdrI.dim[1]) || (hdr0.dim[2] != hdrI.dim[2]) || (hdr0.bitpix != hdrI.bitpix)) { #ifdef myUseCOut std::cout<<"Error: image dimensions differ "<<nameList->str[dcmSort[0].indx]<<" "<<nameList->str[indx]<<std::endl; #else printf("Error: image dimensions differ %s %s",nameList->str[dcmSort[0].indx], nameList->str[indx]); #endif return EXIT_FAILURE; } memcpy(&imgM[(uint64_t)i*imgsz], &img[0], imgsz); free(img); } } //printf("Mango %zd\n", (imgsz* nConvert)); return 0; char pathoutname[2048] = {""}; if (nii_createFilename(dcmList[dcmSort[0].indx], pathoutname, opts) == EXIT_FAILURE) return EXIT_FAILURE; if (strlen(pathoutname) <1) return EXIT_FAILURE; int sliceDir = 0; if (hdr0.dim[3] > 1) sliceDir =headerDcm2Nii2(dcmList[dcmSort[0].indx],dcmList[dcmSort[nConvert-1].indx] , &hdr0); if (sliceDir < 0) { imgM = nii_flipZ(imgM, &hdr0); sliceDir = abs(sliceDir); } bool isFinalADC = nii_SaveDTI(pathoutname,nConvert, dcmSort, dcmList, opts, sliceDir); isFinalADC =isFinalADC; //simply to silence compiler warning when myNoSave defined if ((hdr0.datatype == DT_UINT16) && (!dcmList[dcmSort[0].indx].isSigned)) nii_check16bitUnsigned(imgM, &hdr0); #ifdef myUseCOut std::cout<<"Convert "<<nConvert<<" DICOM as "<<pathoutname<< " ("<<hdr0.dim[1]<<"x"<<hdr0.dim[2]<<"x"<<hdr0.dim[3]<<"x"<<hdr0.dim[4]<<")" <<std::endl; #else printf( "Convert %d DICOM as %s (%dx%dx%dx%d)\n", nConvert, pathoutname, hdr0.dim[1],hdr0.dim[2],hdr0.dim[3],hdr0.dim[4]); #endif if (hdr0.dim[3] < 2) #ifdef myUseCOut std::cout<<"WARNING: check that 2D images are not mirrored"<<std::endl; #else printf("WARNING: check that 2D images are not mirrored.\n"); #endif else fflush(stdout); //GUI buffers printf, display all results if ((dcmList[dcmSort[0].indx].is3DAcq) && (hdr0.dim[3] > 1) && (hdr0.dim[0] < 4)) imgM = nii_setOrtho(imgM, &hdr0); //printf("ortho %d\n", echoInt (33)); else if (opts.isFlipY)//(FLIP_Y) //(dcmList[indx0].CSA.mosaicSlices < 2) && imgM = nii_flipY(imgM, &hdr0); else #ifdef myUseCOut std::cout<<"DICOM row order preserved: may appear upside down in tools that ignore spatial transforms"<<std::endl; #else printf("DICOM row order preserved: may appear upside down in tools that ignore spatial transforms\n"); #endif #ifndef myNoSave if ((hdr0.dim[4] > 1) && (saveAs3D)) nii_saveNII3D(pathoutname, hdr0, imgM,opts); else { if ((isFinalADC) && (hdr0.dim[4] > 2)) { //ADC maps can disrupt analysis: save a copy with the ADC map, and another without char pathoutnameADC[2048] = {""}; strcat(pathoutnameADC,pathoutname); strcat(pathoutnameADC,"_ADC"); nii_saveNII(pathoutnameADC, hdr0, imgM, opts); hdr0.dim[4] = hdr0.dim[4]-1; }; nii_saveNII(pathoutname, hdr0, imgM, opts); } #endif free(imgM); return EXIT_SUCCESS; } //saveDcm2Nii() int compareTDCMsort(void const *item1, void const *item2) { //for quicksort http://blog.ablepear.com/2011/11/objective-c-tuesdays-sorting-arrays.html struct TDCMsort const *dcm1 = (const struct TDCMsort *)item1; struct TDCMsort const *dcm2 = (const struct TDCMsort *)item2; if (dcm1->img < dcm2->img) return -1; else if (dcm1->img > dcm2->img) return 1; return 0; //tie } //compareTDCMsort() bool isSameSet (struct TDICOMdata d1, struct TDICOMdata d2) { //returns true if d1 and d2 should be stacked together as a signle output if (!d1.isValid) return false; if (!d2.isValid) return false; if ((d1.dateTime == d2.dateTime) && (d1.seriesNum == d2.seriesNum) && (d1.bitsAllocated == d2.bitsAllocated) && (d1.xyzDim[1] == d2.xyzDim[1]) && (d1.xyzDim[2] == d2.xyzDim[2]) && (d1.xyzDim[3] == d2.xyzDim[3]) ) return true; return false; } //isSameSet() void searchDirForDICOM(char *path, struct TSearchList *nameList, int maxDepth, int depth) { tinydir_dir dir; tinydir_open(&dir, path); while (dir.has_next) { tinydir_file file; tinydir_readfile(&dir, &file); //printf("%s\n", file.name); char filename[768] =""; strcat(filename, path); strcat(filename,kFileSep); strcat(filename, file.name); if ((file.is_dir) && (depth < maxDepth) && (file.name[0] != '.')) searchDirForDICOM(filename, nameList, maxDepth, depth+1); else if (isDICOMfile(filename)) { if (nameList->numItems < nameList->maxItems) { nameList->str[nameList->numItems] = (char *)malloc(strlen(filename)+1); strcpy(nameList->str[nameList->numItems],filename); //printf("OK\n"); } nameList->numItems++; //printf("dcm %lu %s \n",nameList->numItems, filename); } else { #ifdef MY_DEBUG #ifdef myUseCOut std::cout<<"Not a dicom"<< filename <<std::endl; #else printf("Not a dicom:\t%s\n", filename); #endif #endif } tinydir_next(&dir); } tinydir_close(&dir); } //searchDirForDICOM() int removeDuplicates(int nConvert, struct TDCMsort dcmSort[]){ //done AFTER sorting, so duplicates will be sequential if (nConvert < 2) return nConvert; int nDuplicates = 0; for (int i = 1; i < nConvert; i++) { if (dcmSort[i].img == dcmSort[i-1].img) nDuplicates ++; else { dcmSort[i-nDuplicates].img = dcmSort[i].img; dcmSort[i-nDuplicates].indx = dcmSort[i].indx; } } if (nDuplicates > 0) #ifdef myUseCOut std::cout<<"Some images have identical time, series, acquisition and image values. Duplicates removed."<<std::endl; #else printf("Some images have identical time, series, acquisition and image values. Duplicates removed.\n"); #endif return nConvert - nDuplicates; } //removeDuplicates() int removeDuplicatesVerbose(int nConvert, struct TDCMsort dcmSort[], struct TSearchList *nameList){ //done AFTER sorting, so duplicates will be sequential if (nConvert < 2) return nConvert; int nDuplicates = 0; for (int i = 1; i < nConvert; i++) { if (dcmSort[i].img == dcmSort[i-1].img) { #ifdef myUseCOut std::cout<<"\t"<<nameList->str[dcmSort[i-1].indx]<<"\t"<<nameList->str[dcmSort[i].indx] <<std::endl; #else printf("\t%s\t%s\n",nameList->str[dcmSort[i-1].indx],nameList->str[dcmSort[i].indx]); #endif nDuplicates ++; }else { dcmSort[i-nDuplicates].img = dcmSort[i].img; dcmSort[i-nDuplicates].indx = dcmSort[i].indx; } } if (nDuplicates > 0) #ifdef myUseCOut std::cout<<"Some images have identical time, series, acquisition and image values. Duplicates removed."<<std::endl; #else printf("Some images have identical time, series, acquisition and image values. Duplicates removed.\n"); #endif return nConvert - nDuplicates; } //removeDuplicates() int strcicmp(char const *a, char const *b) //case insensitive compare { for (;; a++, b++) { int d = tolower(*a) - tolower(*b); if (d != 0 || !*a) return d; } } //strcicmp() bool isExt (char *file_name, const char* ext) { char *p_extension; if((p_extension = strrchr(file_name,'.')) != NULL ) if(strcicmp(p_extension,ext) == 0) return true; //if(strcmp(p_extension,ext) == 0) return true; return false; } //isExt() int convert_parRec(struct TDCMopts opts) { //sample dataset from Ed Gronenschild <ed.gronenschild@maastrichtuniversity.nl> struct TSearchList nameList; nameList.numItems = 1; nameList.maxItems = 1; nameList.str = (char **) malloc((nameList.maxItems+1) * sizeof(char *)); //we reserve one pointer (32 or 64 bits) per potential file struct TDICOMdata *dcmList = (struct TDICOMdata *)malloc(nameList.numItems * sizeof(struct TDICOMdata)); nameList.str[0] = (char *)malloc(strlen(opts.indir)+1); strcpy(nameList.str[0],opts.indir); dcmList[0] = nii_readParRec(nameList.str[0]); struct TDCMsort dcmSort[1]; dcmSort[0].indx = 0; saveDcm2Nii(1, dcmSort, dcmList, &nameList, opts); free(dcmList);//if (nConvertTotal == 0) if (nameList.numItems < 1) { #ifdef myUseCOut std::cout<<"No valid PAR/REC files were found"<<std::endl; #else printf("No valid PAR/REC files were found\n"); #endif } if (nameList.numItems > 0) for (int i = 0; i < nameList.numItems; i++) free(nameList.str[i]); free(nameList.str); return EXIT_SUCCESS; } //convert_parRec() int nii_loadDir (struct TDCMopts* opts) { //printf("-->%s",opts->filename); //return EXIT_FAILURE; if (strlen(opts->indir) < 1) { #ifdef myUseCOut std::cout<<"No input"<<std::endl; #else printf("No input\n"); #endif return EXIT_FAILURE; } #ifdef myUseCOut std::cout << "Version " <<kDCMvers <<std::endl; #else printf("Version %s\n",kDCMvers); #endif char indir[512]; strcpy(indir,opts->indir); bool isFile = is_fileNotDir(opts->indir); if (isFile) {//if user passes ~/dicom/mr1.dcm we will look at all files in ~/dicom dropFilenameFromPath(opts->indir);//getParentFolder(opts.indir, opts.indir); } if (strlen(opts->outdir) < 1) strcpy(opts->outdir,opts->indir); else if (!is_dir(opts->outdir,true)) { #ifdef myUseCOut std::cout << "Warning: output folder invalid "<< opts->outdir<<" will try %s\n"<< opts->indir <<std::endl; #else printf("Warning: output folder invalid %s will try %s\n",opts->outdir,opts->indir); #endif strcpy(opts->outdir,opts->indir); } /*if (isFile && ((isExt(indir, ".gz")) || (isExt(indir, ".tgz"))) ) { #ifndef myDisableTarGz #ifndef myDisableZLib untargz( indir, opts->outdir); #endif #endif }*/ if (isFile && ((isExt(indir, ".par")) || (isExt(indir, ".rec"))) ) { strcpy(opts->indir, indir); //set to original file name, not path return convert_parRec(*opts); } getFileName(opts->indirParent, opts->indir); struct TSearchList nameList; nameList.numItems = 0; nameList.maxItems = 64000-1; nameList.str = (char **) malloc((nameList.maxItems+1) * sizeof(char *)); //we reserve one pointer (32 or 64 bits) per potential file //1: find filenames of dicom files searchDirForDICOM(opts->indir, &nameList, 5,1); if (nameList.numItems < 1) { #ifdef myUseCOut std::cout << "Error: unable to find any DICOM images in "<< opts->indir <<std::endl; #else printf("Error: unable to find any DICOM images in %s\n", opts->indir); #endif free(nameList.str); return EXIT_FAILURE; } if (nameList.numItems < 1) { #ifdef myUseCOut std::cout << "Overwhelmed: found more than "<<nameList.maxItems<<" DICOM images in " << opts->indir <<std::endl; #else printf("Overwhelmed: found more than %lu DICOM images in %s\n",nameList.maxItems, opts->indir); #endif //goto freeMem; return EXIT_FAILURE; } long long nDcm = nameList.numItems; #ifdef myUseCOut std::cout << "Found "<< nameList.numItems <<" DICOM images" <<std::endl; #else printf( "Found %lu DICOM images\n", nameList.numItems); #endif // struct TDICOMdata dcmList [nameList.numItems]; //<- this exhausts the stack for large arrays struct TDICOMdata *dcmList = (struct TDICOMdata *)malloc(nameList.numItems * sizeof(struct TDICOMdata)); for (int i = 0; i < nameList.numItems; i++ ) dcmList[i] = readDICOMv(nameList.str[i], opts->isVerbose); //3: stack DICOMs with the same Series int nConvertTotal = 0; for (int i = 0; i < nDcm; i++ ) { if ((dcmList[i].converted2NII == 0) && (dcmList[i].isValid)) { int nConvert = 0; for (int j = i; j < nDcm; j++) if (isSameSet(dcmList[i],dcmList[j]) ) nConvert ++; struct TDCMsort dcmSort[nConvert]; nConvert = 0; for (int j = i; j < nDcm; j++) if (isSameSet(dcmList[i],dcmList[j]) ) { dcmSort[nConvert].indx = j; dcmSort[nConvert].img = ((uint64_t)dcmList[j].seriesNum << 32)+ dcmList[j].imageNum; dcmList[j].converted2NII = 1; nConvert ++; } qsort(dcmSort, nConvert, sizeof(struct TDCMsort), compareTDCMsort); //sort based on series and image numbers.... if (opts->isVerbose) nConvert = removeDuplicatesVerbose(nConvert, dcmSort, &nameList); else nConvert = removeDuplicates(nConvert, dcmSort); nConvertTotal += nConvert; saveDcm2Nii(nConvert, dcmSort, dcmList, &nameList, *opts); }//convert all images of this series } free(dcmList); if (nConvertTotal == 0) #ifdef myUseCOut std::cout << "No valid DICOM files were found\n" <<std::endl; #else printf("No valid DICOM files were found\n"); #endif if (nameList.numItems > 0) for (int i = 0; i < nameList.numItems; i++) free(nameList.str[i]); free(nameList.str); return EXIT_SUCCESS; } //nii_loadDir() void readFindPigz (struct TDCMopts *opts, const char * argv[]) { #if defined(_WIN64) || defined(_WIN32) strcpy(opts->pigzname,"pigz.exe"); if (!is_exe(opts->pigzname)) { #ifdef myUseCOut #ifdef myDisableZLib std::cout << "Compression requires "<<opts->pigzname<<" in the same folder as the executable"<<std::endl; #else //myUseZLib std::cout << "Compression will be faster with "<<opts->pigzname<<" in the same folder as the executable "<<std::endl; #endif #else #ifdef myDisableZLib printf("Compression requires %s in the same folder as the executable\n",opts->pigzname); #else //myUseZLib printf("Compression will be faster with %s in the same folder as the executable\n",opts->pigzname); #endif #endif strcpy(opts->pigzname,""); } else strcpy(opts->pigzname,".\\pigz"); //drop #else strcpy(opts->pigzname,"/usr/local/bin/pigz"); char pigz[1024]; strcpy(pigz, opts->pigzname); if (!is_exe(opts->pigzname)) { strcpy(opts->pigzname,"/usr/bin/pigz"); if (!is_exe(opts->pigzname)) { strcpy(opts->pigzname,"/usr/local/bin/pigz_mricron"); if (!is_exe(opts->pigzname)) { strcpy(opts->pigzname,argv[0]); dropFilenameFromPath(opts->pigzname);//, opts.pigzname); char appendChar[2] = {"a"}; appendChar[0] = kPathSeparator; if (opts->pigzname[strlen(opts->pigzname)-1] != kPathSeparator) strcat (opts->pigzname,appendChar); strcat(opts->pigzname,"pigz_mricron"); #if defined(_WIN64) || defined(_WIN32) strcat(opts->pigzname,".exe"); #endif if (!is_exe(opts->pigzname)) { #ifdef myUseCOut #ifdef myDisableZLib std::cout << "Compression requires "<<pigz<<std::endl; #else //myUseZLib std::cout << "Compression will be faster with "<<pigz<<std::endl; #endif #else #ifdef myDisableZLib printf("Compression requires %s\n",pigz); #else //myUseZLib printf("Compression will be faster with %s\n",pigz); #endif #endif strcpy(opts->pigzname,""); } //no pigz_mricron in exe's folder } //no /usr/local/pigz_mricron }//no /usr/bin/pigz } //no /usr/local/pigz #endif } //readFindPigz() #if defined(_WIN64) || defined(_WIN32) //windows has unusual file permissions for many users - lets save preferences to the registry void saveIniFile (struct TDCMopts opts) { HKEY hKey; if(RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\dcm2nii",0, KEY_SET_VALUE, &hKey) != ERROR_SUCCESS) { RegCloseKey(hKey); return; } DWORD dwValue = opts.isGz; //RegSetValueEx(hKey,"isGZ", 0, REG_DWORD,reinterpret_cast<BYTE *>(&dwValue),sizeof(dwValue)); //RegSetValueExA(hKey, "isGZ", 0, REG_DWORD, (LPDWORD)&dwValue, sizeof(dwValue)); RegSetValueExA(hKey, "isGZ", 0, REG_DWORD, reinterpret_cast<BYTE *>(&dwValue), sizeof(dwValue)); RegSetValueExA(hKey,"filename",0, REG_SZ,(LPBYTE)opts.filename, strlen(opts.filename)+1); RegCloseKey(hKey); } //saveIniFile() void readIniFile (struct TDCMopts *opts, const char * argv[]) { readFindPigz(opts, argv); strcpy(opts->indir,""); strcpy(opts->outdir,""); opts->isGz = false; opts->isFlipY = true; #ifdef myDebug opts->isVerbose = true; #else opts->isVerbose = false; #endif strcpy(opts->filename,"%f_%p_%t_%s"); HKEY hKey; DWORD vSize = 0; DWORD dwDataType = 0; DWORD dwValue = 0; //RegOpenKeyEx(RegOpenKeyEx, key, 0, accessRights, keyHandle); //if(RegOpenKeyEx(HKEY_CURRENT_USER,(WCHAR)"Software\\dcm2nii", 0, KEY_QUERY_VALUE,&hKey) != ERROR_SUCCESS) { if(RegOpenKeyExA(HKEY_CURRENT_USER,"Software\\dcm2nii", 0, KEY_QUERY_VALUE,&hKey) != ERROR_SUCCESS) { RegCloseKey(hKey); return; } vSize = sizeof(dwValue); //if(RegQueryValueExA(hKey,"isGZ", 0, (LPDWORD )&dwDataType, (&dwValue), &vSize) == ERROR_SUCCESS) if(RegQueryValueExA(hKey,"isGZ", 0, (LPDWORD )&dwDataType, reinterpret_cast<BYTE *>(&dwValue), &vSize) == ERROR_SUCCESS) opts->isGz = dwValue; vSize = 512; char buffer[512]; if(RegQueryValueExA(hKey,"filename", 0,NULL,(LPBYTE)buffer,&vSize ) == ERROR_SUCCESS ) strcpy(opts->filename,buffer); RegCloseKey(hKey); } //readIniFile() #else //for Unix we will save preferences in a hidden text file in the home directory #define STATUSFILENAME "/.dcm2nii.ini" void readIniFile (struct TDCMopts *opts, const char * argv[]) { readFindPigz(opts, argv); sprintf(opts->optsname, "%s%s", getenv("HOME"), STATUSFILENAME); strcpy(opts->indir,""); strcpy(opts->outdir,""); opts->isGz = false; opts->isFlipY = true; //false: images in raw DICOM orientation, true: image rows flipped to cartesian coordinates #ifdef myDebug opts->isVerbose = true; #else opts->isVerbose = false; #endif strcpy(opts->filename,"%f_%p_%t_%s"); FILE *fp = fopen(opts->optsname, "r"); if (fp == NULL) return; char Setting[20],Value[255]; //while ( fscanf(fp, "%[^=]=%s\n", Setting, Value) == 2 ) { //while ( fscanf(fp, "%[^=]=%s\n", Setting, Value) == 2 ) { while ( fscanf(fp, "%[^=]=%[^\n]\n", Setting, Value) == 2 ) { //printf(">%s<->'%s'\n",Setting,Value); if ( strcmp(Setting,"isGZ") == 0 ) opts->isGz = atoi(Value); else if ( strcmp(Setting,"filename") == 0 ) strcpy(opts->filename,Value); } fclose(fp); } //readIniFile() void saveIniFile (struct TDCMopts opts) { FILE *fp = fopen(opts.optsname, "w"); //printf("%s\n",localfilename); if (fp == NULL) return; fprintf(fp, "isGZ=%d\n", opts.isGz); fprintf(fp, "filename=%s\n", opts.filename); fclose(fp); } //saveIniFile() #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/wxWidgets/nii_ortho.c���������������������������������������������������������0000664�0000000�0000000�00000031151�13220512030�0020100�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "nifti1.h" #include "nifti1_io_core.h" #include "nii_ortho.h" #include <math.h> #include <stdint.h> #include <stdlib.h> #include <sys/stat.h> #include <stdbool.h> #include <ctype.h> #include <string.h> #include <stddef.h> #include <float.h> #include <unistd.h> #include <stdio.h> //#define MY_DEBUG //verbose text reporting typedef struct { int v[3]; } vec3i; mat33 matDotMul33 (mat33 a, mat33 b) // in Matlab: ret = a'.*b { mat33 ret; for (int i=0; i<3; i++) { for (int j=0; j<3; j++) { ret.m[i][j] = a.m[i][j]*b.m[j][i]; } } return ret; } mat33 matMul33 (mat33 a, mat33 b) // mult = a * b { mat33 mult; for(int i=0;i<3;i++) { for(int j=0;j<3;j++) { mult.m[j][i]=0; for(int k=0;k<3;k++) mult.m[j][i]+=a.m[j][k]*b.m[k][i]; } } return mult; } float getOrthoResidual (mat33 orig, mat33 transform) { mat33 mat = matDotMul33(orig, transform); float ret = 0; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { ret = ret + (mat.m[i][j]); } } return ret; } mat33 getBestOrient(mat44 R, vec3i flipVec) //flipVec reports flip: [1 1 1]=no flips, [-1 1 1] flip X dimension { mat33 ret, newmat, orig; LOAD_MAT33(orig,R.m[0][0],R.m[0][1],R.m[0][2], R.m[1][0],R.m[1][1],R.m[1][2], R.m[2][0],R.m[2][1],R.m[2][2]); float best = 0;//FLT_MAX; float newval; for (int rot = 0; rot < 6; rot++) { //6 rotations switch (rot) { case 0: LOAD_MAT33(newmat,flipVec.v[0],0,0, 0,flipVec.v[1],0, 0,0,flipVec.v[2]); break; case 1: LOAD_MAT33(newmat,flipVec.v[0],0,0, 0,0,flipVec.v[1], 0,flipVec.v[2],0); break; case 2: LOAD_MAT33(newmat,0,flipVec.v[0],0, flipVec.v[1],0,0, 0,0,flipVec.v[2]); break; case 3: LOAD_MAT33(newmat,0,flipVec.v[0],0, 0,0,flipVec.v[1], flipVec.v[2],0,0); break; case 4: LOAD_MAT33(newmat,0,0,flipVec.v[0], flipVec.v[1],0,0, 0,flipVec.v[2],0); break; case 5: LOAD_MAT33(newmat,0,0,flipVec.v[0], 0,flipVec.v[1],0, flipVec.v[2],0,0); break; } newval = getOrthoResidual(orig, newmat); if (newval > best) { best = newval; ret = newmat; } } return ret; } bool isMat44Canonical(mat44 R) //returns true if diagonals >0 and all others =0 // no rotation is necessary - already in perfect orthogonal alignment { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if ((i == j) && (R.m[i][j] <= 0) ) return false; if ((i != j) && (R.m[i][j] != 0) ) return false; }//j }//i return true; } vec3i setOrientVec(mat33 m) // Assumes isOrthoMat NOT computed on INVERSE, hence return INVERSE of solution... //e.g. [-1,2,3] means reflect x axis, [2,1,3] means swap x and y dimensions { vec3i ret = {0, 0, 0}; //mat33 m = {-1,0,0, 0,1,0, 0,0,1}; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (m.m[i][j] > 0) ret.v[j] = i+1; if (m.m[i][j] < 0) ret.v[j] = -(i+1); }//j }//i return ret; } mat44 setMat44Vec(mat33 m33, vec3 Translations) //convert a 3x3 rotation matrix to a 4x4 matrix where the last column stores translations and the last row is 0 0 0 1 { mat44 m44; for (int i=0; i<3; i++) { for (int j=0; j<3; j++) { m44.m[i][j] = m33.m[i][j]; } } m44.m[0][3] = Translations.v[0]; m44.m[1][3] = Translations.v[1]; m44.m[2][3] = Translations.v[2]; m44.m[3][0] = 0; m44.m[3][1] = 0; m44.m[3][2] = 0; m44.m[3][3] = 1; return m44; } mat44 sFormMat(struct nifti_1_header *h) { mat44 s; s.m[0][0]=h->srow_x[0]; s.m[0][1]=h->srow_x[1]; s.m[0][2]=h->srow_x[2]; s.m[0][3]=h->srow_x[3]; s.m[1][0]=h->srow_y[0]; s.m[1][1]=h->srow_y[1]; s.m[1][2]=h->srow_y[2]; s.m[1][3]=h->srow_y[3]; s.m[2][0]=h->srow_z[0]; s.m[2][1]=h->srow_z[1]; s.m[2][2]=h->srow_z[2]; s.m[2][3]=h->srow_z[3]; s.m[3][0] = 0 ; s.m[3][1] = 0 ; s.m[3][2] = 0 ; s.m[3][3] = 1 ; return s; } void mat2sForm (struct nifti_1_header *h, mat44 s) { h->srow_x[0] = s.m[0][0]; h->srow_x[1] = s.m[0][1]; h->srow_x[2] = s.m[0][2]; h->srow_x[3] = s.m[0][3]; h->srow_y[0] = s.m[1][0]; h->srow_y[1] = s.m[1][1]; h->srow_y[2] = s.m[1][2]; h->srow_y[3] = s.m[1][3]; h->srow_z[0] = s.m[2][0]; h->srow_z[1] = s.m[2][1]; h->srow_z[2] = s.m[2][2]; h->srow_z[3] = s.m[2][3]; } size_t* orthoOffsetArray(int dim, int stepBytesPerVox) { //return lookup table of length dim with values incremented by stepBytesPerVox // e.g. if Dim=10 and stepBytes=2: 0,2,4..18, is stepBytes=-2 18,16,14...0 size_t *lut= (size_t *)malloc(dim*sizeof(size_t)); if (stepBytesPerVox > 0) lut[0] = 0; else lut[0] = -stepBytesPerVox *(dim-1); if (dim > 1) for (int i=1; i < dim; i++) lut[i] = lut[i-1] + (size_t)stepBytesPerVox; return lut; } //orthoOffsetArray() //void reOrientImg( unsigned char * restrict img, vec3i outDim, vec3i outInc, int bytePerVox, int nvol) { void reOrientImg( unsigned char * img, vec3i outDim, vec3i outInc, int bytePerVox, int nvol) { //reslice data to new orientation //generate look up tables size_t* xLUT =orthoOffsetArray(outDim.v[0], bytePerVox*outInc.v[0]); size_t* yLUT =orthoOffsetArray(outDim.v[1], bytePerVox*outInc.v[1]); size_t* zLUT =orthoOffsetArray(outDim.v[2], bytePerVox*outInc.v[2]); //convert data size_t bytePerVol = bytePerVox*outDim.v[0]*outDim.v[1]*outDim.v[2]; //number of voxels in spatial dimensions [1,2,3] size_t o = 0; //output address uint8_t *inbuf = (uint8_t *) malloc(bytePerVol); //we convert 1 volume at a time uint8_t *outbuf = (uint8_t *) img; //source image for (int vol= 0; vol < nvol; vol++) { memcpy(&inbuf[0], &outbuf[vol*bytePerVol], bytePerVol); //copy source volume for (int z = 0; z < outDim.v[2]; z++) for (int y = 0; y < outDim.v[1]; y++) for (int x = 0; x < outDim.v[0]; x++) { memcpy(&outbuf[o], &inbuf[xLUT[x]+yLUT[y]+zLUT[z]], bytePerVox); o = o+ bytePerVox; } //for each x } //for each volume //free arrays free(inbuf); free(xLUT); free(yLUT); free(zLUT); } //reOrientImg unsigned char * reOrient(unsigned char* img, struct nifti_1_header *h, vec3i orientVec, mat33 orient, vec3 minMM) //e.g. [-1,2,3] means reflect x axis, [2,1,3] means swap x and y dimensions { size_t nvox = h->dim[1] * h->dim[2] * h->dim[3]; if (nvox < 1) return img; vec3i outDim= {0,0,0}; vec3i outInc= {0,0,0}; for (int i = 0; i < 3; i++) { //set dimension, pixdim and outDim.v[i] = h->dim[abs(orientVec.v[i])]; if (abs(orientVec.v[i]) == 1) outInc.v[i] = 1; if (abs(orientVec.v[i]) == 2) outInc.v[i] = h->dim[1]; if (abs(orientVec.v[i]) == 3) outInc.v[i] = h->dim[1]*h->dim[2]; if (orientVec.v[i] < 0) outInc.v[i] = -outInc.v[i]; //flip } //for each dimension int nvol = 1; //convert all non-spatial volumes from source to destination for (int vol = 4; vol < 8; vol++) { if (h->dim[vol] > 1) nvol = nvol * h->dim[vol]; } reOrientImg(img, outDim, outInc, h->bitpix / 8, nvol); //now change the header.... vec3 outPix= {h->pixdim[abs(orientVec.v[0])],h->pixdim[abs(orientVec.v[1])],h->pixdim[abs(orientVec.v[2])]}; for (int i = 0; i < 3; i++) { h->dim[i+1] = outDim.v[i]; h->pixdim[i+1] = outPix.v[i]; } mat44 s = sFormMat(h); mat33 mat; //computer transform LOAD_MAT33(mat, s.m[0][0],s.m[0][1],s.m[0][2], s.m[1][0],s.m[1][1],s.m[1][2], s.m[2][0],s.m[2][1],s.m[2][2]); mat = matMul33( mat, orient); s = setMat44Vec(mat, minMM); //add offset mat2sForm(h,s); h->qform_code = h->sform_code; //apply to the quaternion as well float dumdx, dumdy, dumdz; nifti_mat44_to_quatern( s , &h->quatern_b, &h->quatern_c, &h->quatern_d,&h->qoffset_x, &h->qoffset_y, &h->qoffset_z, &dumdx, &dumdy, &dumdz,&h->pixdim[0]) ; return img; } //reOrient() float getDistance (vec3 v, vec3 min) //scalar distance between two 3D points - Pythagorean theorem { return sqrt(pow((v.v[0]-min.v[0]),2)+pow((v.v[1]-min.v[1]),2)+pow((v.v[2]-min.v[2]),2) ); } vec3 xyz2mm (mat44 R, vec3 v) { vec3 ret; for (int i = 0; i < 3; i++) { ret.v[i] = ( (R.m[i][0]*v.v[0])+(R.m[i][1]*v.v[1])+ (R.m[i][2]*v.v[2])+R.m[i][3] ); } return ret; } vec3 minCornerFlip (struct nifti_1_header *h, vec3i* flipVec) //orthogonal rotations and reflections applied as 3x3 matrices will cause the origin to shift // a simple solution is to first compute the most left, posterior, inferior voxel in the source image // this voxel will be at location i,j,k = 0,0,0, so we can simply use this as the offset for the final 4x4 matrix... { int i,j, minIndex; vec3i flipVecs[8]; vec3 corner[8], min; mat44 s = sFormMat(h); for (int i = 0; i < 8; i++) { if (i & 1) flipVecs[i].v[0] = -1; else flipVecs[i].v[0] = 1; if (i & 2) flipVecs[i].v[1] = -1; else flipVecs[i].v[1] = 1; if (i & 4) flipVecs[i].v[2] = -1; else flipVecs[i].v[2] = 1; corner[i] = setVec3(0,0,0); //assume no reflections if ((flipVecs[i].v[0]) < 1) corner[i].v[0] = h->dim[1]-1; //reflect X if ((flipVecs[i].v[1]) < 1) corner[i].v[1] = h->dim[2]-1; //reflect Y if ((flipVecs[i].v[2]) < 1) corner[i].v[2] = h->dim[3]-1; //reflect Z corner[i] = xyz2mm(s,corner[i]); } //find extreme edge from ALL corners.... min = corner[0]; for (i = 1; i < 8; i++) { for (j = 0; j < 3; j++) { if (corner[i].v[j]< min.v[j]) min.v[j] = corner[i].v[j]; } } float dx; //observed distance from corner float min_dx = getDistance (corner[0], min); minIndex = 0; //index of corner closest to min //see if any corner is closer to absmin than the first one... for (i = 1; i < 8; i++) { dx = getDistance (corner[i], min); if (dx < min_dx) { min_dx = dx; minIndex = i; } } min = corner[minIndex]; //this is the single corner closest to min from all *flipVec= flipVecs[minIndex]; return min; } #ifdef MY_DEBUG void reportMat44o(char *str, mat44 A) { printf("%s = [%g %g %g %g; %g %g %g %g; %g %g %g %g; 0 0 0 1]\n",str, A.m[0][0],A.m[0][1],A.m[0][2],A.m[0][3], A.m[1][0],A.m[1][1],A.m[1][2],A.m[1][3], A.m[2][0],A.m[2][1],A.m[2][2],A.m[2][3]); } #endif unsigned char * nii_setOrtho(unsigned char* img, struct nifti_1_header *h) { if ((h->dim[0] != 3) || (h->dim[1] < 1) || (h->dim[2] < 1) || (h->dim[3] < 1)) return img; if ((h->sform_code == NIFTI_XFORM_UNKNOWN) && (h->qform_code != NIFTI_XFORM_UNKNOWN)) { //only q-form provided mat44 q = nifti_quatern_to_mat44(h->quatern_b, h->quatern_c, h->quatern_d, h->qoffset_x, h->qoffset_y, h->qoffset_z, h->pixdim[1], h->pixdim[2], h->pixdim[3],h->pixdim[0]); mat2sForm(h,q); //convert q-form to s-form h->sform_code = h->qform_code; } if (h->sform_code == NIFTI_XFORM_UNKNOWN) { #ifdef MY_DEBUG printf("No Q or S spatial transforms - assuming canonical orientation"); #endif return img; } mat44 s = sFormMat(h); if (isMat44Canonical( s)) { #ifdef MY_DEBUG printf("Image in perfect alignment: no need to reorient"); #endif return img; } vec3i flipV; vec3 minMM = minCornerFlip(h, &flipV); mat33 orient = getBestOrient(s, flipV); vec3i orientVec = setOrientVec(orient); if ((orientVec.v[0]==1) && (orientVec.v[1]==2) && (orientVec.v[2]==3) ) { #ifdef MY_DEBUG printf("Image already near best orthogonal alignment: no need to reorient\n"); #endif return img; } bool is24 = false; if (h->bitpix == 24 ) { //RGB stored as planar data. treat as 3 8-bit slices return img; is24 = true; h->bitpix = 8; h->dim[3] = h->dim[3] * 3; } img = reOrient(img, h,orientVec, orient, minMM); if (is24 ) { h->bitpix = 24; h->dim[3] = h->dim[3] / 3; } #ifdef MY_DEBUG printf("NewRotation= %d %d %d\n", orientVec.v[0],orientVec.v[1],orientVec.v[2]); printf("MinCorner= %.2f %.2f %.2f\n", minMM.v[0],minMM.v[1],minMM.v[2]); reportMat44o("input",s); s = sFormMat(h); reportMat44o("output",s); #endif return img; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/wxWidgets/readme.txt����������������������������������������������������������0000664�0000000�0000000�00000001767�13220512030�0017752�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������1.) wxWdigets makefiles are pretty complex and specific for your operating system. For simplicity, install wxWidgets and make the /samples/clipboard, then replace the original clipboard.cpp with our new program 2.) Older XCodes have problems with .cpp files, whereas wxWidgets's makefiles do not compile with "-x cpp". So the core files are called '.c' but we will rename them to .cpp for wxWidgets: rename 's/\.c$/\.cpp/' * For WX widgets it usually helps to "make" the original clipboard project on the computer and then edit the makefile: 3.) Add "nii_dicom.o nifti1_io_core.o nii_ortho.o nii_dicom_batch.o \" to CLIPBOARD_OBJECTS: CLIPBOARD_OBJECTS = \ nii_dicom.o nifti1_io_core.o nii_ortho.o nii_dicom_batch.o \ $(__clipboard___win32rc) \ $(__clipboard_os2_lib_res) \ clipboard_clipboard.o 4.) With wxWidgets we will capture std::cout comments, not printf, so we need to add "-DDmyUseCOut" to CXXFLAGS: CXXFLAGS = -DmyUseCOut -DWX_PRECOMP .... 5.) For a full refresh rm clipboard rm *.o make ���������dcm2niix-1.0.20171215/xcode/������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13220512030�0015056�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/core��������������������������������������������������������������������0000664�0000000�0000000�00000020541�13220512030�0015733�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2013-2014, Cong Xu 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. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef TINYDIR_H #define TINYDIR_H #include <errno.h> #include <stdlib.h> #include <string.h> #ifdef _MSC_VER #define WIN32_LEAN_AND_MEAN #include <windows.h> #pragma warning (disable : 4996) #else #include <dirent.h> #include <sys/stat.h> #endif /* types */ #define _TINYDIR_PATH_MAX 4096 #ifdef _MSC_VER /* extra chars for the "\\*" mask */ #define _TINYDIR_PATH_EXTRA 2 #else #define _TINYDIR_PATH_EXTRA 0 #endif #define _TINYDIR_FILENAME_MAX 256 #ifdef _MSC_VER #define _TINYDIR_FUNC static __inline #else #define _TINYDIR_FUNC static __inline__ #endif typedef struct { char path[_TINYDIR_PATH_MAX]; char name[_TINYDIR_FILENAME_MAX]; int is_dir; int is_reg; #ifdef _MSC_VER #else struct stat _s; #endif } tinydir_file; typedef struct { char path[_TINYDIR_PATH_MAX]; int has_next; size_t n_files; tinydir_file *_files; #ifdef _MSC_VER HANDLE _h; WIN32_FIND_DATA _f; #else DIR *_d; struct dirent *_e; #endif } tinydir_dir; /* declarations */ _TINYDIR_FUNC int tinydir_open(tinydir_dir *dir, const char *path); _TINYDIR_FUNC int tinydir_open_sorted(tinydir_dir *dir, const char *path); _TINYDIR_FUNC void tinydir_close(tinydir_dir *dir); _TINYDIR_FUNC int tinydir_next(tinydir_dir *dir); _TINYDIR_FUNC int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file); _TINYDIR_FUNC int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i); _TINYDIR_FUNC int tinydir_open_subdir_n(tinydir_dir *dir, size_t i); _TINYDIR_FUNC int _tinydir_file_cmp(const void *a, const void *b); /* definitions*/ _TINYDIR_FUNC int tinydir_open(tinydir_dir *dir, const char *path) { if (dir == NULL || path == NULL || strlen(path) == 0) { errno = EINVAL; return -1; } if (strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) { errno = ENAMETOOLONG; return -1; } /* initialise dir */ dir->_files = NULL; #ifdef _MSC_VER dir->_h = INVALID_HANDLE_VALUE; #else dir->_d = NULL; #endif tinydir_close(dir); strcpy(dir->path, path); #ifdef _MSC_VER strcat(dir->path, "\\*"); dir->_h = FindFirstFile(dir->path, &dir->_f); dir->path[strlen(dir->path) - 2] = '\0'; if (dir->_h == INVALID_HANDLE_VALUE) #else dir->_d = opendir(path); if (dir->_d == NULL) #endif { errno = ENOENT; goto bail; } /* read first file */ dir->has_next = 1; #ifndef _MSC_VER dir->_e = readdir(dir->_d); if (dir->_e == NULL) { dir->has_next = 0; } #endif return 0; bail: tinydir_close(dir); return -1; } _TINYDIR_FUNC int tinydir_open_sorted(tinydir_dir *dir, const char *path) { /* Count the number of files first, to pre-allocate the files array */ size_t n_files = 0; if (tinydir_open(dir, path) == -1) { return -1; } while (dir->has_next) { n_files++; if (tinydir_next(dir) == -1) { goto bail; } } tinydir_close(dir); if (tinydir_open(dir, path) == -1) { return -1; } dir->n_files = 0; dir->_files = (tinydir_file *)malloc(sizeof *dir->_files * n_files); if (dir->_files == NULL) { errno = ENOMEM; goto bail; } while (dir->has_next) { tinydir_file *p_file; dir->n_files++; p_file = &dir->_files[dir->n_files - 1]; if (tinydir_readfile(dir, p_file) == -1) { goto bail; } if (tinydir_next(dir) == -1) { goto bail; } /* Just in case the number of files has changed between the first and second reads, terminate without writing into unallocated memory */ if (dir->n_files == n_files) { break; } } qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp); return 0; bail: tinydir_close(dir); return -1; } _TINYDIR_FUNC void tinydir_close(tinydir_dir *dir) { if (dir == NULL) { return; } memset(dir->path, 0, sizeof(dir->path)); dir->has_next = 0; dir->n_files = 0; if (dir->_files != NULL) { free(dir->_files); } dir->_files = NULL; #ifdef _MSC_VER if (dir->_h != INVALID_HANDLE_VALUE) { FindClose(dir->_h); } dir->_h = INVALID_HANDLE_VALUE; #else if (dir->_d) { closedir(dir->_d); } dir->_d = NULL; dir->_e = NULL; #endif } _TINYDIR_FUNC int tinydir_next(tinydir_dir *dir) { if (dir == NULL) { errno = EINVAL; return -1; } if (!dir->has_next) { errno = ENOENT; return -1; } #ifdef _MSC_VER if (FindNextFile(dir->_h, &dir->_f) == 0) #else dir->_e = readdir(dir->_d); if (dir->_e == NULL) #endif { dir->has_next = 0; #ifdef _MSC_VER if (GetLastError() != ERROR_SUCCESS && GetLastError() != ERROR_NO_MORE_FILES) { tinydir_close(dir); errno = EIO; return -1; } #endif } return 0; } _TINYDIR_FUNC int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file) { if (dir == NULL || file == NULL) { errno = EINVAL; return -1; } #ifdef _MSC_VER if (dir->_h == INVALID_HANDLE_VALUE) #else if (dir->_e == NULL) #endif { errno = ENOENT; return -1; } if (strlen(dir->path) + strlen( #ifdef _MSC_VER dir->_f.cFileName #else dir->_e->d_name #endif ) + 1 + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) { /* the path for the file will be too long */ errno = ENAMETOOLONG; return -1; } if (strlen( #ifdef _MSC_VER dir->_f.cFileName #else dir->_e->d_name #endif ) >= _TINYDIR_FILENAME_MAX) { errno = ENAMETOOLONG; return -1; } strcpy(file->path, dir->path); strcat(file->path, "/"); strcpy(file->name, #ifdef _MSC_VER dir->_f.cFileName #else dir->_e->d_name #endif ); strcat(file->path, file->name); #ifndef _MSC_VER if (stat(file->path, &file->_s) == -1) { return -1; } #endif file->is_dir = #ifdef _MSC_VER !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); #else S_ISDIR(file->_s.st_mode); #endif file->is_reg = #ifdef _MSC_VER !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) || ( !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) && !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) && #ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) && #endif #ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) && #endif !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) && !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY)); #else S_ISREG(file->_s.st_mode); #endif return 0; } _TINYDIR_FUNC int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i) { if (dir == NULL || file == NULL) { errno = EINVAL; return -1; } if (i >= dir->n_files) { errno = ENOENT; return -1; } memcpy(file, &dir->_files[i], sizeof(tinydir_file)); return 0; } _TINYDIR_FUNC int tinydir_open_subdir_n(tinydir_dir *dir, size_t i) { char path[_TINYDIR_PATH_MAX]; if (dir == NULL) { errno = EINVAL; return -1; } if (i >= dir->n_files || !dir->_files[i].is_dir) { errno = ENOENT; return -1; } strcpy(path, dir->_files[i].path); tinydir_close(dir); if (tinydir_open_sorted(dir, path) == -1) { return -1; } return 0; } _TINYDIR_FUNC int _tinydir_file_cmp(const void *a, const void *b) { const tinydir_file *fa = (const tinydir_file *)a; const tinydir_file *fb = (const tinydir_file *)b; if (fa->is_dir != fb->is_dir) { return -(fa->is_dir - fb->is_dir); } return strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX); } #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/���������������������������������������������������������0000775�0000000�0000000�00000000000�13220512030�0017677�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/project.pbxproj������������������������������������������0000664�0000000�0000000�00000045010�13220512030�0022753�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 4F1D666719F422D6004BEC23 /* nifti1_io_core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F1D666319F422D6004BEC23 /* nifti1_io_core.cpp */; }; 4F1D666919F422D6004BEC23 /* nii_dicom_batch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F1D666419F422D6004BEC23 /* nii_dicom_batch.cpp */; }; 4F1D666B19F422D6004BEC23 /* nii_dicom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F1D666519F422D6004BEC23 /* nii_dicom.cpp */; }; 4F1D666D19F422D6004BEC23 /* nii_ortho.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F1D666619F422D6004BEC23 /* nii_ortho.cpp */; }; 4F20AC5E191D67F600EA3B33 /* libz.1.2.5.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F20AC5D191D67F600EA3B33 /* libz.1.2.5.dylib */; }; 4F7688E21B173D7D00E959F3 /* miniz.c in Sources */ = {isa = PBXBuildFile; fileRef = 4F7688E11B173D7D00E959F3 /* miniz.c */; }; 4F7688E51B173EBB00E959F3 /* jpg_0XC3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F7688E31B173EBB00E959F3 /* jpg_0XC3.cpp */; }; 4F7688E81B17402B00E959F3 /* ujpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F7688E61B17402B00E959F3 /* ujpeg.cpp */; }; 4F9EB64718F333DF00F6A696 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F9EB64618F333DF00F6A696 /* Cocoa.framework */; }; 4F9EB65118F333DF00F6A696 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4F9EB64F18F333DF00F6A696 /* InfoPlist.strings */; }; 4F9EB65318F333DF00F6A696 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F9EB65218F333DF00F6A696 /* main.m */; }; 4F9EB65718F333DF00F6A696 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 4F9EB65518F333DF00F6A696 /* Credits.rtf */; }; 4F9EB65D18F333DF00F6A696 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4F9EB65B18F333DF00F6A696 /* MainMenu.xib */; }; 4FE4DAD318FF778100EDF667 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F9EB65918F333DF00F6A696 /* AppDelegate.m */; }; 4FE5DAC618F338FA003B4508 /* dcm2niigui.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4FE5DAC518F338FA003B4508 /* dcm2niigui.icns */; }; 4FE5DACC18F33AEE003B4508 /* myWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FE5DACB18F33AEE003B4508 /* myWindow.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 4F1D665D19F422D6004BEC23 /* nifti1_io_core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nifti1_io_core.h; sourceTree = "<group>"; }; 4F1D665E19F422D6004BEC23 /* nifti1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nifti1.h; sourceTree = "<group>"; }; 4F1D665F19F422D6004BEC23 /* nii_dicom_batch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nii_dicom_batch.h; sourceTree = "<group>"; }; 4F1D666019F422D6004BEC23 /* nii_dicom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nii_dicom.h; sourceTree = "<group>"; }; 4F1D666119F422D6004BEC23 /* nii_ortho.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nii_ortho.h; sourceTree = "<group>"; }; 4F1D666219F422D6004BEC23 /* tinydir.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tinydir.h; sourceTree = "<group>"; }; 4F1D666319F422D6004BEC23 /* nifti1_io_core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = nifti1_io_core.cpp; sourceTree = "<group>"; }; 4F1D666419F422D6004BEC23 /* nii_dicom_batch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = nii_dicom_batch.cpp; sourceTree = "<group>"; }; 4F1D666519F422D6004BEC23 /* nii_dicom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = nii_dicom.cpp; sourceTree = "<group>"; }; 4F1D666619F422D6004BEC23 /* nii_ortho.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = nii_ortho.cpp; sourceTree = "<group>"; }; 4F20AC5D191D67F600EA3B33 /* libz.1.2.5.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.1.2.5.dylib; path = ../../../../../../usr/lib/libz.1.2.5.dylib; sourceTree = "<group>"; }; 4F7688E11B173D7D00E959F3 /* miniz.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = miniz.c; sourceTree = "<group>"; }; 4F7688E31B173EBB00E959F3 /* jpg_0XC3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jpg_0XC3.cpp; sourceTree = "<group>"; }; 4F7688E41B173EBB00E959F3 /* jpg_0XC3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jpg_0XC3.h; sourceTree = "<group>"; }; 4F7688E61B17402B00E959F3 /* ujpeg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ujpeg.cpp; sourceTree = "<group>"; }; 4F7688E71B17402B00E959F3 /* ujpeg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ujpeg.h; sourceTree = "<group>"; }; 4F9EB64318F333DF00F6A696 /* dcm2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = dcm2.app; sourceTree = BUILT_PRODUCTS_DIR; }; 4F9EB64618F333DF00F6A696 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 4F9EB64918F333DF00F6A696 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 4F9EB64A18F333DF00F6A696 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 4F9EB64B18F333DF00F6A696 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 4F9EB64E18F333DF00F6A696 /* dcm2-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "dcm2-Info.plist"; sourceTree = "<group>"; }; 4F9EB65018F333DF00F6A696 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; }; 4F9EB65218F333DF00F6A696 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; 4F9EB65418F333DF00F6A696 /* dcm2-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "dcm2-Prefix.pch"; sourceTree = "<group>"; }; 4F9EB65618F333DF00F6A696 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = "<group>"; }; 4F9EB65818F333DF00F6A696 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; }; 4F9EB65918F333DF00F6A696 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; }; 4F9EB65C18F333DF00F6A696 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = "<group>"; }; 4F9EB66418F333DF00F6A696 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; 4FE5DAC518F338FA003B4508 /* dcm2niigui.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = dcm2niigui.icns; sourceTree = "<group>"; }; 4FE5DACA18F33AEE003B4508 /* myWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = myWindow.h; sourceTree = "<group>"; }; 4FE5DACB18F33AEE003B4508 /* myWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = myWindow.m; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 4F9EB64018F333DF00F6A696 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 4F20AC5E191D67F600EA3B33 /* libz.1.2.5.dylib in Frameworks */, 4F9EB64718F333DF00F6A696 /* Cocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 4F9EB63A18F333DF00F6A696 = { isa = PBXGroup; children = ( 4F20AC5D191D67F600EA3B33 /* libz.1.2.5.dylib */, 4FE5DAC518F338FA003B4508 /* dcm2niigui.icns */, 4F9EB64C18F333DF00F6A696 /* dcm2 */, 4F9EB64518F333DF00F6A696 /* Frameworks */, 4F9EB64418F333DF00F6A696 /* Products */, ); sourceTree = "<group>"; }; 4F9EB64418F333DF00F6A696 /* Products */ = { isa = PBXGroup; children = ( 4F9EB64318F333DF00F6A696 /* dcm2.app */, ); name = Products; sourceTree = "<group>"; }; 4F9EB64518F333DF00F6A696 /* Frameworks */ = { isa = PBXGroup; children = ( 4F9EB64618F333DF00F6A696 /* Cocoa.framework */, 4F9EB66418F333DF00F6A696 /* SenTestingKit.framework */, 4F9EB64818F333DF00F6A696 /* Other Frameworks */, ); name = Frameworks; sourceTree = "<group>"; }; 4F9EB64818F333DF00F6A696 /* Other Frameworks */ = { isa = PBXGroup; children = ( 4F9EB64918F333DF00F6A696 /* AppKit.framework */, 4F9EB64A18F333DF00F6A696 /* CoreData.framework */, 4F9EB64B18F333DF00F6A696 /* Foundation.framework */, ); name = "Other Frameworks"; sourceTree = "<group>"; }; 4F9EB64C18F333DF00F6A696 /* dcm2 */ = { isa = PBXGroup; children = ( 4FADC891190AD70F00010758 /* core */, 4F9EB65818F333DF00F6A696 /* AppDelegate.h */, 4F9EB65918F333DF00F6A696 /* AppDelegate.m */, 4FE5DACA18F33AEE003B4508 /* myWindow.h */, 4FE5DACB18F33AEE003B4508 /* myWindow.m */, 4F9EB65B18F333DF00F6A696 /* MainMenu.xib */, 4F9EB64D18F333DF00F6A696 /* Supporting Files */, ); path = dcm2; sourceTree = "<group>"; }; 4F9EB64D18F333DF00F6A696 /* Supporting Files */ = { isa = PBXGroup; children = ( 4F9EB64E18F333DF00F6A696 /* dcm2-Info.plist */, 4F9EB64F18F333DF00F6A696 /* InfoPlist.strings */, 4F9EB65218F333DF00F6A696 /* main.m */, 4F9EB65418F333DF00F6A696 /* dcm2-Prefix.pch */, 4F9EB65518F333DF00F6A696 /* Credits.rtf */, ); name = "Supporting Files"; sourceTree = "<group>"; }; 4FADC891190AD70F00010758 /* core */ = { isa = PBXGroup; children = ( 4F7688E61B17402B00E959F3 /* ujpeg.cpp */, 4F7688E71B17402B00E959F3 /* ujpeg.h */, 4F1D665D19F422D6004BEC23 /* nifti1_io_core.h */, 4F1D665E19F422D6004BEC23 /* nifti1.h */, 4F7688E31B173EBB00E959F3 /* jpg_0XC3.cpp */, 4F7688E41B173EBB00E959F3 /* jpg_0XC3.h */, 4F1D666019F422D6004BEC23 /* nii_dicom.h */, 4F1D666519F422D6004BEC23 /* nii_dicom.cpp */, 4F1D665F19F422D6004BEC23 /* nii_dicom_batch.h */, 4F1D666419F422D6004BEC23 /* nii_dicom_batch.cpp */, 4F1D666119F422D6004BEC23 /* nii_ortho.h */, 4F1D666219F422D6004BEC23 /* tinydir.h */, 4F1D666319F422D6004BEC23 /* nifti1_io_core.cpp */, 4F1D666619F422D6004BEC23 /* nii_ortho.cpp */, 4F7688E11B173D7D00E959F3 /* miniz.c */, ); path = core; sourceTree = "<group>"; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 4F9EB64218F333DF00F6A696 /* dcm2 */ = { isa = PBXNativeTarget; buildConfigurationList = 4F9EB67418F333DF00F6A696 /* Build configuration list for PBXNativeTarget "dcm2" */; buildPhases = ( 4F9EB63F18F333DF00F6A696 /* Sources */, 4F9EB64018F333DF00F6A696 /* Frameworks */, 4F9EB64118F333DF00F6A696 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = dcm2; productName = dcm2; productReference = 4F9EB64318F333DF00F6A696 /* dcm2.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 4F9EB63B18F333DF00F6A696 /* Project object */ = { isa = PBXProject; attributes = { LastTestingUpgradeCheck = 0640; LastUpgradeCheck = 0610; ORGANIZATIONNAME = "Chris Rorden"; }; buildConfigurationList = 4F9EB63E18F333DF00F6A696 /* Build configuration list for PBXProject "dcm2" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, ); mainGroup = 4F9EB63A18F333DF00F6A696; productRefGroup = 4F9EB64418F333DF00F6A696 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 4F9EB64218F333DF00F6A696 /* dcm2 */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 4F9EB64118F333DF00F6A696 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 4F9EB65118F333DF00F6A696 /* InfoPlist.strings in Resources */, 4F9EB65718F333DF00F6A696 /* Credits.rtf in Resources */, 4F9EB65D18F333DF00F6A696 /* MainMenu.xib in Resources */, 4FE5DAC618F338FA003B4508 /* dcm2niigui.icns in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 4F9EB63F18F333DF00F6A696 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 4F7688E51B173EBB00E959F3 /* jpg_0XC3.cpp in Sources */, 4F9EB65318F333DF00F6A696 /* main.m in Sources */, 4FE5DACC18F33AEE003B4508 /* myWindow.m in Sources */, 4FE4DAD318FF778100EDF667 /* AppDelegate.m in Sources */, 4F1D666719F422D6004BEC23 /* nifti1_io_core.cpp in Sources */, 4F7688E81B17402B00E959F3 /* ujpeg.cpp in Sources */, 4F1D666919F422D6004BEC23 /* nii_dicom_batch.cpp in Sources */, 4F1D666B19F422D6004BEC23 /* nii_dicom.cpp in Sources */, 4F1D666D19F422D6004BEC23 /* nii_ortho.cpp in Sources */, 4F7688E21B173D7D00E959F3 /* miniz.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 4F9EB64F18F333DF00F6A696 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( 4F9EB65018F333DF00F6A696 /* en */, ); name = InfoPlist.strings; sourceTree = "<group>"; }; 4F9EB65518F333DF00F6A696 /* Credits.rtf */ = { isa = PBXVariantGroup; children = ( 4F9EB65618F333DF00F6A696 /* en */, ); name = Credits.rtf; sourceTree = "<group>"; }; 4F9EB65B18F333DF00F6A696 /* MainMenu.xib */ = { isa = PBXVariantGroup; children = ( 4F9EB65C18F333DF00F6A696 /* en */, ); name = MainMenu.xib; sourceTree = "<group>"; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 4F9EB67218F333DF00F6A696 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libstdc++"; CLANG_ENABLE_OBJC_ARC = NO; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( myDisableMiniZ, myDisableOpenJPEG, ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.6; ONLY_ACTIVE_ARCH = YES; SDKROOT = ""; }; name = Debug; }; 4F9EB67318F333DF00F6A696 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libstdc++"; CLANG_ENABLE_OBJC_ARC = NO; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_PREPROCESSOR_DEFINITIONS = ( myDisableMiniZ, myDisableOpenJPEG, ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.6; SDKROOT = ""; }; name = Release; }; 4F9EB67518F333DF00F6A696 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_ARC = NO; COMBINE_HIDPI_IMAGES = YES; DEAD_CODE_STRIPPING = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "dcm2/dcm2-Prefix.pch"; INFOPLIST_FILE = "dcm2/dcm2-Info.plist"; MACOSX_DEPLOYMENT_TARGET = 10.6; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; name = Debug; }; 4F9EB67618F333DF00F6A696 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_ARC = NO; COMBINE_HIDPI_IMAGES = YES; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "dcm2/dcm2-Prefix.pch"; INFOPLIST_FILE = "dcm2/dcm2-Info.plist"; MACOSX_DEPLOYMENT_TARGET = 10.6; ONLY_ACTIVE_ARCH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 4F9EB63E18F333DF00F6A696 /* Build configuration list for PBXProject "dcm2" */ = { isa = XCConfigurationList; buildConfigurations = ( 4F9EB67218F333DF00F6A696 /* Debug */, 4F9EB67318F333DF00F6A696 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 4F9EB67418F333DF00F6A696 /* Build configuration list for PBXNativeTarget "dcm2" */ = { isa = XCConfigurationList; buildConfigurations = ( 4F9EB67518F333DF00F6A696 /* Debug */, 4F9EB67618F333DF00F6A696 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 4F9EB63B18F333DF00F6A696 /* Project object */; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/project.xcworkspace/�������������������������������������0000775�0000000�0000000�00000000000�13220512030�0023675�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/project.xcworkspace/contents.xcworkspacedata�������������0000664�0000000�0000000�00000000225�13220512030�0030636�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <Workspace version = "1.0"> <FileRef location = "self:dcm2.xcodeproj"> </FileRef> </Workspace> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/project.xcworkspace/xcshareddata/������������������������0000775�0000000�0000000�00000000000�13220512030�0026330�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/project.xcworkspace/xcshareddata/dcm2.xccheckout���������0000664�0000000�0000000�00000002720�13220512030�0031240�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>IDESourceControlProjectFavoriteDictionaryKey</key> <false/> <key>IDESourceControlProjectIdentifier</key> <string>C798AF42-BE64-44EE-87C0-C50C9566D382</string> <key>IDESourceControlProjectName</key> <string>dcm2</string> <key>IDESourceControlProjectOriginsDictionary</key> <dict> <key>D94D2C5489FA4C6F294552E6DEAD453AC2154E05</key> <string>https://github.com/neurolabusc/dcm2niix.git</string> </dict> <key>IDESourceControlProjectPath</key> <string>xcode/dcm2.xcodeproj</string> <key>IDESourceControlProjectRelativeInstallPathDictionary</key> <dict> <key>D94D2C5489FA4C6F294552E6DEAD453AC2154E05</key> <string>../../..</string> </dict> <key>IDESourceControlProjectURL</key> <string>https://github.com/neurolabusc/dcm2niix.git</string> <key>IDESourceControlProjectVersion</key> <integer>111</integer> <key>IDESourceControlProjectWCCIdentifier</key> <string>D94D2C5489FA4C6F294552E6DEAD453AC2154E05</string> <key>IDESourceControlProjectWCConfigurations</key> <array> <dict> <key>IDESourceControlRepositoryExtensionIdentifierKey</key> <string>public.vcs.git</string> <key>IDESourceControlWCCIdentifierKey</key> <string>D94D2C5489FA4C6F294552E6DEAD453AC2154E05</string> <key>IDESourceControlWCCName</key> <string>dcm2niix</string> </dict> </array> </dict> </plist> ������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/project.xcworkspace/xcuserdata/��������������������������0000775�0000000�0000000�00000000000�13220512030�0026040�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/project.xcworkspace/xcuserdata/administrator.xcuserdatad/0000775�0000000�0000000�00000000000�13220512030�0033226�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������UserInterfaceState.xcuserstate����������������������������������������������������������������������0000664�0000000�0000000�00000130723�13220512030�0041211�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/project.xcworkspace/xcuserdata/administrator.xcuserdatad��������������������������������������������������������������������bplist00Ô������ æ įX$versionX$objectsY$archiverT$top�† ¯ō������)�*�+�,�-�.�/�0�1�2�F�G�H�I�J�K�L�M�N�b�c�d�e�f�g�h�i�j�r�~�ˆ�‰��‘�’�™�œ��Ŗ�§�Ģ�¯�ŗ�¸�ŧ�Ā�Ä�Ę�Ë�Ņ�Ō�Ü�Ũ�Ū�ß�ã�õ�ö�÷�ø�ų�ú�û�ü�ũ    !"#$*+089:;<?GHKNOSYZ[efghijn~€‚ƒ„X[^adgjmpsvy|‚…ˆ‹Ž‘”—š ŖĻŠŦ¯˛ĩ¸ģžÁÄĮĘÍĐĶÖŲÜßâåčëîņô÷úũ�  !$'*-0369<?BEHKNQTWZ]`cfilorux{~„‡Š“–™œŸĸĨ¨ĢŽą´ˇēŊĀÃÆÉĖĪŌÕØÛŪáäįęíđķöųü˙  #&),/258;>ADGJMPSVY\_behknqtwz}€ƒ†‰Œ’•˜›žĄ¤§Ē­°ŗļšŧŋÂÅČËĪ�y�{Ō×ÛÜŨãäčėíîüũū˙�"#$,-.28=EFGOPQRSY^fgopz{|}“”•–—˜™š›œ ¤§Ē­°ąÃÄÅÆĮČÉĘËÎŌÖÚÛāäéęđöū˙�  123456789:;EFGHNRUabcdeijtuvw{€Ž‘’“”•™žŸ ¤ĒŽŧŊžÂĮČŌĶÔÕŲéŊĀÃÆÉĖĪŌÕØÛŪáäįęíđķöųü˙  #&),/258;>ADGJMPSVY\_behknqtwz}€ƒ†‰Œ’•˜›žĄ¤§Ē­°ŗļšŧŋÂÅČËÎŅÔ×ÚŨāãæéėīōõøûū          " % ( + . 1 4 7 : = @ C F I L O R U X [ ^ a d g j m p s v y |  ‚ … ˆ ‹ Ž ‘ ” — š    Ŗ Ļ Š Ŧ ¯ ˛ ĩ ¸ ģ ž Á Ä Į Ę Í Đ Ķ Ö Ų Ü ß â å č ë î ņ ô ÷ ú ũ �         ! $ ' * - 0 4 7 = A D P Q R S T U V W ] ^ _ g h i j k u v w x  ‰ Š ‹ Œ  Ž  ™ š › œ Ļ § ¨ Ŧ ° Ā Á Â Ã Ä Å É Ė Í Î Ō Ø Ü ā á åU$nullĶ� � � � � �V$classWNS.keysZNS.objects€Zĸ��€€ĸ��€Ē_$D6B26977-5BAE-4162-AD20-CBD0FA8CE979_IDEWorkspaceDocumentĶ� � � ��� €^¨��������€€€€€ € € € ¨�!�"�#�$���'�$€ €Ёm€€Qm^IDEWindowFrame_>IDEWorkspaceTabController_3CC4FAFF-D71B-445E-8600-74467E58A12B_!IDEOrderedWorkspaceTabControllers_IDEWindowInFullscreenMode_,IDEWorkspaceWindowControllerUniqueIdentifier_IDEActiveWorkspaceTabController_IDEWindowToolbarIsVisible_IDEWindowTabBarIsVisible_{{932, 266}, {1400, 952}}Ķ� � � ��4�=€^¨�5�6�7�8�9�:�;�<€€€€€€€€¨�>�'�@�A�B�C�'�E€QnouQ¨]IDEEditorArea_IDEShowNavigator_AssistantEditorsLayout_-IDEWorkspaceTabControllerUtilityAreaSplitView_IDENavigatorArea_,IDEWorkspaceTabControllerDesignAreaSplitView_IDEShowUtilities[IDETabLabelĶ� � � ��P�Y€^¨�Q�R�S�T�U�V�W�X€€€€€€€€¨�Z�[�'�]�^�_�`�$€ €3QFRdemZlayoutTree_IDEEditorMode_Standard]IDEShowEditorZEditorMode_IDEDefaultDebugArea_ DefaultPersistentRepresentations_DebuggerSplitView_ShowDebuggerAreaÔ�k�l� �m�n�o�p�q_primaryEditorContextNode_geniusEditorContextNode_rootLayoutTreeNode€!€�€2€/Ö�s�t�u�v� �w�x�y�q�{�|�o_ documentArchivableRepresentation[orientationVparent[contentTypeXchildren€"�€/€1€�Õ��€�� �‚�ƒ�„�…�†�‡_DocumentLocation^IdentifierPath_DomainIdentifier_IndexOfDocumentIdentifier€*€$€#€.€)_/Xcode.IDENavigableItemDomain.WorkspaceStructureŌ� � �Š�‹€(Ą�Œ€%Ō� �Ž��ZIdentifier€'€&Tdcm2Ō�“�”�•�–Z$classnameX$classes_IDEArchivableStringIndexPairĸ�—�˜_IDEArchivableStringIndexPairXNSObjectŌ�“�”�š�›WNSArrayĸ�š�˜˙˙˙˙˙˙˙Ķ�ž� �Ÿ�o�Ą�ĸYtimestamp[documentURL€�€-€+Ō� �¤�Ĩ�ĻYNS.string€,_Jfile://localhost/Users/administrator/Documents/Rorden/dcm2/dcm2.xcodeproj/Ō�“�”�¨�Š_NSMutableStringŖ�¨�Ē�˜XNSStringŌ�“�”�Ŧ�­_DVTDocumentLocationĸ�Ž�˜_DVTDocumentLocationŌ�“�”�°�ą_(IDENavigableItemArchivableRepresentationĸ�˛�˜_(IDENavigableItemArchivableRepresentationÖ�s�t�u�v� �w�o�{�o�y�|�ˇ€�€�€1€0Ō� � �Š�ē€(Ą�n€!Ō�“�”�Ŋ�ž_'IDEWorkspaceTabControllerLayoutTreeNodeĸ�ŋ�˜_'IDEWorkspaceTabControllerLayoutTreeNodeŌ�“�”�Á�Â_#IDEWorkspaceTabControllerLayoutTreeĸ�Ã�˜_#IDEWorkspaceTabControllerLayoutTreeĶ� � � ��Æ�Ȁ^Ą�Į€4Ą�ɀ5_%EditorLayout_PersistentRepresentationĶ� � � ��Í�Ī€^Ą�΀6Ą�Ѐ7TMainĶ� � � � �Ô�؀ZŖ�Õ�Ö�׀8€9€:Ŗ�Ų�]�ۀ;FO_)EditorLayout_StateSavingStateDictionaries_EditorLayout_Selected_EditorLayout_GeometryŌ� � �Š�á€(Ą�â€<Ķ� � � ��å�í€^§�æ�į�č�é�ę�ë�ė€=€>€?€@€A€B€C§�î�ī�đ�ņ�ņ�ķ�ô€D€E€KJJKL\FileDataType_ArchivableRepresentation[EditorState_NavigableItemName_DocumentNavigableItemName_DocumentExtensionIdentifier[DocumentURL_com.apple.xcode.projectÕ��€�� �‚�ū�˙�…�†€J€F€#€.€IŌ� � �Š€(Ą€GŌ� �Ž� €'€HTdcm2˙˙˙˙˙˙˙Ķ�ž� �Ÿ�o�Ą�ĸ€�€-€+Ķ� � � � €ZĨ€L€M€N€O€PĨ€Q€R€_€`I_-Xcode3ProjectEditorPreviousProjectEditorClass_(Xcode3ProjectEditor.sourceList.splitview_,Xcode3ProjectEditorPreviousTargetEditorClass_,Xcode3ProjectEditorSelectedDocumentLocations_-Xcode3ProjectEditor_Xcode3BuildSettingsEditor_Xcode3BuildSettingsEditorĶ� � � �&(€^Ą'€SĄ)€T_DVTSplitViewItemsŌ� � ,-€]ĸ./€U€[Ķ� � � � 25€Zĸ34€V€Wĸ67€X€Y]DVTIdentifier_DVTViewMagnitudeP#@e@�����Ō�“�”=>\NSDictionaryĸ=�˜Ķ� � � � AD€Zĸ34€V€Wĸ6F€X€\#@†0�����Ō�“�”IJ^NSMutableArrayŖI�š�˜Ō�“�”LM_NSMutableDictionaryŖL=�˜_Xcode3TargetEditorŌ� � �ŠQ€(ĄR€aÔ�ž� T�ŸUVWXYselection€cH€d€b_Jfile://localhost/Users/administrator/Documents/Rorden/dcm2/dcm2.xcodeproj/#AšÃĪ_čhĶ� � � �]a€^Ŗ^_`€e€f€gŖbcd€h€i€jWProjectVEditor_"Xcode3BuildSettingsEditorLocationsTdcm2_Xcode3BuildSettingsEditorŌ� � �Šl€(Ąm€kĶ� � � �pw€^Ļqrstuv€l€m€n€o€p€qĻxy�]�]�]}€rDFFFG_#Collapsed Build Property Categories_Selected Build Properties_$Xcode3BuildSettingsEditorDisplayMode_#Xcode3BuildPropertyValueDisplayMode_Xcode3BuildSettingsEditorMode_"Xcode3BuildPropertyNameDisplayModeŌ� � ,†€]¯Ņ‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ĄĸŖ¤Ĩϧ¨ŠĒĢŦ­Ž¯°ą˛ŗ´ĩšēģŧŊžŋĀÁÂÃÄÅÆĮČÉĘËĖÍÎĪĐŅŌĶÔÕÖרŲÚÛÜŨŪßāáâãäåæįčéęëėíîīđņōķôõö÷øųúûüũū˙�      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVW€s€t€u€v€w€x€y€z€{€|€}€~€€€€€‚€ƒ€„€…€†€‡€ˆ€‰€Š€‹€Œ€€Ž€€€‘€’€“€”€•€–€—€˜€™€š€›€œ€€ž€Ÿ€ €Ą€ĸ€Ŗ€¤€Ĩ€Ļ€§€¨€Š€Ē€Ģ€Ŧ€­€Ž€¯€°€ą€˛€ŗ€´€ĩ€ļ€ˇ€¸€š€ē€ģ€ŧ€Ŋ€ž€ŋ€Ā€Á€Â€Ã€Ä€Å€Æ€Į€Č€É€Ę€Ë€Ė€Í€Î€Ī€Đ€Ņ€Ō€Ķ€Ô€Õ€Ö€×€Ø€Ų€Ú€Û€Ü€Ũ€Ū€ß€ā€á€â€ã€ä€å€æ€į€č€é€ę€ë€ė€í€î€ī€đ€ņ€ō€ķ€ô€õ€ö€÷€ø€ų€ú€û€ü€ũ€ū€˙�      !"#$%&'()*+,-./0123456789:;<=>?@ABCŌ� �¤�ĨZ€,_Architectures||ADDITIONAL_SDKSŌ� �¤�Ĩ]€,_Architectures||ARCHSŌ� �¤�Ĩ`€,_Architectures||SDKROOTŌ� �¤�Ĩc€,_#Architectures||SUPPORTED_PLATFORMSŌ� �¤�Ĩf€,_Architectures||VALID_ARCHSŌ� �¤�Ĩi€,_Build Locations||SYMROOTŌ� �¤�Ĩl€,_Build Locations||OBJROOTŌ� �¤�Ĩo€,_%Build Locations||SHARED_PRECOMPS_DIRŌ� �¤�Ĩr€,_Build Options||BUILD_VARIANTSŌ� �¤�Ĩu€,_Build Options||GCC_VERSIONŌ� �¤�Ĩx€,_%Build Options||ENABLE_OPENMP_SUPPORTŌ� �¤�Ĩ{€,_'Build Options||GENERATE_PROFILING_CODEŌ� �¤�Ĩ~€,_@Build Options||PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIRŌ� �¤�Ĩ€,_)Build Options||RUN_CLANG_STATIC_ANALYZERŌ� �¤�Ĩ„€,_2Build Options||SCAN_ALL_SOURCE_FILES_FOR_INCLUDESŌ� �¤�Ĩ‡€,_ Build Options||VALIDATE_PRODUCTŌ� �¤�ĨŠ€,_%Code Signing||CODE_SIGN_ENTITLEMENTSŌ� �¤�Ĩ€,_!Code Signing||CODE_SIGN_IDENTITYŌ� �¤�Ĩ€,_,Code Signing||CODE_SIGN_RESOURCE_RULES_PATHŌ� �¤�Ĩ“€,_$Code Signing||OTHER_CODE_SIGN_FLAGSŌ� �¤�Ĩ–€,_Deployment||STRIPFLAGSŌ� �¤�Ĩ™€,_Deployment||ALTERNATE_GROUPŌ� �¤�Ĩœ€,_Deployment||ALTERNATE_OWNERŌ� �¤�ĨŸ€,_Deployment||ALTERNATE_MODEŌ� �¤�Ĩĸ€,_(Deployment||ALTERNATE_PERMISSIONS_FILESŌ� �¤�ĨĨ€,_!Deployment||COMBINE_HIDPI_IMAGESŌ� �¤�Ĩ¨€,_ Deployment||DEPLOYMENT_LOCATIONŌ� �¤�ĨĢ€,_&Deployment||DEPLOYMENT_POSTPROCESSINGŌ� �¤�ĨŽ€,_Deployment||INSTALL_GROUPŌ� �¤�Ĩą€,_Deployment||INSTALL_OWNERŌ� �¤�Ĩ´€,_Deployment||INSTALL_MODE_FLAGŌ� �¤�Ĩˇ€,_Deployment||DSTROOTŌ� �¤�Ĩē€,_Deployment||INSTALL_PATHŌ� �¤�ĨŊ€,_%Deployment||MACOSX_DEPLOYMENT_TARGETŌ� �¤�ĨĀ€,_%Deployment||PRODUCT_DEFINITION_PLISTŌ� �¤�ĨÀ,_Deployment||SKIP_INSTALLŌ� �¤�Ĩƀ,_$Deployment||STRIP_INSTALLED_PRODUCTŌ� �¤�Ĩɀ,_Deployment||STRIP_STYLEŌ� �¤�ĨĖ€,_Deployment||SEPARATE_STRIPŌ� �¤�ĨĪ€,_Kernel Module||MODULE_NAMEŌ� �¤�ĨŌ€,_Kernel Module||MODULE_STARTŌ� �¤�ĨՀ,_Kernel Module||MODULE_STOPŌ� �¤�Ĩ؀,_Kernel Module||MODULE_VERSIONŌ� �¤�Ĩۀ,_Linking||BUNDLE_LOADERŌ� �¤�ĨŪ€,_%Linking||DYLIB_COMPATIBILITY_VERSIONŌ� �¤�Ĩá€,_Linking||DYLIB_CURRENT_VERSIONŌ� �¤�Ĩä€,_Linking||DEAD_CODE_STRIPPINGŌ� �¤�Ĩį€,_'Linking||LINKER_DISPLAYS_MANGLED_NAMESŌ� �¤�Ĩę€,_Linking||LD_NO_PIEŌ� �¤�Ĩí€,_,Linking||PRESERVE_DEAD_CODE_INITS_AND_TERMSŌ� �¤�Ĩđ€,_Linking||LD_DYLIB_INSTALL_NAMEŌ� �¤�Ĩķ€,_Linking||EXPORTED_SYMBOLS_FILEŌ� �¤�Ĩö€,_Linking||INIT_ROUTINEŌ� �¤�Ĩų€,_&Linking||LINK_WITH_STANDARD_LIBRARIESŌ� �¤�Ĩü€,_Linking||MACH_O_TYPEŌ� �¤�Ĩ˙€,_Linking||LD_OPENMP_FLAGSŌ� �¤�Ĩ€,_Linking||ORDER_FILEŌ� �¤�Ĩ€,_Linking||OTHER_LDFLAGSŌ� �¤�Ĩ€,_%Linking||GENERATE_MASTER_OBJECT_FILEŌ� �¤�Ĩ €,_Linking||PRELINK_LIBSŌ� �¤�Ĩ€,_Linking||KEEP_PRIVATE_EXTERNSŌ� �¤�Ĩ€,_!Linking||LD_RUNPATH_SEARCH_PATHSŌ� �¤�Ĩ€,_Linking||SEPARATE_SYMBOL_EDITŌ� �¤�Ĩ€,_Linking||PRELINK_FLAGSŌ� �¤�Ĩ€,_Linking||SECTORDER_FLAGSŌ� �¤�Ĩ€,_!Linking||UNEXPORTED_SYMBOLS_FILEŌ� �¤�Ĩ €,_Linking||WARNING_LDFLAGSŌ� �¤�Ĩ#€,_Linking||LD_GENERATE_MAP_FILEŌ� �¤�Ĩ&€,_%Packaging||APPLY_RULES_IN_COPY_FILESŌ� �¤�Ĩ)€,_ Packaging||EXECUTABLE_EXTENSIONŌ� �¤�Ĩ,€,_Packaging||EXECUTABLE_PREFIXŌ� �¤�Ĩ/€,_+Packaging||INFOPLIST_EXPAND_BUILD_SETTINGSŌ� �¤�Ĩ2€,_!Packaging||GENERATE_PKGINFO_FILEŌ� �¤�Ĩ5€,_Packaging||FRAMEWORK_VERSIONŌ� �¤�Ĩ8€,_Packaging||INFOPLIST_FILEŌ� �¤�Ĩ;€,_.Packaging||INFOPLIST_OTHER_PREPROCESSOR_FLAGSŌ� �¤�Ĩ>€,_#Packaging||INFOPLIST_OUTPUT_FORMATŌ� �¤�ĨA€,_.Packaging||INFOPLIST_PREPROCESSOR_DEFINITIONSŌ� �¤�ĨD€,_#Packaging||INFOPLIST_PREFIX_HEADERŌ� �¤�ĨG€,_ Packaging||INFOPLIST_PREPROCESSŌ� �¤�ĨJ€,_&Packaging||COPYING_PRESERVES_HFS_DATAŌ� �¤�ĨM€,_'Packaging||PRIVATE_HEADERS_FOLDER_PATHŌ� �¤�ĨP€,_Packaging||PRODUCT_NAMEŌ� �¤�ĨS€,_$Packaging||PLIST_FILE_OUTPUT_FORMATŌ� �¤�ĨV€,_&Packaging||PUBLIC_HEADERS_FOLDER_PATHŌ� �¤�ĨY€,_(Packaging||STRINGS_FILE_OUTPUT_ENCODINGŌ� �¤�Ĩ\€,_Packaging||WRAPPER_EXTENSIONŌ� �¤�Ĩ_€,_'Search Paths||ALWAYS_SEARCH_USER_PATHSŌ� �¤�Ĩb€,_%Search Paths||FRAMEWORK_SEARCH_PATHSŌ� �¤�Ĩe€,_"Search Paths||HEADER_SEARCH_PATHSŌ� �¤�Ĩh€,_#Search Paths||LIBRARY_SEARCH_PATHSŌ� �¤�Ĩk€,_Search Paths||REZ_SEARCH_PATHSŌ� �¤�Ĩn€,_<Search Paths||EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIESŌ� �¤�Ĩq€,_<Search Paths||INCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIESŌ� �¤�Ĩt€,_'Search Paths||USER_HEADER_SEARCH_PATHSŌ� �¤�Ĩw€,_Unit Testing||OTHER_TEST_FLAGSŌ� �¤�Ĩz€,_Unit Testing||TEST_AFTER_BUILDŌ� �¤�Ĩ}€,_Unit Testing||TEST_HOSTŌ� �¤�Ĩ€€,_Unit Testing||TEST_RIGŌ� �¤�Ĩƒ€,_$Versioning||CURRENT_PROJECT_VERSIONŌ� �¤�Ĩ†€,_Versioning||VERSION_INFO_FILEŌ� �¤�Ĩ‰€,_%Versioning||VERSION_INFO_EXPORT_DECLŌ� �¤�ĨŒ€,_ Versioning||VERSION_INFO_PREFIXŌ� �¤�Ĩ€,_ Versioning||VERSION_INFO_SUFFIXŌ� �¤�Ĩ’€,_Versioning||VERSIONING_SYSTEMŌ� �¤�Ĩ•€,_!Versioning||VERSION_INFO_BUILDERŌ� �¤�Ĩ˜€,_AApple LLVM compiler 3.0 - Code Generation||GCC_FAST_OBJC_DISPATCHŌ� �¤�Ĩ›€,_EApple LLVM compiler 3.0 - Code Generation||GCC_ENABLE_SSE3_EXTENSIONSŌ� �¤�Ĩž€,_FApple LLVM compiler 3.0 - Code Generation||GCC_ENABLE_SSE41_EXTENSIONSŌ� �¤�ĨĄ€,_FApple LLVM compiler 3.0 - Code Generation||GCC_ENABLE_SSE42_EXTENSIONSŌ� �¤�Ĩ¤€,_TApple LLVM compiler 3.0 - Code Generation||GCC_ENABLE_SUPPLEMENTAL_SSE3_INSTRUCTIONSŌ� �¤�Ĩ§€,_>Apple LLVM compiler 3.0 - Code Generation||GCC_STRICT_ALIASINGŌ� �¤�ĨĒ€,_IApple LLVM compiler 3.0 - Code Generation||GCC_GENERATE_DEBUGGING_SYMBOLSŌ� �¤�Ĩ­€,_=Apple LLVM compiler 3.0 - Code Generation||GCC_DYNAMIC_NO_PICŌ� �¤�Ĩ°€,_KApple LLVM compiler 3.0 - Code Generation||GCC_GENERATE_TEST_COVERAGE_FILESŌ� �¤�Ĩŗ€,_IApple LLVM compiler 3.0 - Code Generation||GCC_INLINES_ARE_PRIVATE_EXTERNŌ� �¤�Ĩļ€,_KApple LLVM compiler 3.0 - Code Generation||GCC_INSTRUMENT_PROGRAM_FLOW_ARCSŌ� �¤�Ĩš€,_HApple LLVM compiler 3.0 - Code Generation||GCC_ENABLE_KERNEL_DEVELOPMENTŌ� �¤�Ĩŧ€,_3Apple LLVM compiler 3.0 - Code Generation||LLVM_LTOŌ� �¤�Ĩŋ€,_<Apple LLVM compiler 3.0 - Code Generation||GCC_REUSE_STRINGSŌ� �¤�Ĩ€,_?Apple LLVM compiler 3.0 - Code Generation||GCC_NO_COMMON_BLOCKSŌ� �¤�Ĩŀ,_8Apple LLVM compiler 3.0 - Code Generation||GCC_FAST_MATHŌ� �¤�ĨȀ,_AApple LLVM compiler 3.0 - Code Generation||GCC_THREADSAFE_STATICSŌ� �¤�Ĩˀ,_EApple LLVM compiler 3.0 - Code Generation||GCC_SYMBOLS_PRIVATE_EXTERNŌ� �¤�Ĩ΀,_;Apple LLVM compiler 3.0 - Code Generation||GCC_UNROLL_LOOPSŌ� �¤�ĨŅ€,_=Apple LLVM compiler 3.0 - Language||GCC_CHAR_IS_UNSIGNED_CHARŌ� �¤�ĨԀ,_:Apple LLVM compiler 3.0 - Language||GCC_ENABLE_ASM_KEYWORDŌ� �¤�Ĩ׀,_;Apple LLVM compiler 3.0 - Language||GCC_C_LANGUAGE_STANDARDŌ� �¤�Ĩڀ,_?Apple LLVM compiler 3.0 - Language||CLANG_CXX_LANGUAGE_STANDARDŌ� �¤�ĨŨ€,_5Apple LLVM compiler 3.0 - Language||CLANG_CXX_LIBRARYŌ� �¤�Ĩā€,_5Apple LLVM compiler 3.0 - Language||GCC_CW_ASM_SYNTAXŌ� �¤�Ĩã€,_6Apple LLVM compiler 3.0 - Language||GCC_INPUT_FILETYPEŌ� �¤�Ĩæ€,_=Apple LLVM compiler 3.0 - Language||GCC_ENABLE_CPP_EXCEPTIONSŌ� �¤�Ĩé€,_7Apple LLVM compiler 3.0 - Language||GCC_ENABLE_CPP_RTTIŌ� �¤�Ĩė€,_CApple LLVM compiler 3.0 - Language||GCC_LINK_WITH_DYNAMIC_LIBRARIESŌ� �¤�Ĩī€,_>Apple LLVM compiler 3.0 - Language||GCC_ENABLE_OBJC_EXCEPTIONSŌ� �¤�Ĩō€,_8Apple LLVM compiler 3.0 - Language||GCC_ENABLE_TRIGRAPHSŌ� �¤�Ĩõ€,_KApple LLVM compiler 3.0 - Language||GCC_ENABLE_FLOATING_POINT_LIBRARY_CALLSŌ� �¤�Ĩø€,_CApple LLVM compiler 3.0 - Language||GCC_USE_INDIRECT_FUNCTION_CALLSŌ� �¤�Ĩû€,_CApple LLVM compiler 3.0 - Language||GCC_USE_REGISTER_FUNCTION_CALLSŌ� �¤�Ĩū€,_KApple LLVM compiler 3.0 - Language||GCC_INCREASE_PRECOMPILED_HEADER_SHARINGŌ� �¤�Ĩ€,_9Apple LLVM compiler 3.0 - Language||CLANG_ENABLE_OBJC_ARCŌ� �¤�Ĩ€,_6Apple LLVM compiler 3.0 - Language||GCC_ENABLE_OBJC_GCŌ� �¤�Ĩ€,_0Apple LLVM compiler 3.0 - Language||OTHER_CFLAGSŌ� �¤�Ĩ €,_8Apple LLVM compiler 3.0 - Language||OTHER_CPLUSPLUSFLAGSŌ� �¤�Ĩ €,_@Apple LLVM compiler 3.0 - Language||GCC_PRECOMPILE_PREFIX_HEADERŌ� �¤�Ĩ€,_5Apple LLVM compiler 3.0 - Language||GCC_PREFIX_HEADERŌ� �¤�Ĩ€,_@Apple LLVM compiler 3.0 - Language||GCC_ENABLE_BUILTIN_FUNCTIONSŌ� �¤�Ĩ€,_=Apple LLVM compiler 3.0 - Language||GCC_ENABLE_PASCAL_STRINGSŌ� �¤�Ĩ€,_=Apple LLVM compiler 3.0 - Language||GCC_FORCE_CPU_SUBTYPE_ALLŌ� �¤�Ĩ€,_3Apple LLVM compiler 3.0 - Language||GCC_SHORT_ENUMSŌ� �¤�Ĩ€,_FApple LLVM compiler 3.0 - Language||GCC_USE_STANDARD_INCLUDE_SEARCHINGŌ� �¤�Ĩ"€,_ZApple LLVM compiler 3.0 - Preprocessing||GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPSŌ� �¤�Ĩ%€,_DApple LLVM compiler 3.0 - Warnings||GCC_WARN_CHECK_SWITCH_STATEMENTSŌ� �¤�Ĩ(€,_EApple LLVM compiler 3.0 - Warnings||GCC_WARN_FOUR_CHARACTER_CONSTANTSŌ� �¤�Ĩ+€,_3Apple LLVM compiler 3.0 - Warnings||GCC_WARN_SHADOWŌ� �¤�Ĩ.€,_NApple LLVM compiler 3.0 - Warnings||CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIESŌ� �¤�Ĩ1€,_DApple LLVM compiler 3.0 - Warnings||GCC_WARN_64_TO_32_BIT_CONVERSIONŌ� �¤�Ĩ4€,_GApple LLVM compiler 3.0 - Warnings||CLANG_WARN_IMPLICIT_SIGN_CONVERSIONŌ� �¤�Ĩ7€,_FApple LLVM compiler 3.0 - Warnings||GCC_WARN_ALLOW_INCOMPLETE_PROTOCOLŌ� �¤�Ĩ:€,_AApple LLVM compiler 3.0 - Warnings||GCC_WARN_INHIBIT_ALL_WARNINGSŌ� �¤�Ĩ=€,_LApple LLVM compiler 3.0 - Warnings||GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETEDŌ� �¤�Ĩ@€,_>Apple LLVM compiler 3.0 - Warnings||GCC_WARN_ABOUT_RETURN_TYPEŌ� �¤�ĨC€,_@Apple LLVM compiler 3.0 - Warnings||GCC_WARN_MISSING_PARENTHESESŌ� �¤�ĨF€,_MApple LLVM compiler 3.0 - Warnings||GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERSŌ� �¤�ĨI€,_EApple LLVM compiler 3.0 - Warnings||GCC_WARN_ABOUT_MISSING_PROTOTYPESŌ� �¤�ĨL€,_BApple LLVM compiler 3.0 - Warnings||GCC_WARN_ABOUT_MISSING_NEWLINEŌ� �¤�ĨO€,_SApple LLVM compiler 3.0 - Warnings||GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTORŌ� �¤�ĨR€,_CApple LLVM compiler 3.0 - Warnings||GCC_WARN_NON_VIRTUAL_DESTRUCTORŌ� �¤�ĨU€,_=Apple LLVM compiler 3.0 - Warnings||CLANG_WARN_OBJCPP_ARC_ABIŌ� �¤�ĨX€,_2Apple LLVM compiler 3.0 - Warnings||WARNING_CFLAGSŌ� �¤�Ĩ[€,_EApple LLVM compiler 3.0 - Warnings||GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONSŌ� �¤�Ĩ^€,_NApple LLVM compiler 3.0 - Warnings||CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONSŌ� �¤�Ĩa€,_5Apple LLVM compiler 3.0 - Warnings||GCC_WARN_PEDANTICŌ� �¤�Ĩd€,_EApple LLVM compiler 3.0 - Warnings||GCC_WARN_ABOUT_POINTER_SIGNEDNESSŌ� �¤�Ĩg€,_9Apple LLVM compiler 3.0 - Warnings||GCC_WARN_SIGN_COMPAREŌ� �¤�Ĩj€,_BApple LLVM compiler 3.0 - Warnings||GCC_WARN_STRICT_SELECTOR_MATCHŌ� �¤�Ĩm€,_MApple LLVM compiler 3.0 - Warnings||CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSIONŌ� �¤�Ĩp€,_ZApple LLVM compiler 3.0 - Warnings||GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORSŌ� �¤�Ĩs€,_VApple LLVM compiler 3.0 - Warnings||GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORSŌ� �¤�Ĩv€,_@Apple LLVM compiler 3.0 - Warnings||GCC_TREAT_WARNINGS_AS_ERRORSŌ� �¤�Ĩy€,_FApple LLVM compiler 3.0 - Warnings||GCC_WARN_TYPECHECK_CALLS_TO_PRINTFŌ� �¤�Ĩ|€,_@Apple LLVM compiler 3.0 - Warnings||GCC_WARN_UNDECLARED_SELECTORŌ� �¤�Ĩ€,_@Apple LLVM compiler 3.0 - Warnings||GCC_WARN_UNINITIALIZED_AUTOSŌ� �¤�Ĩ‚€,_<Apple LLVM compiler 3.0 - Warnings||GCC_WARN_UNKNOWN_PRAGMASŌ� �¤�Ĩ…€,_<Apple LLVM compiler 3.0 - Warnings||GCC_WARN_UNUSED_FUNCTIONŌ� �¤�Ĩˆ€,_9Apple LLVM compiler 3.0 - Warnings||GCC_WARN_UNUSED_LABELŌ� �¤�Ĩ‹€,_=Apple LLVM compiler 3.0 - Warnings||GCC_WARN_UNUSED_PARAMETERŌ� �¤�ĨŽ€,_9Apple LLVM compiler 3.0 - Warnings||GCC_WARN_UNUSED_VALUEŌ� �¤�Ĩ‘€,_<Apple LLVM compiler 3.0 - Warnings||GCC_WARN_UNUSED_VARIABLEŌ� �¤�Ĩ”€,_?Apple LLVM compiler 3.0 - Warnings||CLANG_WARN_CXX0X_EXTENSIONSŌ� �¤�Ĩ—€,_GApple LLVM compiler 3.0 - Warnings||GCC_WARN_ABOUT_DEPRECATED_FUNCTIONSŌ� �¤�Ĩš€,_IApple LLVM compiler 3.0 - Warnings||GCC_WARN_ABOUT_INVALID_OFFSETOF_MACROŌ� �¤�Ĩ€,_:Interface Builder XIB Compiler - Options||IBC_FLATTEN_NIBSŌ� �¤�Ĩ €,_9Interface Builder XIB Compiler - Options||IBC_OTHER_FLAGSŌ� �¤�ĨŖ€,_SInterface Builder XIB Compiler - Options||IBC_OVERRIDING_PLUGINS_AND_FRAMEWORKS_DIRŌ� �¤�ĨĻ€,_AInterface Builder XIB Compiler - Options||IBC_PLUGIN_SEARCH_PATHSŌ� �¤�ĨŠ€,_5Interface Builder XIB Compiler - Options||IBC_PLUGINSŌ� �¤�ĨŦ€,_4Interface Builder XIB Compiler - Options||IBC_ERRORSŌ� �¤�Ĩ¯€,_5Interface Builder XIB Compiler - Options||IBC_NOTICESŌ� �¤�Ĩ˛€,_6Interface Builder XIB Compiler - Options||IBC_WARNINGSŌ� �¤�Ĩĩ€,_>Static Analyzer - Checkers||CLANG_ANALYZER_DEADCODE_DEADSTORESŌ� �¤�Ĩ¸€,_IStatic Analyzer - Checkers||CLANG_ANALYZER_DEADCODE_IDEMPOTENT_OPERATIONSŌ� �¤�Ĩģ€,_9Static Analyzer - Checkers||CLANG_ANALYZER_OBJC_SELF_INITŌ� �¤�Ĩž€,_,User-Defined||CLANG_WARN_CONSTANT_CONVERSIONŌ� �¤�ĨÁ€,_#User-Defined||CLANG_WARN_EMPTY_BODYŌ� �¤�ĨĀ,_(User-Defined||CLANG_WARN_ENUM_CONVERSIONŌ� �¤�ĨĮ€,_'User-Defined||CLANG_WARN_INT_CONVERSIONŌ� �¤�Ĩʀ,_0User-Defined||CLANG_WARN__DUPLICATE_METHOD_MATCHŌ� � ,̀]Ą΁EŌ� �¤�ĨŅ€,_Architectures||ARCHSŌ�“�”ĶÔ_Xcode3ProjectDocumentLocationŖÕÖ�˜_Xcode3ProjectDocumentLocation_DVTDocumentLocationĶ� � � �Ųڀ^  Tdcm2_7Xcode.Xcode3ProjectSupport.EditorDocument.Xcode3ProjectĶ� Ūßāá�o[NS.relativeWNS.baseNM€�_Jfile://localhost/Users/administrator/Documents/Rorden/dcm2/dcm2.xcodeproj/Ō�“�”åæUNSURLĸį�˜UNSURLŌ� � �Šę€(ĄëP_{{0, 0}, {880, 876}} Ķ� � � �đö€^ĨņōķôõSTUVWĨ}øų}ûGXZG\_LayoutFocusModeYVariablesWConsoleZLayoutMode_IDEDebugArea_SplitViewĶ� � � �€^ĄYĄ}G_VariablesViewSelectedScopeĶ� � � �  €^Ą [Ą�]F_ConsoleFilterModeĶ� � � �€^Ą'€SĄ]Ō� � ,€]ĸ^aĶ� � � � €Zĸ34€V€Wĸ !_`]VariablesView#@{€�����Ķ� � � � &)€Zĸ34€V€Wĸ*+bc[ConsoleArea#@{p�����Ķ� � � �01€^  Ķ� � � �46€^Ą'€SĄ7fŌ� � ,:€]ĸ;<gjĶ� � � � ?B€Zĸ34€V€WĸCDhiYIDEEditor#@i`�����Ķ� � � � IL€Zĸ34€V€WĸMNkl_IDEDebuggerArea#@\Ā�����Ķ� � � �UW€^Ą'€SĄXpŌ� � ,[€]ĸ\]qsĶ� � � � `c€Zĸ34€V€Wĸ6e€Xr#@ƒ�����Ķ� � � � il€Zĸ34€V€Wĸ6n€Xt#@p �����Ķ� � � �rv€^ŖstuvwxŖwxyyЁ‹_Xcode.IDEKit.Navigator.Issues_SelectedNavigator_ Xcode.IDEKit.Navigator.StructureĶ� � � �‰€^Š€‚ƒ„…†‡ˆz{|}~€‚Š�$‹Œ�$�$’mƒ„†m‡ˆm‰_IDEErrorFilteringEnabled^IDEVisibleRect_IDECollapsedFiles_IDEExpandedIssues^IDEShowsByType_IDESelectedNavigables_IDECollapsedTypes_IDERecentFilteringEnabled_IDECollapsedGroups_{{0, 0}, {259, 810}}Ō� � žŸ… Ō�“�”Ąĸ\NSMutableSetŖĄŖ�˜UNSSetŌ� � žρ… Ō� � ,Š€] Ō� � žŦ… Ō� � ž¯… _ Xcode.IDEKit.Navigator.StructureĶ� � � �ŗģ€^§´ĩšēŒށ‘’§ŧ�$ž�$�$Á“m”mm–™^IDEVisibleRect_"IDEUnsavedDocumentFilteringEnabled_+IDENavigatorExpandedItemsBeforeFilteringSet_!IDERecentDocumentFilteringEnabled_IDESCMStatusFilteringEnabled_IDESelectedObjects_IDEExpandedItemsSet_{{0, 0}, {259, 832}}Ō� � Ė́• Ō�“�”ĪĐUNSSetĸŅ�˜UNSSetŌ� � �ŠԀ(ĄՁ—Ō� � ,؀]Ą؁˜Tdcm2Ō� � ĖŨ•ĸŪ߁š›Ō� � ,â€]Ą؁˜Ō� � ,æ€]ĸŲč˜œTdcm2Ķ� � � �ėî€^Ą'€SĄīžŌ� � ,ō€]ŖķôõŸĸĨĶ� � � � øû€Zĸ34€V€Wĸüũ Ą_IDENavigatorArea#@p@�����Ķ� � � � €Zĸ34€V€WĸŖ¤]IDEEditorArea#@‹€�����Ķ� � � �  €Zĸ34€V€Wĸρ§_IDEUtilitiesArea#@p@�����^dcm2.xcodeprojŌ� � �Š€(Ą�€Ķ� � � �&€^Ē !"#$%́Ŧ­ށ¯°ą˛ŗ´Ē�$(�]*+,-.�$�$mĩFāĮˁëėmm_BreakpointsActivated_DefaultEditorStatesForURLs_DebuggingWindowBehavior\ActiveScheme_ActiveRunDestination_0LastCompletedPersistentSchemeBasedActivityReport_DocumentWindows_RecentEditorDocumentURLs_AppFocusInMiniDebugging_MiniDebuggingConsoleĶ� � � �=A€^Ŗ>?@ļˇ¸ŖBCDšׁš_2Xcode.IDEKit.CocoaIntegration.EditorDocument.Cocoa_7Xcode.Xcode3ProjectSupport.EditorDocument.Xcode3Project_&Xcode.IDEKit.EditorDocument.SourceCodeĶ� � � �JL€^ĄKēĄMŧĶ� ŪßāP�oNģ€�Ō� �¤�ĨT€,_Ufile://localhost/Users/administrator/Documents/Rorden/dcm2/dcm2/en.lproj/MainMenu.xibĶ� � � �W\€^¤XYZ[Ŋžŋ¤]^_`ÁÁāĘ_SelectedMemberIdentifiers_IBHybridStructureViewController_SelectionProvider_IBCanvasViewControllerŌ� � ,g€]ĄhÂS537Ķ� � � �lp€^Ŗ_noāŁÆŖqrsĮȁÉ_IBOutlineViewController_IBDockViewController_LastKnownOutlineViewWidthĶ� � � �yz€^  Ķ� � � �}~€^  #@pā�����Ķ� � � �‚ˆ€^Ĩƒ„…†‡ˁˁ́΁ĪĨ‰Š‹ŒЁҁԁ́ÖZZoomFactor[CenterPoint_$MemberIDToLastKnownCanvasPositionMap_EditedTopLevelMemberIDs_$ObjectIDToLastKnownCanvasPositionMap#?đ������\{297, 419.5}Ķ� � � �—˜€^  Ō� � ,›€]ĸœԁÕR29S371Ķ� � � �ĸŖ€^  Ķ� � � �ύ€^Ą§ØĄЁŲĶ� Ūßā�ĸ�oN€+€�Ķ� � � �°ļ€^Ĩ€L€M€N€O€PĨˇšēڀRہ܁I_Xcode3BuildSettingsEditor_Xcode3TargetEditorŌ� � �ŠĀ€(ĄÁŨÔ�ž� T�ŸÃVÅXہH߀b#AšÃĪ^æuĶ� � � �Ę΀^Ŗ^_̀e€fāŖĪĐҁáâã_"Xcode3BuildSettingsEditorLocationsTdcm2_Xcode3BuildSettingsEditorŌ� � �Š׀(Ą؁äĶ� � � �Ûâ€^Ļqrstuv€l€m€n€o€p€qĻãä�]�]�]}åˇFFFGŌ� � ,ë€]¯Ņėíîīđņōķôõö÷øųúûüũū˙�      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ĄĸŖ¤Ĩϧ¨ŠĒĢŦ­Ž¯°ą˛ŗ´ĩšēģŧæįčéęëėíîīđņōķôõö÷øųúûüũū˙�      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Ё‹Œށ‘’“”•–—˜™š›œžŸ ĄĸŖ¤Ĩρ§¨Ёǁ́Ŧ­ށ¯°ą˛ŗ´ĩļŌ� �¤�Ĩŋ€,_Architectures||ADDITIONAL_SDKSŌ� �¤�Ĩ€,_Architectures||ARCHSŌ� �¤�Ĩŀ,_Architectures||SDKROOTŌ� �¤�ĨȀ,_#Architectures||SUPPORTED_PLATFORMSŌ� �¤�Ĩˀ,_Architectures||VALID_ARCHSŌ� �¤�Ĩ΀,_Build Locations||SYMROOTŌ� �¤�ĨŅ€,_Build Locations||OBJROOTŌ� �¤�ĨԀ,_%Build Locations||SHARED_PRECOMPS_DIRŌ� �¤�Ĩ׀,_Build Options||BUILD_VARIANTSŌ� �¤�Ĩڀ,_Build Options||GCC_VERSIONŌ� �¤�ĨŨ€,_%Build Options||ENABLE_OPENMP_SUPPORTŌ� �¤�Ĩā€,_'Build Options||GENERATE_PROFILING_CODEŌ� �¤�Ĩã€,_@Build Options||PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIRŌ� �¤�Ĩæ€,_)Build Options||RUN_CLANG_STATIC_ANALYZERŌ� �¤�Ĩé€,_2Build Options||SCAN_ALL_SOURCE_FILES_FOR_INCLUDESŌ� �¤�Ĩė€,_ Build Options||VALIDATE_PRODUCTŌ� �¤�Ĩī€,_%Code Signing||CODE_SIGN_ENTITLEMENTSŌ� �¤�Ĩō€,_!Code Signing||CODE_SIGN_IDENTITYŌ� �¤�Ĩõ€,_,Code Signing||CODE_SIGN_RESOURCE_RULES_PATHŌ� �¤�Ĩø€,_$Code Signing||OTHER_CODE_SIGN_FLAGSŌ� �¤�Ĩû€,_Deployment||STRIPFLAGSŌ� �¤�Ĩū€,_Deployment||ALTERNATE_GROUPŌ� �¤�Ĩ€,_Deployment||ALTERNATE_OWNERŌ� �¤�Ĩ€,_Deployment||ALTERNATE_MODEŌ� �¤�Ĩ€,_(Deployment||ALTERNATE_PERMISSIONS_FILESŌ� �¤�Ĩ €,_!Deployment||COMBINE_HIDPI_IMAGESŌ� �¤�Ĩ €,_ Deployment||DEPLOYMENT_LOCATIONŌ� �¤�Ĩ€,_&Deployment||DEPLOYMENT_POSTPROCESSINGŌ� �¤�Ĩ€,_Deployment||INSTALL_GROUPŌ� �¤�Ĩ€,_Deployment||INSTALL_OWNERŌ� �¤�Ĩ€,_Deployment||INSTALL_MODE_FLAGŌ� �¤�Ĩ€,_Deployment||DSTROOTŌ� �¤�Ĩ€,_Deployment||INSTALL_PATHŌ� �¤�Ĩ"€,_%Deployment||MACOSX_DEPLOYMENT_TARGETŌ� �¤�Ĩ%€,_%Deployment||PRODUCT_DEFINITION_PLISTŌ� �¤�Ĩ(€,_Deployment||SKIP_INSTALLŌ� �¤�Ĩ+€,_$Deployment||STRIP_INSTALLED_PRODUCTŌ� �¤�Ĩ.€,_Deployment||STRIP_STYLEŌ� �¤�Ĩ1€,_Deployment||SEPARATE_STRIPŌ� �¤�Ĩ4€,_Kernel Module||MODULE_NAMEŌ� �¤�Ĩ7€,_Kernel Module||MODULE_STARTŌ� �¤�Ĩ:€,_Kernel Module||MODULE_STOPŌ� �¤�Ĩ=€,_Kernel Module||MODULE_VERSIONŌ� �¤�Ĩ@€,_Linking||BUNDLE_LOADERŌ� �¤�ĨC€,_%Linking||DYLIB_COMPATIBILITY_VERSIONŌ� �¤�ĨF€,_Linking||DYLIB_CURRENT_VERSIONŌ� �¤�ĨI€,_Linking||DEAD_CODE_STRIPPINGŌ� �¤�ĨL€,_'Linking||LINKER_DISPLAYS_MANGLED_NAMESŌ� �¤�ĨO€,_Linking||LD_NO_PIEŌ� �¤�ĨR€,_,Linking||PRESERVE_DEAD_CODE_INITS_AND_TERMSŌ� �¤�ĨU€,_Linking||LD_DYLIB_INSTALL_NAMEŌ� �¤�ĨX€,_Linking||EXPORTED_SYMBOLS_FILEŌ� �¤�Ĩ[€,_Linking||INIT_ROUTINEŌ� �¤�Ĩ^€,_&Linking||LINK_WITH_STANDARD_LIBRARIESŌ� �¤�Ĩa€,_Linking||MACH_O_TYPEŌ� �¤�Ĩd€,_Linking||LD_OPENMP_FLAGSŌ� �¤�Ĩg€,_Linking||ORDER_FILEŌ� �¤�Ĩj€,_Linking||OTHER_LDFLAGSŌ� �¤�Ĩm€,_%Linking||GENERATE_MASTER_OBJECT_FILEŌ� �¤�Ĩp€,_Linking||PRELINK_LIBSŌ� �¤�Ĩs€,_Linking||KEEP_PRIVATE_EXTERNSŌ� �¤�Ĩv€,_!Linking||LD_RUNPATH_SEARCH_PATHSŌ� �¤�Ĩy€,_Linking||SEPARATE_SYMBOL_EDITŌ� �¤�Ĩ|€,_Linking||PRELINK_FLAGSŌ� �¤�Ĩ€,_Linking||SECTORDER_FLAGSŌ� �¤�Ĩ‚€,_!Linking||UNEXPORTED_SYMBOLS_FILEŌ� �¤�Ĩ…€,_Linking||WARNING_LDFLAGSŌ� �¤�Ĩˆ€,_Linking||LD_GENERATE_MAP_FILEŌ� �¤�Ĩ‹€,_%Packaging||APPLY_RULES_IN_COPY_FILESŌ� �¤�ĨŽ€,_ Packaging||EXECUTABLE_EXTENSIONŌ� �¤�Ĩ‘€,_Packaging||EXECUTABLE_PREFIXŌ� �¤�Ĩ”€,_+Packaging||INFOPLIST_EXPAND_BUILD_SETTINGSŌ� �¤�Ĩ—€,_!Packaging||GENERATE_PKGINFO_FILEŌ� �¤�Ĩš€,_Packaging||FRAMEWORK_VERSIONŌ� �¤�Ĩ€,_Packaging||INFOPLIST_FILEŌ� �¤�Ĩ €,_.Packaging||INFOPLIST_OTHER_PREPROCESSOR_FLAGSŌ� �¤�ĨŖ€,_#Packaging||INFOPLIST_OUTPUT_FORMATŌ� �¤�ĨĻ€,_.Packaging||INFOPLIST_PREPROCESSOR_DEFINITIONSŌ� �¤�ĨŠ€,_#Packaging||INFOPLIST_PREFIX_HEADERŌ� �¤�ĨŦ€,_ Packaging||INFOPLIST_PREPROCESSŌ� �¤�Ĩ¯€,_&Packaging||COPYING_PRESERVES_HFS_DATAŌ� �¤�Ĩ˛€,_'Packaging||PRIVATE_HEADERS_FOLDER_PATHŌ� �¤�Ĩĩ€,_Packaging||PRODUCT_NAMEŌ� �¤�Ĩ¸€,_$Packaging||PLIST_FILE_OUTPUT_FORMATŌ� �¤�Ĩģ€,_&Packaging||PUBLIC_HEADERS_FOLDER_PATHŌ� �¤�Ĩž€,_(Packaging||STRINGS_FILE_OUTPUT_ENCODINGŌ� �¤�ĨÁ€,_Packaging||WRAPPER_EXTENSIONŌ� �¤�ĨĀ,_'Search Paths||ALWAYS_SEARCH_USER_PATHSŌ� �¤�ĨĮ€,_%Search Paths||FRAMEWORK_SEARCH_PATHSŌ� �¤�Ĩʀ,_"Search Paths||HEADER_SEARCH_PATHSŌ� �¤�Ĩ̀,_#Search Paths||LIBRARY_SEARCH_PATHSŌ� �¤�ĨЀ,_Search Paths||REZ_SEARCH_PATHSŌ� �¤�ĨĶ€,_<Search Paths||EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIESŌ� �¤�Ĩր,_<Search Paths||INCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIESŌ� �¤�ĨŲ€,_'Search Paths||USER_HEADER_SEARCH_PATHSŌ� �¤�Ĩ܀,_Unit Testing||OTHER_TEST_FLAGSŌ� �¤�Ĩ߀,_Unit Testing||TEST_AFTER_BUILDŌ� �¤�Ĩâ€,_Unit Testing||TEST_HOSTŌ� �¤�Ĩå€,_Unit Testing||TEST_RIGŌ� �¤�Ĩč€,_$Versioning||CURRENT_PROJECT_VERSIONŌ� �¤�Ĩë€,_Versioning||VERSION_INFO_FILEŌ� �¤�Ĩî€,_%Versioning||VERSION_INFO_EXPORT_DECLŌ� �¤�Ĩņ€,_ Versioning||VERSION_INFO_PREFIXŌ� �¤�Ĩô€,_ Versioning||VERSION_INFO_SUFFIXŌ� �¤�Ĩ÷€,_Versioning||VERSIONING_SYSTEMŌ� �¤�Ĩú€,_!Versioning||VERSION_INFO_BUILDERŌ� �¤�Ĩũ€,_AApple LLVM compiler 3.0 - Code Generation||GCC_FAST_OBJC_DISPATCHŌ� �¤�Ĩ �€,_EApple LLVM compiler 3.0 - Code Generation||GCC_ENABLE_SSE3_EXTENSIONSŌ� �¤�Ĩ €,_FApple LLVM compiler 3.0 - Code Generation||GCC_ENABLE_SSE41_EXTENSIONSŌ� �¤�Ĩ €,_FApple LLVM compiler 3.0 - Code Generation||GCC_ENABLE_SSE42_EXTENSIONSŌ� �¤�Ĩ €,_TApple LLVM compiler 3.0 - Code Generation||GCC_ENABLE_SUPPLEMENTAL_SSE3_INSTRUCTIONSŌ� �¤�Ĩ €,_>Apple LLVM compiler 3.0 - Code Generation||GCC_STRICT_ALIASINGŌ� �¤�Ĩ €,_IApple LLVM compiler 3.0 - Code Generation||GCC_GENERATE_DEBUGGING_SYMBOLSŌ� �¤�Ĩ €,_=Apple LLVM compiler 3.0 - Code Generation||GCC_DYNAMIC_NO_PICŌ� �¤�Ĩ €,_KApple LLVM compiler 3.0 - Code Generation||GCC_GENERATE_TEST_COVERAGE_FILESŌ� �¤�Ĩ €,_IApple LLVM compiler 3.0 - Code Generation||GCC_INLINES_ARE_PRIVATE_EXTERNŌ� �¤�Ĩ €,_KApple LLVM compiler 3.0 - Code Generation||GCC_INSTRUMENT_PROGRAM_FLOW_ARCSŌ� �¤�Ĩ €,_HApple LLVM compiler 3.0 - Code Generation||GCC_ENABLE_KERNEL_DEVELOPMENTŌ� �¤�Ĩ !€,_3Apple LLVM compiler 3.0 - Code Generation||LLVM_LTOŌ� �¤�Ĩ $€,_<Apple LLVM compiler 3.0 - Code Generation||GCC_REUSE_STRINGSŌ� �¤�Ĩ '€,_?Apple LLVM compiler 3.0 - Code Generation||GCC_NO_COMMON_BLOCKSŌ� �¤�Ĩ *€,_8Apple LLVM compiler 3.0 - Code Generation||GCC_FAST_MATHŌ� �¤�Ĩ -€,_AApple LLVM compiler 3.0 - Code Generation||GCC_THREADSAFE_STATICSŌ� �¤�Ĩ 0€,_EApple LLVM compiler 3.0 - Code Generation||GCC_SYMBOLS_PRIVATE_EXTERNŌ� �¤�Ĩ 3€,_;Apple LLVM compiler 3.0 - Code Generation||GCC_UNROLL_LOOPSŌ� �¤�Ĩ 6€,_=Apple LLVM compiler 3.0 - Language||GCC_CHAR_IS_UNSIGNED_CHARŌ� �¤�Ĩ 9€,_:Apple LLVM compiler 3.0 - Language||GCC_ENABLE_ASM_KEYWORDŌ� �¤�Ĩ <€,_;Apple LLVM compiler 3.0 - Language||GCC_C_LANGUAGE_STANDARDŌ� �¤�Ĩ ?€,_?Apple LLVM compiler 3.0 - Language||CLANG_CXX_LANGUAGE_STANDARDŌ� �¤�Ĩ B€,_5Apple LLVM compiler 3.0 - Language||CLANG_CXX_LIBRARYŌ� �¤�Ĩ E€,_5Apple LLVM compiler 3.0 - Language||GCC_CW_ASM_SYNTAXŌ� �¤�Ĩ H€,_6Apple LLVM compiler 3.0 - Language||GCC_INPUT_FILETYPEŌ� �¤�Ĩ K€,_=Apple LLVM compiler 3.0 - Language||GCC_ENABLE_CPP_EXCEPTIONSŌ� �¤�Ĩ N€,_7Apple LLVM compiler 3.0 - Language||GCC_ENABLE_CPP_RTTIŌ� �¤�Ĩ Q€,_CApple LLVM compiler 3.0 - Language||GCC_LINK_WITH_DYNAMIC_LIBRARIESŌ� �¤�Ĩ T€,_>Apple LLVM compiler 3.0 - Language||GCC_ENABLE_OBJC_EXCEPTIONSŌ� �¤�Ĩ W€,_8Apple LLVM compiler 3.0 - Language||GCC_ENABLE_TRIGRAPHSŌ� �¤�Ĩ Z€,_KApple LLVM compiler 3.0 - Language||GCC_ENABLE_FLOATING_POINT_LIBRARY_CALLSŌ� �¤�Ĩ ]€,_CApple LLVM compiler 3.0 - Language||GCC_USE_INDIRECT_FUNCTION_CALLSŌ� �¤�Ĩ `€,_CApple LLVM compiler 3.0 - Language||GCC_USE_REGISTER_FUNCTION_CALLSŌ� �¤�Ĩ c€,_KApple LLVM compiler 3.0 - Language||GCC_INCREASE_PRECOMPILED_HEADER_SHARINGŌ� �¤�Ĩ f€,_9Apple LLVM compiler 3.0 - Language||CLANG_ENABLE_OBJC_ARCŌ� �¤�Ĩ i€,_6Apple LLVM compiler 3.0 - Language||GCC_ENABLE_OBJC_GCŌ� �¤�Ĩ l€,_0Apple LLVM compiler 3.0 - Language||OTHER_CFLAGSŌ� �¤�Ĩ o€,_8Apple LLVM compiler 3.0 - Language||OTHER_CPLUSPLUSFLAGSŌ� �¤�Ĩ r€,_@Apple LLVM compiler 3.0 - Language||GCC_PRECOMPILE_PREFIX_HEADERŌ� �¤�Ĩ u€,_5Apple LLVM compiler 3.0 - Language||GCC_PREFIX_HEADERŌ� �¤�Ĩ x€,_@Apple LLVM compiler 3.0 - Language||GCC_ENABLE_BUILTIN_FUNCTIONSŌ� �¤�Ĩ {€,_=Apple LLVM compiler 3.0 - Language||GCC_ENABLE_PASCAL_STRINGSŌ� �¤�Ĩ ~€,_=Apple LLVM compiler 3.0 - Language||GCC_FORCE_CPU_SUBTYPE_ALLŌ� �¤�Ĩ €,_3Apple LLVM compiler 3.0 - Language||GCC_SHORT_ENUMSŌ� �¤�Ĩ „€,_FApple LLVM compiler 3.0 - Language||GCC_USE_STANDARD_INCLUDE_SEARCHINGŌ� �¤�Ĩ ‡€,_ZApple LLVM compiler 3.0 - Preprocessing||GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPSŌ� �¤�Ĩ Š€,_DApple LLVM compiler 3.0 - Warnings||GCC_WARN_CHECK_SWITCH_STATEMENTSŌ� �¤�Ĩ €,_EApple LLVM compiler 3.0 - Warnings||GCC_WARN_FOUR_CHARACTER_CONSTANTSŌ� �¤�Ĩ €,_3Apple LLVM compiler 3.0 - Warnings||GCC_WARN_SHADOWŌ� �¤�Ĩ “€,_NApple LLVM compiler 3.0 - Warnings||CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIESŌ� �¤�Ĩ –€,_DApple LLVM compiler 3.0 - Warnings||GCC_WARN_64_TO_32_BIT_CONVERSIONŌ� �¤�Ĩ ™€,_GApple LLVM compiler 3.0 - Warnings||CLANG_WARN_IMPLICIT_SIGN_CONVERSIONŌ� �¤�Ĩ œ€,_FApple LLVM compiler 3.0 - Warnings||GCC_WARN_ALLOW_INCOMPLETE_PROTOCOLŌ� �¤�Ĩ Ÿ€,_AApple LLVM compiler 3.0 - Warnings||GCC_WARN_INHIBIT_ALL_WARNINGSŌ� �¤�Ĩ ĸ€,_LApple LLVM compiler 3.0 - Warnings||GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETEDŌ� �¤�Ĩ Ĩ€,_>Apple LLVM compiler 3.0 - Warnings||GCC_WARN_ABOUT_RETURN_TYPEŌ� �¤�Ĩ ¨€,_@Apple LLVM compiler 3.0 - Warnings||GCC_WARN_MISSING_PARENTHESESŌ� �¤�Ĩ Ģ€,_MApple LLVM compiler 3.0 - Warnings||GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERSŌ� �¤�Ĩ Ž€,_EApple LLVM compiler 3.0 - Warnings||GCC_WARN_ABOUT_MISSING_PROTOTYPESŌ� �¤�Ĩ ą€,_BApple LLVM compiler 3.0 - Warnings||GCC_WARN_ABOUT_MISSING_NEWLINEŌ� �¤�Ĩ ´€,_SApple LLVM compiler 3.0 - Warnings||GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTORŌ� �¤�Ĩ ˇ€,_CApple LLVM compiler 3.0 - Warnings||GCC_WARN_NON_VIRTUAL_DESTRUCTORŌ� �¤�Ĩ ē€,_=Apple LLVM compiler 3.0 - Warnings||CLANG_WARN_OBJCPP_ARC_ABIŌ� �¤�Ĩ Ŋ€,_2Apple LLVM compiler 3.0 - Warnings||WARNING_CFLAGSŌ� �¤�Ĩ Ā€,_EApple LLVM compiler 3.0 - Warnings||GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONSŌ� �¤�Ĩ À,_NApple LLVM compiler 3.0 - Warnings||CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONSŌ� �¤�Ĩ ƀ,_5Apple LLVM compiler 3.0 - Warnings||GCC_WARN_PEDANTICŌ� �¤�Ĩ ɀ,_EApple LLVM compiler 3.0 - Warnings||GCC_WARN_ABOUT_POINTER_SIGNEDNESSŌ� �¤�Ĩ Ė€,_9Apple LLVM compiler 3.0 - Warnings||GCC_WARN_SIGN_COMPAREŌ� �¤�Ĩ Ī€,_BApple LLVM compiler 3.0 - Warnings||GCC_WARN_STRICT_SELECTOR_MATCHŌ� �¤�Ĩ Ō€,_MApple LLVM compiler 3.0 - Warnings||CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSIONŌ� �¤�Ĩ Հ,_ZApple LLVM compiler 3.0 - Warnings||GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORSŌ� �¤�Ĩ ؀,_VApple LLVM compiler 3.0 - Warnings||GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORSŌ� �¤�Ĩ ۀ,_@Apple LLVM compiler 3.0 - Warnings||GCC_TREAT_WARNINGS_AS_ERRORSŌ� �¤�Ĩ Ū€,_FApple LLVM compiler 3.0 - Warnings||GCC_WARN_TYPECHECK_CALLS_TO_PRINTFŌ� �¤�Ĩ á€,_@Apple LLVM compiler 3.0 - Warnings||GCC_WARN_UNDECLARED_SELECTORŌ� �¤�Ĩ ä€,_@Apple LLVM compiler 3.0 - Warnings||GCC_WARN_UNINITIALIZED_AUTOSŌ� �¤�Ĩ į€,_<Apple LLVM compiler 3.0 - Warnings||GCC_WARN_UNKNOWN_PRAGMASŌ� �¤�Ĩ ę€,_<Apple LLVM compiler 3.0 - Warnings||GCC_WARN_UNUSED_FUNCTIONŌ� �¤�Ĩ í€,_9Apple LLVM compiler 3.0 - Warnings||GCC_WARN_UNUSED_LABELŌ� �¤�Ĩ đ€,_=Apple LLVM compiler 3.0 - Warnings||GCC_WARN_UNUSED_PARAMETERŌ� �¤�Ĩ ķ€,_9Apple LLVM compiler 3.0 - Warnings||GCC_WARN_UNUSED_VALUEŌ� �¤�Ĩ ö€,_<Apple LLVM compiler 3.0 - Warnings||GCC_WARN_UNUSED_VARIABLEŌ� �¤�Ĩ ų€,_?Apple LLVM compiler 3.0 - Warnings||CLANG_WARN_CXX0X_EXTENSIONSŌ� �¤�Ĩ ü€,_GApple LLVM compiler 3.0 - Warnings||GCC_WARN_ABOUT_DEPRECATED_FUNCTIONSŌ� �¤�Ĩ ˙€,_IApple LLVM compiler 3.0 - Warnings||GCC_WARN_ABOUT_INVALID_OFFSETOF_MACROŌ� �¤�Ĩ €,_:Interface Builder XIB Compiler - Options||IBC_FLATTEN_NIBSŌ� �¤�Ĩ €,_9Interface Builder XIB Compiler - Options||IBC_OTHER_FLAGSŌ� �¤�Ĩ €,_SInterface Builder XIB Compiler - Options||IBC_OVERRIDING_PLUGINS_AND_FRAMEWORKS_DIRŌ� �¤�Ĩ €,_AInterface Builder XIB Compiler - Options||IBC_PLUGIN_SEARCH_PATHSŌ� �¤�Ĩ €,_5Interface Builder XIB Compiler - Options||IBC_PLUGINSŌ� �¤�Ĩ €,_4Interface Builder XIB Compiler - Options||IBC_ERRORSŌ� �¤�Ĩ €,_5Interface Builder XIB Compiler - Options||IBC_NOTICESŌ� �¤�Ĩ €,_6Interface Builder XIB Compiler - Options||IBC_WARNINGSŌ� �¤�Ĩ €,_>Static Analyzer - Checkers||CLANG_ANALYZER_DEADCODE_DEADSTORESŌ� �¤�Ĩ €,_IStatic Analyzer - Checkers||CLANG_ANALYZER_DEADCODE_IDEMPOTENT_OPERATIONSŌ� �¤�Ĩ €,_9Static Analyzer - Checkers||CLANG_ANALYZER_OBJC_SELF_INITŌ� �¤�Ĩ #€,_,User-Defined||CLANG_WARN_CONSTANT_CONVERSIONŌ� �¤�Ĩ &€,_#User-Defined||CLANG_WARN_EMPTY_BODYŌ� �¤�Ĩ )€,_(User-Defined||CLANG_WARN_ENUM_CONVERSIONŌ� �¤�Ĩ ,€,_'User-Defined||CLANG_WARN_INT_CONVERSIONŌ� �¤�Ĩ /€,_0User-Defined||CLANG_WARN__DUPLICATE_METHOD_MATCHŌ� � , 2€]Ą 3¸Ō� �¤�Ĩ 6€,_Architectures||ARCHSĶ� � � � 9 ;€^Ą :ēĄ <ŧĶ� Ūßā ?�oNģ€�Ō� �¤�Ĩ C€,_Qfile://localhost/Users/administrator/Documents/Rorden/dcm2/dcm2/nii_dicom_batch.cĶ� � � � F K€^¤ G H I JŊžŋ¤ L M�$ OÁmÃ_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#AšÃĀfˆ]{25214, 2597}Z{26503, 0}Ķ� � � � Y [€^Ą ZÅĄ \Æ]IDENameStringTdcm2Ķ� � � � a d€^ĸ b cȁÉĸ e fʁË_IDEDeviceLocation_IDEDeviceArchitecture_"dvtdevice-local-computer:localhostVx86_64Ķ� � � � m q€^Ŗ n o p́΁ĪŖ r s tЁę×_0IDEActivityReportCompletionSummaryStringSegments_IDEActivityReportOptions_IDEActivityReportTitleŌ� � , z€]¤ { | } ~ҁ؁܁áĶ� � � �  …€^Ŗ ‚ ƒ „ԁ́ÔŖ † ‡ tՁց×_&IDEActivityReportStringSegmentPriority_+IDEActivityReportStringSegmentBackSeparator_)IDEActivityReportStringSegmentStringValue#@�������Q WArchiveĶ� � � � ‘ •€^Ŗ ‚ ƒ „ԁ́ÔŖ – — ˜؁ځÛ#@������R: Tdcm2Ķ� � � � ž ĸ€^Ŗ ‚ ƒ „ԁ́ÔŖ Ŗ ¤ ĨŨہß#?đ������c� %� Ō� Š Ē ĢWNS.dataāO\bplist00Ô;<X$versionX$objectsY$archiverT$top�† ­$%+147U$nullĶ \NSAttributesV$classXNSString€€ €YSucceededĶ WNS.keysZNS.objects€ ĸ€€ĸ€€ VNSFontWNSColorÔ  !"#VNSNameVNSSizeXNSfFlags€€#@&������ _LucidaGrande-BoldŌ&'()Z$classnameX$classesVNSFontĸ(*XNSObjectĶ ,-./0\NSColorSpaceWNSWhite€ B0�Ō&'23WNSColorĸ2*Ō&'56\NSDictionaryĸ5*Ō&'89_NSAttributedStringĸ:*_NSAttributedString_NSKeyedArchiverŅ=>Troot€����#�-�2�7�E�K�R�_�f�o�q�s�u��†�Ž�™�›�ž� �ĸ�Ĩ�§�Š�°�¸�Á�Č�Ī�Ø�Ú�Ü�å�č�ü (/<DFHKPX[`mpuŠĸ´ˇŧ�������������?��������������žŌ�“�” ­ Ž]NSMutableDataŖ ­ ¯�˜VNSDataĶ� � � � ˛ š€^Ļ ‚ ´ ĩ „ ˇ ¸ԁâãԁäåĻ ē} ŧ Ŋ}}æGįéGG_"IDEActivityReportStringSegmentType_"IDEActivityReportStringSegmentDate_'IDEActivityReportStringSegmentDateStyle_'IDEActivityReportStringSegmentTimeStyle#@������Ō Æ� Į ČWNS.time#AšÃÉũšˇčŌ�“�” Ę ËVNSDateĸ Ę�˜_Today at 11:35 AMęŌ� � , Ѐ]Ą�€Ō� � , Ԁ]Ŗ Õ Ö ×íîđĶ� ŪßāX�oN€b€�Ķ� Ūßā Ū�oNī€�_Qfile://localhost/Users/administrator/Documents/Rorden/dcm2/dcm2/nii_dicom_batch.cĶ� Ūßā ã�oNņ€�_Ufile://localhost/Users/administrator/Documents/Rorden/dcm2/dcm2/en.lproj/MainMenu.xib_NSKeyedArchiverŅ č éUState€���"�+�5�:�?'-:AITV[]_dfi§´ļĮÉËÍĪŅĶÕ×čęėīōôöųü LpŒģŨų0=?PRTVXZ\^`qsvy|‚…ˆ–ŠÂō  4 G S ` b s u w y { }   ƒ ” – ˜ › ž Ą ¤ § Ē ĩ Î Ü į ũ 4 G X s  ĸ ¤ Ļ ¨ Ē Ã æ ō ų         / B Q d € ‚ „ † ˆ Š ŧ Å Į Ę Ė Õ ā â ä é ō ũ  % * I R [ c h q ~ ˆ ” – ˜ š Ŗ ­ ¯ ü    ' 0 F K a j • š Å Ū ā â ä æ ī ņ ô ö ˙).Xa‡Œ˛ŋÁÄÆÉËķ�   ')+-469<h€˜ĄŖĻ¨ĩˇÆČĘĖÎĐŌÔãåįéėīōõ)=Ywƒ˛´ļ¸ēŧÅĮĘĖÕ×ŲŪįôöøú )+-/14dží9FHKMPRfoqvxz‡‰Ž’—™›ŠŧŊÆĪÜáîđõ÷ųū� #*3IPenpsu†’•—™æīüū   'LQmvx{}ŠŒ™›ŸĄŖĨ˛´ˇēŊĀÃé,Rr— ĸGIKMOQSUWY[]_acegikmoqsuwy{}ƒ…‡‰‹‘“•—™›ŸĄŖĨ§ŠĢ­¯ąŗĩˇšģŊŋÁÃÅĮÉËÍĪŅĶÕ×ŲÛŨßáãåįéëíīņķõ÷ųûũ˙   !#%')+-/13579;=?ACEGIKMOQSUWY[]_adgjmpsvy|‚…ˆ‹Ž‘”—š ŖĻŠŦ¯˛ĩ¸ģžÁÄĮĘÍĐĶÖŲÜßâåčëîņô÷úũ�  !$'*-68Zce}†ˆĸĢ­ĶÜŪü#,.JSU}†ˆŠ˛´ŌÛŨ:CEˆ‘“ŋČĘ˙ -68`ik˜šÉŌÔû )+JSUt}Ļ¨ĶÜŪ  09;dmoŒ•—´Ŋŋāéë  )24\eg˜šļŋÁčņķ    7 @ B ` i k Š “ • ŗ ŧ ž ß č ę!! !!7!@!B!d!m!o!!˜!š!Ä!Í!Ī!å!î!đ""("*"L"U"W"y"‚"„""Ļ"¨"Ņ"Ú"Ü"ô"ũ"˙##$#&#=#F#H#b#k#m#•#ž# #š#Â#Ä#å#î#đ$$$$@$I$K$e$n$p$Œ$•$—$ģ$Ä$Æ$â$ë$í%%%%A%J%L%o%x%z%š%Ŗ%Ĩ%Ķ%Ü%Ū&& & &-&6&8&U&^&`&‘&š&œ&Â&Ë&Í&ū'' '/'8':']'f'h'‘'š'œ'Æ'Ī'Ņ'ė'õ'÷(('()(R([(](ˆ(‘(“(ŗ(ŧ(ž(č(ņ(ķ))$)&)K)T)V)|)…)‡)Š)˛)´)ķ)ü)ū*=*F*H*r*{*}*Ÿ*¨*Ē*Ė*Õ*×*ō*û*ũ++ +"+I+R+T+u+~+€+¨+ą+ŗ+Ö+ß+á,, ,,0,9,;,_,h,j,Ž,ˇ,š-- - -U-^-`-Š-˛-´. ...W.`.b.Ž.ˇ.š.ų///R/[/]/Š/˛/´00 0 0X0a0c0™0ĸ0¤0ã0ė0î10191;1v111Å1Î1Đ22!2#2a2j2l2Ŧ2ĩ2ˇ2ô2ũ2˙3=3F3H3Š3“3•3Í3Ö3Ø4444T4]4_4Ÿ4¨4Ē4ä4í4ī555>5@55Š5Œ5Į5Đ5Ō6 6)6+6q6z6|6Â6Ë6Í77$7&7b7k7m7Ļ7¯7ą7ä7í7ī8*83858x88ƒ8ģ8Ä8Æ9 999T9]9_9Ÿ9¨9Ē9ā9é9ë:4:=:?:œ:Ĩ:§:î:÷:ų;A;J;L;‚;‹;;Ū;į;é<0<9<;<…<Ž<<Ų<â<ä=(=1=3=‚=‹==Î=×=Ų>>%>'>w>€>‚>Ę>Ķ>Õ??#?%?{?„?†?Ė?Õ?×@@ @"@W@`@b@Ē@ŗ@ĩAAAAIARATAœAĨA§AãAėAîB3B<B>BŽB—B™BöB˙CCZCcCeC¨CąCŗCüDDDJDSDUD˜DĄDŖDâDëDíE,E5E7EsE|E~EžEĮEÉFFFFOFXFZFœFĨF§FņFúFüGHGQGSGG™G›G×GāGâH8HAHCH‡HH’HĘHĶHÕI IIIOIXIZI“IœIžIßIčIęJ6J?JAJ}J†JˆJˇJĀJÂJčJņJķKK'K)KSK\K^K‘KšKœKŸKĸKĢK­KÅKÎKîKõLL+L8L:L;L<LAL{LˆL”LœLŸLĸL¤LņLúM�MM MMMMM3M4MAMCMNMQMTMWMZM]MhMkMnMqMtMwM‰M“M›MĻMŋMĖMÎMŅMÔM×MÚM÷NNN N NNN&N3N5N8N:N=N@NINKNPNSNVNcNeNjNlNnNsNvNyN‡NNNŸN¤NĻN¨N­N°NŗNŋNČNÕN×NØNŲNæNčNëNíNđNķNüNūOOO OOOOO!O&O)O,O6O?OLONOSOUOWO\O_ObOtO}O~O€OOO’O”O—OšOŖOĨOĒO­O°OŊOŋOÄOÆOČOÍOĪOŌOÛOčOęOīOņOķOøOúOũPPPPPP"P%P,P/P2P5PUPiPŒP™P›PŽPąP´PˇPēPŊPĀPÃPÆPÉPÜPßPâPåPčPëPîPņPôP÷QQ!Q5QIQXQpQ„Q QĩQĖQÕQØQŲQâQīQöQüRRR RRRRR!R"R+R.R/RRR_RaRpRsRvRyR|RR‚R…R”R—RšRR RŖRĻRŠR¸RŨS S/SNScSySS™SœSSĻSŦSąSˇSĀSÂSÅSČSŅSĶSÖSŲSŪSįSęSīSōSõSūT�TTTTTTTT!T.T0T3T5T8T;TDTFTMTPTSTVTcTeTjTlTnTsTvTyTŒT•TĸT¤TŠTĢT­T˛TĩT¸TÆTĪTÜTŪTãTåTįTėTīTōUUUU&U(U+U-U:U<UQUTUWUZU]U`UcUfUiUlUoU„U‡UŠUUU“U–U™UœUŸUĸUšUÖUđUũVVGVYVtVŽVĨV˛V´VģVžVÁVÄVËVÎVŅVÔW WCWlWyW{W~WW„W‡W”W—WšWœWĨW§W˙X XXXXX X#X,X/X2X5X8XTXvXŠXŖXŦXŽXąX´X¸XÅXĮXÎXŅXÔX×XŪXáXäXįYYY4YAYCYDYEYRYTYUYVY_YlYnYyY|YY‚Y…YˆY“Y–Y™YœYŸYĸY­YšYāYúZ!Z*Z7ZDZFZGZHZQZSZXZ[Z^ZaZeZrZtZuZvZƒZ…ZˆZ‹ZŽZ‘ZžZĄZŖZĨZ˛Z´ZŋZÁZÃZÅZĮZÉZÔZ×ZŲZÜZßZâZū[[[[![$[5[8[;[>[@[I[V[X[_[a[c[f[m[p[s[v[›[ [ŧ[Å[Į[Ę[Í[Ú[Ü[é[ë[í[ī[ņ[ķ[õ\\\\ \\\\\]Ä]Į]Ę]Í]Đ]Ķ]Ö]Ų]Ü]ß]â]å]č]ë]î]ņ]ô]÷]ú]ũ^�^^^ ^ ^^^^^^^!^$^'^*^-^0^3^6^9^<^?^B^E^H^K^N^Q^T^W^Z^]^`^c^f^i^l^o^r^u^x^{^~^^„^‡^Š^^^“^–^™^œ^Ÿ^ĸ^Ĩ^¨^Ģ^Ž^ą^´^ˇ^ē^Ŋ^Ā^Ã^Æ^É^Ė^Ī^Ō^Õ^Ø^Û^Ū^á^ä^į^ę^í^đ^ķ^ö^ų^ü^˙____ _______ _#_&_)_,_/_2_5_8_;_>_A_D_G_J_M_P_S_V_Y_\___b_e_h_k_n_q_t_w_z_}_€_ƒ_†_‰_Œ__’_•_˜_›_ž_Ą_¤_§_Ē_­_°_ŗ_ļ_š_ŧ_ŋ_Â_Å_Č_Ë_Î_Ņ_Ô_×_Ú_Ũ_ā_ã_æ_é_ė_ī_ō_õ_ø_û_ū```` ` ```````"`%`(`+`.`1`4`7`@`B`d`m`o`‡``’`Ŧ`ĩ`ˇ`Ũ`æ`čaaaa-a6a8aTa]a_a‡aa’aŗaŧažaÜaåaįbbbbDbMbOb’b›bbÉbŌbÔc ccc7c@cBcjcscuc™cĸc¤cĶcÜcŪdddd*d3d5dTd]d_d~d‡d‰d§d°d˛dŨdædče eee:eCeEeneweye–eŸeĄežeĮeÉeęeķeõf fff3f<f>fffofqf™fĸf¤fĀfÉfËfōfûfũgg!g#gAgJgLgjgsgug”ggŸgŊgÆgČgégōgôhhhhAhJhLhnhwhyh™hĸh¤hÎh×hŲhīhøhúi)i2i4iVi_iaiƒiŒiŽi§i°i˛iÛiäiæiūjj j%j.j0jGjPjRjljujwjŸj¨jĒjÃjĖjÎjījøjúkk'k)kJkSkUkokxkzk–kŸkĄkÅkÎkĐkėkõk÷ll!l#lKlTlVlyl‚l„l¤l­l¯lŨlælčm mmm7m@mBm_mhmjm›m¤mĻmĖmÕm×nnnn9nBnDngnpnrn›n¤nĻnĐnŲnÛnön˙oo(o1o3o\oeogo’o›ooŊoÆoČoōoûoũp%p.p0pUp^p`p†pp‘pŗpŧpžpũqqqGqPqRq|q…q‡qŠq˛q´qÖqßqáqürrr!r*r,rSr\r^rrˆrŠr˛rģrŊrārérëssss:sCsEsisrsts¸sÁsÃt ttt_thtjtŗtŧtžuuu uaujulu¸uÁuÃvv vv\vevgvŗvŧvžw wwwbwkwmwŖwŦwŽwíwöwøx:xCxEx€x‰x‹xĪxØxÚy"y+y-ykytyvyļyŋyÁyūzz zGzPzRz”zzŸz×zāzâ{{#{%{^{g{i{Š{˛{´{î{÷{ų|?|H|J|‹|”|–|Ņ|Ú|Ü}*}3}5}{}„}†}Ė}Õ}×~%~.~0~l~u~w~°~š~ģ~î~÷~ų4=?‚‹ÅÎЀ€€€^€g€i€Š€˛€´€ę€ķ€õ>GIĻ¯ąø‚‚‚K‚T‚V‚Œ‚•‚—‚č‚ņ‚ķƒ:ƒCƒEƒƒ˜ƒšƒãƒėƒî„2„;„=„Œ„•„—„Ø„á„ã…&…/…1……Š…Œ…Ô…Ũ…߆$†-†/†…†Ž††Ö†ß†á‡!‡*‡,‡a‡j‡l‡´‡Ŋ‡ŋˆˆˆˆSˆ\ˆ^ˆĻˆ¯ˆąˆíˆöˆø‰=‰F‰H‰˜‰Ą‰ŖŠ�Š Š ŠdŠmŠoОŠģŠŊ‹‹‹‹T‹]‹_‹ĸ‹Ģ‹­‹ė‹õ‹÷Œ6Œ?ŒAŒ}Œ†ŒˆŒČŒŅŒĶYbdĻ¯ąûŽŽŽRŽ[Ž]ŽšŽŖŽĨŽáŽęŽėBKM‘šœÔŨß!YbdĻ¨éōô‘@‘I‘K‘‡‘‘’‘Á‘Ƒˑō‘û‘ũ’(’1’3’]’f’h’›’¤’Ļ’Š’Ŧ’ĩ’ˇ’Ī’Ü’Ū’á’ä’į’ę’÷’ú’ũ’˙““ “^“k“m“v“y“|““‚“‹“Ž“‘“”“—“˛“Ų“į”””&”1”>”@”C”F”I”L”Z”_”l”n”s”v”y”~””„”˜”°”Քܔé”ë”ō”õ”ø”û•••• •>•Y•r•{•}•†•‰•Œ••’•Ÿ•Ą•¨•Ģ•Ž•ą•¸•ģ•ž•Á•ę––D–M–O–W–d–f–m–p–s–v–}–€–ƒ–†––’–—–¤–Ļ–­–°–ŗ–ļ–Ŋ–Ā–Ã–Æ–Ī–Ö–ß–į–ę™J™S™a™h™o™|™~™‹™Ž™‘™”™—™š™™Ē™­™°™ŗ™ļ™š™ŧ™ášš0šZšcšlštš}š€š‰šš•šŠšĢš´šļšššģšÄšÆšÍšĐšĶšÖšãšæščšęš÷šúšũš˙›S›`›c›f›h›Ā›Ō›×›Ũ������������ ę��������������›ß���������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/project.xcworkspace/xcuserdata/rorden.xcuserdatad/�������0000775�0000000�0000000�00000000000�13220512030�0031637�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������UserInterfaceState.xcuserstate����������������������������������������������������������������������0000664�0000000�0000000�00000327204�13220512030�0037624�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/project.xcworkspace/xcuserdata/rorden.xcuserdatad���������������������������������������������������������������������������bplist00Ô������ą˛X$versionX$objectsY$archiverT$top�† ¯‰������)�*�+�,�-�.�/�0�1�2�J�K�L�M�N�O�P�Q�R�S�T�U�V�n�o�p�q�r�s�t�u�v�w�x�€��‚�ƒ�‹�Œ��—�˜�™�š�ž�ĸ�Š�­�Ž�˛�ŗ�ĩ�ŋ�Ã�Õ�Ö�×�Ø�Ų�Ú�Û�Ü�Ũ�ë�ė�í�î�ī�đ�ņ�÷�ø�ũ  !",-./015EFGHIJKNQRTY_`aklpvw{|€„ˆ‰Š“—˜ž¤Ž¯°ąĩĮČÉĘËĖÍÎĪÛÜŨŪßãäîīđņõųú     !'(.2378<=ABCDHLMU_eimnosx}‚†Œ”ϧ¨ŠĒĢŦ­šēģŧŊžÁĮČÍÕÖ×ßāáįčîīôüũū˙�  Š#+,-567;AFNOWXfghijk}~€‚ƒ„…‹Œ’“›œĨĻŦ˛¸ŧØŲÚÛÜŨŪßāáâãäčėđõöûū 123456789GHIJKLMSTUY]cdjnoptxyz{‘’“”•–—˜™š›žĄ¤§Ē°ļžŋĀČÉĘŌĶÛÜŨŪßãũū˙� !'+,08<=ABHIMSTUZ`aejkltxy}~Š‹ŒŽ’œžŸŖ§ĩšēģÁÂÆĘÖרŲÚŪßéęëėđô ĖĐŅÕÖÚÛßāäåéęîīķôøųũū   !%&*+/0459:>?CDHIMNRSWX\]abfgklpquvz{€„…‰ŠŽ“”˜™žĸŖ§¨Ŧ­ą˛ļˇģŧĀÁÅÆĘËĪĐÔÕŲÚŪßãäčéíîōķ÷øüũ   $%)*./3489=>BCGHLMQRVW[\`aefjkoptuyz~ƒ„ˆ‰Žš›œžŸ Ą­Ž¯°ą˛ŗ´ĀÁÂÃÄÅÆĮĶÔÕÖרŲåæįčéęëėøųúûüũū        ! " # $ 0 1 2 3 4 5 6 7 C D E F G H I U V W c d e f g h i j v w x „ … † ‡ ˆ ‰ Š ‹ — ˜ ™ Ĩ Ļ § ¨ Š Ē Ģ ˇ ¸ š Å Æ Į Ķ Ô Õ Ö × Ø Ų Ú æ į č ô õ ö         ! " # $ % 1 2 3 4 @ A B C D E F G S T U V W X Y Z f g h i j k l x y z { | } ~  ‹ Œ  Ž š › œ ¨ Š Ē ļ ˇ ¸ š ē ģ ŧ Ŋ É Ę Ë Ė Í Î Ī Û Ü Ũ é ę ë ÷ ø ų        % & ' 3 4 5 A B C D E F G H T U V W c d e f r s t u  ‚ ƒ   ‘ ’ ž Ÿ   Ŧ ­ Ž ¯ ° ą ˛ ŗ ŋ Ā Á Â Î Ī Đ Ņ Ũ Ū ß ā á â ã ī đ ņ ō ū ˙ �       ) * + , 8 9 : ; G H I J K L M N Z [ \ ] i j k l x y z { | } ~  ‹ Œ  ™ š › § ¨ Š ĩ ļ ˇ Ã Ä Å Æ Į Č É Ę Ö × Ø Ų å æ į č ô õ ö ÷        ! " # / 0 1 = > ? K L M N O P Q R ^ _ ` l m n o { | } ~ Š ‹ Œ  ™ š › œ ¨ Š Ē ļ ˇ ¸ Ä Å Æ Ō Ķ Ô ā á â î ī đ ü ũ ū ˙    &*+9:;<=>BLMNOSWcdefghlpz~ƒ„’“”•–—˜žŸ¤Ŧ­ĩļˇģĀÁÂĖÍÎĪĐÔäåæįčéęÄĮĘÍĐĶÖŲÜßâåčëîņô÷úũ�  !$'*-0369<?BEHKNQTWZ]`cfilorux{~„‡Š“–™œŸĸĨ¨ĢŽą´ˇēŊĀÃÆÉĖĪŌÕØÛŪáäįęíđķöųü˙  #&),/258;>ADGJMPSVY\_behknqtwz}€ƒ†‰Œ’•˜›žĄ¤§Ē­°ŗļšŧŋÂÅČËÎŅÔ×ÚŨāãæéėīōõøûū  "%(+.147:=@CFIMPTbcdhmnxyz{’•Ŗ¤Ĩϧ¨Š¯°ĩŊžŋĮČÉÍŌĶÔŪßāáåõö÷øųúûÖŲÜßâåčëîņô÷úũ�  !$'*-0369<?BEHKNQTWZ]`cfilorux{~„‡Š“–™œŸĸĨ¨ĢŽą´ˇēŊĀÃÆÉĖĪŌÕØÛŪáäįęíđķöųü˙  #&),/258;>ADGJMPSVY\_behknqtwz}€ƒ†‰Œ’•˜›žĄ¤§Ē­°ŗļšŧŋÂÅČËÎŅÔ×ÚŨāãæéėīōõøûū  "%(+.147:=@CFILORUX[^beiЭޞŗˇ¸ŧŊÁÂÆĮËĖĐŅÕÖÚÛßāäåéęîīķôøųũū   !%&*+/0459:>?EFJRW[`fgkqtz{…ˆŽ“™ĄĨ̝ĩˇŊžÂČÉĖĪĶŲÚŪäæėđöøū $(.17;ACIJNTV\]agiosyz}ƒ‡•™Ÿ ĸ¨Ŧ˛ĩģŧĀÆÉĪĐÔÚÜāæęđōøü  #$*.46<@FHNOSY[opqrstuvwxyz{|‚ƒ„Ž‘™Ŗ¤Ĩϧ¨˛ŗ´ĩŋĀÁËĖĪßāáâãįęëėđ  #$()-.6789ABCKSTU]^fnopx€Ž’“—˜œĄĸϧĢŦ°U$nullĶ� � � � ��WNS.keysZNS.objectsV$classĸ� �€€ĸ��€~€8_$1BF655CB-E54D-46BB-910C-9630991A2D9B_IDEWorkspaceDocumentĶ� � � ���(¨��������€€€€€ € € € ¨� �!�"�#� ��&�#€ €}€Ų€€€€Ų€3^IDEWindowFrame_>IDEWorkspaceTabController_147C5EE4-A3E8-4DD1-B7B9-0ED6666BD470_!IDEOrderedWorkspaceTabControllers_IDEWindowInFullscreenMode_,IDEWorkspaceWindowControllerUniqueIdentifier_IDEActiveWorkspaceTabController_IDEWindowToolbarIsVisible_IDEWindowTabBarIsVisible_{{52, 199}, {1386, 647}}Ķ� � � �3�>�(Ē�4�5�6�7�8�9�:�;�<�=€€€€€€€€€€Ē�?�&�A�B�C�D�E�#�G�H€€€€˙nx€Ų{|€3[IDETabLabel_IDEShowNavigator]IDEEditorArea_-IDEWorkspaceTabControllerUtilityAreaSplitView_IDENavigatorArea_,IDEWorkspaceTabControllerDesignAreaSplitView\ViewDebugger_IDEShowUtilities^IDETabFilePath_AssistantEditorsLayout^dcm2.xcodeproj Ķ� � � �W�b�(Ē�X�Y�Z�[�\�]�^�_�`�a€€€€€ €!€"€#€$€%Ē�c�d�e�&�g�h�i�j�k�#€&€€Ŋ€€k€5€Ė€ö€ū€Ų€3_IDEEditorMode_Genius_IDEEditorMode_StandardZlayoutTree]IDEShowEditorZEditorMode_VersionEditorSubmode_IDEDefaultDebugArea_DebuggerSplitView_ DefaultPersistentRepresentations_ShowDebuggerAreaĶ� � � �y�|�(ĸ�z�{€'€(ĸ�}�~€)€*€3]SplitPosition_%EditorLayout_PersistentRepresentation"?7|ŲĶ� � � �„�‡�(ĸ�…�†€+€,ĸ�ˆ�‰€-€9€3YAlternateTMainĶ� � � �Ž�’�Ŗ���‘€.€/€0Ŗ�“�h�•€1€5€6€8_)EditorLayout_StateSavingStateDictionaries_EditorLayout_Selected_EditorLayout_GeometryŌ� � �›�Ą�œ€2€4Ķ� � � �Ÿ� �(  €3Ō�Ŗ�¤�Ĩ�ĻZ$classnameX$classes_NSMutableDictionaryŖ�Ĩ�§�¨\NSDictionaryXNSObjectŌ�Ŗ�¤�Ē�Ģ^NSMutableArrayŖ�Ē�Ŧ�¨WNSArray�Ō� � �¯�Ą�°€7€4_{{0, 0}, {1126, 171.5}}Ō�Ŗ�¤�§�´ĸ�§�¨Ķ� � � �ļ�ē�Ŗ���‘€.€/€0Ŗ�ģ�h�Ŋ€:€5€€8Ō� � �Ā�ÂĄ�Á€;€lĶ� � � �Ä�Ė�(§�Å�Æ�Į�Č�É�Ę�ˀ<€=€>€?€@€A€B§�Í�Î�Ī�Đ�Đ�Ō�Ķ€C€D€q€{€{€|€}€3\FileDataType[EditorState_ArchivableRepresentation_NavigableItemName_DocumentNavigableItemName_DocumentExtensionIdentifier[DocumentURL_com.apple.xcode.projectĶ� � � �Ū�ä�Ĩ�ß�ā�á�â�ã€E€F€G€H€IĨ�å�æ�į�č�é€J€K€V€W€n€8_-Xcode3ProjectEditorPreviousProjectEditorClass_(Xcode3ProjectEditor.sourceList.splitview_,Xcode3ProjectEditorPreviousTargetEditorClass_,Xcode3ProjectEditorSelectedDocumentLocations_-Xcode3ProjectEditor_Xcode3BuildSettingsEditor_Xcode3BuildSettingsEditorĶ� � � �ō�ô�(Ą�ķ€LĄ�õ€M€3_DVTSplitViewItemsŌ� � �ų�ĸ�ú�û€N€S€4Ķ� � � �ū�ĸ�˙�€O€Pĸ€Q€R€8]DVTIdentifier_DVTViewMagnitude_!sourceListSplitViewItemIdentifier#@e@�����Ķ� � �   �ĸ�˙�€O€Pĸ€T€U€8P#@Ø�����_Xcode3TargetEditorŌ� � �ÂĄ€X€lÔ� YselectionYtimestamp[documentURL€[€Z€Y€m_Cfile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2.xcodeproj/#AŊ‰…ÕÔ ‘Ķ� � � #'�(Ŗ$%&€\€]€^Ŗ()*€_€`€a€3WProjectVEditor_"Xcode3BuildSettingsEditorLocationsTdcm2_Xcode3BuildSettingsEditorŌ� � 2�ÂĄ3€b€lĶ� � � 6=�(Ļ789:;<€c€d€e€f€g€hĻ>?�h�h�h�g€i€j€5€5€5€k€3_#Collapsed Build Property Categories_Selected Build Properties_$Xcode3BuildSettingsEditorDisplayMode_#Xcode3BuildPropertyValueDisplayMode_Xcode3BuildSettingsEditorMode_"Xcode3BuildPropertyNameDisplayModeŌ� � L� €4Ō� � O� €4Ō�Ŗ�¤�ŦSĸ�Ŧ�¨Ō�Ŗ�¤UV_Xcode3ProjectDocumentLocationŖWX�¨_Xcode3ProjectDocumentLocation_DVTDocumentLocationĶ� � � Z\�(Ą[€oĄ]€p€3_"Xcode3BuildSettingsEditorFilterKeyUdwarfÕbc� defghij_DocumentLocation^IdentifierPath_DomainIdentifier_IndexOfDocumentIdentifier€x€s€z€r€w_/Xcode.IDENavigableItemDomain.WorkspaceStructureŌ� � m�ÂĄn€t€lĶqr� s�huZIdentifierUIndex€u€5€vTdcm2Ō�Ŗ�¤xy_IDEArchivableStringIndexPairĸz�¨_IDEArchivableStringIndexPair˙˙˙˙˙˙˙Ķ� ~€Y€y€�Ō�Ŗ�¤‚_DVTDocumentLocationĸƒ�¨_DVTDocumentLocationŌ�Ŗ�¤…†_(IDENavigableItemArchivableRepresentationĸ‡�¨_(IDENavigableItemArchivableRepresentationTdcm2_7Xcode.Xcode3ProjectSupport.EditorDocument.Xcode3ProjectĶ‹� ŒŽWNS.base[NS.relative€�€~€YŌ�Ŗ�¤‘’UNSURLĸ‘�¨Ō� � ”�ÂĄ•€€€l_{{0, 0}, {1126, 609}}Ķ� � � ™›�(Ą�{€(Ąœ€‚€3Ķ� � � ŸĄ�(Ą�†€,Ąĸ€ƒ€3Ķ� � � ĨŠ�ŖĻ§¨€„€…€†ŖĒ�hŦ€‡€5€ģ€8_)EditorLayout_StateSavingStateDictionaries_EditorLayout_Selected_EditorLayout_GeometryŌ� � ˛�ÂĄŗ€ˆ€lĶ� � � ļž�(§ˇ¸šēģŧŊ€‰€Š€‹€Œ€€Ž€§ŋĀÁÂÃÄŀ€‘€­€¸€ą€š€ē€3\FileDataType[EditorState_ArchivableRepresentation_NavigableItemName_DocumentNavigableItemName_DocumentExtensionIdentifier[DocumentURL_%com.apple.InterfaceBuilder3.Cocoa.XIBĶ� � � ĐÕ�¤ŅŌĶԀ’€“€”€•¤Öר؀–€˜€™€Ÿ€8_SelectedMemberIdentifiers_IBHybridStructureViewController_SelectionProvider_IBCanvasViewControllerŌ� � ā�ÂĄဗ€lS537Ķ� � � åé�(ŖØį耙€š€›Ŗęëė€œ€€ž€3_IBOutlineViewController_IBDockViewController_LastKnownOutlineViewWidthĶ� � � ōķ�(  €3Ķ� � � ö÷�(  €3#@i������Ķ� � � û�(Ĩüũū˙�€ €Ą€ĸ€Ŗ€¤Ĩ€Ĩ€Ļ€§€Ē€Ŧ€3ZZoomFactor[CenterPoint_$MemberIDToLastKnownCanvasPositionMap_EditedTopLevelMemberIDs_$ObjectIDToLastKnownCanvasPositionMap#?đ������]{318, 163.25}Ķ� � � �(Ą€¨Ą€Š€3S371Y{2, -222}Ō� � �Âĸ€Ģ€¨€lR29Ķ� � � �(  €3Õbc� de"#h%&€ļ€¯€z€Ž€ĩ_/Xcode.IDENavigableItemDomain.WorkspaceStructureŌ� � )�ÂŖ*+,€°€˛€´€lĶqr� Ã�hu€ą€5€v\MainMenu.xibĶqr� 4�hu€ŗ€5€vTdcm2Ķqr� 4�hu€ŗ€5€v˙˙˙˙˙˙˙Ķ� >~€ˇ€y€�_Mfile://localhost/Users/rorden/Documents/cocoa/dcm2/dcm2/en.lproj/MainMenu.xibYText View_2Xcode.IDEKit.CocoaIntegration.EditorDocument.CocoaĶ‹� ŒŽ>€�€~€ˇŌ� � I�ÂĄJ€ŧ€l_{{0, 0}, {788, 408}}ÔNOP� QRST_rootLayoutTreeNode_geniusEditorContextNode_primaryEditorContextNode€Å€Į€ž€ËÖVW� XYZQ]Q^�­XchildrenVparent[contentType_ documentArchivableRepresentation[orientation€�€Å€Ę€ŋÕbc� de`ahid€Ä€Ā€z€r€ÃŌ� � f�ÂĄg€Á€lĶqr� j�hu€Â€5€vTdcm2˙˙˙˙˙˙˙Ķ� ~€Y€y€�ÖVW� XYZt]�­Q€Æ€�€Ę€�Ō� � y�ÂĸSR€ž€Į€lÖVW� XYZ~Q]�­�­€Č€Å€Ę€�Ō� � ƒ�ÂĄ„€É€lÖVW� XYZR]Š�­€�€Į€Ę€�Ō�Ŗ�¤Ž_'IDEWorkspaceTabControllerLayoutTreeNodeĸ�¨_'IDEWorkspaceTabControllerLayoutTreeNodeŌ�Ŗ�¤‘’_#IDEWorkspaceTabControllerLayoutTreeĸ“�¨_#IDEWorkspaceTabControllerLayoutTreeĶ� � � •�(§–—˜™š›œ€Í€Î€Ī€Đ€Ņ€Ō€Ķ§žŸ ĄĸŖ¤€Ô€Û€ä€æ€ņ€ķ€õ€3XLeftView_IDESplitViewDebugAreaWConsole_IDEDebugArea_SplitViewYVariablesYRightViewZLayoutModeĶ� � � Žŗ�(¤¯°ą˛€Õ€Ö€×€Ø¤�#�hļ�&€Ų€5€Ú€€3_VariablesViewShowsRawValues_VariablesViewSelectedScope_ VariablesViewViewSortDescriptors_VariablesViewShowsTypeŌ� � ŋ� €lĶ� � � ÂÄ�(ĄÃ€ÜĄŀŨ€3_DVTSplitViewItemsŌ� � É�ĸĘË€Ū€á€4Ķ� � � ÎŅ�ĸ�˙�€O€PĸṐ߀ā€8XLeftView#@s������Ķ� � � ØÛ�ĸ�˙�€O€PĸÜŨ€â€ã€8YRightView#@rp�����Ķ� � � âä�(Ąã€åĄ�h€5€3_ConsoleFilterModeĶ� � � éë�(Ąę€įĄė€č€3_DVTSplitViewItemsŌ� � đ�ĸņō€é€î€4Ķ� � � õø�ĸö÷€ę€ëĸųú€ė€í€8]DVTIdentifier_DVTViewMagnitude]VariablesView#@†ˆ�����Ķ� � � �ĸö÷€ę€ëĸ€ī€đ€8[ConsoleArea#@ƒĀ�����Ķ� � �   �(Ą €ōĄ�h€5€3_VariablesViewSelectedScopeĶ� � � �(Ą€ôĄ�h€5€3_ConsoleFilterModeĶ� � � �(Ą�ķ€LĄ€÷€3Ō� � �ĸ !€ø€û€4Ķ� � � $'�ĸ�˙�€O€Pĸ()€ų€ú€8YIDEEditor#@ˆ�����Ķ� � � .1�ĸ�˙�€O€Pĸ23€ü€ũ€8_IDEDebuggerArea#@Y������Ķ� � � 89�(  €3Ķ� � � <>�(Ąę€įĄ?�€3Ō� � B�ĸCD€4Ķ� � � GJ�ĸ�˙�€O€PĸL€T€8#@‚ �����Ķ� � � PS�ĸ�˙�€O€PĸU€T€8#@=������Ķ� � � Y_�(ĨZ[\]^  Ĩ`abcd !>]^€3_ Xcode.IDEKit.Navigator.Structure_ Xcode.IDEKit.Navigator.BatchFind_Xcode.IDEKit.Navigator.Debug_SelectedNavigator_Xcode.IDEKit.Navigator.IssuesĶ� � � lt�(§mnopqrs  §u�#�#x�#z{€Ų€Ų€Ų€3^IDEVisibleRect_"IDEUnsavedDocumentFilteringEnabled_IDESCMStatusFilteringEnabled_IDEExpandedItemsTree_!IDERecentDocumentFilteringEnabled_IDESelectedTree_,IDENavigatorExpandedItemsBeforeFilteringTree_{{0, 0}, {244, 552}}Ķ� � � †ˆ�Ą‡Ą‰€8_IDEValuesAsTreeĶ� � � �(ĄށĄ€3Tdcm2Ķ� � � ”—�(ĸ•–ĸ•™€3PTdcm2Ķ� � � žĄ�(ĸ• ĸ••€3TcoreĶ� � � §Š�Ą‡Ąǁ€8Ķ� � � ­¯�(ĄށĄ•€3Ķ� � � ŗĩ�Ą‡Ąļ €8Ķ� � � šē�(  €3Ķ� � � ŊĘ�(ŦžŋĀÁÂÃÄÅÆĮČɁ"#$%&'()*+,-ŦËĖÍ�#�#�h�h�h�&ÔÕ�h.23€Ų€Ų€5€5€5€:<€5€3_,IDEBatchFindNavigatorReplaceAttributedString_IDEBatchFindScope_)IDEBatchFindNavigatorFindAttributedString_!IDEBatchFindNavigatorShowsOptions_IDEBatchFindSearchFrameworks_IDEBatchFindNavigatorFindMode_IDEBatchFindMatchStyle_#IDEBatchFindNavigatorScrollPosition_IDEBatchFindIgnoreCase_'IDEBatchFindNavigatorSelectedRowIndexes_$IDEBatchFindNavigatorCollapsedGroups_IDEBatchFindFindTypeŌå� æįXNSString/1Ō� éęëYNS.string0PŌ�Ŗ�¤íî_NSMutableStringŖíī�¨XNSStringŌ�Ŗ�¤ņō_NSMutableAttributedStringŖķô�¨_NSMutableAttributedString_NSAttributedStringYworkspaceĶå� ÷øįú\NSAttributes415Ō� éęũ0QcĶ� � � ˙�Ą�6Ą7€8VNSFontÔ�     VNSSizeXNSfFlagsVNSName#@&������ 89_.AppleSystemUIFontŌ�Ŗ�¤VNSFontĸ�¨Ō� �­\NSRangeCount;Ō�Ŗ�¤ZNSIndexSetĸ�¨ZNSIndexSetŌ� �­=Ō�Ŗ�¤_NSMutableIndexSetŖ�¨_NSMutableIndexSetZNSIndexSetĶ� � �  (�(§!"#$%&'?@ABCDE§�h*+�#¤�h�&€5FG€Ų€õ€5€€3_DBGNavigatorContentMode^IDEVisibleRect_IDEDebugTransientStates_IDEShowOnlyInterestingContent_IDEStackCompressionValue_DBGThreadOrQueueMode_IDEShowOnlyRunningBlocks_{{0, 0}, {259, 604}}Ķ� � � :@�(Ĩ;<=>?HIJKLĨAB�&�&EMN€€R€3_'IDEExecutionEnvironmentAddressStringKey_IDEDebugExpandedItems_%IDEDebugAlreadyShownForNewPausedState_1IDEHaveInitiallyExpandedCPUDebuggingChildrenState_IDEDebugSelectedNavigableItems^0x7fc524fe4460Ō� � NRŖ4PQ€ŗOPQYdcm2 %% 6Ydcm2 %% 1Ō�Ŗ�¤VW\NSMutableSetŖVX�¨UNSSetŌ� � Z�Ą[S€4Õbc� de_hab€�U€zT\_1Xcode.IDENavigableItem.ExecutionEnvironmentDomainŌ� � e�ÂŖfghVYZ€lĶqr� kluWX€vXThread 1Ķqr� 4�hu€ŗ€5€vĶqr� u�hu[€5€v_<debug session for dcm2>˙˙˙˙˙˙˙_ Xcode.IDEKit.Navigator.StructureĶ� � � |†�(Š}~€‚ƒ„…_`abcdefgŠ�#ˆ‰Š�#Œ�#€Ųhij€Ųkl€Ųm€3_IDEErrorFilteringEnabled^IDEVisibleRect_IDECollapsedFiles_IDEExpandedIssues^IDEShowsByType_IDESelectedNavigables_IDECollapsedTypes_IDERecentFilteringEnabled_IDECollapsedGroups_{{0, 0}, {259, 523}}Ō� � œR QŌ� � ŸR QŌ� � ĸ� €4Ō� � ĨR QŌ� � ¨R QĶ� � � Ģ­�(Ąę€įĄށo€3Ō� � ą�Ŗ˛ŗ´psv€4Ķ� � � ˇē�ĸ�˙�€O€Pĸģŧqr€8_IDENavigatorArea#@p@�����Ķ� � � ÁÄ�ĸ�˙�€O€PĸÅƁtu€8]IDEEditorArea#@‘˜�����Ķ� � � ËÎ�ĸ�˙�€O€PĸĪŧwr€8_IDEUtilitiesAreaĶ� � � Ô×�(ĸÕցyzĸ�&�&€€€3_ShowsOnlyVisibleViewObjects_ShowsOnlyInterestingViewObjects_;/Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2.xcodeprojŌ� � ā�ÂĄ�€€lĶ� � � äđ�(Ģåæįčéęëėíî€‚ƒ„…†‡ˆ‰Ģ�&ō�hôõö÷øų�#�#€Š€5&47UVz€Ų€Ų€3_BreakpointsActivated_DefaultEditorStatesForURLs_DebuggingWindowBehavior_ActiveRunDestination\ActiveScheme_0LastCompletedPersistentSchemeBasedActivityReport_DocumentWindows_DefaultEditorFrameSizeForURLs_RecentEditorDocumentURLs_AppFocusInMiniDebugging_MiniDebuggingConsoleĶ� � �  �(§    ‹Œށ‘§’–¨ہD\f€3_/Xcode.IDEKit.EditorDocument.GaugeEnergyDocument_IDEQuickLookEditor.Editor_2Xcode.IDEKit.CocoaIntegration.EditorDocument.Cocoa_&Xcode.IDEKit.EditorDocument.SourceCode_0Xcode.IDEKit.InterfaceBuilder.EditorDocument.XIB_7Xcode.Xcode3ProjectSupport.EditorDocument.Xcode3Project_'Xcode.IDEKit.EditorDocument.LogDocumentĶ� � � "$�(Ą#“Ą%•€3Ķ‹� ŒŽ*€�€~”_:x-xcode-perf-energy://report?launchSessionRef=7f8bc7a115a0Ķ� � � -.�(  €3Ķ� � � 14�(ĸ23—™ĸ56›ĸ€3Ķ‹� ŒŽ;€�€~˜_ file:///usr/lib/libz.1.2.5.dylibĶ‹� ŒŽ@€�€~š_9file:///Users/rorden/Documents/cocoa/dcm2/dcm2niigui.icnsĶ� � � CE�(ĄDœĄF€3_SelectedDocumentLocationsŌ� � J�ÂĄKž€lÔN� �hPQR_IDEQuickLookPageNumber€5 ŸĄ_)file://localhost/usr/lib/libz.1.2.5.dylib#AšfúaëĮ´Ō�Ŗ�¤VW_IDEQuickLookDocumentLocationŖXY�¨_IDEQuickLookDocumentLocation_DVTDocumentLocationĶ� � � []�(Ą\ŖĄ^¤€3_SelectedDocumentLocationsŌ� � b�ÂĄcĨ€lÔN� �hghR€5§ρĄ_Bfile://localhost/Users/rorden/Documents/cocoa/dcm2/dcm2niigui.icns#AšŽ*fĄĶ� � � mp�(ĸnoЁĢĸqr­Ā3Ķ‹� ŒŽw€�€~Ē_Jfile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/en.lproj/MainMenu.xibĶ‹� ŒŽ|€�€~Ŧ_Dfile:///Users/rorden/Documents/cocoa/dcm2/dcm2/en.lproj/MainMenu.xibĶ� � � „�(¤€‚ƒށ¯°ą¤…†ƒˆ˛ŗąš€3_SelectedMemberIdentifiers_IBHybridStructureViewController_SelectionProvider_IBCanvasViewControllerŌ� � �ÂĄ€¨€lĶ� � � “—�(Ŗ”•–´ĩļŖ˜™ėˇ¸€ž€3_IBOutlineViewController_IBDockViewController_LastKnownOutlineViewWidthĶ� � �  Ą�(  €3Ķ� � � ¤Ĩ�(  €3Ķ� � � ¨Ž�(ĨŠĒĢŦ­ēģŧŊžĨ°ą˛ŗ€ĨŋÀ3ZZoomFactor[CenterPoint_$MemberIDToLastKnownCanvasPositionMap_EditedTopLevelMemberIDs_$ObjectIDToLastKnownCanvasPositionMap\{133, 53.75}Ķ� � � ŧž�(Ą€¨ĄŋÁ€3\{-215, -130}Ō� � Ã�ÂĄ€¨€lĶ� � � ĮČ�(  €3Ķ� � � ËĐ�(¤ĖÍÎ΁ŁƁĮȤŅŌĶԁɁˁˁŅ€3_SelectedMemberIdentifiers_IBHybridStructureViewController_SelectionProvider_IBCanvasViewControllerŌ� � Û�ÂĄ܁ʀlS685Ķ� � � āä�(ŖĶâãˁ́ÎŖåæė΁Ѐž€3_IBOutlineViewController_IBDockViewController_LastKnownOutlineViewWidthĶ� � � íî�(  €3Ķ� � � ņō�(  €3Ķ� � � õû�(Ĩö÷øųúԁ́ԁՁÖĨũū˙�€Ĩׁ؁؁ڀ3ZZoomFactor[CenterPoint_$MemberIDToLastKnownCanvasPositionMap_EditedTopLevelMemberIDs_$ObjectIDToLastKnownCanvasPositionMap]{392.5, 19.5}Ķ� � �   �(  €3Ō� �  �ÂĄ€¨€lĶ� � � �(  €3Ķ� � � p�(¯Z !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmno܁ہāâäæčęėîđōôöøúüū�   "$&(*,.02468:<>@BDFHJLNPRTVXZ\^`bdfhjlnprtvxz|~€‚„†ˆЁŒޝZqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ĄĸŖ¤Ĩϧ¨ŠĒĢŦ­Ž¯°ą˛ŗ´ĩšēģŧŊžŋĀÁÂÃÄÅÆĮČÉʁ˜ ¨¯ˇžƁ΁ցŨāčëķöũ� #+3:BFILT[^adknqt|€„ˆ‹’šžĸЁ­ą´ˇģŋĮˁ΁ׁځŨāãëīķ÷ûū#&),/259A€3Ķ‹� ŒŽĪ€�€~Ũ_Afile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/core/untgz.cĶ‹� ŒŽԀ�€~ß_Dx-xcode-disassembly://stack_frame?processID=899&threadID=1&frameID=0Ķ‹� ŒŽŲ€�€~á_Gfile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/core/ujpeg.cppĶ‹� ŒŽŪ€�€~ã_Pfile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/core/nifti1_io_core.cppĶ‹� ŒŽã€�€~å_:file:///Users/rorden/Documents/cocoa/dcm2/dcm2/nii_ortho.cĶ‹� ŒŽč€�€~į_Ex-xcode-disassembly://stack_frame?processID=4814&threadID=1&frameID=0Ķ‹� ŒŽí€�€~é_Ffile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/AppDelegate.mĶ‹� ŒŽō€�€~ë_Ffile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/nii_dicom_batch.cĶ‹� ŒŽ÷€�€~í_Afile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/core/untgz.hĶ‹� ŒŽü€�€~ī_Fx-xcode-disassembly://stack_frame?processID=11060&threadID=1&frameID=0Ķ‹� ŒŽ€�€~ņ_Ex-xcode-disassembly://stack_frame?processID=3897&threadID=1&frameID=0Ķ‹� ŒŽ€�€~ķ_:file:///Users/rorden/Documents/cocoa/dcm2/dcm2/nii_dicom.hĶ‹� ŒŽ €�€~õ_Ex-xcode-disassembly://stack_frame?processID=3868&threadID=1&frameID=0Ķ‹� ŒŽ€�€~÷_Gfile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/core/nii_dicom.cppĶ‹� ŒŽ€�€~ų_Ex-xcode-disassembly://stack_frame?processID=3052&threadID=1&frameID=0Ķ‹� ŒŽ€�€~û_Ex-xcode-disassembly://stack_frame?processID=4663&threadID=1&frameID=0Ķ‹� ŒŽ€�€~ũ_Ex-xcode-disassembly://stack_frame?processID=3859&threadID=1&frameID=0Ķ‹� ŒŽ$€�€~˙_?file:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/myWindow.hĶ‹� ŒŽ)€�€~_Kfile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/core/nii_dicom_batch.cĶ‹� ŒŽ.€�€~_Ex-xcode-disassembly://stack_frame?processID=4749&threadID=1&frameID=0Ķ‹� ŒŽ3€�€~_Dx-xcode-disassembly://stack_frame?processID=521&threadID=1&frameID=0Ķ‹� ŒŽ8€�€~_Qfile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/core/nii_dicom_batch.cppĶ‹� ŒŽ=€�€~ _Dx-xcode-disassembly://stack_frame?processID=499&threadID=1&frameID=0Ķ‹� ŒŽB€�€~ _Bfile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/core/nifti1.hĶ‹� ŒŽG€�€~ _Ffile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/AppDelegate.hĶ‹� ŒŽL€�€~_<file:///Users/rorden/Documents/cocoa/dcm2/dcm2/AppDelegate.hĶ‹� ŒŽQ€�€~_¸file:///Applications/zXcode_4.6.3.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSPanel.hĶ‹� ŒŽV€�€~_;file:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/main.mĶ‹� ŒŽ[€�€~_Jfile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/core/nifti1_io_core.hĶ‹� ŒŽ`€�€~_Cfile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/myWindow.mĶ‹� ŒŽe€�€~_Ex-xcode-disassembly://stack_frame?processID=1326&threadID=1&frameID=0Ķ‹� ŒŽj€�€~_Ex-xcode-disassembly://stack_frame?processID=3733&threadID=1&frameID=0Ķ‹� ŒŽo€�€~_?file:///Users/rorden/Documents/cocoa/dcm2/dcm2/nifti1_io_core.cĶ‹� ŒŽt€�€~_Fx-xcode-disassembly://stack_frame?processID=16406&threadID=1&frameID=0Ķ‹� ŒŽy€�€~!_Dx-xcode-disassembly://stack_frame?processID=456&threadID=1&frameID=0Ķ‹� ŒŽ~€�€~#_Hfile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/core/jpg_0XC3.hĶ‹� ŒŽƒ€�€~%_Ex-xcode-disassembly://stack_frame?processID=4858&threadID=1&frameID=0Ķ‹� ŒŽˆ€�€~'_Efile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/nifti1_io_core.cĶ‹� ŒŽ€�€~)_~x-xcode-disassembly://stack_frame?launchSessionRef=7fc5045dc3f0&stackFrameHash=11742044243361242825&stackFrameRef=7fc530bc28d0Ķ‹� ŒŽ’€�€~+_Dx-xcode-disassembly://stack_frame?processID=944&threadID=1&frameID=0Ķ‹� ŒŽ—€�€~-_Dx-xcode-disassembly://stack_frame?processID=909&threadID=1&frameID=0Ķ‹� ŒŽœ€�€~/_Bfile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/AppDelegate.hĶ‹� ŒŽĄ€�€~1_:file:///Users/rorden/Documents/cocoa/dcm2/dcm2/nii_dicom.cĶ‹� ŒŽĻ€�€~3_Jfile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/core/nifti1_io_core.cĶ‹� ŒŽĢ€�€~5_<file:///Users/rorden/Documents/cocoa/dcm2/dcm2/AppDelegate.mĶ‹� ŒŽ°€�€~7_>file:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/tinydir.hĶ‹� ŒŽĩ€�€~9_Kfile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/core/nii_dicom.cppĶ‹� ŒŽē€�€~;_:file:///Users/rorden/Documents/cocoa/dcm2/dcm2/nii_ortho.hĶ‹� ŒŽŋ€�€~=_Nfile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/core/nifti1_io_core.hĶ‹� ŒŽĀ�€~?_Ofile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/core/nii_dicom_batch.hĶ‹� ŒŽɀ�€~A_Efile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/core/nii_dicom.hĶ‹� ŒŽ΀�€~C_9file:///Users/rorden/Documents/cocoa/dcm2/dcm2/myNSView.hĶ‹� ŒŽĶ€�€~E_Bfile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/AppDelegate.mĶ‹� ŒŽ؀�€~G_?file:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/myWindow.mĶ‹� ŒŽŨ€�€~I_9file:///Users/rorden/Documents/cocoa/dcm2/dcm2/myWindow.hĶ‹� ŒŽâ€�€~K_Dx-xcode-disassembly://stack_frame?processID=489&threadID=1&frameID=0Ķ‹� ŒŽį€�€~M_@file:///Users/rorden/Documents/cocoa/dcm2/dcm2/nii_dicom_batch.cĶ‹� ŒŽė€�€~O_Bfile:///Users/rorden/Documents/cocoa/dcm2/dcm2/nii_dicom_batch.cppĶ‹� ŒŽņ€�€~Q_Cfile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/core/nii_x.cppĶ‹� ŒŽö€�€~S_Cfile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/core/tinydir.hĶ‹� ŒŽû€�€~U_Ifile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/core/nii_dicom.hĶ‹� ŒŽ�€�€~W_Efile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/core/nii_ortho.hĶ‹� ŒŽ€�€~Y_Ifile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/core/nii_ortho.hĶ‹� ŒŽ €�€~[_Dx-xcode-disassembly://stack_frame?processID=587&threadID=1&frameID=0Ķ‹� ŒŽ€�€~]_Ffile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/nii_dicom_batch.hĶ‹� ŒŽ€�€~__Dx-xcode-disassembly://stack_frame?processID=922&threadID=1&frameID=0Ķ‹� ŒŽ€�€~a_@file:///Users/rorden/Documents/cocoa/dcm2/dcm2/nii_dicom_batch.hĶ‹� ŒŽ€�€~c_)file:///usr/include/sys/_types/_clock_t.hĶ‹� ŒŽ#€�€~e_Gfile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/core/nii_ortho.cppĶ‹� ŒŽ(€�€~g_Efile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/core/nii_dicom.cĶ‹� ŒŽ-€�€~i_Afile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/core/nii_x.hĶ‹� ŒŽ2€�€~k_Ffile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/core/nifti1.hĶ‹� ŒŽ7€�€~m_Efile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/core/miniz.cĶ‹� ŒŽ<€�€~o_Ex-xcode-disassembly://stack_frame?processID=3850&threadID=1&frameID=0Ķ‹� ŒŽA€�€~q_Ex-xcode-disassembly://stack_frame?processID=4827&threadID=1&frameID=0Ķ‹� ŒŽF€�€~s_„file:///Applications/zXcode_4.6.3.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/usr/include/stdio.hĶ‹� ŒŽK€�€~u_Dx-xcode-disassembly://stack_frame?processID=935&threadID=1&frameID=0Ķ‹� ŒŽP€�€~w_?file:///Users/rorden/Documents/cocoa/dcm2/dcm2/nifti1_io_core.hĶ‹� ŒŽU€�€~y_7file:///Users/rorden/Documents/cocoa/dcm2/dcm2/nifti1.hĶ‹� ŒŽZ€�€~{_Kfile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/core/nii_dicom_batch.hĶ‹� ŒŽ_€�€~}_Efile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/core/nii_ortho.cĶ‹� ŒŽd€�€~_Ex-xcode-disassembly://stack_frame?processID=3074&threadID=1&frameID=0Ķ‹� ŒŽi€�€~_?file:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/main.mĶ‹� ŒŽn€�€~ƒ_Jfile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/core/jpg_0XC3.cppĶ‹� ŒŽs€�€~…_Ex-xcode-disassembly://stack_frame?processID=1703&threadID=1&frameID=0Ķ‹� ŒŽx€�€~‡_}x-xcode-disassembly://stack_frame?launchSessionRef=7fc5045dc3f0&stackFrameHash=3688102889886328965&stackFrameRef=7fc52e2f0a20Ķ‹� ŒŽ}€�€~‰_Ex-xcode-disassembly://stack_frame?processID=1441&threadID=1&frameID=0Ķ‹� ŒŽ‚€�€~‹_9file:///Users/rorden/Documents/cocoa/dcm2/dcm2/myNSView.mĶ‹� ŒŽ‡€�€~_9file:///Users/rorden/Documents/cocoa/dcm2/dcm2/myWindow.mĶ‹� ŒŽŒ€�€~_Ex-xcode-disassembly://stack_frame?processID=3065&threadID=1&frameID=0Ķ� � � ”�(¤‘’“‘’“”¤•–�#˜•–€Ų—€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#AšnĶĻCÖR\{10190, 991}Z{10735, 0}Ķ� � � ĸ§�(¤Ŗ¤Ĩρ™š›œ¤¨Š�#́ž€ŲŸ€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#A¸ķT9›ŌČ[{624, 1061}V{0, 0}Ķ� � � ĩē�(¤ļˇ¸šĄĸŖ¤¤ģŧ�#žĨπ؁§€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#AģîßÕ\{7967, 1134}Y{8807, 0}Ķ� � � ČÍ�(¤ÉĘËˁЁǁ́Ŧ¤ÎĪ�#́­ހ؁Ÿ€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Aģ@qS(ĻX{0, 866}Ķ� � � Úß�(¤ÛÜŨہ°ą˛ŗ¤āá�#ぴĩ€Ųļ€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#A¸ø$g˛%\{3210, 1005}[{3769, 349}Ķ� � � íō�(¤îīđņ¸šēģ¤ķô�#́ŧŊ€ŲŸ€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#A¸ôĐMFŽY{0, 1079}Ķ� � � ˙ �(¤ �   ŋÁ¤  �# ÁĀ؁ŀ3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Aģ”.DŠH\{4129, 1266}Y{494, 37}Ķ� � �  �(¤    ĮȁɁʤ  �# ˁˀ؁̀3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Aš Ōc/L\{45530, 807}Z{45895, 6}Ķ� � � % *�(¤ & ' ( )΁ЁҁŌ¤ + ,�# .́Ô€ŲՀ3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Ašg9w~ÃX{0, 131}X{85, 38}Ķ� � � 8 =�(¤ 9 : ; <ׁ؁؁Ú¤ > ?�#́ہ܀؁Ÿ€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#AšŽ?3ŸōNY{0, 1781}Ķ� � � J O�(¤ & ' ( )΁ЁҁŌ¤ P Q�#́ہ߀؁Ÿ€3#AšgüUM”[{695, 1487}Ķ� � � X ]�(¤ Y Z [ \áâãä¤ ^ _�# aåæ€Ųį€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#A¸ūõā?ŦO[{332, 2076}Y{1685, 9}Ķ� � � k p�(¤ & ' ( )΁ЁҁŌ¤ q r�#́éę€ŲŸ€3#AšgŋC=)Y{0, 1091}Ķ� � � y ~�(¤ z { | }ėíîī¤  €�# ‚đņ€Ųō€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Ašô$Ú¯;!]{84388, 1167}Z{58942, 0}Ķ� � � Œ ‘�(¤ & ' ( )΁ЁҁŌ¤ ’ “�#́ôõ€ŲŸ€3#AšføÜČŊ[{2886, 998}Ķ� � � š Ÿ�(¤ › œ  ž÷øųú¤   Ą�#́ûü€ŲŸ€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#AšŨcCÉ\{2729, 1058}Ķ� � � Ŧ ą�(¤ & ' ( )΁ЁҁŌ¤ ˛ ŗ�#́ū˙€ŲŸ€3#Ašg˛Ųj[{695, 1487}Ķ� � � ē ŋ�(¤ & ' ( )΁ЁҁŌ¤ Ā Á�#́€ŲŸ€3#Ašfé’R3X{0, 193}Ķ� � � Č Í�(¤ É Ę Ë Ė¤ Î Ī�# ҁ €Ų €3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Ašôd@Ļ"]{53822, 1250}Z{54145, 0}Ķ� � � Û ā�(¤ › œ  ž÷øųú¤ á â�#́  €ŲŸ€3#AšßÛ܌\{2729, 1058}Ķ� � � é î�(¤Ŗ¤Ĩρ™š›œ¤ ī đ�#́€ŲŸ€3#A¸ķM:” [{2101, 708}Ķ� � � ÷ ü�(¤ �   ŋÁ¤ ũ ū�# �€Ų€3#Aģ † H˜Ô\{76313, 921}Z{75137, 0}Ķ� � �  �(¤Ŗ¤Ĩρ™š›œ¤ �#́€ŲŸ€3#A¸ķMĒ‚[{2101, 708}Ķ� � �  �(¤    ¤  �#́€ŲŸ€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Ašô"Į”Y§Y{0, 1762}Ķ� � � & +�(¤ �   ŋÁ¤ , -�# / !€Ų"€3#Aģ“˙å6ĩZ{354, 764}Y{872, 11}Ķ� � � 5 :�(¤ 6 7 8 9$%&'¤ ; <�# >()€Ų*€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Aš ´tõ–§X{0, 515}X{272, 3}Ķ� � � H M�(¤ I J K L,-./¤ N O�# Q01€Ų2€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#A¸ô•+Šƒ[{3778, 624}Z{4128, 23}Ķ� � � [ `�(¤ \ ] ^ _4567¤ a b�#́89€ŲŸ€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#AšŽ>~ÁAX{0, 246}Ķ� � � m r�(¤ n o p q;<=>¤ s t�# v?@€ŲA€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Aš]íčŦŽ[{720, 1471}Y{1417, 0}Ķ� � � € …�(¤ �   ŋÁ¤ † ‡�# ‰CD€ŲE€3#Aģ”'Ô­6X{0, 611}Y{1178, 0}Ķ� � �  ”�(¤Ŗ¤Ĩρ™š›œ¤ • –�#́GH€ŲŸ€3#A¸ķü†FŽ“\{2269, 1032}Ķ� � �  ĸ�(¤ › œ  ž÷øųú¤ Ŗ ¤�#́JK€ŲŸ€3#AšŨ7Ęôā\{1909, 1139}Ķ� � � Ģ °�(¤ Ŧ ­ Ž ¯MNOP¤ ą ˛�# ´QR€ŲS€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Ašž;ԚŨ[{1207, 767}Y{1703, 7}Ķ� � � ž Ã�(¤ ŋ Ā Á UVWX¤ Ä Å�#́YZ€ŲŸ€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#AšČÜGąžb[{2101, 949}Ķ� � � Đ Õ�(¤Ŗ¤Ĩρ™š›œ¤ Ö ×�#́\]€ŲŸ€3#A¸ķLÂÉšé[{2101, 708}Ķ� � � Ū ã�(¤ļˇ¸šĄĸŖ¤¤ ä å�#́_`€ŲŸ€3#AģzQVŌX{0, 788}Ķ� � � ė ņ�(¤ & ' ( )΁ЁҁŌ¤ ō ķ�#́bc€ŲŸ€3#Ašg9ģÖųŠX{0, 610}Ķ� � � ú ˙�(¤ û ü ũ ūefgh¤ � �#́ij€ŲŸ€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Aš Ôå*EÔX{0, 630}Ķ� � � �(¤ļˇ¸šĄĸŖ¤¤  �#́lm€ŲŸ€3#AģŲÉ €$X{0, 416}Ķ� � �  �(¤Ŗ¤Ĩρ™š›œ¤ !�#́op€ŲŸ€3#A¸ķUČ7‚[{624, 1061}Ķ� � � ( -�(¤Ŗ¤Ĩρ™š›œ¤ . /�#́rs€ŲŸ€3#A¸ķToĪú[{624, 1061}Ķ� � � 6 ;�(¤ 7 8 9 :uvwx¤ < =�# ?yz€Ų{€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Ašā-ŊũiZ{328, 752}X{230, 0}Ķ� � � I N�(¤ 6 7 8 9$%&'¤ O P�# R}~€Ų€3#Aš ĄQ÷‡]{54937, 1329}[{55594, 44}Ķ� � � X ]�(¤ ŋ Ā Á UVWX¤ ^ _�# a‚€Ųƒ€3#AšČŨ`]tX{0, 679}X{227, 0}Ķ� � � g l�(¤ 6 7 8 9$%&'¤ m n�# p…†€Ų‡€3#Aš ēÉô\{3987, 1317}Y{4474, 7}Ķ� � � v {�(¤    ĮȁɁʤ | }�#́‰Ѐ؁Ÿ€3#Aš ŌlŠsY{0, 1304}Ķ� � � „ ‰�(¤ �   ŋÁ¤ Š ‹�# Œ€ŲŽ€3#AģŠÎõÖ]{52835, 1772}Z{53816, 0}Ķ� � � “ ˜�(¤Ŗ¤Ĩρ™š›œ¤ ™ š�#́‘€ŲŸ€3#A¸ķLœÎ hX{0, 236}Ķ� � � Ą Ļ�(¤ ĸ Ŗ ¤ Ĩ“”•–¤ § ¨�# ǁ—˜€Ų™€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Aģ‘đ2įÅ\{1277, 1252}X{340, 0}Ķ� � � ´ š�(¤ �   ŋÁ¤ ē ģ�# Ŋ›œ€Ų€3#Aģ•­S[÷X{0, 923}X{237, 9}Ķ� � � à Č�(¤    ¤ É Ę�# ˁŸ €ŲĄ€3#Ašô$WŅ~X{0, 617}X{179, 0}Ķ� � � Ō ×�(¤ Ķ Ô Õ ÖŖ¤ĨϤ Ø Ų�#́§¨€ŲŸ€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#A¸ķ:ŋ´ŽCX{0, 191}Ķ� � � ä é�(¤ É Ę Ë Ė¤ ę ë�# íǁ̀؁Ŧ€3#Ašô ~€Ū\{1225, 1861}Y{6872, 0}Ķ� � � ķ ø�(¤ 7 8 9 :uvwx¤ ų ú�# üށ¯€Ų°€3#AšáŖ÷´;‚X{0, 692}Y{308, 23}Ķ� � �  �(¤ 6 7 8 9$%&'¤  �#́˛ŗ€ŲŸ€3#Aš ĄS‡DX{0, 193}Ķ� � �  �(¤Ŗ¤Ĩρ™š›œ¤  �#́ĩļ€ŲŸ€3#A¸ķLķJßĩ[{2101, 708}Ķ� � �  #�(¤ 6 7 8 9$%&'¤ $ %�# '¸š€Ųē€3#Aš ēÂ+9\{45554, 744}Z{45895, 6}Ķ� � � - 2�(¤ÛÜŨہ°ą˛ŗ¤ 3 4�# 6ŧŊ€Ųž€3#A¸øg†Jy%]{27587, 1802}Z{36493, 0}Ķ� � � < A�(¤ = > ? @Áä B C�# Eāŀ؁ƀ3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#AšÂ7˙ë]{13528, 1399}[{14133, 76}Ķ� � � O T�(¤ É Ę Ë Ė¤ U V�# Xȁɀ؁ʀ3#Ašô]åu[{1814, 459}Y{1956, 6}Ķ� � � ^ c�(¤ �   ŋÁ¤ d e�# gˁÍ€Ų΀3#Aģ ‘=áˆ]Z{113, 931}X{350, 0}Ķ� � � m r�(¤ n o p qЁҁԁͤ s t�# vԁՀ؁ր3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Aš!�” +X{0, 236}Y{100, 19}Ķ� � � € …�(¤ÉĘËˁЁǁ́Ŧ¤ † ‡�#́؁؀؁Ÿ€3#AģC@ų[ĢX{0, 327}Ķ� � � Ž “�(¤Ŗ¤Ĩρ™š›œ¤ ” •�#́ہ܀؁Ÿ€3#A¸ķMАE[{2101, 708}Ķ� � � œ Ą�(¤    ĮȁɁʤ ĸ Ŗ�#́ہ߀؁Ÿ€3#Aš ŌŽCQ‹X{0, 563}Ķ� � � Ē ¯�(¤Ŗ¤Ĩρ™š›œ¤ ° ą�#́áâ€ŲŸ€3#A¸ķTš{5[{624, 1061}Ķ� � � ¸ Ŋ�(¤ š ē ģ ŧäåæᤠž ŋ�# Áčé€Ųę€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Aš­5äė–X{0, 772}X{734, 0}Ķ� � � Ë Đ�(¤ n o p qЁҁԁͤ Ņ Ō�# ԁėí€Ųî€3#Aš û~,‡#Y{0, 1426}Z{1363, 41}Ķ� � � Ú ß�(¤    ¤ ā á�# ãđņ€Ųō€3#Ašô$yį˜[{3797, 758}Y{2688, 0}Ķ� � � é î�(¤ É Ę Ë Ė¤ ī đ�# ōôõ€Ųö€3#Ašô V“]{78258, 1998}Z{78632, 0}Ķ� � � ø ũ�(¤ = > ? @Áä ū ˙�# øų€Ųú€3#AšÂ,æ*X{0, 378}X{238, 0}Ķ� � �  �(¤ �   ŋÁ¤ �#́üũ€ŲŸ€3#Aģ”Ō)t\{6509, 1268}Ķ� � �  �(¤ļˇ¸šĄĸŖ¤¤  �# ˙�€Ų€3#AģîUJ#^{186364, 1070}[{186943, 0}Ķ� � � $ )�(¤ & ' ( )΁ЁҁŌ¤ * +�#́€ŲŸ€3#AšgĨ:?[{695, 1487}Ķ� � � 2 7�(¤îīđņ¸šēģ¤ 8 9�#́€ŲŸ€3#A¸ôĐ]UũŊY{0, 1079}Ķ� � � @ E�(¤ A B C D    ¤ F G�# I €Ų€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#A¸˙sw[{8087, 896}Z{8438, 25}Ķ� � � S X�(¤Ŗ¤Ĩρ™š›œ¤ Y Z�#́€ŲŸ€3#A¸ķTū”Í…[{624, 1061}Ķ� � � a f�(¤ š ē ģ ŧäåæᤠg h�# j€Ų€3#AšŠeë]ŧ\{1205, 1775}Z{2443, 40}Ķ� � � p u�(¤ š ē ģ ŧäåæᤠv w�# y€Ų€3#Aš¨č}ģ(]{25324, 1958}Z{25839, 0}Ķ� � �  „�(¤ 7 8 9 :uvwx¤ … †�# ˆ€Ų€3#AšāãpąY{34, 757}Y{208, 22}Ķ� � � Ž “�(¤ \ ] ^ _4567¤ ” •�# — !€Ų"€3#AšŽ>gë:]{11547, 1358}Z{11935, 0}Ķ� � �  ĸ�(¤ & ' ( )΁ЁҁŌ¤ Ŗ ¤�#́$%€ŲŸ€3#AšføJ9\ę[{2886, 998}Ķ� � � Ģ °�(¤ļˇ¸šĄĸŖ¤¤ ą ˛�#́'(€ŲŸ€3#AģڐQÉOX{0, 246}Ķ� � � š ž�(¤ļˇ¸šĄĸŖ¤¤ ŋ Ā�#́*+€ŲŸ€3#Aģz(X›Y{0, 1135}Ķ� � � Į Ė�(¤Ŗ¤Ĩρ™š›œ¤ Í Î�#́-.€ŲŸ€3#A¸ô5†DĻZ{861, 580}Ķ� � � Õ Ú�(¤ļˇ¸šĄĸŖ¤¤ Û Ü�#́01€ŲŸ€3#AģŲĶ‚+X{0, 285}Ķ� � � ã č�(¤Ŗ¤Ĩρ™š›œ¤ é ę�#́34€ŲŸ€3#A¸ķūš›å\{2269, 1032}Ķ� � � ņ ö�(¤ Ķ Ô Õ ÖŖ¤ĨϤ ÷ ø�# ú67€Ų8€3#A¸ķ:™QíZ{338, 820}X{447, 0}Ķ� � � ��(¤:;<=¤�# >?€Ų@€3_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Aš7KĪ€ˆY{69, 562}Y{1009, 0}Ķ� � � �(¤ & ' ( )΁ЁҁŌ¤�#́BC€ŲŸ€3#Ašfø/Úø[{2886, 998}Ķ� � � !#�(Ą"EĄ$G€3Ķ‹� ŒŽ)€�€~F_Nfile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/en.lproj/MainMenu.xibĶ� � � ,2�(Ĩ-./01HIJKLĨ34�h67MN€5OT€3_SelectedMemberIdentifiers_IBHybridStructureViewController_;LastVersionForSuppressingAutolayoutUpgradePromptForDocument_SelectionProvider_IBCanvasViewControllerŌ� � ?�ÂĄ€Ģ€lĶ� � � CG�(Ŗ6EFOPQŖHIėRS€ž€3_IBOutlineViewController_IBDockViewController_LastKnownOutlineViewWidthĶ� � � PQ�(  €3Ķ� � � TU�(  €3Ķ� � � X]�(¤YZ[\UVWX¤_`a€ĨYZ[€3ZZoomFactor[CenterPoint_EditedTopLevelMemberIDs_$ObjectIDToLastKnownCanvasPositionMap\{445, 152.5}Ō� � i�ÂĄ€Ģ€lĶ� � � mn�(  €3Ķ� � � qu�(Ŗr�Ķt]€}_Ŗvwxa\j€3Ķ‹� ŒŽ}€�€~^_>file:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2.xcodeprojĶ‹� ŒŽ‚€�€~`_8file:///Users/rorden/Documents/cocoa/dcm2/dcm2.xcodeprojĶ� � � …‹�(Ĩ†‡ˆ‰ЁbcdefĨŒŽghop[€3_-Xcode3ProjectEditorPreviousProjectEditorClass_(Xcode3ProjectEditor.sourceList.splitview_,Xcode3ProjectEditorPreviousTargetEditorClass_,Xcode3ProjectEditorSelectedDocumentLocations_-Xcode3ProjectEditor_Xcode3BuildSettingsEditor_Xcode3BuildSettingsEditorĶ� � � ™›�(ĄšiĄœj€3_DVTSplitViewItemsŌ� �  �ĸĄĸkm€4Ķ� � � Ĩ¨�ĸö÷€ę€ëĸЁl€R€8PĶ� � � Žą�ĸö÷€ę€ëĸŠŗln€8#@‡ �����_Xcode3BuildSettingsEditorŌ� � ¸�ÂĄšq€lÔ� ŧŊžtsr€m_Hfile://localhost/Users/rorden/Documents/cocoa/dcm2/xcode/dcm2.xcodeproj/#Ašô(›Ë%Ķ� � � ÃĮ�(ŖÄÅƁuvwŖČ4ʁx€ŗy€3VEditorVTarget_"Xcode3BuildSettingsEditorLocations_Xcode3BuildSettingsEditorŌ� � Ņ�ÂĄԁz€lĶ� � � ÕÜ�(ĻÖרŲÚہ{|}~€ĻŨŪ�g�h�h�gY€k€5€5€k€3_#Collapsed Build Property Categories_Selected Build Properties_$Xcode3BuildSettingsEditorDisplayMode_#Xcode3BuildPropertyValueDisplayMode_Xcode3BuildSettingsEditorMode_"Xcode3BuildPropertyNameDisplayModeŌ� � ë�¯×ėíîīđņōķôõö÷øųúûüũū˙�      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ĄĸŖ¤Ĩϧ¨ŠĒĢŦ­Ž¯°ą˛ŗ´ĩšēģŧŊžŋĀÁ‚ƒ„…†‡ˆ‰Ё‹Œށ‘’“”•–—˜™š›œžŸ ĄĸŖ¤Ĩρ§¨Ёǁ́Ŧ­ށ¯°ą˛ŗ´ĩļˇ¸šēģŧŊžŋÁÁāŁƁĮȁɁʁˁˁ́΁΁Ёҁԁ́ԁՁցׁ؁؁ځہ܁Ũہ߁āáâãäåæįčéęëėíîīđņōķôõö÷øųúûüũū˙�      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWX€4Ō� éęƁ0_Architectures||ADDITIONAL_SDKSŌ� éęɁ0_Architectures||ARCHSŌ� éęˁ0_Architectures||SDKROOTŌ� éę΁0_#Architectures||SUPPORTED_PLATFORMSŌ� éęԁ0_Architectures||VALID_ARCHSŌ� éęՁ0_Build Locations||SYMROOTŌ� éę؁0_Build Locations||OBJROOTŌ� éęہ0_%Build Locations||SHARED_PRECOMPS_DIRŌ� éęہ0_Build Options||BUILD_VARIANTSŌ� éęá0_Build Options||GCC_VERSIONŌ� éęä0_'Build Options||GENERATE_PROFILING_CODEŌ� éęį0_@Build Options||PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIRŌ� éęę0_)Build Options||RUN_CLANG_STATIC_ANALYZERŌ� éęí0_2Build Options||SCAN_ALL_SOURCE_FILES_FOR_INCLUDESŌ� éęđ0_ Build Options||VALIDATE_PRODUCTŌ� éęķ0_%Code Signing||CODE_SIGN_ENTITLEMENTSŌ� éęö0_!Code Signing||CODE_SIGN_IDENTITYŌ� éęų0_,Code Signing||CODE_SIGN_RESOURCE_RULES_PATHŌ� éęü0_$Code Signing||OTHER_CODE_SIGN_FLAGSŌ� éę˙0_Deployment||STRIPFLAGSŌ� éę0_Deployment||ALTERNATE_GROUPŌ� éę0_Deployment||ALTERNATE_OWNERŌ� éę0_Deployment||ALTERNATE_MODEŌ� éę 0_(Deployment||ALTERNATE_PERMISSIONS_FILESŌ� éę0_!Deployment||COMBINE_HIDPI_IMAGESŌ� éę0_ Deployment||DEPLOYMENT_LOCATIONŌ� éę0_&Deployment||DEPLOYMENT_POSTPROCESSINGŌ� éę0_Deployment||INSTALL_GROUPŌ� éę0_Deployment||INSTALL_OWNERŌ� éę0_Deployment||INSTALL_MODE_FLAGŌ� éę 0_Deployment||DSTROOTŌ� éę#0_Deployment||INSTALL_PATHŌ� éę&0_%Deployment||MACOSX_DEPLOYMENT_TARGETŌ� éę)0_%Deployment||PRODUCT_DEFINITION_PLISTŌ� éę,0_Deployment||SKIP_INSTALLŌ� éę/0_$Deployment||STRIP_INSTALLED_PRODUCTŌ� éę20_Deployment||STRIP_STYLEŌ� éę50_Deployment||SEPARATE_STRIPŌ� éę80_Kernel Module||MODULE_NAMEŌ� éę;0_Kernel Module||MODULE_STARTŌ� éę>0_Kernel Module||MODULE_STOPŌ� éęA0_Kernel Module||MODULE_VERSIONŌ� éęD0_Linking||BUNDLE_LOADERŌ� éęG0_%Linking||DYLIB_COMPATIBILITY_VERSIONŌ� éęJ0_Linking||DYLIB_CURRENT_VERSIONŌ� éęM0_Linking||DEAD_CODE_STRIPPINGŌ� éęP0_'Linking||LINKER_DISPLAYS_MANGLED_NAMESŌ� éęS0_,Linking||PRESERVE_DEAD_CODE_INITS_AND_TERMSŌ� éęV0_Linking||LD_DYLIB_INSTALL_NAMEŌ� éęY0_Linking||EXPORTED_SYMBOLS_FILEŌ� éę\0_Linking||LD_NO_PIEŌ� éę_0_Linking||INIT_ROUTINEŌ� éęb0_&Linking||LINK_WITH_STANDARD_LIBRARIESŌ� éęe0_Linking||MACH_O_TYPEŌ� éęh0_Linking||ORDER_FILEŌ� éęk0_Linking||OTHER_LDFLAGSŌ� éęn0_%Linking||GENERATE_MASTER_OBJECT_FILEŌ� éęq0_Linking||PRELINK_LIBSŌ� éęt0_Linking||KEEP_PRIVATE_EXTERNSŌ� éęw0_!Linking||LD_RUNPATH_SEARCH_PATHSŌ� éęz0_Linking||SEPARATE_SYMBOL_EDITŌ� éę}0_Linking||PRELINK_FLAGSŌ� éꀁ0_Linking||SECTORDER_FLAGSŌ� éęƒ0_!Linking||UNEXPORTED_SYMBOLS_FILEŌ� éꆁ0_Linking||WARNING_LDFLAGSŌ� éꉁ0_Linking||LD_GENERATE_MAP_FILEŌ� éęŒ0_%Packaging||APPLY_RULES_IN_COPY_FILESŌ� éꏁ0_ Packaging||EXECUTABLE_EXTENSIONŌ� éꒁ0_Packaging||EXECUTABLE_PREFIXŌ� éꕁ0_+Packaging||INFOPLIST_EXPAND_BUILD_SETTINGSŌ� éę˜0_!Packaging||GENERATE_PKGINFO_FILEŌ� éꛁ0_Packaging||FRAMEWORK_VERSIONŌ� éꞁ0_Packaging||INFOPLIST_FILEŌ� éꥁ0_.Packaging||INFOPLIST_OTHER_PREPROCESSOR_FLAGSŌ� é꤁0_#Packaging||INFOPLIST_OUTPUT_FORMATŌ� é꧁0_.Packaging||INFOPLIST_PREPROCESSOR_DEFINITIONSŌ� éęǁ0_#Packaging||INFOPLIST_PREFIX_HEADERŌ� éꭁ0_ Packaging||INFOPLIST_PREPROCESSŌ� é각0_&Packaging||COPYING_PRESERVES_HFS_DATAŌ� é溁0_'Packaging||PRIVATE_HEADERS_FOLDER_PATHŌ� éęļ0_Packaging||PRODUCT_NAMEŌ� éꚁ0_$Packaging||PLIST_FILE_OUTPUT_FORMATŌ� éęŧ0_&Packaging||PUBLIC_HEADERS_FOLDER_PATHŌ� éęŋ0_(Packaging||STRINGS_FILE_OUTPUT_ENCODINGŌ� éę0_Packaging||WRAPPER_EXTENSIONŌ� éęŁ0_'Search Paths||ALWAYS_SEARCH_USER_PATHSŌ� éęȁ0_%Search Paths||FRAMEWORK_SEARCH_PATHSŌ� éęˁ0_"Search Paths||HEADER_SEARCH_PATHSŌ� éę΁0_#Search Paths||LIBRARY_SEARCH_PATHSŌ� éęҁ0_Search Paths||REZ_SEARCH_PATHSŌ� éęԁ0_<Search Paths||EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIESŌ� éęׁ0_<Search Paths||INCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIESŌ� éęځ0_'Search Paths||USER_HEADER_SEARCH_PATHSŌ� éęŨ0_Unit Testing||OTHER_TEST_FLAGSŌ� éęā0_Unit Testing||TEST_AFTER_BUILDŌ� éęã0_Unit Testing||TEST_HOSTŌ� é濁0_Unit Testing||TEST_RIGŌ� éęé0_$Versioning||CURRENT_PROJECT_VERSIONŌ� éęė0_Versioning||VERSION_INFO_FILEŌ� éęī0_%Versioning||VERSION_INFO_EXPORT_DECLŌ� éęō0_ Versioning||VERSION_INFO_PREFIXŌ� éęõ0_ Versioning||VERSION_INFO_SUFFIXŌ� é渁0_Versioning||VERSIONING_SYSTEMŌ� éęû0_!Versioning||VERSION_INFO_BUILDERŌ� éęū0_AApple LLVM compiler 4.2 - Code Generation||GCC_FAST_OBJC_DISPATCHŌ� éę0_HApple LLVM compiler 4.2 - Code Generation||CLANG_X86_VECTOR_INSTRUCTIONSŌ� éę0_>Apple LLVM compiler 4.2 - Code Generation||GCC_STRICT_ALIASINGŌ� éę0_IApple LLVM compiler 4.2 - Code Generation||GCC_GENERATE_DEBUGGING_SYMBOLSŌ� éę 0_=Apple LLVM compiler 4.2 - Code Generation||GCC_DYNAMIC_NO_PICŌ� éę 0_KApple LLVM compiler 4.2 - Code Generation||GCC_GENERATE_TEST_COVERAGE_FILESŌ� éę0_IApple LLVM compiler 4.2 - Code Generation||GCC_INLINES_ARE_PRIVATE_EXTERNŌ� éę0_KApple LLVM compiler 4.2 - Code Generation||GCC_INSTRUMENT_PROGRAM_FLOW_ARCSŌ� éę0_HApple LLVM compiler 4.2 - Code Generation||GCC_ENABLE_KERNEL_DEVELOPMENTŌ� éę0_3Apple LLVM compiler 4.2 - Code Generation||LLVM_LTOŌ� éę0_<Apple LLVM compiler 4.2 - Code Generation||GCC_REUSE_STRINGSŌ� éę0_?Apple LLVM compiler 4.2 - Code Generation||GCC_NO_COMMON_BLOCKSŌ� éę"0_8Apple LLVM compiler 4.2 - Code Generation||GCC_FAST_MATHŌ� éę%0_AApple LLVM compiler 4.2 - Code Generation||GCC_THREADSAFE_STATICSŌ� éę(0_;Apple LLVM compiler 4.2 - Code Generation||GCC_UNROLL_LOOPSŌ� éę+0_=Apple LLVM compiler 4.2 - Language||GCC_CHAR_IS_UNSIGNED_CHARŌ� éę.0_:Apple LLVM compiler 4.2 - Language||GCC_ENABLE_ASM_KEYWORDŌ� éę10_;Apple LLVM compiler 4.2 - Language||GCC_C_LANGUAGE_STANDARDŌ� éę40_?Apple LLVM compiler 4.2 - Language||CLANG_CXX_LANGUAGE_STANDARDŌ� éę70_5Apple LLVM compiler 4.2 - Language||CLANG_CXX_LIBRARYŌ� éę:0_5Apple LLVM compiler 4.2 - Language||GCC_CW_ASM_SYNTAXŌ� éę=0_6Apple LLVM compiler 4.2 - Language||GCC_INPUT_FILETYPEŌ� éę@0_=Apple LLVM compiler 4.2 - Language||GCC_ENABLE_CPP_EXCEPTIONSŌ� éęC0_7Apple LLVM compiler 4.2 - Language||GCC_ENABLE_CPP_RTTIŌ� éęF0_CApple LLVM compiler 4.2 - Language||GCC_LINK_WITH_DYNAMIC_LIBRARIESŌ� éęI0_>Apple LLVM compiler 4.2 - Language||GCC_ENABLE_OBJC_EXCEPTIONSŌ� éęL0_8Apple LLVM compiler 4.2 - Language||GCC_ENABLE_TRIGRAPHSŌ� éęO0_KApple LLVM compiler 4.2 - Language||GCC_ENABLE_FLOATING_POINT_LIBRARY_CALLSŌ� éęR0_CApple LLVM compiler 4.2 - Language||GCC_USE_INDIRECT_FUNCTION_CALLSŌ� éęU0_CApple LLVM compiler 4.2 - Language||GCC_USE_REGISTER_FUNCTION_CALLSŌ� éęX0_;Apple LLVM compiler 4.2 - Language||CLANG_LINK_OBJC_RUNTIMEŌ� éę[0_KApple LLVM compiler 4.2 - Language||GCC_INCREASE_PRECOMPILED_HEADER_SHARINGŌ� éę^0_9Apple LLVM compiler 4.2 - Language||CLANG_ENABLE_OBJC_ARCŌ� éęa0_0Apple LLVM compiler 4.2 - Language||OTHER_CFLAGSŌ� éęd0_8Apple LLVM compiler 4.2 - Language||OTHER_CPLUSPLUSFLAGSŌ� éęg0_@Apple LLVM compiler 4.2 - Language||GCC_PRECOMPILE_PREFIX_HEADERŌ� éęj0_5Apple LLVM compiler 4.2 - Language||GCC_PREFIX_HEADERŌ� éęm0_@Apple LLVM compiler 4.2 - Language||GCC_ENABLE_BUILTIN_FUNCTIONSŌ� éęp0_=Apple LLVM compiler 4.2 - Language||GCC_ENABLE_PASCAL_STRINGSŌ� éęs0_3Apple LLVM compiler 4.2 - Language||GCC_SHORT_ENUMSŌ� éęv0_FApple LLVM compiler 4.2 - Language||GCC_USE_STANDARD_INCLUDE_SEARCHINGŌ� éęy0_ZApple LLVM compiler 4.2 - Preprocessing||GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPSŌ� éę|0_HApple LLVM compiler 4.2 - Warning Polices||GCC_WARN_INHIBIT_ALL_WARNINGSŌ� éę0_<Apple LLVM compiler 4.2 - Warning Polices||GCC_WARN_PEDANTICŌ� éꂁ0_GApple LLVM compiler 4.2 - Warning Polices||GCC_TREAT_WARNINGS_AS_ERRORSŌ� éꅁ0_TApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_CHECK_SWITCH_STATEMENTSŌ� éęˆ0_WApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_ABOUT_DEPRECATED_FUNCTIONSŌ� éꋁ0_IApple LLVM compiler 4.2 - Warnings - All languages||CLANG_WARN_EMPTY_BODYŌ� éęށ0_UApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_FOUR_CHARACTER_CONSTANTSŌ� éꑁ0_CApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_SHADOWŌ� éꔁ0_RApple LLVM compiler 4.2 - Warnings - All languages||CLANG_WARN_CONSTANT_CONVERSIONŌ� éꗁ0_TApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_64_TO_32_BIT_CONVERSIONŌ� éꚁ0_NApple LLVM compiler 4.2 - Warnings - All languages||CLANG_WARN_ENUM_CONVERSIONŌ� éꝁ0_MApple LLVM compiler 4.2 - Warnings - All languages||CLANG_WARN_INT_CONVERSIONŌ� éꠁ0_WApple LLVM compiler 4.2 - Warnings - All languages||CLANG_WARN_IMPLICIT_SIGN_CONVERSIONŌ� é檁0_\Apple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETEDŌ� éęρ0_NApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_ABOUT_RETURN_TYPEŌ� éęЁ0_PApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_MISSING_PARENTHESESŌ� éęŦ0_]Apple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERSŌ� é杁0_UApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_ABOUT_MISSING_PROTOTYPESŌ� é겁0_RApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_ABOUT_MISSING_NEWLINEŌ� éęĩ0_BApple LLVM compiler 4.2 - Warnings - All languages||WARNING_CFLAGSŌ� é긁0_UApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_ABOUT_POINTER_SIGNEDNESSŌ� éęģ0_IApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_SIGN_COMPAREŌ� éꞁ0_]Apple LLVM compiler 4.2 - Warnings - All languages||CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSIONŌ� éęÁ0_jApple LLVM compiler 4.2 - Warnings - All languages||GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORSŌ� éęā0_fApple LLVM compiler 4.2 - Warnings - All languages||GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORSŌ� éęĮ0_VApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_TYPECHECK_CALLS_TO_PRINTFŌ� éęʁ0_PApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_UNINITIALIZED_AUTOSŌ� éę́0_LApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_UNKNOWN_PRAGMASŌ� éęЁ0_LApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_UNUSED_FUNCTIONŌ� éę́0_IApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_UNUSED_LABELŌ� éęց0_MApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_UNUSED_PARAMETERŌ� éę؁0_IApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_UNUSED_VALUEŌ� éę܁0_LApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_UNUSED_VARIABLEŌ� éę߁0_KApple LLVM compiler 4.2 - Warnings - C++||CLANG_WARN__EXIT_TIME_DESTRUCTORSŌ� éęâ0_IApple LLVM compiler 4.2 - Warnings - C++||GCC_WARN_NON_VIRTUAL_DESTRUCTORŌ� éęå0_KApple LLVM compiler 4.2 - Warnings - C++||GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONSŌ� éęč0_OApple LLVM compiler 4.2 - Warnings - C++||GCC_WARN_ABOUT_INVALID_OFFSETOF_MACROŌ� éęë0_EApple LLVM compiler 4.2 - Warnings - C++||CLANG_WARN_CXX0X_EXTENSIONSŌ� éęî0_TApple LLVM compiler 4.2 - Warnings - Objective C||CLANG_WARN__DUPLICATE_METHOD_MATCHŌ� éęņ0_\Apple LLVM compiler 4.2 - Warnings - Objective C||CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIESŌ� éęô0_\Apple LLVM compiler 4.2 - Warnings - Objective C||CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESISŌ� éę÷0_TApple LLVM compiler 4.2 - Warnings - Objective C||GCC_WARN_ALLOW_INCOMPLETE_PROTOCOLŌ� éęú0_aApple LLVM compiler 4.2 - Warnings - Objective C||GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTORŌ� éęũ0_\Apple LLVM compiler 4.2 - Warnings - Objective C||CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONSŌ� éę�0_OApple LLVM compiler 4.2 - Warnings - Objective C||CLANG_WARN_OBJC_RECEIVER_WEAKŌ� éę0_PApple LLVM compiler 4.2 - Warnings - Objective C||GCC_WARN_STRICT_SELECTOR_MATCHŌ� éę0_NApple LLVM compiler 4.2 - Warnings - Objective C||GCC_WARN_UNDECLARED_SELECTORŌ� éę 0_TApple LLVM compiler 4.2 - Warnings - Objective C||CLANG_WARN__ARC_BRIDGE_CAST_NONARCŌ� éę 0_1OSACompile - Build Options||OTHER_OSACOMPILEFLAGSŌ� éę0_3OSACompile - Build Options||OSACOMPILE_EXECUTE_ONLYŌ� éę0_<Static Analyzer - Checks||CLANG_ANALYZER_DEADCODE_DEADSTORESŌ� éę0_,Static Analyzer - Checks||CLANG_ANALYZER_GCDŌ� éę0_/Static Analyzer - Checks||CLANG_ANALYZER_MALLOCŌ� éę0_BStatic Analyzer - Checks - Objective-C||CLANG_ANALYZER_OBJC_ATSYNCŌ� éę0_EStatic Analyzer - Checks - Objective-C||CLANG_ANALYZER_OBJC_NSCFERRORŌ� éę!0_OStatic Analyzer - Checks - Objective-C||CLANG_ANALYZER_OBJC_INCOMP_METHOD_TYPESŌ� éę$0_DStatic Analyzer - Checks - Objective-C||CLANG_ANALYZER_OBJC_CFNUMBERŌ� éę'0_GStatic Analyzer - Checks - Objective-C||CLANG_ANALYZER_OBJC_COLLECTIONSŌ� éę*0_HStatic Analyzer - Checks - Objective-C||CLANG_ANALYZER_OBJC_UNUSED_IVARSŌ� éę-0_EStatic Analyzer - Checks - Objective-C||CLANG_ANALYZER_OBJC_SELF_INITŌ� éę00_HStatic Analyzer - Checks - Objective-C||CLANG_ANALYZER_OBJC_RETAIN_COUNTŌ� éę30_MStatic Analyzer - Checks - Security||CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTERŌ� éę60_IStatic Analyzer - Checks - Security||CLANG_ANALYZER_SECURITY_KEYCHAIN_APIŌ� éę90_XStatic Analyzer - Checks - Security||CLANG_ANALYZER_SECURITY_INSECUREAPI_UNCHECKEDRETURNŌ� éę<0_SStatic Analyzer - Checks - Security||CLANG_ANALYZER_SECURITY_INSECUREAPI_GETPW_GETSŌ� éę?0_PStatic Analyzer - Checks - Security||CLANG_ANALYZER_SECURITY_INSECUREAPI_MKSTEMPŌ� éęB0_MStatic Analyzer - Checks - Security||CLANG_ANALYZER_SECURITY_INSECUREAPI_RANDŌ� éęE0_OStatic Analyzer - Checks - Security||CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPYŌ� éęH0_NStatic Analyzer - Checks - Security||CLANG_ANALYZER_SECURITY_INSECUREAPI_VFORKŌ� � J�ĄKZ€4Ō� éęO0_Deployment||DSTROOTĶ� � � QR�(  €3Ķ� � � U[�(Ĩ�ß�ā�á�â�ã€E€F€G€H€IĨ\�æ^_�é]€K^_€n€3_Xcode3BuildSettingsEditor_Xcode3TargetEditorŌ� � e�ÂĄf`€lÔ� ijba€Y€m#AŊ‰…ÕĶĮ“Ķ� � � os�(Ŗ$%r€\€]cŖtuvdef€3_"Xcode3BuildSettingsEditorLocationsTdcm2_Xcode3BuildSettingsEditorŌ� � |�ÂĄ}g€lĶ� � � €‡�(Ļ789:;<€c€d€e€f€g€hĻˆ‰�h�h�h�ghi€5€5€5€k€3Ō� � � €4Ō� � “� €4Ķ� � � –œ�(Ĩ—˜™š›klmnoĨžŸ Ąpqyze€3_-Xcode3ProjectEditorPreviousProjectEditorClass_(Xcode3ProjectEditor.sourceList.splitview_,Xcode3ProjectEditorPreviousTargetEditorClass_,Xcode3ProjectEditorSelectedDocumentLocations_-Xcode3ProjectEditor_Xcode3BuildSettingsEditor_Xcode3BuildSettingsEditorĶ� � � ĒŦ�(Ą́rĄ­s€3_DVTSplitViewItemsŌ� � ą�ĸ˛ŗtw€4Ķ� � � ļš�ĸˇ¸uvĸЁl€R€8]DVTIdentifier_DVTViewMagnitudeĶ� � � ĀÃ�ĸˇ¸uvĸŠŁlx€8#@ƒø�����_Xcode3BuildSettingsEditorŌ� � Ę�ÂĄˁ{€lÔ� ÎĪЁ~}|€m_Bfile://localhost/Users/rorden/Documents/cocoa/dcm2/dcm2.xcodeproj/#Aš:˜ė˛tĶ� � � ÕŲ�(ŖÖÄ؁u€Ŗ4Û܀ŗ‚€3WProject_"Xcode3BuildSettingsEditorLocations_Xcode3BuildSettingsEditorŌ� � â�ÂĄãƒ€lĶ� � � æí�(Ļįčéęë끄…†‡ˆ‰Ļîī�h�h�h�gЁc€5€5€5€k€3_#Collapsed Build Property Categories_Selected Build Properties_$Xcode3BuildSettingsEditorDisplayMode_#Xcode3BuildPropertyValueDisplayMode_Xcode3BuildSettingsEditorMode_"Xcode3BuildPropertyNameDisplayModeŌ� � ü�¯Øũū˙�      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ĄĸŖ¤Ĩϧ¨ŠĒĢŦ­Ž¯°ą˛ŗ´ĩšēģŧŊžŋĀÁÂÃÄÅÆĮČÉĘËĖÍÎĪĐŅŌĶԁ‹Œށ‘’“”•–—˜™š›œžŸ ĄĸŖ¤Ĩρ§¨Ёǁ́Ŧ­ށ¯°ą˛ŗ´ĩļˇ¸šēģŧŊžŋÁÁāŁƁĮȁɁʁˁˁ́΁΁Ёҁԁ́ԁՁցׁ؁؁ځہ܁Ũہ߁āáâãäåæįčéęëėíîīđņōķôõö÷øųúûüũū˙�      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ab€4Ō� éę؁0_Architectures||ADDITIONAL_SDKSŌ� éęہ0_Architectures||ARCHSŌ� éęہ0_Architectures||SDKROOTŌ� éęá0_#Architectures||SUPPORTED_PLATFORMSŌ� éęä0_Architectures||VALID_ARCHSŌ� éęį0_Build Locations||SYMROOTŌ� éęę0_Build Locations||OBJROOTŌ� éęí0_%Build Locations||SHARED_PRECOMPS_DIRŌ� éęđ0_Build Options||BUILD_VARIANTSŌ� éęķ0_Build Options||GCC_VERSIONŌ� éęö0_'Build Options||GENERATE_PROFILING_CODEŌ� éęų0_@Build Options||PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIRŌ� éęü0_)Build Options||RUN_CLANG_STATIC_ANALYZERŌ� éę˙0_2Build Options||SCAN_ALL_SOURCE_FILES_FOR_INCLUDESŌ� éę0_ Build Options||VALIDATE_PRODUCTŌ� éę0_%Code Signing||CODE_SIGN_ENTITLEMENTSŌ� éę0_!Code Signing||CODE_SIGN_IDENTITYŌ� éę 0_,Code Signing||CODE_SIGN_RESOURCE_RULES_PATHŌ� éę0_$Code Signing||OTHER_CODE_SIGN_FLAGSŌ� éę0_Deployment||STRIPFLAGSŌ� éę0_Deployment||ALTERNATE_GROUPŌ� éę0_Deployment||ALTERNATE_OWNERŌ� éę0_Deployment||ALTERNATE_MODEŌ� éę0_(Deployment||ALTERNATE_PERMISSIONS_FILESŌ� éę 0_!Deployment||COMBINE_HIDPI_IMAGESŌ� éę#0_ Deployment||DEPLOYMENT_LOCATIONŌ� éę&0_&Deployment||DEPLOYMENT_POSTPROCESSINGŌ� éę)0_Deployment||INSTALL_GROUPŌ� éę,0_Deployment||INSTALL_OWNERŌ� éę/0_Deployment||INSTALL_MODE_FLAGŌ� éę20_Deployment||DSTROOTŌ� éę50_Deployment||INSTALL_PATHŌ� éę80_%Deployment||MACOSX_DEPLOYMENT_TARGETŌ� éę;0_%Deployment||PRODUCT_DEFINITION_PLISTŌ� éę>0_Deployment||SKIP_INSTALLŌ� éęA0_$Deployment||STRIP_INSTALLED_PRODUCTŌ� éęD0_Deployment||STRIP_STYLEŌ� éęG0_Deployment||SEPARATE_STRIPŌ� éęJ0_Kernel Module||MODULE_NAMEŌ� éęM0_Kernel Module||MODULE_STARTŌ� éęP0_Kernel Module||MODULE_STOPŌ� éęS0_Kernel Module||MODULE_VERSIONŌ� éęV0_Linking||BUNDLE_LOADERŌ� éęY0_%Linking||DYLIB_COMPATIBILITY_VERSIONŌ� éę\0_Linking||DYLIB_CURRENT_VERSIONŌ� éę_0_Linking||DEAD_CODE_STRIPPINGŌ� éęb0_'Linking||LINKER_DISPLAYS_MANGLED_NAMESŌ� éęe0_,Linking||PRESERVE_DEAD_CODE_INITS_AND_TERMSŌ� éęh0_Linking||LD_DYLIB_INSTALL_NAMEŌ� éęk0_Linking||EXPORTED_SYMBOLS_FILEŌ� éęn0_Linking||LD_NO_PIEŌ� éęq0_Linking||INIT_ROUTINEŌ� éęt0_&Linking||LINK_WITH_STANDARD_LIBRARIESŌ� éęw0_Linking||MACH_O_TYPEŌ� éęz0_Linking||ORDER_FILEŌ� éę}0_Linking||OTHER_LDFLAGSŌ� éꀁ0_%Linking||GENERATE_MASTER_OBJECT_FILEŌ� éęƒ0_Linking||PRELINK_LIBSŌ� éꆁ0_Linking||KEEP_PRIVATE_EXTERNSŌ� éꉁ0_!Linking||LD_RUNPATH_SEARCH_PATHSŌ� éęŒ0_Linking||SEPARATE_SYMBOL_EDITŌ� éꏁ0_Linking||PRELINK_FLAGSŌ� éꒁ0_Linking||SECTORDER_FLAGSŌ� éꕁ0_!Linking||UNEXPORTED_SYMBOLS_FILEŌ� éę˜0_Linking||WARNING_LDFLAGSŌ� éꛁ0_Linking||LD_GENERATE_MAP_FILEŌ� éꞁ0_%Packaging||APPLY_RULES_IN_COPY_FILESŌ� éꥁ0_ Packaging||EXECUTABLE_EXTENSIONŌ� é꤁0_Packaging||EXECUTABLE_PREFIXŌ� é꧁0_+Packaging||INFOPLIST_EXPAND_BUILD_SETTINGSŌ� éęǁ0_!Packaging||GENERATE_PKGINFO_FILEŌ� éꭁ0_Packaging||FRAMEWORK_VERSIONŌ� é각0_Packaging||INFOPLIST_FILEŌ� é溁0_.Packaging||INFOPLIST_OTHER_PREPROCESSOR_FLAGSŌ� éęļ0_#Packaging||INFOPLIST_OUTPUT_FORMATŌ� éꚁ0_.Packaging||INFOPLIST_PREPROCESSOR_DEFINITIONSŌ� éęŧ0_#Packaging||INFOPLIST_PREFIX_HEADERŌ� éęŋ0_ Packaging||INFOPLIST_PREPROCESSŌ� éę0_&Packaging||COPYING_PRESERVES_HFS_DATAŌ� éęŁ0_'Packaging||PRIVATE_HEADERS_FOLDER_PATHŌ� éęȁ0_Packaging||PRODUCT_NAMEŌ� éęˁ0_$Packaging||PLIST_FILE_OUTPUT_FORMATŌ� éę΁0_&Packaging||PUBLIC_HEADERS_FOLDER_PATHŌ� éęҁ0_(Packaging||STRINGS_FILE_OUTPUT_ENCODINGŌ� éęԁ0_Packaging||WRAPPER_EXTENSIONŌ� éęׁ0_'Search Paths||ALWAYS_SEARCH_USER_PATHSŌ� éęځ0_%Search Paths||FRAMEWORK_SEARCH_PATHSŌ� éęŨ0_"Search Paths||HEADER_SEARCH_PATHSŌ� éęā0_#Search Paths||LIBRARY_SEARCH_PATHSŌ� éęã0_Search Paths||REZ_SEARCH_PATHSŌ� é濁0_<Search Paths||EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIESŌ� éęé0_<Search Paths||INCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIESŌ� éęė0_'Search Paths||USER_HEADER_SEARCH_PATHSŌ� éęī0_Unit Testing||OTHER_TEST_FLAGSŌ� éęō0_Unit Testing||TEST_AFTER_BUILDŌ� éęõ0_Unit Testing||TEST_HOSTŌ� é渁0_Unit Testing||TEST_RIGŌ� éęû0_$Versioning||CURRENT_PROJECT_VERSIONŌ� éęū0_Versioning||VERSION_INFO_FILEŌ� éę0_%Versioning||VERSION_INFO_EXPORT_DECLŌ� éę0_ Versioning||VERSION_INFO_PREFIXŌ� éę0_ Versioning||VERSION_INFO_SUFFIXŌ� éę 0_Versioning||VERSIONING_SYSTEMŌ� éę 0_!Versioning||VERSION_INFO_BUILDERŌ� éę0_AApple LLVM compiler 4.2 - Code Generation||GCC_FAST_OBJC_DISPATCHŌ� éę0_HApple LLVM compiler 4.2 - Code Generation||CLANG_X86_VECTOR_INSTRUCTIONSŌ� éę0_>Apple LLVM compiler 4.2 - Code Generation||GCC_STRICT_ALIASINGŌ� éę0_IApple LLVM compiler 4.2 - Code Generation||GCC_GENERATE_DEBUGGING_SYMBOLSŌ� éę0_=Apple LLVM compiler 4.2 - Code Generation||GCC_DYNAMIC_NO_PICŌ� éę0_KApple LLVM compiler 4.2 - Code Generation||GCC_GENERATE_TEST_COVERAGE_FILESŌ� éę"0_IApple LLVM compiler 4.2 - Code Generation||GCC_INLINES_ARE_PRIVATE_EXTERNŌ� éę%0_KApple LLVM compiler 4.2 - Code Generation||GCC_INSTRUMENT_PROGRAM_FLOW_ARCSŌ� éę(0_HApple LLVM compiler 4.2 - Code Generation||GCC_ENABLE_KERNEL_DEVELOPMENTŌ� éę+0_3Apple LLVM compiler 4.2 - Code Generation||LLVM_LTOŌ� éę.0_<Apple LLVM compiler 4.2 - Code Generation||GCC_REUSE_STRINGSŌ� éę10_?Apple LLVM compiler 4.2 - Code Generation||GCC_NO_COMMON_BLOCKSŌ� éę40_8Apple LLVM compiler 4.2 - Code Generation||GCC_FAST_MATHŌ� éę70_AApple LLVM compiler 4.2 - Code Generation||GCC_THREADSAFE_STATICSŌ� éę:0_EApple LLVM compiler 4.2 - Code Generation||GCC_SYMBOLS_PRIVATE_EXTERNŌ� éę=0_;Apple LLVM compiler 4.2 - Code Generation||GCC_UNROLL_LOOPSŌ� éę@0_=Apple LLVM compiler 4.2 - Language||GCC_CHAR_IS_UNSIGNED_CHARŌ� éęC0_:Apple LLVM compiler 4.2 - Language||GCC_ENABLE_ASM_KEYWORDŌ� éęF0_;Apple LLVM compiler 4.2 - Language||GCC_C_LANGUAGE_STANDARDŌ� éęI0_?Apple LLVM compiler 4.2 - Language||CLANG_CXX_LANGUAGE_STANDARDŌ� éęL0_5Apple LLVM compiler 4.2 - Language||CLANG_CXX_LIBRARYŌ� éęO0_5Apple LLVM compiler 4.2 - Language||GCC_CW_ASM_SYNTAXŌ� éęR0_6Apple LLVM compiler 4.2 - Language||GCC_INPUT_FILETYPEŌ� éęU0_=Apple LLVM compiler 4.2 - Language||GCC_ENABLE_CPP_EXCEPTIONSŌ� éęX0_7Apple LLVM compiler 4.2 - Language||GCC_ENABLE_CPP_RTTIŌ� éę[0_CApple LLVM compiler 4.2 - Language||GCC_LINK_WITH_DYNAMIC_LIBRARIESŌ� éę^0_>Apple LLVM compiler 4.2 - Language||GCC_ENABLE_OBJC_EXCEPTIONSŌ� éęa0_8Apple LLVM compiler 4.2 - Language||GCC_ENABLE_TRIGRAPHSŌ� éęd0_KApple LLVM compiler 4.2 - Language||GCC_ENABLE_FLOATING_POINT_LIBRARY_CALLSŌ� éęg0_CApple LLVM compiler 4.2 - Language||GCC_USE_INDIRECT_FUNCTION_CALLSŌ� éęj0_CApple LLVM compiler 4.2 - Language||GCC_USE_REGISTER_FUNCTION_CALLSŌ� éęm0_;Apple LLVM compiler 4.2 - Language||CLANG_LINK_OBJC_RUNTIMEŌ� éęp0_KApple LLVM compiler 4.2 - Language||GCC_INCREASE_PRECOMPILED_HEADER_SHARINGŌ� éęs0_9Apple LLVM compiler 4.2 - Language||CLANG_ENABLE_OBJC_ARCŌ� éęv0_0Apple LLVM compiler 4.2 - Language||OTHER_CFLAGSŌ� éęy0_8Apple LLVM compiler 4.2 - Language||OTHER_CPLUSPLUSFLAGSŌ� éę|0_@Apple LLVM compiler 4.2 - Language||GCC_PRECOMPILE_PREFIX_HEADERŌ� éę0_5Apple LLVM compiler 4.2 - Language||GCC_PREFIX_HEADERŌ� éꂁ0_@Apple LLVM compiler 4.2 - Language||GCC_ENABLE_BUILTIN_FUNCTIONSŌ� éꅁ0_=Apple LLVM compiler 4.2 - Language||GCC_ENABLE_PASCAL_STRINGSŌ� éęˆ0_3Apple LLVM compiler 4.2 - Language||GCC_SHORT_ENUMSŌ� éꋁ0_FApple LLVM compiler 4.2 - Language||GCC_USE_STANDARD_INCLUDE_SEARCHINGŌ� éęށ0_ZApple LLVM compiler 4.2 - Preprocessing||GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPSŌ� éꑁ0_HApple LLVM compiler 4.2 - Warning Polices||GCC_WARN_INHIBIT_ALL_WARNINGSŌ� éꔁ0_<Apple LLVM compiler 4.2 - Warning Polices||GCC_WARN_PEDANTICŌ� éꗁ0_GApple LLVM compiler 4.2 - Warning Polices||GCC_TREAT_WARNINGS_AS_ERRORSŌ� éꚁ0_TApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_CHECK_SWITCH_STATEMENTSŌ� éꝁ0_WApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_ABOUT_DEPRECATED_FUNCTIONSŌ� éꠁ0_IApple LLVM compiler 4.2 - Warnings - All languages||CLANG_WARN_EMPTY_BODYŌ� é檁0_UApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_FOUR_CHARACTER_CONSTANTSŌ� éęρ0_CApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_SHADOWŌ� éęЁ0_RApple LLVM compiler 4.2 - Warnings - All languages||CLANG_WARN_CONSTANT_CONVERSIONŌ� éęŦ0_TApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_64_TO_32_BIT_CONVERSIONŌ� é杁0_NApple LLVM compiler 4.2 - Warnings - All languages||CLANG_WARN_ENUM_CONVERSIONŌ� é겁0_MApple LLVM compiler 4.2 - Warnings - All languages||CLANG_WARN_INT_CONVERSIONŌ� éęĩ0_WApple LLVM compiler 4.2 - Warnings - All languages||CLANG_WARN_IMPLICIT_SIGN_CONVERSIONŌ� é긁0_\Apple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETEDŌ� éęģ0_NApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_ABOUT_RETURN_TYPEŌ� éꞁ0_PApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_MISSING_PARENTHESESŌ� éęÁ0_]Apple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERSŌ� éęā0_UApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_ABOUT_MISSING_PROTOTYPESŌ� éęĮ0_RApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_ABOUT_MISSING_NEWLINEŌ� éęʁ0_BApple LLVM compiler 4.2 - Warnings - All languages||WARNING_CFLAGSŌ� éę́0_UApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_ABOUT_POINTER_SIGNEDNESSŌ� éęЁ0_IApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_SIGN_COMPAREŌ� éę́0_]Apple LLVM compiler 4.2 - Warnings - All languages||CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSIONŌ� éęց0_jApple LLVM compiler 4.2 - Warnings - All languages||GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORSŌ� éę؁0_fApple LLVM compiler 4.2 - Warnings - All languages||GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORSŌ� éę܁0_VApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_TYPECHECK_CALLS_TO_PRINTFŌ� éę߁0_PApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_UNINITIALIZED_AUTOSŌ� éęâ0_LApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_UNKNOWN_PRAGMASŌ� éęå0_LApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_UNUSED_FUNCTIONŌ� éęč0_IApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_UNUSED_LABELŌ� éęë0_MApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_UNUSED_PARAMETERŌ� éęî0_IApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_UNUSED_VALUEŌ� éęņ0_LApple LLVM compiler 4.2 - Warnings - All languages||GCC_WARN_UNUSED_VARIABLEŌ� éęô0_KApple LLVM compiler 4.2 - Warnings - C++||CLANG_WARN__EXIT_TIME_DESTRUCTORSŌ� éę÷0_IApple LLVM compiler 4.2 - Warnings - C++||GCC_WARN_NON_VIRTUAL_DESTRUCTORŌ� éęú0_KApple LLVM compiler 4.2 - Warnings - C++||GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONSŌ� éęũ0_OApple LLVM compiler 4.2 - Warnings - C++||GCC_WARN_ABOUT_INVALID_OFFSETOF_MACROŌ� éę�0_EApple LLVM compiler 4.2 - Warnings - C++||CLANG_WARN_CXX0X_EXTENSIONSŌ� éę0_TApple LLVM compiler 4.2 - Warnings - Objective C||CLANG_WARN__DUPLICATE_METHOD_MATCHŌ� éę0_\Apple LLVM compiler 4.2 - Warnings - Objective C||CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIESŌ� éę 0_\Apple LLVM compiler 4.2 - Warnings - Objective C||CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESISŌ� éę 0_TApple LLVM compiler 4.2 - Warnings - Objective C||GCC_WARN_ALLOW_INCOMPLETE_PROTOCOLŌ� éę0_aApple LLVM compiler 4.2 - Warnings - Objective C||GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTORŌ� éę0_\Apple LLVM compiler 4.2 - Warnings - Objective C||CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONSŌ� éę0_OApple LLVM compiler 4.2 - Warnings - Objective C||CLANG_WARN_OBJC_RECEIVER_WEAKŌ� éę0_PApple LLVM compiler 4.2 - Warnings - Objective C||GCC_WARN_STRICT_SELECTOR_MATCHŌ� éę0_NApple LLVM compiler 4.2 - Warnings - Objective C||GCC_WARN_UNDECLARED_SELECTORŌ� éę0_TApple LLVM compiler 4.2 - Warnings - Objective C||CLANG_WARN__ARC_BRIDGE_CAST_NONARCŌ� éę!0_1OSACompile - Build Options||OTHER_OSACOMPILEFLAGSŌ� éę$0_3OSACompile - Build Options||OSACOMPILE_EXECUTE_ONLYŌ� éę'0_<Static Analyzer - Checks||CLANG_ANALYZER_DEADCODE_DEADSTORESŌ� éę*0_,Static Analyzer - Checks||CLANG_ANALYZER_GCDŌ� éę-0_/Static Analyzer - Checks||CLANG_ANALYZER_MALLOCŌ� éę00_BStatic Analyzer - Checks - Objective-C||CLANG_ANALYZER_OBJC_ATSYNCŌ� éę30_EStatic Analyzer - Checks - Objective-C||CLANG_ANALYZER_OBJC_NSCFERRORŌ� éę60_OStatic Analyzer - Checks - Objective-C||CLANG_ANALYZER_OBJC_INCOMP_METHOD_TYPESŌ� éę90_DStatic Analyzer - Checks - Objective-C||CLANG_ANALYZER_OBJC_CFNUMBERŌ� éę<0_GStatic Analyzer - Checks - Objective-C||CLANG_ANALYZER_OBJC_COLLECTIONSŌ� éę?0_HStatic Analyzer - Checks - Objective-C||CLANG_ANALYZER_OBJC_UNUSED_IVARSŌ� éęB0_EStatic Analyzer - Checks - Objective-C||CLANG_ANALYZER_OBJC_SELF_INITŌ� éęE0_HStatic Analyzer - Checks - Objective-C||CLANG_ANALYZER_OBJC_RETAIN_COUNTŌ� éęH0_MStatic Analyzer - Checks - Security||CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTERŌ� éęK0_IStatic Analyzer - Checks - Security||CLANG_ANALYZER_SECURITY_KEYCHAIN_APIŌ� éęN0_XStatic Analyzer - Checks - Security||CLANG_ANALYZER_SECURITY_INSECUREAPI_UNCHECKEDRETURNŌ� éęQ0_SStatic Analyzer - Checks - Security||CLANG_ANALYZER_SECURITY_INSECUREAPI_GETPW_GETSŌ� éęT0_PStatic Analyzer - Checks - Security||CLANG_ANALYZER_SECURITY_INSECUREAPI_MKSTEMPŌ� éęW0_MStatic Analyzer - Checks - Security||CLANG_ANALYZER_SECURITY_INSECUREAPI_RANDŌ� éęZ0_OStatic Analyzer - Checks - Security||CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPYŌ� éę]0_NStatic Analyzer - Checks - Security||CLANG_ANALYZER_SECURITY_INSECUREAPI_VFORKŌ� � _�Ą`d€4Ō� éęd0_Architectures||SDKROOTĶ� � � fg�(  €3Ķ� � � j‰�(¯klmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆgikmoqsuwy{}ƒ…‡‰‹‘“•—™›ŸĄ¯Š‹ŒŽ‘’“”•–—˜™š›œžŸ ĄĸŖ¤ĨϧŖǁ¯´ģŋƁĮˁЁԁ؁Ũáæëđôųũ !€3Ķ‹� ŒŽŦ€�€~h_2x-xcode-log://DE8940B5-77D8-4B25-AB8D-2FDA243B25DEĶ‹� ŒŽą€�€~j_2x-xcode-log://A798E2F3-26A7-4AE3-9475-FB39995E813FĶ‹� ŒŽļ€�€~l_2x-xcode-log://FCD51FD8-2066-4EE7-8D89-41D6CA8898D0Ķ‹� ŒŽģ€�€~n_2x-xcode-log://6F65C15B-E683-4675-98BC-8AFBA4D35F44Ķ‹� ŒŽĀ€�€~p_2x-xcode-log://6E79080F-1833-4306-A652-2B02FBAB28B0Ķ‹� ŒŽŀ�€~r_2x-xcode-log://BD8519B7-D8F8-46C5-AD07-A33E4DFFC4C0Ķ‹� ŒŽʀ�€~t_2x-xcode-log://099490AA-214F-4A7A-9FE1-36D682FF1F0AĶ‹� ŒŽĪ€�€~v_2x-xcode-log://77A3D145-6E28-4835-AA87-DD1A6B1D51D8Ķ‹� ŒŽԀ�€~x_2x-xcode-log://BB46D6DE-798A-4757-B704-82974CAE0540Ķ‹� ŒŽŲ€�€~z_2x-xcode-log://CF176A60-BA06-4C01-ADCC-7A8F8B1C2E88Ķ‹� ŒŽŪ€�€~|_2x-xcode-log://83F584E9-44A3-4907-9E64-CE0314622B75Ķ‹� ŒŽã€�€~~_2x-xcode-log://ACD8AC38-B036-4BE1-B2F7-914CD15D7738Ķ‹� ŒŽč€�€~€_2x-xcode-log://5B39E9E2-7900-4A18-9362-0D63A2B92689Ķ‹� ŒŽí€�€~‚_2x-xcode-log://80030144-613D-4B75-AFE2-8F17B707B39FĶ‹� ŒŽō€�€~„_2x-xcode-log://FE4E244D-536D-47C7-B815-470654BD5E4FĶ‹� ŒŽ÷€�€~†_2x-xcode-log://A447429E-6D3C-4B2C-9CA1-0A735BC110D9Ķ‹� ŒŽü€�€~ˆ_2x-xcode-log://68EABF0E-1A54-46A9-AD4A-FC629ACD223AĶ‹� ŒŽ€�€~Š_2x-xcode-log://656962D3-6963-4EFD-92B0-233AA2754867Ķ‹� ŒŽ€�€~Œ_2x-xcode-log://04063464-ADF8-42A2-BD5F-F536307FFE0AĶ‹� ŒŽ €�€~Ž_2x-xcode-log://8FB8192E-09AF-4727-AD96-581E7A735286Ķ‹� ŒŽ€�€~_2x-xcode-log://E15D5048-B643-4CD2-8C36-9F17FE21E479Ķ‹� ŒŽ€�€~’_2x-xcode-log://FC731DBA-E809-491A-A6EC-C060767C17F0Ķ‹� ŒŽ€�€~”_2x-xcode-log://67BDB151-1D16-4955-93FF-207471A7CC8FĶ‹� ŒŽ€�€~–_2x-xcode-log://ECEBEBF9-8A9A-45D6-A144-F51CD0C20F2CĶ‹� ŒŽ$€�€~˜_2x-xcode-log://723AD21D-79AB-42A4-9012-830C1848138FĶ‹� ŒŽ)€�€~š_2x-xcode-log://204240C4-DAE1-4118-89B1-CA325467727BĶ‹� ŒŽ.€�€~œ_2x-xcode-log://13EEA4E1-C90E-481E-A7FB-1D966C5A24DBĶ‹� ŒŽ3€�€~ž_2x-xcode-log://1DDD844D-EC3E-4263-AB9A-E789FDF2DB1BĶ‹� ŒŽ8€�€~ _2x-xcode-log://45A2FA18-21EC-4A6D-9FC2-8E0A148A6600Ķ‹� ŒŽ=€�€~ĸ_2x-xcode-log://7922D6FE-47A9-424D-A8B1-4CB122E89878Ķ� � � @B�(ĄA¤ĄCĨ€3_SelectedDocumentLocationsŌ� � G�ÂĄHĻ€lÕ� KLMŊŦQ_expandTranscriptYindexPathŠ€�h§ĶST� QUV_NSIndexPathLength_NSIndexPathValue ¨Ō�Ŗ�¤XY[NSIndexPathĸZ�¨[NSIndexPathŌ�Ŗ�¤\]_IDELogDocumentLocationŖ^_�¨_IDELogDocumentLocation_DVTDocumentLocationĶ� � � ac�(ĄbĢĄdŦ€3_SelectedDocumentLocationsŌ� � h�ÂĄi­€lÕ� KLMŊąpŠ€�jŽĶST� QrV ¨Ķ� � � uw�(Ąv°Ąxą€3_SelectedDocumentLocationsŌ� � |�ÂĄ}˛€lÕ� KLMŊЀ�lŗĶST� Q†V¨Ķ� � � ‰‹�(ĄЁĩĄŒļ€3_SelectedDocumentLocationsŌ� � �ÂĄ‘ˇ€lÕ� KLMŊģ˜Š€�n¸ĶSš� Š›V_NSIndexPathDataš¨Ōž� Ÿ WNS.dataB ēŌ�Ŗ�¤ĸŖ]NSMutableDataŖĸ¤�¨VNSDataĶ� � � ύ�(Ąv°ĄЁŧ€3Ō� � Ŧ�ÂĄ­Ŋ€lÕ� KLMŊĀ´Š€�pžĶST� QoV¨Ķ� � � ¸ē�(ĄšĀĄģÁ€3_SelectedDocumentLocationsŌ� � ŋ�ÂĄ€lÕ� KLMŊÆĮŠ€�ÁÄ_2x-xcode-log://BD8519B7-D8F8-46C5-AD07-A33E4DFFC4C0ĶSš� ŠĘVŁ¨Ōž� Í B��ēĶ� � � ĐŅ�(  €3Ķ� � � ÔÖ�(ĄՁČĄׁɀ3_SelectedDocumentLocationsŌ� � Û�ÂĄ܁ʀlÕ� KLMŊĪお€�vËĶST� QUV¨Ķ� � � įé�(ĄšĀĄę̀3Ō� � í�ÂĄî΀lÕ� KLMŊÔõŠ€�xĪĶST� Q†V¨Ķ� � � ųû�(Ąv°ĄüŅ€3Ō� � ˙�ÂĄ�Ō€lÕ� KLMŊ؁Ѐ�zĶĶST� Q†V¨Ķ� � �   �(Ą ÕĄր3_SelectedDocumentLocationsŌ� � �ÂĄ׀lÕ� KLMŊہЀ�|ØĶST� QV ¨Ķ� � � !�(ĄЁĩĄ"ڀ3Ō� � %�ÂĄ&ۀlÕ� KLMŊã-Š€�~ÜĶST� Q/V ¨Ķ� � � 24�(ĄbĢĄ5Ū€3Ō� � 8�ÂĄ9߀lÕ� KLMŊč@Š€�€āĶST� QrV¨Ķ� � � DF�(ĄEâĄGã€3_SelectedDocumentLocationsŌ� � K�ÂĄLä€lÕ� KLMŊíSŠ€�‚åĶST� QrV¨Ķ� � � WY�(ĄXįĄZč€3_SelectedDocumentLocationsŌ� � ^�ÂĄ_é€lÕ� KLMŊōfŠ€�„ęĶST� QV¨Ķ� � � jl�(Ąv°Ąmė€3Ō� � p�ÂĄqí€lÕ� KLMŊwxŠ€�îī_2x-xcode-log://A447429E-6D3C-4B2C-9CA1-0A735BC110D9ĶST� Q{V¨Ķ� � � ~€�(ĄšĀĄņ€3Ō� � „�ÂĄ…ō€lÕ� KLMŊüŒŠ€�ˆķĶST� Q†V¨Ķ� � � ’�(ĄšĀĄ“õ€3Ō� � –�ÂĄ—ö€lÕ� KLMŊžŠ€�÷ø_2x-xcode-log://656962D3-6963-4EFD-92B0-233AA2754867ĶST� Q�­V¨Ķ� � � ŖĨ�(ĄbĢĄρú€3Ō� � Š�ÂĄǁû€lÕ� KLMŊąŠ€�ŒüĶST� QŗV ¨Ķ� � � ļ¸�(ĄˇūĄš˙€3_SelectedDocumentLocationsŌ� � Ŋ�ÂĄž�€lÕ� KLMŊ ŁŠ€�ށĶST� QĮV¨Ķ� � � ĘĖ�(ĄˁĄ́€3_SelectedDocumentLocationsŌ� � Ņ�ÂĄԁ€lÕ� KLMŊ؁Š€�ĶST� Q{V¨Ķ� � � ŨŪ�(  €3Ķ� � � áã�(ĄšĀĄä €3Ō� � į�ÂĄč €lÕ� KLMŊīŠ€�” ĶST� Q†V¨Ķ� � � ķõ�(ĄšĀĄö €3Ō� � ų�ÂĄú€lÕ� KLMŊ�Š€�_2x-xcode-log://ECEBEBF9-8A9A-45D6-A144-F51CD0C20F2CĶST� QV¨Ķ� � �  �(ĄĄ €3_SelectedDocumentLocationsŌ� � �ÂĄ€lÕ� KLMŊ$Š€�˜ĶST� QV¨Ķ� � � �(  €3Ķ� � �  �(ĄĄļ€Ú€3_SelectedDocumentLocationsĶ� � � %'�(ĄšĀĄ(€3Ō� � +�ÂĄ,€lÕ� KLMŊ33Š€�žĶST� Q†V¨Ķ� � � 79�(ĄšĀĄ:€3Ō� � =�ÂĄ>€lÕ� KLMŊ8EŠ€�  ĶST� Q†V¨Ķ� � � IK�(ĄJ"ĄL#€3_SelectedDocumentLocationsŌ� � P�ÂĄQ$€lÕ� KLMŊ=XŠ€�ĸ%ĶST� Q†V¨Ķ� � � \e�¨]^_`abcd'()*+,-.¨f�&�#ijkl�&/€€Ų0123€€8YtargetSDKZisEligible_targetDeviceIsWireless_targetDeviceLocation_targetArchitecture_targetDeviceFamily_targetDeviceModelCode_targetDeviceIsConcrete[macosx10.11_"dvtdevice-local-computer:localhostVx86_64P^MacBookPro11,1Ķ� � � }�Ą~5Ą€6€8]IDENameStringTdcm2Ķ� � � …‰�(Ŗ†‡ˆ89:ŖŠ‹4;T€ŗ€3_0IDEActivityReportCompletionSummaryStringSegments_IDEActivityReportOptions_IDEActivityReportTitleŌ� � ’�Ĩ“”•–—<BFIL€4Ķ� � � šž�(Ŗ›œ=>?ŖŸ 4@A€ŗ€3_&IDEActivityReportStringSegmentPriority_+IDEActivityReportStringSegmentBackSeparator_)IDEActivityReportStringSegmentStringValue#@������c� %� Ķ� � � Š­�(Ŗ›œ=>?ŖŽ¯°CDE€3#@�������Q UBuildĶ� � � ļē�(Ŗ›œ=>?Ŗģŧ4GH€ŗ€3#@������R: Ķ� � � ÂÆ�(Ŗ›œ=>?ŖČɀĨJK€3c� %� Ōž� Í Oabplist00Ô;<X$versionX$objectsY$archiverT$top�† ­$%+147U$nullĶ XNSStringV$class\NSAttributes€€ €YSucceededĶ WNS.keysZNS.objectsĸ€€ĸ€€ € VNSFontWNSColorÔ !"#VNSSizeXNSfFlagsVNSName#@&������ €€_.AppleSystemUIFontBoldŌ&'()Z$classnameX$classesVNSFontĸ(*XNSObjectĶ,- ./0WNSWhite\NSColorSpaceB0�€ Ō&'23WNSColorĸ2*Ō&'56\NSDictionaryĸ5*Ō&'89_NSAttributedStringĸ:*_NSAttributedString_NSKeyedArchiverŅ=>Troot€����#�-�2�7�E�K�R�[�b�o�q�s�u��†�Ž�™�œ�ž� �Ŗ�Ĩ�§�Š�°�¸�Á�Č�Ņ�Ø�á�ä�æ�č!$-4<ILNPU]`eruz’§šŧÁ�������������?��������������ÁēĶ� � � Đ×�(Ļ›ŌĶÕց=MN?OPĻŸ�gÚÛ�g�g@€kQS€k€k€3_"IDEActivityReportStringSegmentType_"IDEActivityReportStringSegmentDate_'IDEActivityReportStringSegmentDateStyle_'IDEActivityReportStringSegmentTimeStyleŌä� åæWNS.time#Aŧ†Œ}RŌ�Ŗ�¤čéVNSDateĸč�¨_12/6/15 at 10:06 AMjŌ� � í�Ą� €€4Ķ� � � ņû�(ŠōķôõöøųúWY[]_aceŠüũū˙�gknortuxy€3Ķ‹� ŒŽ €�€~X_Nfile://localhost/Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/core/nii_dicom.cĶ‹� ŒŽ€�€~Z_Bfile://localhost/Users/rorden/Documents/cocoa/dcm2/dcm2.xcodeproj/Ķ‹� ŒŽ€�€~\_Cfile://localhost/Users/rorden/Documents/cocoa/dcm2/dcm2/nii_dicom.cĶ‹� ŒŽ€�€~^_Sfile://localhost/Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/en.lproj/MainMenu.xibĶ‹� ŒŽ€�€~`_Tfile://localhost/Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/core/nii_dicom_batch.cĶ‹� ŒŽ"€�€~b_Mfile://localhost/Users/rorden/Documents/cocoa/dcm2/dcm2/en.lproj/MainMenu.xibĶ‹� ŒŽ'€�€~d_Efile:///Users/rorden/Documents/cocoa/dcm2/xcode/dcm2/core/nii_dicom.cĶ‹� ŒŽ,€�€~f_Efile://localhost/Users/rorden/Documents/cocoa/dcm2/dcm2/AppDelegate.hĶ� � � /2�ĸ01hiĸ33jj€8UwidthVheight#@‚Ā�����Ķ� � � :=�ĸ01hiĸ>?lm€8#@ü�����#@ƒH�����Ķ� � � DG�ĸ01hiĸ33jj€8Ķ� � � LO�ĸ01hiĸPQpq€8#@“ü�����#@…x�����Ķ� � � VY�ĸ01hiĸÆ[us€8#@†�����Ķ� � � _b�ĸ01hiĸ33jj€8Ķ� � � gj�ĸ01hiĸklvw€8#@đ�����#@„°�����Ķ� � � qt�ĸ01hiĸ33jj€8Ķ� � � y|�ĸ01hiĸ33jj€8Ō� � �Ģ�Ķƒ„…†€ˆ‰k<Œ€}{}‘ƒ…†(‡€4Ķ‹� ŒŽ‘€�€~|_2x-xcode-log://099490AA-214F-4A7A-9FE1-36D682FF1F0AĶ‹� ŒŽ–€�€~~_Ffile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/AppDelegate.mĶ‹� ŒŽ›€�€~€_Cfile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/myWindow.mĶ‹� ŒŽ €�€~‚_Ffile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/AppDelegate.hĶ‹� ŒŽĨ€�€~„_Nfile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/en.lproj/MainMenu.xibĶ‹� ŒŽĒ€�€~†_?file:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/main.mĶ‹� ŒŽ¯€�€~ˆ_Cfile:///Users/rorden/Documents/cocoa/dcm2niix/xcode/dcm2/myWindow.h_NSKeyedArchiverŅŗ´UState€���������"���+���5���:���?��U��[��h��p��{��‚��‡��‰��‹����’��•��—��ž��Õ��â��ķ��õ��÷��ų��û��ũ��˙������������������!��#��%��'��6��w��›��ˇ��æ����$��?��Z��g��|��~��€��‚��„��†��ˆ��Š��Œ��Ž����Ĩ��§��Š��Ģ��­��°��ŗ��ļ��¸��ģ��ž��Ā��Ė��ß��í����0��_��l����Ž��§��ļ��ˇ��Ä��Ų��Û��Ũ��ß��á��ã��å��į��é��ë��í���������� �� ��������������/��H��S��a��l��ƒ��™��­��Đ��ã��đ��õ��÷��ų��ū�����������:��?��L��Q��S��U��Z��\��^��`��j��o��|��ƒ��…��‡��‰����’��”��–��˜��Ä��Ü��ô��ũ�������������������)��2��H��O��\��e��n��}��„��Œ��Ž��—��š��œ��ž��¸��Á��Æ��Ķ��Ú��Ü��Ū��ā��į��é��ë��í��ī��ø��û��ũ��˙�� ��������!��#��%��'��)��8��:��<��>��@��B��D��F��H��U��a��|����Ŧ��Ę��Ö��đ��ũ���� �� ������������!��#��%��'��)��Y��„��ŗ��â����.��;��>��@��C��E��G��[��d��i��k��m��o��|����ƒ��…��Š��Œ��Ž����ž��ą��Õ��Ū��ë��đ��ō��ô��ų��û��ũ��˙����� ����'��*��,��.��?��I��S��_��a��c��e��g��­��ļ��Ã��Ę��Ė��Î��Đ��×��Ų��Û��Ũ��ß��į��î������4��=��@��B��D��Q��^��`��b��d��f��h��j��w��y��{��}������ƒ��…��Ģ��Į��î����4��Y��b��c��e��n��o��q��s��|����Š��Ē��ą��Ņ��į��ô��÷��ų��ü��ū�����%��+��@��S��b��u��‘��“��•��—��™��›��Í��Ö��Ų��Û��Ũ��ę��õ��û��ũ��˙��������.��3��R��[��h��j��l��n��w����’��¨��ą��Ü��á�� ����K��X��`��l��n��p��r��{����†����’��”��–��Ž��ģ��ž��Ā��Ã��Å��Į��Ô��×��Ų��Ü��Ū��ā��í��ô��ö��ø��ú���������� ��5��M��e��n��q��s��u��‚��‘��“��•��—��™��›����Ÿ��Ž��°��˛��´��ļ��¸��ē��ŧ��ž��Ë��×��ō�� �� "�� @�� L�� t�� �� Š�� Œ�� Ž�� �� ’�� ›�� �� Ÿ�� Ą�� Ŗ�� Ĩ�� Á�� ã�� ÷��!��!��!��!��! ��!$��!1��!8��!:��!<��!>��!E��!G��!I��!K��!M��!g��!~��!š��!§��!¨��!Š��!Ģ��!¸��!š��!ē��!ŧ��!Å��!Ō��!Ũ��!ß��!á��!ã��!å��!į��!ō��!ô��!ö��!ø��!ú��!ü��!ū��" ��"��"<��"V��"}��"†��"”��"Ą��"¤��"Ļ��"Š��"Ģ��"­��"ą��"ģ��"Ä��"É��"Ë��"Í��"Ī��"Ō��"ß��"ā��"á��"ã��"ø��"ú��"ü��"ū��#���#��#4��#=��#D��#F��#H��#J��#L��#Y��#[��#]��#_��#l��#y��#{��#}��#��#„��#‘��#“��#•��#—��# ��#­��#¯��#ą��#ŗ��$��$ ��$B��$O��$Q��$S��$U��$^��$a��$c��$e��$|��$��$ĸ��$ŧ��$×��$Ų��$Û��$Ũ��$ß��$ø��%��%��%��%7��%C��%E��%G��%I��%K��%`��%b��%d��%f��%h��%j��%s��%v��%x��%z��%‡��%‰��%‹��%��%’��%›��%¨��%Ē��%Ŧ��%Ž��%Į��%É��%Ë��%Í��%Ī��%Ø��%Ũ��%ß��%á��%ã��%ü��%ū��&���&��&��& ��&��&��&��&-��&/��&1��&3��&5��&7��&@��&j��&o��&™��&ĸ��&Č��&Í��&ķ��'���'��'��'��'��'��'��'��'��',��'.��'0��'2��'4��'6��'8��':��'<��'E��']��'e��'~��'ˆ��'’��'��'Ē��'ŗ��'ĩ��'ˇ��'š��'ģ��'Ä��'Æ��'Č��'Ę��'Ė��'Î��'ė��( ��(,��(E��(F��(O��(P��(R��(_��(b��(d��(g��(i��(k��(��(ˆ��(��(��(‘��(“��( ��(Ĩ��(§��(Š��(Ž��(°��(˛��(´��(Ŋ��(Æ��(Ķ��(Ø��(Ú��(Ü��(á��(ã��(å��(į��(ņ��(ú��)��) ��) ��)��)��)��)'��)4��)7��)9��)<��)>��)@��)T��)]��)b��)d��)f��)h��)u��)z��)|��)~��)ƒ��)…��)‡��)‰��)—��)Ē��)¸��)Á��)Î��)Ķ��)Õ��)×��)Ü��)Ū��)ā��)â��)î��)÷��*��*��* ��* ��*��*��*-��*:��*=��*?��*B��*D��*F��*Z��*g��*j��*l��*o��*q��*s��*|��*��*ƒ��*…��*‡��*”��*™��*›��*��*ĸ��*¤��*Ļ��*¨��*˛��*ģ��*Č��*Í��*Ī��*Ņ��*Ö��*Ø��*Ú��*Ü��*î��*÷��+��+��+��+��+��+��+��+��+ ��+"��++��+0��+3��+6��+8��+E��+J��+L��+N��+S��+U��+X��+Z��+c��+p��+u��+w��+y��+~��+€��+ƒ��+…��+Ž��+›��+Ļ��+Š��+Ŧ��+¯��+˛��+ĩ��+Ā��+Ã��+Æ��+É��+Ė��+Ī��+Ņ��+ô��,��,6��,J��,j��,w��,†��,‰��,Œ��,��,’��,•��,˜��,›��,Ē��,­��,¯��,ą��,´��,ļ��,š��,ŧ��,ž��,Í��,ō��-��-(��-L��-^��-��-¤��-ą��-´��-ˇ��-ē��-Ŋ��-ŋ��-Ņ��-Ū��-á��-ä��-į��-ę��-ė��-ņ��-ū��.��.��. ��.��.��.��.��.��.��.)��..��.1��.4��.9��.<��.?��.A��.F��.S��.V��.Y��.\��._��.a��.n��.q��.t��.w��.z��.|��.‰��.Œ��.��.’��.•��.—��.¤��.Ĩ��.Ļ��.¨��.ĩ��.Î��.Ņ��.Ô��.×��.Ú��.Ũ��.ā��.ã��.æ��.é��.ė��.ī��.ō��/ ��/��/��/��/��/��/��/��/��/ ��/#��/&��/(��/*��/Y��/m��/™��/Ŋ��/Ü��/ü��0��0;��0T��0~��0Ĩ��0ŧ��0Å��0Î��0Ņ��0Ô��0Ũ��0į��0ę��0ë��0ô��1��1 ��1��1��1;��1B��1^��1s��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/��24��2=��2J��2M��2V��2a��2f��2q��2z��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;��3U��3u��3��3§��3Â��3Ų��3æ��3ņ��3ô��3÷��3ú��3ũ��4���4 ��4��4��4��4��4��4��4D��4\��4„��4¸��4Ų��4č��4ņ��4ø��4ú��4ũ��5���5��5 ��5��5 ��5-��54��5:��5C��5F��5I��5K��5`��5b��5e��5g��5j��5m��5Ą��5Ē��5ą��5´��5ˇ��5ē��5ŧ��5É��5Ė��5Ī��5Ņ��5Ú��5Ü��5é��5ë��5í��5ī��5ü��5˙��6��6��6��6'��6J��6W��6j��6m��6p��6s��6v��6y��6|��6��6‚��6…��6˜��6š��6��6 ��6Ŗ��6Ĩ��6¨��6Ģ��6­��6°��6˛��6Í��6Ü��6đ��7��7��7+��7?��7[��7p��7‡��7��7‘��7”��7��7ž��7Ą��7Ē��7Ģ��7­��7ļ��7ˇ��7ē��7Ã��7Ä��7Į��7Ô��7×��7Ų��7Ü��7ß��7á��7ę��7ņ��7ô��7÷��7ú��7ü��8 ��8��8��8��8��8��8��8��82��8;��8H��8M��8O��8Q��8V��8Y��8\��8^��8l��8u��8‚��8‡��8‰��8‹��8��8“��8–��8˜��8Ģ��8¸��8Ŋ��8Ā��8Ã��8Č��8Ę��8Ė��8Î��8ė��9��9L��9N��9W��9Z��9\��9^��9k��9‚��9…��9ˆ��9‹��9Ž��9‘��9”��9—��9š��9��9 ��9Ŗ��9ē��9ŧ��9ŋ��9Á��9Ä��9Į��9Ę��9Í��9Đ��9Ķ��9Õ��9×��9Ų��9đ��: ��:'��:>��:K��:~��:��:°��:Ë��:å��:ü��; ��;��;��;��;!��;$��;'��;*��;-��;<��;?��;B��;E��;H��;K��;N��;Q��;S��;…��;Ą��;Ö��;˙��<2��<l��<–��<Ŗ��<Ļ��<Š��<Ŧ��<¯��<ą��<ž��<Ā��<Â��<Å��=��=��=��=��=��= ��=%��=(��=+��=0��=3��=6��=8��=E��=G��=I��=L��=o��=|��=~��=€��=ƒ��=ŋ��=Ė��=Ī��=Ō��=Õ��=Ø��=Ú��=ö��=˙��>��>��>��>��>1��>3��>6��>9��><��>h��>q��>z��>™��> ��>ŋ��>Õ��>â��>å��>č��>ë��>î��>đ��? ��?��?��?��?��?.��?0��?3��?6��?9��?~��?‡��?”��?™��?œ��?Ÿ��?¤��?§��?Ē��?Ŧ��?š��?ģ��?Ŋ��?Ā��@ ��@��@��@��@!��@h��@u��@~��@��@„��@‡��@Š��@“��@–��@™��@œ��@Ÿ��@Ą��@Ŋ��@ß��@ķ��A ��A��A��A��A��A)��A0��A3��A6��A9��A@��AC��AF��AH��AJ��Ad��A{��A—��A¤��AĨ��AĻ��A¨��Aĩ��Aļ��Aˇ��Aš��AÆ��AŅ��AÔ��A×��AÚ��AŨ��Aā��Aë��Aí��Ađ��Aķ��Aö��Aų��Aû��B��B��B9��BS��Bz��B‡��B”��B—��B™��Bœ��BŸ��BĄ��BŽ��Bˇ��Bē��Bŧ��Bž��BË��BĖ��BÍ��BĪ��BÜ��Bå��Bč��Bë��Bî��Bņ��Bú��Bũ��C���C��C��C��C$��CF��CZ��Cs��C|��C��C‚��C„��Cˆ��C•��Cœ��CŸ��Cĸ��CĨ��CŦ��C¯��C˛��C´��Cļ��CĐ��Cį��D��D��D��D��D��D!��D"��D#��D%��D2��D=��D@��DC��DF��DI��DL��DW��DY��D\��D_��Db��De��Dg��Dr��D~��DĨ��Dŋ��Dæ��Dô��E��E��E��E��E��E��E��E��E"��E#��E$��E&��E3��Eę��Eí��Eđ��Eķ��Eö��Eų��Eü��E˙��F��F��F��F ��F��F��F��F��F��F��F ��F#��F&��F)��F,��F/��F2��F5��F8��F;��F>��FA��FD��FG��FJ��FM��FP��FS��FV��FY��F\��F_��Fb��Fe��Fh��Fk��Fn��Fq��Ft��Fw��Fz��F}��F€��Fƒ��F†��F‰��FŒ��F��F’��F•��F˜��F›��Fž��FĄ��F¤��F§��FĒ��F­��F°��Fŗ��Fļ��Fš��Fŧ��Fŋ��FÂ��FÅ��FČ��FË��FÎ��FŅ��FÔ��F×��FÚ��FŨ��Fā��Fã��Fæ��Fé��Fė��Fī��Fō��Fõ��Fø��G¯��G˛��Gĩ��G¸��Gģ��Gž��GÁ��GÄ��GĮ��GĘ��GÍ��GĐ��GĶ��GÖ��GŲ��GÜ��Gß��Gâ��Gå��Gč��Gë��Gî��Gņ��Gô��G÷��Gú��Gũ��H���H��H��H ��H ��H��H��H��H��H��H��H!��H$��H'��H*��H-��H0��H3��H6��H9��H<��H?��HB��HE��HH��HK��HN��HQ��HT��HW��HZ��H]��H`��Hc��Hf��Hi��Hl��Ho��Hr��Hu��Hx��H{��H~��H��H„��H‡��HŠ��H��H��H“��H–��H™��Hœ��HŸ��Hĸ��HĨ��H¨��HĢ��HŽ��Hą��H´��Hˇ��Hē��HŊ��Hŋ��HĖ��HÎ��HĐ��HĶ��I��I$��I&��I(��I+��Ir��I��I��Iƒ��I†��IĐ��IŨ��Iß��Iá��Iä��J7��JD��JF��JH��JK��Jˆ��J•��J—��J™��Jœ��Jä��Jņ��Jķ��Jõ��Jø��KA��KN��KP��KR��KU��Kž��KĢ��K­��K¯��K˛��Kö��L��L��L��L ��LS��L`��Lb��Ld��Lg��L¯��Lŧ��Lž��LĀ��LÃ��M���M ��M��M��M��M\��Mi��Mk��Mm��Mp��Mē��MĮ��MÉ��MË��MÎ��N��N#��N%��N'��N*��Nr��N��N��Nƒ��N†��NÎ��NÛ��NŨ��Nß��Nâ��O$��O1��O3��O5��O8��O†��O“��O•��O—��Oš��Oâ��Oī��Oņ��Oķ��Oö��P=��PJ��PL��PN��PQ��PĨ��P˛��P´��Pļ��Pš��Q���Q ��Q��Q��Q��QY��Qf��Qh��Qj��Qm��Qļ��QÃ��QÅ��QĮ��QĘ��R ��R��R��R��R��RØ��Rå��Rį��Ré��Rė��S*��S7��S9��S;��S>��S‹��S˜��Sš��Sœ��SŸ��Så��Sō��Sô��Sö��Sų��TA��TN��TP��TR��TU��T��TĒ��TŦ��TŽ��Tą��Tķ��U���U��U��U��UP��U]��U_��Ua��Ud��UĢ��U¸��Uē��Uŧ��Uŋ��V ��V��V��V��V��Vf��Vs��Vu��Vw��Vz��VÂ��VĪ��VŅ��VĶ��VÖ��WW��Wd��Wf��Wh��Wk��W˛��Wŋ��WÁ��WÃ��WÆ��X ��X��X��X��X!��Xf��Xs��Xu��Xw��Xz��Xˇ��XÄ��XÆ��XČ��XË��Y��Y%��Y'��Y)��Y,��Yk��Yx��Yz��Y|��Y��YĀ��YÍ��YĪ��YŅ��YÔ��Z"��Z/��Z1��Z3��Z6��Zs��Z€��Z‚��Z„��Z‡��ZØ��Zå��Zį��Zé��Zė��[>��[K��[M��[O��[R��[š��[§��[Š��[Ģ��[Ž��[ę��[÷��[ų��[û��[ū��\C��\P��\R��\T��\W��\™��\Ļ��\¨��\Ē��\­��\é��\ö��\ø��\ú��\ũ��]D��]Q��]S��]U��]X��]›��]¨��]Ē��]Ŧ��]¯��]ô��^��^��^��^��^N��^[��^]��^_��^b��^¨��^ĩ��^ˇ��^š��^ŧ��_��_��_��_��_��_d��_q��_s��_u��_x��_Ä��_Ņ��_Ķ��_Õ��_Ø��`��`,��`.��`0��`3��`|��`‰��`‹��`��`��`×��`ä��`æ��`č��`ë��a.��a;��a=��a?��aB��an��a{��a}��a��a‚��aĖ��aŲ��aÛ��aŨ��aā��b(��b5��b7��b9��b<��b€��b��b��b‘��b”��bŨ��bę��bė��bî��bņ��c9��cF��cH��cJ��cM��c•��cĸ��c¤��cĻ��cŠ��cņ��cū��d���d��d��dŒ��d™��d›��d��d ��dį��dô��dö��dø��dû��e=��eJ��eL��eN��eQ��e‹��e˜��eš��eœ��eŸ��eí��eú��eü��eū��f��fI��fV��fX��fZ��f]��fĨ��f˛��f´��fļ��fš��fû��g��g ��g ��g��g\��gi��gk��gm��gp��g¸��gÅ��gĮ��gÉ��gĖ��hL��hY��h[��h]��h`��h¨��hĩ��hˇ��hš��hŧ��hø��i��i��i ��i ��iH��iU��iW��iY��i\��i¤��ią��iē��iŊ��iĀ��iÃ��iÆ��iĪ��iŌ��iÕ��i×��iÚ��iÜ��i÷��j��j,��jT��j]��jj��ju��j‚��j‹��jŽ��j‘��j”��j—��j ��jŖ��jĻ��j¨��jĢ��j­��jČ��jī��jũ��k%��k.��k:��kA��kN��kW��kZ��k]��k`��kc��kl��ko��kr��kt��kw��ky��k”��kģ��kÉ��kņ��kú��l��l��l��l'��l*��l-��l0��l3��l<��l?��lB��lD��lG��lI��ld��l‹��l™��lÁ��lĘ��lĶ��lā��lé��lė��lī��lō��lõ��lū��m��m��m��m ��m ��m&��mM��m[��mƒ��mŒ��m™��mĨ��m˛��mģ��mž��mÁ��mÄ��mĮ��mĐ��mĶ��mÖ��mØ��mÛ��mŨ��mø��n��n-��nU��n^��nh��nu��n~��n��n„��n‡��nŠ��n“��n–��n™��n›��nž��n ��nģ��nâ��nđ��o��o!��o.��o8��oE��oN��oQ��oT��oW��oZ��oc��of��oi��ok��on��op��o‹��o˛��oĀ��oč��oņ��oū��p ��p��p��p"��p%��p(��p+��p4��p7��p:��p<��p?��pA��p\��pƒ��p‘��pš��pÂ��pË��pÔ��pá��pę��pí��pđ��pķ��pö��p˙��q��q��q��q ��q ��q'��qN��q\��q„��q��q—��q¤��q­��q°��qŗ��qļ��qš��qÂ��qÅ��qČ��qĘ��qÍ��qĪ��qØ��qä��qņ��qú��qũ��r���r��r��r��r��r��r��r��r��r7��r^��rl��r”��r��rŠ��rŗ��rĀ��rÉ��rĖ��rĪ��rŌ��rÕ��rŪ��rá��rä��ræ��ré��rë��rô��rū��s ��s��s��s��s��s ��s)��s,��s/��s1��s4��s6��sQ��sx��s†��sŽ��sˇ��sÅ��sĐ��sŨ��sæ��sé��sė��sī��sō��sû��sū��t��t��t��t��t��t��t*��t3��t6��t9��t<��t?��tH��tK��tN��tP��tS��tU��tp��t—��tĨ��tÍ��tÖ��tã��tđ��tų��tü��t˙��u��u��u��u��u��u��u��u��u$��u0��u=��uF��uI��uL��uO��uR��u[��u^��ua��uc��uf��uh��uq��uz��u‡��u��u“��u–��u™��uœ��uĨ��u¨��uĢ��u­��u°��u˛��uÍ��uô��v��v*��v3��vA��vL��vY��vb��ve��vh��vk��vn��vw��vz��v}��v��v‚��v„��v��vš��v§��v°��vŗ��vļ��vš��vŧ��vÅ��vČ��vË��vÍ��vĐ��vŌ��vÛ��vį��vô��vũ��w���w��w��w ��w��w��w��w��w��w��w(��w5��w@��wM��wV��wY��w\��w_��wb��wk��wn��wq��ws��wv��wx��w��w��wš��wŖ��wĻ��wŠ��wŦ��w¯��w¸��wģ��wž��wĀ��wÃ��wÅ��wā��x��x��x=��xF��xP��x]��xf��xi��xl��xo��xr��x{��x~��x��xƒ��x†��xˆ��x‘��xœ��xĻ��xŗ��xŧ��xŋ��xÂ��xÅ��xČ��xŅ��xÔ��x×��xŲ��xÜ��xŪ��xų��y ��y.��yV��y_��yh��yq��y~��y‡��yŠ��y��y��y“��yœ��yŸ��yĸ��y¤��y§��yŠ��yÄ��yë��yų��z!��z*��z6��zA��zN��zW��zZ��z]��z`��zc��zl��zo��zr��zt��zw��zy��z”��zģ��zÉ��zņ��zú��{��{��{��{��{��{"��{%��{.��{1��{4��{6��{9��{;��{V��{}��{‹��{ŗ��{ŧ��{Č��{Ō��{ß��{č��{ë��{î��{ņ��{ô��{ũ��|���|��|��|��| ��|��|��|&��|3��|<��|?��|B��|E��|H��|Q��|T��|W��|Y��|\��|^��|g��|t��|��|Š��|��|��|“��|–��|Ÿ��|ĸ��|Ĩ��|§��|Ē��|Ŧ��|ĩ��|Â��|Ī��|Ø��|Û��|Ū��|á��|ä��|í��|đ��|ķ��|õ��|ø��|ú��}��}<��}J��}r��}{��}‡��}‘��}ž��}§��}Ē��}­��}°��}ŗ��}ŧ��}ŋ��}Â��}Ä��}Į��}É��}ä��~ ��~��~A��~J��~V��~c��~l��~o��~r��~u��~x��~��~„��~‡��~‰��~Œ��~Ž��~—��~Ŗ��~°��~š��~ŧ��~ŋ��~Â��~Å��~Î��~Ņ��~Ô��~Ö��~Ų��~Û��~ä��~í��~ú������ �� ���������� ��#��%��.��7��D��M��P��S��V��Y��b��e��h��j��m��o��Š��ą��ŋ��į��đ��ų��€��€��€��€��€��€��€$��€'��€*��€,��€/��€1��€:��€C��€P��€Y��€\��€_��€b��€e��€n��€q��€t��€v��€y��€{��€„��€��€��€Ļ��€Š��€Ŧ��€¯��€˛��€ģ��€ž��€Á��€Ã��€Æ��€Č��€Ņ��€Ũ��€ę��€ķ��€ö��€ų��€ü��€˙���� ����������0��W��e����–��Ą��Ē��ˇ��Ā��Ã��Æ��É��Ė��Õ��Ø��Û��Ũ��ā��â��ë��ų��‚��‚��‚��‚��‚!��‚$��‚'��‚0��‚3��‚6��‚8��‚;��‚=��‚F��‚O��‚X��‚e��‚n��‚q��‚t��‚w��‚z��‚ƒ��‚†��‚‰��‚‹��‚Ž��‚��‚™��‚Ļ��‚°��‚Ŋ��‚Æ��‚É��‚Ė��‚Ī��‚Ō��‚Û��‚Ū��‚á��‚ã��‚æ��‚č��‚ņ��‚û��ƒ��ƒ��ƒ��ƒ��ƒ��ƒ��ƒ&��ƒ)��ƒ,��ƒ.��ƒ1��ƒ3��ƒ<��ƒJ��ƒU��ƒb��ƒk��ƒn��ƒq��ƒt��ƒw��ƒ€��ƒƒ��ƒ†��ƒˆ��ƒ‹��ƒ��ƒ–��ƒŸ��ƒŦ��ƒĩ��ƒ¸��ƒģ��ƒž��ƒÁ��ƒĘ��ƒÍ��ƒĐ��ƒŌ��ƒÕ��ƒ×��ƒō��„��„'��„O��„X��„e��„n��„{��„„��„‡��„Š��„��„��„™��„œ��„Ÿ��„Ą��„¤��„Ļ��„¯��„¸��„Á��„Î��„×��„Ú��„Ũ��„ā��„ã��„ė��„ī��„ō��„ô��„÷��„ų��…��… ��…��…!��…*��…-��…0��…3��…6��…?��…B��…E��…G��…J��…L��…g��…Ž��…œ��…Ä��…Í��…Ö��…ã��…ė��…ī��…ō��…õ��…ø��†��†��†��† ��† ��†��†��†$��†.��†;��†D��†G��†J��†M��†P��†Y��†\��†_��†a��†d��†f��†o��†x��†‚��†��†˜��†›��†ž��†Ą��†¤��†­��†°��†ŗ��†ĩ��†¸��†ē��†Ã��†Ė��†Ų��†â��†å��†č��†ë��†î��†÷��†ú��†ũ��†˙��‡��‡��‡ ��‡��‡&��‡/��‡2��‡5��‡8��‡;��‡D��‡G��‡J��‡L��‡O��‡Q��‡Z��‡g��‡r��‡��‡ˆ��‡‹��‡Ž��‡‘��‡”��‡��‡ ��‡Ŗ��‡Ĩ��‡¨��‡Ē��‡ŗ��‡Á��‡Ė��‡Ų��‡â��‡å��‡č��‡ë��‡î��‡÷��‡ú��‡ũ��‡˙��ˆ��ˆ��ˆ��ˆF��ˆT��ˆ|��ˆ…��ˆ“��ˆŸ��ˆŦ��ˆĩ��ˆ¸��ˆģ��ˆž��ˆÁ��ˆĘ��ˆÍ��ˆĐ��ˆŌ��ˆÕ��ˆ×��ˆā��ˆė��ˆö��‰��‰ ��‰��‰��‰��‰��‰!��‰$��‰'��‰)��‰,��‰.��‰7��‰B��‰K��‰X��‰a��‰d��‰g��‰j��‰m��‰v��‰y��‰|��‰~��‰��‰ƒ��‰ž��‰Å��‰Ķ��‰û��Š��Š ��Š��Š$��Š-��Š0��Š3��Š6��Š9��ŠB��ŠE��ŠH��ŠJ��ŠM��ŠO��ŠX��Ša��Šn��Šw��Šz��Š}��Š€��Šƒ��ŠŒ��Š��Š’��Š”��Š—��Š™��Šĸ��ŠŽ��Šģ��ŠÄ��ŠĮ��ŠĘ��ŠÍ��ŠĐ��ŠŲ��ŠÜ��Šß��Šá��Šä��Šæ��Šī��Šø��‹��‹��‹��‹��‹��‹��‹#��‹&��‹)��‹+��‹.��‹0��‹9��‹E��‹R��‹[��‹^��‹a��‹d��‹g��‹p��‹s��‹v��‹x��‹{��‹}��‹˜��‹ŋ��‹Í��‹õ��‹ū��Œ��Œ��Œ��Œ&��Œ)��Œ,��Œ/��Œ2��Œ;��Œ>��ŒA��ŒC��ŒF��ŒH��ŒQ��Œ[��Œf��Œs��Œ|��Œ��Œ‚��Œ…��Œˆ��Œ‘��Œ”��Œ—��Œ™��Œœ��Œž��Œ§��Œŗ��ŒŊ��ŒĘ��ŒĶ��ŒÖ��ŒŲ��ŒÜ��Œß��Œč��Œë��Œî��Œđ��Œķ��Œõ��Œū�� ����$��-��0��3��6��9��B��E��H��J��M��O��X��a��j��w��€��ƒ��†��‰��Œ��•��˜��›���� ��ĸ��Ģ��¸��Å��Î��Ņ��Ô��×��Ú��ã��æ��é��ë��î��đ��ų��Ž��Ž��Ž!��Ž*��Ž-��Ž0��Ž3��Ž6��Ž?��ŽB��ŽE��ŽG��ŽJ��ŽL��ŽU��Ža��Žn��Žw��Žz��Ž}��Ž€��Žƒ��ŽŒ��Ž��Ž’��Ž”��Ž—��Ž™��Žĸ��ŽŦ��Žš��ŽÂ��ŽÅ��ŽČ��ŽË��ŽÎ��Ž×��ŽÚ��ŽŨ��Žß��Žâ��Žä��Ž˙��&��4��\��e��q��|��‰��’��•��˜��›��ž��§��Ē��­��¯��˛��´��Ŋ��É��Ö��ß��â��å��č��ë��ô��÷��ú��ü��˙���� ����"��/��8��;��>��A��D��M��P��S��U��X��Z��c��q��|��‰��’��•��˜��›��ž��§��Ē��­��¯��˛��´��Ŋ��Į��Ņ��Ū��į��ę��í��đ��ķ��ü��˙��‘��‘��‘��‘ ��‘��‘ ��‘+��‘8��‘A��‘D��‘G��‘J��‘M��‘V��‘Y��‘\��‘^��‘a��‘c��‘l��‘x��‘…��‘Ž��‘‘��‘”��‘—��‘š��‘Ŗ��‘Ļ��‘Š��‘Ģ��‘Ž��‘°��‘š��‘Â��‘Ī��‘Ø��‘Û��‘Ū��‘á��‘ä��‘í��‘đ��‘ķ��‘õ��‘ø��‘ú��’��’ ��’��’#��’&��’)��’,��’/��’8��’;��’>��’@��’C��’E��’N��’Y��’f��’o��’r��’u��’x��’{��’„��’‡��’Š��’Œ��’��’‘��’š��’Ŗ��’°��’š��’ŧ��’ŋ��’Â��’Å��’Î��’Ņ��’Ô��’Ö��’Ų��’Û��’ä��’ņ��’ū��“��“ ��“ ��“��“��“��“��“"��“$��“'��“)��“2��“=��“F��“S��“\��“_��“b��“e��“h��“q��“t��“w��“y��“|��“~��“™��“Ā��“Î��“ö��“˙��” ��”��” ��”)��”,��”/��”2��”5��”>��”A��”D��”F��”I��”K��”T��”`��”m��”p��”s��”v��”y��”{��”ˆ��”Š��”Œ��”��”ā��”í��”ø��”û��”ū��•��•��•��•��•��•��•��•��• ��•"��•>��•`��•ž��•˛��•Ë��•Ô��•×��•Ų��•Û��•č��•ī��•ō��•õ��•ø��•˙��–��–��–��– ��–#��–:��–V��–c��–d��–e��–g��–t��–u��–v��–x��–…��–Ž��–‘��–”��–—��–š��–Ŗ��–Ĩ��–¨��–Ģ��–Ž��–°��–ģ��–Į��–á��—��—��—��—!��—#��—%��—2��—3��—4��—6��—C��—J��—M��—O��—R��—Y��—\��—_��—b��—d��—q��—s��—u��—x��—š��—Æ��—Č��—Ę��—Í��˜��˜��˜ ��˜#��˜&��˜)��˜,��˜/��˜:��˜=��˜@��˜C��˜F��˜I��˜K��˜{��˜Ļ��˜Õ��™��™4��™P��™]��™`��™c��™f��™i��™k��™��™ˆ��™��™��™“��™•��™ĸ��™§��™Š��™Ģ��™°��™ŗ��™ĩ��™ˇ��™¸��™Å��™Ę��™Ė��™Î��™Ķ��™Ö��™Ų��™Û��™ä��š���š ��š ��š��š��š"��š%��š(��š+��š-��šx��š��šŽ��š•��š˜��š›��šž��šĨ��š¨��šĒ��š­��š¯��šļ��šŊ��šâ��šū��›��› ��› ��›��›��›)��›,��›/��›2��›5��›8��›;��›H��›K��›N��›P��›R��›T��›V��›X��›~��›š��›Á��›į��œ��œ,��œ5��æ��é��ė��ī��ō��õ��ø��û��ū��ž��ž��ž��ž ��ž ��ž��ž��ž��ž��ž��ž��ž"��ž%��ž(��ž+��ž.��ž1��ž4��ž7��ž:��ž=��ž@��žC��žF��žI��žL��žO��žR��žU��žX��ž[��ž^��ža��žd��žg��žj��žm��žp��žs��žv��žy��ž|��ž��ž‚��ž…��žˆ��ž‹��žŽ��ž‘��ž”��ž—��žš��ž��ž ��žŖ��žĻ��žŠ��žŦ��ž¯��ž˛��žĩ��ž¸��žģ��žž��žÁ��žÄ��žĮ��žĘ��žÍ��žĐ��žĶ��žÖ��žŲ��žÜ��žß��žâ��žå��žč��žë��žî��žņ��žô��ž÷��žú��žũ��Ÿ���Ÿ��Ÿ��Ÿ ��Ÿ ��Ÿ��Ÿ��Ÿ��Ÿ��Ÿ��Ÿ��Ÿ!��Ÿ$��Ÿ'��Ÿ*��Ÿ-��Ÿ0��Ÿ3��Ÿ6��Ÿ9��Ÿ<��Ÿ?��ŸB��ŸE��ŸH��ŸK��ŸN��ŸQ��ŸT��ŸW��ŸZ��Ÿ]��Ÿ`��Ÿc��Ÿf��Ÿi��Ÿl��Ÿo��Ÿr��Ÿu��Ÿx��Ÿ{��Ÿ~��Ÿ��Ÿ„��Ÿ‡��ŸŠ��Ÿ��Ÿ��Ÿ“��Ÿ–��Ÿ™��Ÿœ��ŸŸ��Ÿĸ��ŸĨ��Ÿ¨��ŸĢ��ŸŽ��Ÿą��Ÿ´��Ÿˇ��Ÿē��ŸŊ��ŸĀ��ŸÃ��ŸÆ��ŸÉ��ŸĖ��ŸĪ��ŸŌ��ŸÕ��ŸØ��ŸÛ��ŸŪ��Ÿá��Ÿä��Ÿį��Ÿę��Ÿí��Ÿđ��Ÿķ��Ÿö��Ÿų��Ÿü��Ÿ˙�� �� �� ��  �� �� �� �� �� �� ��  �� #�� &�� )�� ,�� /�� 2�� 5�� 8�� ;�� >�� A�� D�� G�� J�� M�� P�� S�� V�� Y�� \�� _�� b�� e�� h�� k�� m�� v�� y�� ›�� ¤�� §�� ŋ�� Č�� Ë�� å�� î�� ņ��Ą��Ą ��Ą#��ĄA��ĄJ��ĄM��Ąi��Ąr��Ąu��Ą‘��Ąš��Ą��ĄÅ��ĄÎ��ĄŅ��Ąō��Ąû��Ąū��ĸ��ĸ%��ĸ(��ĸR��ĸ[��ĸ^��ĸĄ��ĸĒ��ĸ­��ĸŲ��ĸâ��ĸå��Ŗ��Ŗ#��Ŗ&��ŖI��ŖR��ŖU��Ŗ}��Ŗ†��Ŗ‰��Ŗ­��Ŗļ��Ŗš��Ŗč��Ŗņ��Ŗô��¤��¤$��¤'��¤A��¤J��¤M��¤l��¤u��¤x��¤—��¤ ��¤Ŗ��¤Á��¤Ę��¤Í��¤ø��Ĩ��Ĩ��Ĩ(��Ĩ1��Ĩ4��ĨW��Ĩ`��Ĩc��ĨŒ��Ĩ•��Ĩ˜��Ĩĩ��Ĩž��ĨÁ��ĨŪ��Ĩį��Ĩę��Ļ ��Ļ��Ļ��Ļ.��Ļ7��Ļ:��ĻV��Ļ_��Ļb��ϊ��Ļ“��Ļ–��Ļž��ĻĮ��ĻĘ��Ļæ��Ļī��Ļō��§��§"��§%��§@��§I��§L��§j��§s��§v��§”��§��§ ��§ŋ��§Č��§Ë��§é��§ō��§õ��¨��¨��¨"��¨<��¨E��¨H��¨p��¨y��¨|��¨ž��¨§��¨Ē��¨Ę��¨Ķ��¨Ö��Š���Š ��Š ��Š;��ŠD��ŠG��Ši��Šr��Šu��Š—��Š ��ŠŖ��Šš��ŠÂ��ŠÅ��ŠŪ��Šį��Šę��Ē��Ē��Ē��Ē7��Ē@��ĒC��ĒZ��Ēc��Ēf��Ē€��lj��nj��Ē´��ĒŊ��ĒĀ��ĒŲ��Ēâ��Ēå��Ģ��Ģ��Ģ��Ģ6��Ģ?��ĢB��Ģc��Ģl��Ģo��̉��Ģ’��Ģ•��Ģą��Ģē��ĢŊ��Ģá��Ģę��Ģí��Ŧ ��Ŧ��Ŧ��Ŧ6��Ŧ?��ŦB��Ŧj��Ŧs��Ŧv��Ŧ™��Ŧĸ��ŦĨ��ŦÅ��ŦÎ��ŦŅ��Ŧ˙��­��­ ��­/��­8��­;��­[��­d��­g��­„��­��­��­Á��­Ę��­Í��­ķ��­ü��­˙��Ž0��Ž9��Ž<��Žb��Žk��Žn��Ž‘��ޚ��Ž��ŽÆ��ŽĪ��ŽŌ��Žü��¯��¯��¯#��¯,��¯/��¯V��¯_��¯b��¯‹��¯”��¯—��¯Â��¯Ë��¯Î��¯î��¯÷��¯ú��°$��°-��°0��°X��°a��°d��°‰��°’��°•��°ģ��°Ä��°Į��°é��°ō��°õ��ą4��ą=��ą@��ą��ąˆ��ą‹��ąĩ��ąž��ąÁ��ąã��ąė��ąī��˛��˛��˛��˛8��˛A��˛D��˛^��˛g��˛j��˛‘��˛š��˛��˛ž��˛Į��˛Ę��˛ō��˛û��˛ū��ŗ!��ŗ*��ŗ-��ŗP��ŗY��ŗ\��ŗ}��ŗ†��ŗ‰��ŗ­��ŗļ��ŗš��ŗũ��´��´ ��´T��´]��´`��´Ą��´Ē��´­��´ų��ĩ��ĩ��ĩE��ĩN��ĩQ��ĩŸ��ĩ¨��ĩĢ��ĩ÷��ļ���ļ��ļQ��ļZ��ļ]��ļ¨��ļą��ļ´��ļę��ļķ��ļö��ˇ5��ˇ>��ˇA��ˇƒ��ˇŒ��ˇ��ˇĘ��ˇĶ��ˇÖ��¸��¸#��¸&��¸d��¸m��¸p��¸°��¸š��¸ŧ��¸ų��š��š��šC��šL��šO��š‘��šš��š��šÕ��šŪ��šá��ē��ē"��ē%��ē^��ēg��ēj��ēĒ��ēŗ��ēļ��ēđ��ēų��ēü��ģB��ģK��ģN��ģ��ģ˜��ģ›��ģÖ��ģß��ģâ��ŧ0��ŧ9��ŧ<��ŧ‚��ŧ‹��ŧŽ��ŧÔ��ŧŨ��ŧā��Ŋ��Ŋ'��Ŋ*��Ŋx��Ŋ��Ŋ„��ŊĀ��ŊÉ��ŊĖ��Ŋ˙��ž��ž ��žF��žO��žR��ž•��žž��žĄ��žŲ��žâ��žå��ŋ(��ŋ1��ŋ4��ŋt��ŋ}��ŋ€��ŋļ��ŋŋ��ŋÂ��Ā ��Ā��Ā��Āt��Ā}��Ā€��ĀË��ĀÔ��Ā×��Á��Á��Á"��Ál��Áu��Áx��ÁĪ��ÁØ��ÁÛ��Â5��Â>��ÂA����–��™��Âņ��Âú��Âũ��ÃC��ÃL��ÃO��ä��í��ð��Ä��Ä��Ä��Äd��Äm��Äp��ÄĀ��ÄÉ��ÄĖ��Å&��Å/��Å2��ő��Ś��ŝ��Åî��Å÷��Åú��ÆM��ÆV��ÆY��Æš��ÆÂ��ÆÅ��Į��Į&��Į)��Į~��Į‡��ĮŠ��ĮĪ��ĮØ��ĮÛ��Č3��Č<��Č?��ȋ��Ȕ��ȗ��Č÷��É���É��Ép��Éy��É|��Éå��Éî��Éņ��ĘJ��ĘS��ĘV��ĘŠ��ʲ��Ęĩ��Ë��Ë ��Ë��Ë_��Ëh��Ëk��ˡ��ËĀ��ËÃ��Ė��Ė��Ė��Ėk��Ėt��Ėw��ĖÆ��ĖĪ��ĖŌ��Í ��Í)��Í,��Íx��́��̈́��ÍŌ��ÍÛ��ÍŪ��Î0��Î9��Î<��΄��΍��ΐ��Îį��Îđ��Îķ��ĪR��Ī[��Ī^��ĪŊ��ĪÆ��ĪÉ��Đ ��Đ)��Đ,��А��Й��М��Đû��Ņ��Ņ��ŅY��Ņb��Ņe��ҏ��ŅÁ��ŅÄ��Ō��Ō��Ō!��Ōx��ԁ��Ō„��Ō¸��ŌÁ��ŌÄ��Ōú��Ķ��Ķ��ĶE��ĶN��ĶQ��Ķ€��͉��͌��Ķž��ĶĮ��ĶĘ��Ô��Ô��Ô��Ôc��Ôl��Ôo��ÔÁ��ÔĘ��ÔÍ��Õ��Õ��Õ ��Õj��Õs��Õv��ÕÁ��ÕĘ��ÕÍ��Ö��Ö��Ö!��Öl��Öu��Öx��ÖČ��ÖŅ��ÖÔ��× ��×)��×,��ׇ��א��ד��×é��×ō��×õ��ØH��ØQ��ØT��ؤ��Ø­��ذ��Ų��Ų ��Ų��Ų_��Ųh��Ųk��Ųn��Ųp��Ųy��Ų|��Ų“��Ų ��ŲĄ��Ųĸ��Ų¤��Ųą��Ųŧ��Ųž��ŲĀ��ŲÂ��ŲÄ��ŲÆ��ŲŅ��ŲÔ��ŲÖ��ŲŲ��ŲÜ��ŲŪ��Ųā��Ųü��Ú��Ú��Ú��Ú ��Ú"��Ú3��Ú6��Ú9��Ú;��Ú=��ÚF��ÚS��ÚZ��Ú\��Ú^��Úa��Úh��Úk��Ún��Úq��Ús��ژ��ڝ��Úš��ÚÂ��ÚÅ��ÚČ��ÚĘ��Ú×��Úä��Úæ��Úč��Úę��Úė��Úî��Úđ��Úũ��Û���Û��Û��Û��Û ��Û ��Û ��Û��Û��Û��Û"��Û#��Û%��Û2��Û=��Û@��ÛC��ÛF��ÛI��ÛL��ÛW��ÛZ��Û]��Û`��Ûc��Ûf��Ûh��ۘ��ÛÃ��Ûō��Ü!��ÜQ��Üm��Üz��Ü}��܀��܃��܆��܈��ܜ��ÜĨ��ÜĒ��Ü­��ܰ��ܲ��Üŋ��ÜÄ��ÜĮ��ÜĘ��ÜĪ��ÜŌ��ÜÔ��ÜÖ��Üä��Ü÷��Ũ��Ũ ��Ũ ��Ũ��Ũ��Ũ��Ũ��Ũ��Ũ%��ŨA��ŨJ��ŨM��ŨP��ŨR��Ũc��Ũf��Ũi��Ũl��Ũn��Ũŗ��Ũŧ��ŨÉ��ŨĐ��ŨĶ��ŨÖ��ŨŲ��Ũā��Ũâ��Ũå��Ũč��Ũę��Ũō��Ū��Ū3��Ū<��Ū?��ŪB��ŪD��ŪQ��Ū^��Ūa��Ūd��Ūg��Ūj��Ūm��Ūp��Ū}��Ū€��ۃ��Ū…��Ū‡��Ū‰��Ū‹��Ū��Ūŗ��ŪĪ��Ūö��ß��ß<��ßa��ßj��á��á ��á#��á&��á)��á,��á/��á2��á5��á8��á;��á>��áA��áD��áG��áJ��áM��áP��áS��áV��áY��á\��á_��áb��áe��áh��ák��án��áq��át��áw��áz��á}��á€��áƒ��á†��á‰��áŒ��á��á’��á•��á˜��á›��áž��áĄ��á¤��á§��áĒ��á­��á°��áŗ��áļ��áš��áŧ��áŋ��áÂ��áÅ��áČ��áË��áÎ��áŅ��áÔ��á×��áÚ��áŨ��áā��áã��áæ��áé��áė��áī��áō��áõ��áø��áû��áū��â��â��â��â ��â ��â��â��â��â��â��â��â"��â%��â(��â+��â.��â1��â4��â7��â:��â=��â@��âC��âF��âI��âL��âO��âR��âU��âX��â[��â^��âa��âd��âg��âj��âm��âp��âs��âv��ây��â|��â��â‚��â…��âˆ��â‹��âŽ��â‘��â”��â—��âš��â��â ��âŖ��âĻ��âŠ��âŦ��â¯��â˛��âĩ��â¸��âģ��âž��âÁ��âÄ��âĮ��âĘ��âÍ��âĐ��âĶ��âÖ��âŲ��âÜ��âß��ââ��âå��âč��âë��âî��âņ��âô��â÷��âú��âũ��ã���ã��ã��ã ��ã ��ã��ã��ã��ã��ã��ã��ã!��ã$��ã'��ã*��ã-��ã0��ã3��ã6��ã9��ã<��ã?��ãB��ãE��ãH��ãK��ãN��ãQ��ãT��ãW��ãZ��ã]��ã`��ãc��ãf��ãi��ãl��ão��ãr��ãu��ãx��ã{��ã~��ã��ã„��ã‡��ãŠ��ã��ã��ã“��ã–��ã™��ãœ��ãŸ��ãĸ��ãĨ��ã§��ã°��ãŗ��ãÕ��ãŪ��ãá��ãų��ä��ä��ä��ä(��ä+��äQ��äZ��ä]��ä{��ä„��ä‡��äŖ��äŦ��ä¯��äË��äÔ��ä×��ä˙��å��å ��å,��å5��å8��åV��å_��åb��åŒ��å•��å˜��åÛ��åä��åį��æ��æ��æ��æT��æ]��æ`��æƒ��æŒ��æ��æˇ��æĀ��æÃ��æį��æđ��æķ��į"��į+��į.��įU��į^��įa��į{��į„��į‡��įĻ��į¯��į˛��įŅ��įÚ��įŨ��įû��č��č��č2��č;��č>��čb��čk��čn��č‘��čš��č��čÆ��čĪ��čŌ��čī��čø��čû��é��é!��é$��éE��éN��éQ��éh��éq��ét��é��é™��éœ��éÄ��éÍ��éĐ��éø��ę��ę��ę ��ę)��ę,��ęS��ę\��ę_��ęz��ęƒ��ę†��ę¤��ę­��ę°��ęÎ��ę×��ęÚ��ęų��ë��ë��ë#��ë,��ë/��ëP��ëY��ë\��ëv��ë��ë‚��ëĒ��ëŗ��ëļ��ëØ��ëá��ëä��ė��ė ��ė��ė:��ėC��ėF��ėu��ė~��ė��ėŖ��ėŦ��ė¯��ėŅ��ėÚ��ėŨ��ėķ��ėü��ė˙��í��í!��í$��íM��íV��íY��íq��íz��í}��í”��í��í ��íē��íÃ��íÆ��íî��í÷��íú��î��î��î��î@��îI��îL��îp��îy��î|��î��îĻ��îŠ��îÃ��îĖ��îĪ��îë��îô��î÷��ī��ī$��ī'��īC��īL��īO��īp��īy��ī|��ī¤��ī­��ī°��īĶ��īÜ��īß��ī˙��đ��đ ��đ9��đB��đE��đi��đr��đu��đ•��đž��đĄ��đž��đĮ��đĘ��đû��ņ��ņ��ņ-��ņ6��ņ9��ņj��ņs��ņv��ņœ��ņĨ��ņ¨��ņË��ņÔ��ņ×��ō���ō ��ō ��ō6��ō?��ōB��ō]��ōf��ōi��ō��ō™��ōœ��ōÅ��ōÎ��ōŅ��ōü��ķ��ķ��ķ(��ķ1��ķ4��ķ^��ķg��ķj��ķ’��ķ›��ķž��ķÃ��ķĖ��ķĪ��ķõ��ķū��ô��ô#��ô,��ô/��ôn��ôw��ôz��ôš��ôÂ��ôÅ��ôī��ôø��ôû��õ��õ&��õ)��õK��õT��õW��õr��õ{��õ~��õ˜��õĄ��õ¤��õË��õÔ��õ×��õø��ö��ö��ö,��ö5��ö8��ö[��öd��ög��öŠ��ö“��ö–��öˇ��öĀ��öÃ��öį��öđ��öķ��÷7��÷@��÷C��÷Ž��÷—��÷š��÷Û��÷ä��÷į��ø3��ø<��ø?��ø��øˆ��ø‹��øŲ��øâ��øå��ų1��ų:��ų=��ų‹��ų”��ų—��ųâ��ųë��ųî��ú$��ú-��ú0��úo��úx��ú{��úŊ��úÆ��úÉ��û��û ��û��ûT��û]��û`��û¨��ûą��û´��ûō��ûû��ûū��ü>��üG��üJ��ü‡��ü��ü“��üŅ��üÚ��üŨ��ũ��ũ(��ũ+��ũc��ũl��ũo��ũ§��ũ°��ũŗ��ũė��ũõ��ũø��ū8��ūA��ūD��ū~��ū‡��ūŠ��ūĐ��ūŲ��ūÜ��˙��˙&��˙)��˙d��˙m��˙p��˙ž��˙Į��˙Ę��������b��k��n��Ŧ��ĩ��¸����N�W�Z��–�™�Ô�Ũ�ā�#�,�/�g�p�s�ļ�ŋ�Â�� ��D�M�P�™�ĸ�Ĩ�� ��Y�b�e�¤�­�°�ú���]�f�i�Ã�Ė�Ī��$�'��ˆ�‹�Ņ�Ú�Ũ�2�;�>�•�ž�Ą�ō�û�ū�N�W�Z�´�Ŋ�Ā� � (� +� |� …� ˆ� Û� ä� į� G� P� S� Ģ� ´� ˇ� � � � ]� f� i� Á� Ę� Í� � "� %� …� Ž� ‘� ū� � � s� |� � Ø� á� ä�7�@�C�’�›�ž�í�ö�ų�E�N�Q�Ą�Ē�­�ų���T�]�`�Ž�ˇ�ē����`�i�l�ž�Į�Ę����u�~��ā�é�ė�K�T�W�Ž�ˇ�ē��'�*�‰�’�•�į�đ�ķ�F�O�R�Ŗ�Ŧ�¯����F�O�R�ˆ�‘�”�Ķ�Ü�ß����L�U�X��Ļ�Š�ņ�ú�ũ�O�X�[�ĸ�Ģ�Ž�ø���O�X�[�Ŗ�Ŧ�¯�ú���V�_�b�Ž�ˇ�ē���!�w�€�ƒ�Ö�ß�â�2�;�>��™�œ�í�ö�ų�ü�ū�� �$�1�2�3�5�B��„�‡�Š���“�–�™�œ�Ÿ�ĸ�Ĩ�¨�Ģ�Ž�ą�´�ˇ�ē�Ŋ�Ā�Ã�Æ�É�Ė�Ī�Ō�Õ�Ø�Û��� �#�&�)�,�/�2�5�8�;�>�A�D�G�J�M�P�S�V�Y�\�_�b�e�h�k�n�q�t�v�ƒ�…�‡�Š�ŋ�Ė�Î�Đ�Ķ������Q�^�`�b�e�š�§�Š�Ģ�Ž�ã�đ�ō�ô�÷� ,� 9� ;� =� @� u� ‚� „� †� ‰� ž� Ë� Í� Ī� Ō�!�!�!�!�!�!P�!]�!_�!a�!d�!™�!Ļ�!¨�!Ē�!­�!â�!ī�!ņ�!ķ�!ö�"+�"8�":�"<�"?�"t�"�"ƒ�"…�"ˆ�"Ŋ�"Ę�"Ė�"Î�"Ņ�#�#�#�#�#�#O�#\�#^�#`�#c�#˜�#Ĩ�#§�#Š�#Ŧ�#á�#î�#đ�#ō�#õ�$*�$7�$9�$;�$>�$s�$€�$‚�$„�$‡�$ŧ�$É�$Ë�$Í�$Đ�%�%�%�%�%�%N�%[�%]�%_�%b�%—�%¤�%Ļ�%¨�%Ģ�%ā�%í�%ī�%ņ�%ô�&)�&6�&8�&:�&=�&r�&�&�&ƒ�&†�&ģ�&Č�&Ę�&Ė�&Ī�'�'�'�'�'�'�'�';�'D�'G�'J�'L�'a�'t�'~�'�'ƒ�'„�'‡�'Š�'—�'Ģ�'ž�'Ā�'Ã�'Ė�'Ø�'Ũ�'é�'ō�( �(�(+�(A�(N�(Q�(T�(W�(Z�(\�(x�(�(„�(‡�(‰�(ž�(Ą�(Ŗ�(¤�(§�(Ē�(ˇ�(š�(ŧ�(É�(Ė�(Ī�(Ō�(Õ�(×�(ķ�(ü�(˙�)�)�)�)�)�)�)"�)%�)2�)4�)7�)D�)G�)J�)M�)P�)R�)n�)w�)z�)}�)�)”�)—�)™�)š�)�) �)­�)ŋ�)Â�)Å�)Î�)Ö�)Ų�)Ü�)å�)ķ�)ú�*�*�*�*�*�*�*�*%�*(�*+�*-�*B�*E�*G�*H�*K�*N�*[�*^�*k�*n�*q�*t�*w�*y�*•�*ž�*Ą�*¤�*Ļ�*ģ�*ž�*Ā�*Á�*Ä�*Į�*ü�+ �+ �+�+�+�+�++�+,�+-�+/�+<�+?�+B�+E�+H�+J�+f�+o�+r�+u�+w�+Œ�+�+‘�+’�+•�+˜�+Ĩ�+¨�+ĩ�+¸�+ģ�+ž�+Á�+Ã�+Ė�+Ī�+Ō�+Ô�+é�+ė�+î�+ī�+ō�+õ�,�,�,�,�,�,�,�, �,)�,,�,/�,1�,F�,I�,K�,L�,O�,R�,_�,b�,o�,r�,u�,x�,{�,}�,™�,ĸ�,Ĩ�,¨�,Ē�,ŋ�,Â�,Ä�,Å�,Č�,Ë�,Ø�,Ú�,Ũ�,ę�,í�,đ�,ķ�,ö�,ø�-�-�-�- �-�-!�-#�-$�-'�-*�-7�-9�-<�-I�-L�-O�-R�-U�-W�-`�-c�-f�-h�-}�-€�-‚�-ƒ�-†�-‰�-–�-™�-Ļ�-Š�-Ŧ�-¯�-˛�-´�-Đ�-Ų�-Ü�-ß�-á�-ö�-ų�-û�-ü�-˙�.�.�.�.�."�.%�.(�.+�.-�.I�.R�.U�.X�.Z�.o�.r�.t�.u�.x�.{�.ˆ�.‹�.˜�.›�.ž�.Ą�.¤�.Ļ�.¯�.˛�.ĩ�.ˇ�.Ė�.Ī�.Ņ�.Ō�.Õ�.Ø�/ �/�/�/�/,�//�/2�/5�/8�/:�/C�/F�/I�/K�/`�/c�/e�/f�/i�/l�/y�/|�/‰�/Œ�/�/’�/•�/—�/ �/Ŗ�/Ļ�/¨�/Ŋ�/Ā�/Â�/Ã�/Æ�/É�/ū�0 �0�0�0�0!�0$�0'�0)�02�05�08�0:�0O�0R�0T�0U�0X�0[�0h�0j�0m�0z�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.�10�1E�1H�1J�1K�1N�1Q�1^�1a�1n�1o�1p�1r�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�2Q�2^�2`�2c�2p�2s�2v�2y�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#�30�33�36�39�3<�3>�3G�3J�3M�3O�3d�3g�3i�3j�3m�3p�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ø�4�4�4 �4#�4%�4:�4=�4?�4@�4C�4F�4S�4V�4c�4t�4w�4z�4}�4€�4ƒ�4†�4‰�4Œ�4�4 �4ĸ�4¤�4§�4Ē�4­�4°�4˛�4´�4ž�4É�4â�4ų�5�5#�5;�5T�5`�5…�5Œ�5�5œ�5Š�5Ŧ�5¯�5˛�5ĩ�5ˇ�5Å�5Ę�5×�5Ū�5á�5ä�5į�5î�5ņ�5ô�5ö�5ø�6+�6F�6_�6h�6s�6v�6y�6|�6�6‚�6„�6‘�6˜�6›�6ž�6Ą�6¨�6Ģ�6Ž�6°�6˛�6Û�7 �75�7>�7E�7R�7Y�7\�7_�7b�7i�7l�7o�7r�7t�7}�7�7…�7’�7™�7œ�7Ÿ�7ĸ�7Š�7Ŧ�7¯�7ą�7ŗ�7ŧ�7ŋ�7Ė�7Ķ�7Ö�7Ų�7Ü�7ã�7å�7č�7ë�7í�7ô�7ũ�:b�:e�:r�:�:‚�:…�:ˆ�:‹�:Ž�:‘�:ž�:Ą�:Ŗ�:Ļ�:Š�:Ģ�:­�:¯�:Ô�:ų�;#�;M�;V�;^�;g�;j�;s�;z�;�;•�;—�; �;Ŗ�;Ĩ�;§�;´�;Į�;Ę�;Í�;Đ�;Ķ�;Ö�;Ų�;Ü�;ß�;â�;õ�;ø�;û�;ū�<�<�<�< �< �<�<�<�<!�<#�<&�<w�<„�<†�<ˆ�<‹�<Đ�<Ũ�<ß�<á�<ä�=*�=7�=9�=;�=>�=”�=Ą�=Ŗ�=Ĩ�=¨�=˙�> �>�>�>�>c�>p�>r�>t�>w�>ŋ�>Ė�>Î�>Đ�>Ķ�?�?(�?-�?0�?3�?8�?;�?>�?@�?F�?M�?V�?c�?h�?k�?n�?s�?v�?y�?{�?„�?�?š�?Ÿ�?ĸ�?Ĩ�?Ē�?­�?°�?˛�?ŋ�?Ä�?Į�?Ę�?Ī�?Ō�?Õ�?×�?ā�?é�?ö�?û�?ū�@�@�@ �@ �@�@�@$�@)�@,�@/�@4�@7�@:�@<�@I�@N�@Q�@T�@Y�@\�@_�@a�@j�@s�@€�@…�@ˆ�@‹�@�@“�@–�@˜�@Ĩ�@Ē�@­�@°�@ĩ�@¸�@ģ�@Ŋ�@Æ�@Ũ�@ß�@â�@å�@č�@ë�@î�@ņ�@ô�@÷�@ú�@ũ�@˙�A �A�A�A�AH�AU�AW�AY�A\�AĨ�A˛�A´�Aļ�Aš�A˙�B �B�B�B�B\�Bi�Bk�Bm�Bp�BÁ�BÎ�BĐ�BŌ�BÕ�C�C$�C&�C(�C+�Cq�Cƒ�Cˆ�CŽ������������ĩ�������������C��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������WorkspaceSettings.xcsettings������������������������������������������������������������������������0000664�0000000�0000000�00000000515�13220512030�0037355�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/project.xcworkspace/xcuserdata/rorden.xcuserdatad���������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges</key> <true/> <key>SnapshotAutomaticallyBeforeSignificantChanges</key> <false/> </dict> </plist> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/xcuserdata/����������������������������������������������0000775�0000000�0000000�00000000000�13220512030�0022042�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/xcuserdata/administrator.xcuserdatad/��������������������0000775�0000000�0000000�00000000000�13220512030�0027230�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/xcuserdata/administrator.xcuserdatad/xcschemes/����������0000775�0000000�0000000�00000000000�13220512030�0031212�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2.xcscheme���������������������������������������������������������������������������������������0000664�0000000�0000000�00000006561�13220512030�0033511�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/xcuserdata/administrator.xcuserdatad/xcschemes������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <Scheme version = "1.3"> <BuildAction parallelizeBuildables = "YES" buildImplicitDependencies = "YES"> <BuildActionEntries> <BuildActionEntry buildForTesting = "YES" buildForRunning = "YES" buildForProfiling = "YES" buildForArchiving = "YES" buildForAnalyzing = "YES"> <BuildableReference BuildableIdentifier = "primary" BlueprintIdentifier = "4F9EB64218F333DF00F6A696" BuildableName = "dcm2.app" BlueprintName = "dcm2" ReferencedContainer = "container:dcm2.xcodeproj"> </BuildableReference> </BuildActionEntry> </BuildActionEntries> </BuildAction> <TestAction selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB" shouldUseLaunchSchemeArgsEnv = "YES" buildConfiguration = "Debug"> <Testables> <TestableReference skipped = "NO"> <BuildableReference BuildableIdentifier = "primary" BlueprintIdentifier = "4F9EB66218F333DF00F6A696" BuildableName = "dcm2Tests.octest" BlueprintName = "dcm2Tests" ReferencedContainer = "container:dcm2.xcodeproj"> </BuildableReference> </TestableReference> </Testables> <MacroExpansion> <BuildableReference BuildableIdentifier = "primary" BlueprintIdentifier = "4F9EB64218F333DF00F6A696" BuildableName = "dcm2.app" BlueprintName = "dcm2" ReferencedContainer = "container:dcm2.xcodeproj"> </BuildableReference> </MacroExpansion> </TestAction> <LaunchAction selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB" launchStyle = "0" useCustomWorkingDirectory = "NO" buildConfiguration = "Debug" debugDocumentVersioning = "YES" allowLocationSimulation = "YES"> <BuildableProductRunnable> <BuildableReference BuildableIdentifier = "primary" BlueprintIdentifier = "4F9EB64218F333DF00F6A696" BuildableName = "dcm2.app" BlueprintName = "dcm2" ReferencedContainer = "container:dcm2.xcodeproj"> </BuildableReference> </BuildableProductRunnable> <AdditionalOptions> </AdditionalOptions> </LaunchAction> <ProfileAction shouldUseLaunchSchemeArgsEnv = "YES" savedToolIdentifier = "" useCustomWorkingDirectory = "NO" buildConfiguration = "Release" debugDocumentVersioning = "YES"> <BuildableProductRunnable> <BuildableReference BuildableIdentifier = "primary" BlueprintIdentifier = "4F9EB64218F333DF00F6A696" BuildableName = "dcm2.app" BlueprintName = "dcm2" ReferencedContainer = "container:dcm2.xcodeproj"> </BuildableReference> </BuildableProductRunnable> </ProfileAction> <AnalyzeAction buildConfiguration = "Debug"> </AnalyzeAction> <ArchiveAction buildConfiguration = "Release" revealArchiveInOrganizer = "YES"> </ArchiveAction> </Scheme> �����������������������������������������������������������������������������������������������������������������������������������������������xcschememanagement.plist����������������������������������������������������������������������������0000664�0000000�0000000�00000001066�13220512030�0036047�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/xcuserdata/administrator.xcuserdatad/xcschemes������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>SchemeUserState</key> <dict> <key>dcm2.xcscheme</key> <dict> <key>orderHint</key> <integer>0</integer> </dict> </dict> <key>SuppressBuildableAutocreation</key> <dict> <key>4F9EB64218F333DF00F6A696</key> <dict> <key>primary</key> <true/> </dict> <key>4F9EB66218F333DF00F6A696</key> <dict> <key>primary</key> <true/> </dict> </dict> </dict> </plist> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/xcuserdata/rorden.xcuserdatad/���������������������������0000775�0000000�0000000�00000000000�13220512030�0025641�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/xcuserdata/rorden.xcuserdatad/xcdebugger/����������������0000775�0000000�0000000�00000000000�13220512030�0027760�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Breakpoints.xcbkptlist������������������������������������������������������������������������������0000664�0000000�0000000�00000000133�13220512030�0034270�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/xcuserdata/rorden.xcuserdatad/xcdebugger������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <Bucket type = "1" version = "1.0"> </Bucket> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/xcuserdata/rorden.xcuserdatad/xcschemes/�����������������0000775�0000000�0000000�00000000000�13220512030�0027623�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/xcuserdata/rorden.xcuserdatad/xcschemes/dcm2.xcscheme����0000664�0000000�0000000�00000006700�13220512030�0032174�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <Scheme LastUpgradeVersion = "0610" version = "1.3"> <BuildAction parallelizeBuildables = "YES" buildImplicitDependencies = "YES"> <BuildActionEntries> <BuildActionEntry buildForTesting = "YES" buildForRunning = "YES" buildForProfiling = "YES" buildForArchiving = "YES" buildForAnalyzing = "YES"> <BuildableReference BuildableIdentifier = "primary" BlueprintIdentifier = "4F9EB64218F333DF00F6A696" BuildableName = "dcm2.app" BlueprintName = "dcm2" ReferencedContainer = "container:dcm2.xcodeproj"> </BuildableReference> </BuildActionEntry> </BuildActionEntries> </BuildAction> <TestAction selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES" buildConfiguration = "Debug"> <Testables> <TestableReference skipped = "NO"> <BuildableReference BuildableIdentifier = "primary" BlueprintIdentifier = "4F9EB66218F333DF00F6A696" BuildableName = "dcm2Tests.octest" BlueprintName = "dcm2Tests" ReferencedContainer = "container:dcm2.xcodeproj"> </BuildableReference> </TestableReference> </Testables> <MacroExpansion> <BuildableReference BuildableIdentifier = "primary" BlueprintIdentifier = "4F9EB64218F333DF00F6A696" BuildableName = "dcm2.app" BlueprintName = "dcm2" ReferencedContainer = "container:dcm2.xcodeproj"> </BuildableReference> </MacroExpansion> </TestAction> <LaunchAction selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" launchStyle = "0" useCustomWorkingDirectory = "NO" buildConfiguration = "Debug" ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" allowLocationSimulation = "YES"> <BuildableProductRunnable> <BuildableReference BuildableIdentifier = "primary" BlueprintIdentifier = "4F9EB64218F333DF00F6A696" BuildableName = "dcm2.app" BlueprintName = "dcm2" ReferencedContainer = "container:dcm2.xcodeproj"> </BuildableReference> </BuildableProductRunnable> <AdditionalOptions> </AdditionalOptions> </LaunchAction> <ProfileAction shouldUseLaunchSchemeArgsEnv = "YES" savedToolIdentifier = "" useCustomWorkingDirectory = "NO" buildConfiguration = "Release" debugDocumentVersioning = "YES"> <BuildableProductRunnable> <BuildableReference BuildableIdentifier = "primary" BlueprintIdentifier = "4F9EB64218F333DF00F6A696" BuildableName = "dcm2.app" BlueprintName = "dcm2" ReferencedContainer = "container:dcm2.xcodeproj"> </BuildableReference> </BuildableProductRunnable> </ProfileAction> <AnalyzeAction buildConfiguration = "Debug"> </AnalyzeAction> <ArchiveAction buildConfiguration = "Release" revealArchiveInOrganizer = "YES"> </ArchiveAction> </Scheme> ����������������������������������������������������������������xcschememanagement.plist����������������������������������������������������������������������������0000664�0000000�0000000�00000001066�13220512030�0034460�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�dcm2niix-1.0.20171215/xcode/dcm2.xcodeproj/xcuserdata/rorden.xcuserdatad/xcschemes�������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>SchemeUserState</key> <dict> <key>dcm2.xcscheme</key> <dict> <key>orderHint</key> <integer>0</integer> </dict> </dict> <key>SuppressBuildableAutocreation</key> <dict> <key>4F9EB64218F333DF00F6A696</key> <dict> <key>primary</key> <true/> </dict> <key>4F9EB66218F333DF00F6A696</key> <dict> <key>primary</key> <true/> </dict> </dict> </dict> </plist> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2/�������������������������������������������������������������������0000775�0000000�0000000�00000000000�13220512030�0015703�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2/AppDelegate.h������������������������������������������������������0000664�0000000�0000000�00000002136�13220512030�0020231�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // AppDelegate.h // dcm2 // // Created by Chris Rorden on 4/7/14. // Copyright (c) 2014 Chris Rorden. All rights reserved. // //for NSTextView, turn off non-contiguous layout for background refresh #import <Cocoa/Cocoa.h> #include "./core/nii_dicom_batch.h" @interface AppDelegate : NSObject <NSApplicationDelegate> { struct TDCMopts opts; dispatch_source_t source; #if __has_feature(objc_arc_weak) //automatic #else NSWindow *_window; NSTextView *_theTextView; NSButton *_compressCheck; NSTextField *_outputFilenameEdit; NSButton *_folderButton; #endif } @property (assign) IBOutlet NSButton *compressCheck; @property (assign) IBOutlet NSTextField *outputFilenameEdit; @property (assign) IBOutlet NSButton *folderButton; @property (assign) IBOutlet NSWindow *window; @property (assign) IBOutlet NSTextView *theTextView; - (void) processFile: (NSString*) fname; - (IBAction)outputFolderClick:(id)sender; - (IBAction)par2niiClick:(id)sender; - (IBAction)dicom2niiClick:(id)sender; //- (IBAction)compressCheckClick:(id)sender; - (IBAction)compressCheckClick:(id)sender; @end ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2/AppDelegate.m������������������������������������������������������0000664�0000000�0000000�00000017205�13220512030�0020241�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // AppDelegate.m // dcm2 // // Created by Chris Rorden on 4/7/14. // Copyright (c) 2014 Chris Rorden. All rights reserved. // #import "AppDelegate.h" #include "./core/nii_dicom_batch.h" @implementation AppDelegate #if __has_feature(objc_arc_weak) //automatic #else @synthesize window = _window; @synthesize theTextView = _theTextView; @synthesize compressCheck = _compressCheck; @synthesize outputFilenameEdit = _outputFilenameEdit; @synthesize folderButton = _folderButton; #endif - (void) processFile: (NSString*) fname { //convert folder (DICOM) or file (PAR/REC) to NIFTI format [_theTextView setString: @""];//clear display struct TDCMopts optsTemp; optsTemp = opts; //conversion may change values like the outdir (if not specified) strcpy(optsTemp.indir, [fname cStringUsingEncoding:1]); optsTemp.isVerbose = true; clock_t start = clock(); nii_loadDir (&(optsTemp)); printf("required %fms\n", ((double)(clock()-start))/1000); fflush(stdout); //GUI buffers printf, display all results } - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename { //convert images [self processFile: filename]; return true; } -(void) showExampleFilename { char niiFilename[1024]; nii_createDummyFilename(niiFilename, opts); [self.theTextView setString: [NSString stringWithFormat:@"%s\nVersion %s\n", niiFilename,kDCMvers ]];//clear display //printf("Version %s\n",kDCMvers); //[self.theTextView setString: [NSString stringWithFormat:@"Output images will have names like %s", niiFilename ]];//clear display [self.theTextView setNeedsDisplay:YES]; } - (void)controlTextDidChange:(NSNotification *)notification { //user has changed text field, 1: get desired file name mask NSTextField *textField = [notification object]; //next: display example of what the provided filename mask will generate strcpy(opts.filename, [[textField stringValue] cStringUsingEncoding:1]); [self showExampleFilename]; } -(void)showPrefs { _compressCheck.state = opts.isGz; //NSString *title = [NSString stringWithCString:opts.filename encoding:NSASCIIStringEncoding]; //[_outputFilenameEdit setStringValue:title]; [_outputFilenameEdit setStringValue:[NSString stringWithCString:opts.filename encoding:NSASCIIStringEncoding]]; NSString *outdir = [NSString stringWithCString:opts.outdir encoding:NSASCIIStringEncoding]; if ([outdir length] < 1) [_folderButton setTitle:@"input folder"]; else if ([outdir length] > 40) [_folderButton setTitle:[NSString stringWithFormat:@"%@%@", @"...",[outdir substringFromIndex:[outdir length]-36]]]; else [_folderButton setTitle:outdir]; [self showExampleFilename]; } - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { //re-direct printf statements to appear in the text view component //Here we use Grand Central Dispatch, alternatively we could use NSNotification as described here: // http://stackoverflow.com/questions/2406204/what-is-the-best-way-to-redirect-stdout-to-nstextview-in-cocoa NSPipe* pipe = [NSPipe pipe]; NSFileHandle* pipeReadHandle = [pipe fileHandleForReading]; dup2([[pipe fileHandleForWriting] fileDescriptor], fileno(stdout)); source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, [pipeReadHandle fileDescriptor], 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); dispatch_source_set_event_handler(source, ^{ void* data = malloc(4096); ssize_t readResult = 0; do { errno = 0; readResult = read([pipeReadHandle fileDescriptor], data, 4096); } while (readResult == -1 && errno == EINTR); if (readResult > 0) { //AppKit UI should only be updated from the main thread NSString* stdOutString = [[NSString alloc] initWithBytesNoCopy:data length:readResult encoding:NSUTF8StringEncoding freeWhenDone:YES]; dispatch_async(dispatch_get_main_queue(),^{ [[[_theTextView textStorage] mutableString] appendString:stdOutString]; [_theTextView setNeedsDisplay:YES]; }); [stdOutString release]; } else{free(data);} }); dispatch_resume(source); //read and display preferences const char *appPath = [[[NSBundle mainBundle] bundlePath] UTF8String]; readIniFile (&opts, &appPath); [self showPrefs]; fflush(stdout); //GUI buffers printf, display all results //finally, remove any bizarre options that XCode appends to Edit menu NSMenu* edit = [[[[NSApplication sharedApplication] mainMenu] itemWithTitle: @"Edit"] submenu]; if ([[edit itemAtIndex: [edit numberOfItems] - 1] action] == NSSelectorFromString(@"orderFrontCharacterPalette:")) [edit removeItemAtIndex: [edit numberOfItems] - 1]; if ([[edit itemAtIndex: [edit numberOfItems] - 1] action] == NSSelectorFromString(@"startDictation:")) [edit removeItemAtIndex: [edit numberOfItems] - 1]; if ([[edit itemAtIndex: [edit numberOfItems] - 1] isSeparatorItem]) [edit removeItemAtIndex: [edit numberOfItems] - 1]; #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_6 //NSLog(@"yyyy"); #endif } - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication { return YES; } - (IBAction)compressCheckClick:(id)sender { opts.isGz = _compressCheck.state; [self showExampleFilename]; } - (IBAction)outputFolderClick:(id)sender { //http://stackoverflow.com/questions/5621513/cocoa-select-choose-file-panel NSOpenPanel* openDlg = [NSOpenPanel openPanel]; [openDlg setTitle: @"Select output folder (cancel to use input folder)"]; [openDlg setCanChooseFiles:NO]; [openDlg setCanChooseDirectories:YES]; [openDlg setPrompt:@"Select"]; NSInteger isOKButton = [openDlg runModal]; NSArray* files = [openDlg URLs]; NSString* outdir = @""; if ((isOKButton == NSOKButton) && ([files count] > 0)) outdir = [[files objectAtIndex:0] path]; strcpy(opts.outdir, [outdir cStringUsingEncoding:1]); [self showPrefs]; } - (IBAction)dicom2niiClick:(id)sender { NSOpenPanel* openDlg = [NSOpenPanel openPanel]; [openDlg setTitle: @"Select folder that contains DICOM images"]; [openDlg setCanChooseFiles:NO]; [openDlg setCanChooseDirectories:YES]; [openDlg setPrompt:@"Select"]; if ([openDlg runModal] != NSOKButton ) return; NSArray* files = [openDlg URLs]; if ([files count] < 1) return; [self processFile: [[files objectAtIndex:0] path] ]; } - (IBAction)par2niiClick:(id)sender { #if __has_feature(objc_arc_weak) //NSOpenPanel *panel; #else //NSOpenPanel *panel = [[NSOpenPanel alloc] init]; #endif NSOpenPanel* panel = [NSOpenPanel openPanel]; NSArray* fileTypes = [[NSArray alloc] initWithObjects:@"par", @"PAR", nil]; [panel setTitle: @"Select Philips PAR images"]; panel = [NSOpenPanel openPanel]; [panel setFloatingPanel:YES]; [panel setCanChooseDirectories:NO]; [panel setCanChooseFiles:YES]; [panel setAllowsMultipleSelection:YES]; [panel setAllowedFileTypes:fileTypes]; if ([panel runModal] != NSOKButton) { [fileTypes release]; return; } [fileTypes release]; NSArray* files = [panel URLs]; NSUInteger n = [files count]; if (n < 1) return; for (int i = 0; i < n; i++) [self processFile: [[files objectAtIndex:i] path] ]; } -(void) dealloc { #if __has_feature(objc_arc_weak) //automatic #else [super dealloc]; #endif } -(NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender { saveIniFile (opts); //save preferences return NSTerminateNow; } @end �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2/dcm2-Info.plist����������������������������������������������������0000664�0000000�0000000�00000002566�13220512030�0020507�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>CFBundleDevelopmentRegion</key> <string>en</string> <key>CFBundleDocumentTypes</key> <array> <dict> <key>CFBundleTypeExtensions</key> <array> <string>*</string> </array> <key>CFBundleTypeName</key> <string>NSFilenamesPboardType</string> <key>CFBundleTypeRole</key> <string>None</string> </dict> </array> <key>CFBundleExecutable</key> <string>${EXECUTABLE_NAME}</string> <key>CFBundleIconFile</key> <string>dcm2niigui</string> <key>CFBundleIdentifier</key> <string>www.mricro.com.${PRODUCT_NAME:rfc1034identifier}</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundleName</key> <string>${PRODUCT_NAME}</string> <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> <string>1.0</string> <key>CFBundleSignature</key> <string>????</string> <key>CFBundleVersion</key> <string>1</string> <key>LSMinimumSystemVersion</key> <string>${MACOSX_DEPLOYMENT_TARGET}</string> <key>NSHumanReadableCopyright</key> <string>Copyright Š 2014 Chris Rorden. All rights reserved.</string> <key>NSMainNibFile</key> <string>MainMenu</string> <key>NSPrincipalClass</key> <string>NSApplication</string> </dict> </plist> ������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2/dcm2-Prefix.pch����������������������������������������������������0000664�0000000�0000000�00000000213�13220512030�0020453�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // Prefix header for all source files of the 'dcm2' target in the 'dcm2' project // #ifdef __OBJC__ #import <Cocoa/Cocoa.h> #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2/en.lproj/����������������������������������������������������������0000775�0000000�0000000�00000000000�13220512030�0017432�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2/en.lproj/Credits.rtf�����������������������������������������������0000664�0000000�0000000�00000004216�13220512030�0021547�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;} {\colortbl;\red255\green255\blue255;} \paperw9840\paperh8400 \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural \f0\b\fs24 \cf0 About: \b0 \ This software is designed to convert medical images stored in complicated formats (DICOM or Philips proprietary PAR/REC) to the simple NIfTI format used by popular scientific tools. For DICOM images, simply drop the folder containing the files onto this software. For Philips PAR/REC images, drag and drop the file you wish to convert. This software attempts to preserve spatial orientation and position. However, you should be careful to validate and inspect images. In particular, verify the orientation of 2D slices (relative to 3D volumes) and diffusion tensor gradient directions. \ \ \b License: \b0 \ Copyright (c) 2014, Chris Rorden\ 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. \ \ This software is provided by the copyright holders and contributors "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.\ \ For more information, see www.mricro.com} ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2/en.lproj/InfoPlist.strings�����������������������������������������0000664�0000000�0000000�00000000055�13220512030�0022754�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Localized versions of Info.plist keys */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2/en.lproj/MainMenu.xib����������������������������������������������0000664�0000000�0000000�00000153720�13220512030�0021657�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="8.00"> <data> <int key="IBDocument.SystemTarget">1060</int> <string key="IBDocument.SystemVersion">13E28</string> <string key="IBDocument.InterfaceBuilderVersion">3084</string> <string key="IBDocument.AppKitVersion">1265.21</string> <string key="IBDocument.HIToolboxVersion">698.00</string> <object class="NSMutableDictionary" key="IBDocument.PluginVersions"> <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="NS.object.0">3084</string> </object> <array key="IBDocument.IntegratedClassDependencies"> <string>NSButton</string> <string>NSButtonCell</string> <string>NSCustomObject</string> <string>NSMenu</string> <string>NSMenuItem</string> <string>NSScrollView</string> <string>NSScroller</string> <string>NSTextField</string> <string>NSTextFieldCell</string> <string>NSTextView</string> <string>NSView</string> <string>NSWindowTemplate</string> </array> <array key="IBDocument.PluginDependencies"> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> </array> <object class="NSMutableDictionary" key="IBDocument.Metadata"> <string key="NS.key.0">PluginDependencyRecalculationVersion</string> <integer value="1" key="NS.object.0"/> </object> <array class="NSMutableArray" key="IBDocument.RootObjects" id="1048"> <object class="NSCustomObject" id="1021"> <string key="NSClassName">NSApplication</string> </object> <object class="NSCustomObject" id="1014"> <string key="NSClassName">FirstResponder</string> </object> <object class="NSCustomObject" id="1050"> <string key="NSClassName">NSApplication</string> </object> <object class="NSMenu" id="649796088"> <string key="NSTitle">AMainMenu</string> <array class="NSMutableArray" key="NSMenuItems"> <object class="NSMenuItem" id="694149608"> <reference key="NSMenu" ref="649796088"/> <string key="NSTitle">dcm2</string> <string key="NSKeyEquiv"/> <int key="NSKeyEquivModMask">1048576</int> <int key="NSMnemonicLoc">2147483647</int> <object class="NSCustomResource" key="NSOnImage" id="35465992"> <string key="NSClassName">NSImage</string> <string key="NSResourceName">NSMenuCheckmark</string> </object> <object class="NSCustomResource" key="NSMixedImage" id="502551668"> <string key="NSClassName">NSImage</string> <string key="NSResourceName">NSMenuMixedState</string> </object> <string key="NSAction">submenuAction:</string> <reference key="NSTarget" ref="110575045"/> <object class="NSMenu" key="NSSubmenu" id="110575045"> <string key="NSTitle">dcm2</string> <array class="NSMutableArray" key="NSMenuItems"> <object class="NSMenuItem" id="238522557"> <reference key="NSMenu" ref="110575045"/> <string key="NSTitle">About dcm2</string> <string key="NSKeyEquiv"/> <int key="NSMnemonicLoc">2147483647</int> <reference key="NSOnImage" ref="35465992"/> <reference key="NSMixedImage" ref="502551668"/> </object> <object class="NSMenuItem" id="304266470"> <reference key="NSMenu" ref="110575045"/> <bool key="NSIsDisabled">YES</bool> <bool key="NSIsSeparator">YES</bool> <string key="NSTitle"/> <string key="NSKeyEquiv"/> <int key="NSKeyEquivModMask">1048576</int> <int key="NSMnemonicLoc">2147483647</int> <reference key="NSOnImage" ref="35465992"/> <reference key="NSMixedImage" ref="502551668"/> </object> <object class="NSMenuItem" id="609285721"> <reference key="NSMenu" ref="110575045"/> <string key="NSTitle">Preferencesâ€Ļ</string> <string key="NSKeyEquiv">,</string> <int key="NSKeyEquivModMask">1048576</int> <int key="NSMnemonicLoc">2147483647</int> <reference key="NSOnImage" ref="35465992"/> <reference key="NSMixedImage" ref="502551668"/> </object> <object class="NSMenuItem" id="481834944"> <reference key="NSMenu" ref="110575045"/> <bool key="NSIsDisabled">YES</bool> <bool key="NSIsSeparator">YES</bool> <string key="NSTitle"/> <string key="NSKeyEquiv"/> <int key="NSKeyEquivModMask">1048576</int> <int key="NSMnemonicLoc">2147483647</int> <reference key="NSOnImage" ref="35465992"/> <reference key="NSMixedImage" ref="502551668"/> </object> <object class="NSMenuItem" id="1046388886"> <reference key="NSMenu" ref="110575045"/> <string key="NSTitle">Services</string> <string key="NSKeyEquiv"/> <int key="NSKeyEquivModMask">1048576</int> <int key="NSMnemonicLoc">2147483647</int> <reference key="NSOnImage" ref="35465992"/> <reference key="NSMixedImage" ref="502551668"/> <string key="NSAction">submenuAction:</string> <reference key="NSTarget" ref="752062318"/> <object class="NSMenu" key="NSSubmenu" id="752062318"> <string key="NSTitle">Services</string> <array class="NSMutableArray" key="NSMenuItems"/> <string key="NSName">_NSServicesMenu</string> </object> </object> <object class="NSMenuItem" id="646227648"> <reference key="NSMenu" ref="110575045"/> <bool key="NSIsDisabled">YES</bool> <bool key="NSIsSeparator">YES</bool> <string key="NSTitle"/> <string key="NSKeyEquiv"/> <int key="NSKeyEquivModMask">1048576</int> <int key="NSMnemonicLoc">2147483647</int> <reference key="NSOnImage" ref="35465992"/> <reference key="NSMixedImage" ref="502551668"/> </object> <object class="NSMenuItem" id="755159360"> <reference key="NSMenu" ref="110575045"/> <string key="NSTitle">Hide dcm2</string> <string key="NSKeyEquiv">h</string> <int key="NSKeyEquivModMask">1048576</int> <int key="NSMnemonicLoc">2147483647</int> <reference key="NSOnImage" ref="35465992"/> <reference key="NSMixedImage" ref="502551668"/> </object> <object class="NSMenuItem" id="342932134"> <reference key="NSMenu" ref="110575045"/> <string key="NSTitle">Hide Others</string> <string key="NSKeyEquiv">h</string> <int key="NSKeyEquivModMask">1572864</int> <int key="NSMnemonicLoc">2147483647</int> <reference key="NSOnImage" ref="35465992"/> <reference key="NSMixedImage" ref="502551668"/> </object> <object class="NSMenuItem" id="908899353"> <reference key="NSMenu" ref="110575045"/> <string key="NSTitle">Show All</string> <string key="NSKeyEquiv"/> <int key="NSKeyEquivModMask">1048576</int> <int key="NSMnemonicLoc">2147483647</int> <reference key="NSOnImage" ref="35465992"/> <reference key="NSMixedImage" ref="502551668"/> </object> <object class="NSMenuItem" id="1056857174"> <reference key="NSMenu" ref="110575045"/> <bool key="NSIsDisabled">YES</bool> <bool key="NSIsSeparator">YES</bool> <string key="NSTitle"/> <string key="NSKeyEquiv"/> <int key="NSKeyEquivModMask">1048576</int> <int key="NSMnemonicLoc">2147483647</int> <reference key="NSOnImage" ref="35465992"/> <reference key="NSMixedImage" ref="502551668"/> </object> <object class="NSMenuItem" id="632727374"> <reference key="NSMenu" ref="110575045"/> <string key="NSTitle">Quit dcm2</string> <string key="NSKeyEquiv">q</string> <int key="NSKeyEquivModMask">1048576</int> <int key="NSMnemonicLoc">2147483647</int> <reference key="NSOnImage" ref="35465992"/> <reference key="NSMixedImage" ref="502551668"/> </object> </array> <string key="NSName">_NSAppleMenu</string> </object> </object> <object class="NSMenuItem" id="379814623"> <reference key="NSMenu" ref="649796088"/> <string key="NSTitle">File</string> <string key="NSKeyEquiv"/> <int key="NSKeyEquivModMask">1048576</int> <int key="NSMnemonicLoc">2147483647</int> <reference key="NSOnImage" ref="35465992"/> <reference key="NSMixedImage" ref="502551668"/> <string key="NSAction">submenuAction:</string> <reference key="NSTarget" ref="720053764"/> <object class="NSMenu" key="NSSubmenu" id="720053764"> <string key="NSTitle">File</string> <array class="NSMutableArray" key="NSMenuItems"> <object class="NSMenuItem" id="722745758"> <reference key="NSMenu" ref="720053764"/> <string key="NSTitle">DICOM to NIfTI...</string> <string key="NSKeyEquiv">d</string> <int key="NSKeyEquivModMask">1048576</int> <int key="NSMnemonicLoc">2147483647</int> <reference key="NSOnImage" ref="35465992"/> <reference key="NSMixedImage" ref="502551668"/> </object> <object class="NSMenuItem" id="482520206"> <reference key="NSMenu" ref="720053764"/> <string key="NSTitle">PAR/REC to NIfTI...</string> <string key="NSKeyEquiv">d</string> <int key="NSKeyEquivModMask">1048576</int> <int key="NSMnemonicLoc">2147483647</int> <reference key="NSOnImage" ref="35465992"/> <reference key="NSMixedImage" ref="502551668"/> </object> </array> </object> </object> <object class="NSMenuItem" id="952259628"> <reference key="NSMenu" ref="649796088"/> <string key="NSTitle">Edit</string> <string key="NSKeyEquiv"/> <int key="NSKeyEquivModMask">1048576</int> <int key="NSMnemonicLoc">2147483647</int> <reference key="NSOnImage" ref="35465992"/> <reference key="NSMixedImage" ref="502551668"/> <string key="NSAction">submenuAction:</string> <reference key="NSTarget" ref="789758025"/> <object class="NSMenu" key="NSSubmenu" id="789758025"> <string key="NSTitle">Edit</string> <array class="NSMutableArray" key="NSMenuItems"> <object class="NSMenuItem" id="860595796"> <reference key="NSMenu" ref="789758025"/> <string key="NSTitle">Copy</string> <string key="NSKeyEquiv">c</string> <int key="NSKeyEquivModMask">1048576</int> <int key="NSMnemonicLoc">2147483647</int> <reference key="NSOnImage" ref="35465992"/> <reference key="NSMixedImage" ref="502551668"/> </object> <object class="NSMenuItem" id="583158037"> <reference key="NSMenu" ref="789758025"/> <string key="NSTitle">Select All</string> <string key="NSKeyEquiv">a</string> <int key="NSKeyEquivModMask">1048576</int> <int key="NSMnemonicLoc">2147483647</int> <reference key="NSOnImage" ref="35465992"/> <reference key="NSMixedImage" ref="502551668"/> </object> </array> </object> </object> </array> <string key="NSName">_NSMainMenu</string> </object> <object class="NSWindowTemplate" id="972006081"> <int key="NSWindowStyleMask">15</int> <int key="NSWindowBacking">2</int> <string key="NSWindowRect">{{10, 10}, {640, 300}}</string> <int key="NSWTFlags">1954021376</int> <string key="NSWindowTitle">dcm2</string> <string key="NSWindowClass">myWindow</string> <nil key="NSViewClass"/> <nil key="NSUserInterfaceItemIdentifier"/> <string key="NSWindowContentMinSize">{640, 120}</string> <object class="NSView" key="NSWindowView" id="439893737"> <reference key="NSNextResponder"/> <int key="NSvFlags">256</int> <array class="NSMutableArray" key="NSSubviews"> <object class="NSScrollView" id="248496456"> <reference key="NSNextResponder" ref="439893737"/> <int key="NSvFlags">274</int> <array class="NSMutableArray" key="NSSubviews"> <object class="NSClipView" id="455232561"> <reference key="NSNextResponder" ref="248496456"/> <int key="NSvFlags">2322</int> <array class="NSMutableArray" key="NSSubviews"> <object class="NSTextView" id="746145959"> <reference key="NSNextResponder" ref="455232561"/> <int key="NSvFlags">2322</int> <string key="NSFrameSize">{625, 266}</string> <reference key="NSSuperview" ref="455232561"/> <reference key="NSWindow"/> <reference key="NSNextKeyView" ref="992720392"/> <string key="NSReuseIdentifierKey">_NS:13</string> <object class="NSTextContainer" key="NSTextContainer" id="33714781"> <object class="NSLayoutManager" key="NSLayoutManager"> <object class="NSTextStorage" key="NSTextStorage"> <object class="NSMutableString" key="NSString"> <characters key="NS.bytes"/> </object> <nil key="NSDelegate"/> </object> <array class="NSMutableArray" key="NSTextContainers"> <reference ref="33714781"/> </array> <int key="NSLMFlags">38</int> <nil key="NSDelegate"/> </object> <reference key="NSTextView" ref="746145959"/> <double key="NSWidth">625</double> <int key="NSTCFlags">1</int> </object> <object class="NSTextViewSharedData" key="NSSharedData"> <int key="NSFlags">67121121</int> <int key="NSTextCheckingTypes">0</int> <nil key="NSMarkedAttributes"/> <object class="NSColor" key="NSBackgroundColor" id="640036751"> <int key="NSColorSpace">3</int> <bytes key="NSWhite">MQA</bytes> </object> <dictionary key="NSSelectedAttributes"> <object class="NSColor" key="NSBackgroundColor"> <int key="NSColorSpace">6</int> <string key="NSCatalogName">System</string> <string key="NSColorName">selectedTextBackgroundColor</string> <object class="NSColor" key="NSColor" id="426337731"> <int key="NSColorSpace">3</int> <bytes key="NSWhite">MC42NjY2NjY2NjY3AA</bytes> </object> </object> <object class="NSColor" key="NSColor"> <int key="NSColorSpace">6</int> <string key="NSCatalogName">System</string> <string key="NSColorName">selectedTextColor</string> <object class="NSColor" key="NSColor" id="971210461"> <int key="NSColorSpace">3</int> <bytes key="NSWhite">MAA</bytes> </object> </object> </dictionary> <reference key="NSInsertionColor" ref="971210461"/> <dictionary key="NSLinkAttributes"> <object class="NSColor" key="NSColor"> <int key="NSColorSpace">1</int> <bytes key="NSRGB">MCAwIDEAA</bytes> </object> <object class="NSCursor" key="NSCursor"> <string key="NSHotSpot">{8, -8}</string> <int key="NSCursorType">13</int> </object> <integer value="1" key="NSUnderline"/> </dictionary> <nil key="NSDefaultParagraphStyle"/> <nil key="NSTextFinder"/> <int key="NSPreferredTextFinderStyle">1</int> </object> <int key="NSTVFlags">6</int> <string key="NSMaxSize">{755, 10000000}</string> <nil key="NSDelegate"/> </object> </array> <string key="NSFrame">{{1, 1}, {625, 266}}</string> <reference key="NSSuperview" ref="248496456"/> <reference key="NSWindow"/> <reference key="NSNextKeyView" ref="746145959"/> <string key="NSReuseIdentifierKey">_NS:11</string> <reference key="NSDocView" ref="746145959"/> <reference key="NSBGColor" ref="640036751"/> <object class="NSCursor" key="NSCursor"> <string key="NSHotSpot">{4, 5}</string> <object class="NSImage" key="NSImage"> <int key="NSImageFlags">79691776</int> <array key="NSReps"> <array> <integer value="5"/> <object class="NSURL"> <nil key="NS.base"/> <string key="NS.relative">file://localhost/Applications/zXcode_4.6.3.app/Contents/SharedFrameworks/DVTKit.framework/Resources/DVTIbeamCursor.tiff</string> </object> </array> </array> <object class="NSColor" key="NSColor"> <int key="NSColorSpace">3</int> <bytes key="NSWhite">MCAwAA</bytes> </object> </object> </object> <int key="NScvFlags">4</int> </object> <object class="NSScroller" id="992720392"> <reference key="NSNextResponder" ref="248496456"/> <int key="NSvFlags">256</int> <string key="NSFrame">{{626, 1}, {15, 266}}</string> <reference key="NSSuperview" ref="248496456"/> <reference key="NSWindow"/> <reference key="NSNextKeyView" ref="268801594"/> <string key="NSReuseIdentifierKey">_NS:83</string> <bool key="NSAllowsLogicalLayoutDirection">NO</bool> <reference key="NSTarget" ref="248496456"/> <string key="NSAction">_doScroller:</string> <double key="NSCurValue">1</double> <double key="NSPercent">0.85256409645080566</double> </object> <object class="NSScroller" id="424197942"> <reference key="NSNextResponder" ref="248496456"/> <int key="NSvFlags">-2147483392</int> <string key="NSFrame">{{-100, -100}, {87, 18}}</string> <reference key="NSSuperview" ref="248496456"/> <reference key="NSWindow"/> <reference key="NSNextKeyView" ref="455232561"/> <string key="NSReuseIdentifierKey">_NS:33</string> <bool key="NSAllowsLogicalLayoutDirection">NO</bool> <int key="NSsFlags">1</int> <reference key="NSTarget" ref="248496456"/> <string key="NSAction">_doScroller:</string> <double key="NSCurValue">1</double> <double key="NSPercent">0.94565218687057495</double> </object> </array> <string key="NSFrame">{{-2, -1}, {642, 268}}</string> <reference key="NSSuperview" ref="439893737"/> <reference key="NSWindow"/> <reference key="NSNextKeyView" ref="424197942"/> <string key="NSReuseIdentifierKey">_NS:9</string> <int key="NSsFlags">133138</int> <reference key="NSVScroller" ref="992720392"/> <reference key="NSHScroller" ref="424197942"/> <reference key="NSContentView" ref="455232561"/> <double key="NSMinMagnification">0.25</double> <double key="NSMaxMagnification">4</double> <double key="NSMagnification">1</double> </object> <object class="NSButton" id="931890582"> <reference key="NSNextResponder" ref="439893737"/> <int key="NSvFlags">268</int> <string key="NSFrame">{{8, 275}, {86, 18}}</string> <reference key="NSSuperview" ref="439893737"/> <reference key="NSWindow"/> <reference key="NSNextKeyView" ref="711969047"/> <string key="NSReuseIdentifierKey">_NS:9</string> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="382435753"> <int key="NSCellFlags">-2080374784</int> <int key="NSCellFlags2">268435456</int> <string key="NSContents">Compress</string> <object class="NSFont" key="NSSupport" id="839552639"> <string key="NSName">LucidaGrande</string> <double key="NSSize">13</double> <int key="NSfFlags">1044</int> </object> <string key="NSCellIdentifier">_NS:9</string> <reference key="NSControlView" ref="931890582"/> <int key="NSButtonFlags">1210863872</int> <int key="NSButtonFlags2">2</int> <object class="NSCustomResource" key="NSNormalImage"> <string key="NSClassName">NSImage</string> <string key="NSResourceName">NSSwitch</string> </object> <object class="NSButtonImageSource" key="NSAlternateImage"> <string key="NSImageName">NSSwitch</string> </object> <string key="NSAlternateContents"/> <string key="NSKeyEquivalent"/> <int key="NSPeriodicDelay">200</int> <int key="NSPeriodicInterval">25</int> </object> <bool key="NSAllowsLogicalLayoutDirection">NO</bool> </object> <object class="NSTextField" id="711969047"> <reference key="NSNextResponder" ref="439893737"/> <int key="NSvFlags">268</int> <string key="NSFrame">{{98, 277}, {93, 17}}</string> <reference key="NSSuperview" ref="439893737"/> <reference key="NSWindow"/> <reference key="NSNextKeyView" ref="699751417"/> <string key="NSReuseIdentifierKey">_NS:1535</string> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="951805185"> <int key="NSCellFlags">68157504</int> <int key="NSCellFlags2">272630784</int> <string key="NSContents">Output name</string> <reference key="NSSupport" ref="839552639"/> <string key="NSCellIdentifier">_NS:1535</string> <reference key="NSControlView" ref="711969047"/> <object class="NSColor" key="NSBackgroundColor" id="253962311"> <int key="NSColorSpace">6</int> <string key="NSCatalogName">System</string> <string key="NSColorName">controlColor</string> <reference key="NSColor" ref="426337731"/> </object> <object class="NSColor" key="NSTextColor" id="290465496"> <int key="NSColorSpace">6</int> <string key="NSCatalogName">System</string> <string key="NSColorName">controlTextColor</string> <reference key="NSColor" ref="971210461"/> </object> </object> <bool key="NSAllowsLogicalLayoutDirection">NO</bool> <int key="NSTextFieldAlignmentRectInsetsVersion">1</int> </object> <object class="NSTextField" id="699751417"> <reference key="NSNextResponder" ref="439893737"/> <int key="NSvFlags">268</int> <string key="NSFrame">{{191, 275}, {231, 22}}</string> <reference key="NSSuperview" ref="439893737"/> <reference key="NSWindow"/> <reference key="NSNextKeyView" ref="328931960"/> <string key="NSReuseIdentifierKey">_NS:9</string> <string key="NSHuggingPriority">{1000, 750}</string> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="786656625"> <int key="NSCellFlags">-1804599231</int> <int key="NSCellFlags2">272630784</int> <string key="NSContents"/> <reference key="NSSupport" ref="839552639"/> <string key="NSCellIdentifier">_NS:9</string> <reference key="NSControlView" ref="699751417"/> <bool key="NSDrawsBackground">YES</bool> <object class="NSColor" key="NSBackgroundColor"> <int key="NSColorSpace">6</int> <string key="NSCatalogName">System</string> <string key="NSColorName">textBackgroundColor</string> <reference key="NSColor" ref="640036751"/> </object> <object class="NSColor" key="NSTextColor"> <int key="NSColorSpace">6</int> <string key="NSCatalogName">System</string> <string key="NSColorName">textColor</string> <reference key="NSColor" ref="971210461"/> </object> </object> <bool key="NSAllowsLogicalLayoutDirection">NO</bool> <int key="NSTextFieldAlignmentRectInsetsVersion">1</int> </object> <object class="NSTextField" id="328931960"> <reference key="NSNextResponder" ref="439893737"/> <int key="NSvFlags">268</int> <string key="NSFrame">{{433, 277}, {90, 17}}</string> <reference key="NSSuperview" ref="439893737"/> <reference key="NSWindow"/> <reference key="NSNextKeyView" ref="248496456"/> <string key="NSReuseIdentifierKey">_NS:1535</string> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="617713799"> <int key="NSCellFlags">68157504</int> <int key="NSCellFlags2">272630784</int> <string key="NSContents">Output folder</string> <reference key="NSSupport" ref="839552639"/> <string key="NSCellIdentifier">_NS:1535</string> <reference key="NSControlView" ref="328931960"/> <reference key="NSBackgroundColor" ref="253962311"/> <reference key="NSTextColor" ref="290465496"/> </object> <bool key="NSAllowsLogicalLayoutDirection">NO</bool> <int key="NSTextFieldAlignmentRectInsetsVersion">1</int> </object> <object class="NSButton" id="268801594"> <reference key="NSNextResponder" ref="439893737"/> <int key="NSvFlags">266</int> <string key="NSFrame">{{516, 266}, {125, 32}}</string> <reference key="NSSuperview" ref="439893737"/> <reference key="NSWindow"/> <reference key="NSNextKeyView"/> <string key="NSReuseIdentifierKey">_NS:9</string> <string key="NSHuggingPriority">{250, 1}</string> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="771251508"> <int key="NSCellFlags">67108864</int> <int key="NSCellFlags2">134217728</int> <string key="NSContents">Button</string> <reference key="NSSupport" ref="839552639"/> <string key="NSCellIdentifier">_NS:9</string> <reference key="NSControlView" ref="268801594"/> <int key="NSButtonFlags">-2038284288</int> <int key="NSButtonFlags2">129</int> <string key="NSAlternateContents"/> <string key="NSKeyEquivalent"/> <int key="NSPeriodicDelay">200</int> <int key="NSPeriodicInterval">25</int> </object> <bool key="NSAllowsLogicalLayoutDirection">NO</bool> </object> </array> <string key="NSFrameSize">{640, 300}</string> <reference key="NSSuperview"/> <reference key="NSWindow"/> <reference key="NSNextKeyView" ref="931890582"/> </object> <string key="NSScreenRect">{{0, 0}, {1280, 778}}</string> <string key="NSMinSize">{640, 142}</string> <string key="NSMaxSize">{10000000000000, 10000000000000}</string> <bool key="NSWindowIsRestorable">YES</bool> </object> <object class="NSCustomObject" id="976324537"> <string key="NSClassName">AppDelegate</string> </object> <object class="NSCustomObject" id="755631768"> <string key="NSClassName">NSFontManager</string> </object> </array> <object class="IBObjectContainer" key="IBDocument.Objects"> <array class="NSMutableArray" key="connectionRecords"> <object class="IBConnectionRecord"> <object class="IBActionConnection" key="connection"> <string key="label">terminate:</string> <reference key="source" ref="1050"/> <reference key="destination" ref="632727374"/> </object> <int key="connectionID">449</int> </object> <object class="IBConnectionRecord"> <object class="IBActionConnection" key="connection"> <string key="label">orderFrontStandardAboutPanel:</string> <reference key="source" ref="1021"/> <reference key="destination" ref="238522557"/> </object> <int key="connectionID">142</int> </object> <object class="IBConnectionRecord"> <object class="IBOutletConnection" key="connection"> <string key="label">delegate</string> <reference key="source" ref="1021"/> <reference key="destination" ref="976324537"/> </object> <int key="connectionID">495</int> </object> <object class="IBConnectionRecord"> <object class="IBActionConnection" key="connection"> <string key="label">copy:</string> <reference key="source" ref="1014"/> <reference key="destination" ref="860595796"/> </object> <int key="connectionID">224</int> </object> <object class="IBConnectionRecord"> <object class="IBActionConnection" key="connection"> <string key="label">selectAll:</string> <reference key="source" ref="1014"/> <reference key="destination" ref="583158037"/> </object> <int key="connectionID">232</int> </object> <object class="IBConnectionRecord"> <object class="IBActionConnection" key="connection"> <string key="label">hide:</string> <reference key="source" ref="1014"/> <reference key="destination" ref="755159360"/> </object> <int key="connectionID">367</int> </object> <object class="IBConnectionRecord"> <object class="IBActionConnection" key="connection"> <string key="label">hideOtherApplications:</string> <reference key="source" ref="1014"/> <reference key="destination" ref="342932134"/> </object> <int key="connectionID">368</int> </object> <object class="IBConnectionRecord"> <object class="IBActionConnection" key="connection"> <string key="label">unhideAllApplications:</string> <reference key="source" ref="1014"/> <reference key="destination" ref="908899353"/> </object> <int key="connectionID">370</int> </object> <object class="IBConnectionRecord"> <object class="IBOutletConnection" key="connection"> <string key="label">delegate</string> <reference key="source" ref="972006081"/> <reference key="destination" ref="1021"/> </object> <int key="connectionID">555</int> </object> <object class="IBConnectionRecord"> <object class="IBOutletConnection" key="connection"> <string key="label">window</string> <reference key="source" ref="976324537"/> <reference key="destination" ref="972006081"/> </object> <int key="connectionID">532</int> </object> <object class="IBConnectionRecord"> <object class="IBActionConnection" key="connection"> <string key="label">compressCheckClick:</string> <reference key="source" ref="976324537"/> <reference key="destination" ref="931890582"/> </object> <int key="connectionID">551</int> </object> <object class="IBConnectionRecord"> <object class="IBOutletConnection" key="connection"> <string key="label">compressCheck</string> <reference key="source" ref="976324537"/> <reference key="destination" ref="931890582"/> </object> <int key="connectionID">556</int> </object> <object class="IBConnectionRecord"> <object class="IBOutletConnection" key="connection"> <string key="label">outputFilenameEdit</string> <reference key="source" ref="976324537"/> <reference key="destination" ref="699751417"/> </object> <int key="connectionID">571</int> </object> <object class="IBConnectionRecord"> <object class="IBActionConnection" key="connection"> <string key="label">outputFolderClick:</string> <reference key="source" ref="976324537"/> <reference key="destination" ref="268801594"/> </object> <int key="connectionID">691</int> </object> <object class="IBConnectionRecord"> <object class="IBOutletConnection" key="connection"> <string key="label">folderButton</string> <reference key="source" ref="976324537"/> <reference key="destination" ref="268801594"/> </object> <int key="connectionID">720</int> </object> <object class="IBConnectionRecord"> <object class="IBActionConnection" key="connection"> <string key="label">par2niiClick:</string> <reference key="source" ref="976324537"/> <reference key="destination" ref="482520206"/> </object> <int key="connectionID">774</int> </object> <object class="IBConnectionRecord"> <object class="IBActionConnection" key="connection"> <string key="label">dicom2niiClick:</string> <reference key="source" ref="976324537"/> <reference key="destination" ref="722745758"/> </object> <int key="connectionID">775</int> </object> <object class="IBConnectionRecord"> <object class="IBOutletConnection" key="connection"> <string key="label">theTextView</string> <reference key="source" ref="976324537"/> <reference key="destination" ref="746145959"/> </object> <int key="connectionID">798</int> </object> <object class="IBConnectionRecord"> <object class="IBOutletConnection" key="connection"> <string key="label">delegate</string> <reference key="source" ref="746145959"/> <reference key="destination" ref="1021"/> </object> <int key="connectionID">791</int> </object> <object class="IBConnectionRecord"> <object class="IBAccessibilityConnection" key="connection"> <string key="label">link</string> <reference key="source" ref="711969047"/> <reference key="destination" ref="699751417"/> </object> <int key="connectionID">693</int> </object> <object class="IBConnectionRecord"> <object class="IBAccessibilityConnection" key="connection"> <string key="label">link</string> <reference key="source" ref="711969047"/> <reference key="destination" ref="699751417"/> </object> <int key="connectionID">694</int> </object> <object class="IBConnectionRecord"> <object class="IBOutletConnection" key="connection"> <string key="label">delegate</string> <reference key="source" ref="699751417"/> <reference key="destination" ref="976324537"/> </object> <int key="connectionID">583</int> </object> </array> <object class="IBMutableOrderedSet" key="objectRecords"> <array key="orderedObjects"> <object class="IBObjectRecord"> <int key="objectID">0</int> <array key="object" id="0"/> <reference key="children" ref="1048"/> <nil key="parent"/> </object> <object class="IBObjectRecord"> <int key="objectID">-2</int> <reference key="object" ref="1021"/> <reference key="parent" ref="0"/> <string key="objectName">File's Owner</string> </object> <object class="IBObjectRecord"> <int key="objectID">-1</int> <reference key="object" ref="1014"/> <reference key="parent" ref="0"/> <string key="objectName">First Responder</string> </object> <object class="IBObjectRecord"> <int key="objectID">-3</int> <reference key="object" ref="1050"/> <reference key="parent" ref="0"/> <string key="objectName">Application</string> </object> <object class="IBObjectRecord"> <int key="objectID">29</int> <reference key="object" ref="649796088"/> <array class="NSMutableArray" key="children"> <reference ref="694149608"/> <reference ref="952259628"/> <reference ref="379814623"/> </array> <reference key="parent" ref="0"/> </object> <object class="IBObjectRecord"> <int key="objectID">56</int> <reference key="object" ref="694149608"/> <array class="NSMutableArray" key="children"> <reference ref="110575045"/> </array> <reference key="parent" ref="649796088"/> </object> <object class="IBObjectRecord"> <int key="objectID">217</int> <reference key="object" ref="952259628"/> <array class="NSMutableArray" key="children"> <reference ref="789758025"/> </array> <reference key="parent" ref="649796088"/> </object> <object class="IBObjectRecord"> <int key="objectID">83</int> <reference key="object" ref="379814623"/> <array class="NSMutableArray" key="children"> <reference ref="720053764"/> </array> <reference key="parent" ref="649796088"/> </object> <object class="IBObjectRecord"> <int key="objectID">81</int> <reference key="object" ref="720053764"/> <array class="NSMutableArray" key="children"> <reference ref="722745758"/> <reference ref="482520206"/> </array> <reference key="parent" ref="379814623"/> </object> <object class="IBObjectRecord"> <int key="objectID">72</int> <reference key="object" ref="722745758"/> <reference key="parent" ref="720053764"/> </object> <object class="IBObjectRecord"> <int key="objectID">205</int> <reference key="object" ref="789758025"/> <array class="NSMutableArray" key="children"> <reference ref="583158037"/> <reference ref="860595796"/> </array> <reference key="parent" ref="952259628"/> </object> <object class="IBObjectRecord"> <int key="objectID">198</int> <reference key="object" ref="583158037"/> <reference key="parent" ref="789758025"/> </object> <object class="IBObjectRecord"> <int key="objectID">197</int> <reference key="object" ref="860595796"/> <reference key="parent" ref="789758025"/> </object> <object class="IBObjectRecord"> <int key="objectID">57</int> <reference key="object" ref="110575045"/> <array class="NSMutableArray" key="children"> <reference ref="238522557"/> <reference ref="755159360"/> <reference ref="908899353"/> <reference ref="632727374"/> <reference ref="646227648"/> <reference ref="609285721"/> <reference ref="481834944"/> <reference ref="304266470"/> <reference ref="1046388886"/> <reference ref="1056857174"/> <reference ref="342932134"/> </array> <reference key="parent" ref="694149608"/> </object> <object class="IBObjectRecord"> <int key="objectID">58</int> <reference key="object" ref="238522557"/> <reference key="parent" ref="110575045"/> </object> <object class="IBObjectRecord"> <int key="objectID">134</int> <reference key="object" ref="755159360"/> <reference key="parent" ref="110575045"/> </object> <object class="IBObjectRecord"> <int key="objectID">150</int> <reference key="object" ref="908899353"/> <reference key="parent" ref="110575045"/> </object> <object class="IBObjectRecord"> <int key="objectID">136</int> <reference key="object" ref="632727374"/> <reference key="parent" ref="110575045"/> </object> <object class="IBObjectRecord"> <int key="objectID">144</int> <reference key="object" ref="646227648"/> <reference key="parent" ref="110575045"/> </object> <object class="IBObjectRecord"> <int key="objectID">129</int> <reference key="object" ref="609285721"/> <reference key="parent" ref="110575045"/> </object> <object class="IBObjectRecord"> <int key="objectID">143</int> <reference key="object" ref="481834944"/> <reference key="parent" ref="110575045"/> </object> <object class="IBObjectRecord"> <int key="objectID">236</int> <reference key="object" ref="304266470"/> <reference key="parent" ref="110575045"/> </object> <object class="IBObjectRecord"> <int key="objectID">131</int> <reference key="object" ref="1046388886"/> <array class="NSMutableArray" key="children"> <reference ref="752062318"/> </array> <reference key="parent" ref="110575045"/> </object> <object class="IBObjectRecord"> <int key="objectID">149</int> <reference key="object" ref="1056857174"/> <reference key="parent" ref="110575045"/> </object> <object class="IBObjectRecord"> <int key="objectID">145</int> <reference key="object" ref="342932134"/> <reference key="parent" ref="110575045"/> </object> <object class="IBObjectRecord"> <int key="objectID">130</int> <reference key="object" ref="752062318"/> <reference key="parent" ref="1046388886"/> </object> <object class="IBObjectRecord"> <int key="objectID">371</int> <reference key="object" ref="972006081"/> <array class="NSMutableArray" key="children"> <reference ref="439893737"/> </array> <reference key="parent" ref="0"/> </object> <object class="IBObjectRecord"> <int key="objectID">372</int> <reference key="object" ref="439893737"/> <array class="NSMutableArray" key="children"> <reference ref="931890582"/> <reference ref="711969047"/> <reference ref="699751417"/> <reference ref="328931960"/> <reference ref="248496456"/> <reference ref="268801594"/> </array> <reference key="parent" ref="972006081"/> </object> <object class="IBObjectRecord"> <int key="objectID">420</int> <reference key="object" ref="755631768"/> <reference key="parent" ref="0"/> </object> <object class="IBObjectRecord"> <int key="objectID">494</int> <reference key="object" ref="976324537"/> <reference key="parent" ref="0"/> </object> <object class="IBObjectRecord"> <int key="objectID">536</int> <reference key="object" ref="248496456"/> <array class="NSMutableArray" key="children"> <reference ref="746145959"/> <reference ref="424197942"/> <reference ref="992720392"/> </array> <reference key="parent" ref="439893737"/> </object> <object class="IBObjectRecord"> <int key="objectID">537</int> <reference key="object" ref="746145959"/> <reference key="parent" ref="248496456"/> </object> <object class="IBObjectRecord"> <int key="objectID">538</int> <reference key="object" ref="424197942"/> <reference key="parent" ref="248496456"/> </object> <object class="IBObjectRecord"> <int key="objectID">539</int> <reference key="object" ref="992720392"/> <reference key="parent" ref="248496456"/> </object> <object class="IBObjectRecord"> <int key="objectID">547</int> <reference key="object" ref="931890582"/> <array class="NSMutableArray" key="children"> <reference ref="382435753"/> </array> <reference key="parent" ref="439893737"/> </object> <object class="IBObjectRecord"> <int key="objectID">548</int> <reference key="object" ref="382435753"/> <reference key="parent" ref="931890582"/> </object> <object class="IBObjectRecord"> <int key="objectID">557</int> <reference key="object" ref="711969047"/> <array class="NSMutableArray" key="children"> <reference ref="951805185"/> </array> <reference key="parent" ref="439893737"/> </object> <object class="IBObjectRecord"> <int key="objectID">558</int> <reference key="object" ref="951805185"/> <reference key="parent" ref="711969047"/> </object> <object class="IBObjectRecord"> <int key="objectID">564</int> <reference key="object" ref="699751417"/> <array class="NSMutableArray" key="children"> <reference ref="786656625"/> </array> <reference key="parent" ref="439893737"/> </object> <object class="IBObjectRecord"> <int key="objectID">565</int> <reference key="object" ref="786656625"/> <reference key="parent" ref="699751417"/> </object> <object class="IBObjectRecord"> <int key="objectID">701</int> <reference key="object" ref="328931960"/> <array class="NSMutableArray" key="children"> <reference ref="617713799"/> </array> <reference key="parent" ref="439893737"/> </object> <object class="IBObjectRecord"> <int key="objectID">702</int> <reference key="object" ref="617713799"/> <reference key="parent" ref="328931960"/> </object> <object class="IBObjectRecord"> <int key="objectID">685</int> <reference key="object" ref="268801594"/> <array class="NSMutableArray" key="children"> <reference ref="771251508"/> </array> <reference key="parent" ref="439893737"/> </object> <object class="IBObjectRecord"> <int key="objectID">686</int> <reference key="object" ref="771251508"/> <reference key="parent" ref="268801594"/> </object> <object class="IBObjectRecord"> <int key="objectID">773</int> <reference key="object" ref="482520206"/> <reference key="parent" ref="720053764"/> </object> </array> </object> <dictionary class="NSMutableDictionary" key="flattenedProperties"> <string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="-3.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="129.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="130.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="131.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="134.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="136.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="143.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="144.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="145.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="149.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="150.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="197.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="198.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="205.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="217.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="236.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="29.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <boolean value="YES" key="371.IBNSWindowAutoPositionCentersHorizontal"/> <boolean value="YES" key="371.IBNSWindowAutoPositionCentersVertical"/> <string key="371.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="371.IBWindowTemplateEditedContentRect">{{380, 496}, {480, 360}}</string> <integer value="1" key="371.NSWindowTemplate.visibleAtLaunch"/> <string key="372.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="420.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="494.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="536.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="537.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="538.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="539.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <object class="NSMutableDictionary" key="547.IBAttributePlaceholdersKey"> <string key="NS.key.0">ToolTip</string> <object class="IBToolTipAttribute" key="NS.object.0"> <string key="name">ToolTip</string> <reference key="object" ref="931890582"/> <string key="toolTip">If checked, images will be stored as compressed .nii.gz</string> </object> </object> <string key="547.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="548.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="557.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="558.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="56.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <object class="NSMutableDictionary" key="564.IBAttributePlaceholdersKey"> <string key="NS.key.0">ToolTip</string> <object class="IBToolTipAttribute" key="NS.object.0"> <string key="name">ToolTip</string> <reference key="object" ref="699751417"/> <string key="toolTip">Name of NIfTI images (%c= comments, %f folder name, %i ID of patient %n name of patient, %p protocol name, %s series, %t time)</string> </object> </object> <string key="564.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="565.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="57.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="58.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <object class="NSMutableDictionary" key="685.IBAttributePlaceholdersKey"> <string key="NS.key.0">ToolTip</string> <object class="IBToolTipAttribute" key="NS.object.0"> <string key="name">ToolTip</string> <reference key="object" ref="268801594"/> <string key="toolTip">Set location for converted images (or choose to have files saved to the same location as the input images)</string> </object> </object> <string key="685.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="686.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="701.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="702.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="72.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="773.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="81.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="83.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> </dictionary> <dictionary class="NSMutableDictionary" key="unlocalizedProperties"/> <nil key="activeLocalization"/> <dictionary class="NSMutableDictionary" key="localizations"/> <nil key="sourceID"/> <int key="maxID">815</int> </object> <object class="IBClassDescriber" key="IBDocument.Classes"> <array class="NSMutableArray" key="referencedPartialClassDescriptions"> <object class="IBPartialClassDescription"> <string key="className">AppDelegate</string> <string key="superclassName">NSObject</string> <dictionary class="NSMutableDictionary" key="actions"> <string key="compressCheckClick:">id</string> <string key="dicom2niiClick:">id</string> <string key="outputFolderClick:">id</string> <string key="par2niiClick:">id</string> </dictionary> <dictionary class="NSMutableDictionary" key="actionInfosByName"> <object class="IBActionInfo" key="compressCheckClick:"> <string key="name">compressCheckClick:</string> <string key="candidateClassName">id</string> </object> <object class="IBActionInfo" key="dicom2niiClick:"> <string key="name">dicom2niiClick:</string> <string key="candidateClassName">id</string> </object> <object class="IBActionInfo" key="outputFolderClick:"> <string key="name">outputFolderClick:</string> <string key="candidateClassName">id</string> </object> <object class="IBActionInfo" key="par2niiClick:"> <string key="name">par2niiClick:</string> <string key="candidateClassName">id</string> </object> </dictionary> <dictionary class="NSMutableDictionary" key="outlets"> <string key="compressCheck">NSButton</string> <string key="folderButton">NSButton</string> <string key="outputFilenameEdit">NSTextField</string> <string key="theTextView">NSTextView</string> <string key="window">NSWindow</string> </dictionary> <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName"> <object class="IBToOneOutletInfo" key="compressCheck"> <string key="name">compressCheck</string> <string key="candidateClassName">NSButton</string> </object> <object class="IBToOneOutletInfo" key="folderButton"> <string key="name">folderButton</string> <string key="candidateClassName">NSButton</string> </object> <object class="IBToOneOutletInfo" key="outputFilenameEdit"> <string key="name">outputFilenameEdit</string> <string key="candidateClassName">NSTextField</string> </object> <object class="IBToOneOutletInfo" key="theTextView"> <string key="name">theTextView</string> <string key="candidateClassName">NSTextView</string> </object> <object class="IBToOneOutletInfo" key="window"> <string key="name">window</string> <string key="candidateClassName">NSWindow</string> </object> </dictionary> <object class="IBClassDescriptionSource" key="sourceIdentifier"> <string key="majorKey">IBProjectSource</string> <string key="minorKey">./Classes/AppDelegate.h</string> </object> </object> <object class="IBPartialClassDescription"> <string key="className">myWindow</string> <string key="superclassName">NSWindow</string> <object class="IBClassDescriptionSource" key="sourceIdentifier"> <string key="majorKey">IBProjectSource</string> <string key="minorKey">./Classes/myWindow.h</string> </object> </object> </array> </object> <int key="IBDocument.localizationMode">0</int> <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string> <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies"> <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string> <real value="1060" key="NS.object.0"/> </object> <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies"> <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string> <integer value="4600" key="NS.object.0"/> </object> <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool> <int key="IBDocument.defaultPropertyAccessControl">3</int> <dictionary class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes"> <string key="NSMenuCheckmark">{11, 11}</string> <string key="NSMenuMixedState">{10, 3}</string> <string key="NSSwitch">{15, 15}</string> </dictionary> </data> </archive> ������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2/main.m�������������������������������������������������������������0000664�0000000�0000000�00000000366�13220512030�0017012�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // main.m // dcm2 // // Created by Chris Rorden on 4/7/14. // Copyright (c) 2014 Chris Rorden. All rights reserved. // #import <Cocoa/Cocoa.h> int main(int argc, char *argv[]) { return NSApplicationMain(argc, (const char **)argv); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2/myWindow.h���������������������������������������������������������0000664�0000000�0000000�00000000301�13220512030�0017663�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // myWindow.h // dcm2 // // Created by Chris Rorden on 4/7/14. // Copyright (c) 2014 Chris Rorden. All rights reserved. // #import <Cocoa/Cocoa.h> @interface myWindow : NSWindow @end �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2/myWindow.m���������������������������������������������������������0000664�0000000�0000000�00000002265�13220512030�0017703�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // myWindow.m // dcm2 // // Created by Chris Rorden on 4/7/14. // Copyright (c) 2014 Chris Rorden. All rights reserved. // #import "myWindow.h" #import "AppDelegate.h" @implementation myWindow - (void)awakeFromNib { //http://stackoverflow.com/questions/8567348/accepting-dragged-files-on-a-cocoa-application [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]]; } - (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender { return NSDragOperationCopy; } - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender { return NSDragOperationCopy; } -(BOOL)canBecomeKeyWindow { return YES; } - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender { NSPasteboard *pboard = [sender draggingPasteboard]; NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType]; if (1 == filenames.count) if ([[NSApp delegate] respondsToSelector:@selector(application:openFile:)]) { //return [[NSApp delegate] application:NSApp openFile:[filenames lastObject]]; [(AppDelegate *)[NSApp delegate] processFile:[filenames lastObject]]; return TRUE; } return FALSE; } @end �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������dcm2niix-1.0.20171215/xcode/dcm2niigui.icns���������������������������������������������������������0000664�0000000�0000000�00000122012�13220512030�0017764�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������icns��¤ ics#���H����Āøøü˙ü˙ü˙ü˙ū˙üøđ<���������Āøøü˙ü˙ü˙ü˙ū˙üøđ<�����is32��9Ÿ� ž¯ĨS ˙‚� Ŧ˛”; €�B,’˙��¤ļ‘�<U:�)b��›ĨŦ-�“ˇ¸ŊŒ���ą��…Ŋ–™­Į~� .� ąo)˛˛��ĢÃŽ €�<ĸąĒą°ļĨga”ēĄ Sǟ˛Žļĩ° Ž…€ŽŖĸ™Ģ­ĩļŠp{{zz‰•�™¤§ą´’��Z€� Hž§ŗ´ 9 J‘Ŧ‚� ” ŸĄ­y]ŧĨĄ�Ÿ� žĸĨœ—™š3‚� ŦŽŽ§Ą€Ÿ—œ¨€�|¤ŗŽŸ˜—žĄœ˜ž”��›Ĩ¯ĸ˜˜­ą˛ˇŠ˜ŸĄ��­Ē™—°´–™­ĀŦšĸ� ­ĸ™™ģ¯��ĢÂ皘‘�ĸąĢŗąĩĨga”¸˛™“ŠjŸ˛­ĩĩ°Žƒ~¨Ŧĸ™Ģ­ĩĩ­‘–ŒŒ’™•�™¤§ą˛§•›œ˜–š€� Hž§ŗŗ¨‡‡†‰“ŖŸ‚� ” Ÿž–yi‚—Ą�Ÿ�žĸĨš€‡f‚�  Ŧ¯ŦĄ˜–•••¨€�|¤ŗŦ—Ž˜–”—”��›Ĩ¯›ŽĢą˛¸§‘•š��­¨Ž­´–™­Āǐ•‹� ­Ÿ’“ē¯��Ģš‘ˆ�ĸąĢŗąĩĨga”¸ą‘‹ŠŸ˛­ĩĩ°Žƒ~§Ģĸ™Ģ­ĩĩŦŽ–‹Œ‹‘™•�™¤§ą˛Ļ’”“Š€� Hž§ŗŗ¨ƒ€ƒĸŸ‚� ” Ÿž–yi‚žĄ�s8mk������������������������������������%:Pev}c/�����|ëö˙˙˙˙˙˙ûĪj�� â˙˙˙˙˙˙˙˙˙˙÷��.û˙˙˙˙˙ôī˙˙˙˙†��Q˙˙˙˙˙Ę'Ŧ˙˙˙Ü �i˙˙˙˙˙]��4˙˙˙ō�n˙˙˙˙˙Ÿ;/u˙˙˙ë. c˙˙˙˙˙˙˙ū˙˙˙˙˙ß0K˙˙˙˙˙˙˙˙˙˙˙÷€?�-˙˙˙˙˙˙˙˙˙˙˙ ���¤˙˙˙˙úųøøų÷;�����JihW,*))*%�����������������������������������ICN#�������������������Ā�˙ū�?˙˙€?˙˙Ā˙˙ā˙˙ā˙˙đ˙˙đ˙˙đ˙˙ø˙˙˙ø˙˙˙ø˙˙˙ø˙˙˙ø˙˙˙˙˙˙ū˙˙ü˙˙ā˙˙Ā˙˙Ā˙˙€?˙˙€˙˙��������������������������������������Ā�˙ū�?˙˙€?˙˙Ā˙˙ā˙˙ā˙˙đ˙˙đ˙˙đ˙˙ø˙˙˙ø˙˙˙ø˙˙˙ø˙˙˙ø˙˙˙˙˙˙ū˙˙ü˙˙ā˙˙Ā˙˙Ā˙˙€?˙˙€˙˙���������������������il32��~˙�…� UĒĒK('*)-˙‹�i–œŖ¤§Ŧ­‹Q( 'f˙‡� Ą¸ēē¸ēŸN†�]ĒĨ„�r¨Ž°ŽŽ˛ ‹�PĒǃ�‘¨Ļ¯Žŗ|ƒ� ƒ�A‹ƒ�œĢĒŽ˛‚�6~¤°°Ĩ€8�(ƒ�ĄŠŦ¯Ē:� s¸ģēēˇļē¸t€�0�$ĨĻŽŗz€�}ŧš˛ĒŠŽē´´ŧ|��(�{§¤¯°;� MŊšŦ“��¤¸´ĩÄQ€�7€�‰¨Ŗąž€�žÆĩ†‚� ĒÂŋÅ �� €�’­¤´Š�ŊÉŦƒ�ÄĀÁŊ!€��€� •°ž¤ˆ654/O˛ŧЃ�’žÁĀÂ0€��€�”˛ŠĨĢ瀏Ѓ�¤ĮÂÂŋ$€��€�”˛°ŠĒĩ´ĩŽĩ~iikf_ļ´ēŖĒ__‘°¯ŠĒĩ´ĩĒąĄŒŠ‡‚wyy “’‘žŖž‹Ē¯ŠĒĩ´ļĨ’”š›œ–|Œ­ąąŗ´­’�‚Ŗ¯¨Ģĩ´亂š††ŒŒ‰ˆ…„~’ŖĄĄĸ•��n ĢĨŠĩ´ ˇ—kd|•ĄŸzŒ€‘’~3:€�3Ēĸ¤˛´ēq��&;9! K™ƒ� šŦŖĄŽŽ°°¯´i‰�[–„�–˛Ĩ ­Ŧŗc‡�(…­„„�z˜ĻŖ‚­ą–Z, "J…´Á‡�‘•¨‚Ē žŠž‚bV]u“Ēϟˆˆ�H„‚‚�?˙��˙�…� U‰Ž™™”‘Œ�i–œŖ¤ĒŠĢ̤ ŸĄĸĄŸœ˜“f‡� Ą¸ēē¸ļŗŦĄ›ƒ™šĄĄĒ–„�r¨Ž°ŽŖ™˜ƒ™�š™˛°ƒ�‘¨Ļ¯€Žĸ˜™�–€”“•™˜Ē™ƒ�œĢĒŽŽ¯¤˜€™ ––ĸ̝¯Ģĸ––€™ œƒ�ĄŠŦޝЁ™ “ž˛ˇšēļĩ”€™ĸ́�$ĨĻŽŽ­˜™š“Ÿĩ¸˛ĒŠŽē´´ļĄ•™™ ¨�{§¤Ž¯Ĩ˜™™˜›ļšŦ“��¤¸´ĩŊž˜™™ŖŽ€�‰¨Ŗ¯­œ€™•ąÃĩ†‚� ĒÂŋ²•™˜ŖĨ€�’­¤°Ē™—ŊÉŦƒ� ÄĀĀŧ—™™ ™€�•°žĸŸ—–¯ģЃ�’žÁĀž€™ ™€�”˛ŠĨĒ´ŗ˛°Šƒ� ¤ĮÂÁŊ˜™™Ą™€�”˛°ŠĒĩ´ĩŽĩ~iikf_ļ´¸Ž‘”•’u�__‘°¯ŠĒĩ´ĩĒąĄŒŠ‡‚wyyŖŖĸĸŸŸŖž‹Ē¯ŠĒĩ´ļĨ’”š›œ–|ŒĢ¯¯ąŗ­’�‚Ŗ¯¨Ģĩ´滛•„†ŒŒˆ‡…ƒ}‘ŖĄĄĸ•��n ĢĨŠĩ´ļŸŽ”œšŽ…“•”“’”~3:€�3Ēĸ¤˛´ĩž”—•™œ›•‘‘’‘’}„� šŦŖĄŽŽ°°¯Ž„™šš›š‘–„�–˛Ĩ ­Ŧ¯ƒ–›€œ›™”‹‹ ¨z„�z˜ĻŖ‚­°Ÿˆ}~‚„ƒ‚ƒ ŗŊ‡�‘•¨ĒĢ¤œytw…•ĄĄŸˆˆ�H„‚‚�?˙��˙�…� U‰ŠŠ“ˆŒ�i–œŖ¤ĒŠĢǟ™—˜™™–”‹‰f‡� Ą¸ēē¸ļ˛Ļ˜‘ƒ‘”˜–Ē›„�r¨Ž°€ŽĢ›ˆ”Ŧ°Šƒ�‘¨Ļ¯ŽŽĢ™€ŒŒŒ€¤™ƒ�œĢĒŽŽ­œ€  Ģ¯¯Ģ €–•ƒ�ĄŠŦŽŽĸ ‹›˛ˇšēļĩﺜŒ€™¤�$ĨĻŽŽĒ”‹ļ¸˛ĒŠŽē´´ļžŒ—Ą�{§¤Ž¯žŽ—ˇšŦ“��¤¸´ĩŊ™Žœ§€�‰¨Ŗ¯Ŧ”€Œ°Ãĩ†‚� ĒÂŋ°Œ›€�’­¤ąŠŊÉŦƒ�ÄĀĀŧ€–‘€�•°žĸ’€‘’¯ģЃ� ’žÁĀž“–€�”˛ŠĨĒĩŗ˛°Šƒ� ¤ĮÂÁŊ‘—‘€�”˛°ŠĒĩ´ĩŽĩ~iikf_ļ´¸­‰Œ‰p�__‘°¯ŠĒĩ´ĩĒąĄŒŠ‡‚wyyŖĸĄĄžŸŖž‹Ē¯ŠĒĩ´ļĨ’”š›œ–|ŒŦ¯¯ąŗ­’�‚Ŗ¯¨Ģĩ´滛•„†ŒŒˆ‡…ƒ}‘ŖĄĄĸ•��n ĢĨŠĩ´ļž‹ŒŽ”œšŽ„“””“’”~3:€�3Ēĸ¤˛´ ĩ›ŠŽ’—•މ€Š‹‰z„� šŦŖĄŽŽ°°¯¯™ ‘‘’ˆ™–„�–˛Ĩ ­Ŧ¯Œ{‚’‹ƒ…Ÿ¨z„�z˜ĻŖ‚­°ž†xx{}|{~‰Ÿŗž‡�‘•¨ĒĢ¤œŒyrw…•ĄĄŸˆˆ�H„‚‚�?˙��l8mk������������������������������������������������������������������������������������������������������������������������������������������� '6DQZU>��������������NhƒžˇÍßíø˙˙˙˙˙˙đɉA ����������ií˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ې3�������î˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙í������F˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙_������˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ĩ������˛˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙I����Ø˙˙˙˙˙˙˙˙˙˙˙˙˙ÖËō˙˙˙˙˙˙˙˙˙ŧ����ņ˙˙˙˙˙˙˙˙˙˙˙ŪE� Š˙˙˙˙˙˙˙˙ų)���4ū˙˙˙˙˙˙˙˙˙˙÷5����Â˙˙˙˙˙˙˙˙^���I˙˙˙˙˙˙˙˙˙˙˙ą������Y˙˙˙˙˙˙˙˙€���W˙˙˙˙˙˙˙˙˙˙˙’������=˙˙˙˙˙˙˙˙‡���[˙˙˙˙˙˙˙˙˙˙˙ģ������e˙˙˙˙˙˙˙˙y���Y˙˙˙˙˙˙˙˙˙˙˙ų‘thZMCŅ˙˙˙˙˙˙˙˙]O˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙čäęC>˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙x�+ų˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙âįĸ��ë˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙— ��Ô˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ö������˛˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙u�������„˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙î�������§˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙����������O×˙üüüüūƜ™““““““““—+�����������0/////����������������������������������������������������������������������������������������������������������������������������������������������������ich#��H�������������������������������������������˙ū��˙˙˙ā�˙˙˙ü�˙˙˙ū�?˙˙˙˙�?˙˙˙˙�?˙˙˙˙�?˙˙˙˙€˙˙˙˙€˙˙˙˙Ā˙˙˙˙Ā˙˙˙˙ā˙˙˙˙ā˙˙˙˙ā˙˙˙˙ā˙˙˙˙ā˙˙˙˙˙ā˙˙˙˙˙ā˙˙˙˙˙ā˙˙˙˙˙ā˙˙˙˙˙˙˙˙˙ū˙˙˙˙ü˙˙˙˙ø˙˙˙˙ø˙˙˙˙�˙˙˙ū�˙˙˙ū�˙˙˙ū�?˙˙˙ü�?˙˙˙ü�˙˙˙ø�˙˙˙ø�˙€����������������������������������������������������������������������������������������˙ū��˙˙˙ā�˙˙˙ü�˙˙˙ū�?˙˙˙˙�?˙˙˙˙�?˙˙˙˙�?˙˙˙˙€˙˙˙˙€˙˙˙˙Ā˙˙˙˙Ā˙˙˙˙ā˙˙˙˙ā˙˙˙˙ā˙˙˙˙ā˙˙˙˙ā˙˙˙˙˙ā˙˙˙˙˙ā˙˙˙˙˙ā˙˙˙˙˙ā˙˙˙˙˙˙˙˙˙ū˙˙˙˙ü˙˙˙˙ø˙˙˙˙ø˙˙˙˙�˙˙˙ū�˙˙˙ū�˙˙˙ū�?˙˙˙ü�?˙˙˙ü�˙˙˙ø�˙˙˙ø�˙€���������������������������������������������ih32��/˙�˙�Ŧ� ĒĒmA*20%3?™�*^yˆŽ•šĸ¨–oI/ !#-�6Ą¤ŠĢŽ´¸ģžĄe.‚� #Q†Œ�ŠžĢĀŊ玴఺žZ‹� 9„Ģ‹ˆ�kа­˛¯€Ž¯ą0�qŋĒO‡�‹´ ‚ޝ¯l“�MŊ˜‡�™´›¯€Ž¯¯hˆ�ˆ�M™‡� ­Ÿ°€Ž˛s†�NuŠ‘‹xR …�Ey…� fĨĨ¤¯ŽŽ˛Œ)„� !xŽģģš¸šģģ°}&„�.…� „¯Ÿ¨ŽŽ¯¨D„�QŽģĩ„´ĩģ°Vƒ�%5„�ļœĢŽŽŗwƒ� dēļ´´¸ģŧēļģhƒ�1„�™ļœ­Ž¯Ē:„� Qēĩ´ˇšĒŸœŖ­ŧ‚´ģU‚� .ƒ�ŗœ¯Ž˛‰ƒ�­ĩŗ¸Ŧ’$€� nŸš´´ŗŗēē$�*.‚�.Ą°œ¯Ž´Z„�sĮēēŽ{„�šģĩˇŧĀČ}‚�/‚�g¤­¯Ž°.ƒ�¯ÃÁŗ…�/ĢĀĀ´�˙�yĻĢž°¯§ƒ�2ÃĀÃ˛f†�šĮĀÅ;‚�*�ĒŠŸąą ƒ�HÉÁĮLJ�ŠĀÁ€ĀČU‚� �†Ž¨—Ÿ•–XOOMKJDqŦĻļLJ�†ŋÁ€ĀČ]‚� �ƒŽ­ŖĒ ­ŧŧ‚쎞ŗ¨­U†�•ƁĀČP‚�$�ƒŽŽ¯¯ŖĒĩ…´ĩĻŧ‹†�ĢÉÂÂÁÁÄ1‚�˙�ƒ­¯Ž¯¤Šĩ…´ĩĻŋĸionnkkjdqĻŦŽą´ēŖ ‚��€� �}ǝޝ¤¨ĩ…´'ĩĻ쯓ƒ…„‚~zvxz}y™ˆˆ‡…„ƒœ›œ˜ow¤¯Ž¯¤Šĩ…´ļœ”›Œ€š™—–•~s{yxtƒŠ˛°°ą °˛ŗ’fj ¯Ž¯ŖĒĩ…´ļ𔙆—ƒšˆ|†ƒĨ­‚Ŧ ­Ŧ��ZŽŽ¯ĸĢĩ…´ ļ˜—š‹v‚‹”—‰€| }{‡­Ģ̝˛˛ˇ˜�šŦ­¯ĄŦĩ…´ĩ˜ŸĄ™‡ƒ‹‹Š‰€—™•’‹‡„•€Š‰ˆw�—Ŧ§ŠžŠĩ…´ˇƒiaUq› ¤¨ĸœw–˜šœ€ž…U…�‘­Ļ§¤˛…´ģH€�&MelcH#',+IU†�‹ŦϧžĄŽ°ŗ´ŗ°ĩA�'{‡�§ĻĻ žŽŦ­¯°°¯­Ŧŗ;�=¤™‡�tž¨ĻŖ›Ž…Ŧ˛6�jļĄ‰�—¯§Ĩ™Ž…Ŧ˛9‹�Sžĩ´Ž‰�g‘¤Ŧ™Ģ…ŦŽ“p9ƒ� ;pŖˇŗŗŖf‹�ƒš Ŧ˛ƒąŗ´š­Ģ›‚hVLMWi€™ĒĢĨ€¤�“�3ƒ–Ąƒ ĸ›ts{Š™‘—€Ē—„q^gmU˙�˙�Ô�˙�˙�Ŧ� UdozˆŒˆ_™�*^yˆŽ•˜ĄĸŸœĄĄžŸŸž”}U�6Ą¤ŠĢŽ´¸šģģ´Ģ¤Ÿœ›š™™š›žĄ¤œ•’‹PŒ�ŠžĢĀŊēˇ´ą¯ŽŽĢ¤œ™˜ˆ™šŖĨĨĸ‹ˆ�kа­˛¯ƒŽ¨ž™ ļžĻ_‡�‹´ …ŽϚ˜…™€š‡™ĒĀ’‡�™´›¯‚ޝĨ…™š˜•’‘‘”—š„™š­œ‡� ­Ÿ°Ž¯Ļ„™ 𔐔› ĸ š““„™ŸœU…�fĨĨ¤¯€Ž¯Ēš‚™š—›Ŧĩ‚ļĩ­œ–š‚™Ĩ …�„¯Ÿ¨Ž­˜™š“’Ŧļĩ„´ĩļ­““š™ž¨Ē„�ļœĢ€Ž¯Ŗ˜™ š’”ŗĩ´´¸ģŧēĩ´–“š€™˜ĢĢ„�™ļœ­€ŽК‚™ •ŗĩ´ˇšĒŸœŖ­ŧ‚´ŗ•–™ ĒŸƒ�ŗœ¯ŽŽ­ ˜™š­ĩŗ¸Ŧ’$€� nŸš´´ŗŗē¸‘‚™ލ‚�.Ą°œ¯Ž¯¨š‚™• ÂēēŽ{„�šģĩˇŧĀÃĸ”€™˜ĒĒ‚�g¤­¯ŽŽ ˜™š‘´ÂÁŗ…�/ĢĀĀÁļ‘š€™Ÿĸ‚�yĻĢž°Ž­œƒ™”žĀÃ˛f†�šĮĀŋ•™›™Ē�ĒПą¯Ŧš‚˜—˜ÂÁĮLJ�ŠĀÁ€Āš—€™›™™�†Ž¨—Ÿ”›–•”“”¨ĻļLJ�†ŋÁ€Āœ—€™›™™�ƒŽ­ŖĒ Ŧļƒĩ´˛ŗ¨­U†�•ƁĀÁ™˜€™›šŖ�ƒŽŽ¯¯ŖĒĩ…´ĩĻŧ‹†�ĢÉÂÂÁÁž”€š™“‚�ƒ­¯Ž¯¤Šĩ…´ĩĻŋĸionnkkjdqĻŦŽą´šĒ‹”““”‘i€� �}ǝޝ¤¨ĩ…´ĩĻ쯓ƒ…„‚~zvxz}y™ ›œ€›˜”››œ˜ow¤¯Ž¯¤Šĩ…´ļœ”›Œ€š ™—–•~s{yxtƒŠ°‚Ž °ą˛ŗ’fj ¯Ž¯ŖĒĩ…´ļ𔙆—ƒšˆ|†ƒĨ­‚Ŧ ­Ŧ��ZŽŽ¯ĸĢĩ…´ ļ˜—š‹v‚‹”—‰€| }{‡­Ģ̝˛˛ˇ˜�šŦ­¯ĄŦĩ…´ļ—™šœ—‡ƒ‹‹Š‰–™•’‹‡„•€Š‰ˆw�—Ŧ§ŠžŠĩ…´ļ‰Š†…˜šžĸœ–‹|”•–—€˜œˆU…�‘­Ļ§¤˛†´‘”–—“”› Ąž•‹‹ŠŒ€‹Š‹‰rU†�‹ŦϧžĄŽ°ŗ´ŗ°¯’€™ šš˜—–—˜š››šœ–Œ‡�§ĻĻ žŽŦ­¯°°¯­ŦŦ’Œ™ššŠ§‹‡�tž¨ĻŖ›Ž†Ŧ‹šœš†™šœšŽ‚“ŗ ‰�—¯§Ĩ™Ž…Ŧ¯me‘™ƒœ š–y‰Ļą´Ž‰�g‘¤Ŧ™Ģ…Ŧ­š‘sfgnuxwuqmo{’ĒŗąŗŖf‹�ƒš Ŧ˛ƒąŗ´™ĒĒ “ƒyssz„’ŸĻρ¤�“�3ƒ–Ąƒ ĸ›ts{ƒŠv‚zqg^gmU˙�˙�Ô�˙�˙�Ŧ� UUdot„ˆzw_™�*^yˆŽ•˜ĄĄ™–••™š––˜•ŽyU�6Ą¤ŠĢŽ´¸šģ𰤛–“’‘‘’”˜›”Ž‹]Œ�ŠžĢĀŊēˇ´ą¯¯­Ļœ“‰”šžŖĸ‹ˆ�kа­˛¯‚ŽĢĄ•—˛žĻ_‡�‹´ „ŽĒž‘†‘‘‡ĨĀ’‡�™´›¯‚ŽĒ…‘Œ€‰‹Ž‘…¨œ‡� ­Ÿ°ŽĢž… ‹‰˜ŸĄŸ™ˆŠ„––U…�fĨĨ¤¯€Ž­ĸ‘‚‘ˆ™­ĩˇ€ļˇĩ­š‰‘‚™…�„¯Ÿ¨Ž§”‘ŠŽŦˇĩ„´ĩˇŽŠ‘• Ē„�ļœĢ€Ž̚‚ ‰‘´ĩ´´¸ģŧēĩ´”Š‘€ŖŖ„�™ļœ­€ŽŖ‘‚ ŒŒ´ĩ´ˇšĒŸœŖ­ŧƒ´‘—ĸŸƒ�ŗœ¯ŽŽĢ—‚Š­ĩŗ¸Ŧ’$€� nŸš´´ŗŗē¸‹‚§ĸ‚�.Ą°œ¯Ž¯ŖƒŒÂēēŽ{„�šģĩˇŧĀà‹€ĸŖ‚�g¤­¯ŽŽ™‘ŠŗÂÁŗ…�/ĢĀĀÁļŠ‘€–™‚�yĻĢž°Ž­”ƒŽžĀÃ˛f†�šĮĀŋ€’‘ǁ�ĒПą¯Ģ‘‚“ÃÁĮLJ�ŠĀÁ€Ā–Ž€‘™�†Ž¨—Ÿ”›“€‘’ŠĻļLJ�†ŋÁ€Ā˜Ž€’™�ƒŽ­ŖĒ Ŧļƒĩ´˛ŗ¨­U†�•Ɓ•ހ’‘‘�ƒŽŽ¯¯ŖĒĩ…´ĩĻŧ‹†�ĢÉÂÂÁÁŋށ”Œ‚�ƒ­¯Ž¯¤Šĩ…´ĩĻŋĸionnkkjdqĻŦŽą´šŠ„‹ŠŠ‹‰f€� �}ǝޝ¤¨ĩ…´ĩĻ쯓ƒ…„‚~zvxz}y™ š›€š—“››œ˜ow¤¯Ž¯¤Šĩ…´ļœ”›Œ€š ™—–•~s{yxtƒŠ°‚Ž °ą˛ŗ’fj ¯Ž¯ŖĒĩ…´ļ𔙆—ƒšˆ|†ƒĨ­‚Ŧ ­Ŧ��ZŽŽ¯ĸĢĩ…´ ļ˜—š‹v‚‹”—‰€| }{‡­Ģ̝˛˛ˇ˜�šŦ­¯ĄŦĩ…´ļ—™šœ—‡ƒ‹‹Š‰–™•’‹‡„•€Š‰ˆw�—Ŧ§ŠžŠĩ…´ļއˆƒ„˜šžŖœ–‹|”•–—˜™˜œˆU…�‘­Ļ§¤˛…´ĩŠŽ‹Ž–žš‘…ƒƒ€…„„…ƒpU†�‹ŦϧžĄŽ°ŗ´ ŗ°¯‘€�„‘’‰‹‡�§ĻĻ žŽŦ­¯°°¯­Ŧ­Œ‘‘…†§‹‡�tž¨ĻŖ›Ž…Ŧ­‡‘’‘†‘’…|‘ŗ ‰�—¯§Ĩ™Ž…Ŧ¯j_w‰’‚“ ‘„xt†Ļą´Ž‰�g‘¤Ŧ™Ģ…Ŧ­špbbhnqqnjhjx‘Ē´ąŗŖf‹�ƒš Ŧ˛ƒąŗ´™ĒĒ ’‚wqrxƒ‘ŸĻ§¤�“�3ƒ–Ąƒ ĸ›ts{ƒŠvzz„zzqg^gmU˙�˙�Ô�h8mk�� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ '08:0��������������������������� .E]vŽĨšĖÜįđøũ˙˙üōÚąz= �������������������l˜˛Ëßīü˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ā R���������������QŲ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙įĸK �����������ô˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ė����������T˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙W����������–˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ģ���������Ė˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ë��������ō˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙i��������G˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙æ�������u˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙†������� ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙á°ŖÃø˙˙˙˙˙˙˙˙˙˙˙˙˙˙ã������Å˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ôn���,˛˙˙˙˙˙˙˙˙˙˙˙˙˙˙S����� ß˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙÷D�������ĸ˙˙˙˙˙˙˙˙˙˙˙˙˙—�����đ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙€��������ß˙˙˙˙˙˙˙˙˙˙˙˙Æ����,û˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ō���������„˙˙˙˙˙˙˙˙˙˙˙˙ā ����;˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Đ���������O˙˙˙˙˙˙˙˙˙˙˙˙é����F˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ë����������J˙˙˙˙˙˙˙˙˙˙˙˙é����H˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙č���������r˙˙˙˙˙˙˙˙˙˙˙˙â����H˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙_���������É˙˙˙˙˙˙˙˙˙˙˙˙Ę����F˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ėĩĒ‚tgT‡˙˙˙˙˙˙˙˙˙˙˙˙˙ �;˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ë×ŲŲä@/ü˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ŧķ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Š�å˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ū��Ķ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Äv|Q����¸˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ō��������˜˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ų-���������q˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙°����������I˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙K����������#÷˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ō����������Ė˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙q������������Ą˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙í��������������HĶ˙˙˙˙˙˙˙˙˙˙˙ûõöööņđđđđđđđđđđđđų‘���������������� ‚ÛÛÛÛÛÛÛÛŨÃ9!### ������������������ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������it32��:d����˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�’�6U{gm;U;$D93@% 88K‰˙Ü�??Z^doo~†‹„|dR=3'%$"#%€$ " "&2@k˙Í�3?K\inw|€‡’˜Ą¨ąšģŦ‹eE0'$%&''&$#$&'())(&# !0Fz˙Ā�,US^jqz‚ˆ•§Ž´ˇšēģ€ēŊžĢ€O/""#! ‚ !!-Io´�3IVfjs{„‹’™ĸ̞ˇē€ģēš¸¸ˇļ€ˇ¸Ŋˇ’Z0"#" � 3Uru*Ž�fT{‘—ĄŦŗ¸ģŧŧģēš¸¸ˇļļĩ´˛€ą ŗš°D&$#™�  4hˆƒ\"Ē�iŽ´ŧēš¸¸…ˇļļ´´˛ą°¯¯€Ž ŗŦz<%'  �  /{¨”x_.Ĩ�W€Ē˛…ģ…ˇļļ´ŗ˛ą°¯„Žąą…@'*Ļ�  2ÁŽ{]D �fwššŧš–쀎ļĩ´ŗ˛ą°¯‡Ž¯´›Q*-"Ē�  L­Įŧ¨sWž� o´šˇģ‡Ģ¸ˇļŗą°¯‹Žą°t2.*Ž� ÄÂÁģ ‡nN›� „玎ŗ‚¸ˇĩąŽŽŗ P,1 ˛� QˇÅŋÁÂąjš� U—š´¸ŖŽģĩąŽŽ´‹:1.´� .ĸÆŋŋˆš�f¯´°ˇ ļ°Ž¯ŗv06' ˇ�ŒÅŋÁžZ™�{ˇ°Žŗ‚ްŽ¯ąe/8!ē� yÃĀģw˜�‹ĩŽŽ¨˛Ž°¯]18ŧ� ožÁ˜�^°Ž°™ˆ˛ŒŽ¯¯Z28ž��œ�nŊĄW—�n­¯Ž˛‰˜°‹Ž¯ą]29›�„—�u¸n—�zŗŽŽą~§¯‹Žŗg2:š�&.440) �•�‰‹–�"‰ŗŽŽĢ{¯‹Žĩx1<˜�=dƒ™§¯ŗĩĩ´°ĒžŠmI$ –� $„d•�UšąŽ¯ ‚˛ŠŽŗŽ4=#–� G€Š¸ģšˇļĩĩ€´ ĩĩˇšģēŽX$“�7c•�gĢ¯Žą“ą‰Ž°¤?<+ •�"iĻēšĩ´ĩ¸ģŽ|5’�%˙”�w˛ŽŽ˛‡›°‰Ž˛X73”�q°ģĩŗ’´ĩēĩ†.’� '?”�ƒ´ŽŽąϝˆŽ´€3;’�WŠģ™´š˛q�+$p’�8˛ŽŽ­|­ˆŽ°¤=<#��"ˆēĩŗ›´ģž8� *'-˙‘�] °ŽŽ§~ąˆŽ´d5/ ��>¨šž´ŗˇĩ[�2!L‘�l­¯Ž¯Ÿ„˛‡Žą—59�Tŗļĸ´ērŽ�-,.˙�uŗŽŽ°–Œ˛‡ŽŗZ6%Ž�Z¸ĩŖ´ŗŧ{�7&X�´ŽŽ˛•ą†Žą–43Ž�Vˇĩ‡´‚ŗ‚´ŗ´ŗģx� 2.*˙Ž�5ŗŽŽ˛†°†Ž´`1Ž�Bĩļ…´€ŗ´ĩ¸ŧŋ€ÁĀŋģˇĩ´ŗŗŒ´ŗŧd�$:!HŽ�Q•ąŽŽ˛Ŗ¯…ްĸ7- Ž�)Ў…´ŗŗ´ˇžÁļœŽ{~‰”ĢŋÃģļ´ŗŗŠ´ŗ´¸D�:+,Ž�a¤¯ŽŽą~ІŽ´w+Ž� Žē„´ ŗŗ´ļžÁŸz[C$� .Jl‹ļÚĩŗŗ‰´ŗŗš°Œ�07#‰�nޝ€Ž|­†Ž°I& �Y纃´ŗŗ´šÃ§uSˆ�`‹ŋŊĩŗŗ†´ŗŗ´ˇŧĮ†Œ�">%C�wŗ€ŽĢ}¯…Žą›-�˛¸ŗŗ‚´ŗ´ēŒZŒ�'mąŋ´ŗ…´ŗŗĩšŊĀĀÆBŒ�<-)�´€Ž§ą…Ž´u �wĮ쎴‚ŗ´šÁ~<�aĨŊ´ŗ´€ŗ´ˇģŋ€ĀÅ ‹� 47#ŋŒ�ˆ´ŽŽ¯Ŗ‚˛…ŽŗO�ēÁĀžģ¸ļĩ´´ˇÂ:‘�\Ŧ烺´ļēŊƒĀĮG‹�'= E‹�/˛ŽŽ¯Ÿ…˛„ޝ¨, �iȁĀŋŊŧģģÐC“�kˇˇ€ĩļ¸ģŊŋ„ĀƔ‹�:&3‹�K•ąŽŽ°š‰˛„Žą”� ¨Ã…Āï]”�(ÂŧŧŊžŋ‡ĀÁŋ(‹�$%'‹�W °ŽŽ°–˛„Ž´w�7ĆĀÅ}–�OŠÃŒĀČeŒ� ˙Š�f¨¯ŽŽą’‘ą„ŽĩY‘�mȅĀÃĒU—�}ƌĀŘ‹�‰Š�lŽ¯ŽŽąŽ”ą„Ž´A�™Å…Āƃ˜�^ĩ‹ĀÁˇ� \Š�r°€Ž˛‹—°„Žą/�´Â„ĀÁĀg˜�5•ÅŒĀÅ8�4Š�sŗ€Ž˛‰š°ƒŽ¯­"�+Á…ĀÃŽP™�€ĮŒĀČW�4Š�~ŗ€Ž˛†°ƒŽ¯¨�@ƅĀŚD™�sČĀČo�*Š�}´€Ž˛„ ¯ƒŽ¯Ļ�QȅĀƐš�oĀÁ‹ĀĮ�Š�}ĩ€Ž ˛‚ĸ¯Ž¯ąą°°ąĻ�[̓ÄÁĀÆŽš�[žÁ‹Āƈ�Š�ˆŗ€Ž+˛§ŗ¯Ŗ‘m}€{7**)(&%$"! R˜””•––˜ŊÁĮš�mŋÁ‹ĀƊ�Š�‡´€Ž ą|ކ{€Ļ‚ ą°€ļĩ´´ŗŗ˛˛ą°°¯¯Ž­ŦĢ̧ŖĸĸĄ Ą‘ÄŖ1™�pÌĀĮ…�*Š�†´€Ž °Ž‡Ŧ˛ą˛Žšˇƒ´Œĩ„ŊħK™�}ƌĀĮy�:Š�†´Žą˛°€Žą•’¸™´¸‹ŽÃģ]˜�3ÆŒĀČf�5Š�„´‡Ž°›Œ¸™´ˇ–ĄÃÆz˜�OŦÌĀĮK� UŠ�†´‡Ž¯ ‡¸™´ļ™Äř>—�uÌĀÁÁ+� Š�†´‡Ž¯Ŗ…¸™´ļĸ“ÅĀÁm–�JšÄŒĀ­ �˙Š�†´‡Ž¯Ļ‚ˇ™´ĩĻÅĀř:H”�vĀĮ†Ž�!˙Š�‡ŗˆŽ§‚ˇ™´ĩĻÅĀÁžW_]\_Q\NNS<TD<OJ'Z�[ąČÅÄÄÃÃÂÂÁÁĀČL�.‹�´ˆŽ¨ˇ™´3ĩĻÅĀĀðjˆ‹‰‰‡†„‚~yvskmfef\aZPZ~ƒ‰Ž“˜žŖ¨­ąĩģŊŧŊŋ­ �6‹�xĩˆŽЁˇ™´ĩ§Å€ĀÅĸalqty|‚„†ˆ‰‰Š‰ŠŠ‰Š€ˆ6ƒ~|zwusqonmlkm]|‚ƒtGGFDCB@?><;:97653;a`acefhiikmobU�ŗˆŽЁˇ™´ĩ¤‘ŁĀĮyswrnjgedccegjlpswz}€‚†ˆˆ„‡€†‰k—°­­Ž‚ŗ…˛€ą�°¯€Ž€­ŦŦąw<�sŗˆŽ¨ˇ™´ ČÃÃÂÂĮ‰’ž€ œš˜–“‹†|xsokhgV]djmqvy}€‚„†ˆv†ąŦąl��u˛­‡Ž¨‚ˇ™´ ļ}ĸŖĻĒ­´Žš…™šš›œœ€ œ›šmofuokgda`€_b]b§°šŦ ޞĄZ��p¯Ē­†Ž§‚ˇ™´ ˇ™mƒ}|{|`Žš™˜qƒmˆ†‡ †„ƒ€}zrez̘Ŧ Žv\]��lŦ¨Ģ…ޝĨ„¸™´¸”ƒžœ w‹š™–pŒh‡‡„…†‡k‚˛–Ŧ­Žf‚�`ύЅŽ¯Ŗ…¸™´¸…›™œw‰›™”o”e†Š„…d–°­“Ŧ¯˛~<‚�]ž¨¨­„ޝ ‡¸™´¸‰ˆ›™œy‰šŽ™“pše‹„…goŖ­‘ŦŽ™jLƒ�Q”ЧŦ„ޝžŠ¸™´š„Œš™ œ{h‹’˜œœ›š‡™ šqžg„‡†……‡„…‡q|ą‘Ŧąk…�1ĢĻDŽް›Œ¸™´¸€š™š–cH`mmr|†—›œ›š™šŽs mblqvz‚…†‡‡††…€„‚k­Ŧ­ĻX…�(‰Ŧύ­ƒŽ°™Ž¸™´¸}’ƒ™-šž‘qqŠŽ€vnklr{…–›œœŒs–Šƒ|uojfeegkoty}„ˆjĸą‚°Š¯ŗŒ>†�„­ĻĻǃް—¸™´ˇz”…™0šœ‰lv– š”Œ‚xplmrz†|mŸ™››œœš—’Œ†xqlhegWpŒŠŠ‹ŒŒ€�”•–šl‡�|ŽĻϧŦ‚ް—¸™´ļx–‡™››„jy—œšš›œš•„zm]Žœ…™ šš›œœ›˜”…„„€ƒ‚‚‡PDJEAA=951/-‡�t­€Ļ§Ģ­€Ž°—¸š´w˜ƒ™šš››œœž‚j|‘–šœœ›šš›œž”h‹œŽ™�š…›Ÿ“Z’�nŠ‚Ļ�§€ŠĢ—ļ™´#˛xŸŸ ŸŸš–’ކ|zy{w`l|xy}„Œ“˜›œ—i‰œ•™ŒWU’�bŖ§…ύ™‰´˛˜´$˛aeZOD9.$E†§°˛ĩšŊÃÄÂŧ˛Ļ˜‹€zx}‡‘•mĄŸ€ž€œ€›š€™ž„Q”�Vš¨…ϧ›†˛¯˛—´ĩ(‡�0@zĨŊĮÉČĮÆÅÆČËÍĖÆˇ—c"-*TY]bfjoswz~‚…ˆ‹Ž’”–˜šœtI•�KĒ…ϧž‚˛Ž¯˛•´ĩŗ%Š�?]u†‘——“ŠzcG' ‹� !(>U–�†Ŧ†ĻĄąŽ­¯ąŗ’´ŗ´ą ĩ�Yw—�}­†ĻĨ}¯€­ްŗ´˛ą°°­´�_•I—�s­†Ļ¨|Ŧ­ޝຊ´ŗ˛ą¯Ž­¯¨ŗ�b¸{˜�l¨†ĻŠ}§ŽŦ­Ž¯ą˛ŗ„´ŗ˛ą°¯Ž€­¯Ĩą�pˇ^˜�]ž¨…ĻĒ€Ą¯­ŦŦ­ޝ°€ą˛ąą°°¯Žƒ­°ĸ°�„ĩ´™�IĒ…ĻĒ…™°­Ŧƒ­‚Žƒ­€Ŧ­°ž ¯�.›´¯Šhš�†­…ĻŠŽą­„Ŧˆ­ƒŦ­ąš­�XŦ˛ŽŗŠ<š�~­…ϧ—†˛­•Ŧ­ą—Ŧ�$‡ĩ¯ŽŽ°o›�q̆ĻŸ˛­•Ŧ­˛•Ē� ]Š´€Ž˛’S›�`Ą¨…ĻĨ{¯­­”Ŧ­ŗ“¨�E™ĩ¯Žŗyœ�F‘Ž…ĻŠ|ŠŽ­“Ŧ­­´��€ĸ�A´ą‚ްĄ^œ�?o”­§ƒĻĒ‚ž°­“Ŧ­Ž´‘€�™��€�N“´˛„Ž´„Ÿ�Z~ĨĢ‚ĻЌ’˛­“Ŧ­Žĩ’”…F��€€�1mĸĩ˛…ޝĢgĄ�k‹ŦЀϧ˜†ŗ­’Ŧ ­­Žļ˜ĩĩŖyF�Ž‚�4g–ąĩ°‡ŽŗŒFĸ� VwŦ§ĻĻĸ}˛­­Ŧ­­Ž¯ˇ˜°Ž°´ĩŠgB" �5Y€ ˛ĩąŠŽątĨ� _ƒŠĒύ{ŦŽ­ŽŦ­¯ą¸Žšą¯°ŗļĩ­ž‹taM?4,(&'*1;HYmƒ˜Š´ˇĩą‡¯‚°ŗ˜UĻ�<n’­Ŧ‚Ą°‘­ޝຏ™¯­…Ŧ­¯¯°°¯­ĒЍŠĒĢŽ¯°¯­ĢЍ…§„Ļ€ĨĒzŠ�Z}Ļ”“´Ž¯€°ąŗ´ĩģxdu€w€yzvIH€KNN‚QTVV€Y€\__bee€hkklhĢ�m€…ēļ‚ˇ‹¸ĩ„]ā�LWt€r€poo…m�l€morrfU˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�Ŧ�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�’�$PBaCkhYn‡€|„qfqgiNŨ�??Z^doo{ƒ„‰‰•—žĸ¤¨Ē §ĨĄš””Ž…xÎ�3?K\inw|€‡’˜Ą¨¯ŗˇˇŗ¯­ŦĢĢĒŠ¨¨€§¨¨ŠĒĢŦ­Ž­Š ˜Ž†uUÁ�,US^jqz‚ˆ•§Ž´ˇšēģēē€šļ°Ē¨§Ĩĸ žœ…›œœžŸĄŖĨ§ĒĢŠœˆxgYĩ�3IVfjs{„‹’™ĸ̞ˇē€ģēš¸¸ˇļˇ ¸ˇŗŦ¨¨Š§¤ ›š‘™ š›ŸĸĨ¨¨Ą€tb*Ž�fT{‘—ĄŦŗ¸ģŧŧģēš¸¸ˇļļĩ´˛ąą˛˛´´°Ē¨Š¨Ŗž››™ ›Ÿĸ¤ĻĨœŒz\"Ē�iŽ´ŧēš¸¸…ˇļļ´´˛ą°¯¯€Ž¯¯ĢŠŠĒĻ œĄ™ šœž Ą§°Ēx_.Ĩ�W€Ē˛…ģ…ˇļļ´ŗ˛ą°¯…ޝŦŠĒĢϟšĻ™ šžžĨļŊĢ{]D �fwššŧš–쀎ļĩ´ŗ˛ą°¯ˆŽ¯­ŠĒŦ§ŸšĒ™ šœžŠģÁŧ¨sWž� o´šˇģ‡Ģ¸ˇļŗą°¯ŒŽ¯ĢĒ­ĒĄ›Ž™ šœœ ŗĀĀÁģ ‡nN›� „玎ŗ‚¸ˇĩąŽŽ¯ŽĒŦ­Ĩœ˛™ šœœĒŊĀŋÁÂąjš� U—š´¸ŖŽģĩąŽŽ¯­Ē¯ĢĄš´™ šœœŖšĀŋŋˆš�f¯´°ˇ ļ°ŽŽ¯ŦĢ°Šž¸™› ´ŋŋÁžZ™�{ˇ°Žŗ‚Ž°ŽŽ¯Ģ­°Ļœē™›žž¯Ŋŋģw˜�‹ĩŽŽ¨˛ŽŽ¯ĢްĨ›ŧ™›žŸĢšÁ˜�^°Ž°™ˆ˛Ž¯Ģ¯¯Ŗš™˜˜œ™›  ŠˇĄW—�n­¯Ž˛‰˜°ŒŽ¯Ģ¯°Ŗš™™šš™˜—€–—˜™™˜—™›ĄĄŠ˛n—�zŗŽŽą~§¯‹Ž¯Ŧ¯ą¤š–™››—Ž…}xvuuvuuvzŠ“™š•™œŖĸ¯…–�"‰ŗŽŽĢ{¯‹Ž¯­Ž˛Ĩš”™šœ•…xvŒ™ŖŠ­¯°ŽĢĨ‚wu™š“™žĨĨ˜S•�UšąŽ¯ ‚˛ŒŽ­˛§›“™ ››Œww‹¤˛ˇ¸ˇļĩĩ€´ ĩĩˇ¸¸´¨‘zt„—›’™ĄĻĨl•�gĢ¯Žą“ąŠŽ¯Ŧ˛Ē’™š›‰t€ĸ´ˇĩ´ĩˇļ¨ˆs€˜š™›ĨĢ„•�w˛ŽŽ˛‡›°‰Ž¯Ŧୟ’™›t‚Џĩŗ’´ĩ¸¯Œr‡›™žŠ¨­”�ƒ´ŽŽąϝˆŽ¯­Žąĸ‘™š™}x¤¸™´ˇŦ‚v–›Ž™šŖ­§Ė’�8˛ŽŽ­|­‰Ž¯Ŧ˛§›™š‘rĩĩœ´¸›rŠœŽ™œĒ¯Ģ’�] °ŽŽ§~ąˆŽ¯ŦąŦ‘™ŠqŖ¸ž´ŗļŽx‚œŽ™ŖŽĨ˛‘�l­¯Ž¯Ÿ„˛‰Ž­°ĸ™˜™„u­ļĸ´ĩ|œ™œĢ°Ē3�uŗŽŽ°–Œ˛‡Ž¯Ŧ°¨›Ž™˜˜„w˛ļ¤´¸…{œ™¤°Ģ¸�´ŽŽ˛•ąˆŽ­­ŸŽ™˜–†v˛ĩ‡´‚ŗ‚´ŗ´ŗˇ„œŒ™ž­ąŠ�5ŗŽŽ˛†°†Ž¯Ŧ¯Ĩš™˜–q¯ļ…´€ŗ´ĩ¸ŧŋ€ÁĀŋģˇĩ´ŗŗŒ´ŗˇ|ˆ›‹™š§ąĻąŽ�Q•ąŽŽ˛Ŗ¯‡ŽŦŦŽ™—“p¤ˇ…´ŗŗ´ˇžÁļœŽ{~‰”ĢŋÃģļ´ŗŗŠ´ŗ´˛s’š‹™Ą°ąŠŽ�a¤¯ŽŽą~ІŽ¯ŦŦ¤™šyš„´ ŗŗ´ļžÁŸz[C$� .Jl‹ļÚĩŗŗ‰´ŗŗšĒs›‹™œŦ˛Ļč�nޝ€Ž|­†Ž¯ĢĒŽ™šŽyĩŗƒ´ŗŗ´šÃ§uSˆ�`‹ŋŊĩŗŗ†´ŗŗ´ˇŧՂœŠ™š§˛Ŧą�wŗ€ŽĢ}¯‡ŽФšŽ™›tŦ¸ŗŗ‚´ŗ´ēŒZŒ�'mąŋ´ŗ…´ ŗŗĩšŊĀĀŋw–‹™Ąą˛Š�´€Ž§ą…ޝŦ§žŽ™›‰‰Å쎴‚ŗ´šÁ~<�aĨŊ´ŗ´€ŗ´ˇģŋ€Āğ{œŠ™Ž˛Ļ˙Œ�ˆ´ŽŽ¯Ŗ‚˛…ޝ¨ŖšŽ™›uŗÂĀžģ¸ļĩ´´ˇÂ:‘�\Ŧ烺´ļēŊƒĀÁx–Š™šŠ˛Ĩ´‹�/˛ŽŽ¯Ÿ…˛†Ž¤žŽ™šŽƒÅĀŋŊŧģģÐC“�kˇˇ€ĩļ¸ģŊŋ„ĀřœŠ™ĸ°ŽĢ‹�K•ąŽŽ°š‰˛…ŽŦ ›Ž™œ{ĻÅĀï]”�(ÂŧŧŊžŋ‡ĀÁšv›Š™›§Ž§‹�W °ŽŽ°–˛„ޝ¨›™xž†ĀÅ}–�OŠÃŒĀѐšŠ™§¤‹�f¨¯ŽŽą’‘ą„Ž¯¤˜Ž™šŽ†Å…ĀÃĒU—�}ƌĀĜœ‹™ž ÄŠ�lŽ¯ŽŽąŽ”ą„Ž¯Ą˜Ž™›Ä…Āƃ˜�^ĩ‹ĀÁąxœ‹™šœ¸Š�r°€Ž˛‹—°…ŽŸ˜Ž™œy¯Â„ĀÁĀg˜�5•ÅŒĀžy™œ­Š�sŗ€Ž˛‰š°…Ž�™›xē…ĀÃŽP™�€ĮŒĀĀ“Œ™žĢŠ�~ŗ€Ž˛†°„Ž­œ™˜z†ĀŚD™�sČĀňŽš‹™ŸĢŠ�}´€Ž˛„ ¯„Ž­œ™•~ÅĀƐš�oĀÁ‹ĀŏŠš‹™ŸŠ�}ĩ€Ž˛‚ĸ¯Ž¯ąą€°¯†›†œ–ƒÉƒÄÁĀÆŽš�[žÁ‹ĀŔ‡›‹™ž—Š�ˆŗ€Ž ˛§ŗ¯Ŗ‘m}€w†vƒw€x tj–””•––˜ŊÁĮš�mŋÁ‹Āŕ†›‹™ŸŠ�‡´€Ž,ą|ކ{€Ļ‚ ą¯°¯¯ŽŽ­­ŦŦĢĢĒĒŠŠ¨§§ĻĨĨŖĸĸĄ Ą‘ÄŖ1™�pÌĀŒˆ›‹™ ¨Š�†´€Ž °Ž‡Ŧ˛ą˛Žšˇƒ´ĩƒļˇ‚ŊħK™�}ƌĀō‹š‹™ŸŦŠ�†´Žą˛°€Žą•’¸™´¸‹ŽÃģ]˜�3ÆŒĀҐš‹™œĢŠ�„´‡Ž°›Œ¸™´ˇ–ĄÃÆz˜�OŦÌĀÂ|–‹™š›ąŠ�†´‡Ž¯ ‡¸™´ļ™Äř>—�uÌĀÁēw›‹™š˜ŋŠ�†´‡Ž¯Ŗ…¸™´ļĸ“ÅĀÁm–�JšÄŒĀÂĒ{œŠ™š—‹?Š�†´‡Ž¯Ļ‚ˇ™´ĩĻÅĀř:H”�vĀő‡›Š™„‹�‡ŗˆŽ§‚ˇ™´ĩĻÅĀÁžW_]\_Q\NNS<TD<OJ'Z�[ąČÅÄÄÃÃÂÂÁÁĀÂ{•Š™šŒa‹�´ˆŽ¨ˇ™´6ĩĻÅĀĀðjˆ‹‰‰‡†„‚~yvskmfef\aZPZ~ƒ‰Ž“˜žŖ¨­ąĩģŊŧŊŋĒt™––—˜€™›hXĒ‹�xĩˆŽЁˇ™´ĩ§Å€ĀÅĸalqty|‚„†ˆ‰‰Š‰ŠŠ‰Š€ˆƒ~|zwusqonmlkm]|‚ƒvowvvu„ts[[f`acefhiikmobU�ŗˆŽЁˇ™´ĩ¤‘ŁĀĮyswrnjgedccegjlpswz}€‚†ˆˆ„‡€†‰k—°­­Ž¯Ž‚­‚Ŧ€ĢĒŦŦ€¯€Ž€­ŦŦąw<�sŗˆŽ¨ˇ™´ ČÃÃÂÂĮ‰’ž€ œš˜–“‹†|xsokhgV]djmqvy}€‚„†ˆv†ąŦąl��u˛­‡Ž¨‚ˇ™´ ļ}ĸŖĻĒ­´Žš…™šš›œœ€ œ›šmofuokgda`€_b]b§°šŦ ޞĄZ��p¯Ē­†Ž§‚ˇ™´ ˇ™mƒ}|{|`Žš™˜qƒmˆ†‡ †„ƒ€}zrez̘Ŧ Žv\]��lŦ¨Ģ…ޝĨ„¸™´¸”ƒžœ w‹š™–pŒh‡‡„…†‡k‚˛–Ŧ­Žf‚�`ύЅŽ¯Ŗ…¸™´¸…›™œw‰›™”o”e†Š„…d–°­“Ŧ¯˛~<‚�]ž¨¨­„ޝ ‡¸™´¸‰ˆ›™œy‰šŽ™“pše‹„…goŖ­‘ŦŽ™jLƒ�Q”ЧŦ„ޝžŠ¸™´š„Œš™ œ{h‹’˜œœ›š‡™ šqžg„‡†……‡„…‡q|ą‘Ŧąk…�1ĢĻDŽް›Œ¸™´¸€š™š–cH`mmr|†—›œ›š™šŽs mblqvz‚…†‡‡††…€„‚k­Ŧ­ĻX…�(‰Ŧύ­ƒŽ°™Ž¸™´¸}’ƒ™-šž‘qqŠŽ€vnklr{…–›œœŒs–Šƒ|uojfeegkoty}„ˆjĸą‚°Š¯ŗŒ>†�„­ĻĻǃް—¸™´ˇz”…™0šœ‰lv– š”Œ‚xplmrz†|mŸ™››œœš—’Œ†xqlhegWpŒŠŠ‹ŒŒ€�”•–šl‡�|ŽĻϧŦ‚ް—¸™´ļx–‡™››„jy—œšš›œš•„zm]Žœ…™ šš›œœ›˜”…„„€ƒ‚‚‡PDJEAA=951/-‡�t­€Ļ§Ģ­€Ž°—¸š´w˜ƒ™šš››œœž‚j|‘–šœœ›šš›œž”h‹œŽ™�š…›Ÿ“Z’�nŠ‚Ļ�§€ŠĢ—ļ™´#˛xœœ›™—“Œ‹†|zy{w`l|xy}„Œ“˜›œ—i‰œ•™ŒWU’�bŖ§…ύ™‰´˛˜´(˛furonmnos`Zƒ ­ąĩšŊÃÄÂŧ˛Ļ˜‹€zx}…ŒŽgˆžœœƒœ›‚š€™ž„Q”�Vš¨…ϧ›†˛¯˛—´<¯m‹’–˜š›œ~{ĨļĀÄÅÅÄÄÅĮÉĘČÁ°•pSljOorstvwy{}~€‚„†ˆ‰‹Ž’”™vM•�KĒ…ϧž‚˛Ž¯˛•´ĩ­wœš„™1šœ˜‹~wzŠ“™œœš”„{x}‹šœš–ŽŒŠ‰‡…ƒ€~|zywvtsrrlRKU–�†Ŧ†ĻĄąŽ­¯ąŗ’´ŗ´Ēvœˆ™šœœ˜’Œ‡„‚‚ƒ†‹‘—›œ›™‚š›ƒœœœši€u—�}­†ĻĨ}¯€­ްŗ´˛ą°°§vœ™�šƒ›�š˜™›–g…’I—�s­†Ļ¨|Ŧ­ޝຊ´ ŗ˛ą¯Ž­¯Ŗxœ¯™›’e†ļx˜�l¨†ĻŠ}§ŽŦ­Ž¯ą˛ŗ„´ŗ˛ą°¯Ž€­¯ yœŽ™œ‰d´^˜�]ž¨…ĻĒ€Ą¯­ŦŦ­ޝ°€ą˛ąą°°¯Žƒ­¯žzœŦ™ššzg›˛´™�IĒ…ĻĒ…™°­Ŧƒ­‚Žƒ­€Ŧ­°œ{œĢ™œis§ą¯Šhš�†­…ĻŠŽą­„Ŧˆ­ƒŦ­°™}œŠ™ œ˜ya‰°¯ŽŗŠ<š�~­…ϧ—†˛­•Ŧ­ą—€œĻ™šœ™`oĸ˛€Ž°o›�q̆ĻŸ˛­•Ŧ­ą•qœœŖ™šœ—€_b¯°€Ž˛’S›�`Ą¨…ĻĨ{¯­­”Ŧ­˛”Ff‚—œ›™ šœšŽuZ]†Ģą‚Žŗyœ�F‘Ž…ĻŠ|ŠŽ­“Ŧ ­­ŗ’DQQ^v™œ›š•™ šœœ˜{bS^…¨˛¯‚ްĄ^œ�?o”­§ƒĻĒ‚ž°­“Ŧ­Ž´’`YOOPTcw‰•›œœ›šˆ™šš››œœš–ށp_QQgŒŠ˛¯„Ž´„Ÿ�Z~ĨĢ‚ĻЌ’˛­“Ŧ­Žĩ’—¤ƒeT€O S[gu‚Š’–˜š‚›š™˜•‘…|sg\SMO_|›Žą¯…ޝĢgĄ�k‹ŦЀϧ˜†ŗ­’Ŧ,­­Žļ—˛˛­›hXPNNOPSW\`dhiijgeb_ZWTPNLNUe}—ĒఈŽŗŒFĸ� VwŦ§ĻĻĸ}˛­­Ŧ­­Ž¯ˇ˜°ŽŽ°˛Žĸ|j]UQO‰N PU\izŽĄ­ąą¯ŠŽątĨ� _ƒŠĒύ{ŦŽ­ŽŦ­¯ą¸Žšą‚¯°˛ŗ°Š •‰€wrmjijmqx‰”ŸŠ°ŗŗą°‡¯‚°ŗ˜UĻ�<n’­Ŧ‚Ą°‘­ޝຏ™¯­†Ŧ�­ŦĢĒ€ŠĒĢ€ŦĢĒŠ¨¨…§„Ļ€ĨĒzŠ�Z}Ļ”“´Ž¯€°ąŗ´ĩģxdu€w€yzvIH€K€NQTVV€Y€\__bee€hkklhĢ�m€…ēļ‚ˇ‹¸ĩ„]ā�LWt€r€poo…m�l€morrfU˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�Ŧ�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�’�$PBeChfTiƒ|x€l`nciNŨ�??Z^doo{€…„‰‡Šˆ—›œ ĸ Ÿ™“Ž‰Š‡xÎ�3?K\inw|€‡’˜Ą¨¯´¸ļąĢ§¤ŖŖĸĄ€ ŸŸ ĄĄĸŖ¤ĨĻĨĄ˜‘ˆrUÁ�,US^jqz‚ˆ•§Ž´ˇšēģēē€š ĩ­ĨĄ ĄĄŸš—–”“…’““”•—™›ŸĄŖ ”‚tgYĩ�3IVfjs{„‹’™ĸ̞ˇē€ģēš¸¸ˇļˇ ¸¸ą§Ą ĄŸœ˜”’‘‘ ‘’”—šŸŸ™‰~te*Ž�fT{‘—ĄŦŗ¸ģŧŧģēš¸¸ˇļļĩ´˛ąą˛˛´´­¤ĄĄ ›–’‘𐠒”—™›ž™Œz\"Ē�iŽ´ŧēš¸¸…ˇļļ´´˛ą°¯¯€Ž¯Ž¨€ĸž—“‘  ‘“–—˜Ÿ­Ēx_.Ĩ�W€Ē˛…ģ…ˇļļ´ŗ˛ą°¯…ޝǪĸŖž–’ϐ ‘”••ž´ŊĢ{]D �fwššŧš–쀎ļĩ´ŗ˛ą°¯ˆŽ¯­¤ŖĨŸ–‘ǐ ‘“”•ŖēÁŧ¨sWž� o´šˇģ‡Ģ¸ˇļŗą°¯ŒŽ¯¨ŖĻĸ™’ސ ‘““˜°ĀĀÁģ ‡nN›� „玎ŗ‚¸ˇĩąŽŽ¯­ĨĨĻ”˛ ‘“”ĨŊĀŋÁÂąjš� U—š´¸ŖŽģĩąŽŽ¯ĢŖ§¤™‘´ ‘““¸Áŋŋˆš�f¯´°ˇ ļ°ŽŽ¯Š¤¨Ą•¸’”˜ąĀŋÁžZ™�{ˇ°Žŗ‚Ž°ŽŽ¯§ĻŠž“琒•–ŦŊŋģw˜�‹ĩŽŽ¨˛ŽŽ¯§§Šœ’ŧ’––§šÁ˜�^°Ž°™ˆ˛Ž¯Ļ§Š›’œ’——ĨˇĄW—�n­¯Ž˛‰˜°ŒŽ¯Ļ¨Š›‘™€‘ŽŽš’™˜Ļ˛n—�zŗŽŽą~§¯‹Ž¯¨¨Ēœ‘–‘’ކ}vs€q�r€qtz‚А‘•“›š­…–�"‰ŗŽŽĢ{¯‹Ž¯Ē§Ģ’”‘’Œ~rs~Œ™ŖĒް°¯ĢĻupx‡‘“•œ—S•�UšąŽ¯ ‚˛ŠŽ¯ŦĻĢ ’“ ‘‘ƒqtФ˛ˇ¸ˇļĩĩ€´ ĩĩˇ¸¸ĩŠ‘xo}Ž’’™žŸl•�gĢ¯Žą“ą‹ŽĨĢŖ”’‘’€oĸĩ¸ĩ´ĩ¸ˇ¨‡oy‘’ĸ3”�w˛ŽŽ˛‡›°‰Ž¯§ĒĻ–’’†oĒ¸ĩŗ’´ĩ¸¯Œn‘•ĸ Ĩ”�ƒ´ŽŽąϝˆŽ¯Ģ§Ē𑒐vv¤¸™´ˇŦpŒ’ސ‘›ĨŸĖ’�8˛ŽŽ­|­ŠŽĨ̟’‘‰mļĩœ´¸›o‚’ސ”ĸ§Ŗ’�] °ŽŽ§~ąˆŽ¯¨ŠĨ•‘‚nŖ¸ž´ŗˇ¯wz’ސ›§Ž‘�l­¯Ž¯Ÿ„˛ˆŽ­ĻК|s­ļĸ´ĩ€u’“Ŗ¨¤3�uŗŽŽ°–Œ˛‡Ž¯§Š ’ސ|u˛ļ¤´¸…u’œ¨Ŗ´�´ŽŽ˛•ą‡Ž­ĻϖސŽt˛ĩ‡´‚ŗ‚´ŗ´ŗˇƒw“Œ•ĻŠĸ�5ŗŽŽ˛†°†Ž¯§§‘…n¯ļ…´€ŗ´ĩ¸ŧŋ€ÁĀŋģˇĩ´ŗŗŒ´ŗˇz’‹‘ ĒžĢŽ�Q•ąŽŽ˛Ŗ¯‡ŽĨ¤•ސŽ‹k¤ˇ…´ŗŗ´ˇžÁļœŽ{~‰”ĢŋÃģļ´ŗŗŠ´ŗ´˛q‰‘‹™ŠŠĸŽ�a¤¯ŽŽą~ІŽ¯ĒĨœ‘ސ‘sš„´ ŗŗ´ļžÁŸz[C$� .Jl‹ļÚĩŗŗ‰´ŗŗšĢn’‹”ĨĒžÄ�nޝ€Ž|­†Ž¯Ĩĸ”ސ‘…wﺃ´ŗŗ´šÃ§uSˆ�`‹ŋŊĩŗŗ†´ŗŗ´ˇŧŏz’А‘ŸĢ¤Ģ�wŗ€ŽĢ}¯†Ž­ĸœ‘ސ’oŦ¸ŗŗ‚´ŗ´ēŒZŒ�'mąŋ´ŗ…´ŗŗĩšŊ€Āt‹™ĒĒĄ�´€Ž§ą…ޝ¨Ÿ•ސ’€ˆÅ쎴‚ŗ´šÁ~<�aĨŊ´ŗ´€ŗ´ˇģŋ€Āğu“А”§ĒŸ˙Œ�ˆ´ŽŽ¯Ŗ‚˛…ޝŖš‘ސ’p´ÂĀžģ¸ļĩ´´ˇÂ:‘�\Ŧ烺´ļēŊƒĀÁvŒŠ’ĄĢž¯‹�/˛ŽŽ¯Ÿ…˛†Ž•ސ‘…‚ŁĀŋŊŧģģÐC“�kˇˇ€ĩļ¸ģŊŋ„Āęy’АšŠĻĻ‹�K•ąŽŽ°š‰˛…Ž̘’ސ“tĻÅĀï]”�(ÂŧŧŊžŋ‡ĀÁšq‘А“ Ļ ‹�W °ŽŽ°–˛„ޝĨ’tž†ĀÅ}–�OŠÃŒĀه‘А•Ÿœ‹�f¨¯ŽŽą’‘ą„Ž¯ Ž‘……Å…ĀÃĒU—�}ƌĀĜy’‹–˜ÄŠ�lŽ¯ŽŽąŽ”ą„Ž¯›Ž’yœÄ…Āƃ˜�^ĩ‹ĀÁ˛r“‹‘”ąŠ�r°€Ž˛‹—°„ޝ˜Ž“t¯Â„ĀÁĀg˜�5•ÅŒĀŋuŒ“ŖŠ�sŗ€Ž˛‰š°…Ž–Ž‘sģ…ĀÃŽP™�€ĮŒĀÄ~ŠŒ•¤Š�~ŗ€Ž˛†°„Ž­”ŽŽw†ĀŚD™�sČĀ҅‘‹–ĸŠ�}´€Ž˛„ ¯„Ž­”‹|ÅĀƐš�oĀÁ‹ĀŎ‘‹•”Š�}ĩ€Ž˛‚ĸ¯Ž¯ąą€°Ž”€‘‡’‚“ŒÉƒÄÁĀÆŽš�[žÁ‹Āœ’‹•ŽŠ�ˆŗ€Ž ˛§ŗ¯Ŗ‘m}€sŠq‚r nh–””•––˜ŊÁĮš�mŋÁ‹ĀŔ~’‹•”Š�‡´€Ž ą|ކ{€Ļ‚ ą€°¯¯ŽŽ­­ŦŦĢĢĒĒŠ¨¨§ĻĻĨŖĸĸĄ Ą‘ÄŖ1™�pÌĀő‘‹–ĸŠ�†´€Ž °Ž‡Ŧ˛ą˛Žšˇƒ´ĩƒļˇ‚ŊħK™�}ƌĀԃ‘‹–ĨŠ�†´Žą˛°€Žą•’¸™´¸‹ŽÃģ]˜�3ÆŒĀчŒ“ĻŠ�„´‡Ž°›Œ¸™´ˇ–ĄÃÆz˜�OŦÌĀÂz‹‘“ąŠ�†´‡Ž¯ ‡¸™´ļ™Äř>—�uÌĀÁēs‘‹‘ŋŠ�†´‡Ž¯Ŗ…¸™´ļĸ“ÅĀÁm–�JšÄŒĀÂĒt“А‘Ž…?Š�†´‡Ž¯Ļ‚ˇ™´ĩĻÅĀř:H”�vĀő~’А“z}‹�‡ŗˆŽ§‚ˇ™´ĩĻÅĀÁžW_]\_Q\NNS<TD<OJ'Z�[ąČÅÄÄÃÃÂÂÁÁĀÂxŒŠ‘„\…‹�´ˆŽ¨ˇ™´4ĩĻÅĀĀðjˆ‹‰‰‡†„‚~yvskmfef\aZPZ~ƒ‰Ž“˜žŖ¨­ąĩģŊŧŊŋĒn€Ž’bTĒ‹�xĩˆŽЁˇ™´ĩ§Å€ĀÅĸalqty|‚„†ˆ‰‰Š‰ŠŠ‰Š€ˆƒ~|zwusqonmlkm]|‚ƒvmtssrqpYYf`acefhiikmobU�ŗˆŽЁˇ™´ĩ¤‘ŁĀĮyswrnjgedccegjlpswz}€‚†ˆˆ„‡€†‰k—°­­Ž¯€Ž‚­Ŧ€ĢŦŦ€¯€Ž€­ŦŦąw<�sŗˆŽ¨ˇ™´ ČÃÃÂÂĮ‰’ž€ œš˜–“‹†|xsokhgV]djmqvy}€‚„†ˆv†ąŦąl��u˛­‡Ž¨‚ˇ™´ ļ}ĸŖĻĒ­´Žš…™šš›œœ€ œ›šmofuokgda`€_b]b§°šŦ ޞĄZ��p¯Ē­†Ž§‚ˇ™´ ˇ™mƒ}|{|`Žš™˜qƒmˆ†‡ †„ƒ€}zrez̘Ŧ Žv\]��lŦ¨Ģ…ޝĨ„¸™´¸”ƒžœ w‹š™–pŒh‡‡„…†‡k‚˛–Ŧ­Žf‚�`ύЅŽ¯Ŗ…¸™´¸…›™œw‰›™”o”e†Š„…d–°­“Ŧ¯˛~<‚�]ž¨¨­„ޝ ‡¸™´¸‰ˆ›™œy‰šŽ™“pše‹„…goŖ­‘ŦŽ™jLƒ�Q”ЧŦ„ޝžŠ¸™´š„Œš™ œ{h‹’˜œœ›š‡™ šqžg„‡†……‡„…‡q|ą‘Ŧąk…�1ĢĻDŽް›Œ¸™´¸€š™š–cH`mmr|†—›œ›š™šŽs mblqvz‚…†‡‡††…€„‚k­Ŧ­ĻX…�(‰Ŧύ­ƒŽ°™Ž¸™´¸}’ƒ™-šž‘qqŠŽ€vnklr{…–›œœŒs–Šƒ|uojfeegkoty}„ˆjĸą‚°Š¯ŗŒ>†�„­ĻĻǃް—¸™´ˇz”…™0šœ‰lv– š”Œ‚xplmrz†|mŸ™››œœš—’Œ†xqlhegWpŒŠŠ‹ŒŒ€�”•–šl‡�|ŽĻϧŦ‚ް—¸™´ļx–‡™››„jy—œšš›œš•„zm]Žœ…™ šš›œœ›˜”…„„€ƒ‚‚‡PDJEAA=951/-‡�t­€Ļ§Ģ­€Ž°—¸š´w˜ƒ™šš››œœž‚j|‘–šœœ›šš›œž”h‹œŽ™�š…›Ÿ“Z’�nŠ‚Ļ�§€ŠĢ—ļ™´#˛xœ›™—”Œ‹†|zy{w`l|xy}„Œ“˜›œ—i‰œ•™ŒWU’�bŖ§…ύ™‰´˛˜´%˛ftqnkjjkm[YƒĄ­˛ĩšŊÃÄÂŧ˛Ļ˜‹€zx}…ŒŽh‰žœ‚œ›‚š€™ž„Q”�Vš¨…ϧ›†˛¯˛—´°i‚†‰Œ‘’““‡wxŒĨˇÁ€Å'ÄÄÅĮÉĘČÁ°•oPggMmprsuwxz|~€‚„†ˆŠŒ’”™uM•�KĒ…ϧž‚˛Ž¯˛•´ĩ­r“‘„1‘“‚vrv‰’˜›œ™”Œ‚xsvƒ“‘‡‡…ƒ‚€}{zxwutrqponmmgPKU–�†Ŧ†ĻĄąŽ­¯ąŗ’´ŗ´Ģq’ˆ‘“’‰„|{{|~‚ˆ’“‘ƒ‘’†“”c~u—�}­†ĻĨ}¯€­ްŗ´˛ą°°§q“‘‘‚’�‘˜’aƒ“I—�s­†Ļ¨|Ŧ­ޝຊ´ ŗ˛ą¯Ž­¯Ŗr“¯’‰`„ļx˜�l¨†ĻŠ}§ŽŦ­Ž¯ą˛ŗ„´ŗ˛ą°¯Ž€­¯Ąs“ސ“_Ž´^˜�]ž¨…ĻĒ€Ą¯­ŦŦ­ޝ°€ą˛ąą°°¯Žƒ­¯žs“Ŧ‘‘sb𞴙�IĒ…ĻĒ…™°­Ŧƒ­‚Žƒ­€Ŧ­°œt“̐“ˆco§ą¯Šhš�†­…ĻŠŽą­„Ŧˆ­ƒŦ­°™v“А ’r\‡°°ŽŗŠ<š�~­…ϧ—†˛­•Ŧ­ą—x“§“z[l ˛€Ž°o›�q̆ĻŸ˛­•Ŧ­ą•k“’Ŗ‘“ŽyZ^¯°€Ž˛’S›�`Ą¨…ĻĨ{¯­­”Ŧ­˛”C`zŽ“‘ ‘“‘†oUY‚Ēą‚Žŗyœ�F‘Ž…ĻŠ|ŠŽ­“Ŧ ­­ŗ’@MMYp„“’‘• ‘’“…t]NY§˛¯‚ްĄ^œ�?o”­§ƒĻĒ‚ž°­“Ŧ­Ž´’\UKKLP^p‘“’’‘ˆ€‘’““‘ކyjZMMc‰¨˛¯„Ž´„Ÿ�Z~ĨĢ‚ĻЌ’˛­“Ŧ­Žĩ’—ĸ€aO€K OVanzƒ‰‘’‘‘Œ‰„}ulaWOIK[x˜­ą¯…ޝĢgĄ�k‹ŦЀϧ˜†ŗ­’Ŧ,­­Žļ—˛˛Ŧ™~dSLJJKLOSX[_ccddb_]ZVSPLJHJQ`y”ŠąąˆŽŗŒFĸ� VwŦ§ĻĻĸ}˛­­Ŧ­­Ž¯ˇ˜°ŽŽ°˛ŽĄŽyfYQMK‰J LPXew‹ŸŦąą¯ŠŽątĨ� _ƒŠĒύ{ŦŽ­ŽŦ­¯ą¸Žšą‚¯°˛ŗ°ŠŸ“‡}unigfgint|‡’ž¨¯ŗŗą°‡¯‚°ŗ˜UĻ�<n’­Ŧ‚Ą°‘­ޝຏ™¯­†Ŧ­Ŧ­­ŦĢĒ€ŠĒĢ€ŦĢĒŠ¨¨…§„Ļ€ĨĒzŠ�Z}Ļ”“´Ž¯€°ąŗ´ĩģxdu€w€yzvIH€K€NQTVV€Y€\__bee€hkklhĢ�m€…ēļ‚ˇ‹¸ĩ„]ā�LWt€r€poo…m�l€morrfU˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�˙�Ŧ�t8mk��@����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#*?@Z_^j‚ƒl_Q;" ���������������������������������������������������������������������������������������������� ,ATh€ĒžËÔäę÷ú˙˙˙˙˙˙˙˙˙˙˙˙˙˙õá×k;�������������������������������������������������������������������������������$6Mf}–°ÂÖčķû˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙éŋŠL������������������������������������������������������������������*=Vq¨ÁØęö˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙īŊy9������������������������������������������������������&>ZuŦÅÚėų˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ڗN�������������������������������������������������Ž×éø˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙čĢX��������������������������������������������få˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ëĒX����������������������������������������@Č˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ëĒO�����������������������������������Ĩ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ã”:��������������������������������Œ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ís �����������������������������Î˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙u�����������������������������-û˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ė����������������������������k˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ū3����������������������������Ž˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙‡���������������������������ä˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ų��������������������������A˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙=��������������������������}˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙��������������������������ŗ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ø������������������������ä˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙=������������������������9˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙������������������������l˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ø�����������������������ĸ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙a����������������������Î˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙č���������������������đ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙œ��������������������G˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ū9��������������������v˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ŧ�������������������Ą˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙H�������������������É˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ŗ�����������������é˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ú.�����������������/ũ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ëŌÆĘáõ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙�����������������T˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ær5�� !Oę˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ā ����������������}˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ēC�����������|î˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙O����������������¤˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ōc��������������� ŗ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˜����������������Â˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ũ.������������������‹˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ú�������������� Û˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ū#��������������������„˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ũ0��������������đ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ö5����������������������Š˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙h��������������/ü˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙p�����������������������ā˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙š��������������F˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ė������������������������Z˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ã�������������_˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙V�������������������������Ī˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙â �������������v˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ų��������������������������q˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ö$�������������Œ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙���������������������������&÷˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙5�������������›˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙R���������������������������Í˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙I�������������´˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ü-����������������������������¨˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙U�������������ģ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ę����������������������������”˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙T�������������Á˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ä����������������������������x˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙T������������×˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙æ����������������������������Œ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙T������������×˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙÷$����������������������������Ÿ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙U������������×˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙D����������������������������Å˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙J������������×˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙x���������������������������î˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙4������������×˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Å���������������������������S˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ô!������������×˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ū5��������������������������ļ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ā ������������×˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙¨�������������������������:˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ã������������×˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙J����������������������ž˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˜������������Ø˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ôáĪÉÃĒĢ“ˆ€egRD@))��r˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙c�������������Ë˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙úúîčæŌŅŋ´÷˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ø!��������������ē˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ô ™šššššššššœ“�ŧ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙í�Ĩ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙°��š˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙s��Š˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ú��s˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙´����Z˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ü.�����G˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ŋC������2ū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Á��������ķ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙v��������į˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ũ1��������Ô˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ņ���������ģ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ë8>>>>>>>>@'����������ž˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙“���������������������˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙—���������������������]˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙�����������������������>˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙€������������������������"õ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ß ������������������������ ā˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙†��������������������������Á˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ö&��������������������������›˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ē���������������������������v˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙F���������������������������L˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ė���������������������������&ø˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙k���������������������������� Ū˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙č�����������������������������ē˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙������������������������������‹˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ü1������������������������������W˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ļ�������������������������������(˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Q�������������������������������•˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Ø���������������������������������DŅ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙v�����������������������������������~õ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ņ�������������������������������������,¸˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ž����������������������������������������]ã˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙9�����������������������������������������˜˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙Å��������������������������������������������AÍ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙į|zzzzzzzzzz{vZXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\,���������������������������������������������yķ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙öJ���������������������������������������������������������������������������������������������������(˜¯ŦŦŦŦŦŦŦŦŦŦŦŦŦŦŦŦŦŦŦŦŦŦ­‘0�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������