[med-svn] [dcm2niix] 01/01: New upstream version 1.0.20170429
Ghislain Vaillant
ghisvail-guest at moszumanska.debian.org
Thu May 11 18:39:07 UTC 2017
This is an automated email from the git hooks/post-receive script.
ghisvail-guest pushed a commit to branch upstream/latest
in repository dcm2niix.
commit 9e8d453f5e4397fd76ae9e6d88621d0406a38d15
Author: Ghislain Antony Vaillant <ghisvail at gmail.com>
Date: Thu May 11 09:42:22 2017 +0100
New upstream version 1.0.20170429
---
.gitignore | 3 +
.travis.yml | 33 +-
CMakeLists.txt | 38 +-
README.md => COMPILE.md | 164 +----
Dockerfile | 16 +
README.md | 192 ++----
SuperBuild/External-OPENJPEG.cmake | 15 +
SuperBuild/External-YAML-CPP.cmake | 15 +
SuperBuild/SuperBuild.cmake | 111 ++++
appveyor.yml | 32 +-
console/CMakeLists.txt | 169 +++--
console/jpg_0XC3.cpp | 78 ++-
console/main_console.cpp | 84 ++-
console/main_console_batch.cpp | 107 ++-
console/makefile | 2 +-
console/nifti1_io_core.cpp | 28 +-
console/nifti1_io_core.h | 7 +
console/nii_dicom.cpp | 1191 ++++++++++++++++++---------------
console/nii_dicom.h | 58 +-
console/nii_dicom_batch.cpp | 1273 ++++++++++++++++++++----------------
console/nii_dicom_batch.h | 25 +-
console/nii_foreign.cpp | 404 ++++++++++++
console/nii_foreign.h | 19 +
console/nii_ortho.cpp | 16 +-
console/nii_ortho.h | 3 +
console/print.h | 45 ++
ucm.cmake => console/ucm.cmake | 1245 ++++++++++++++++++-----------------
console/ujpeg.cpp | 290 +++++++-
console/ujpeg.h | 8 -
docs/CMakeLists.txt | 36 +
docs/source/conf.py | 340 ++++++++++
docs/source/dcm2niibatch.rst | 81 +++
docs/source/dcm2niix.rst | 73 +++
docs/source/index.rst | 22 +
license.txt | 4 +-
35 files changed, 3964 insertions(+), 2263 deletions(-)
diff --git a/.gitignore b/.gitignore
index e43b0f9..4afaec8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,4 @@
.DS_Store
+build/
+bin/
+
diff --git a/.travis.yml b/.travis.yml
index 2f84669..79f8259 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,39 +1,30 @@
language: cpp
+
+git:
+ depth: 1
+
matrix:
include:
- # Stable channel
- os: linux
- env: TARGET=linux
dist: trusty
- sudo: required
- addons:
- apt:
- packages: pkg-config libyaml-cpp-dev libyaml-cpp0.5 cmake libboost-dev
+ env: TARGET=lnx
- os: osx
- env: TARGET=osx
+ env: TARGET=mac
-before_install:
- - "if [ ${TRAVIS_OS_NAME} = 'osx' ]; then brew update && brew install yaml-cpp; fi"
script:
- - mkdir build && cd build && cmake -DBATCH_VERSION=ON .. && make && cd -
+ - mkdir build && cd build && cmake -DBATCH_VERSION=ON -DUSE_OPENJPEG=ON .. && make && cd -
before_deploy:
- - zip ${PROJECT_NAME}-${TRAVIS_TAG}-${TARGET}.zip build/bin/dcm2niibatch build/bin/dcm2niix
+ - 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
- # TODO Regenerate this api_key for your project, this one won't work for you. Here's how:
- # - Go to 'https://github.com/settings/tokens/new' and generate a Token with only the
- # `public_repo` scope enabled
- # - Call `travis encrypt $github_token` where $github_token is the token you got in the previous
- # step and `travis` is the official Travis CI gem (see https://rubygems.org/gems/travis/)
- # - Enter the "encrypted value" below
api_key:
- secure: aLyVgmimaRMCvh2YF4ek0ZseWqKpqCSrvvYdf5rxhZUBnYK+KKlHBsPudtmWbHTh19PPC1C3wSHa8y94IObGl3iRRTICh5e/Oz9xzCiPYlQ0ZGmtM+MfRtUq2xFRc6aaAkrnfW0ur3uxk4a7ZBIOMg3D7kG7Ah4dg5dVf26OmpSBdEijo9bcvOLHXwZIc1bby+MJl8kxzdlwaV7UTrqHQFh+tmMsoO0GQcQpCP3y45f5/8aTkHRmnC1IKCiHKDZpRFDWhdoz02NGWcCnoH43iSwOobe1jacHicmZ7dNHKT/e1tfUIPpbO81fvQ7FHifvBRoO64Wvl5l/IaoDcngv6o11JlWRxIuZnr01oDv+DW8kv9POLYeh2xzMaQkZ8NkPZl82hqr8t0q0OtXZjm/Hysdcvr0T26hbwqs1sOkAAaeRdR0zl/Log53hNqM4HaZZ0CnKGU8dSatC+NOgHEfz68fHArf8DUdK [...]
+ secure: sVIYRakcEQdMPEdGSSePtMVCMQvaohqV7NNzEErAgZ+b/4ofv2aPpJb5kNTv3JRl2FrPy7iXJ8lOUQ/95pqvimX6jv5ztksTNXtSMnHZNbjjWwIc99enPY+mSdWMO2lb9vGBWQ9GNfXjmk7MgtDHPjjygbuZfUw9fmGy4ocxkws=
file_glob: true
- file: ${PROJECT_NAME}-${TRAVIS_TAG}-${TARGET}.*
- # don't delete the artifacts from previous phases
+ file: dcm2niix*.zip
skip_cleanup: true
- # deploy when a new tag is pushed
on:
tags: true
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 707d390..e392dea 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,37 +1,9 @@
-project(dcm2niix)
-cmake_minimum_required(VERSION 2.6)
-
-include(ucm.cmake)
+cmake_minimum_required(VERSION 2.8.11)
-# Option1: Choose whether to build the batch version
-option(BATCH_VERSION "Build dcm2niibatch for multiple conversions as well" OFF)
-message(${BATCH_VERSION})
-
-# Option2: Choose whether to use static runtime
-option(USE_STATIC_RUNTIME "Use static runtime" ON)
-if(USE_STATIC_RUNTIME)
- ucm_set_runtime(STATIC)
-else()
- ucm_set_runtime(DYNAMIC)
+if(COMMAND CMAKE_POLICY)
+ CMAKE_POLICY(SET CMP0003 NEW)
endif()
-# Option3: Choose build type
-set(CMAKE_BUILD_TYPE "Release" CACHE STRING
- "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
-
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin")
-
-#
-# Zlib
-#
-#find_package(ZLIB)
-
-# Predefined permission set to enforce proper permissions
-# during install even if files in the sources have different
-# settings
-set(FILE_PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
+project(dcm2niix)
-##
-## Sub-projects
-##
-add_subdirectory(console)
+include(${CMAKE_SOURCE_DIR}/SuperBuild/SuperBuild.cmake)
diff --git a/README.md b/COMPILE.md
similarity index 50%
copy from README.md
copy to COMPILE.md
index 9d6b339..ed69b48 100644
--- a/README.md
+++ b/COMPILE.md
@@ -1,179 +1,61 @@
-[![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/xdkqua54f90x4049/branch/master?svg=true)](https://ci.appveyor.com/project/chrisfilo/dcm2niix)
-
## About
-dcm2niix is a designed to convert neuroimaging data from the DICOM format to the NIfTI format. For details and compiled versions visit the [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 or have similar licenses. See the license.txt file for more details.
-
-## Versions
-
-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.
+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.
-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).
+## Choosing your compiler
-12-Dec-2015
- - Support PAR/REC FP values when possible (see PMC3998685).
+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` [...]
-11-Nov-2015
- - Minor refinements.
-12-June-2015
- - Uses less memory (helpful for large datasets).
+## Building the command line version without cmake
-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.
-
-## 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 file [...]
-
-**Optional batch processing version:**
-
-Perform a batch conversion of multiple dicoms using the configurations specified in a yaml file.
-```bash
-dcm2niibatch batch_config.yml
-```
+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
-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.
-
-## Build
-
-### Build command line version with cmake (Linux, Windows, OSx)
-
-```bash
-mkdir build && cd build
-cmake ..
-make
+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 -o dcm2niix -DmyDisableOpenJPEG -DmyDisableJasper
```
-`dcm2niix` will be created in the `bin` folder
-
-**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 the following libraries:
-- pkg-config
-- yaml-cpp
-- a compiler that supports c++11
+The following sub-sections list how you can modify this basic recipe for your needs.
-e.g. the dependencies can be installed as follows:
+##### ZLIB BUILD
+ If we have zlib, we can use it (-lz) and disable [miniz](https://code.google.com/p/miniz/) (-myDisableMiniZ)
-Ubuntu 14.04
```
-sudo apt-get install pkg-config libyaml-cpp-dev libyaml-cpp0.5 cmake libboost-dev
-```
-OSX
-```
-brew install pkg-config yaml-cpp cmake
+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 -dead_strip -o dcm2niix -lz -DmyDisableMiniZ
```
+##### MINGW 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
+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++ -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 -o dcm2niix -DmyDisableOpenJPEG -DmyDisableJasper
+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 -o dcm2niix -static-libgcc
```
-The following sub-sections list how you can modify this basic recipe for your needs.
+##### DISABLING CLASSIC JPEG
-##### ZLIB BUILD
- If we have zlib, we can use it (-lz) and disable [miniz](https://code.google.com/p/miniz/) (-myDisableMiniZ)
+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 `my [...]
```
-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 -dead_strip -o dcm2niix -lz -DmyDisableMiniZ
+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 -o dcm2niix -DmyDisableClassicJPEG -DmyDisableOpenJPEG -DmyDisableJasper
```
-##### MINGW BUILD
+##### USING LIBJPEG-TURBO TO DECODE CLASSIC JPEG
-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.
+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 execu [...]
```
-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 -o dcm2niix -static-libgcc
+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 -o dcm2niix -DmyDisableOpenJPEG -DmyDisableJasper -DmyTurboJPEG -I/opt/libjpeg-turbo/include /opt/libjpeg-turbo/lib/libturbojpeg.a
```
##### JPEG2000 BUILD
- If you want to build this with JPEG2000 decompression support using OpenJPEG. You will need to have the OpenJPEG 2.1 libraries installed (https://code.google.com/p/openjpeg/wiki/Installation). I suggest building static libraries...
- svn checkout http://openjpeg.googlecode.com/svn/trunk/ openjpeg-read-only
+ If you want to build this with JPEG2000 decompression support using OpenJPEG. You will need to have the OpenJPEG 2.1 libraries installed (https://code.google.com/p/openjpeg/wiki/Installation). I suggest building static libraries where you would [download the code](https://github.com/uclouvain/openjpeg) and run
+```
cmake -DBUILD_SHARED_LIBS:bool=off .
make
sudo make install
+```
You should then be able to run then run:
```
@@ -232,7 +114,7 @@ file ./dcm2niix
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 SUPPORT - FOLLOWING LINES FOR FUTURE VERSIONS
+##### 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
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..6f394ce
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,16 @@
+FROM ubuntu:trusty
+MAINTAINER <alexcohen at gmail.com> # 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"]
diff --git a/README.md b/README.md
index 9d6b339..2e1fb63 100644
--- a/README.md
+++ b/README.md
@@ -1,19 +1,41 @@
[![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/xdkqua54f90x4049/branch/master?svg=true)](https://ci.appveyor.com/project/chrisfilo/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. For details and compiled versions visit the [NITRC wiki](http://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage).
+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 or have similar licenses. See the license.txt file for more details.
+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.
## Versions
+Development
+ - Remove all derived images from [Philips DTI series](http://www.nitrc.org/forum/message.php?msg_id=21025).
+
+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 morning times 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)
+ - 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.
@@ -21,20 +43,19 @@ This software is open source. The bulk of the code is covered by the BSD license
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")
+ - 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)
-
+ - 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).
@@ -112,150 +133,33 @@ You can add as many files as you want to convert as long as this structure stays
## Build
-### Build command line version with cmake (Linux, Windows, OSx)
+### 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` folder
+`dcm2niix` will be created in the `bin` subfolder. To install on the system run `make install`.
-**optional batch processing version:**
+**optional building with OpenJPEG:**
-The batch processing binary `dcm2niibatch` is optional. To build `dcm2niibatch` as well change the cmake command to `cmake -DBATCH_VERSION=ON ..`
+Support for JPEG2000 using OpenJPEG is optional. To build with OpenJPEG change the cmake command to `cmake -DUSE_OPENJPEG=ON ..`
-This requires the following libraries:
-- pkg-config
-- yaml-cpp
-- a compiler that supports c++11
+**optional batch processing version:**
-e.g. the dependencies can be installed as follows:
+The batch processing binary `dcm2niibatch` is optional. To build `dcm2niibatch` as well change the cmake command to `cmake -DBATCH_VERSION=ON ..`
-Ubuntu 14.04
-```
-sudo apt-get install pkg-config libyaml-cpp-dev libyaml-cpp0.5 cmake libboost-dev
-```
-OSX
-```
-brew install pkg-config yaml-cpp cmake
-```
+This requires a compiler that supports c++11.
### 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 -o dcm2niix -DmyDisableOpenJPEG -DmyDisableJasper
-```
-
-The following sub-sections list how you can modify this basic recipe for your needs.
-
-##### 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 -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 -o dcm2niix -static-libgcc
-```
-
-##### JPEG2000 BUILD
-
- If you want to build this with JPEG2000 decompression support using OpenJPEG. You will need to have the OpenJPEG 2.1 libraries installed (https://code.google.com/p/openjpeg/wiki/Installation). I suggest building static libraries...
- svn checkout http://openjpeg.googlecode.com/svn/trunk/ openjpeg-read-only
- cmake -DBUILD_SHARED_LIBS:bool=off .
- 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 -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 two options:
-
-```
-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 -o dcm2niix -I/usr/local/include /usr/local/lib/libopenjp2.a
-```
-
-```
-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 -o dcm2niix -I/usr/local/lib /usr/local/lib/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 -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 -DmyDisableOpenJPEG -DmyDisableJasper /odcm2niix
-```
-
-##### 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 -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 -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 SUPPORT - 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
+[See the COMPILE.md file for details on manual compilation](./COMPILE.md).
diff --git a/SuperBuild/External-OPENJPEG.cmake b/SuperBuild/External-OPENJPEG.cmake
new file mode 100644
index 0000000..38f7301
--- /dev/null
+++ b/SuperBuild/External-OPENJPEG.cmake
@@ -0,0 +1,15 @@
+set(OPENJPEG_TAG 151e322) # version openjepg-2.1
+
+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=${CMAKE_BINARY_DIR}
+)
+
+set(OPENJPEG_DIR ${CMAKE_BINARY_DIR}/lib/openjpeg-2.1)
diff --git a/SuperBuild/External-YAML-CPP.cmake b/SuperBuild/External-YAML-CPP.cmake
new file mode 100644
index 0000000..ada7d8f
--- /dev/null
+++ b/SuperBuild/External-YAML-CPP.cmake
@@ -0,0 +1,15 @@
+set(YAML-CPP_TAG 5c3cb09) # 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=${CMAKE_BINARY_DIR}
+)
+
+set(YAML-CPP_DIR ${CMAKE_BINARY_DIR}/lib/cmake/yaml-cpp)
diff --git a/SuperBuild/SuperBuild.cmake b/SuperBuild/SuperBuild.cmake
new file mode 100644
index 0000000..969b95a
--- /dev/null
+++ b/SuperBuild/SuperBuild.cmake
@@ -0,0 +1,111 @@
+# 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(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_SYSTEM_TURBOJPEG "Use the system TurboJPEG" OFF)
+option(USE_JASPER "Compile with Jasper support" OFF)
+
+option(USE_OPENJPEG "Build with OpenJPEG support" OFF)
+option(BATCH_VERSION "Build dcm2niibatch for multiple conversions" OFF)
+
+include(ExternalProject)
+
+set(DEPENDENCIES)
+
+if(USE_OPENJPEG)
+ message("-- Build with OpenJPEG: ${USE_OPENJPEG}")
+
+ find_package(PkgConfig)
+ if(PKG_CONFIG_FOUND)
+ pkg_check_modules(OPENJPEG libopenjp2)
+ endif()
+
+ if(OPENJPEG_FOUND)
+ set(OPENJPEG_DIR ${OPENJPEG_LIBDIR}/openjepg-2.1)
+ message("-- Using the system OpenJPEG")
+ else()
+ include(${CMAKE_SOURCE_DIR}/SuperBuild/External-OPENJPEG.cmake)
+ list(APPEND DEPENDENCIES openjpeg)
+ set(BUILD_OPENJPEG TRUE)
+ message("-- Will build OpenJPEG from github")
+ endif()
+endif()
+
+if(BATCH_VERSION)
+ message("-- Build dcm2niibatch: ${BATCH_VERSION}")
+
+ 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)
+ message("-- Using the system yaml-cpp")
+ 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 from github")
+ 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}
+ -DUSE_STATIC_RUNTIME:BOOL=${USE_STATIC_RUNTIME}
+ -DUSE_SYSTEM_ZLIB:BOOL=${USE_SYSTEM_ZLIB}
+ -DUSE_SYSTEM_TURBOJPEG:BOOL=${USE_SYSTEM_TURBOJPEG}
+ -DUSE_SYSTEM_JASPER:BOOL=${USE_SYSTEM_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}
+ -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}
+)
+
+install(DIRECTORY ${CMAKE_BINARY_DIR}/bin/ DESTINATION bin
+ USE_SOURCE_PERMISSIONS)
+
+# Install dependent libraries for future use
+if(BUILD_OPENJPEG OR BUILD_YAML-CPP)
+ option(INSTALL_DEPENDENCIES "Optionally install dependent libraries (OpenJPEG and yaml-cpp) for future use." OFF)
+ if(INSTALL_DEPENDENCIES)
+ install(DIRECTORY ${CMAKE_BINARY_DIR}/include/ DESTINATION include
+ USE_SOURCE_PERMISSIONS)
+ install(DIRECTORY ${CMAKE_BINARY_DIR}/lib/ DESTINATION lib
+ USE_SOURCE_PERMISSIONS)
+ endif()
+endif()
+
+option(BUILD_DOCS "Build documentation (manpages)" OFF)
+if(BUILD_DOCS)
+ add_subdirectory(docs)
+endif()
+
diff --git a/appveyor.yml b/appveyor.yml
index f964fdc..3c099d1 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -14,25 +14,25 @@ clone_folder: c:\projects\dcm2niix
init:
- ps: >-
- $version = Get-Date -Format "d-MMM-yyyy"
+ $env:DATE = $(Get-Date -Format d-MMM-yyyy)
$githash = $env:APPVEYOR_REPO_COMMIT.Substring(0, 7)
- Update-AppveyorBuild -Version "$version-$githash"
+ $gittag = if ($env:APPVEYOR_REPO_TAG -eq $True) {"_$($env:APPVEYOR_REPO_TAG_NAME)"} else {""}
-before_build:
-- cmd: >-
- echo "Clone yaml-cpp"
+ Update-AppveyorBuild -Version "$($env:DATE)_g${githash}${gittag}"
- git clone -q --depth=1 --branch=master https://github.com/jbeder/yaml-cpp.git c:\projects\dcm2niix\yaml-cpp
+ $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 -Wno-dev ..\
+ cmake -G "Visual Studio 14 2015 Win64" -DBATCH_VERSION=ON -DUSE_OPENJPEG=ON ..\
build:
project: c:\projects\dcm2niix\build\dcm2niix.sln
@@ -43,8 +43,22 @@ after_build:
- ps: >-
cd c:\projects\dcm2niix
- 7z a dcm2niix-$version-win.zip c:\projects\dcm2niix\bin\Release\* >$null
+ 7z a dcm2niix_$($env:DATE)_win.zip c:\projects\dcm2niix\build\bin\* >$null
artifacts:
- - path: 'dcm2niix-*.zip'
+ - 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
diff --git a/console/CMakeLists.txt b/console/CMakeLists.txt
index 30199f8..e57086a 100644
--- a/console/CMakeLists.txt
+++ b/console/CMakeLists.txt
@@ -1,93 +1,130 @@
+cmake_minimum_required(VERSION 2.8.11)
+
project(console)
-set(PROGRAMS dcm2niix)
-if(POLICY CMP0054)
- cmake_policy(SET CMP0054 NEW)
+# 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()
-if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+set(PROGRAMS dcm2niix)
+
+if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
# using Clang
- add_definitions(-dead_strip)
-elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
+ add_definitions(-Wno-deprecated-register)
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-dead_strip")
+elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
# using GCC
-elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")
+ add_definitions(-Wno-unused-result)
+elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")
# using Intel C++
-elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
+elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# using Visual Studio C++
-endif ()
+ 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_SYSTEM_TURBOJPEG "Use the system TurboJPEG" OFF)
+if(USE_SYSTEM_TURBOJPEG)
+ find_package(PkgConfig REQUIRED)
+ pkg_check_modules(TurboJPEG libturbojpeg)
+ add_definitions(-DmyTurboJPEG)
+ target_include_directories(dcm2niix PRIVATE ${TurboJPEG_INCLUDE_DIRS})
+ target_link_libraries(dcm2niix ${TurboJPEG_LIBRARIES})
+endif()
+option(USE_JASPER "Compile with Jasper support" 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})
+else ()
+ add_definitions(-DmyDisableJASPER)
+endif()
-#We now use miniz, removing zlib dependency
-#if(ZLIB_FOUND)
-# TARGET_LINK_LIBRARIES(dcm2niix z)
-#else(ZLIB_FOUND)
-# ADD_DEFINITIONS(-DmyDisableZlib)
-#endif(ZLIB_FOUND)
-add_definitions(-DmyDisableJasper)
-add_definitions(-DmyDisableOpenJPEG)
+option(USE_OPENJPEG "Build with OpenJPEG support" OFF)
+if(USE_OPENJPEG)
+ option(OpenJPEG_DIR "Path to OpenJPEG configuration file" "")
-add_executable(dcm2niix
- main_console.cpp
+ find_package(OpenJPEG)
+ 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)
+ option(YAML-CPP_DIR "Path to yaml-cpp configuration file" "")
-if (BATCH_VERSION)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+ find_package(YAML-CPP)
+ target_include_directories(dcm2niibatch PRIVATE ${YAML_CPP_INCLUDE_DIR})
+ target_link_libraries(dcm2niibatch ${YAML_CPP_LIBRARIES})
- message("Creating batch version")
-
- # Resolve system dependency on yaml-cpp, which apparently does not
- # provide a CMake find_package() module.
-
- # Step 1: Search for library using pkg-config
- find_package(PkgConfig)
- if (PKG_CONFIG_FOUND)
- pkg_check_modules(YAML_CPP yaml-cpp)
- find_path(YAML_CPP_INCLUDE_DIR
- NAMES yaml-cpp
- PATHS ${YAML_CPP_INCLUDE_DIRS})
- find_library(YAML_CPP_LIBRARY
- NAMES yaml-cpp
- PATHS ${YAML_CPP_LIBRARY_DIRS})
- endif ()
-
- # Step 2: Search for library locally. Only searches for a folder called yaml-cpp
- if (NOT YAML_CPP_LIBRARY)
- message("Checking for yaml-cpp local folder: yaml-cpp")
- if (EXISTS ${CMAKE_SOURCE_DIR}/yaml-cpp)
- message("local version found")
- SET(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "Build test packages off")
- add_subdirectory(${CMAKE_SOURCE_DIR}/yaml-cpp ${CMAKE_SOURCE_DIR}/yaml-cpp)
- set(YAML_CPP_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/yaml-cpp/include)
- else()
- message("Local version not found")
- endif()
- endif ()
+ if(ZLIB_FOUND)
+ add_definitions(-DmyDisableMiniZ)
+ target_link_libraries(dcm2niibatch z)
+ endif()
- add_executable(dcm2niibatch
- main_console_batch.cpp
- nii_dicom.cpp
- jpg_0XC3.cpp
- ujpeg.cpp
- nifti1_io_core.cpp
- nii_ortho.cpp
- nii_dicom_batch.cpp)
-
- if (YAML_CPP_LIBRARY)
- target_link_libraries(dcm2niibatch ${YAML_CPP_LIBRARY})
+ if(JASPER_FOUND)
+ target_link_libraries(dcm2niibatch ${JASPER_LIBRARIES})
else()
- target_link_libraries(dcm2niibatch yaml-cpp)
+ add_definitions(-DmyDisableJASPER)
endif()
+ if(USE_OPENJPEG)
+ target_include_directories(dcm2niibatch PRIVATE ${OPENJPEG_INCLUDE_DIRS})
+ target_link_libraries(dcm2niibatch ${OPENJPEG_LIBRARIES})
+ else()
+ add_definitions(-DmyDisableOpenJPEG)
+ endif()
- if (YAML_CPP_INCLUDE_DIR)
- include_directories(${YAML_CPP_INCLUDE_DIR})
- endif ()
-endif ()
+ list(APPEND PROGRAMS dcm2niibatch)
+endif()
install(TARGETS ${PROGRAMS} DESTINATION bin)
diff --git a/console/jpg_0XC3.cpp b/console/jpg_0XC3.cpp
index 742ea1c..c8ca375 100644
--- a/console/jpg_0XC3.cpp
+++ b/console/jpg_0XC3.cpp
@@ -4,6 +4,7 @@
#include <stdint.h>
#include <stdbool.h>
#include "jpg_0XC3.h"
+#include "print.h"
unsigned char readByte(unsigned char *lRawRA, long *lRawPos, long lRawSz) {
unsigned char ret = 0x00;
@@ -103,27 +104,32 @@ int decodePixelDifference(unsigned char *lRawRA, long *lRawPos, int *lCurrentBit
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)
- #define abortGoto() free(lRawRA); return NULL;
+ //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");
- fseek(reader, 0, SEEK_END);
+ 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 (lRawSz <= 8) {
- printf("Error opening %s\n", fn);
+ 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
}
- fseek(reader, skipBytes, SEEK_SET);
unsigned char *lRawRA = (unsigned char*) malloc(lRawSz);
- fread(lRawRA, 1, lRawSz, reader);
+ size_t lSz = fread(lRawRA, 1, lRawSz, reader);
fclose(reader);
- if ((lRawRA[0] != 0xFF) || (lRawRA[1] != 0xD8) || (lRawRA[2] != 0xFF)) {
- printf("Error: JPEG signature 0xFFD8FF not found at offset %d of %s\n", skipBytes, fn);
- abortGoto();//goto abortGoto; //signature failure http://en.wikipedia.org/wiki/List_of_file_signatures
+ if ((lSz < 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)
- printf("JPEG signature 0xFFD8FF found at offset %d of %s\n", skipBytes, fn);
+ 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
@@ -138,8 +144,7 @@ unsigned char * decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbo
do {
btS1 = readByte(lRawRA, &lRawPos, lRawSz);
if (btS1 != 0xFF) {
- printf("JPEG header tag must begin with 0xFF\n");
- abortGoto(); //goto abortGoto;
+ abortGoto("JPEG header tag must begin with 0xFF\n");
}
btMarkerType = readByte(lRawRA, &lRawPos, lRawSz);
if ((btMarkerType == 0x01) || (btMarkerType == 0xFF) || ((btMarkerType >= 0xD0) && (btMarkerType <= 0xD7) ) )
@@ -148,10 +153,11 @@ unsigned char * decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbo
} while ((lRawPos < lRawSz) && (btMarkerType == 0));
uint16_t lSegmentLength = readWord (lRawRA, &lRawPos, lRawSz); //read marker length
long lSegmentEnd = lRawPos+(lSegmentLength - 2);
- if (lSegmentEnd > lRawSz)
- abortGoto(); //goto abortGoto;
+ if (lSegmentEnd > lRawSz) {
+ abortGoto("Segment larger than image\n");
+ }
if (verbose)
- printf("btMarkerType %#02X length %d@%ld\n", btMarkerType, lSegmentLength, lRawPos);
+ 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);
@@ -160,18 +166,16 @@ unsigned char * decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbo
SOFnf = readByte(lRawRA, &lRawPos, lRawSz);
//SOFarrayPos = lRawPos;
lRawPos = (lSegmentEnd);
- if (verbose) printf(" [Precision %d X*Y %d*%d Frames %d]\n", SOFprecision, SOFxdim, SOFydim, SOFnf);
+ if (verbose) printMessage(" [Precision %d X*Y %d*%d Frames %d]\n", SOFprecision, SOFxdim, SOFydim, SOFnf);
if (btMarkerType != 0xC3) { //lImgTypeC3 = true;
- printf("This JPEG decoder can only decompress lossless JPEG ITU-T81 images (SoF must be 0XC3, not %#02X)\n",btMarkerType );
- abortGoto(); //goto abortGoto;
+ 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)) ) {
- printf("Scalar data must be 1..16 bit, RGB data must be 8-bit (%d-bit, %d frames)\n", SOFprecision, SOFnf);
- abortGoto(); //goto abortGoto;
+ 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) printf(" [Huffman Length %d]\n", lSegmentLength);
+ if (verbose) printMessage(" [Huffman Length %d]\n", lSegmentLength);
int lFrameCount = 1;
do {
uint8_t DHTnLi = readByte(lRawRA, &lRawPos, lRawSz ); //we read but ignore DHTtcth.
@@ -181,10 +185,11 @@ unsigned char * decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbo
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) {
- printf("Huffman table corrupted.\n");
- abortGoto(); //goto abortGoto;
+ abortGoto("Huffman table corrupted.\n");
}
int lIncY = 0; //frequency
for (int lInc = 0; lInc <= 31; lInc++) {//lInc := 0 to 31 do begin
@@ -200,11 +205,11 @@ unsigned char * decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbo
btS1 = readByte(lRawRA, &lRawPos, lRawSz);
l[lFrameCount].HufVal[lIncY] = btS1;
l[lFrameCount].MaxHufVal = btS1;
- if ((btS1 >= 0) && (btS1 <= 16))
+ 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 {
- printf("Huffman size array corrupted.\n");
- abortGoto(); //goto abortGoto;
+ abortGoto("Huffman size array corrupted.\n");
}
}
}
@@ -228,15 +233,14 @@ unsigned char * decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbo
} while (K <= DHTnLi);
//if (verbose)
// for (int j = 1; j <= DHTnLi; j++)
- // printf(" [%d Sz %d Code %d Value %d]\n", j, l[lFrameCount].HufSz[j], l[lFrameCount].HufCode[j], l[lFrameCount].HufVal[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) printf(" [FrameCount %d]\n", lnHufTables);
+ if (verbose) printMessage(" [FrameCount %d]\n", lnHufTables);
} else if (btMarkerType == 0xDD) { //if DHT marker else if Define restart interval (DRI) marker
- printf("This image uses Restart Segments - please contact Chris Rorden to add support for this format.\n");
- abortGoto(); //goto abortGoto;
+ 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
@@ -257,17 +261,17 @@ unsigned char * decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbo
SOSahal = readByte(lRawRA, &lRawPos, lRawSz); //lower 4bits= pointtransform
SOSpttrans = SOSahal & 16;
if (verbose)
- printf(" [Predictor: %d Transform %d]\n", SOSss, SOSahal);
+ 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) {
- printf("Decoding error: no Huffman tables.\n");
- abortGoto(); //goto abortGoto;
+ 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 {
@@ -277,10 +281,14 @@ unsigned char * decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbo
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 ++) {
@@ -306,7 +314,7 @@ unsigned char * decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbo
}
} else
l[lFrameCount].LookUpRA[k] = lHufVal; //SSSS
- //printf("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);
+ //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
@@ -489,6 +497,6 @@ unsigned char * decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbo
*dimY = SOFydim;
*frames = SOFnf;
if (verbose)
- printf("JPEG ends %ld@%ld\n", lRawPos, lRawPos+skipBytes);
+ printMessage("JPEG ends %ld@%ld\n", lRawPos, lRawPos+skipBytes);
return lImgRA8;
}// decode_JPEG_SOF_0XC3()
\ No newline at end of file
diff --git a/console/main_console.cpp b/console/main_console.cpp
index b1ecc1d..3137e01 100644
--- a/console/main_console.cpp
+++ b/console/main_console.cpp
@@ -41,9 +41,22 @@
#include <stdio.h>
#include "nii_dicom_batch.h"
#include "nii_dicom.h"
-#include "nifti1_io_core.h"
#include <math.h>
+#if !defined(_WIN64) && !defined(_WIN32)
+#include <time.h>
+#include <sys/time.h>
+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)
@@ -58,18 +71,26 @@ void showHelp(const char * argv[], struct TDCMopts opts) {
const char *cstr = removePath(argv[0]);
printf("usage: %s [options] <in_folder>\n", cstr);
printf(" Options :\n");
- printf(" -b : BIDS sidecar (y/n, default n)\n");
+ printf(" -1..-9 : gz compression level (1=fastest, 9=smallest)\n");
+ char bidsCh = 'n';
+ if (opts.isCreateBIDS) bidsCh = 'y';
+ printf(" -b : BIDS sidecar (y/n, default %c)\n", bidsCh);
+ if (opts.isAnonymizeBIDS) bidsCh = 'y'; else bidsCh = 'n';
+ printf(" -ba : anonymize BIDS (y/n, default %c)\n", bidsCh);
#ifdef mySegmentByAcq
- printf(" -f : filename (%%a=antenna (coil) number, %%c=comments, %%d=description, %%e echo number, %%f=folder name, %%i ID of patient, %%m=manufacturer, %%n=name of patient, %%p=protocol, %%q=sequence number, %%s=series number, %%t=time, %%u=acquisition number, %%z sequence name; default '%s')\n",opts.filename);
+ 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, %%q=sequence number, %%s=series number, %%t=time, %%u=acquisition number, %%z sequence name; default '%s')\n",opts.filename);
#else
- printf(" -f : filename (%%a=antenna (coil) number, %%c=comments, %%d=description, %%e echo number, %%f=folder name, %%i ID of patient, %%m=manufacturer, %%n=name of patient, %%p=protocol, %%s=series number, %%t=time, %%u=acquisition number, %%z sequence name; default '%s')\n",opts.filename);
+ 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=series number, %%t=time, %%u=acquisition number, %%z sequence name; default '%s')\n",opts.filename);
#endif
printf(" -h : show help\n");
+ printf(" -i : ignore derived, localizer and 2D images (y/n, default n)\n");
+ printf(" -t : text notes includes private patient details (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(" -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");
- printf(" -v : verbose (y/n, default n)\n");
+ 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';
@@ -79,7 +100,11 @@ void showHelp(const char * argv[], struct TDCMopts opts) {
else
printf(" -z : gz compress images (y/n, default %c) [REQUIRES pigz]\n", gzCh);
#else
+ #ifdef myDisableMiniZ
+ printf(" -z : gz compress images (y/i/n, default %c) [y=pigz, i=internal:zlib, n=no]\n", gzCh);
+ #else
printf(" -z : gz compress images (y/i/n, default %c) [y=pigz, i=internal, n=no]\n", gzCh);
+ #endif
#endif
#if defined(_WIN64) || defined(_WIN32)
@@ -94,6 +119,7 @@ void showHelp(const char * argv[], struct TDCMopts opts) {
printf(" Examples :\n");
printf(" %s /Users/chris/dir\n", cstr);
printf(" %s -o /users/cr/outdir/ -z y ~/dicomdir\n", cstr);
+ printf(" %s -f %%pp_%%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
@@ -122,25 +148,52 @@ int main(int argc, const char * argv[])
showHelp(argv, opts);
return 0;
}
- bool isCustomOutDir = false;
+ //bool isCustomOutDir = false;
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] == 'b') && ((i+1) < argc)) {
+ 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 ((argv[i][0] == 'n') || (argv[i][0] == 'N') || (argv[i][0] == '0'))
+ opts.isCreateBIDS = false;
+ else
+ opts.isCreateBIDS = true;
+ } else if (argv[i][2] == 'a') {//"-ba y"
+ i++;
+ 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] == 'i') && ((i+1) < argc)) {
i++;
if ((argv[i][0] == 'n') || (argv[i][0] == 'N') || (argv[i][0] == '0'))
- opts.isCreateBIDS = false;
+ opts.isIgnoreDerivedAnd2D = false;
else
- opts.isCreateBIDS = true;
+ opts.isIgnoreDerivedAnd2D = true;
} else if ((argv[i][1] == 'm') && ((i+1) < argc)) {
i++;
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 ((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 ((argv[i][0] == 'n') || (argv[i][0] == 'N') || (argv[i][0] == '0'))
@@ -181,7 +234,7 @@ int main(int argc, const char * argv[])
strcpy(opts.filename,argv[i]);
} else if ((argv[i][1] == 'o') && ((i+1) < argc)) {
i++;
- isCustomOutDir = true;
+ //isCustomOutDir = true;
strcpy(opts.outdir,argv[i]);
}
lastCommandArg = i;
@@ -198,14 +251,21 @@ int main(int argc, const char * argv[])
return EXIT_SUCCESS;
}
#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]
- if (!isCustomOutDir) strcpy(opts.outdir,opts.indir);
+ //if (!isCustomOutDir) strcpy(opts.outdir,opts.indir);
if (nii_loadDir(&opts) != EXIT_SUCCESS)
return EXIT_FAILURE;
}
- printf ("Conversion required %f seconds.\n",((float)(clock()-start))/CLOCKS_PER_SEC);
+ #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;
}
diff --git a/console/main_console_batch.cpp b/console/main_console_batch.cpp
index 0230554..23c8662 100644
--- a/console/main_console_batch.cpp
+++ b/console/main_console_batch.cpp
@@ -3,18 +3,25 @@
// Copyright (c) 2014 Chris Rorden. All rights reserved.
// yaml batch suport by Benjamin Irving, 2016 - maintains copyright
-#include <stdlib.h>
+#ifdef _MSC_VER
+ #include <io.h> //access()
+ #ifndef F_OK
+ #define F_OK 0 /* existence check */
+ #endif
+#else
+ #include <unistd.h> //access()
+#endif
+#include <cmath>
+#include <fstream>
+#include <iostream>
#include <stdbool.h>
-#include <time.h> // clock_t, clock, CLOCKS_PER_SEC
#include <stdio.h>
-#include <cmath>
+#include <stdlib.h>
#include <string>
-#include <iostream>
-
-#include "nii_dicom_batch.h"
-
-#include <fstream>
+#include <time.h> // clock_t, clock, CLOCKS_PER_SEC
#include <yaml-cpp/yaml.h>
+#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, '\\');
@@ -26,30 +33,77 @@ const char* removePath(const char* path) { // "/usr/path/filename.exe" -> "filen
return path;
} //removePath()
-
-int rmainbatch(TDCMopts opts)
-{
- printf("Chris Rorden's dcm2niiX version %s (%lu-bit)\n",kDCMvers, sizeof(size_t)*8);
-
+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;
-}
-
-int main(int argc, const char * argv[])
-{
-
+} //rmainbatch()
+
+void showHelp(const char * argv[]) {
+ const char *cstr = removePath(argv[0]);
+ printf("Usage: %s <batch_config.yml>\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) {
- std::cout << "Do not include additional inputs with a config file \n";
- throw;
+ 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<bool>();
@@ -63,18 +117,13 @@ int main(int argc, const char * argv[])
//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<std::string>();
strcpy(opts.indir, indir.c_str());
-
std::string outdir = i["out_dir"].as<std::string>();
strcpy(opts.outdir, outdir.c_str());
-
std::string filename = i["filename"].as<std::string>();
strcpy(opts.filename, filename.c_str());
-
rmainbatch(opts);
-
}
- return 1;
-}
+ return EXIT_SUCCESS;
+} // main()
diff --git a/console/makefile b/console/makefile
old mode 100644
new mode 100755
index 3cf9f9a..f311167
--- a/console/makefile
+++ b/console/makefile
@@ -6,4 +6,4 @@ ifneq ($(OS),Windows_NT)
endif
endif
all:
- g++ $(CFLAGS) -O3 -I. main_console.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -o dcm2niix -DmyDisableOpenJPEG -DmyDisableJasper
+ g++ $(CFLAGS) -O3 -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 -DmyDisableJasper
diff --git a/console/nifti1_io_core.cpp b/console/nifti1_io_core.cpp
index 73c3add..34efd04 100644
--- a/console/nifti1_io_core.cpp
+++ b/console/nifti1_io_core.cpp
@@ -1,3 +1,20 @@
+//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 <math.h>
#include <stdlib.h>
@@ -12,6 +29,9 @@
#include <unistd.h>
#endif
+#include "print.h"
+
+#ifndef HAVE_R
void nifti_swap_8bytes( size_t n , void *ar ) // 4 bytes at a time
{
size_t ii ;
@@ -58,6 +78,7 @@ void nifti_swap_2bytes( size_t n , void *ar ) // 2 bytes at a time
}
return ;
}
+#endif
int isSameFloat (float a, float b) {
return (fabs (a - b) <= FLT_EPSILON);
@@ -130,7 +151,7 @@ mat44 nifti_dicom2mat(float orient[7], float patientPosition[4], float xyzMM[4])
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] );
+ //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 ){
@@ -167,6 +188,7 @@ mat44 nifti_dicom2mat(float orient[7], float patientPosition[4], float xyzMM[4])
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 ;
@@ -189,6 +211,7 @@ mat33 nifti_mat33_mul( mat33 A , mat33 B ) /* multiply 2 3x3 matrices */
+ A.m[i][2] * B.m[2][j] ;
return C ;
}
+#endif
mat44 nifti_mat44_mul( mat44 A , mat44 B ) /* multiply 2 3x3 matrices */
{
@@ -212,6 +235,7 @@ mat33 nifti_mat33_transpose( mat33 A ) /* transpose 3x3 matrix */
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 ;
@@ -468,7 +492,7 @@ mat44 nifti_mat44_inverse( mat44 R )
Q.m[3][3] = (deti == 0.0l) ? 0.0l : 1.0l ; // failure flag if deti == 0
return Q ;
}
-
+#endif
diff --git a/console/nifti1_io_core.h b/console/nifti1_io_core.h
index 41f4615..0e1588e 100644
--- a/console/nifti1_io_core.h
+++ b/console/nifti1_io_core.h
@@ -4,6 +4,11 @@
#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
@@ -11,12 +16,14 @@ extern "C" {
#include <stdbool.h>
#include <string.h>
+#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 ;
diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp
index 94c74d9..059d5b9 100644
--- a/console/nii_dicom.cpp
+++ b/console/nii_dicom.cpp
@@ -16,7 +16,10 @@
#include <unistd.h>
#endif
//#include <time.h> //clock()
+#ifndef HAVE_R
#include "nifti1.h"
+#endif
+#include "print.h"
#include "nii_dicom.h"
#include <sys/types.h>
#include <sys/stat.h> // discriminate files from folders
@@ -30,15 +33,24 @@
#include <float.h>
#include <stdint.h>
#include "nifti1_io_core.h"
-#include "ujpeg.h"
-#ifdef myUseCOut
-#include <iostream>
+
+#ifdef HAVE_R
+#undef isnan
+#define isnan ISNAN
+#endif
+
+#ifndef myDisableClassicJPEG
+ #ifdef myTurboJPEG
+ #include <turbojpeg.h>
+ #else
+ #include "ujpeg.h"
+ #endif
#endif
#ifdef myEnableJasper
#include <jasper/jasper.h>
#endif
#ifndef myDisableOpenJPEG
- #include <openjpeg-2.1/openjpeg.h>//"openjpeg.h"
+ #include <openjpeg.h>
#ifdef myEnableJasper
ERROR: YOU CAN NOT COMPILE WITH myEnableJasper AND NOT myDisableOpenJPEG OPTIONS SET SIMULTANEOUSLY
@@ -67,15 +79,15 @@ unsigned char * imagetoimg(opj_image_t * image)
}
if ((image->comps[0].prec < 1) || (image->comps[0].prec > 16)) isOK = false; //currently we only handle 1 and 2 byte data
if (!isOK) {
- printf("jpeg decode failure w*h %d*%d bpp %d sgnd %d components %d OpenJPEG=%s\n", width, height, bpp, sgnd, numcmpts, opj_version());
+ 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
- printf("w*h %d*%d bpp %d sgnd %d components %d OpenJPEG=%s\n", width, height, bpp, sgnd, numcmpts, opj_version());
+ 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)) {
- printf("Serious catastrophic decompression error\n");
+ printError("Catastrophic decompression error\n");
return NULL;
}
unsigned char *img = (unsigned char *)malloc(imgbytes);
@@ -84,7 +96,7 @@ unsigned char * imagetoimg(opj_image_t * image)
if (sgnd) bpp = -bpp;
if (bpp == -1) {
free(img);
- printf("Error: Signed 8-bit DICOM?\n");
+ printError("Signed 8-bit DICOM?\n");
return NULL;
}
//n.b. Analyze rgb-24 are PLANAR e.g. RRR..RGGG..GBBB..B not RGBRGBRGB...RGB
@@ -126,7 +138,6 @@ static void my_stream_free (void * p_user_data) { //do nothing
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;
@@ -196,8 +207,9 @@ unsigned char * nii_loadImgCoreOpenJPEG(char* imgname, struct nifti_1_header hdr
if (size <= 8) return NULL;
fseek(reader, dcm.imageStart, SEEK_SET);
unsigned char *data = (unsigned char*) malloc(size);
- fread(data, 1, size, reader);
+ 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;
@@ -213,12 +225,12 @@ unsigned char * nii_loadImgCoreOpenJPEG(char* imgname, struct nifti_1_header hdr
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)){
- printf( "OpenJPEG error: failed to read the header %s\n", imgname);
+ printError( "OpenJPEG failed to read the header %s\n", imgname);
goto cleanup2;
}
// Get the decoded image
if ( !( opj_decode(codec, stream, jpx) && opj_end_decompress(codec,stream) ) ) {
- printf( "OpenJPEG error: j2k_to_image: failed to decode %s\n",imgname);
+ printError( "OpenJPEG j2k_to_image failed to decode %s\n",imgname);
goto cleanup1;
}
ret = imagetoimg(jpx);
@@ -230,105 +242,6 @@ cleanup2:
opj_destroy_codec(codec);
return ret;
}
-
-/* OpenJPEG code very clumsy for common instance when JPEG is embedded in a file, like PDF, DICOM, etc. Perhaps future versions will allow foo2's simpler methods...
-int foo (float vx) {
- const char *fn = "/Users/rorden/Desktop/tester/mecanix.dcm";
- int dcmimageStart = 1282;
- unsigned char * ret = NULL;
- //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
- opj_dparameters_t params;
- opj_codec_t *codec;
- opj_image_t *jpx;
- opj_stream_t *stream;
- FILE *reader = fopen(fn, "rb");
- fseek(reader, 0, SEEK_END);
- long size = ftell(reader)- dcmimageStart;
- if (size <= 8) return NULL;
- fseek(reader, dcmimageStart, SEEK_SET);
- unsigned char *data = (unsigned char*) malloc(size);
- fread(data, 1, size, reader);
- fclose(reader);
- 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)){
- printf( "OpenJPEG error: failed to read the header %s\n",fn);
- goto cleanup2;
- }
- // Get the decoded image
- if ( !( opj_decode(codec, stream, jpx) && opj_end_decompress(codec,stream) ) ) {
- printf( "OpenJPEG error: j2k_to_image: failed to decode %s\n",fn);
- goto cleanup1;
- }
- ret = imagetoimg(jpx);
- if (ret != NULL) free(ret);
- cleanup1:
- opj_image_destroy(jpx);
- cleanup2:
- free(dx.buf);
- opj_stream_destroy(stream);
- opj_destroy_codec(codec);
- return NULL;
-} //foo()
-
-int foo2 (float vx) {
- const char *fn = "/Users/rorden/Desktop/tester/mecanix.dcm";
- unsigned char * ret = NULL;
- opj_dparameters_t parameters; // decompression parameters
- opj_image_t *jpx = NULL;
- opj_codec_t *l_codec = NULL; // handle to a decompressor
- opj_stream_t *l_stream = NULL;
- // set decoding parameters to default values
- opj_set_default_decoder_parameters(¶meters);
- // set a byte stream
- l_stream = opj_stream_create_default_file_stream( fn, OPJ_TRUE);
- if (!l_stream){
- printf("OpenJPEG error: failed to open the file %s\n", fn);
- return NULL;
- }
- // get a decoder handle - DICOM specifies JPEG-2000 codestream
- //l_codec = opj_create_decompress(OPJ_CODEC_JP2);
- l_codec = opj_create_decompress(OPJ_CODEC_J2K);
- // setup the decoder decoding parameters using user parameters
- if ( !opj_setup_decoder(l_codec, ¶meters) ) return NULL;
- // Read the main header of the codestream and if necessary the JP2 boxes
- if(! opj_read_header( l_stream, l_codec, &jpx)){
- printf( "OpenJPEG error: failed to read the header %s\n",fn);
- opj_stream_destroy(l_stream);
- opj_destroy_codec(l_codec);
- opj_image_destroy(jpx);
- return NULL;
- }
- // Get the decoded image
- if ( !( opj_decode(l_codec, l_stream, jpx) && opj_end_decompress(l_codec,l_stream) ) ) {
- printf( "OpenJPEG error: j2k_to_image: failed to decode %s\n",fn);
- opj_stream_destroy(l_stream);
- opj_destroy_codec(l_codec);
- opj_image_destroy(jpx);
- return NULL;
- }
- printf("image is decoded!\n");
- opj_stream_destroy(l_stream);
- ret = imagetoimg(jpx);
- if (ret != NULL) free(ret);
- if(l_codec) opj_destroy_codec(l_codec);
- opj_image_destroy(jpx);
- return EXIT_SUCCESS;
-} //foo() */
#endif //if
#ifndef M_PI
@@ -345,7 +258,7 @@ float deFuzz(float v) {
}
void reportMat33(char *str, mat33 A) {
- printf("%s = [%g %g %g ; %g %g %g; %g %g %g ]\n",str,
+ 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]));
@@ -353,7 +266,7 @@ void reportMat33(char *str, mat33 A) {
void reportMat44(char *str, mat44 A) {
//example: reportMat44((char*)"out",*R);
- printf("%s = [%g %g %g %g; %g %g %g %g; %g %g %g %g; 0 0 0 1]\n",str,
+ 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]));
@@ -373,14 +286,14 @@ int verify_slice_dir (struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_
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);
+ 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)) printf("position determined using last (4d) %f\n",pos);
+ if (!isnan(pos)) printMessage("position determined using last (4d) %f\n",pos);
#endif
}
if (isnan(pos) && ( !isnan(d.stackOffcentre[iSL])) )
@@ -396,11 +309,7 @@ int verify_slice_dir (struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_
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
if (!d.isNonImage) //do not warn user if image is derived
-#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
+ printWarning("Unable to determine slice direction: please check whether slices are flipped\n");
}
if (flip) {
for (int i = 0; i < 4; i++)
@@ -409,16 +318,16 @@ int verify_slice_dir (struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_
if (flip)
iSL = -iSL;
#ifdef MY_DEBUG
- printf("verify slice dir %d %d %d\n",h->dim[1],h->dim[2],h->dim[3]);
+ printMessage("verify slice dir %d %d %d\n",h->dim[1],h->dim[2],h->dim[3]);
//reportMat44((char*)"Rout",*R);
- printf("flip = %d\n",flip);
- printf("sliceDir = %d\n",iSL);
- printf(" pos1 = %f\n",pos1);
+ 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) //simplify any headers that have NaN values
+mat44 noNaN(mat44 Q44, bool isVerbose) //simplify any headers that have NaN values
{
mat44 ret = Q44;
bool isNaN44 = false;
@@ -427,7 +336,8 @@ mat44 noNaN(mat44 Q44) //simplify any headers that have NaN values
if (isnan(ret.m[i][j]))
isNaN44 = true;
if (isNaN44) {
- printf("Warning: bogus spatial matrix (perhaps non-spatial image): inspect spatial orientation\n");
+ 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)
@@ -439,8 +349,8 @@ mat44 noNaN(mat44 Q44) //simplify any headers that have NaN values
return ret;
}
-void setQSForm(struct nifti_1_header *h, mat44 Q44i) {
- mat44 Q44 = noNaN(Q44i);
+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];
@@ -516,12 +426,12 @@ mat44 xform_mat(struct TDICOMdata d) {
R = nifti_mat33_transpose(R);
//reportMat33((char*)"R",R);
ivec3 ixyz = maxCol(R);
- //printf("%d %d %d\n", ixyz.v[0], ixyz.v[1], ixyz.v[2]);
+ //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];
- //printf("cosSL\t%g\n", cosSL);
+ //printMessage("cosSL\t%g\n", cosSL);
//vec3 pixdim = setVec3(d.xyzMM[1], d.xyzMM[2], d.xyzMM[3]);
- //printf("%g %g %g\n", pixdim.v[0], pixdim.v[1], pixdim.v[2]);
+ //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,
@@ -546,10 +456,10 @@ mat44 xform_mat(struct TDICOMdata d) {
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 );
- //printf("%g %g %g\n", dim.v[0], dim.v[1], dim.v[2]);
- //printf("%g %g %g\n", dim4.v[0], dim4.v[1], dim4.v[2]);
- //printf("%g %g %g %g\n", offset.v[0], offset.v[1], offset.v[2], offset.v[3]);
- //printf("nRowCol\t%g\n", nRowCol);
+ //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];
@@ -564,11 +474,11 @@ mat44 xform_mat(struct TDICOMdata d) {
return R44;
} else if (true) {
//SliceNormalVector TO DO
- printf("Not completed");
+ printMessage("Not completed");
exit(2);
return R44;
}
- printf("Unable to determine spatial transform\n");
+ printMessage("Unable to determine spatial transform\n");
exit(1);
}
@@ -585,6 +495,14 @@ mat44 set_nii_header(struct TDICOMdata d) {
}
#endif
+float deFuzz(float v) {
+ if (fabs(v) < 0.00001)
+ return 0;
+ else
+ return v;
+
+}
+
// 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) {
*sliceDir = 0;
@@ -593,6 +511,7 @@ mat44 set_nii_header_x(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_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;
+ //printf("%g %g\n", lFactorX, lFactorY);
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]);
@@ -622,11 +541,15 @@ mat44 set_nii_header_x(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1
return Q44;
}
-int headerDcm2NiiSForm(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h) { //fill header s and q form
+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) return sliceDir; //don't care direction for single slice
+ if (h->dim[3] < 2) {
+ mat44 Q44 = set_nii_header_x(d, d2, h, &sliceDir);
+ 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;
@@ -636,14 +559,14 @@ int headerDcm2NiiSForm(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_
//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.isNonImage) || ((d.bitsAllocated == 8) && (d.samplesPerPixel == 3) && (d.manufacturer == kMANUFACTURER_SIEMENS)))
- printf("Unable to determine spatial orientation: 0020,0037 missing (probably not a problem: derived image)\n");
- else
- printf("Unable to determine spatial orientation: 0020,0037 missing!\n");
+ if ((d.isNonImage) || ((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(d);
mat44 Q44 = set_nii_header_x(d, d2, h, &sliceDir);
- setQSForm(h,Q44);
+ setQSForm(h,Q44, isVerbose);
return sliceDir;
} //headerDcm2NiiSForm()
@@ -675,7 +598,7 @@ int headerDcm2Nii2(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_hea
snprintf(h->descrip,80, "%s",txt);
if (strlen(d.imageComments) > 0)
snprintf(h->aux_file,24,"%s",d.imageComments);
- return headerDcm2NiiSForm(d,d2, h);
+ return headerDcm2NiiSForm(d,d2, h, true);
} //headerDcm2Nii2()
int dcmStrLen (int len) {
@@ -703,48 +626,64 @@ struct TDICOMdata clear_dicom_data() {
d.xyzDim[i] = 1;
for (int i = 0; i < 7; i++)
d.orient[i] = 0.0f;
- d.patientPositionSequentialRepeats = 0;//d.isHasMixed = false;
+ 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, "N/A");
+ strcpy(d.procedureStepDescription, "");
+ strcpy(d.seriesInstanceUID, "");
+ strcpy(d.studyInstanceUID, "");
+ strcpy(d.bodyPartExamined,"");
+ d.patientPositionSequentialRepeats = 0;
d.isHasPhase = false;
d.isHasMagnitude = false;
d.sliceOrient = kSliceOrientUnknown;
- 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.manufacturersModelName, "N/A");
d.dateTime = (double)19770703150928.0;
d.acquisitionTime = 0.0f;
d.acquisitionDate = 0.0f;
- strcpy(d.protocolName, "MPRAGE");
- strcpy(d.seriesDescription, "T1_mprage");
- strcpy(d.sequenceName, "T1");
- strcpy(d.scanningSequence, "tfl3d1_ns");
d.manufacturer = kMANUFACTURER_UNKNOWN;
d.isPlanarRGB = false;
d.lastScanLoc = NAN;
- d.TR = 0;
- d.TE = 0;
+ d.TR = 0.0;
+ d.TE = 0.0;
+ d.TI = 0.0;
d.flipAngle = 0.0;
d.fieldStrength = 0.0;
d.numberOfDynamicScans = 0;
d.echoNum = 1;
d.coilNum = 1;
+ 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.isSlicesSpatiallySequentialPhilips = true; //Philips can save slices in random order, e.g. 4,5,6,1,2,3
d.isNonImage = false; //0008,0008 = DERIVED,CSAPARALLEL,POSDISP
d.bitsAllocated = 16;//bits
d.bitsStored = 0;
d.samplesPerPixel = 1;
d.isValid = false;
+ d.isXRay = 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
@@ -783,15 +722,15 @@ void dcmStr(int lLength, unsigned char lBuffer[], char* lOut) {
//lLength = (int)strlen(test);
if (lLength < 1) return;
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
char * cString = (char *)malloc(sizeof(char) * (lLength + 1));
-#else
- char cString[lLength + 1];
-#endif
+//#else
+// char cString[lLength + 1];
+//#endif
cString[lLength] =0;
memcpy(cString, (char*)&lBuffer[0], lLength);
//memcpy(cString, test, lLength);
- //printf("X%dX\n", (unsigned char)d.patientName[1]);
+ //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) {
@@ -832,21 +771,27 @@ void dcmStr(int lLength, unsigned char lBuffer[], char* lOut) {
//while ((len > 0) && (cString[len]=='_')) len--; //remove trailing '_'
cString[len] = 0; //null-terminate, strlcpy does this anyway
len = dcmStrLen(len);
+ if (len == kDICOMStr) { //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
+//#ifdef _MSC_VER
free(cString);
-#endif
+//#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
-#ifdef __BIG_ENDIAN__
- bool swap = littleEndian;
-#else
- bool swap = !littleEndian;
-#endif
- float retVal;
+ bool swap = (littleEndian != littleEndianPlatform());
+ float retVal = 0;
+ if (lByteLength < 4) return retVal;
memcpy(&retVal, (char*)&lBuffer[0], 4);
if (!swap) return retVal;
float swapVal;
@@ -856,17 +801,13 @@ float dcmFloat(int lByteLength, unsigned char lBuffer[], bool littleEndian) {//r
outFloat[1] = inFloat[2];
outFloat[2] = inFloat[1];
outFloat[3] = inFloat[0];
- //printf("swapped val = %f\n",swapVal);
+ //printMessage("swapped val = %f\n",swapVal);
return swapVal;
} //dcmFloat()
double dcmFloatDouble(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
+ bool swap = (littleEndian != littleEndianPlatform());
double retVal = 0.0f;
if (lByteLength < 8) return retVal;
memcpy(&retVal, (char*)&lBuffer[0], 8);
@@ -882,7 +823,7 @@ double dcmFloatDouble(int lByteLength, unsigned char lBuffer[], bool littleEndia
returnFloat[5] = floatToConvert[2];
returnFloat[6] = floatToConvert[1];
returnFloat[7] = floatToConvert[0];
- //printf("swapped val = %f\n",retVal);
+ //printMessage("swapped val = %f\n",retVal);
return retVal;
} //dcmFloatDouble()
@@ -898,33 +839,32 @@ int dcmInt (int lByteLength, unsigned char lBuffer[], bool littleEndian) { //rea
} //dcmInt()
int dcmStrInt (int lByteLength, unsigned char lBuffer[]) {//read float stored as a string
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
char * cString = (char *)malloc(sizeof(char) * (lByteLength + 1));
-#else
- char cString[lByteLength + 1];
-#endif
-
+//#else
+// char cString[lByteLength + 1];
+//#endif
cString[lByteLength] =0;
memcpy(cString, (char*)&lBuffer[0], lByteLength);
- //printf(" --> *%s* %s%s\n",cString, &lBuffer[0],&lBuffer[1]);
+ //printMessage(" --> *%s* %s%s\n",cString, &lBuffer[0],&lBuffer[1]);
int ret = atoi(cString);
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
free(cString);
-#endif
+//#endif
return ret;
} //dcmStrInt()
int dcmStrManufacturer (int lByteLength, unsigned char lBuffer[]) {//read float stored as a string
if (lByteLength < 2) return kMANUFACTURER_UNKNOWN;
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
char * cString = (char *)malloc(sizeof(char) * (lByteLength + 1));
-#else
- char cString[lByteLength + 1];
-#endif
+//#else
+// char cString[lByteLength + 1];
+//#endif
int ret = kMANUFACTURER_UNKNOWN;
cString[lByteLength] =0;
memcpy(cString, (char*)&lBuffer[0], lByteLength);
- //printf("MANU %s\n",cString);
+ //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'))
@@ -933,9 +873,9 @@ int dcmStrManufacturer (int lByteLength, unsigned char lBuffer[]) {//read float
ret = kMANUFACTURER_PHILIPS;
if ((toupper(cString[0])== 'T') && (toupper(cString[1])== 'O'))
ret = kMANUFACTURER_TOSHIBA;
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
free(cString);
-#endif
+//#endif
return ret;
} //dcmStrManufacturer
@@ -978,21 +918,19 @@ float csaMultiFloat (unsigned char buff[], int nItems, float Floats[], int *Item
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));
-#else
- char cString[itemCSA.xx2_Len];
-#endif
- memcpy(cString, &buff[lPos], sizeof(cString)); //TPX memcpy(&cString, &buff[lPos], sizeof(cString));
+ 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;
- //printf(" %d item length %d = %s\n",lI, itemCSA.xx2_Len, cString);
+ //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
-
-#ifdef _MSC_VER
- free(cString);
-#endif
+ free(cString);
}
} //for each item
return Floats[1];
@@ -1006,20 +944,25 @@ bool csaIsPhaseMap (unsigned char buff[], int nItems) {
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));
-#else
- char cString[itemCSA.xx2_Len];
-#endif
- memcpy(cString, &buff[lPos], sizeof(cString)); //TPX memcpy(&cString, &buff[lPos], sizeof(cString));
+//#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;
- //printf(" %d item length %d = %s\n",lI, itemCSA.xx2_Len, cString);
+ //printMessage(" %d item length %d = %s\n",lI, itemCSA.xx2_Len, cString);
if (strcmp(cString, "CC:ComplexAdd") == 0)
return true;
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
free(cString);
-#endif
+//#endif
}
} //for each item
return false;
@@ -1027,7 +970,7 @@ bool csaIsPhaseMap (unsigned char buff[], int nItems) {
int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, int isVerbose, struct TDTI4D *dti4D) {
//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]);
+ //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
@@ -1041,8 +984,13 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i
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
- printf("%d CSA of %s %d\n",lPos, tagCSA.name, tagCSA.nitems);
+ 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);
@@ -1051,7 +999,7 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i
else if (strcmp(tagCSA.name, "B_value") == 0) {
CSA->dtiV[0] = csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK);
if (CSA->dtiV[0] < 0.0) {
- printf("WARNING: (Corrupt) CSA reports negative b-value! %g\n",CSA->dtiV[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
@@ -1061,23 +1009,23 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i
CSA->dtiV[2] = lFloats[2];
CSA->dtiV[3] = lFloats[3];
if (isVerbose)
- printf("DiffusionGradientDirection %f %f %f\n",lFloats[1],lFloats[2],lFloats[3]);
+ 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)
- printf("SliceNormalVector %f %f %f\n",CSA->sliceNormV[1],CSA->sliceNormV[2],CSA->sliceNormV[3]);
+ 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) ){
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
float * sliceTimes = (float *)malloc(sizeof(float) * (tagCSA.nitems + 1));
-#else
- float sliceTimes[tagCSA.nitems + 1];
-#endif
+//#else
+// float sliceTimes[tagCSA.nitems + 1];
+//#endif
csaMultiFloat (&buff[lPos], tagCSA.nitems,sliceTimes, &itemsOK);
float maxTimeValue, minTimeValue, timeValue1;
for (int z = 0; z < kMaxDTI4D; z++)
@@ -1096,10 +1044,10 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i
minTimeValue = sliceTimes[1];
maxTimeValue = minTimeValue;
if (isVerbose)
- printf("sliceTimes %g\t", sliceTimes[1]);
+ printMessage("sliceTimes %g\t", sliceTimes[1]);
for (int z = 2; z <= itemsOK; z++) { //find index and value of fastest time
if (isVerbose)
- printf("%g\t", sliceTimes[z]);
+ printMessage("%g\t", sliceTimes[z]);
if (sliceTimes[z] == 0)
nTimeZero++;
if (sliceTimes[z] < minTimeValue) {
@@ -1113,7 +1061,7 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i
if (sliceTimes[z] == timeValue1) CSA->multiBandFactor++;
}
if (isVerbose)
- printf("\n");
+ printMessage("\n");
CSA->slice_start = minTimeIndex -1;
CSA->slice_end = maxTimeIndex -1;
if (minTimeIndex == 2)
@@ -1134,17 +1082,17 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i
[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");
+ printWarning("Unable to determine slice order from CSA tag MosaicRefAcqTimes\n");
}
if ((CSA->sliceOrder != NIFTI_SLICE_UNKNOWN) && (nTimeZero > 1)) {
if (isVerbose)
- printf(" Multiband x%d sequence: setting slice order as UNKNOWN (instead of %d)\n", nTimeZero, CSA->sliceOrder);
+ 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
+//#ifdef _MSC_VER
free(sliceTimes);
-#endif
+//#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)
@@ -1152,6 +1100,9 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i
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
@@ -1162,11 +1113,11 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i
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
+//#ifdef _MSC_VER
char * cString = (char *)malloc(sizeof(char) * (lByteLength + 1));
-#else
- char cString[lByteLength + 1];
-#endif
+//#else
+// char cString[lByteLength + 1];
+//#endif
memcpy(cString, (char*)&lBuffer[0], lByteLength);
cString[lByteLength] = 0; //null terminate
char *temp=( char *)malloc(lByteLength+1);
@@ -1177,40 +1128,40 @@ void dcmMultiFloat (int lByteLength, char lBuffer[], int lnFloats, float *lFloat
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);
+ //printMessage("dcmMultiFloat %s\n",temp);
if (f < lnFloats) {
f ++;
lFloats[f] = (float) atof(temp);
isOK = false;
- //printf("%d == %f\n", f, atof(temp));
+ //printMessage("%d == %f\n", f, atof(temp));
} //if f <= nFloats
lStart = i+1;
} //if isOK
} //for i to length
free(temp);
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
free(cString);
-#endif
+//#endif
} //dcmMultiFloat()
float dcmStrFloat (int lByteLength, unsigned char lBuffer[]) { //read float stored as a string
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
char * cString = (char *)malloc(sizeof(char) * (lByteLength + 1));
-#else
- char cString[lByteLength + 1];
-#endif
+//#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
+//#ifdef _MSC_VER
free(cString);
-#endif
+//#endif
return ret;
} //dcmStrFloat()
-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);
- memset(h, 0, sizeof(nifti_1_header)); //zero-fill structure so unused items are consistent
+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;
@@ -1234,11 +1185,7 @@ int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h) {
else if ((d.bitsAllocated == 64) && (d.isFloat))
h->datatype = DT_FLOAT64;
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
+ 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))
@@ -1305,7 +1252,8 @@ int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h) {
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);
+ if (isComputeSForm)
+ headerDcm2Nii2(d, d, h);
return EXIT_SUCCESS;
} // headerDcm2Nii()
@@ -1332,13 +1280,6 @@ void changeExt (char *file_name, const char* ext) {
}
} //changeExt()
-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);
-}
-
struct TDICOMdata nii_readParRec (char * parname, int isVerbose, struct TDTI4D *dti4D) {
struct TDICOMdata d = clear_dicom_data();
strcpy(d.protocolName, ""); //erase dummy with empty
@@ -1381,7 +1322,7 @@ struct TDICOMdata nii_readParRec (char * parname, int isVerbose, struct TDTI4D
#define kv3 46
#define kASL 48
char buff[LINESZ];
- float intenScalePhilips = 0.0f;
+ //float intenScalePhilips = 0.0f;
float maxBValue = 0.0f;
float maxDynTime = 0.0f;
float minDynTime = 999999.0f;
@@ -1411,11 +1352,7 @@ struct TDICOMdata nii_readParRec (char * parname, int isVerbose, struct TDTI4D
,Comment[4], Comment[5],Comment[6]);
parVers = (int)round(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
+ 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)
@@ -1444,7 +1381,7 @@ struct TDICOMdata nii_readParRec (char * parname, int isVerbose, struct TDTI4D
strcat(d.patientName, Comment[5]);
strcat(d.patientName, Comment[6]);
strcat(d.patientName, Comment[7]);
- //printf("%s\n",d.patientName);
+ //printMessage("%s\n",d.patientName);
}
if ((strcmp(Comment[0], "Protocol") == 0) && (strcmp(Comment[1], "name") == 0)) {
@@ -1453,7 +1390,7 @@ struct TDICOMdata nii_readParRec (char * parname, int isVerbose, struct TDTI4D
strcat(d.protocolName, Comment[5]);
strcat(d.protocolName, Comment[6]);
strcat(d.protocolName, Comment[7]);
- //printf("%s\n",d.protocolName);
+ //printMessage("%s\n",d.protocolName);
}
if ((strcmp(Comment[0], "Examination") == 0) && (strcmp(Comment[1], "date/time") == 0)) {
strcpy(d.studyDate, Comment[3]);
@@ -1484,11 +1421,7 @@ struct TDICOMdata nii_readParRec (char * parname, int isVerbose, struct TDTI4D
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
+ printError("PAR files should have 'CLINICAL TRYOUT' line with a version from 2.0-4.2: %s\n", parname);
free (cols);
return d;
}
@@ -1516,14 +1449,10 @@ struct TDICOMdata nii_readParRec (char * parname, int isVerbose, struct TDTI4D
d.bitsStored = (int) cols[kBitsPerVoxel];
d.intenIntercept = cols[kRI];
d.intenScale = cols[kRS];
- intenScalePhilips = cols[kSS];
+ d.intenScalePhilips = cols[kSS];
} 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
+ printError("Slice dimensions or bit depth varies %s\n", parname);
return d;
}
if ((d.patientPositionSequentialRepeats == 0) && ((!isSameFloat(d.patientPosition[1],cols[kPositionRL])) ||
@@ -1538,6 +1467,21 @@ struct TDICOMdata nii_readParRec (char * parname, int isVerbose, struct TDTI4D
if (cols[kImageType] != 0) d.isHasPhase = true;
if (cols[kDynTime] > maxDynTime) maxDynTime = cols[kDynTime];
if (cols[kDynTime] < minDynTime) minDynTime = cols[kDynTime];
+ 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) ) {
@@ -1551,8 +1495,8 @@ struct TDICOMdata nii_readParRec (char * parname, int isVerbose, struct TDTI4D
int dir = d.CSA.numDti;
if (dir <= kMaxDTI4D) {
if (isVerbose ) {
- if (d.CSA.numDti == 1) printf("n\tdir\tbValue\tV1\tV2\tV3\n");
- printf("%d\t%g\t%g\t%g\t%g\t%g\n", dir-1, cols[kGradientNumber], cols[kbval], cols[kv1], cols[kv2], cols[kv3]);
+ 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];
@@ -1564,7 +1508,7 @@ struct TDICOMdata nii_readParRec (char * parname, int isVerbose, struct TDTI4D
}
} //if DTI directions
- //printf("%f %f %lu\n",cols[9],cols[kGradientNumber], strlen(buff))
+ //printMessage("%f %f %lu\n",cols[9],cols[kGradientNumber], strlen(buff))
p = fgets (buff, LINESZ, fp);//get next line
}
@@ -1574,37 +1518,25 @@ struct TDICOMdata nii_readParRec (char * parname, int isVerbose, struct TDTI4D
d.isValid = true;
d.isSigned = true;
if ((slice % d.xyzDim[3]) != 0) {
- printf("Error: slices (%d) not divisible by slices (%d) [acquisition aborted]. Try nii_rescue_par to fix this: %s\n", slice, d.xyzDim[3], parname);
+ printError("Total number of slices (%d) not divisible by slices per 3D volume (%d) [acquisition aborted]. Try nii_rescue_par to fix this: %s\n", slice, d.xyzDim[3], parname);
d.isValid = 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 << " volumes"<<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");
-
+ printWarning("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 volumes\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 = (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;
+ 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);
@@ -1634,9 +1566,9 @@ struct TDICOMdata nii_readParRec (char * parname, int isVerbose, struct TDTI4D
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;
+ 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]);
@@ -1666,34 +1598,22 @@ struct TDICOMdata nii_readParRec (char * parname, int isVerbose, struct TDTI4D
}
//finish up
changeExt (parname, "REC");
+ #ifndef _MSC_VER //Linux is case sensitive, #include <unistd.h>
+ 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) {
- printf("Error: unable to convert DTI [increase kMaxDTI4D]\n");
+ printError("Unable to convert DTI [increase kMaxDTI4D]\n");
d.CSA.numDti = 0;
};
if ((maxBValue <= 0.0f) && (maxDynTime > minDynTime) && (d.CSA.numDti > 1)) {
float TRms = 1000.0f * (maxDynTime - minDynTime) / (float)(d.CSA.numDti-1);
if (fabs(TRms - d.TR) > 0.005f)
- printf("Warning: reported TR=%gms, measured TR=%gms (prospect. motion corr.?)\n", d.TR, TRms);
+ printWarning("Reported TR=%gms, measured TR=%gms (prospect. motion corr.?)\n", d.TR, TRms);
d.TR = TRms;
}
- if (intenScalePhilips != 0.0) {
- //printf("Philips Precise RS:RI:SS = %g:%g:%g (see PMC3998685)\n",d.intenScale,d.intenIntercept,intenScalePhilips);
- //we will report calibrated "FP" values http://www.ncbi.nlm.nih.gov/pmc/articles/PMC3998685/
- //# === PIXEL VALUES =============================================================
- //# PV = pixel value in REC file, FP = floating point value, DV = displayed value on console
- //# RS = rescale slope, RI = rescale intercept, SS = scale slope
- //# DV = PV * RS + RI FP = DV / (RS * SS)
- float l0 = PhilipsPreciseVal (0, d.intenScale, d.intenIntercept, intenScalePhilips);
- float l1 = PhilipsPreciseVal (1, d.intenScale, d.intenIntercept, intenScalePhilips);
- if (l0 != l1) {
- d.intenIntercept = l0;
- d.intenScale = l1-l0;
- }
- }
-
return d;
} //nii_readParRec()
@@ -1704,7 +1624,7 @@ size_t nii_SliceBytes(struct nifti_1_header hdr) {
if (hdr.dim[i] > 1)
imgsz = imgsz * hdr.dim[i];
return imgsz;
-} //nii_ImgBytes()
+} //nii_SliceBytes()
size_t nii_ImgBytes(struct nifti_1_header hdr) {
size_t imgsz = hdr.bitpix/8;
@@ -1746,11 +1666,7 @@ unsigned char * nii_demosaic(unsigned char* inImg, struct nifti_1_header *hdr, i
} //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) {
-#ifdef myUseCOut
- std::cout<<"WARNING: CSA 'ProtocolSliceNumber' SUGGESTS REVERSED SLICE ORDER: SPATIAL AND DTI COORDINATES UNTESTED" <<std::endl;
-#else
- printf("WARNING: WEIRD CSA 'ProtocolSliceNumber': SPATIAL AND DTI TRANSFORMS UNTESTED\n");
-#endif
+ 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;
@@ -1778,11 +1694,11 @@ unsigned char * nii_flipImgY(unsigned char* bImg, struct nifti_1_header *hdr){
lineBytes = hdr->dim[1];
dim3to7 = dim3to7 * 3;
} //rgb data saved planar (RRR..RGGGG..GBBB..B
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
unsigned char * line = (unsigned char *)malloc(sizeof(unsigned char) * (lineBytes));
-#else
- unsigned char line[lineBytes];
-#endif
+//#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
@@ -1797,9 +1713,9 @@ unsigned char * nii_flipImgY(unsigned char* bImg, struct nifti_1_header *hdr){
slBottom += lineBytes;
} //for y
} //for each slice
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
free(line);
-#endif
+//#endif
return bImg;
} // nii_flipImgY()
@@ -1811,15 +1727,15 @@ unsigned char * nii_flipImgZ(unsigned char* bImg, struct nifti_1_header *hdr){
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];
-#ifdef _MSC_VER
+ 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
+//#else
+// unsigned char slice[sliceBytes];
+//#endif
for (int vol = 0; vol < dim4to7; vol++) { //for each 2D slice
- long long slBottom = vol*volBytes;
- long long slTop = ((vol+1)*volBytes)-sliceBytes;
+ 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);
@@ -1829,12 +1745,41 @@ unsigned char * nii_flipImgZ(unsigned char* bImg, struct nifti_1_header *hdr){
slBottom += sliceBytes;
} //for Z
} //for each volume
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
free(slice);
-#endif
+//#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
+ 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;
@@ -1853,9 +1798,9 @@ unsigned char * nii_flipZ(unsigned char* bImg, struct nifti_1_header *h){
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);
+ //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()
@@ -1876,8 +1821,8 @@ unsigned char * nii_flipY(unsigned char* bImg, struct nifti_1_header *h){
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);
+ 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()
@@ -1885,7 +1830,7 @@ unsigned char * nii_flipY(unsigned char* bImg, struct nifti_1_header *h){
//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
- printf("Warning: support for images that allocate 12 bits is experimental\n");
+ 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;
@@ -1912,7 +1857,7 @@ 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
- printf("Warning: support for images that allocate 12 bits is experimental\n");
+ 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;
@@ -1936,19 +1881,23 @@ unsigned char * nii_loadImgCore(char* imgname, struct nifti_1_header hdr, int bi
imgszRead = round(imgsz * 0.75);
FILE *file = fopen(imgname , "rb");
if (!file) {
- printf("Error: unable to open %s\n", imgname);
+ printError("Unable to open %s\n", imgname);
return NULL;
}
fseek(file, 0, SEEK_END);
- long long fileLen=ftell(file);
+ long fileLen=ftell(file);
if (fileLen < (imgszRead+hdr.vox_offset)) {
- printf("File not large enough to store image data: %s\n", imgname);
+ 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);
- fread(bImg, imgszRead, 1, file);
+ 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;
@@ -1965,12 +1914,11 @@ unsigned char * nii_planar2rgb(unsigned char* bImg, struct nifti_1_header *hdr,
//int sliceBytes24 = hdr->dim[1]*hdr->dim[2] * hdr->bitpix/8;
int sliceBytes8 = hdr->dim[1]*hdr->dim[2];
int sliceBytes24 = sliceBytes8 * 3;
- //printf("planar->rgb %dx%dx%d\n", hdr->dim[1],hdr->dim[2], dim3to7);
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
unsigned char * slice24 = (unsigned char *)malloc(sizeof(unsigned char) * (sliceBytes24));
-#else
- unsigned char slice24[ sliceBytes24 ];
-#endif
+//#else
+// unsigned char slice24[ sliceBytes24 ];
+//#endif
int sliceOffsetRGB = 0;
int sliceOffsetR = 0;
int sliceOffsetG = sliceOffsetR + sliceBytes8;
@@ -1987,9 +1935,9 @@ unsigned char * nii_planar2rgb(unsigned char* bImg, struct nifti_1_header *hdr,
}
sliceOffsetRGB += sliceBytes24;
} //for each slice
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
free(slice24);
-#endif
+//#endif
return bImg;
} //nii_rgb2Planar()
@@ -2004,12 +1952,12 @@ unsigned char * nii_rgb2planar(unsigned char* bImg, struct nifti_1_header *hdr,
//int sliceBytes24 = hdr->dim[1]*hdr->dim[2] * hdr->bitpix/8;
int sliceBytes8 = hdr->dim[1]*hdr->dim[2];
int sliceBytes24 = sliceBytes8 * 3;
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
unsigned char * slice24 = (unsigned char *)malloc(sizeof(unsigned char) * (sliceBytes24));
-#else
- unsigned char slice24[ sliceBytes24 ];
-#endif
- //printf("rgb->planar %dx%dx%d\n", hdr->dim[1],hdr->dim[2], dim3to7);
+//#else
+// unsigned char slice24[ sliceBytes24 ];
+//#endif
+ //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);
@@ -2025,9 +1973,9 @@ unsigned char * nii_rgb2planar(unsigned char* bImg, struct nifti_1_header *hdr,
}
sliceOffsetR += sliceBytes24;
} //for each slice
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
free(slice24);
-#endif
+//#endif
return bImg;
} //nii_rgb2Planar()
@@ -2074,9 +2022,9 @@ unsigned char * nii_XYTZ_XYZT(unsigned char* bImg, struct nifti_1_header *hdr, i
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;
- printf("Converting XYTZ to XYZT with %d slices (Z) and %d volumes (T).\n",hdr->dim[3], dim4to7);
+ printMessage("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)\n", seqRepeats,dim4to7);
+ 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;
@@ -2123,20 +2071,20 @@ unsigned char * nii_loadImgCoreJasper(char* imgname, struct nifti_1_header hdr,
jas_image_t *image;
jas_setdbglevel(0);
if (!(in = jas_stream_fopen(imgname, "rb"))) {
- printf( "Error: cannot open input image file %s\n", imgname);
+ 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) {
- printf( "Error: input image has unknown format %s offset %d bytes %d\n", imgname, dcm.imageStart, dcm.imageBytes);
+ 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))) {
- printf("Error: cannot decode image data %s offset %d bytes %d\n", imgname, dcm.imageStart, dcm.imageBytes);
+ printError("Cannot decode image data %s offset %d bytes %d\n", imgname, dcm.imageStart, dcm.imageBytes);
return NULL;
}
int numcmpts;
@@ -2144,7 +2092,7 @@ unsigned char * nii_loadImgCoreJasper(char* imgname, struct nifti_1_header hdr,
switch (jas_clrspc_fam(jas_image_clrspc(image))) {
case JAS_CLRSPC_FAM_RGB:
if (jas_image_clrspc(image) != JAS_CLRSPC_SRGB)
- printf("Warning: inaccurate color\n");
+ printWarning("Inaccurate color\n");
numcmpts = 3;
if ((cmpts[0] = jas_image_getcmptbytype(image,
JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R))) < 0 ||
@@ -2152,22 +2100,22 @@ unsigned char * nii_loadImgCoreJasper(char* imgname, struct nifti_1_header hdr,
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) {
- printf("Error: missing color component\n");
+ printError("Missing color component\n");
return NULL;
}
break;
case JAS_CLRSPC_FAM_GRAY:
if (jas_image_clrspc(image) != JAS_CLRSPC_SGRAY)
- printf("Warning: inaccurate color\n");
+ printWarning("Inaccurate color\n");
numcmpts = 1;
if ((cmpts[0] = jas_image_getcmptbytype(image,
JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_GRAY_Y))) < 0) {
- printf("Error: missing color component\n");
+ printError("Missing color component\n");
return NULL;
}
break;
default:
- printf("error: unsupported color space\n");
+ printError("Unsupported color space\n");
return NULL;
break;
}
@@ -2176,7 +2124,7 @@ unsigned char * nii_loadImgCoreJasper(char* imgname, struct nifti_1_header hdr,
int prec = jas_image_cmptprec(image, cmpts[0]);
int sgnd = jas_image_cmptsgnd(image, cmpts[0]);
#ifdef MY_DEBUG
- printf("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());
+ 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 ||
@@ -2187,7 +2135,7 @@ unsigned char * nii_loadImgCoreJasper(char* imgname, struct nifti_1_header hdr,
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)) {
- printf("The NIfTI format cannot be used to represent an image with this geometry.\n");
+ printMessage("The NIfTI format cannot be used to represent an image with this geometry.\n");
return NULL;
}
}
@@ -2195,7 +2143,7 @@ unsigned char * nii_loadImgCoreJasper(char* imgname, struct nifti_1_header hdr,
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)) {
- printf("Serious catastrophic error decompression error\n");
+ printError("Catastrophic decompression error\n");
return NULL;
}
jas_seqent_t v;
@@ -2204,7 +2152,7 @@ unsigned char * nii_loadImgCoreJasper(char* imgname, struct nifti_1_header hdr,
int16_t * img16i = (int16_t*) img; //signed 16-bit
if (sgnd) bpp = -bpp;
if (bpp == -1) {
- printf("Error: Signed 8-bit DICOM?\n");
+ printError("Signed 8-bit DICOM?\n");
return NULL;
}
jas_matrix_t *data;
@@ -2263,13 +2211,17 @@ TJPEG * decode_JPEG_SOF_0XC3_stack (const char *fn, int skipBytes, bool isVerbo
fseek(reader, 0, SEEK_END);
long lRawSz = ftell(reader)- skipBytes;
if (lRawSz <= 8) {
- printf("Error opening %s\n", fn);
+ printError("Unable to open %s\n", fn);
abortGoto(); //read failure
}
fseek(reader, skipBytes, SEEK_SET);
unsigned char *lRawRA = (unsigned char*) malloc(lRawSz);
- fread(lRawRA, 1, lRawSz, reader);
+ size_t lSz = fread(lRawRA, 1, lRawSz, reader);
fclose(reader);
+ if (lSz < 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)) {
@@ -2278,11 +2230,11 @@ TJPEG * decode_JPEG_SOF_0XC3_stack (const char *fn, int skipBytes, bool isVerbo
int tagLength = dcmInt(4,&lRawRA[lRawPos],isLittleEndian);
long tagEnd =lRawPos + tagLength + 4;
if (isVerbose)
- printf("Tag %#x length %d end at %ld\n", tag, tagLength, tagEnd+skipBytes);
+ 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)
- printf("Warning: JPEG signature 0xFFD8FF not found at offset %d of %s\n", skipBytes, fn);
+ printWarning("JPEG signature 0xFFD8FF not found at offset %d of %s\n", skipBytes, fn);
} else {
lOffsetRA[frame].offset = lRawPos+skipBytes;
lOffsetRA[frame].size = tagLength;
@@ -2292,7 +2244,7 @@ TJPEG * decode_JPEG_SOF_0XC3_stack (const char *fn, int skipBytes, bool isVerbo
}
free(lRawRA);
if (frame < frames) {
- printf("Only found %d of %d JPEG fragments. Please use dcmdjpeg to uncompress data.\n", frame, frames);
+ printMessage("Only found %d of %d JPEG fragments. Please use dcmdjpeg to uncompress data.\n", frame, frames);
abortGoto();
}
return lOffsetRA;
@@ -2302,10 +2254,26 @@ unsigned char * nii_loadImgJPEGC3(char* imgname, struct nifti_1_header hdr, stru
//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);
+ //printf("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);
- //printf("JPEG %fms\n", ((double)(clock()-start))/1000);
+ 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
- //printf("Unable to decode all slices (%d/%d). Please use dcmdjpeg to uncompress data.\n", frames, hdr.dim[3]);
+ //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;
@@ -2315,10 +2283,10 @@ unsigned char * nii_loadImgJPEGC3(char* imgname, struct nifti_1_header hdr, stru
unsigned char *bImg = (unsigned char *)malloc(imgsz);
for (int frame = 0; frame < hdr.dim[3]; frame++) {
if (isVerbose)
- printf("JPEG frame %d has %ld bytes @ %ld\n", frame, offsetRA[frame].size, offsetRA[frame].offset);
+ 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) {
- printf("Unable to decode JPEG. Please use dcmdjpeg to uncompress data.\n");
+ printMessage("Unable to decode JPEG. Please use dcmdjpeg to uncompress data.\n");
free(bImg);
return NULL;
}
@@ -2336,10 +2304,65 @@ unsigned char * nii_loadImgJPEGC3(char* imgname, struct nifti_1_header hdr, stru
#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) {
+//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) {
- //printf("50 offset %d\n", dcm.imageStart);
+//decode classic JPEG using nanoJPEG
+ //printMessage("50 offset %d\n", dcm.imageStart);
if( access(imgname, F_OK ) == -1 ) {
- printf("Error: unable to find '%s'\n", imgname);
+ printError("Unable to find '%s'\n", imgname);
return NULL;
}
//load compressed data
@@ -2348,7 +2371,7 @@ unsigned char * nii_loadImgJPEG50(char* imgname, struct nifti_1_header hdr, stru
int size = (int) ftell(f);
size = size - dcm.imageStart;
if (size < 8) {
- printf("Error file too small\n");
+ printError("File too small '%s'\n", imgname);
fclose(f);
return NULL;
}
@@ -2359,7 +2382,7 @@ unsigned char * nii_loadImgJPEG50(char* imgname, struct nifti_1_header hdr, stru
//decode
njInit();
if (njDecode(buf, size)) {
- printf("Error decoding JPEG image.\n");
+ printError("Unable to decode JPEG image.\n");
return NULL;
}
free(buf);
@@ -2368,13 +2391,22 @@ unsigned char * nii_loadImgJPEG50(char* imgname, struct nifti_1_header hdr, stru
njDone();
return bImg;
}
+#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) == EXIT_FAILURE) return NULL;
+ if (headerDcm2Nii(dcm, hdr, true) == EXIT_FAILURE) return NULL; //TOFU
unsigned char * img;
if (dcm.compressionScheme == kCompress50) {
- img = nii_loadImgJPEG50(imgname, *hdr, dcm);
+ #ifdef myDisableClassicJPEG
+ printMessage("Software not compiled to decompress classic JPEG DICOM images\n");
+ return NULL;
+ #else
+ img = nii_loadImgJPEG50(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
+ #endif
} else if (dcm.compressionScheme == kCompressC3) {
img = nii_loadImgJPEGC3(imgname, *hdr, dcm, (isVerbose > 0));
} else
@@ -2390,19 +2422,14 @@ unsigned char * nii_loadImgXL(char* imgname, struct nifti_1_header *hdr, struct
#endif
#endif
if (dcm.compressionScheme == kCompressYes) {
- printf("Software not set up to decompress DICOM\n");
+ 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) {
-#ifdef __BIG_ENDIAN__
- if ((dcm.isLittleEndian) && (hdr->bitpix > 8))
- img = nii_byteswap(img, hdr);
-#else
- if ((!dcm.isLittleEndian) && (hdr->bitpix > 8))
+ if ((dcm.isLittleEndian != littleEndianPlatform()) && (hdr->bitpix > 8))
img = nii_byteswap(img, hdr);
-#endif
}
if ((dcm.compressionScheme == kCompressNone) && (hdr->datatype ==DT_RGB24)) //img = nii_planar2rgb(img, hdr, dcm.isPlanarRGB); //
img = nii_rgb2planar(img, hdr, dcm.isPlanarRGB);//do this BEFORE Y-Flip, or RGB order can be flipped
@@ -2422,7 +2449,7 @@ unsigned char * nii_loadImgXL(char* imgname, struct nifti_1_header *hdr, struct
}
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);
+ headerDcm2NiiSForm(dcm,dcm, hdr, false);
return img;
} //nii_loadImgXL()
@@ -2430,15 +2457,16 @@ int isDICOMfile(const char * fname) { //0=NotDICOM, 1=DICOM, 2=Maybe(not Part 10
FILE *fp = fopen(fname, "rb");
if (!fp) return 0;
fseek(fp, 0, SEEK_END);
- long long fileLen=ftell(fp);
+ long fileLen=ftell(fp);
if (fileLen < 256) {
fclose(fp);
return 0;
}
fseek(fp, 0, SEEK_SET);
unsigned char buffer[256];
- fread(buffer, 256, 1, fp);
+ 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))
@@ -2458,7 +2486,7 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
struct stat s;
if( stat(fname,&s) == 0 ) {
if( !(s.st_mode & S_IFREG) ){
- printf( "DICOM read fail: not a valid file (perhaps a directory) %s\n",fname);
+ printMessage( "DICOM read fail: not a valid file (perhaps a directory) %s\n",fname);
return d;
}
}
@@ -2472,34 +2500,30 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
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
+ printMessage("Unable to open file %s\n", fname);
return d;
}
fseek(file, 0, SEEK_END);
- long long fileLen=ftell(file); //Get file length
+ 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
+ printMessage( "File too small to be a DICOM image %s\n", fname);
return d;
}
fseek(file, 0, SEEK_SET);
//Allocate memory
unsigned char *buffer=(unsigned char *)malloc(fileLen+1);
if (!buffer) {
- printf( "Memory error!");
+ printError( "Memory exhausted!");
fclose(file);
return d;
}
//Read file contents into buffer
- fread(buffer, fileLen, 1, file);
+ size_t sz = fread(buffer, 1, fileLen, file);
fclose(file);
+ if (sz < fileLen) {
+ printError("Only loaded %zu of %ld bytes for %s\n", sz, fileLen, fname);
+ return d;
+ }
//bool isPart10prefix = true; //assume 132 byte header http://nipy.bic.berkeley.edu/nightly/nibabel/doc/dicom/dicom_intro.html
//if ((buffer[128] != 'D') || (buffer[129] != 'I') || (buffer[130] != 'C') || (buffer[131] != 'M')) {
// if ((buffer[0] != 8) || (buffer[1] != 0) || (buffer[2] != 5) || (buffer[3] != 0)){
@@ -2508,7 +2532,7 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
// }
// isPart10prefix = false; //no 132 byte header, not a valid part 10 file http://fileformats.archiveteam.org/wiki/DICOM
// d.isExplicitVR = false;
- // //printf("Warning: not a valid part 10 DICOM (missing 'DICM' signature): %s\n", fname);
+ // //printWarning("Not a valid part 10 DICOM (missing 'DICM' signature): %s\n", fname);
//}
//DEFINE DICOM TAGS
#define kUnused 0x0001+(0x0001 << 16 )
@@ -2518,6 +2542,7 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
#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 kManufacturer 0x0008+(0x0070 << 16 )
@@ -2527,17 +2552,23 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
#define kComplexImageComponent (uint32_t) 0x0008+(0x9208 << 16 )//'0008' '9208' 'CS' 'ComplexImageComponent'
#define kPatientName 0x0010+(0x0010 << 16 )
#define kPatientID 0x0010+(0x0020 << 16 )
+#define kBodyPartExamined 0x0018+(0x0015 << 16)
#define kScanningSequence 0x0018+(0x0020 << 16)
+#define kSequenceVariant 0x0018+(0x0021 << 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 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 kFlipAngle 0x0018+(0x1314 << 16 )
@@ -2548,6 +2579,8 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
#define kDiffusionDirectionGEX 0x0019+(0x10BB<< 16 ) //DS
#define kDiffusionDirectionGEY 0x0019+(0x10BC<< 16 ) //DS
#define kDiffusionDirectionGEZ 0x0019+(0x10BD<< 16 ) //DS
+#define kStudyInstanceUID 0x0020+(0x000D << 16 )
+#define kSeriesInstanceUID 0x0020+(0x000E << 16 )
#define kPatientPosition 0x0020+(0x0032 << 16 )
#define kSeriesNum 0x0020+(0x0011 << 16 )
#define kAcquNum 0x0020+(0x0012 << 16 )
@@ -2571,15 +2604,20 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
#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 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 kDiffusionBFactorGE 0x0043+(0x1039 << 16 ) //IS dicm2nii's SlopInt_6_9
#define kCoilSiemens 0x0051+(0x100F << 16 )
#define kLocationsInAcquisition 0x0054+(0x0081 << 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 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'
@@ -2613,14 +2651,11 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
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)
- #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
+ printMessage("DICOM appears corrupt: first group:element should be 0x0002:0x0000 '%s'\n", fname);
}
char vr[2];
- float intenScalePhilips = 0.0;
+ //float intenScalePhilips = 0.0;
+ char acquisitionDateTimeTxt[kDICOMStr] = "";
bool isEncapsulatedData = false;
bool isOrient = false;
bool isIconImageSequence = false;
@@ -2632,9 +2667,11 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
bool isAtFirstPatientPosition = false; //for 3d and 4d files: flag is true for slices at same position as first slice
bool isMosaic = false;
int phaseEncodingSteps = 0;
- int patientPositionCount = 0;
+ int patientPositionNum = 0;
int sqDepth = 0;
- //long coilNum = 0; //Siemens can save one image per coil (H12,H13,etc) or one combined image for array (HEA;HEP)
+ 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) < fileLen)) {
if (d.isLittleEndian)
groupElement = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24);
@@ -2656,7 +2693,7 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
vr[0] = 'N';
vr[1] = 'A';
if (groupElement == kUnnest2) sqDepth--;
- //if (groupElement == kUnnest2) printf("SQend %d\n", sqDepth);
+ //if (groupElement == kUnnest2) printMessage("SQend %d\n", sqDepth);
//if (groupElement == kUnnest) geiisBug = false; //don't exit if there is a proprietary thumbnail
lLength = 4;
@@ -2706,36 +2743,36 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
if ((vr[0] == 'S') && (vr[1] == 'Q')) {
sqDepth++;
- //printf("SQstart %d\n", sqDepth);
+ //printMessage("SQstart %d\n", sqDepth);
}
if ((groupElement == kNest) || ((vr[0] == 'S') && (vr[1] == 'Q'))) nest++;
if (groupElement == kUnnest) nest--;
//next: look for required tags
if ((groupElement == kNest) && (isEncapsulatedData)) {
d.imageBytes = dcmInt(4,&buffer[lPos-4],d.isLittleEndian);
- //printf("compressed data %d-> %ld\n",d.imageBytes, lPos);
- if (d.imageBytes > 12) {
+ //printMessage("compressed data %d-> %ld\n",d.imageBytes, lPos);
+ if (d.imageBytes > 128) {
d.imageStart = (int)lPos;
}
}
if ((isIconImageSequence) && ((groupElement & 0x0028) == 0x0028 )) groupElement = kUnused; //ignore icon dimensions
if (true) { //(nest <= 0) { //some Philips images have different 0020,0013
//verbose reporting :
- // printf("Pos %ld GroupElement %#08x,%#08x Length %d isLittle %d\n", lPos, (groupElement & 0xFFFF), (groupElement >> 16), lLength, d.isLittleEndian);
+ // printMessage("Pos %ld GroupElement %#08x,%#08x Length %d isLittle %d\n", lPos, (groupElement & 0xFFFF), (groupElement >> 16), lLength, d.isLittleEndian);
switch ( groupElement ) {
case kTransferSyntax: {
char transferSyntax[kDICOMStr];
dcmStr (lLength, &buffer[lPos], transferSyntax);
- //printf("transfer syntax '%s'\n", transferSyntax);
+ //printMessage("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.4.50") == 0) {
d.compressionScheme = kCompress50;
- //printf("Lossy JPEG: please decompress with Osirix or dcmdjpg. %s\n", transferSyntax);
+ //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;
- //printf("Lossy JPEG: please decompress with Osirix or dcmdjpg. %s\n", transferSyntax);
+ //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) {
@@ -2744,18 +2781,18 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
//d.isCompressed = true;
//https://www.medicalconnections.co.uk/kb/Transfer_Syntax should be SOF = 0xC3
d.compressionScheme = kCompressC3;
- //printf("Ancient JPEG-lossless (SOF type 0xc3): please check conversion\n");
+ //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.isCompressed = true;
d.compressionScheme = kCompressC3;
- //printf("Ancient JPEG-lossless (SOF type 0xc3): please check conversion\n");
+ //printMessage("Ancient JPEG-lossless (SOF type 0xc3): please check conversion\n");
//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;
- //printf("JPEG2000 Lossless support is new: please validate conversion\n");
+ //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;
- //printf("JPEG2000 support is new: please validate conversion\n");
+ //printMessage("JPEG2000 support is new: please validate conversion\n");
} 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.4.91") == 0)
@@ -2763,11 +2800,7 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
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' (see www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage)\n",transferSyntax);
-#endif
+ 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
@@ -2790,6 +2823,11 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
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;
@@ -2804,6 +2842,7 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
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);
@@ -2815,7 +2854,6 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
dcmStr (lLength, &buffer[lPos], d.patientID);
break;
case kSeriesDescription: {
- //if (strlen(d.protocolName) < 1)
dcmStr (lLength, &buffer[lPos], d.seriesDescription);
break; }
case kManufacturersModelName :
@@ -2840,7 +2878,7 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
break;
/*case kDiffusionBFactorSiemens :
if (d.manufacturer == kMANUFACTURER_SIEMENS)
- printf(">>>>%f\n,",dcmStrFloat(lLength, &buffer[lPos]));
+ printMessage("last scan location %f\n,",dcmStrFloat(lLength, &buffer[lPos]));
break;*/
case kDiffusionDirectionGEX :
@@ -2855,31 +2893,41 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
d.CSA.numDti = 1;
}
break;
+ case kStudyInstanceUID :
+ dcmStr (lLength, &buffer[lPos], d.studyInstanceUID);
+ break;
+
+ case kSeriesInstanceUID :
+ dcmStr (lLength, &buffer[lPos], d.seriesInstanceUID);
+ 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
+ printWarning("Philips R3.2.2 can report different positions for the same slice. Attempting patch.\n");
is2005140FSQwarned = true;
} else {
- patientPositionCount++;
+ patientPositionNum++;
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
+ 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 = patientPositionCount-1;
+ d.patientPositionSequentialRepeats = patientPositionNum-1;
} //if different position from 1st slice in file
} //if not first slice in file
+ 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:
@@ -2920,6 +2968,9 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
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;
@@ -2935,6 +2986,9 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
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;
@@ -2950,19 +3004,30 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
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.TE = dcmStrFloat(lLength, &buffer[lPos]);
+ 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))
- intenScalePhilips = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+ d.intenScalePhilips = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
break;
case kIntercept :
@@ -2987,12 +3052,14 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
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 kStackSliceNumber: { //https://github.com/Kevin-Mattheus-Moerman/GIBBON/blob/master/dicomDict/PMS-R32-dict.txt
+ int stackSliceNumber = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+ printf("%d\n",stackSliceNumber);
+ break;
+ }*/
case kNumberOfDynamicScans:
d.numberOfDynamicScans = dcmStrInt(lLength, &buffer[lPos]);
break;
@@ -3001,10 +3068,18 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
if (lLength > 1) d.is3DAcq = (buffer[lPos]=='3') && (toupper(buffer[lPos+1]) == 'D');
#endif
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 kSequenceName : {
//if (strlen(d.protocolName) < 1) //precedence given to kProtocolName and kProtocolNameGE
dcmStr (lLength, &buffer[lPos], d.sequenceName);
@@ -3052,7 +3127,6 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
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];
@@ -3060,6 +3134,44 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
d.CSA.dtiV[d.CSA.numDti-1][0] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);*/
}
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);
@@ -3083,7 +3195,7 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
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];
- //printf("dti XYZ %g %g %g\n",d.CSA.dtiV[1],d.CSA.dtiV[2],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);*/
@@ -3091,18 +3203,14 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
break;
case kWaveformSq:
d.imageStart = 1; //abort!!!
-#ifdef myUseCOut
- std::cout<<"Skipping DICOM (audio not image) " <<std::endl;
-#else
- printf("Skipping DICOM (audio not image) '%s'\n", fname);
-#endif
+ 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:
- // printf("---->%d,",lLength);
+ // printMessage("---->%d,",lLength);
// break;
case kRealWorldIntercept:
if (isSameFloat(0.0, d.intenIntercept)) //give precedence to standard value
@@ -3118,16 +3226,14 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
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
+ 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;
@@ -3138,7 +3244,6 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
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
@@ -3146,12 +3251,11 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
//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) {
+ if ((d.compressionScheme != kCompressNone) && (!isIconImageSequence)) {
lLength = 0;
isEncapsulatedData = true;
}
-
- isIconImageSequence = false;
+ isIconImageSequence = false;
break;
case kImageStartFloat:
d.isFloat = true;
@@ -3160,7 +3264,7 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
isIconImageSequence = false;
break;
case kImageStartDouble:
- printf("WARNING: double-precision DICOM conversion untested: please provide samples to developer\n");
+ 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;
@@ -3170,15 +3274,46 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
} //switch/case for groupElement
} //if nest
//#ifdef MY_DEBUG
- if (isVerbose > 1)
- printf(" Tag\t%04x,%04x\tSize=%u\tOffset=%ld\tnest=%d\n", groupElement & 65535,groupElement>>16, lLength, lPos, nest);
- //printf(" tag=%04x,%04x length=%u pos=%ld %c%c nest=%d\n", groupElement & 65535,groupElement>>16, lLength, lPos,vr[0], vr[1], 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 (int pos = 0; pos<strlen(tagStr); pos ++)
+ if ((tagStr[pos] == '<') || (tagStr[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, 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, lPos, nest);
+ } //printMessage(" tag=%04x,%04x length=%u pos=%ld %c%c nest=%d\n", groupElement & 65535,groupElement>>16, lLength, lPos,vr[0], vr[1], nest);
//#endif
lPos = lPos + (lLength);
- }
+ //printMessage("%d\n",d.imageStart);
+ } //while d.imageStart == 0
free (buffer);
+ //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);
- //printf("slices in Acq %d %d\n",d.locationsInAcquisition,locationsInAcquisitionPhilips);
+ //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))
@@ -3187,27 +3322,27 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
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!
+ //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)) {
- printf("Please check voxel size\n");
+ printMessage("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");
+ printMessage("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");
+ printMessage("Unable to determine slice thickness: please check voxel size\n");
d.xyzMM[3] = 1.0;
}
- //printf("Patient Position\t%g\t%g\t%g\tThick\t%g\n",d.patientPosition[1],d.patientPosition[2],d.patientPosition[3], d.xyzMM[3]);
- //printf("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);
- // printf("ser %ld\n", d.seriesNum);
+ //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
@@ -3219,12 +3354,12 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
// 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
- printf("Error: unable to decode %d-bit images with Transfer Syntax 1.2.840.10008.1.2.4.51, decompress with dcmdjpg\n", d.bitsAllocated);
+ printError("Unable to decode %d-bit images with Transfer Syntax 1.2.840.10008.1.2.4.51, decompress with dcmdjpg\n", d.bitsAllocated);
d.isValid = false;
}
if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (isMosaic) && (d.CSA.mosaicSlices < 1) && (phaseEncodingSteps > 0) && ((d.xyzDim[1] % phaseEncodingSteps) == 0) && ((d.xyzDim[2] % phaseEncodingSteps) == 0) ) {
d.CSA.mosaicSlices = (d.xyzDim[1] / phaseEncodingSteps) * (d.xyzDim[2] / phaseEncodingSteps);
- printf("Warning: mosaic inferred without CSA header (check number of slices and spatial orientation)\n");
+ 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.
@@ -3236,32 +3371,35 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
strcpy(d.protocolName, d.sequenceName);
// if (!isOrient) {
// if (d.isNonImage)
- // printf("Warning: spatial orientation ambiguous (tag 0020,0037 not found) [probably not important: derived image]: %s\n", fname);
+ // 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))
- // printf("Warning: spatial orientation ambiguous (tag 0020,0037 not found) [perhaps derived FA that is not required]: %s\n", fname);
+ // printWarning("Spatial orientation ambiguous (tag 0020,0037 not found) [perhaps derived FA that is not required]: %s\n", fname);
// else
- // printf("Serious error: spatial orientation ambiguous (tag 0020,0037 not found): %s\n", fname);
+ // 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) {
- printf("%s\n patient position\t%g\t%g\t%g\n",fname, d.patientPosition[1],d.patientPosition[2],d.patientPosition[3]);
- printf(" 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\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, [...]
+ printMessage("%s\n patient position\t%g\t%g\t%g\n",fname, d.patientPosition[1],d.patientPosition[2],d.patientPosition[3]);
+ 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.isLittle [...]
if (d.CSA.dtiV[0] > 0)
- printf(" DWI bxyz %g %g %g %g\n", d.CSA.dtiV[0], d.CSA.dtiV[1], d.CSA.dtiV[2], d.CSA.dtiV[3]);
+ 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.CSA.numDti >= kMaxDTI4D) {
- printf("Error: unable to convert DTI [increase kMaxDTI4D]\n");
+ 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 (intenScalePhilips != 0.0) {
- printf("Philips Precise RS:RI:SS = %g:%g:%g (see PMC3998685)\n",d.intenScale,d.intenIntercept,intenScalePhilips);
- //we will report calibrated "FP" values http://www.ncbi.nlm.nih.gov/pmc/articles/PMC3998685/
- float l0 = PhilipsPreciseVal (0, d.intenScale, d.intenIntercept, intenScalePhilips);
- float l1 = PhilipsPreciseVal (1, d.intenScale, d.intenIntercept, intenScalePhilips);
- if (l0 != l1) {
- d.intenIntercept = l0;
- d.intenScale = l1-l0;
- }
+ 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;
}
+ //d.isValid = false; //debug only - will not create output!
return d;
} // readDICOM()
@@ -3271,3 +3409,4 @@ struct TDICOMdata readDICOM(char * fname) {
} // readDICOM()
+
diff --git a/console/nii_dicom.h b/console/nii_dicom.h
index e087d38..7792b9a 100644
--- a/console/nii_dicom.h
+++ b/console/nii_dicom.h
@@ -1,6 +1,9 @@
#include <stdbool.h>
#include <string.h>
+#include "nifti1_io_core.h"
+#ifndef HAVE_R
#include "nifti1.h"
+#endif
#ifndef MRIpro_nii_dcm_h
@@ -10,18 +13,34 @@
extern "C" {
#endif
-#ifdef myEnableJasper
- #define kDCMvers "1Nov2016j" //JASPER for JPEG2000
+#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
- #ifdef myDisableOpenJPEG
- #define kDCMvers "1Nov2016" //no decompressor
- #else
- #define kDCMvers "1Nov2016o" //OPENJPEG for JPEG2000
- #endif
+ #define kCCsuf " CompilerNA" //unknown compiler!
#endif
-static const int kMaxDTI4D = 4000; //#define kMaxDTIv 4000
-#define kDICOMStr 31
+ #define kDCMvers "v1.0.20170429" kDCMsuf kCCsuf
+
+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 kMANUFACTURER_UNKNOWN 0
#define kMANUFACTURER_SIEMENS 1
#define kMANUFACTURER_GE 2
@@ -39,6 +58,7 @@ static const int kCompress50 = 3; //obsolete JPEG lossy
struct TDTI {
float V[4];
float sliceTiming;
+ int sliceNumberMrPhilips;
};
struct TDTI4D {
struct TDTI S[kMaxDTI4D];
@@ -51,14 +71,16 @@ static const int kCompress50 = 3; //obsolete JPEG lossy
};
struct TDICOMdata {
long seriesNum;
- int xyzDim[5];//, xyzOri[4];
- int coilNum, echoNum,sliceOrient,numberOfDynamicScans, manufacturer, converted2NII, acquNum, imageNum, imageStart, imageBytes, bitsStored, bitsAllocated, samplesPerPixel,patientPositionSequentialRepeats,locationsInAcquisition, compressionScheme; //
- float flipAngle, fieldStrength, TE, TR,intenScale,intenIntercept, gantryTilt, lastScanLoc, angulation[4];
- float orient[7], patientPosition[4], patientPositionLast[4], xyzMM[4], stackOffcentre[4]; //patientPosition2nd[4],
+ int xyzDim[5];
+ int patientPositionNumPhilips, coilNum, echoNum, sliceOrient,numberOfDynamicScans, manufacturer, converted2NII, acquNum, imageNum, imageStart, imageBytes, bitsStored, bitsAllocated, samplesPerPixel,patientPositionSequentialRepeats,locationsInAcquisition, compressionScheme;
+ float 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;
- bool isNonImage, isValid, is3DAcq, isExplicitVR, isLittleEndian, isPlanarRGB, isSigned, isHasPhase,isHasMagnitude,isHasMixed, isFloat, isResampled;
+ bool isXRay, isSlicesSpatiallySequentialPhilips, isNonImage, isValid, is3DAcq, isExplicitVR, isLittleEndian, isPlanarRGB, isSigned, isHasPhase,isHasMagnitude,isHasMixed, isFloat, isResampled;
char phaseEncodingRC;
- char imageType[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr],seriesDescription[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr],scanningSequence[kDICOMStr], birthDate[kDICOMStr], gender[kDICOMStr], age[kDICOMStr], studyDate[kDICOMStr],studyTime[kDICOMStr], imageComments[kDICOMStr];
+ char seriesInstanceUID[kDICOMStr], studyInstanceUID[kDICOMStr], bodyPartExamined[kDICOMStr], procedureStepDescription[kDICOMStr], imageType[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr],seriesDescription[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr],sequenceVariant[kDICOMStr],scanningSequence[kDICOMStr], birthDate[kDICOMStr], gender[kDICOMStr], age[kDICOMStr], studyDate[kDICOMStr],studyTime[kD [...]
struct TCSAdata CSA;
};
@@ -66,14 +88,16 @@ static const int kCompress50 = 3; //obsolete JPEG lossy
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);
- struct TDICOMdata nii_readParRec (char * parname, int isVerbose, struct TDTI4D *dti4D);
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 headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h, bool isComputeSForm) ;
//unsigned char * nii_loadImgX(char* imgname, struct nifti_1_header *hdr, struct TDICOMdata dcm, bool iVaries);
unsigned char * nii_loadImgXL(char* imgname, struct nifti_1_header *hdr, struct TDICOMdata dcm, bool iVaries, int compressFlag, int isVerbose);
//int foo (float vx);
diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp
index dd874f6..0220742 100755
--- a/console/nii_dicom_batch.cpp
+++ b/console/nii_dicom_batch.cpp
@@ -16,7 +16,6 @@
#endif
#if defined(__APPLE__) && defined(__MACH__)
-//#include "nii_foreign.h"
#endif
#ifndef myDisableZLib
#ifdef MiniZ
@@ -25,14 +24,15 @@
#include <zlib.h>
#endif
#endif
-#ifdef myUseCOut
- #include <iostream>
-#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 "tinydir.h"
#include <ctype.h> //toupper
#include <float.h>
#include <math.h>
@@ -62,6 +62,13 @@
const char kFileSep[2] = "/";
#endif
+#ifdef HAVE_R
+#include "ImageList.h"
+
+#undef isnan
+#define isnan ISNAN
+#endif
+
struct TDCMsort {
uint64_t indx, img;
};
@@ -176,22 +183,18 @@ void geCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI *vx){
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;
+ printMessage("GE DTI directions require head first supine acquisition\n");
+ return;
}
bool col = false;
- if (d->phaseEncodingRC== 'C')
+ if (d->phaseEncodingRC == 'C')
col = true;
- else if (d->phaseEncodingRC!= 'R') {
- printf("Error: Unable to determine DTI gradients, 0018,1312 should be either R or C");
+ else if (d->phaseEncodingRC != 'R') {
+ printWarning("Unable to determine DTI gradients, 0018,1312 should be either R or C");
return;
}
if (abs(sliceDir) != 3)
- printf("Warning: GE DTI only tested for axial acquisitions (solution: use Xiangrui Li's dicm2nii)\n");
+ 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)
@@ -201,12 +204,12 @@ void geCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI *vx){
else if (abs(sliceDir) == 3)
flp = setiVec3(0, 0, 1); //AXIAL
else
- printf("Impossible GE slice orientation!");
+ printMessage("Impossible GE slice orientation!");
if (sliceDir < 0)
flp.v[2] = 1 - flp.v[2];
- printf("Reorienting %s : %d GE DTI vectors: please validate. isCol=%d sliceDir=%d flp=%d %d %d\n", d->protocolName, d->CSA.numDti, col, sliceDir, flp.v[0], flp.v[1],flp.v[2]);
+ printMessage("Reorienting %s : %d GE DTI vectors: please validate. isCol=%d sliceDir=%d flp=%d %d %d\n", d->protocolName, d->CSA.numDti, col, sliceDir, flp.v[0], flp.v[1],flp.v[2]);
if (!col)
- printf(" reorienting for ROW phase-encoding untested.\n");
+ 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])
@@ -252,11 +255,7 @@ void siemensPhilipsCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI
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
+ 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]);
@@ -271,7 +270,7 @@ void siemensPhilipsCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI
+ (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)
- printf("Warning: volume %d appears to be an ADC map (non-zero b-value with zero vector length)\n", i);
+ 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
@@ -286,13 +285,13 @@ void siemensPhilipsCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI
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)
- 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");
+ if (abs(sliceDir) == kSliceOrientMosaicNegativeDeterminant) {
+ printWarning("Please validate DTI vectors (matrix had a negative determinant, perhaps Siemens sagittal).\n");
+ } else if ( d->sliceOrient == kSliceOrientTra) {
+ printMessage("Saving %d DTI gradients. Please validate if you are conducting DTI analyses.\n", d->CSA.numDti);
+ } else {
+ printWarning("DTI gradient directions only tested for axial (transverse) acquisitions. Please validate bvec files.\n");
+ }
}// siemensPhilipsCorrectBvecs()
bool isNanPosition(struct TDICOMdata d) { //in 2007 some Siemens RGB DICOMs did not include the PatientPosition 0020,0032 tag
@@ -315,9 +314,9 @@ void nii_SaveText(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts,
char txtname[2048] = {""};
strcpy (txtname,pathoutname);
strcat (txtname,".txt");
- //printf("Saving text %s\n",txtname);
+ //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%s\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",
+ 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%s\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.birthDate, d.gender, d.age, 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],
@@ -325,6 +324,14 @@ void nii_SaveText(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts,
fclose(fp);
}// nii_SaveText()
+bool isDerived(struct TDICOMdata d) {
+ #define kDerivedStr "DERIVED"
+ if ((strlen(d.imageType) < strlen(kDerivedStr)) || (strstr(d.imageType, kDerivedStr) == NULL))
+ return false;
+ else
+ return true;
+}
+
void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts, struct TDTI4D *dti4D, struct nifti_1_header *h) {
//https://docs.google.com/document/d/1HFUkAEE-pB-angVcYe6pf_-fVf4sCpOHKesUvfb8Grc/edit#
// Generate Brain Imaging Data Structure (BIDS) info
@@ -336,7 +343,6 @@ void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts,
char txtname[2048] = {""};
strcpy (txtname,pathoutname);
strcat (txtname,".json");
- //printf("Saving DTI %s\n",txtname);
FILE *fp = fopen(txtname, "w");
fprintf(fp, "{\n");
switch (d.manufacturer) {
@@ -354,6 +360,26 @@ void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts,
break;
};
fprintf(fp, "\t\"ManufacturersModelName\": \"%s\",\n", d.manufacturersModelName );
+ if (!opts.isAnonymizeBIDS) {
+ if (strlen(d.seriesInstanceUID) > 0)
+ fprintf(fp, "\t\"SeriesInstanceUID\": \"%s\",\n", d.seriesInstanceUID );
+ if (strlen(d.studyInstanceUID) > 0)
+ fprintf(fp, "\t\"StudyInstanceUID\": \"%s\",\n", d.studyInstanceUID );
+ }
+ if (strlen(d.procedureStepDescription) > 0)
+ fprintf(fp, "\t\"ProcedureStepDescription\": \"%s\",\n", d.procedureStepDescription );
+ if (strlen(d.scanningSequence) > 0)
+ fprintf(fp, "\t\"ScanningSequence\": \"%s\",\n", d.scanningSequence );
+ if (strlen(d.sequenceVariant) > 0)
+ fprintf(fp, "\t\"SequenceVariant\": \"%s\",\n", d.sequenceVariant );
+ if (strlen(d.seriesDescription) > 0)
+ fprintf(fp, "\t\"SeriesDescription\": \"%s\",\n", d.seriesDescription );
+ if (strlen(d.bodyPartExamined) > 0)
+ fprintf(fp, "\t\"BodyPartExamined\": \"%s\",\n", d.bodyPartExamined );
+ if (strlen(d.protocolName) > 0)
+ fprintf(fp, "\t\"ProtocolName\": \"%s\",\n", d.protocolName );
+ if (strlen(d.sequenceName) > 0)
+ fprintf(fp, "\t\"SequenceName\": \"%s\",\n", d.sequenceName );
if (strlen(d.imageType) > 0) {
fprintf(fp, "\t\"ImageType\": [\"");
bool isSep = false;
@@ -370,26 +396,68 @@ void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts,
}
//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 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
+ if (d.radionuclidePositronFraction > 0.0) fprintf(fp, "\t\"RadionuclidePositronFraction\": %g,\n", d.radionuclidePositronFraction );
+ if (d.radionuclideTotalDose > 0.0) fprintf(fp, "\t\"RadionuclideTotalDose\": %g,\n", d.radionuclideTotalDose );
+ if (d.radionuclideHalfLife > 0.0) fprintf(fp, "\t\"RadionuclideHalfLife\": %g,\n", d.radionuclideHalfLife );
+ if (d.doseCalibrationFactor > 0.0) fprintf(fp, "\t\"DoseCalibrationFactor\": %g,\n", d.doseCalibrationFactor );
+ //MRI parameters
if (d.fieldStrength > 0.0) fprintf(fp, "\t\"MagneticFieldStrength\": %g,\n", d.fieldStrength );
if (d.flipAngle > 0.0) fprintf(fp, "\t\"FlipAngle\": %g,\n", d.flipAngle );
- if (d.TE > 0.0) fprintf(fp, "\t\"EchoTime\": %g,\n", d.TE / 1000.0 );
+ if ((d.TE > 0.0) && (!d.isXRay)) fprintf(fp, "\t\"EchoTime\": %g,\n", d.TE / 1000.0 );
+ if ((d.TE > 0.0) && (d.isXRay)) fprintf(fp, "\t\"XRayExposure\": %g,\n", d.TE );
if (d.TR > 0.0) fprintf(fp, "\t\"RepetitionTime\": %g,\n", d.TR / 1000.0 );
+ if (d.TI > 0.0) fprintf(fp, "\t\"InversionTime\": %g,\n", d.TI / 1000.0 );
+ if (d.ecat_isotope_halflife > 0.0) fprintf(fp, "\t\"IsotopeHalfLife\": %g,\n", d.ecat_isotope_halflife);
+ if (d.ecat_dosage > 0.0) fprintf(fp, "\t\"Dosage\": %g,\n", d.ecat_dosage);
if ((d.CSA.bandwidthPerPixelPhaseEncode > 0.0) && (h->dim[2] > 0) && (h->dim[1] > 0)) {
- float dwellTime = 0;
- if (d.phaseEncodingRC =='C')
+ float dwellTime = 0.0f;
+ if (h->dim[2] == h->dim[2]) //phase encoding does not matter
dwellTime = 1.0/d.CSA.bandwidthPerPixelPhaseEncode/h->dim[2];
- else
+ else if (d.phaseEncodingRC =='R')
+ dwellTime = 1.0/d.CSA.bandwidthPerPixelPhaseEncode/h->dim[2];
+ else if (d.phaseEncodingRC =='C')
dwellTime = 1.0/d.CSA.bandwidthPerPixelPhaseEncode/h->dim[1];
- fprintf(fp, "\t\"EffectiveEchoSpacing\": %g,\n", dwellTime );
-
+ if (dwellTime != 0.0f) //as long as phase encoding = R or C or does not matter
+ fprintf(fp, "\t\"EffectiveEchoSpacing\": %g,\n", dwellTime );
}
bool first = 1;
if (dti4D->S[0].sliceTiming >= 0.0) {
fprintf(fp, "\t\"SliceTiming\": [\n");
- for (int i = 0; i < kMaxDTI4D; i++) {
+ for (int i = 0; i < kMaxDTI4D; i++) {
if (dti4D->S[i].sliceTiming >= 0.0){
if (!first)
fprintf(fp, ",\n");
@@ -400,30 +468,68 @@ void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts,
}
fprintf(fp, "\t],\n");
}
- if (d.phaseEncodingRC == 'C')
- fprintf(fp, "\t\"PhaseEncodingDirection\": \"j");
- else
- fprintf(fp, "\t\"PhaseEncodingDirection\": \"i");
- //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"
- if ((d.CSA.phaseEncodingDirectionPositive == 1) && ((opts.isFlipY)))
- fprintf(fp, "-");
- if ((d.CSA.phaseEncodingDirectionPositive == 0) && ((!opts.isFlipY)))
- fprintf(fp, "-");
- fprintf(fp, "\"\n");
+ if (((d.phaseEncodingRC == 'R') || (d.phaseEncodingRC == 'C')) && ((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"
+ if (d.CSA.phaseEncodingDirectionPositive == -1)
+ fprintf(fp, "?"); //unknown
+ else if ((d.CSA.phaseEncodingDirectionPositive == 1) && ((opts.isFlipY)))
+ fprintf(fp, "-");
+ else if ((d.CSA.phaseEncodingDirectionPositive == 0) && ((!opts.isFlipY)))
+ fprintf(fp, "-");
+ fprintf(fp, "\",\n");
+ } //only save PhaseEncodingDirection if BOTH direction and POLARITY are known
+ 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()
+}// nii_SaveBIDS() step
+
+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)) ) );
+}
-int nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[], struct TDCMopts opts, int sliceDir, struct TDTI4D *dti4D) {
- //reports non-zero if last volumes should be excluded (e.g. philip stores an ADC maps)
+unsigned char * removeADC(struct nifti_1_header *hdr, unsigned char *inImg, bool * isADC) {
+ int numVolOut = 0;
+ int numVolIn = hdr->dim[4];
+ int numVolBytes = hdr->dim[1]*hdr->dim[2]*hdr->dim[3]*(hdr->bitpix/8);
+ if ((!isADC) || (numVolIn < 1) || (numVolBytes < 1)) return inImg;
+ for (int i = 0; i < numVolIn; i++)
+ if (!isADC[i])
+ numVolOut++;
+ if (numVolOut < 1) return inImg;
+ //printMessage("Removing ADC maps, %d volumes reduced to %d\n", numVolIn, numVolOut);
+ unsigned char *outImg = (unsigned char *)malloc(numVolBytes * numVolOut);
+ int outPos = 0;
+ for (int i = 0; i < numVolIn; i++) {
+ if (!isADC[i]) {
+ memcpy(&outImg[outPos], &inImg[i * numVolBytes], numVolBytes); // dest, src, bytes
+ outPos += numVolBytes;
+ }
+ } //for each volume
+ free(isADC);
+ free(inImg);
+ hdr->dim[4] = numVolOut;
+ return outImg;
+} //removeADC()
+
+
+bool * nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[], struct TDCMopts opts, int sliceDir, struct TDTI4D *dti4D) {
+ //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....
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) return NULL;
+ if ((numDti < 3) && (nConvert < 3)) return NULL;
TDTI * vx = NULL;
if (numDti > 2) {
vx = (TDTI *)malloc(numDti * sizeof(TDTI));
@@ -439,10 +545,9 @@ int nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struc
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;
+ 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
@@ -456,13 +561,13 @@ int nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struc
}
if (!bVecVaries) {
free(vx);
- return false;
+ return NULL;
}
for (int i = 1; i < numDti; i++)
- printf("bxyz %g %g %g %g\n",vx[i].V[0],vx[i].V[1],vx[i].V[2],vx[i].V[3]);
- printf("Error: only one B-value reported for all volumes: %g\n",vx[0].V[0]);
+ 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 false;
+ return NULL;
}
int firstB0 = -1;
for (int i = 0; i < numDti; i++) //check if all bvalues match first volume
@@ -470,38 +575,39 @@ int nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struc
firstB0 = i;
break;
}
- #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
- int numFinalADC = 0;
+ if (firstB0 < 0) printWarning("This diffusion series does not have a B0 (reference) volume\n");
+ if (firstB0 > 0) printMessage("Note: B0 not the first volume in the series (FSL eddy reference volume is %d)\n", firstB0);
+ int numADC = 0;
+ bool * isADC = NULL;
if (dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_PHILIPS) {
- int i = numDti - 1;
- while ((i > 0) && (!isSameFloat(vx[i].V[0],0.0f)) && //not a B-0 image
- ((isSameFloat(vx[i].V[1],0.0f)) &&
- (isSameFloat(vx[i].V[2],0.0f)) &&
- (isSameFloat(vx[i].V[3],0.0f)) ) ){//yet all vectors are zero!!!! must be ADC
- numFinalADC++; //final volume is ADC map
- numDti --; //remove final volume - it is a computed ADC map!
- dcmList[indx0].CSA.numDti = numDti;
- i --;
+ isADC = (bool *)malloc(numDti * sizeof(bool));
+ int o = 0; //output index
+ for (int i = 0; i < numDti; i++) {//check if all bvalues match first volume
+ if (isADCnotDTI(vx[i])) {
+ isADC[i] = true;
+ numADC++;
+ printMessage("Volume %d is not a normal DTI image (ADC?)\n", i+1);
+ } else { //save output
+ vx[o] = vx[i];
+ isADC[i] = false;
+ o++;
+ }
} //
- if (numFinalADC > 0)
- printf("Note: final %d volumes appear to be ADC images that will be removed to allow processing\n", numFinalADC);
- /*for (int i = 0; i < (numDti); i++) {
- if ((!isSameFloat(vx[i].V[0],0.0f)) && //not a B-0 image
- ((isSameFloat(vx[i].V[1],0.0f)) &&
- (isSameFloat(vx[i].V[2],0.0f)) &&
- (isSameFloat(vx[i].V[3],0.0f)) ) )
- printf("Warning: volume %d appears to be an ADC volume %g %g %g\n", i+1, vx[i].V[1], vx[i].V[2], vx[i].V[3]);
-
- }*/
+ numDti = numDti - numADC;
+ dcmList[indx0].CSA.numDti = numDti; //warning structure not changed outside scope!
+ if (numADC > 0) {
+ printMessage("Note: %d volumes appear to be ADC images that will be removed to allow processing\n", numADC);
+ if (numDti == 0) {
+ printWarning("No bvec/bval files created: no volumes with bvecs applied \n");
+ free(isADC);
+ free(vx);
+ return NULL;
+ }
+ }
+ if (numADC == 0) { //no ADC images
+ free(isADC);
+ isADC = NULL;
+ }
}
// philipsCorrectBvecs(&dcmList[indx0]); //<- replaced by unified siemensPhilips solution
geCorrectBvecs(&dcmList[indx0],sliceDir, vx);
@@ -514,20 +620,34 @@ int nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struc
} //if not a mosaic
if (opts.isVerbose) {
for (int i = 0; i < (numDti); i++) {
- printf("%d\tB=\t%g\tVec=\t%g\t%g\t%g\n",i, vx[i].V[0],
+ 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
}
- //printf("%f\t%f\t%f",dcmList[indx0].CSA.dtiV[1][1],dcmList[indx0].CSA.dtiV[1][2],dcmList[indx0].CSA.dtiV[1][3]);
+ //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<double> bValues(numDti);
+ std::vector<double> 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");
- //printf("Saving DTI %s\n",txtname);
+ //printMessage("Saving DTI %s\n",txtname);
FILE *fp = fopen(txtname, "w");
if (fp == NULL) {
free(vx);
- return numFinalADC;
+ return isADC;
}
for (int i = 0; i < (numDti-1); i++) {
if (opts.isCreateBIDS) {
@@ -540,11 +660,11 @@ int nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struc
fclose(fp);
strcpy(txtname,pathoutname);
strcat (txtname,".bvec");
- //printf("Saving DTI %s\n",txtname);
+ //printMessage("Saving DTI %s\n",txtname);
fp = fopen(txtname, "w");
if (fp == NULL) {
free(vx);
- return numFinalADC;
+ return isADC;
}
for (int v = 1; v < 4; v++) {
for (int i = 0; i < (numDti-1); i++) {
@@ -557,8 +677,9 @@ int nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struc
fprintf(fp, "%g\n", vx[numDti-1].V[v]);
}
fclose(fp);
+#endif
free(vx);
- return numFinalADC;
+ return isADC;
}// nii_SaveDTI()
float sqr(float v){
@@ -580,23 +701,23 @@ float intersliceDistance(struct TDICOMdata d1, struct TDICOMdata d2) {
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
+//#ifdef _MSC_VER
TDCMsort * dcmSortIn = (TDCMsort *)malloc(nConvert * sizeof(TDCMsort));
-#else
- struct TDCMsort dcmSortIn[nConvert];
-#endif
+//#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;
- //printf("%d -> %d %d ->%d\n",i,a, b, k);
+ //printMessage("%d -> %d %d ->%d\n",i,a, b, k);
dcmSort[k] = dcmSortIn[i];
i++;
}
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
free(dcmSortIn);
-#endif
+//#endif
} //swapDim3Dim4()
bool intensityScaleVaries(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[]){
@@ -638,7 +759,7 @@ bool intensityScaleVaries(int nConvert, struct TDCMsort dcmSort[],struct TDICOMd
sliceOffsetR += sliceBytes8;
} //for each slice
return bImg;
- } //nii_ImgBytes()*/
+ } */
bool niiExists(const char*pathoutname) {
char niiname[2048] = {""};
@@ -665,18 +786,10 @@ int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopt
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
+ printError("You do not have write permissions for the directory %s\n",opts.outdir);
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
+ printWarning("%s write permission denied. Saving to working directory %s \n", opts.outdir, pth);
}
}
}
@@ -706,19 +819,25 @@ int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopt
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 == 'C')
- strcat (outname,dcm.imageComments);
- if (f == 'D')
- strcat (outname,dcm.seriesDescription);
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");
@@ -737,16 +856,6 @@ int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopt
strcat (outname,dcm.protocolName);
if (f == 'Q')
strcat (outname,dcm.scanningSequence);
- if ((f >= '0') && (f <= '9')) {
- if ((pos<strlen(inname)) && (toupper(inname[pos+1]) == 'S')) {
- char zeroPad[12] = {""};
- //sprintf(zeroPad,"%%0%dd",atoi(&f));
- sprintf(zeroPad,"%%0%dd",f - '0');
- 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);
@@ -760,11 +869,21 @@ int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopt
sprintf(newstr, "%d", dcm.acquNum);
strcat (outname,newstr);
#else
- printf("Warning: ignoring '%%f' in output filename (recompile to segment by acquisition)\n");
+ printWarning("Ignoring '%%f' in output filename (recompile to segment by acquisition)\n");
#endif
}
if (f == 'Z')
strcat (outname,dcm.sequenceName);
+ if ((f >= '0') && (f <= '9')) {
+ if ((pos<strlen(inname)) && (toupper(inname[pos+1]) == 'S')) {
+ char zeroPad[12] = {""};
+ //sprintf(zeroPad,"%%0%dd",atoi(&f));
+ sprintf(zeroPad,"%%0%dd",f - '0');
+ sprintf(newstr, zeroPad, dcm.seriesNum);
+ strcat (outname,newstr);
+ pos++; // e.g. %3f requires extra increment: skip both number and following character
+ }
+ }
start = pos + 1;
} //found a % character
pos++;
@@ -793,7 +912,7 @@ int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopt
|| (outname[pos] == '^')
|| (outname[pos] == '*') || (outname[pos] == '|') || (outname[pos] == '?'))
outname[pos] = '_';
- //printf("outname=*%s* %d %d\n", outname, pos,start);
+ //printMessage("outname=*%s* %d %d\n", outname, pos,start);
char baseoutname[2048] = {""};
strcat (baseoutname,pth);
char appendChar[2] = {"a"};
@@ -811,25 +930,37 @@ int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopt
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
+ printError("Too many NIFTI images with the name %s\n", baseoutname);
return EXIT_FAILURE;
}
- //printf("-->%s\n",pathoutname); return EXIT_SUCCESS;
- //printf("outname=%s\n", pathoutname);
+ //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 dcm = clear_dicom_data();
+ 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(dcm, niiFilenameBase, opts) ;
+ nii_createFilename(d, niiFilenameBase, opts) ;
strcpy(niiFilename,"Example output filename: '");
strcat(niiFilename,niiFilenameBase);
if (opts.isGz)
@@ -850,7 +981,11 @@ unsigned long mz_crc32(unsigned long crc, const unsigned char *ptr, size_t buf_l
}
#endif
-void writeNiiGz (char * baseName, struct nifti_1_header hdr, unsigned char* src_buffer, unsigned long src_len) {
+#ifndef MZ_UBER_COMPRESSION //defined in miniz, not defined in zlib
+ #define MZ_UBER_COMPRESSION 9
+#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] = {""};
@@ -867,8 +1002,12 @@ void writeNiiGz (char * baseName, struct nifti_1_header hdr, unsigned char* src
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) {
+ int zLevel = 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;
}
@@ -928,13 +1067,91 @@ void writeNiiGz (char * baseName, struct nifti_1_header hdr, unsigned char* src
} //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) {
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 myDisableZLib
if ((opts.isGz) && (strlen(opts.pigzname) < 1) && ((imgsz+hdr.vox_offset) < 2147483647) ) { //use internal compressor
- //if (true) {//TPX
- writeNiiGz (niiFilename, hdr, im,imgsz);
+ writeNiiGz (niiFilename, hdr, im, imgsz, opts.gzLevel);
return EXIT_SUCCESS;
}
#endif
@@ -952,7 +1169,12 @@ int nii_saveNII(char * niiFilename, struct nifti_1_header hdr, unsigned char* im
char command[768];
strcpy(command, "\"" );
strcat(command, opts.pigzname );
- 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 ':\'
+ 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)
@@ -962,24 +1184,22 @@ int nii_saveNII(char * niiFilename, struct nifti_1_header hdr, unsigned char* im
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)) {
- //printf("compression --- %s\n",command);
+ //printMessage("compression --- %s\n",command);
WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
} else
- printf("compression failed %s\n",command);
+ printMessage("compression failed %s\n",command);
#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
+ 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;
@@ -1018,9 +1238,10 @@ void nii_check16bitUnsigned(unsigned char *img, struct nifti_1_header *hdr){
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)
- printf("Note: rare 16-bit UNSIGNED integer image. Older tools may require 32-bit conversion\n");
+ //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()
@@ -1034,11 +1255,7 @@ int siemensCtKludge(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dc
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
+ printMessage("Slices skipped: image position not sequential, admonish your vendor (Siemens OOG?)\n");
return i;
}
prevDx = dx;
@@ -1057,10 +1274,10 @@ unsigned char * nii_saveNII3Dtilt(char * niiFilename, struct nifti_1_header * hd
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) {
- printf("Only able to correct gantry tilt for 16-bit integer data with at least 3 slices.");
+ printMessage("Only able to correct gantry tilt for 16-bit integer data with at least 3 slices.");
return im;
}
- printf("Gantry Tilt Correction is new: please validate conversions\n");
+ 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
@@ -1073,7 +1290,7 @@ unsigned char * nii_saveNII3Dtilt(char * niiFilename, struct nifti_1_header * hd
; //do nothing
else
if (gantryTiltDeg < 0.0) GNTtanPx = - GNTtanPx; //see Toshiba examples from John Muschelli
- // printf("gantry tilt pixels per mm %g\n",GNTtanPx);
+ // 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
@@ -1081,7 +1298,7 @@ unsigned char * nii_saveNII3Dtilt(char * niiFilename, struct nifti_1_header * hd
float maxSliceMM = fabs(s * hdrIn.pixdim[3]);
if (sliceMMarray != NULL) maxSliceMM = fabs(sliceMMarray[s]);
int pxOffset = ceil(fabs(GNTtanPx*maxSliceMM));
- // printf("Tilt extends slice by %d pixels", pxOffset);
+ // 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) );
@@ -1133,7 +1350,7 @@ int nii_saveNII3Deq(char * niiFilename, struct nifti_1_header hdr, unsigned char
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)) {
- printf("Only able to make equidistant slices from 3D 8,16,24-bit volumes with at least 3 slices.");
+ 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];
@@ -1144,7 +1361,7 @@ int nii_saveNII3Deq(char * niiFilename, struct nifti_1_header hdr, unsigned char
mn = sliceMMarray[i] - sliceMMarray[i-1];
}
if (mn <= 0.0f) {
- printf("Unable to equalize slice distances: slice number not consistent with slice position.\n");
+ printMessage("Unable to equalize slice distances: slice number not consistent with slice position.\n");
return EXIT_FAILURE;
}
int slices = hdr.dim[3];
@@ -1153,7 +1370,7 @@ int nii_saveNII3Deq(char * niiFilename, struct nifti_1_header hdr, unsigned char
slices = 2 * hdr.dim[3];
mn = (sliceMMarray[hdr.dim[3]-1]) / (slices-1);
}
- //printf("-->%g mn slices %d orig %d\n", mn, slices, hdr.dim[3]);
+ //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;
@@ -1228,6 +1445,45 @@ int nii_saveNII3Deq(char * niiFilename, struct nifti_1_header hdr, unsigned char
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);
@@ -1238,13 +1494,13 @@ void smooth1D(int num, double * im) {
free(src);
}// smooth1D()
-void nii_saveCrop(char * niiFilename, struct nifti_1_header hdr, unsigned char* im, struct TDCMopts opts) {
+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()
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;
+ 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)) {
- printf("Only able to crop 16-bit volumes.");
- return;
+ printMessage("Only able to crop 16-bit volumes.");
+ return EXIT_FAILURE;
}
short * im16 = ( short*) im;
unsigned short * imu16 = (unsigned short*) im;
@@ -1275,18 +1531,17 @@ void nii_saveCrop(char * niiFilename, struct nifti_1_header hdr, unsigned char*
}
if (maxSliceVal <= 0) {
free(sliceSums);
- return;
+ return EXIT_FAILURE;
}
smooth1D(slices, sliceSums);
- for (int i = 0; i < slices; i++)
- sliceSums[i] = sliceSums[i] / maxSliceVal; //so brightest slice has value 1
+ 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;
+ return EXIT_FAILURE;
}
/*
//find brightest band within 90mm of top of head
@@ -1300,7 +1555,7 @@ void nii_saveCrop(char * niiFilename, struct nifti_1_header hdr, unsigned char*
ventralMaxSlice = maxSlice - round(45 /fabs(hdr.pixdim[3])); //gap at least 60mm
if (ventralMaxSlice < 0) {
free(sliceSums);
- return;
+ return EXIT_FAILURE;
}
int ventralMinSlice = maxSlice - round(90/fabs(hdr.pixdim[3])); //gap no more than 120mm
if (ventralMinSlice < 0) ventralMinSlice = 0;
@@ -1312,12 +1567,12 @@ void nii_saveCrop(char * niiFilename, struct nifti_1_header hdr, unsigned char*
for (int i = ventralMaxSlice; i < maxSlice; i++)
if (sliceSums[i] < sliceSums[minSlice])
minSlice = i;
- //printf("%d %d %d\n", ventralMaxSlice, minSlice, maxSlice);
+ //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;
+ if (ventralCrop > dorsalCrop) return EXIT_FAILURE;
//FindDVCrop2
const double kMaxDVmm = 180.0;
double sliceMM = hdr.pixdim[3] * (dorsalCrop-ventralCrop);
@@ -1330,7 +1585,7 @@ void nii_saveCrop(char * niiFilename, struct nifti_1_header hdr, unsigned char*
ventralCrop = dorsalCrop - round( kMaxDVmm / hdr.pixdim[3]);
if (ventralCrop < 0) ventralCrop = 0;
//apply crop
- printf(" Cropping from slice %d to %d (of %d)\n", ventralCrop, dorsalCrop, slices);
+ 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;
@@ -1352,9 +1607,9 @@ void nii_saveCrop(char * niiFilename, struct nifti_1_header hdr, unsigned char*
char niiFilenameCrop[2048] = {""};
strcat(niiFilenameCrop,niiFilename);
strcat(niiFilenameCrop,"_Crop");
- nii_saveNII3D(niiFilenameCrop, hdrX, imX, opts);
+ const int returnCode = nii_saveNII3D(niiFilenameCrop, hdrX, imX, opts);
free(imX);
- return;
+ return returnCode;
}// nii_saveCrop()
int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[], struct TSearchList *nameList, struct TDCMopts opts, struct TDTI4D *dti4D) {
@@ -1362,25 +1617,34 @@ int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmLis
float *sliceMMarray = NULL; //only used if slices are not equidistant
uint64_t indx = dcmSort[0].indx;
uint64_t indx0 = dcmSort[0].indx;
+ if (opts.isIgnoreDerivedAnd2D && isDerived(dcmList[indx])) {
+ 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, "_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 (opts.isVerbose)
- #ifdef myUseCOut
- std::cout<<"Converting "<<nameList->str[indx]<<std::endl;
- #else
- printf("Converting %s\n",nameList->str[indx]);
- #endif
+ 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);
- //printf(" %d %d %d %d %lu\n", hdr0.dim[1], hdr0.dim[2], hdr0.dim[3], hdr0.dim[4], (unsigned long)[imgM length]);
+ //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)
- printf(" Warning: note these images have gantry tilt of %g degrees (manufacturer ID = %d)\n", dcmList[indx0].gantryTilt, dcmList[indx0].manufacturer);
+ 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;
@@ -1399,7 +1663,7 @@ int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmLis
}
/*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);
+ 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) ) {
@@ -1409,7 +1673,7 @@ int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmLis
} else {
hdr0.dim[3] = nConvert;
if (nAcq > 1) {
- printf("Slice positions repeated, but number of slices (%d) not divisible by number of repeats (%d): missing images?\n", nConvert, nAcq);
+ 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]);
@@ -1421,25 +1685,25 @@ int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmLis
if (dxVaries) {
sliceMMarray = (float *) malloc(sizeof(float)*nConvert);
sliceMMarray[0] = 0.0f;
- printf("Dims %d %d %d %d %d\n", hdr0.dim[1], hdr0.dim[2], hdr0.dim[3], hdr0.dim[4], nAcq);
- printf("Warning: interslice distance varies in this volume (incompatible with NIfTI format).\n");
- printf(" Distance from first slice:\n");
- printf("dx=[0");
+ 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]);
- printf(" %g", dx0);
+ printMessage(" %g", dx0);
sliceMMarray[i] = dx0;
}
- printf("]\n");
+ 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]);
- printf("swizzling 3rd and 4th dimensions (XYTZ -> XYZT), assuming interslice distance is %f\n",dx);
+ 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
- printf(" Warning: all images appear to be a single slice - please check slice/vector orientation\n");
+ 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;
@@ -1454,7 +1718,20 @@ int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmLis
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]);
+ /*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;
@@ -1462,11 +1739,7 @@ int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmLis
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)) {
- #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
+ printError("Image dimensions differ %s %s",nameList->str[dcmSort[0].indx], nameList->str[indx]);
free(imgM);
free(img);
return EXIT_FAILURE;
@@ -1485,14 +1758,12 @@ int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmLis
return EXIT_FAILURE;
}
int sliceDir = 0;
- if (hdr0.dim[3] > 1)
- sliceDir = headerDcm2Nii2(dcmList[dcmSort[0].indx],dcmList[dcmSort[nConvert-1].indx] , &hdr0);
+ if (hdr0.dim[3] > 1)sliceDir = headerDcm2Nii2(dcmList[dcmSort[0].indx],dcmList[dcmSort[nConvert-1].indx] , &hdr0);
//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) {
- printf("WARNING: WEIRD CSA 'ProtocolSliceNumber': SPATIAL, SLICE-ORDER AND DTI TRANSFORMS UNTESTED\n");
+ printWarning("Weird CSA 'ProtocolSliceNumber' (%d): SPATIAL, SLICE-ORDER AND DTI TRANSFORMS UNTESTED\n", dcmList[dcmSort[0].indx].CSA.protocolSliceNumber1);
//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);
@@ -1500,67 +1771,76 @@ int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmLis
}
nii_SaveBIDS(pathoutname, dcmList[dcmSort[0].indx], opts, dti4D, &hdr0);
nii_SaveText(pathoutname, dcmList[dcmSort[0].indx], opts, &hdr0, nameList->str[indx]);
- int numFinalADC = nii_SaveDTI(pathoutname,nConvert, dcmSort, dcmList, opts, sliceDir, dti4D);
- numFinalADC = numFinalADC; //simply to silence compiler warning when myNoSave defined
+ bool * isADC = nii_SaveDTI(pathoutname,nConvert, dcmSort, dcmList, opts, sliceDir, dti4D);
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
+ 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)
- #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
+ 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); //printf("ortho %d\n", echoInt (33));
+ 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
- #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
+ printMessage("DICOM row order preserved: may appear upside down in tools that ignore spatial transforms\n");
#ifndef myNoSave
- //printf(" x--> %d ----\n", nConvert);
+ // 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);
if ((hdr0.dim[4] > 1) && (saveAs3D))
- nii_saveNII3D(pathoutname, hdr0, imgM,opts);
+ returnCode = nii_saveNII3D(pathoutname, hdr0, imgM,opts);
else {
- if ((numFinalADC > 0) && (hdr0.dim[4] > (numFinalADC+1))) { //ADC maps can disrupt analysis: save a copy with the ADC map, and another without
+ if (isADC) { //ADC maps can disrupt analysis: save a copy with the ADC map, and another without
+#ifndef HAVE_R
char pathoutnameADC[2048] = {""};
strcat(pathoutnameADC,pathoutname);
strcat(pathoutnameADC,"_ADC");
nii_saveNII(pathoutnameADC, hdr0, imgM, opts);
- hdr0.dim[4] = hdr0.dim[4]-numFinalADC;
+#endif
+ imgM = removeADC(&hdr0, imgM, isADC);
+ //hdr0.dim[4] = hdr0.dim[4]-numFinalADC;
};
- nii_saveNII(pathoutname, hdr0, imgM, opts);
+#ifndef HAVE_R
+ returnCode = nii_saveNII(pathoutname, hdr0, imgM, opts);
+#endif
}
#endif
if (dcmList[indx0].gantryTilt != 0.0) {
- if (dcmList[indx0].isResampled)
- printf("Tilt correction skipped: 0008,2111 reports RESAMPLED\n");
- else if (opts.isTiltCorrect)
+ 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);
- else
- printf("Tilt correction skipped\n");
+ strcat(pathoutname,"_Tilt");
+ } else
+ printMessage("Tilt correction skipped\n");
}
if (sliceMMarray != NULL) {
- if (dcmList[indx0].isResampled)
- printf("Slice thickness correction skipped: 0008,2111 reports RESAMPLED\n");
+ if (dcmList[indx0].isResampled) {
+ printMessage("Slice thickness correction skipped: 0008,2111 reports RESAMPLED\n");
+ }
else
- nii_saveNII3Deq(pathoutname, hdr0, imgM,opts, sliceMMarray);
+ 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)
- nii_saveCrop(pathoutname, hdr0, imgM,opts); //n.b. must be run AFTER nii_setOrtho()!
+ 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 EXIT_SUCCESS;
}// saveDcm2Nii()
@@ -1578,11 +1858,13 @@ int compareTDCMsort(void const *item1, void const *item2) {
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);
}
@@ -1612,45 +1894,47 @@ bool isSameSet (struct TDICOMdata d1, struct TDICOMdata d2, bool isForceStackSam
#else
if (d1.acquNum != d2.acquNum) {
if (!warnings->acqNumVaries)
- printf("slices stacked despite varying acquisition numbers (if this is not desired please recompile)\n");
+ 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)
- printf("slices not stacked: dimensions or bit-depth varies\n");
+ printMessage("slices not stacked: dimensions or bit-depth varies\n");
warnings->bitDepthVaries = true;
return false;
}
if (isForceStackSameSeries) return true; //we will stack these images, even if they differ in the following attributes
if (!isSameFloatDouble(d1.dateTime, d2.dateTime)) { //beware, some vendors incorrectly store Image Time (0008,0033) as Study Time (0008,0030).
if (!warnings->dateTimeVaries)
- printf("slices not stacked: Study Data/Time (0008,0020 / 0008,0030) varies %12.12f ~= %12.12f\n", d1.dateTime, d2.dateTime);
+ printMessage("slices not stacked: Study Data/Time (0008,0020 / 0008,0030) varies %12.12f ~= %12.12f\n", d1.dateTime, d2.dateTime);
warnings->dateTimeVaries = true;
return false;
}
if ((d1.TE != d2.TE) || (d1.echoNum != d2.echoNum)) {
- if (!warnings->echoVaries)
- printf("slices not stacked: echo varies (TE %g, %g; echo %d, %d)\n", 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 (%g, %g; number %d, %d)\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)\n", d1.TE, d2.TE,d1.echoNum, d2.echoNum );
warnings->echoVaries = true;
return false;
}
if (d1.coilNum != d2.coilNum) {
if (!warnings->coilVaries)
- printf("slices not stacked: coil varies\n");
+ printMessage("slices not stacked: coil varies\n");
warnings->coilVaries = true;
return false;
}
if ((strcmp(d1.protocolName, d2.protocolName) != 0)) {
if ((!warnings->nameVaries))
- printf("slices not stacked: protocol name varies\n");
+ printMessage("slices not stacked: protocol name varies\n");
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)
- printf("slices not stacked: orientation varies (localizer?) [%g %g %g %g %g %g] != [%g %g %g %g %g %g]\n",
+ 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;
@@ -1659,49 +1943,11 @@ bool isSameSet (struct TDICOMdata d1, struct TDICOMdata d2, bool isForceStackSam
return true;
}// isSameSet()
-/*
-#if defined(__APPLE__) && defined(__MACH__)
-void convertForeign2Nii(char * fname, struct TDCMopts* opts) {//, struct TDCMopts opts) {
-
- struct nifti_1_header niiHdr;
- unsigned char * img = nii_readForeignC(fname, &niiHdr, 0, 65535);
- if (img == NULL) return;
- 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) {
- printf("Error: you do not have write permissions for the directory %s\n",opts->outdir);
- return;
- }
- printf("Warning: %s write permission denied. Saving to working directory %s \n", opts->outdir, pth);
-
- }
- }
- char appendChar[2] = {"a"};
- appendChar[0] = kPathSeparator;
- if (pth[strlen(pth)-1] != kPathSeparator)
- strcat (pth,appendChar);
- char fn[1024] = {""};
- getFileName(fn, fname);
- strcat(pth,fn);
- } else {
- strcat(pth, fname);
- }
- printf("Converted foreign image '%s'\n",fname);
- nii_saveNII(pth, niiHdr, img, *opts);
- free(img);
-} //convertForeign2Nii()
-#endif */
-
int singleDICOM(struct TDCMopts* opts, char *fname) {
char filename[768] ="";
strcat(filename, fname);
if (isDICOMfile(filename) == 0) {
- printf("Error: not a DICOM image : %s\n", filename);
+ printError("Not a DICOM image : %s\n", filename);
return 0;
}
struct TDICOMdata *dcmList = (struct TDICOMdata *)malloc( sizeof(struct TDICOMdata));
@@ -1721,41 +1967,58 @@ int singleDICOM(struct TDCMopts* opts, char *fname) {
return saveDcm2Nii(1, dcmSort, dcmList, &nameList, *opts, &dti4D);
}// singleDICOM()
-void searchDirForDICOM(char *path, struct TSearchList *nameList, int maxDepth, int depth) {
+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);
- //printf("%s\n", file.name);
+ //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);
- else if (!file.is_reg) //ignore hidden files...
+ 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);
- //printf("OK\n");
+ //printMessage("OK\n");
}
nameList->numItems++;
- //printf("dcm %lu %s \n",nameList->numItems, filename);
+ //printMessage("dcm %lu %s \n",nameList->numItems, filename);
} else {
- #if defined(__APPLE__) && defined(__MACH__)
- //convertForeign2Nii(filename, opts);
- #endif
- #ifdef MY_DEBUG
- #ifdef myUseCOut
- std::cout<<"Not a dicom"<< filename <<std::endl;
- #else
- printf("Not a dicom:\t%s\n", filename);
- #endif
- #endif
+ if (fileBytes(filename) > 2048)
+ convert_foreign (filename, *opts);
+ #ifdef MY_DEBUG
+ printMessage("Not a dicom:\t%s\n", filename);
+ #endif
}
tinydir_next(&dir);
}
@@ -1775,11 +2038,7 @@ int removeDuplicates(int nConvert, struct TDCMsort dcmSort[]){
}
}
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
+ printMessage("Some images have identical time, series, acquisition and image values. DUPLICATES REMOVED.\n");
return nConvert - nDuplicates;
}// removeDuplicates()
@@ -1789,35 +2048,18 @@ int removeDuplicatesVerbose(int nConvert, struct TDCMsort dcmSort[], struct TSea
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=\t"<<nameList->str[dcmSort[i].indx] <<std::endl;
- #else
- printf("\t%s\t=\t%s\n",nameList->str[dcmSort[i-1].indx],nameList->str[dcmSort[i].indx]);
- #endif
+ printMessage("\t%s\t=\t%s\n",nameList->str[dcmSort[i-1].indx],nameList->str[dcmSort[i].indx]);
nDuplicates ++;
- }else {
+ } 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;
+ printMessage("Some images have identical time, series, acquisition and image values. Duplicates removed.\n");
+ 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 )
@@ -1826,115 +2068,6 @@ bool isExt (char *file_name, const char* ext) {
return false;
}// isExt()
-/*int nii_readpic(char * fname, struct nifti_1_header *nhdr) {
- //https://github.com/jefferis/pic2nifti/blob/master/libpic2nifti.c
-#define BIORAD_HEADER_SIZE 76
-#define BIORAD_NOTE_HEADER_SIZE 16
-#define BIORAD_NOTE_SIZE 80
- typedef struct
- {
- unsigned short nx, ny; // 0 2*2 image width and height in pixels
- short npic; // 4 2 number of images in file
- short ramp1_min; // 6 2*2 LUT1 ramp min. and max.
- short ramp1_max;
- int32_t notes; // 10 4 no notes=0; has notes=non zero
- short byte_format; // 14 2 bytes=TRUE(1); words=FALSE(0)
- unsigned short n; // 16 2 image number within file
- char name[32]; // 18 32 file name
- short merged; // 50 2 merged format
- unsigned short color1; // 52 2 LUT1 color status
- unsigned short file_id; // 54 2 valid .PIC file=12345
- short ramp2_min; // 56 2*2 LUT2 ramp min. and max.
- short ramp2_max;
- unsigned short color2; // 60 2 LUT2 color status
- short edited; // 62 2 image has been edited=TRUE(1)
- short lens; // 64 2 Integer part of lens magnification
- float mag_factor; // 66 4 4 byte real mag. factor (old ver.)
- unsigned short dummy[3]; // 70 6 NOT USED (old ver.=real lens mag.)
- } biorad_header;
- typedef struct
- {
- short blank; // 0 2
- int note_flag; // 2 4
- int blank2; // 6 4
- short note_type; // 10 2
- int blank3; // 12 4
- } biorad_note_header;
- size_t n;
- unsigned char buffer[BIORAD_HEADER_SIZE];
- FILE *f = fopen(fname, "rb");
- if (f)
- n = fread(&buffer, BIORAD_HEADER_SIZE, 1, f);
- if(!f || n!=1) {
- printf("Problem reading biorad file!\n");
- fclose(f);
- return EXIT_FAILURE;
- }
- biorad_header bhdr;
- memcpy( &bhdr.nx, buffer+0, sizeof( bhdr.nx ) );
- memcpy( &bhdr.ny, buffer+2, sizeof( bhdr.ny ) );
- memcpy( &bhdr.npic, buffer+4, sizeof( bhdr.npic ) );
- memcpy( &bhdr.byte_format, buffer+14, sizeof( bhdr.byte_format ) );
- memcpy( &bhdr.file_id, buffer+54, sizeof( bhdr.file_id ) );
- if (bhdr.file_id != 12345) {
- fclose(f);
- return EXIT_FAILURE;
- }
- nhdr->dim[0]=3;//3D
- nhdr->dim[1]=bhdr.nx;
- nhdr->dim[2]=bhdr.ny;
- nhdr->dim[3]=bhdr.npic;
- nhdr->dim[4]=0;
- nhdr->pixdim[1]=1.0;
- nhdr->pixdim[2]=1.0;
- nhdr->pixdim[3]=1.0;
- if (bhdr.byte_format == 1)
- nhdr->datatype = DT_UINT8; // 2
- else
- nhdr->datatype = DT_UINT16;
- nhdr->vox_offset = BIORAD_HEADER_SIZE;
- if(fseek(f, bhdr.nx*bhdr.ny*bhdr.npic*bhdr.byte_format, SEEK_CUR)==0) {
- biorad_note_header nh;
- char noteheaderbuf[BIORAD_NOTE_HEADER_SIZE];
- char note[BIORAD_NOTE_SIZE];
- while (!feof(f)) {
- fread(¬eheaderbuf, BIORAD_NOTE_HEADER_SIZE, 1, f);
- fread(¬e, BIORAD_NOTE_SIZE, 1, f);
- memcpy(&nh.note_flag, noteheaderbuf+2, sizeof(nh.note_flag));
- memcpy(&nh.note_type, noteheaderbuf+10, sizeof(nh.note_type));
- // printf("regular note line %s\n",note);
- // printf("note flag = %d, note type = %d\n",nh.note_flag,nh.note_type);
- // These are not interesting notes
- if(nh.note_type==1) continue;
-
- // Look for calibration information
- double d1, d2, d3;
- if ( 3 == sscanf( note, "AXIS_2 %lf %lf %lf", &d1, &d2, &d3 ) )
- nhdr->pixdim[1] = d3;
- if ( 3 == sscanf( note, "AXIS_3 %lf %lf %lf", &d1, &d2, &d3 ) )
- nhdr->pixdim[2] = d3;
- if ( 3 == sscanf( note, "AXIS_4 %lf %lf %lf", &d1, &d2, &d3 ) )
- nhdr->pixdim[3] = d3;
- if(nh.note_flag==0) break;
- }
- }
- nhdr->sform_code = 1;
- nhdr->srow_x[0]=nhdr->pixdim[1];nhdr->srow_x[1]=0.0f;nhdr->srow_x[2]=0.0f;nhdr->srow_x[3]=0.0f;
- nhdr->srow_y[0]=0.0f;nhdr->srow_y[1]=nhdr->pixdim[2];nhdr->srow_y[2]=0.0f;nhdr->srow_y[3]=0.0f;
- nhdr->srow_z[0]=0.0f;nhdr->srow_z[1]=0.0f;nhdr->srow_z[2]=nhdr->pixdim[3];nhdr->srow_z[3]=0.0f;
- fclose(f);
- convertForeignToNifti(nhdr);
- return EXIT_SUCCESS;
-}
-
-
-int convert_foreign(struct TDCMopts opts) {
- nifti_1_header nhdr ;
- int OK = EXIT_FAILURE;
- OK = nii_readpic(opts.indir, &nhdr);
- return OK;
-}*/
-
int convert_parRec(struct TDCMopts opts) {
//sample dataset from Ed Gronenschild <ed.gronenschild at maastrichtuniversity.nl>
struct TSearchList nameList;
@@ -1950,13 +2083,8 @@ int convert_parRec(struct TDCMopts opts) {
dcmSort[0].indx = 0;
saveDcm2Nii(1, dcmSort, dcmList, &nameList, opts, &dti4D);
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 < 1)
+ printMessage("No valid PAR/REC files were found\n");
if (nameList.numItems > 0)
for (int i = 0; i < nameList.numItems; i++)
free(nameList.str[i]);
@@ -1978,30 +2106,26 @@ void freeNameList(struct TSearchList nameList) {
int nii_loadDir(struct TDCMopts* opts) {
//Identifies all the DICOM files in a folder and its subfolders
if (strlen(opts->indir) < 1) {
- #ifdef myUseCOut
- std::cout<<"No input"<<std::endl;
- #else
- printf("No input\n");
- #endif
+ 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)
+ if (strlen(opts->outdir) < 1) {
strcpy(opts->outdir,opts->indir);
- dropTrailingFileSep(opts->outdir);
- if (is_fileNotDir(opts->outdir)) //if user passes ~/dicom/mr1.dcm we will look at all files in ~/dicom
- dropFilenameFromPath(opts->outdir);//getParentFolder(opts.indir, opts.indir);
+ } else
+ dropTrailingFileSep(opts->outdir);
if (!is_dir(opts->outdir,true)) {
#ifdef myUseInDirIfOutDirUnavailable
- printf("Warning: output folder invalid %s will try %s\n",opts->outdir,opts->indir);
+ printWarning("Output folder invalid %s will try %s\n",opts->outdir,opts->indir);
strcpy(opts->outdir,opts->indir);
#else
- printf("Error: output folder invalid: %s\n",opts->outdir);
+ printError("Output folder invalid: %s\n",opts->outdir);
return EXIT_FAILURE;
#endif
}
@@ -2012,56 +2136,46 @@ int nii_loadDir(struct TDCMopts* opts) {
#endif
#endif
}*/
- /*if (isFile && ((isExt(indir, ".mha")) || (isExt(indir, ".mhd"))) ) {
- strcpy(opts->indir, indir); //set to original file name, not path
- return convert_foreign(*opts);
- }*/
getFileName(opts->indirParent, opts->indir);
- if (isFile && ((isExt(indir, ".par")) || (isExt(indir, ".rec"))) ) {
+ 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 <unistd.h>
+ 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);
- } else if (isExt(indir, ".par")) //Linux is case sensitive...
- return convert_parRec(*opts);
+ };
}
if ((isFile) && (opts->isOnlySingleFile))
return singleDICOM(opts, indir);
struct TSearchList nameList;
- nameList.maxItems = 32000; // larger requires more memory, smaller more passes
+ 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);
+ searchDirForDICOM(opts->indir, &nameList, 5, 1, opts);
if (nameList.numItems <= nameList.maxItems)
break;
freeNameList(nameList);
nameList.maxItems = nameList.numItems+1;
- //printf("Second pass required, found %ld images\n", nameList.numItems);
+ //printMessage("Second pass required, found %ld images\n", nameList.numItems);
}
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
+ 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 EXIT_FAILURE;
}
- long long nDcm = nameList.numItems;
- #ifdef myUseCOut
- //stdout is piped in XCode just like printf, if this works with QT then we could replace these duplicate commands...
- //try this out the next QT build:
- fprintf(stdout, "STDOUT PIPE TEST\n");
- std::cout << "Found "<< nameList.numItems <<" DICOM images" <<std::endl;
- #else
- printf( "Found %lu DICOM image(s)\n", nameList.numItems);
- #endif
+ 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;
@@ -2069,7 +2183,7 @@ int nii_loadDir(struct TDCMopts* opts) {
bool compressionWarning = false;
for (int i = 0; i < 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].CSA.numDti > 1) { //4D dataset: dti4D arrays require huge amounts of RAM - write this immediately
+ 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];
dcmSort[0].indx = i;
dcmSort[0].img = ((uint64_t)dcmList[i].seriesNum << 32) + dcmList[i].imageNum;
@@ -2079,9 +2193,45 @@ int nii_loadDir(struct TDCMopts* opts) {
}
if ((dcmList[i].compressionScheme != kCompressNone) && (!compressionWarning) && (opts->compressFlag != kCompressNone)) {
compressionWarning = true; //generate once per conversion rather than once per image
- printf("Image Decompression is new: please validate conversions\n");
+ 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++) {
+ if (isSameSet(opts->series[j].representativeData, dcmList[i], opts->isForceStackSameSeries, &warnings)) {
+ 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 < nDcm; i++ ) {
if ((dcmList[i].converted2NII == 0) && (dcmList[i].isValid)) {
@@ -2092,11 +2242,11 @@ int nii_loadDir(struct TDCMopts* opts) {
nConvert++;
if (nConvert < 1) nConvert = 1; //prevents compiler warning for next line: never executed since j=i always causes nConvert ++
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
TDCMsort * dcmSort = (TDCMsort *)malloc(nConvert * sizeof(TDCMsort));
-#else
- struct TDCMsort dcmSort[nConvert];
-#endif
+//#else
+// struct TDCMsort dcmSort[nConvert];
+//#endif
nConvert = 0;
//warnings = setWarnings();
for (int j = i; j < nDcm; j++)
@@ -2113,18 +2263,17 @@ int nii_loadDir(struct TDCMopts* opts) {
nConvert = removeDuplicates(nConvert, dcmSort);
nConvertTotal += nConvert;
saveDcm2Nii(nConvert, dcmSort, dcmList, &nameList, *opts, &dti4D);
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
free(dcmSort);
-#endif
+//#endif
}//convert all images of this series
}
+#ifdef HAVE_R
+ }
+#endif
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
+ printMessage("No valid DICOM files were found\n");
}
freeNameList(nameList);
//if (nameList.numItems > 0)
@@ -2162,19 +2311,11 @@ 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);
+ printMessage("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);
+ printMessage("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
@@ -2186,7 +2327,12 @@ void readFindPigz (struct TDCMopts *opts, const char * argv[]) {
strcpy(opts->pigzname,"/usr/bin/pigz");
if (!is_exe(opts->pigzname)) {
strcpy(opts->pigzname,"/usr/local/bin/pigz_mricron");
- if (!is_exe(opts->pigzname)) {
+ if (argv == NULL) { //no exectuable path provided
+ if (!is_exe(opts->pigzname))
+ strcpy(opts->pigzname,"");
+ return;
+ }
+ if (!is_exe(opts->pigzname)) {
strcpy(opts->pigzname,argv[0]);
dropFilenameFromPath(opts->pigzname);//, opts.pigzname);
char appendChar[2] = {"a"};
@@ -2197,20 +2343,12 @@ void readFindPigz (struct TDCMopts *opts, const char * argv[]) {
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);
+ #ifdef myDisableZLib
+ printMessage("Compression requires %s\n",pigz);
#else //myUseZLib
- printf("Compression will be faster with %s\n",pigz);
+ printMessage("Compression will be faster with %s\n",pigz);
#endif
- #endif
- strcpy(opts->pigzname,"");
+ strcpy(opts->pigzname,"");
} //no pigz_mricron in exe's folder
} //no /usr/local/pigz_mricron
}//no /usr/bin/pigz
@@ -2218,23 +2356,8 @@ void readFindPigz (struct TDCMopts *opts, const char * argv[]) {
#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[]) {
+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
@@ -2245,23 +2368,52 @@ void readIniFile (struct TDCMopts *opts, const char * argv[]) {
opts->compressFlag = kCompressYes; //OPENJPEG for JPEG2000
#endif
#endif
+ //printMessage("%d %s\n",opts->compressFlag, opts->compressname);
strcpy(opts->indir,"");
strcpy(opts->outdir,"");
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->isFlipY = true;
+ opts->gzLevel = -1;
+ opts->isFlipY = true; //false: images in raw DICOM orientation, true: image rows flipped to cartesian coordinates
opts->isRGBplanar = false;
- opts->isCreateBIDS = false;
+ opts->isCreateBIDS = true;
+ #ifdef isAnonymizeBIDS
+ opts->isAnonymizeBIDS = true;
+ #else
+ opts->isAnonymizeBIDS = false;
+ #endif
opts->isCreateText = false;
#ifdef myDebug
- opts->isVerbose = true;
+ opts->isVerbose = true;
#else
- opts->isVerbose = false;
+ opts->isVerbose = false;
#endif
opts->isTiltCorrect = true;
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<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[]) {
+ setDefaultOpts(opts, argv);
HKEY hKey;
DWORD vSize = 0;
DWORD dwDataType = 0;
@@ -2288,42 +2440,15 @@ void readIniFile (struct TDCMopts *opts, const char * argv[]) {
#define STATUSFILENAME "/.dcm2nii.ini"
void readIniFile (struct TDCMopts *opts, const char * argv[]) {
- 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
- //printf("%d %s\n",opts->compressFlag, opts->compressname);
+ setDefaultOpts(opts, argv);
sprintf(opts->optsname, "%s%s", getenv("HOME"), STATUSFILENAME);
- strcpy(opts->indir,"");
- strcpy(opts->outdir,"");
- opts->isOnlySingleFile = false; //convert all files in a directory, not just a single file
- opts->isForceStackSameSeries = false;
- opts->isCrop = false;
- opts->isGz = false;
- opts->isFlipY = true; //false: images in raw DICOM orientation, true: image rows flipped to cartesian coordinates
- opts->isRGBplanar = false;
- opts->isCreateBIDS = false;
- opts->isCreateText = false;
-#ifdef myDebug
- opts->isVerbose = true;
-#else
- opts->isVerbose = false;
-#endif
- opts->isTiltCorrect = true;
- 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);
+ //printMessage(">%s<->'%s'\n",Setting,Value);
if ( strcmp(Setting,"isGZ") == 0 )
opts->isGz = atoi(Value);
else if ( strcmp(Setting,"isBIDS") == 0 )
@@ -2336,7 +2461,7 @@ void readIniFile (struct TDCMopts *opts, const char * argv[]) {
void saveIniFile (struct TDCMopts opts) {
FILE *fp = fopen(opts.optsname, "w");
- //printf("%s\n",localfilename);
+ //printMessage("%s\n",localfilename);
if (fp == NULL) return;
fprintf(fp, "isGZ=%d\n", opts.isGz);
fprintf(fp, "isBIDS=%d\n", opts.isCreateBIDS);
diff --git a/console/nii_dicom_batch.h b/console/nii_dicom_batch.h
index cc8c67f..37a25a8 100644
--- a/console/nii_dicom_batch.h
+++ b/console/nii_dicom_batch.h
@@ -10,21 +10,36 @@ extern "C" {
#include <stdbool.h>
#include <string.h>
+#ifndef HAVE_R
#include "nifti1.h"
-#include "nifti1.h"
+#endif
#include "nii_dicom.h"
+#ifdef HAVE_R
+ struct TDicomSeries {
+ TDICOMdata representativeData;
+ std::vector<std::string> files;
+ };
+#endif
+
struct TDCMopts {
- bool isGz, isFlipY, isCreateBIDS, isCreateText, isTiltCorrect, isRGBplanar, isOnlySingleFile, isForceStackSameSeries, isCrop;
- int isVerbose, compressFlag; //support for compressed data 0=none,
+ bool isGz, isFlipY, isCreateBIDS, isAnonymizeBIDS, 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];
+#ifdef HAVE_R
+ bool isScanOnly;
+ void *imageList;
+ std::vector<TDicomSeries> 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) ;
- //int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, 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);
+ 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
diff --git a/console/nii_foreign.cpp b/console/nii_foreign.cpp
new file mode 100644
index 0000000..1096201
--- /dev/null
+++ b/console/nii_foreign.cpp
@@ -0,0 +1,404 @@
+#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 <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <math.h>
+#include "print.h"
+#ifdef _MSC_VER
+ #include <direct.h>
+ #define getcwd _getcwd
+ #define chdir _chrdir
+ #include "io.h"
+ #include <math.h>
+ //#define snprintMessage _snprintMessage
+ //#define vsnprintMessage _vsnprintMessage
+ #define strcasecmp _stricmp
+ #define strncasecmp _strnicmp
+#else
+ #include <unistd.h>
+#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 = 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);
+ fread(&lhdr, sizeof(lhdr), 1, f);
+ 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;
+ fread(&ihdr, sizeof(ihdr), 1, f);
+ 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);
+ fread(&lhdr, 512, 1, f);
+ 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
+ fread(&ihdrN, sizeof(ihdrN), 1, f);
+ 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);
+ fread( &imgIn[0], 1, bytesPerVolumeIn, f);
+ 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 ((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;
+ dti4D.S[0].sliceTiming = -1.0;
+ nii_SaveBIDS(niiFilename, dcm, opts, &dti4D, &hdr);
+ ret = nii_saveNII(niiFilename, hdr, img, opts);
+ free(img);
+ return ret;
+}// open_foreign()
diff --git a/console/nii_foreign.h b/console/nii_foreign.h
new file mode 100644
index 0000000..cf4bc46
--- /dev/null
+++ b/console/nii_foreign.h
@@ -0,0 +1,19 @@
+//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
+
+#endif
\ No newline at end of file
diff --git a/console/nii_ortho.cpp b/console/nii_ortho.cpp
index 5f00430..6f72ce4 100644
--- a/console/nii_ortho.cpp
+++ b/console/nii_ortho.cpp
@@ -1,4 +1,6 @@
+#ifndef HAVE_R
#include "nifti1.h"
+#endif
#include "nifti1_io_core.h"
#include "nii_ortho.h"
#include <math.h>
@@ -19,6 +21,8 @@
#endif
//#define MY_DEBUG //verbose text reporting
+#include "print.h"
+
typedef struct {
int v[3];
} vec3i;
@@ -313,7 +317,7 @@ vec3 minCornerFlip (struct nifti_1_header *h, vec3i* flipVec)
#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,
+ 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]);
@@ -331,14 +335,14 @@ unsigned char * nii_setOrtho(unsigned char* img, struct nifti_1_header *h) {
}
if (h->sform_code == NIFTI_XFORM_UNKNOWN) {
#ifdef MY_DEBUG
- printf("No Q or S spatial transforms - assuming canonical orientation");
+ printMessage("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");
+ printMessage("Image in perfect alignment: no need to reorient");
#endif
return img;
}
@@ -348,7 +352,7 @@ unsigned char * nii_setOrtho(unsigned char* img, struct nifti_1_header *h) {
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");
+ printMessage("Image already near best orthogonal alignment: no need to reorient\n");
#endif
return img;
}
@@ -365,8 +369,8 @@ unsigned char * nii_setOrtho(unsigned char* img, struct nifti_1_header *h) {
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]);
+ 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);
diff --git a/console/nii_ortho.h b/console/nii_ortho.h
index 1fc9ac7..3fe6124 100644
--- a/console/nii_ortho.h
+++ b/console/nii_ortho.h
@@ -5,7 +5,10 @@
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);
diff --git a/console/print.h b/console/print.h
new file mode 100755
index 0000000..497d3ec
--- /dev/null
+++ b/console/print.h
@@ -0,0 +1,45 @@
+//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 <stdarg.h>
+ #ifdef HAVE_R
+ #define R_USE_C99_IN_CXX
+ #include <R_ext/Print.h>
+ #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 <iostream>
+ 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;
+ }
+ #else
+ #include<stdio.h>
+ #define printMessage printf
+ #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)
+ #define printError(...) do { printMessage("Error: "); printMessage(__VA_ARGS__);} while(0)
+ #endif //HAVE_R
+#endif //_R_PRINT_H_
diff --git a/ucm.cmake b/console/ucm.cmake
similarity index 82%
rename from ucm.cmake
rename to console/ucm.cmake
index 32b407c..2ab74ea 100644
--- a/ucm.cmake
+++ b/console/ucm.cmake
@@ -1,611 +1,634 @@
-#
-# 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.7" 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_<LANG>_FLAGS or to a specific config
-macro(ucm_add_flags)
- cmake_parse_arguments(ARG "C;CXX;CLEAR_OLD" "CONFIG" "" ${ARGN})
-
- # determine to which flags to add
- set(CXX_FLAGS CMAKE_CXX_FLAGS)
- set(C_FLAGS CMAKE_C_FLAGS)
- if(NOT "${ARG_CONFIG}" STREQUAL "")
- string(TOUPPER ${ARG_CONFIG} ARG_CONFIG)
- set(CXX_FLAGS CMAKE_CXX_FLAGS_${ARG_CONFIG})
- set(C_FLAGS CMAKE_C_FLAGS_${ARG_CONFIG})
- 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()
-endmacro()
-
-# ucm_set_flags
-# Sets the CMAKE_<LANG>_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_<TYPE>_LINKER_FLAGS or to a specific config
-macro(ucm_add_linker_flags)
- cmake_parse_arguments(ARG "CLEAR_OLD;EXE;MODULE;SHARED;STATIC" "CONFIG" "" ${ARGN})
-
- string(TOUPPER "${ARG_CONFIG}" ARG_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 "${ARG_CONFIG}" STREQUAL "")
- list(APPEND flags_configs CMAKE_EXE_LINKER_FLAGS_${ARG_CONFIG})
- else()
- list(APPEND flags_configs CMAKE_EXE_LINKER_FLAGS)
- endif()
- endif()
- if(${ARG_MODULE})
- if(NOT "${ARG_CONFIG}" STREQUAL "")
- list(APPEND flags_configs CMAKE_MODULE_LINKER_FLAGS_${ARG_CONFIG})
- else()
- list(APPEND flags_configs CMAKE_MODULE_LINKER_FLAGS)
- endif()
- endif()
- if(${ARG_SHARED})
- if(NOT "${ARG_CONFIG}" STREQUAL "")
- list(APPEND flags_configs CMAKE_SHARED_LINKER_FLAGS_${ARG_CONFIG})
- else()
- list(APPEND flags_configs CMAKE_SHARED_LINKER_FLAGS)
- endif()
- endif()
- if(${ARG_STATIC})
- if(NOT "${ARG_CONFIG}" STREQUAL "")
- list(APPEND flags_configs CMAKE_STATIC_LINKER_FLAGS_${ARG_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()
-endmacro()
-
-# ucm_set_linker_flags
-# Sets the CMAKE_<TYPE>_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(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(NOT ${flags} MATCHES "-static-libstdc\\+\\+")
- set(${flags} "${${flags}} -static-libstdc++")
- 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)
- 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()
-
- # 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}")
- 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" "" ${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}")
-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})
-
- # 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 "")
- set_target_properties(all_unity PROPERTIES FOLDER "")
- set_target_properties(clean_cotire PROPERTIES FOLDER "")
- endif()
- 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})
- 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()
+#
+# 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.7" 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_<LANG>_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_<LANG>_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_<TYPE>_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 "${ARG_CONFIG}" ARG_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_<TYPE>_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(NOT ${flags} MATCHES "-static-libstdc\\+\\+")
+ set(${flags} "${${flags}} -static-libstdc++")
+ 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()
diff --git a/console/ujpeg.cpp b/console/ujpeg.cpp
index 516292e..14eebec 100644
--- a/console/ujpeg.cpp
+++ b/console/ujpeg.cpp
@@ -1,7 +1,253 @@
+// NanoJPEG -- KeyJ's Tiny Baseline JPEG Decoder
+// version 1.3.5 (2016-11-14)
+// Copyright (c) 2009-2016 Martin J. Fiedler <martin.fiedler at gmx.net>
+// 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "ujpeg.h"
+
+int main(int argc, char* argv[]) {
+ int size;
+ char *buf;
+ FILE *f;
+
+ if (argc < 2) {
+ printf("Usage: %s <input.jpg> [<output.ppm>]\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
///////////////////////////////////////////////////////////////////////////////
@@ -19,8 +265,6 @@
#define NJ_FORCE_INLINE static inline
#endif
-#define NJ_USE_LIBC 1
-
#if NJ_USE_LIBC
#include <stdlib.h>
#include <string.h>
@@ -280,10 +524,12 @@ 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) {
@@ -312,19 +558,17 @@ NJ_INLINE void njDecodeSOF(void) {
}
nj.mbsizex = ssxmax << 3;
nj.mbsizey = ssymax << 3;
- if ((nj.mbsizex == 0) || (nj.mbsizey == 0)) return;
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->stride = (c->width + 7) & 0x7FFFFFF8;
c->height = (nj.height * c->ssy + ssymax - 1) / ssymax;
- c->stride = nj.mbwidth * nj.mbsizex * c->ssx / ssxmax;
+ 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 * nj.mbsizey * c->ssy / ssymax)))) njThrow(NJ_OUT_OF_MEM);
+ 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);
+ nj.rgb = (unsigned char*) njAllocMem(nj.width * nj.height * nj.ncomp);
if (!nj.rgb) njThrow(NJ_OUT_OF_MEM);
}
njSkip(nj.length);
@@ -335,6 +579,7 @@ NJ_INLINE void njDecodeDHT(void) {
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);
@@ -353,7 +598,7 @@ NJ_INLINE void njDecodeDHT(void) {
remain -= currcnt << (16 - codelen);
if (remain < 0) njThrow(NJ_SYNTAX_ERROR);
for (i = 0; i < currcnt; ++i) {
- unsigned char code = nj.pos[i];
+ register unsigned char code = nj.pos[i];
for (j = spread; j; --j) {
vlc->bits = (unsigned char) codelen;
vlc->code = code;
@@ -374,6 +619,7 @@ 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);
@@ -388,6 +634,7 @@ NJ_INLINE void njDecodeDQT(void) {
NJ_INLINE void njDecodeDRI(void) {
njDecodeLength();
+ njCheckError();
if (nj.length < 2) njThrow(NJ_SYNTAX_ERROR);
nj.rstinterval = njDecode16(nj.pos);
njSkip(nj.length);
@@ -433,6 +680,7 @@ NJ_INLINE void njDecodeScan(void) {
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);
@@ -489,7 +737,7 @@ NJ_INLINE void njUpsampleH(nj_component_t* c) {
const int xmax = c->width - 3;
unsigned char *out, *lin, *lout;
int x, y;
- out = njAllocMem((c->width * c->height) << 1);
+ out = (unsigned char*) njAllocMem((c->width * c->height) << 1);
if (!out) njThrow(NJ_OUT_OF_MEM);
lin = c->pixels;
lout = out;
@@ -509,7 +757,7 @@ NJ_INLINE void njUpsampleH(nj_component_t* c) {
}
c->width <<= 1;
c->stride = c->width;
- njFreeMem(c->pixels);
+ njFreeMem((void*)c->pixels);
c->pixels = out;
}
@@ -517,7 +765,7 @@ 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 = njAllocMem((c->width * c->height) << 1);
+ 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];
@@ -538,7 +786,7 @@ NJ_INLINE void njUpsampleV(nj_component_t* c) {
}
c->height <<= 1;
c->stride = c->width;
- njFreeMem(c->pixels);
+ njFreeMem((void*) c->pixels);
c->pixels = out;
}
@@ -549,9 +797,9 @@ NJ_INLINE void njUpsample(nj_component_t* c) {
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);
+ out = (unsigned char*) njAllocMem(c->width * c->height);
if (!out) njThrow(NJ_OUT_OF_MEM);
- //lin = c->pixels;
+ lin = c->pixels;
lout = out;
for (y = 0; y < c->height; ++y) {
lin = &c->pixels[(y >> yshift) * c->stride];
@@ -560,13 +808,13 @@ NJ_INLINE void njUpsample(nj_component_t* c) {
lout += c->width;
}
c->stride = c->width;
- njFreeMem(c->pixels);
+ njFreeMem((void*) c->pixels);
c->pixels = out;
}
#endif
-NJ_INLINE void njConvert() {
+NJ_INLINE void njConvert(void) {
int i;
nj_component_t* c;
for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) {
@@ -592,9 +840,9 @@ NJ_INLINE void njConvert() {
const unsigned char *pcr = nj.comp[2].pixels;
for (yy = nj.height; yy; --yy) {
for (x = 0; x < nj.width; ++x) {
- int y = py[x] << 8;
- int cb = pcb[x] - 128;
- int cr = pcr[x] - 128;
+ 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);
@@ -665,4 +913,4 @@ 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_ONLY
+#endif // _NJ_INCLUDE_HEADER_ONLY
\ No newline at end of file
diff --git a/console/ujpeg.h b/console/ujpeg.h
index 9321970..cf913b8 100644
--- a/console/ujpeg.h
+++ b/console/ujpeg.h
@@ -1,10 +1,6 @@
#ifndef _NANOJPEG_H
#define _NANOJPEG_H
-#ifdef __cplusplus
-extern "C" {
-#endif
-
// nj_result_t: Result codes for njDecode().
typedef enum _nj_result {
NJ_OK = 0, // no error, decoding successful
@@ -61,9 +57,5 @@ int njGetImageSize(void);
// allocated at run-time by NanoJPEG. It is still possible to decode another
// image after a njDone() call.
void njDone(void);
-
-#ifdef __cplusplus
-}
-#endif
#endif//_NANOJPEG_H
diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt
new file mode 100644
index 0000000..7edff52
--- /dev/null
+++ b/docs/CMakeLists.txt
@@ -0,0 +1,36 @@
+# 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")
diff --git a/docs/source/conf.py b/docs/source/conf.py
new file mode 100644
index 0000000..526b366
--- /dev/null
+++ b/docs/source/conf.py
@@ -0,0 +1,340 @@
+# -*- 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 at gmail.com>'], 1),
+ ('dcm2niibatch', 'dcm2niibatch', u'DICOM to NifTI batch converter',
+ ['This manual was developed and is maintained by Benjamin Irving <mail at 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
diff --git a/docs/source/dcm2niibatch.rst b/docs/source/dcm2niibatch.rst
new file mode 100644
index 0000000..1e6bc14
--- /dev/null
+++ b/docs/source/dcm2niibatch.rst
@@ -0,0 +1,81 @@
+: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.
+
+
diff --git a/docs/source/dcm2niix.rst b/docs/source/dcm2niix.rst
new file mode 100644
index 0000000..8d27ca8
--- /dev/null
+++ b/docs/source/dcm2niix.rst
@@ -0,0 +1,73 @@
+: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.
diff --git a/docs/source/index.rst b/docs/source/index.rst
new file mode 100644
index 0000000..73b3174
--- /dev/null
+++ b/docs/source/index.rst
@@ -0,0 +1,22 @@
+.. 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`
+
diff --git a/license.txt b/license.txt
index c956305..0b422b9 100644
--- a/license.txt
+++ b/license.txt
@@ -1,9 +1,9 @@
The following files are from different authors and have their own licenses
-nifti.h, nifti1_io.h/nifti1_io.c is public domain
+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.c see web site for licenses
+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/
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/dcm2niix.git
More information about the debian-med-commit
mailing list