Full Code of bbrister/SIFT3D for AI

master 6245b245b5d1 cached
69 files
591.7 KB
164.6k tokens
337 symbols
1 requests
Download .txt
Showing preview only (617K chars total). Download the full file or copy to clipboard to get everything.
Repository: bbrister/SIFT3D
Branch: master
Commit: 6245b245b5d1
Files: 69
Total size: 591.7 KB

Directory structure:
gitextract_mi4zrg4r/

├── .gitignore
├── CHANGES.md
├── CMakeLists.txt
├── LICENSE
├── README.md
├── SIFT3DConfig.cmake.in
├── SIFT3DConfigVersion.cmake.in
├── cli/
│   ├── CMakeLists.txt
│   ├── denseSift3D.c
│   ├── kpSift3D.c
│   └── regSift3D.c
├── cmake/
│   ├── FindDCMTK.cmake
│   ├── FindMingw.cmake
│   ├── FindNIFTI.cmake
│   ├── FindOpenMP.cmake
│   └── SIFT3DPackage.cmake
├── doc/
│   ├── INSTALL_LINUX.md
│   ├── INSTALL_MAC.md
│   └── INSTALL_WINDOWS.md
├── examples/
│   ├── CMakeLists.txt
│   ├── featuresC.c
│   ├── featuresMatlab.m
│   ├── ioC.c
│   ├── ioMatlab.m
│   ├── manualFeaturesMatlab.m
│   ├── registerC.c
│   └── registerMatlab.m
├── imutil/
│   ├── CMakeLists.txt
│   ├── dicom.cpp
│   ├── dicom.h
│   ├── immacros.h
│   ├── imtypes.h
│   ├── imutil.c
│   ├── imutil.h
│   ├── kernels.cl
│   ├── nifti.c
│   ├── nifti.h
│   └── templates/
│       ├── CMakeLists.txt
│       └── sep_fir_3d.template
├── reg/
│   ├── CMakeLists.txt
│   ├── reg.c
│   └── reg.h
├── sift3d/
│   ├── CMakeLists.txt
│   ├── sift.c
│   └── sift.h
└── wrappers/
    ├── CMakeLists.txt
    └── matlab/
        ├── CMakeLists.txt
        ├── README.md
        ├── Sift3DParser.m
        ├── Sift3DTest.m
        ├── checkUnits3D.m
        ├── detectSift3D.m
        ├── extractSift3D.m
        ├── imRead3D.m
        ├── imWrite3D.m
        ├── keypoint3D.m
        ├── matchSift3D.m
        ├── mexDetectSift3D.c
        ├── mexExtractSift3D.c
        ├── mexImRead3D.c
        ├── mexImWrite3D.c
        ├── mexMatchSift3D.c
        ├── mexOrientation3D.c
        ├── mexRegisterSift3D.c
        ├── mexutil.c
        ├── mexutil.h
        ├── orientation3D.m
        ├── registerSift3D.m
        └── setupSift3D.m

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
################################################################################
# Copyright (c) 2015 Blaine Rister et al., see LICENSE for details.
################################################################################
# Files to be ignored by git.
################################################################################

*.o
*.nii
*.DS_Store
*.png

build
build/*
debug
debug/*
scripts
scripts/*
*DEBUG*


================================================
FILE: CHANGES.md
================================================
# SIFT3D version history

## 1.0.0

* Initial release

## 1.1.0

* Added DICOM IO
* Wrapped DICOM and NIFTI IO in C functions im_read and im_write
* Added matlab wrappers keypoint3D, imRead3D and imWrite3D
* Changed C function SIFT3D_Extract_Descriptors to SIFT3D_Extract_Descriptors and SIFT3D_Extract_Raw_Descriptors
* Fixed various bugs, improving keypoint and descriptor accuracy

## 1.1.1 October 30, 2015

* Performance optimizations
* Fixes to DICOM slice ordering (im_read, imRead3D)
* Write more DICOM metadata (im_write, imWrite3D)
* Corrected Mac build instructions

## 1.2.0 January 5, 2016

* Fixed Mac linking issues
* Take real-world units into account in SIFT3D keypoints, descriptors
* Added Matlab wrapper for image registration
* Optionally resample input images prior to registration (regSift3D, register_SIFT3D_resample)
* Write more NIFTI metadata (im_write, imWrite3D)
* Changed C interface for descriptor extraction to disallow providing a custom Gaussian scale-space pyramid (SIFT3D_Extract_Descriptors)
* Allow arbitrary output sizes in im_inv_transform
* Minor bug fixes

## 1.3.0 March 28, 2016

* Add parameters for keypoint detection to Matlab interface
* Add registration parameters to Matlab and CLI
* Default in Matlab to faster version of image registration that avoids resampling
* Improved error handling
* Removed unused, faulty options
* Print internal error messages to the Matlab command prompt

## 1.3.1 March 31, 2016

* Removed option to set the number of octaves, which was causing bugs 

## 1.4.0 May 11, 2016

* Fixed bugs to improve keypoint and registration accuracy, especially concerning rotations
* Fixed bug that caused crashing on some large images
* Dramatically reduced memory consumption of keypoint matching
* Refactored SIFT3D_nn_match_fb into SIFT3D_nn_match, as there is now no reason to prefer forward to forward-backward matching
* Renamed headers macros.h and types.h to immacros.h and imtypes.h, respectively

## 1.4.1 May 25, 2016

* Removed keypoint refinement step, which did not improve the accuracy.

## 1.4.2 June 15, 2016

* Added multithreading with OpenMP
* Improved keypoint matching speed

## 1.4.3 July 24, 2016

* Fixed CMake settings to allow linking to CMake targets without dependencies
* Updated for newer versions of CMake, MinGW
* Update package dependencies for Ubuntu 16.04
* Removed POSIX dependencies in header files to allow linking with Visual Studio
* Added support for reading JPEG-compressed DICOM files
* Ship both MS (.lib) and MinGW (.dll.a) import libraries on Windows
* Ship with MinGW runtime libraries on Windows
* Ship with OpenBLAS on Windows

## 1.4.4 September 13, 2017

* Add support for reading Dicom Segmentation Objects (DSOs)
* Add the option to compile without DCMTK and nifticlib
* Reading images no longer scales them (im_read, imRead3D)
* Read DICOM CT scans in Hounsfield units (im_read, imRead3D)
* Fix header includes for newer builds of MinGW (TDM-GCC)

## 1.4.5 January 17, 2018

* Fixed a bug in orientation assignment to improve the accuracy of SIFT3D descriptors. Thanks to KinMan for finding this bug.
  * Change the default value of corner_thresh parameter to 0.4, to get the same number of keypoints as before the fix
* Add a new Matlab wrapper function to enable matching pre-computed descriptors (matchSift3D.m)
* Compute the slice spacing of multi-file Dicom series, using this instead of the Slice Thickness metadata. Warn the user if the slice spacing differs from the slice thickness. Throw an error if the slice spacing is inconsistent between pairs of adjacent images.
* Add keypoint octave indices to kpSift3D output. Thanks to v8korb for this suggestion.
* Refactor RANSAC code for improved clarity and efficiency. Thanks to cslayers for this suggestion.

## 1.4.6 November 12, 2019

* Fix MEX file compilation for Matlab 2018b and newer
* Improve Nifti-1 image reading to take into account slope and intercept
* Convert PET scans to SUV
* Read Dicom series which are stored in unusual orientations (e.g., Y-Z planes instead of X-Y). This is needed for reading 3D mammograms.
* Support 4D Nifti files


================================================
FILE: CMakeLists.txt
================================================
################################################################################
# Copyright (c) 2015-2017 Blaine Rister et al., see LICENSE for details.
################################################################################
# Top-level build file for SIFT3D.
################################################################################

include (ExternalProject)

# Minimum CMake version
cmake_minimum_required (VERSION 3.3)

# Minimum C++ version
set (CMAKE_CXX_STANDARD 11)
set (CMAKE_CXX_STANDARD_REQUIRED ON)

# Convert GNU archives to .lib files in Windows
if (WIN32)
	set (CMAKE_GNUtoMS ON
		CACHE BOOL 
		"Convert .dll.a files to .lib" 
	)
endif ()

# Project name
project (SIFT3D)

# Project version
set (SIFT3D_VERSION 1.4.6)

# Default build type
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
        message (STATUS  "Build type not specified. Defaulting to 'Release'.")
        set (CMAKE_BUILD_TYPE Release CACHE STRING "The type of build" FORCE)
        set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release")
endif ()

# Default settings for optional components
if (WIN32)
        set (_BUILD_CLI OFF)
else ()
        set (_BUILD_CLI ON)
endif()

# Optional component variables
set (BUILD_CLI ${_BUILD_CLI} CACHE BOOL 
        "If ON, builds the command line interface")
set (BUILD_EXAMPLES "ON" CACHE BOOL "If ON, builds the example programs")
set (BUILD_PACKAGE "OFF" CACHE BOOL "If ON, builds the package generator")

# Configurable paths        
set (INSTALL_LIB_DIR "lib/sift3d" CACHE PATH 
        "Installation directory for libraries")
set (INSTALL_BIN_DIR "bin" CACHE PATH 
        "Installation directory for executables")
set (INSTALL_INCLUDE_DIR "include/sift3d" CACHE PATH 
        "Installation directory for header files")
if (WIN32 AND NOT CYGWIN)
        set (DEFAULT_INSTALL_CMAKE_DIR "cmake")
else ()
        set (DEFAULT_INSTALL_CMAKE_DIR "lib/cmake/sift3d")
endif ()
set (INSTALL_CMAKE_DIR ${DEFAULT_INSTALL_CMAKE_DIR} CACHE PATH
        "Installation directory for CMake files")

# Internal paths 
list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake)
set (LICENSE_FILE ${CMAKE_CURRENT_LIST_DIR}/LICENSE)
set (README_FILE ${CMAKE_CURRENT_LIST_DIR}/README.md)

# Configure RPATH options
set (INSTALL_LIB_PATH "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}")
set (CMAKE_SKIP_BUILD_RPATH OFF)
set (CMAKE_BUILD_WITH_INSTALL_RPATH OFF)
set (CMAKE_INSTALL_RPATH ${INSTALL_LIB_PATH})
set (CMAKE_INSTALL_RPATH_USE_LINK_PATH ON)

# Output directories
set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

# The name and location of the wrappers build and install directories
set (WRAPPERS_DIR "wrappers")
set (BUILD_WRAPPERS_DIR "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${WRAPPERS_DIR}")
set (INSTALL_WRAPPERS_DIR "${INSTALL_LIB_DIR}/${WRAPPERS_DIR}")

# Build flags
set (DEBUG_FLAGS "-g -DVERBOSE")
set (RELEASE_FLAGS "-O3 -DNDEBUG")

# GCC-specific flags
if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR 
    CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
    set (DEBUG_FLAGS "${DEBUG_FLAGS} -ggdb3")
endif ()

# OS-specific build flags
if (APPLE)
        # Enable undefined shared library symbols
        set (CMAKE_SHARED_LINKER_FLAGS 
		"${CMAKE_SHARED_LINKER_FLAGS} -undefined dynamic_lookup")

        # Use rpath
        set (CMAKE_MACOSX_RPATH ON)
endif ()

set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${DEBUG_FLAGS}")
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${DEBUG_FLAGS}")
set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${RELEASE_FLAGS}")
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${RELEASE_FLAGS}")

# Find the system libraries
if (WIN32)
        # m is not a separate library in Windows
        set (M_LIBRARY "")
else () 
        find_library (M_LIBRARY m)
endif ()
find_package (ZLIB REQUIRED)

# Try to find and use OpenMP
find_package (OpenMP)
if (OPENMP_FOUND)
        # Nothing extra needs to be done here.
elseif (WITH_OpenMP)
        message (FATAL_ERROR "OpenMP not found. Please provide a compiler that "
                "supports OpenMP, or disable the OpenMP by setting "
                "the variable WITH_OpenMP to false.")
else ()
        message (STATUS "Failed to find OpenMP. Compiling without it.")
endif ()
set (WITH_OpenMP ${OPENMP_FOUND} CACHE BOOL "If ON, parallelizes with OpenMP "
        "in release mode")
if (WITH_OpenMP)
        set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${OpenMP_C_FLAGS}")
        set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${OpenMP_CXX_FLAGS}")
endif ()

# Look for MATLAB in the default locations
find_package (Matlab QUIET)

# If not found, look for MATLAB in OS-specific locations
if (NOT Matlab_FOUND)
        if (APPLE)
                set (Matlab_ROOT_DIR 
                        "/Applications/Matlab.app")
                find_package (Matlab QUIET)
        endif ()
endif ()

# Set up Matlab paths
if (Matlab_FOUND)
	message (STATUS "Found Matlab.")
elseif (BUILD_Matlab)
        message (FATAL_ERROR "Matlab not found. Please set the variable "
                "Matlab_ROOT_DIR to the location of your Matlab installation, "
                "or disable the Matlab toolbox by setting BUILD_Matlab to "
                "false.")
else ()
        message (STATUS "Matlab not found. The toolbox will not be built.")
endif ()
set (BUILD_Matlab ${Matlab_FOUND} CACHE BOOL "If ON, builds the Matlab toolbox")
if (BUILD_Matlab)
        message (STATUS "Building the Matlab toolbox.")

        # Set the name and location of the Matlab toolbox
        set (TOOLBOX_NAME "matlab")
        set (BUILD_TOOLBOX_DIR "${BUILD_WRAPPERS_DIR}/${TOOLBOX_NAME}")
        set (INSTALL_TOOLBOX_DIR "${INSTALL_WRAPPERS_DIR}/${TOOLBOX_NAME}"
                CACHE PATH "Installation directory for the Matlab toolbox")
        set (INSTALL_TOOLBOX_PATH "${CMAKE_INSTALL_PREFIX}/${INSTALL_TOOLBOX_DIR}")
        list (APPEND CMAKE_INSTALL_RPATH ${INSTALL_TOOLBOX_PATH})

        # Get the path of the Matlab libraries
        get_filename_component (Matlab_LIBRARIES_PATH ${Matlab_MEX_LIBRARY} 
                DIRECTORY)

	# Check for the MX library, which CMake cannot find on Windows
	if (NOT Matlab_MX_LIBRARY_FOUND)

		# Find Matlab's MX library
		find_library (Matlab_MX_LIBRARY mx libmx 
                        PATHS ${Matlab_LIBRARIES_PATH}
                )
	endif ()

        # Find the Matlab LAPACK and BLAS libraries
        find_library (Matlab_MWLAPACK_LIBRARY mwlapack libmwlapack 
                PATHS ${Matlab_LIBRARIES_PATH}
        )
        find_library (Matlab_MWBLAS_LIBRARY mwblas libmwblas 
                PATHS ${Matlab_LIBRARIES_PATH}
        )
endif ()

# Generate the package configuration files
set (CMAKE_CONFIG_FILE ${CMAKE_CURRENT_BINARY_DIR}/SIFT3DConfig.cmake)
set (CMAKE_VERSION_FILE ${CMAKE_CURRENT_BINARY_DIR}/SIFT3DConfigVersion.cmake)
configure_file (SIFT3DConfig.cmake.in
        ${CMAKE_CONFIG_FILE} @ONLY
)
configure_file (SIFT3DConfigVersion.cmake.in
        ${CMAKE_VERSION_FILE} @ONLY)

# Install the package configuration files
install (FILES ${CMAKE_CONFIG_FILE} ${CMAKE_VERSION_FILE} 
        DESTINATION ${INSTALL_CMAKE_DIR})

# Install the targets file
install (EXPORT SIFT3D-targets DESTINATION ${INSTALL_CMAKE_DIR})

# Mandatory source subdirectories
add_subdirectory (imutil)
add_subdirectory (sift3d)
add_subdirectory (reg)
add_subdirectory (wrappers)

# Command line interface
if (BUILD_CLI)
        add_subdirectory (cli)
endif ()

# Examples
if (BUILD_EXAMPLES)
        add_subdirectory (examples)
endif ()

# Packager file
if (BUILD_PACKAGE)
        include (SIFT3DPackage)
endif ()


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2015-2016 Blaine Rister et al.

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.



================================================
FILE: README.md
================================================
# SIFT3D

Copyright (c) 2015-2019 Blaine Rister et al., see LICENSE for details.

SIFT3D is an analogue of the scale-invariant feature transform (SIFT) for three-dimensional images. It leverages volumetric data and real-world units to detect keypoints and extract a robust description of their content. It can also perform 3D image registration by matching SIFT3D features and fitting geometric transformations with the RANSAC algorithm. All of this is implemented in a cross-platform C library, with wrappers for Matlab.

SIFT3D includes imutil, a utility library for image processing and linear algebra. This library performs file IO in a variety of medical imaging formats, including DICOM and NIFTI.

## Contents

This code creates the following executables:
- kpSift3D - Extract keypoints and descriptors from a single image.
- regSift3D - Extract matches and a geometric transformation from two images. 

and the following libraries:
- libreg.so - Image registration from SIFT3D features
- libsift3d.so - Extract and match SIFT3D features
- libimutil.so - Utility library for image processing, regression and linear algebra. Includes IO functions for DICOM and NIFTI file formats.

It also contains a Matlab toolbox for calling the library functions from Matlab scripts. See the README in /wrappers/matlab for more information.

## Installation instructions

See doc/INSTALL_\<PLATFORM\>.md for instructions on installing SIFT3D for your specific platform.

## Usage instructions

For instructions on using the CLI, use the "--help" option, e.g. 
        kpSift3D --help

See /examples for sample programs using the C and Matlab APIs.

The following sections describe how to link a program to the SIFT3D libraries.

### Linking to SIFT3D libraries with CMake

SIFT3D exports a CMake package to the install directories. Here is an example of compiling a C program with SIFT3D from a CMake list.

        find_package (SIFT3D) # Find SIFT3D
        add_executable (helloWorld helloWorld.c) # Declare a target
        target_link_libraries (helloWorld PUBLIC ${SIFT3D_LIBRARIES}) # Link to the SIFT3D libraries
        if (WIN32) # Find the SIFT3D headers
            target_include_directories (helloWorld PUBLIC "${SIFT3D_DIR}/../${SIFT3D_INCLUDE_DIRS}") 
        else()
            target_include_directories (helloWorld PUBLIC ${SIFT3D_INCLUDE_DIRS}) 
        endif()

### Linking to SIFT3D libraries without CMake

The header files and libraries are installed to "sift3d" subdirectories in your installation tree. On most systems, you will need to add these subdirectories to your include and linker search paths. You will also need to link to the dependencies listed below.

- libimutil - requires linking to LAPACK, BLAS, and zlib. Linking to DCMTK and nifticlib are optional.
- libsift3d - requires linking to imutil
- libreg - requires linking to sift3d and imutil

Information about the dependencies can be found in the installation instructions.

*Note: On Windows systems, some of the dependencies are statically linked to the SIFT3D libraries. In this case, it suffices to link to the DLLs in the "bin" subdirectory of your installation.*

## Contact

Please contact me at blaine@stanford.edu if you have any questions or concerns.

If you would like to cite this work, please refer to the following paper:

B. Rister, M. A. Horowitz and D. L. Rubin, "Volumetric Image Registration From Invariant Keypoints," in *IEEE Transactions on Image Processing*, vol. 26, no. 10, pp. 4900-4910, Oct. 2017.
doi: 10.1109/TIP.2017.2722689

The paper and automatic citations are available [here](http://ieeexplore.ieee.org/document/7967757/citations).


================================================
FILE: SIFT3DConfig.cmake.in
================================================
################################################################################
# Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.
################################################################################
# SIFT3D package configuration file. Defines the following variables:
#   SIFT3D_INCLUDE_DIRS - Include directories
#   SIFT3D_LIBRARIES - Library targets
################################################################################

# Import the targets, if not already imported
if (NOT TARGET reg)
        include (${CMAKE_CURRENT_LIST_DIR}/SIFT3D-targets.cmake)
endif() 

# Get the libraries
set (SIFT3D_LIBRARIES reg sift3D imutil)
set (SIFT3D_INCLUDE_DIRS @INSTALL_INCLUDE_DIR@)


================================================
FILE: SIFT3DConfigVersion.cmake.in
================================================
################################################################################
# Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.
################################################################################
# SIFT3D package version file.
################################################################################

set (PACKAGE_VERSION @SIFT3D_VERSION@)

# Check whether the requested PACKAGE_FIND_VERSION is compatible
if ("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
        set (PACKAGE_VERSION_COMPATIBLE FALSE)
else ()
        set (PACKAGE_VERSION_COMPATIBLE TRUE)
        if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
                set (PACKAGE_VERSION_EXACT TRUE)
        endif ()
endif ()


================================================
FILE: cli/CMakeLists.txt
================================================
################################################################################
# Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.
################################################################################
# Build file for the command line interfaces.
################################################################################

add_executable (denseSift3D denseSift3D.c)
target_link_libraries(denseSift3D PUBLIC sift3D imutil)
target_link_libraries(denseSift3D PRIVATE ${M_LIBRARY})

add_executable(kpSift3D kpSift3D.c)
target_link_libraries(kpSift3D PUBLIC sift3D imutil)

add_executable(regSift3D regSift3D.c)
target_link_libraries(regSift3D PUBLIC reg sift3D imutil)

install (TARGETS denseSift3D kpSift3D regSift3D
	 RUNTIME DESTINATION ${INSTALL_BIN_DIR} 
	 LIBRARY DESTINATION ${INSTALL_LIB_DIR} 
	 ARCHIVE DESTINATION ${INSTALL_LIB_DIR})


================================================
FILE: cli/denseSift3D.c
================================================
/* -----------------------------------------------------------------------------
 * denseSift3d.c 
 * -----------------------------------------------------------------------------
 * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.
 * -----------------------------------------------------------------------------
 * This file contains a command-line tool to extract dense SIFT3D features from 
 * an image.
 * -----------------------------------------------------------------------------
 */

#include <stdio.h>
#include <string.h>
#include <math.h>
#include "immacros.h"
#include "imutil.h"
#include "sift.h"

#define BUF_SIZE (1 << 10)

/* The log tag */
const char tag[] = "denseSift3d";

/* The help message */
const char help_msg[] = 
        "Usage: denseSift3D [input.nii] [descriptors%.nii] \n"
        "\n"
        "Extracts a dense gradient histogram image from the input file. The \n"
        "output is a set of 12 images, each representing a channel or \n"
        "histogram bin. The last '%' character in the output filename is \n"
        "replaced by the channel index.\n"
        "\n"
        "Supported image formats: \n"
	"	.dcm (DICOM) \n"
        "	.nii (nifti-1) \n"
        "	.nii.gz (gzip-compressed nifti-1) \n"
	"	directory containing .dcm files \n"
        "\n"
        "Example: \n"
        "       denseSift3d in.nii.gz out%.nii.gz \n"
        "\n"
        "Upon completion, the output would be the following 12 images: \n"
        "       -out0.nii.gz \n"
        "       -out1.nii.gz \n"
        "            ... \n"
        "       -out11.nii.gz \n"
        "\n";
          
/* Print an error message. */      
void err_msg(const char *msg) {
        SIFT3D_ERR("%s: %s \n"
                "Use \"denseSift3d --help\" for more information. \n", tag, 
                msg);
}

/* Report an unexpected error. */
void err_msgu(const char *msg) {
        err_msg(msg);
        print_bug_msg();
}

int main(int argc, char **argv) {

        char out_name[BUF_SIZE], chan_str[BUF_SIZE];
        Image im, desc, chan;
        SIFT3D sift3d;
        char *in_path, *out_path, *marker;
        size_t len;
        int c, marker_pos;

        /* Parse the GNU standard options */
        switch (parse_gnu(argc, argv)) {
                case SIFT3D_HELP:
                        puts(help_msg);
                        return 0;
                case SIFT3D_VERSION:
                        return 0;
        }

        /* Parse the arguments */
        if (argc < 3) {
                err_msg("Not enough arguments.");
                return 1;
        } else if (argc > 3) {
                err_msg("Too many arguments.");
                return 1;
        }
        in_path = argv[1];
        out_path = argv[2];

        /* Initialize data */
        init_im(&im);
        init_im(&desc);
        init_im(&chan);
        if (init_SIFT3D(&sift3d)) {
                err_msgu("Failed to initialize SIFT3D data.");
                return 1;
        }

        /* Read the image */        
        if (im_read(in_path, &im)) {
                
                char msg[BUF_SIZE];

                snprintf(msg, BUF_SIZE, "Failed to read input image \"%s\".",
			in_path);
                err_msg(msg);
                return 1;
        }

        /* Ensure the output file name has a % character */
        if ((marker = strrchr(out_path, '%')) == NULL) {
                err_msg("output filename must contain '%'.");
                return 1;
        }
        marker_pos = marker - out_path;

        /* Get the output file name length */
        len = strlen(out_path) + (int) ceil(log10((double) im.nc)) - 1;
        if (len > BUF_SIZE) {

                char msg[BUF_SIZE];

                snprintf(msg, BUF_SIZE, "Ouput filename cannot exceed %d "
			"characters.", BUF_SIZE);
                err_msg(msg);
                return 1;
        }

        /* Extract the descriptors */
        if (SIFT3D_extract_dense_descriptors(&sift3d, &im, &desc)) {
                err_msgu("Failed to extract descriptors.");
                return 1;
        }
        

        /* Write each channel as a separate image */
        for (c = 0; c < desc.nc; c++) {

                /* Get the channel */
                if (im_channel(&desc, &chan, c)) {
                        err_msgu("Failed to extract the channel.");
                        return 1;
                }

                /* Form the output file name */
                out_name[0] = '\0';
                snprintf(chan_str, BUF_SIZE, "%d", c);
                strncat(out_name, out_path, marker_pos);
                strcat(out_name, chan_str);
                strcat(out_name, marker + 1);

                /* Write the channel */
                if (im_write(out_name, &chan)) {

                        char msg[BUF_SIZE];

                        snprintf(msg, BUF_SIZE, "Failed to write output image "
				"\"%s\".", out_name);
                        err_msg(msg);
                        return 1;
                }
        }

        return 0;
}


================================================
FILE: cli/kpSift3D.c
================================================
/* -----------------------------------------------------------------------------
 * kpSift3D.c
 * -----------------------------------------------------------------------------
 * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.
 * -----------------------------------------------------------------------------
 * This file contains the CLI to extract SIFT3D keypoints and descriptors from
 * a single image. 
 * -----------------------------------------------------------------------------
 */

#include <stdio.h>
#include <getopt.h>
#include "immacros.h"
#include "imutil.h"
#include "sift.h"

/* Options */
#define KEYS 'a'
#define DESC 'b'
#define DRAW 'c'

/* Message buffer size */
#define BUF_SIZE 1024

/* Help message */
const char help_msg[] = 
        "Usage: kpSift3D [image.nii] \n"
        "\n"
        "Detects SIFT3D keypoints and extracts their descriptors from an "
        "image.\n" 
        "\n"
        "Example: \n"
        " kpSift3D --keys keys.csv --desc desc.csv image.nii \n"
        "\n"
        "Output options: \n"
        " --keys [filename] \n"
        "       Specifies the output file name for the keypoints. \n"
        "       Supported file formats: .csv, .csv.gz \n"
        " --desc [filename] \n"
        "       Specifies the output file name for the descriptors. \n"
        "       Supported file formats: .csv, .csv.gz \n"
        " --draw [filename] \n"
        "       Draws the keypoints in image space. \n"
        "       Supported file formats: .dcm, .nii, .nii.gz, directory \n"
        "At least one of the output options must be specified. \n"
        "\n";

/* Print an error message */
static void err_msg(const char *msg) {
        SIFT3D_ERR("kpSift3D: %s \n"
                "Use \"kpSift3D --help\" for more information. \n", msg);
}

/* Report an unexpected error. */
static void err_msgu(const char *msg) {
        err_msg(msg);
        print_bug_msg();
}

/* CLI for 3D SIFT */
int main(int argc, char *argv[]) {

	Image im;
	SIFT3D sift3d;
	Keypoint_store kp;
	SIFT3D_Descriptor_store desc;
	char *im_path, *keys_path, *desc_path, *draw_path;
        int c, num_args;

        const struct option longopts[] = {
                {"keys", required_argument, NULL, KEYS},
                {"desc", required_argument, NULL, DESC},
                {"draw", required_argument, NULL, DRAW},
                {0, 0, 0, 0}
        };

        // Parse the GNU standard options
        switch (parse_gnu(argc, argv)) {
                case SIFT3D_HELP:
                        puts(help_msg);
                        print_opts_SIFT3D();
                        return 0;
                case SIFT3D_VERSION:
                        return 0;
                case SIFT3D_FALSE:
                        break;
                default:
                        err_msgu("Unexpected return from parse_gnu \n");
                        return 1;
        }

	// Initialize the SIFT data 
	if (init_SIFT3D(&sift3d)) {
		err_msgu("Failed to initialize SIFT data.");
                return 1;
        }

        // Parse the SIFT3D options and increment the argument list
        if ((argc = parse_args_SIFT3D(&sift3d, argc, argv, SIFT3D_FALSE)) < 0)
                return 1;

        // Parse the kpSift3d options
        opterr = 1;
        keys_path = desc_path = draw_path = NULL;
        while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
                switch (c) {
                        case KEYS:
                                keys_path = optarg;
                                break;
                        case DESC:
                                desc_path = optarg;
                                break;
                        case DRAW:
                                draw_path = optarg;
                                break;
                        case '?':
                        default:
                                return 1;
                }
        }

        // Ensure we have at least one output
        if (keys_path == NULL && desc_path == NULL && draw_path == NULL) {
                err_msg("No outputs specified.");
                return 1;
        }

        // Parse the required arguments
        num_args = argc - optind;
        if (num_args < 1) {
                err_msg("Not enough arguments.");
                return 1;
        } else if (num_args > 1) {
                err_msg("Too many arguments.");
                return 1;
        }
        im_path = argv[optind];

	// Initialize data 
	init_Keypoint_store(&kp); 
	init_SIFT3D_Descriptor_store(&desc); 
	init_im(&im);

	// Read the image
	if (im_read(im_path, &im)) {
		err_msg("Could not read image.");
                return 1;
        }

	// Extract keypoints
	if (SIFT3D_detect_keypoints(&sift3d, &im, &kp)) {
		err_msgu("Failed to detect keypoints.");
                return 1;
        }

        // Optionally write the keypoints 
        if (keys_path != NULL && write_Keypoint_store(keys_path, &kp)) {

                char msg[BUF_SIZE];

                snprintf(msg, BUF_SIZE, "Failed to write the keypoints to "
			"\"%s\"", keys_path);
                err_msg(msg);
                return 1;
        }

        // Optionally extract descriptors
        if (desc_path != NULL) {

                // Extract descriptors
	        if (SIFT3D_extract_descriptors(&sift3d, &kp,&desc)) {
                        err_msgu("Failed to extract descriptors.");
                        return 1;
                }

                // Write the descriptors
                if (write_SIFT3D_Descriptor_store(desc_path, &desc)) {

                        char msg[BUF_SIZE];

                        snprintf(msg, BUF_SIZE, "Failed to write the "
				"descriptors to \"%s\"", desc_path);
                        err_msg(msg);
                        return 1;
                }
        }

        // Optionally draw the keypoints
        if (draw_path != NULL) {

                Image draw;
                Mat_rm keys;

                // Initialize intermediates
                init_im(&draw);
                if (init_Mat_rm(&keys, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE))
                        err_msgu("Failed to initialize keys matrix");

                // Convert to matrices
                if (Keypoint_store_to_Mat_rm(&kp, &keys)) {
                        err_msgu("Failed to convert the keypoints to "
                                 "a matrix.");
                        return 1;
                }

                // Draw the points
                if (draw_points(&keys, SIFT3D_IM_GET_DIMS(&im), 1, &draw)) {
                        err_msgu("Failed to draw the points.");
                        return 1;
                }

                // Write the output
                if (im_write(draw_path, &draw)) {
                        
                        char msg[BUF_SIZE];

                        snprintf(msg, BUF_SIZE, "Failed to draw the keypoints "
				"to \"%s\"", draw_path);
                        err_msg(msg);
                        return 1;
                }

                // Clean up
                im_free(&draw);
        }

	return 0;
}


================================================
FILE: cli/regSift3D.c
================================================
/* -----------------------------------------------------------------------------
 * regSift3D.c
 * -----------------------------------------------------------------------------
 * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.
 * -----------------------------------------------------------------------------
 * This file contains the CLI to detect and match SIFT3D features between a pair
 * of images, and register one image to another.
 * -----------------------------------------------------------------------------
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include "immacros.h"
#include "imutil.h"
#include "sift.h"
#include "reg.h"

/* Option tags */
#define MATCHES 'a'
#define TRANSFORM 'b'
#define WARPED 'c'
#define CONCAT 'd'
#define KEYS 'e'
#define LINES 'f'
#define NN_THRESH 'g'
#define ERR_THRESH 'h'
#define NUM_ITER 'l'
#define TYPE 'm' 
#define RESAMPLE 'n'

/* Message buffer size */
#define BUF_SIZE 1024

/* Internal parameters */
const interp_type interp = LINEAR; // Interpolation used for the warped image
const tform_type type_default = AFFINE; // Default transformation type
        
/* Print the help message */
static void print_help() {
        printf(
        "Usage: regSift3D [source.nii] [reference.nii] \n"
        "\n"
        "Matches SIFT3D features. \n"
        "\n"
        "Supported input formats: \n"
        " .nii (nifti-1) \n"
        " .nii.gz (gzip-compressed nifti-1) \n"
        "\n"
        "Example: \n"
        " regSift3D --nn_thresh 0.8 --matches matches.csv src.nii ref.nii \n"
        "\n"
        "Output options: \n"
        " --matches [filename] - The feature matches. \n"
        "       Supported file formats: .csv, .csv.gz \n"
        " --transform [filename] - The transformation parameters. \n"
        "       Supported file formats: .csv, .csv.gz \n"
        " --warped [filename] -  The warped source image. \n"
        "       Supported file formats: .dcm, .nii, .nii.gz, directory \n"
        " --concat [filename] - Concatenated images, with source on the left \n"
        "       Supported file formats: .dcm, .nii, .nii.gz, directory \n"
        " --keys [filename] - Keypoints drawn in the concatenated image \n"
        "       Supported file formats: .dcm, .nii, .nii.gz, directory \n"
        " --lines [filename] - Lines drawn between matching keypoints \n"
        "       Supported file formats: .dcm, .nii, .nii.gz, directory \n"
        "At least one output option must be specified. \n"
        "\n"
        "Other options: \n"
        " --nn_thresh [value] - Matching threshold on the nearest neighbor \n"
        "       ratio, in the interval (0, 1]. (default: %.2f) \n"
        " --err_thresh [value] - RANSAC inlier threshold, in the interval \n"
        "       (0, inf). This is a threshold on the squared Euclidean \n"
        "       distance in real-world units. (default: %.1f) \n"
        " --num_iter [value] - Number of RANSAC iterations. (default: %d) \n"
        " --type [value] - Type of transformation to be applied. \n"
        "       Supported arguments: \"affine\" (default: affine) \n"
	" --resample - Internally resample the images to have the same \n"
	"	physical resolution. This is slow. Use it when the images \n"
	"	have very different resolutions, for example registering 5mm \n"
	"	to 1mm slices. \n"
        "\n",
        SIFT3D_nn_thresh_default, SIFT3D_err_thresh_default, 
        SIFT3D_num_iter_default);
        print_opts_SIFT3D();
}

/* Print an error message */
static void err_msg(const char *msg) {
        SIFT3D_ERR("regSift3D: %s \n"
                "Use \"regSift3D --help\" for more information. \n", msg);
}

/* Report an unexpected error. */
static void err_msgu(const char *msg) {
        err_msg(msg);
        print_bug_msg();
}

int main(int argc, char *argv[]) {

        Reg_SIFT3D reg;
        SIFT3D sift3d;
        Ransac ran;
        Image src, ref;
        Mat_rm match_src, match_ref;
        void *tform, *tform_arg;
        char *src_path, *ref_path, *warped_path, *match_path, *tform_path,
                *concat_path, *keys_path, *lines_path;
        tform_type type;
        int num_args, c, have_match, have_tform, resample;

        const struct option longopts[] = {
                {"matches", required_argument, NULL, MATCHES},
                {"transform", required_argument, NULL, TRANSFORM},
                {"warped", required_argument, NULL, WARPED},
                {"concat", required_argument, NULL, CONCAT},
                {"keys", required_argument, NULL, KEYS},
                {"lines", required_argument, NULL, LINES},
                {"nn_thresh", required_argument, NULL, NN_THRESH},
                {"err_thresh", required_argument, NULL, ERR_THRESH},
                {"num_iter", required_argument, NULL, NUM_ITER},
                {"type", required_argument, NULL, TYPE},
		{"resample", no_argument, NULL, RESAMPLE},
                {0, 0, 0, 0}
        };

        const char str_affine[] = "affine";

        // Parse the GNU standard options
        switch (parse_gnu(argc, argv)) {
                case SIFT3D_HELP:
                        print_help();
                        return 0;
                case SIFT3D_VERSION:
                        return 0;
                case SIFT3D_FALSE:
                        break;
                default:
                        err_msgu("Unexpected return from parse_gnu.");
                        return 1;
        }

        // Initialize the data
        init_im(&src);
        init_im(&ref);
        init_Reg_SIFT3D(&reg);
        init_Ransac(&ran);
        if (init_SIFT3D(&sift3d) ||
                init_Mat_rm(&match_src, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE) ||
                init_Mat_rm(&match_ref, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE)) {
                err_msgu("Failed basic initialization.");
                return 1;
        }

        // Initialize parameters to defaults        
        type = type_default;

        // Parse the SIFT3D options
        if ((argc = parse_args_SIFT3D(&sift3d, argc, argv, SIFT3D_FALSE)) < 0)
                return 1;
        if (set_SIFT3D_Reg_SIFT3D(&reg, &sift3d) ||
                set_Ransac_Reg_SIFT3D(&reg, &ran)) {
                err_msgu("Failed to save the SIFT3D or Ransac parameters.");
                return 1;
        }

        // Parse the remaining options 
        opterr = 1;
        have_match = have_tform = resample = SIFT3D_FALSE;
        match_path = tform_path = warped_path = concat_path = keys_path =
                lines_path = NULL;
        while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
                switch (c) {
                case MATCHES:
                        match_path = optarg;
                        have_match = SIFT3D_TRUE;
                        break;
                case TRANSFORM:
                        tform_path = optarg;
                        have_tform = SIFT3D_TRUE;
                        break;
                case WARPED:
                        warped_path = optarg;
                        have_tform = SIFT3D_TRUE;
                        break;
                case CONCAT:
                        concat_path = optarg;
                        have_match = SIFT3D_TRUE;
                        break;
                case KEYS:
                        keys_path = optarg;
                        have_match = SIFT3D_TRUE;
                        break;
                case LINES:
                        lines_path = optarg;
                        have_match = SIFT3D_TRUE;
                        break;
                case NN_THRESH:
                {
                        const double nn_thresh = atof(optarg);
                        if (set_nn_thresh_Reg_SIFT3D(&reg, nn_thresh)) {
                                err_msg("Invalid value for nn_thresh.");
                                return 1;
                        }
                        break;
                }
                case ERR_THRESH:
                {
                        const double err_thresh = atof(optarg);
                        if (set_err_thresh_Ransac(&ran, err_thresh)) {
                                err_msg("Invalid value for err_thresh.");
                                return 1;
                        }
                        break;
                }
                case NUM_ITER:
                {
                        const int num_iter = atoi(optarg);
                        if (set_num_iter_Ransac(&ran, num_iter)) {
                                err_msg("Invalid value for num_iter.");
                                return 1;
                        }
                        break;
                }
                case TYPE:
                        if (!strcmp(optarg, str_affine)) {
                                type = AFFINE;
                        } else {

                                char msg[BUF_SIZE];

                                snprintf(msg, BUF_SIZE,
                                        "Unrecognized transformation type: %s",
                                        optarg);
                                err_msg(msg);
                                return 1;
                        }
                        break;
                case RESAMPLE:
                        resample = SIFT3D_TRUE;
                        break;
                case '?':
                default:
                        return 1;
                }
        }

        // Ensure that at least one output was specified
        if (!have_match && !have_tform) {
                err_msg("No outputs were specified.");
                return 1;
        }

        // Parse the required arguments
        num_args = argc - optind;
        if (num_args < 2) {
                err_msg("Not enough arguments.");
                return 1;
        } else if (num_args > 2) {
                err_msg("Too many arguments.");
                return 1;
        }
        src_path = argv[optind];
        ref_path = argv[optind + 1];

        // Allocate memory for the transformation
        if ((tform = malloc(tform_type_get_size(type))) == NULL) {
                err_msg("Out of memory.");
                return 1;
        }

        // Initialize the transformation
        if (init_tform(tform, type))
                return 1;

        // Read the images
        if (im_read(src_path, &src)) {

                char msg[BUF_SIZE];

                snprintf(msg, BUF_SIZE, "Failed to read the source image "
			"\"%s\"", src_path);
                err_msg(msg);
                return 1;
        }
        if (im_read(ref_path, &ref)) {

                char msg[BUF_SIZE];

                snprintf(msg, BUF_SIZE, "Failed to read the reference image "
			"\"%s\"", ref_path);
                err_msg(msg);
                return 1;
        }

	// Process the images	
	tform_arg = have_tform ? tform : NULL;
	if (resample) {
		// Optionally register with resampling
		if (register_SIFT3D_resample(&reg, &src, &ref, LINEAR,
			tform_arg)) {
			err_msg("Failed to register the images with "
				 "resampling. \n");
			return 1;
		}
	} else {

		// Set the images
		if (set_src_Reg_SIFT3D(&reg, &src)) {
			err_msg("Failed to set the source image.");
			return 1;
		}
		if (set_ref_Reg_SIFT3D(&reg, &ref)) {
			err_msg("Failed to set the reference image.");
			return 1;
		}

		// Match the features, optionally registering the images 
		if (register_SIFT3D(&reg, tform_arg)) {
			err_msg("Failed to register the images.");
			return 1;
		}
	}

        // Convert the matches to matrices
        if (get_matches_Reg_SIFT3D(&reg, &match_src, &match_ref)) {
                err_msgu("Failed to convert matches to coordinates.");
                return 1;
        }

        // Write the outputs
        if (match_path != NULL) {

                Mat_rm matches;

                // Initialize intermediates
                init_Mat_rm(&matches, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE);

                // Form a combined matrix for both sets of matches
                if (concat_Mat_rm(&match_src, &match_ref, &matches, 1)) {
                        err_msgu("Failed to concatenate the matches.");
                        return 1;
                }

                if (write_Mat_rm(match_path, &matches)) {

                        char msg[BUF_SIZE];

                        snprintf(msg, BUF_SIZE, "Failed to write the matches "
				"\"%s\"", match_path);
                        err_msg(msg);
                        return 1;
                }

                // Clean up
                cleanup_Mat_rm(&matches);
        }
        if (tform_path != NULL && write_tform(tform_path, tform)) {

                char msg[BUF_SIZE];

                snprintf(msg, BUF_SIZE, "Failed to write the transformation "
			"parameters \"%s\"", tform_path);
                err_msg(msg);
                return 1;
        }

        // Optionally warp the source image
        if (warped_path != NULL) {

                Image warped;

                // Initialize intermediates
                init_im(&warped);

                // Set the output to the reference image size
                if (im_copy_dims(&ref, &warped)) {
                        err_msgu("Failed to resize the warped image.");
                        return 1;
                }

                // Warp
                if (im_inv_transform(tform, &src, interp, SIFT3D_FALSE, 
                        &warped)) {
                        err_msgu("Failed to warp the source image.");
                        return 1;
                }

                // Write the warped image
                if (im_write(warped_path, &warped)) {

                        char msg[BUF_SIZE];

                        snprintf(msg, BUF_SIZE, "Failed to write the warped "
				"image \"%s\"", warped_path);
                        err_msg(msg);
                        return 1;
                }

                // Clean up
                im_free(&warped);
        }

        // Optionally draw the matches
        if (concat_path != NULL || keys_path != NULL || lines_path != NULL) {

                Image concat, keys, lines;
                Mat_rm keys_src, keys_ref;

                // Set up the pointers for the images
                Image *const concat_arg = concat_path == NULL ? NULL : &concat;
                Image *const keys_arg = keys_path == NULL ? NULL : &keys;
                Image *const lines_arg = lines_path == NULL ? NULL : &lines;

                // Initialize intermediates
                init_im(&concat);
                init_im(&keys);
                init_im(&lines);
                if (init_Mat_rm(&keys_src, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE) ||
                        init_Mat_rm(&keys_ref, 0, 0, SIFT3D_DOUBLE, 
				SIFT3D_FALSE)) {
                        err_msgu("Failed to initialize keypoint matrices.");
                        return 1;
                }

                // Convert the keypoint coordinates to matrices
                if (SIFT3D_Descriptor_coords_to_Mat_rm(&reg.desc_src, 
                        &keys_src) ||
                    SIFT3D_Descriptor_coords_to_Mat_rm(&reg.desc_ref, 
                        &keys_ref)) {
                        err_msgu("Failed to convert the keypoints to "
                                 "matrices.");
                        return 1;
                }

                // Draw the matches
                if (draw_matches(&src, &ref, &keys_src, &keys_ref, 
                        &match_src, &match_ref, concat_arg, keys_arg,
                        lines_arg)) {
                        err_msgu("Failed to draw the matches.");
                        return 1;
                }

                // Optionally write a concatenated image
                if (concat_path != NULL && im_write(concat_path, &concat)) {

                        char msg[BUF_SIZE];

                        snprintf(msg, BUF_SIZE, "Failed to write the "
				"concatenated image \"%s\"", concat_path);
                        err_msg(msg);
                        return 1;
                }

                // Optionally write the keypoints
                if (keys_path != NULL && im_write(keys_path, &keys)) {

                        char msg[BUF_SIZE];

                        snprintf(msg, BUF_SIZE, "Failed to write the keypoint "
				"image \"%s\"", concat_path);
                        err_msg(msg);
                        return 1;
                }

                // Optionally write the matches
                if (lines_path != NULL && im_write(lines_path, &lines)) {

                        char msg[BUF_SIZE];

                        snprintf(msg, BUF_SIZE, "Failed to write the line "
				"image \"%s\"", concat_path);
                        err_msg(msg);
                        return 1;
                }

                // Clean up
                im_free(&concat);
                im_free(&keys);
                im_free(&lines);
        }

	return 0;
}


================================================
FILE: cmake/FindDCMTK.cmake
================================================
#.rst:
# FindDCMTK
# ---------
#
# Find DCMTK libraries and applications
#
# The module defines the following variables::
#
#  DCMTK_INCLUDE_DIRS  - Directories to include to use DCMTK
#  DCMTK_LIBRARIES     - Files to link against to use DCMTK
#  DCMTK_FOUND         - If false, don't try to use DCMTK
#  DCMTK_DIR           - (optional) Source directory for DCMTK
#
# Compatibility
# ^^^^^^^^^^^^^
#
# This module is able to find a version of DCMTK that does or does not export
# a *DCMTKConfig.cmake* file. It applies a two step process:
#
# * Step 1:  Attempt to find DCMTK version providing a *DCMTKConfig.cmake* file.
# * Step 2:  If step 1 failed, rely on *FindDCMTK.cmake* to set `DCMTK_*` variables details below.
#
#
# `Recent DCMTK
# <http://git.dcmtk.org/web?p=dcmtk.git;a=commit;h=662ae187c493c6b9a73dd5e3875372cebd0c11fe>`_
# provides a *DCMTKConfig.cmake* :manual:`package configuration file
# <cmake-packages(7)>`. To exclusively use the package configuration file
# (recommended when possible), pass the `NO_MODULE` option to
# :command:`find_package`. For example, `find_package(DCMTK NO_MODULE)`.
# This requires official DCMTK snapshot *3.6.1_20140617* or newer.
#
#
# Until all clients update to the more recent DCMTK, build systems will need
# to support different versions of DCMTK.
#
# On any given system, the following combinations of DCMTK versions could be
# considered:
#
# +--------+---------------------+-----------------------+-------------------+
# |        |   SYSTEM DCMTK      |      LOCAL DCMTK      |     Supported ?   |
# +--------+---------------------+-----------------------+-------------------+
# | Case A |   NA                |      [ ] DCMTKConfig  |         YES       |
# +--------+---------------------+-----------------------+-------------------+
# | Case B |   NA                |      [X] DCMTKConfig  |         YES       |
# +--------+---------------------+-----------------------+-------------------+
# | Case C |   [ ] DCMTKConfig   |      NA               |         YES       |
# +--------+---------------------+-----------------------+-------------------+
# | Case D |   [X] DCMTKConfig   |      NA               |         YES       |
# +--------+---------------------+-----------------------+-------------------+
# | Case E |   [ ] DCMTKConfig   |      [ ] DCMTKConfig  |         YES (*)   |
# +--------+---------------------+-----------------------+-------------------+
# | Case F |   [X] DCMTKConfig   |      [ ] DCMTKConfig  |         NO        |
# +--------+---------------------+-----------------------+-------------------+
# | Case G |   [ ] DCMTKConfig   |      [X] DCMTKConfig  |         YES       |
# +--------+---------------------+-----------------------+-------------------+
# | Case H |   [X] DCMTKConfig   |      [X] DCMTKConfig  |         YES       |
# +--------+---------------------+-----------------------+-------------------+
#
#  (*) See Troubleshooting section.
#
# Legend:
#
#   NA ...............: Means that no System or Local DCMTK is available
#
#   [ ] DCMTKConfig ..: Means that the version of DCMTK does NOT export a DCMTKConfig.cmake file.
#
#   [X] DCMTKConfig ..: Means that the version of DCMTK exports a DCMTKConfig.cmake file.
#
#
# Troubleshooting
# ^^^^^^^^^^^^^^^
#
# What to do if my project finds a different version of DCMTK?
#
# Remove DCMTK entry from the CMake cache per :command:`find_package`
# documentation.

#=============================================================================
# Copyright 2004-2009 Kitware, Inc.
# Copyright 2009-2010 Mathieu Malaterre <mathieu.malaterre@gmail.com>
# Copyright 2010 Thomas Sondergaard <ts@medical-insight.com>
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
#  License text for the above reference.)

#
# Written for VXL by Amitha Perera.
# Upgraded for GDCM by Mathieu Malaterre.
# Modified for EasyViz by Thomas Sondergaard.
#

set(_dcmtk_dir_description "The directory of DCMTK build or install tree.")

# Ensure that DCMTK_DIR is set to a reasonable default value
# so that DCMTK libraries can be found on a standard Unix distribution.
# It also overwrite the value of DCMTK_DIR after this one has been
# set by a successful discovery of DCMTK by the unpatched FindDCMTK.cmake module
# distributed with CMake (as of 0167cea)
if(NOT DCMTK_DIR OR DCMTK_DIR STREQUAL "/usr/include/dcmtk")
  set(DCMTK_DIR "/usr" CACHE PATH ${_dcmtk_dir_description} FORCE)
endif()

set(_SAVED_DCMTK_DIR ${DCMTK_DIR})

#
# Step1: Attempt to find a version of DCMTK providing a DCMTKConfig.cmake file.
#
if(NOT DCMTK_FIND_QUIETLY)
  message(STATUS "Trying to find DCMTK expecting DCMTKConfig.cmake")
endif()
find_package(DCMTK QUIET NO_MODULE)
if(DCMTK_FOUND
    AND NOT "x" STREQUAL "x${DCMTK_LIBRARIES}"
    AND NOT "x" STREQUAL "x${DCMTK_INCLUDE_DIRS}")

  if(NOT DCMTK_FIND_QUIETLY)
    message(STATUS "Trying to find DCMTK expecting DCMTKConfig.cmake - ok")
  endif()
  return()
else()
  if(NOT DCMTK_FIND_QUIETLY)
    message(STATUS "Trying to find DCMTK expecting DCMTKConfig.cmake - failed")
  endif()
endif()

if(NOT DCMTK_FIND_QUIETLY)
  message(STATUS "Trying to find DCMTK relying on FindDCMTK.cmake")
endif()

# Restore the value reset by the previous call to 'find_package(DCMTK QUIET NO_MODULE)'
set(DCMTK_DIR ${_SAVED_DCMTK_DIR} CACHE PATH ${_dcmtk_dir_description} FORCE)


#
# Step2: Attempt to find a version of DCMTK that does NOT provide a DCMTKConfig.cmake file.
#

# prefer DCMTK_DIR over default system paths like /usr/lib
if(DCMTK_DIR)
  set(CMAKE_PREFIX_PATH ${DCMTK_DIR}/lib ${CMAKE_PREFIX_PATH}) # this is given to FIND_LIBRARY or FIND_PATH
endif()

# Find all libraries, store debug and release separately
foreach(lib
    dcmpstat
    dcmsr
    dcmsign
    dcmtls
    dcmqrdb
    dcmnet
    dcmjpeg
    dcmimage
    dcmimgle
    dcmdata
    oflog
    ofstd
    ijg12
    ijg16
    ijg8
    )

  # Find Release libraries
  find_library(DCMTK_${lib}_LIBRARY_RELEASE
    ${lib}
    PATHS
    ${DCMTK_DIR}/${lib}/libsrc
    ${DCMTK_DIR}/${lib}/libsrc/Release
    ${DCMTK_DIR}/${lib}/Release
    ${DCMTK_DIR}/lib
    ${DCMTK_DIR}/lib/Release
    ${DCMTK_DIR}/dcmjpeg/lib${lib}/Release
    NO_DEFAULT_PATH
    )

  # Find Debug libraries
  find_library(DCMTK_${lib}_LIBRARY_DEBUG
    ${lib}${DCMTK_CMAKE_DEBUG_POSTFIX}
    PATHS
    ${DCMTK_DIR}/${lib}/libsrc
    ${DCMTK_DIR}/${lib}/libsrc/Debug
    ${DCMTK_DIR}/${lib}/Debug
    ${DCMTK_DIR}/lib
    ${DCMTK_DIR}/lib/Debug
    ${DCMTK_DIR}/dcmjpeg/lib${lib}/Debug
    NO_DEFAULT_PATH
    )

  mark_as_advanced(DCMTK_${lib}_LIBRARY_RELEASE)
  mark_as_advanced(DCMTK_${lib}_LIBRARY_DEBUG)

  # Add libraries to variable according to build type
  if(DCMTK_${lib}_LIBRARY_RELEASE)
    list(APPEND DCMTK_LIBRARIES optimized ${DCMTK_${lib}_LIBRARY_RELEASE})
  endif()

  if(DCMTK_${lib}_LIBRARY_DEBUG)
    list(APPEND DCMTK_LIBRARIES debug ${DCMTK_${lib}_LIBRARY_DEBUG})
  endif()

endforeach()

set(CMAKE_THREAD_LIBS_INIT)
if(DCMTK_oflog_LIBRARY_RELEASE OR DCMTK_oflog_LIBRARY_DEBUG)
  # Hack - Not having a DCMTKConfig.cmake file to read the settings from, we will attempt to
  # find the library in all cases.
  # Ideally, pthread library should be discovered only if DCMTK_WITH_THREADS is enabled.
  set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
  find_package(Threads)
endif()

if(CMAKE_THREAD_LIBS_INIT)
  list(APPEND DCMTK_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})
endif()

#
# SPECIFIC CASE FOR DCMTK BUILD DIR as DCMTK_DIR
# (as opposed to a DCMTK install dir)
# Have to find the source directory.
if(EXISTS ${DCMTK_DIR}/CMakeCache.txt)
          load_cache(${DCMTK_DIR} READ_WITH_PREFIX "EXT"
          DCMTK_SOURCE_DIR)
  if(NOT EXISTS ${EXTDCMTK_SOURCE_DIR})
    message(FATAL_ERROR
      "DCMTK build directory references
nonexistant DCMTK source directory ${EXTDCMTK_SOURCE_DIR}")
  endif()
endif()

set(DCMTK_config_TEST_HEADER osconfig.h)
set(DCMTK_dcmdata_TEST_HEADER dctypes.h)
set(DCMTK_dcmimage_TEST_HEADER dicoimg.h)
set(DCMTK_dcmimgle_TEST_HEADER dcmimage.h)
set(DCMTK_dcmjpeg_TEST_HEADER djdecode.h)
set(DCMTK_dcmnet_TEST_HEADER assoc.h)
set(DCMTK_dcmpstat_TEST_HEADER dcmpstat.h)
set(DCMTK_dcmqrdb_TEST_HEADER dcmqrdba.h)
set(DCMTK_dcmsign_TEST_HEADER sicert.h)
set(DCMTK_dcmsr_TEST_HEADER dsrtree.h)
set(DCMTK_dcmtls_TEST_HEADER tlslayer.h)
set(DCMTK_ofstd_TEST_HEADER ofstdinc.h)
set(DCMTK_oflog_TEST_HEADER oflog.h)
set(DCMTK_dcmjpls_TEST_HEADER djlsutil.h)

set(DCMTK_INCLUDE_DIR_NAMES)

foreach(dir
    config
    dcmdata
    dcmimage
    dcmimgle
    dcmjpeg
    dcmjpls
    dcmnet
    dcmpstat
    dcmqrdb
    dcmsign
    dcmsr
    dcmtls
    ofstd
    oflog)
  if(EXTDCMTK_SOURCE_DIR)
    set(SOURCE_DIR_PATH
      ${EXTDCMTK_SOURCE_DIR}/${dir}/include/dcmtk/${dir})
  endif()
  find_path(DCMTK_${dir}_INCLUDE_DIR
    ${DCMTK_${dir}_TEST_HEADER}
    PATHS
    ${DCMTK_DIR}/${dir}/include
    ${DCMTK_DIR}/${dir}
    ${DCMTK_DIR}/include/dcmtk/${dir}
    ${DCMTK_DIR}/${dir}/include/dcmtk/${dir}
    ${DCMTK_DIR}/include/${dir}
    ${SOURCE_DIR_PATH}
    )
  mark_as_advanced(DCMTK_${dir}_INCLUDE_DIR)
  list(APPEND DCMTK_INCLUDE_DIR_NAMES DCMTK_${dir}_INCLUDE_DIR)

  if(DCMTK_${dir}_INCLUDE_DIR)
    # add the 'include' path so eg
    #include "dcmtk/dcmimgle/dcmimage.h"
    # works
    get_filename_component(_include ${DCMTK_${dir}_INCLUDE_DIR} PATH)
    get_filename_component(_include ${_include} PATH)
    list(APPEND
      DCMTK_INCLUDE_DIRS
      ${DCMTK_${dir}_INCLUDE_DIR}
      ${_include})
  endif()
endforeach()

list(APPEND DCMTK_INCLUDE_DIRS ${DCMTK_DIR}/include)

if(WIN32)
  list(APPEND DCMTK_LIBRARIES netapi32 wsock32)
endif()

if(DCMTK_ofstd_INCLUDE_DIR)
  get_filename_component(DCMTK_dcmtk_INCLUDE_DIR
    ${DCMTK_ofstd_INCLUDE_DIR}
    PATH
    CACHE)
  list(APPEND DCMTK_INCLUDE_DIRS ${DCMTK_dcmtk_INCLUDE_DIR})
  mark_as_advanced(DCMTK_dcmtk_INCLUDE_DIR)
endif()

# Compatibility: This variable is deprecated
set(DCMTK_INCLUDE_DIR ${DCMTK_INCLUDE_DIRS})

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(DCMTK
  REQUIRED_VARS ${DCMTK_INCLUDE_DIR_NAMES} DCMTK_LIBRARIES
  FAIL_MESSAGE "Please set DCMTK_DIR and re-run configure")

# Workaround bug in packaging of DCMTK 3.6.0 on Debian.
# See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=637687
if(DCMTK_FOUND AND UNIX AND NOT APPLE)
  include(CheckCXXSourceCompiles)
  set(CMAKE_REQUIRED_FLAGS )
  set(CMAKE_REQUIRED_DEFINITIONS )
  set(CMAKE_REQUIRED_INCLUDES ${DCMTK_INCLUDE_DIRS})
  set(CMAKE_REQUIRED_LIBRARIES ${DCMTK_LIBRARIES})
  set(CMAKE_REQUIRED_QUIET ${DCMTK_FIND_QUIETLY})
  check_cxx_source_compiles("#include <dcmtk/config/osconfig.h>\n#include <dcmtk/ofstd/ofstream.h>\nint main(int,char*[]){return 0;}"
    DCMTK_HAVE_CONFIG_H_OPTIONAL
    )
  if(NOT DCMTK_HAVE_CONFIG_H_OPTIONAL)
    set(DCMTK_DEFINITIONS "HAVE_CONFIG_H")
  endif()
endif()

if(NOT DCMTK_FIND_QUIETLY)
  message(STATUS "Trying to find DCMTK relying on FindDCMTK.cmake - ok")
endif()


================================================
FILE: cmake/FindMingw.cmake
================================================
# FindNIFTI.cmake
#
# Finds the MinGW runtime dependencies:
#
#   -libgcc
#   -libstdc++
#   -libgfortran
#   -libquadmath
#
# The following variables will be exported:
#   MINGW_LIBRARIES - The dependencies

# Standard MinGW locations
set (MINGW_HINTS  "C:/TDM-GCC/")
set (MINGW_SUFFIXES "bin")


# Get the names of the libraries, depending on 32- or 64-bit
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
	message(STATUS "Using 64-bit MinGW...")
	set (MINGW_GCC_LIBRARY_NAME "libgcc_s_seh_64-1")
	set (MINGW_CXX_LIBRARY_NAME "libstdc++_64-6")
	set (MINGW_GFORTRAN_LIBRARY_NAME "libgfortran_64-3")
	set (MINGW_QUADMATH_LIBRARY_NAME "libquadmath_64-0")
elseif ("${CMAKE_SIZEOF_VOID_P}" EQUAL "4")
	message(STATUS "Using 32-bit MinGW...")
	set (MINGW_GCC_LIBRARY_NAME "libgcc_s_dw2-1")
	set (MINGW_CXX_LIBRARY_NAME "libstdc++-6")
	set (MINGW_GFORTRAN_LIBRARY_NAME "libgfortran-3")
	set (MINGW_QUADMATH_LIBRARY_NAME "libquadmath-0")
else ()
        message(FATAL_ERROR "Unrecognized byte width: ${CMAKE_SIZE_OF_VOID_P}")
endif ()


find_library (
	MINGW_GCC_LIBRARY
	NAMES ${MINGW_GCC_LIBRARY_NAME} 
	HINTS ${MINGW_HINTS}
	PATH_SUFFIXES ${MINGW_SUFFIXES} 
	DOC "MinGW C standard library"
)

find_library (
	MINGW_CXX_LIBRARY
	names ${MINGW_CXX_LIBRARY_NAME}
	HINTS ${MINGW_HINTS}
	PATH_SUFFIXES ${MINGW_SUFFIXES} 
	DOC "MinGW C++ standard library"	
)

find_library (
	MINGW_GFORTRAN_LIBRARY
	NAMES ${MINGW_GFORTRAN_LIBRARY_NAME}
	HINTS ${MINGW_HINTS}
	PATH_SUFFIXES ${MINGW_SUFFIXES}
	DOC "MinGW Fortran standard library"
)

find_library (
	MINGW_QUADMATH_LIBRARY
	NAMES ${MINGW_QUADMATH_LIBRARY_NAME}
	HINTS ${MINGW_HINTS}
	PATH_SUFFIXES ${MINGW_SUFFIXES}
	DOC "MinGW quadmath library"
)

set (MINGW_LIBRARIES ${MINGW_GCC_LIBRARY} ${MINGW_CXX_LIBRARY} ${MINGW_GFORTRAN_LIBRARY} ${MINGW_QUADMATH_LIBRARY})

# handle the QUIETLY and REQUIRED arguments and set PNG_FOUND to TRUE if
# all listed variables are TRUE
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(MINGW
	"Cannot find package MinGW."
	MINGW_GCC_LIBRARY
	MINGW_CXX_LIBRARY
	MINGW_GFORTRAN_LIBRARY
	MINGW_QUADMATH_LIBRARY
)

# these variables are only visible in 'advanced mode'
MARK_AS_ADVANCED(
	MINGW_GCC_LIBRARY
	MINGW_CXX_LIBRARY
	MINGW_GFORTRAN_LIBRARY
	MINGW_QUADMATH_LIBRARY
)


================================================
FILE: cmake/FindNIFTI.cmake
================================================
# - FindNIFTI.cmake
#
# Author: Thomas Proeger
# Modified by Blaine Rister
#
# This cmake find module looks for the header files and libraries from the
# 'libnifti1-dev' package.
#
# The following variables will be exported:
#
# NIFTI_INCLUDE_DIR - the directory that contains nifti1.h
# NIFTI_NIFTICDF_LIBRARY - the libnifticdf library file
# NIFTI_NIFTIIO_LIBRARY - the libniftiio library file
# NIFTI_ZNZ_LIBRARY - the libznz library file
# NIFTI_FOUND - TRUE if and only if ALL other variables have
# correct values.
#

# INCLUDE directory
FIND_PATH(NIFTI_INCLUDE_DIRS
NAMES nifti1.h
PATHS ${NIFTI_DIR}
HINTS "C:/Program Files (x86)/NIFTI" "C:/Program Files/NIFTI"
PATH_SUFFIXES include nifti include/nifti
DOC "The include directory containing nifti1.h"
)

# LIBRARY files
FIND_LIBRARY(NIFTI_NIFTICDF_LIBRARY
NAMES nifticdf
PATHS ${NIFTI_DIR}
HINTS "C:/Program Files (x86)/NIFTI" "C:/Program Files/NIFTI"
PATH_SUFFIXES lib
DOC "The library file libnifticdf.so"
)
FIND_LIBRARY(NIFTI_NIFTIIO_LIBRARY
NAMES niftiio
PATHS ${NIFTI_DIR}
HINTS "C:/Program Files (x86)/NIFTI" "C:/Program Files/NIFTI"
PATH_SUFFIXES lib
DOC "The library file libniftiiof.so"
)
FIND_LIBRARY(NIFTI_ZNZ_LIBRARY
NAMES znz
PATHS ${NIFTI_DIR}
HINTS "C:/Program Files (x86)/NIFTI" "C:/Program Files/NIFTI"
PATH_SUFFIXES lib
DOC "The library file libznz.so"
)

# Allow the user to specify the path to the NIFTI installation
get_filename_component (_NIFTI_DIR ${NIFTI_NIFTIIO_LIBRARY} DIRECTORY)
set (NIFTI_DIR ${_NIFTI_DIR} 
        CACHE PATH "The directory containing the NIFTI installation")

# The combined NIFTI libraries
set (NIFTI_LIBRARIES ${NIFTI_NIFTICDF_LIBRARY} ${NIFTI_NIFTIIO_LIBRARY} ${NIFTI_ZNZ_LIBRARY})

# handle the QUIETLY and REQUIRED arguments and set PNG_FOUND to TRUE if
# all listed variables are TRUE
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(NIFTI
"Cannot find package NIFTI. Did you install the package libnifti1-dev?"
NIFTI_INCLUDE_DIRS
NIFTI_NIFTICDF_LIBRARY
NIFTI_NIFTIIO_LIBRARY
NIFTI_ZNZ_LIBRARY
)
# these variables are only visible in 'advanced mode'
MARK_AS_ADVANCED(NIFTI_INCLUDE_DIRS
NIFTI_NIFTICDF_LIBRARY
NIFTI_NIFTIIO_LIBRARY
NIFTI_ZNZ_LIBRARY
)


================================================
FILE: cmake/FindOpenMP.cmake
================================================
#.rst:
# FindOpenMP
# ----------
#
# Finds OpenMP support
#
# This module can be used to detect OpenMP support in a compiler.  If
# the compiler supports OpenMP, the flags required to compile with
# OpenMP support are returned in variables for the different languages.
# The variables may be empty if the compiler does not need a special
# flag to support OpenMP.
#
# The following variables are set:
#
# ::
#
#    OpenMP_C_FLAGS - flags to add to the C compiler for OpenMP support
#    OpenMP_C_LIBRARIES - OpenMP C libraries to link against
#    OpenMP_CXX_FLAGS - flags to add to the CXX compiler for OpenMP support
#    OpenMP_CXX_LIBRARIES - OpenMP CXX libraries to link against
#    OpenMP_Fortran_FLAGS - flags to add to the Fortran compiler for OpenMP support
#    OpenMP_Fortran_LIBRARIES - OpenMP Fortran libraries to link against
#    OPENMP_FOUND - true if openmp is detected
#
# The following internal variables are set, if detected:
#    OpenMP_C_SPEC_DATE - specification date of OpenMP version of C compiler
#    OpenMP_CXX_SPEC_DATE - specification date of OpenMP version of CXX compiler
#    OpenMP_Fortran_SPEC_DATE - specification date of OpenMP version of Fortran compiler
#
#
#
# Supported compilers can be found at
# http://openmp.org/wp/openmp-compilers/
# Specification dates for each version can be found at
# http://openmp.org/wp/openmp-specifications/

#=============================================================================
# Copyright 2009 Kitware, Inc.
# Copyright 2008-2009 André Rigland Brodtkorb <Andre.Brodtkorb@ifi.uio.no>
# Copyright 2012 Rolf Eike Beer <eike@sf-mail.de>
# Copyright 2014 Nicolas Bock <nicolasbock@gmail.com>
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
#  License text for the above reference.)


set(_OPENMP_REQUIRED_VARS)
set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET})
set(CMAKE_REQUIRED_QUIET ${OpenMP_FIND_QUIETLY})

function(_OPENMP_FLAG_CANDIDATES LANG)
  set(OpenMP_FLAG_CANDIDATES
    #Empty, if compiler automatically accepts openmp
    " "
    #GNU
    "-fopenmp"
    #Microsoft Visual Studio
    "/openmp"
    #Intel windows
    "-Qopenmp"
    #PathScale, Intel
    "-openmp"
    #Sun
    "-xopenmp"
    #HP
    "+Oopenmp"
    #IBM XL C/c++
    "-qsmp"
    #Portland Group
    "-mp"
  )

  set(OMP_FLAG_GNU "-fopenmp")
  set(OMP_FLAG_HP "+Oopenmp")
  if(WIN32)
    set(OMP_FLAG_Intel "-Qopenmp")
  elseif(CMAKE_${LANG}_COMPILER_ID STREQUAL "Intel" AND
         "${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS "15.0.0.20140528")
    set(OMP_FLAG_Intel "-openmp")
  else()
    set(OMP_FLAG_Intel "-qopenmp")
  endif()
  set(OMP_FLAG_MSVC "/openmp")
  set(OMP_FLAG_PathScale "-openmp")
  set(OMP_FLAG_PGI "-mp")
  set(OMP_FLAG_SunPro "-xopenmp")
  set(OMP_FLAG_XL "-qsmp")
  set(OMP_FLAG_Cray " ")

  # Move the flag that matches the compiler to the head of the list,
  # this is faster and doesn't clutter the output that much. If that
  # flag doesn't work we will still try all.
  if(OMP_FLAG_${CMAKE_${LANG}_COMPILER_ID})
    list(REMOVE_ITEM OpenMP_FLAG_CANDIDATES "${OMP_FLAG_${CMAKE_${LANG}_COMPILER_ID}}")
    list(INSERT OpenMP_FLAG_CANDIDATES 0 "${OMP_FLAG_${CMAKE_${LANG}_COMPILER_ID}}")
  endif()

  set(OpenMP_${LANG}_FLAG_CANDIDATES "${OpenMP_FLAG_CANDIDATES}" PARENT_SCOPE)
endfunction()

# sample openmp source code to test
set(OpenMP_C_TEST_SOURCE
"
#include <omp.h>
int main() {
#ifdef _OPENMP
  return 0;
#else
  breaks_on_purpose
#endif
}
")

# same in Fortran
set(OpenMP_Fortran_TEST_SOURCE
  "
      program test
      use omp_lib
      integer :: n
      n = omp_get_num_threads()
      end program test
  "
  )

set(OpenMP_C_CXX_CHECK_VERSION_SOURCE
"
#include <stdio.h>
#include <omp.h>
const char ompver_str[] = { 'I', 'N', 'F', 'O', ':', 'O', 'p', 'e', 'n', 'M',
                            'P', '-', 'd', 'a', 't', 'e', '[',
                            ('0' + ((_OPENMP/100000)%10)),
                            ('0' + ((_OPENMP/10000)%10)),
                            ('0' + ((_OPENMP/1000)%10)),
                            ('0' + ((_OPENMP/100)%10)),
                            ('0' + ((_OPENMP/10)%10)),
                            ('0' + ((_OPENMP/1)%10)),
                            ']', '\\0' };
int main(int argc, char *argv[])
{
  printf(\"%s\\n\", ompver_str);
  return 0;
}
")

set(OpenMP_Fortran_CHECK_VERSION_SOURCE
"
      program omp_ver
      use omp_lib
      integer, parameter :: zero = ichar('0')
      integer, parameter :: ompv = openmp_version
      character, dimension(24), parameter :: ompver_str =&
      (/ 'I', 'N', 'F', 'O', ':', 'O', 'p', 'e', 'n', 'M', 'P', '-',&
         'd', 'a', 't', 'e', '[',&
         char(zero + mod(ompv/100000, 10)),&
         char(zero + mod(ompv/10000, 10)),&
         char(zero + mod(ompv/1000, 10)),&
         char(zero + mod(ompv/100, 10)),&
         char(zero + mod(ompv/10, 10)),&
         char(zero + mod(ompv/1, 10)), ']' /)
      print *, ompver_str
      end program omp_ver
")

function(_OPENMP_GET_SPEC_DATE LANG SPEC_DATE)
  set(WORK_DIR ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/FindOpenMP)
  if("${LANG}" STREQUAL "C")
    set(SRC_FILE ${WORK_DIR}/ompver.c)
    file(WRITE ${SRC_FILE} "${OpenMP_C_CXX_CHECK_VERSION_SOURCE}")
  elseif("${LANG}" STREQUAL "CXX")
    set(SRC_FILE ${WORK_DIR}/ompver.cpp)
    file(WRITE ${SRC_FILE} "${OpenMP_C_CXX_CHECK_VERSION_SOURCE}")
  else() # ("${LANG}" STREQUAL "Fortran")
    set(SRC_FILE ${WORK_DIR}/ompver.f90)
    file(WRITE ${SRC_FILE} "${OpenMP_Fortran_CHECK_VERSION_SOURCE}")
  endif()

  set(BIN_FILE ${WORK_DIR}/ompver_${LANG}.bin)
  try_compile(OpenMP_TRY_COMPILE_RESULT ${CMAKE_BINARY_DIR} ${SRC_FILE}
              COMPILE_DEFINITIONS ${OpenMP_${LANG}_FLAGS}
              LINK_LIBRARIES "${OpenMP_${LANG}_LIBRARIES}"
              COPY_FILE ${BIN_FILE})

  if(${OpenMP_TRY_COMPILE_RESULT})
    file(STRINGS ${BIN_FILE} specstr LIMIT_COUNT 1 REGEX "INFO:OpenMP-date")
    set(regex_spec_date ".*INFO:OpenMP-date\\[0*([^]]*)\\].*")
    if("${specstr}" MATCHES "${regex_spec_date}")
      set(${SPEC_DATE} "${CMAKE_MATCH_1}" PARENT_SCOPE)
    endif()
  endif()
endfunction()


# check c compiler
if(CMAKE_C_COMPILER_LOADED)
  # if these are set then do not try to find them again,
  # by avoiding any try_compiles for the flags
  if(OpenMP_C_FLAGS)
    unset(OpenMP_C_FLAG_CANDIDATES)
  else()
    _OPENMP_FLAG_CANDIDATES("C")
    include(CheckCSourceCompiles)
  endif()

  foreach(FLAG IN LISTS OpenMP_C_FLAG_CANDIDATES)
    set(SAFE_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
    set(CMAKE_REQUIRED_FLAGS "${FLAG}")
    unset(OpenMP_FLAG_DETECTED CACHE)
    if(NOT CMAKE_REQUIRED_QUIET)
      message(STATUS "Try OpenMP C flag = [${FLAG}]")
    endif()
    check_c_source_compiles("${OpenMP_C_TEST_SOURCE}" OpenMP_FLAG_DETECTED)
    set(CMAKE_REQUIRED_FLAGS "${SAFE_CMAKE_REQUIRED_FLAGS}")
    if(OpenMP_FLAG_DETECTED)
      set(OpenMP_C_FLAGS_INTERNAL "${FLAG}")
      break()
    endif()
  endforeach()

  set(OpenMP_C_FLAGS "${OpenMP_C_FLAGS_INTERNAL}"
    CACHE STRING "C compiler flags for OpenMP parallization")

  list(APPEND _OPENMP_REQUIRED_VARS OpenMP_C_FLAGS)
  unset(OpenMP_C_FLAG_CANDIDATES)

  if ("x${CMAKE_C_COMPILER_ID}" STREQUAL "xMSVC")
  elseif (MINGW)
    # Get the names of the C libraries, depending on 32- or 64-bit
    if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
        message(STATUS "Using 64-bit OpenMP...")
        set (OpenMP_C_LIBRARY_NAME "libgomp_64-1")
    elseif ("${CMAKE_SIZEOF_VOID_P}" EQUAL "4")
        message(STATUS "Using 32-bit OpenMP...")
        set (OpenMP_C_LIBRARY_NAME "libgomp-1")
    else ()
        message(FATAL_ERROR "Unrecognized byte width: ${CMAKE_SIZE_OF_VOID_P}")
    endif ()

    # Find the OpenMP C library for MinGW
    find_library(
        OpenMP_C_LIBRARY
        NAMES ${OpenMP_C_LIBRARY_NAME}
        HINTS "C:/TDM-GCC"
        PATH_SUFFIXES "bin"
        DOC "The OpenMP C library"
    )
  else ()
    set (OpenMP_C_LIBRARY "${OpenMP_C_FLAGS}" CACHE
      STRING "C library for OpenMP")
    list (APPEND _OPENMP_REQUIRED_VARS OpenMP_C_LIBRARY)
      set (OpenMP_CXX_LIBRARIES OpenMP_C_LIBRARIES)
  endif()
  set (OpenMP_C_LIBRARIES "${OpenMP_C_LIBRARY}")

  if (NOT OpenMP_C_SPEC_DATE)
    _OPENMP_GET_SPEC_DATE("C" OpenMP_C_SPEC_DATE_INTERNAL)
    set(OpenMP_C_SPEC_DATE "${OpenMP_C_SPEC_DATE_INTERNAL}" CACHE
      INTERNAL "C compiler's OpenMP specification date")
  endif()
endif()

# check cxx compiler
if(CMAKE_CXX_COMPILER_LOADED)
  # if these are set then do not try to find them again,
  # by avoiding any try_compiles for the flags
  if(OpenMP_CXX_FLAGS)
    unset(OpenMP_CXX_FLAG_CANDIDATES)
  else()
    _OPENMP_FLAG_CANDIDATES("CXX")
    include(CheckCXXSourceCompiles)

    # use the same source for CXX as C for now
    set(OpenMP_CXX_TEST_SOURCE ${OpenMP_C_TEST_SOURCE})
  endif()

  foreach(FLAG IN LISTS OpenMP_CXX_FLAG_CANDIDATES)
    set(SAFE_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
    set(CMAKE_REQUIRED_FLAGS "${FLAG}")
    unset(OpenMP_FLAG_DETECTED CACHE)
    if(NOT CMAKE_REQUIRED_QUIET)
      message(STATUS "Try OpenMP CXX flag = [${FLAG}]")
    endif()
    check_cxx_source_compiles("${OpenMP_CXX_TEST_SOURCE}" OpenMP_FLAG_DETECTED)
    set(CMAKE_REQUIRED_FLAGS "${SAFE_CMAKE_REQUIRED_FLAGS}")
    if(OpenMP_FLAG_DETECTED)
      set(OpenMP_CXX_FLAGS_INTERNAL "${FLAG}")
      break()
    endif()
  endforeach()

  set(OpenMP_CXX_FLAGS "${OpenMP_CXX_FLAGS_INTERNAL}"
    CACHE STRING "C++ compiler flags for OpenMP parallization")

  list(APPEND _OPENMP_REQUIRED_VARS OpenMP_CXX_FLAGS)
  unset(OpenMP_CXX_FLAG_CANDIDATES)
  unset(OpenMP_CXX_TEST_SOURCE)
  unset(OpenMP_C_TEST_SOURCE)

  if (NOT "x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC")
    set(OpenMP_CXX_LIBRARY "${OpenMP_CXX_FLAGS}" CACHE
      STRING "CXX library for OpenMP")
    list(APPEND _OPENMP_REQUIRED_VARS OpenMP_CXX_LIBRARY)
  endif()
  set(OpenMP_CXX_LIBRARIES "${OpenMP_CXX_LIBRARY}")

  if (NOT OpenMP_CXX_SPEC_DATE)
    _OPENMP_GET_SPEC_DATE("CXX" OpenMP_CXX_SPEC_DATE_INTERNAL)
    set(OpenMP_CXX_SPEC_DATE "${OpenMP_CXX_SPEC_DATE_INTERNAL}" CACHE
      INTERNAL "C++ compiler's OpenMP specification date")
  endif()
  unset(OpenMP_C_CXX_CHECK_VERSION_SOURCE)
endif()

# check Fortran compiler
if(CMAKE_Fortran_COMPILER_LOADED)
  # if these are set then do not try to find them again,
  # by avoiding any try_compiles for the flags
  if(OpenMP_Fortran_FLAGS)
    unset(OpenMP_Fortran_FLAG_CANDIDATES)
  else()
    _OPENMP_FLAG_CANDIDATES("Fortran")
    include(CheckFortranSourceCompiles)
  endif()

  foreach(FLAG IN LISTS OpenMP_Fortran_FLAG_CANDIDATES)
    set(SAFE_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
    set(CMAKE_REQUIRED_FLAGS "${FLAG}")
    unset(OpenMP_FLAG_DETECTED CACHE)
    if(NOT CMAKE_REQUIRED_QUIET)
      message(STATUS "Try OpenMP Fortran flag = [${FLAG}]")
    endif()
    check_fortran_source_compiles("${OpenMP_Fortran_TEST_SOURCE}" OpenMP_FLAG_DETECTED)
    set(CMAKE_REQUIRED_FLAGS "${SAFE_CMAKE_REQUIRED_FLAGS}")
    if(OpenMP_FLAG_DETECTED)
      set(OpenMP_Fortran_FLAGS_INTERNAL "${FLAG}")
      break()
    endif()
  endforeach()

  set(OpenMP_Fortran_FLAGS "${OpenMP_Fortran_FLAGS_INTERNAL}"
    CACHE STRING "Fortran compiler flags for OpenMP parallization")

  list(APPEND _OPENMP_REQUIRED_VARS OpenMP_Fortran_FLAGS)
  unset(OpenMP_Fortran_FLAG_CANDIDATES)
  unset(OpenMP_Fortran_TEST_SOURCE)

  set(OpenMP_Fortran_LIBRARY "${OpenMP_Fortran_FLAGS}" CACHE
    STRING "Fortran library for OpenMP")
  list(APPEND _OPENMP_REQUIRED_VARS OpenMP_Fortran_LIBRARY)
  set(OpenMP_Fortran_LIBRARIES "${OpenMP_Fortran_LIBRARY}")

  if (NOT OpenMP_Fortran_SPEC_DATE)
    _OPENMP_GET_SPEC_DATE("Fortran" OpenMP_Fortran_SPEC_DATE_INTERNAL)
    set(OpenMP_Fortran_SPEC_DATE "${OpenMP_Fortran_SPEC_DATE_INTERNAL}" CACHE
      INTERNAL "Fortran compiler's OpenMP specification date")
  endif()
  unset(OpenMP_Fortran_CHECK_VERSION_SOURCE)
endif()

set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE})

if(_OPENMP_REQUIRED_VARS)
  include(FindPackageHandleStandardArgs)

  find_package_handle_standard_args(OpenMP
                                    REQUIRED_VARS ${_OPENMP_REQUIRED_VARS})

  mark_as_advanced(${_OPENMP_REQUIRED_VARS})

  unset(_OPENMP_REQUIRED_VARS)
else()
  message(SEND_ERROR "FindOpenMP requires C or CXX language to be enabled")
endif()


================================================
FILE: cmake/SIFT3DPackage.cmake
================================================
################################################################################
# SIFT3DPackage.cmake
################################################################################
# Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.
################################################################################
# Build file to populate SIFT3D packages.
################################################################################

# Set the default package generator based on the OS 
if (WIN32)
        # NSIS for Windows
        set (_CPACK_GENERATOR "NSIS")
elseif (APPLE)
        # Bundle for Mac
        set (_CPACK_GENERATOR "BUNDLE")
elseif (UNIX)
        # Default to .tar.gz on Unix-like platforms
        set (_CPACK_GENERATOR "TGZ")

        # Override for specific Linux distributions
        if (CMAKE_SYSTEM_NAME MATCHES "Linux")
                # Try to read the Linux distribution
                if (EXISTS "/etc/issue")
                        file (READ "/etc/issue" LINUX_ISSUE)
                        # .deb for Ubuntu and Debian
                        if (${LINUX_ISSUE} MATCHES "Ubuntu" OR 
                                ${LINUX_ISSUE} MATCHES "Debian")
                                set (_CPACK_GENERATOR "DEB")
                        # .rpm for Fedora and OpenSuSE
                        elseif (${LINUX_ISSUE} MATCHES "Fedora" OR
                                ${LINUX_ISSUE} MATCHES "SUSE")
                                set (_CPACK_GENERATOR "RPM")
                        endif ()
                endif ()
        endif ()
else ()
        message (FATAL_ERROR "Unable to determine the default package generator for this operating system")
endif ()
set (CPACK_GENERATOR ${_CPACK_GENERATOR} CACHE STRING "The package generation 
        program")

# Global CPack variables
set (CPACK_PACKAGE_NAME "SIFT3D")
set (CPACK_PACKAGE_CONTACT "Blaine Rister blaine@stanford.edu")
set (CPACK_RESOURCE_FILE_LICENSE ${LICENSE_FILE})
set (CPACK_RESOURCE_FILE_README ${README_FILE})
set (CPACK_PACKAGE_VERSION ${SIFT3D_VERSION})
set (CPACK_PACKAGE_DESCRIPTION "Extracts keypoints and descriptors from 3D images. Also contians libraries for image processing and registration. Includes wrappers for Matlab.")

# Use the CMake install path, unless this is Windows, in which case this would
# break CPack
if (NOT WIN32)
        set (CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
endif ()

# Generator-specific CPack variables
if (CPACK_GENERATOR STREQUAL "NSIS")

        # Add the option to append SIFT3D to the path
        set (CPACK_NSIS_MODIFY_PATH ON)

elseif (CPACK_GENERATOR STREQUAL "BUNDLE")
        message(FATAL_ERROR "Bundle support not yet implemented")
        # TODO Do we need a bundle info file?
elseif (CPACK_GENERATOR STREQUAL "DEB")

        # Set the target platform in Debian terms
        if (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
                set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE amd64)
        elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "i686")
                set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE i386)
        else ()
                set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
        endif () 
        message ("-- Configured Debian package for architecture ${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")

        # Set the standard Debian package dependencies
        set (CPACK_DEBIAN_PACKAGE_DEPENDS "libc6, zlib1g, libnifti2, libdcmtk5, liblapack3")

        # Set compiler-specific Debian package dependencies
        if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR
               CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
                set (CPACK_DEBIAN_PACKAGE_DEPENDS 
                        "${CPACK_DEBIAN_PACKAGE_DEPENDS}, libgcc1")
        endif () 
elseif (CPACK_GENERATOR STREQUAL "RPM")
        message(FATAL_ERROR "RPM support not yet implemented")
        # TODO Add RPM package dependencies (see debian dependencies)
endif ()

# Finally include the CPack module
include (CPack)


================================================
FILE: doc/INSTALL_LINUX.md
================================================
# SIFT3D Linux Installation Instructions

Copyright (c) 2015-2018 Blaine Rister et al., see LICENSE for details.

# Installing the dependencies

This program requires the following external libraries:
- [zlib](http://www.zlib.net/)
- [LAPACK](http://www.netlib.org/lapack/)

In addition, the following libraries add optional support for reading and writing DICOM and NIFTI files:
- [DCMTK](http://dicom.offis.de/dcmtk.php.en)
- [nifticlib](http://sourceforge.net/projects/niftilib/files/nifticlib/)

The optional I/O support will be compiled if DMCTK and nifticlib are found on your system. Otherwise, the programs will run as normal, throwing an error if you try to use an unsupported file format.

On Ubuntu 16.04, the following command will install all dependencies:

	sudo apt-get install zlib1g-dev liblapack-dev libdcmtk-dev libnifti-dev

# Installing SIFT3D

## Installing from binaries

You can install SIFT3D in one of two ways, from binaries or from source. The easiest way is to install from binaries. Simply visit our [releases](https://github.com/bbrister/SIFT3D/releases) page, download the appropriate installer for your system, and run it. 

## Installing from source

This program has been successfully compiled and executed on the following Linux platforms:
- Ubuntu Linux 16.04, using GCC 5.4.0 and CMake 3.5.1.

This program requires the following tools to compile:
- [CMake](http://www.cmake.org)
- A suitable C/C++ compiler, such as GCC or Clang/LLVM.

On Ubuntu 16.04, the following command will install CMake and GCC:

        sudo apt-get install build-essential cmake

The following commands will generate Makefiles and use them to compile the binaries in a subdirectory called "build":

        cd /path/to/SIFT3D # Go to the directory where you downloaded SIFT3D 
	mkdir build # Make a directory to store the binaries
	cd build
	cmake .. # Create the Makefiles
	make # Compile the program

If for some reason CMake cannot find the dependencies, you can specify the paths manually with the CMake GUI. 

Use the following command to install the files:

	sudo make install

### Packaging

To create your own binary package, invoke CMake with the BUILD_PACKAGE variable set to ON. Then build the 'package' target using the Makefiles. For example,

        cmake .. -DBUILD_PACKAGE=ON
        make package

### Troubleshooting

If you are using an older version of Ubuntu, then you will need to update [DCMTK](http://dicom.offis.de/dcmtk.php.en) to the latest version before compiling SIFT3D.



================================================
FILE: doc/INSTALL_MAC.md
================================================
# SIFT3D Mac Installation Instructions

Copyright (c) 2015-2017 Blaine Rister et al., see LICENSE for details.

# Installing the dependencies

This program requires the following external libraries:
- [zlib](http://www.zlib.net/)
- [LAPACK](http://www.netlib.org/lapack/)

In addition, the following libraries add optional support for reading and writing DICOM and NIFTI files:
- [DCMTK](http://dicom.offis.de/dcmtk.php.en)
- [nifticlib](http://sourceforge.net/projects/niftilib/files/nifticlib/)

The required dependencies come pre-installed on Mac. The optional I/O support will be compiled if DMCTK and nifticlib are found on your system. Otherwise, the programs will run as normal, throwing an error if you try to use an unsupported file format.

As of version 10.10.5, you can install the optional I/O dependencies with [Homebrew](http://brew.sh/). First install Homebrew, if you haven't already, then run the following command:
 
        brew install dcmtk niftilib

# Installing SIFT3D

## Installing from binaries

We do not currently have binaries for SIFT3D on Mac. You must install from source.

## Installing from source

This program has been successfully compiled and executed on the following Mac platforms:
- Mac OSX 10.10.5, using Clang 6.1.0, LLVM 3.6.0 and CMake 3.3.1

This program requires the following tools to compile:
- [CMake](http://www.cmake.org)
- A suitable C/C++ compiler. such as GCC or Clang/LLVM.

Clang/LLVM comes pre-installed. Using [Homebrew](http://brew.sh), the following command will install CMake.

        brew install cmake

The following commands will generate Makefiles and use them to compile the binaries in a subdirectory called "build":

        cd /path/to/SIFT3D # Go to the directory where you downloaded SIFT3D 
	mkdir build # Make a directory to store the binaries
	cd build
	cmake .. # Create the Makefiles
	make # Compile the program

*Note: If you are compiling without DICOM support, leave DCMTK_DIR blank. If you installed DCMTK to some place other than /usr/local, set DCMTK_DIR to that path in the above CMake command.*

If CMake cannot find the dependencies, you can specify the paths manually with the CMake GUI.

Use the following command to install the files:

	sudo make install



================================================
FILE: doc/INSTALL_WINDOWS.md
================================================
# SIFT3D Windows Installation Instructions

Copyright (c) 2015-2018 Blaine Rister et al., see LICENSE for details.

## Installing from binaries

You can install SIFT3D in one of two ways, from binaries or from source. The easiest way is to install from binaries. Simply visit our [releases](https://github.com/bbrister/SIFT3D/releases) page, download the appropriate installer for your system, and run it.

## Installing from source

*This is a difficult process on Windows, recommended only for advanced users.*

This program has been successfully compiled and executed on the following Windows platforms:
* Windows 10 64-bit, using [TDM-GCC](http://tdm-gcc.tdragon.net/) 5.10 and CMake 3.3.1
* Windows 8 64-bit, using the same
* Windows 7 64-bit, using the same

In addition, the compiled C libraries have been linked to on the following platforms:
* Windows 7 64-bit, using Visual Studio 2012

Please follow the instructions below to compile and install SIFT3D from source.

1. Install [CMake](http://www.cmake.org).

2. Install MinGW via [TDM-GCC](http://tdm-gcc.tdragon.net/)
	1. Download the installer
	2. Run the installer and remember to select the GCC packages C, C++, Fortran and OpenMP
		* OpenMP is for optional multithreading, the rest are mandatory

3. Use [gnumex](http://gnumex.sourceforge.net/documentation.html#L131) to hack Matlab to compile MEX files with MinGW. *This needed only for the Matlab toolbox. Non-Matlab users can skip this step.*
	1. Download and extract gnumex
	2. Run Matlab as an administrator
	3. Run the "gnumex" program from within Matlab
	4. Make sure the required paths are detected
	5. Generate the files
	6. Run "mex -setup" from within Matlab. If all goes well, GCC will show up as an option. Follow the prompt to set up MEX with GCC.

4. Install [LAPACK for Windows](http://icl.cs.utk.edu/lapack-for-windows/lapack/index.html#libraries). *(Note: Any other BLAS/LAPACK package should work as well. I have had success compiling* [OpenBlas](http://www.openblas.net/) *with MinGW and CMake.)*
	1. Download the binaries for MinGW and your verison of Windows (lapack.dll, blas.dll)
	2. Move lapack.dll and blas.dll to the TDM-GCC/bin directory

5. Install [zlib](http://zlib.net/).
	1. Download and extract the most recent version
	2. Use the CMake GUI to generate MinGW Makefiles
		1. Set the source folder to the location of zlib
		2. Generate -> MinGW Makefiles
	3. Compile and install with MinGW
		1. cd to the build directory
		2. "mingw32-make"
		3. "mingw32-make install" (may require administrator privileges)

6. Install [nifticlib](http://sourceforge.net/projects/niftilib/files/nifticlib/). *This step is needed only for NIFTI I/O. Users not needing to read and write NIFTI files can skip this step.*
	1. Download and extract the newest version
	2. Generate MinGW Makefiles with CMake
		1. Set the source folder to the location of nifticlib
		2. Generate -> MinGW Makefiles
                        * You may have to set the variable ZLIB_LIBRARY to the directory where zlib was installed.
	3. Compile and install with MinGW
		1. cd to build directory
		2. "mingw32-make"
		3. "mingw32-make install" (may require administrator privileges)

7. Install [DCMTK](http://www.dcmtk.org). *This step is needed only for DICOM I/O. Users not needing to read and write DICOM files can skip this step.* 
	1. Download and extract the newest version. If there is a binary installer for your version of Windows, you can use that and skip the following sections. As of 10/06/2015, there is no binary for 64-bit Windows, so we compiled DCMTK from source. *Note: We could not compile DCMTK 3.6.0 on Windows with CMake and MinGW. Instead, we used a snapshot of version 3.6.1, retrieved on 09/24/2015.*
	2. Generate MinGW Makefiles with CMake
		1. Set the source folder to the location of DCMTK
		2. Generate -> MinGW Makefiles
	3. Compile and install with MinGW
		1. cd to build directory
		2. "mingw32-make"
		3. "mingw32-make install" (may require administrator privileges)

8. Install SIFT3D.
	1. Download this repository
	2. Generate MinGW Makefiles with CMake
		1. Navigate to SIFT3D
		2. Select "MinGW Makefiles"
                3. Configure
                4. Configuration may fail if CMake cannot find the dependencies. If so, set the appropriate variables and configure again.
                        1. If configuration fails due to zlib, check 'advanced' and set all ZLIB_* variables to the appropriate paths. For example, set ZLIB_LIBRARY (or ZLIB_LIBRARY_RELEASE) to libzlib.dll and ZLIB_INCLUDE_DIR to the include directory of your zlib installation.
		        2. If configuration fails due to DCMTK, set DCMTK_DIR to the install location of DCMTK.
		        3. If configuration fails due to NIFTI, set NIFTI_DIR to the install location of nifticlib.
		5. *Note: this step applies only to users compiling the optional Matlab toolbox.* Ensure that the Matlab libraries are .dll's and not .lib's. Manually edit the paths for Matlab_MEX_LIBRARY, Matlab_MX_LIBRARY, MWLAPACK_LIBRARY, and MWBLAS_LIBRARY, so that "libmex.lib" is changed to "libmex.dll", etc. This requires locating these files within your Matlab installation. Check the "bin" directories for .dll's.
		6. Generate -> MinGW Makefiles
	3. Compile and install with MinGW
		1. cd to the build directory
		2. "mingw32-make"
		3. "mingw32-make install" (may require administrator privileges)
### Packaging

To create your own binary package, set the BUILD_PACKAGE variable to ON in the CMake GUI. Then build the 'package' target using the Makefiles. For example,

        mingw32-make package


## Caveats

This program was originally developed for Unix-like platforms, so some features have been disabled in the Windows version. 

* By default, the command line interface is not compiled on Windows systems. If you wish to compile it, you can set the CMake variable BUILD_CLI to ON. This is not officially supported, and the resulting executables may not function correctly. The good news is that you can still access SIFT3D through the C libraries and wrappers for other languages.

* There are problems writing .nii.gz files in Windows. Note that all other image formats still work, including .nii without gzip compression. From the Matlab wrapper function imWrite3D, we work around this issue by first writing the .nii file, then compressing with a Matlab function call. However, you may experience issues when writing a .nii.gz file from the C library function im_write.


================================================
FILE: examples/CMakeLists.txt
================================================
################################################################################
# Copyright (c) 2015 Blaine Rister et al., see LICENSE for details.
################################################################################
# Build file for example programs.
################################################################################

# The data directory
set (DATA_DIR "data")

# The name of the examples subdirectory, where compiled examples are found. On Windows, this is simply bin, as the executables must be in the same directory as the DLLs. On other platforms, this is a separate "examples" directory.
if (WIN32)
	set (EXAMPLES_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
else ()
	set (EXAMPLES_DIR "examples")
	set (EXAMPLES_PATH "${CMAKE_BINARY_DIR}/${EXAMPLES_DIR}")
endif ()

# Copy the example data
configure_file("${DATA_DIR}/1.nii.gz" "${EXAMPLES_PATH}/1.nii.gz" COPYONLY)
configure_file("${DATA_DIR}/2.nii.gz" "${EXAMPLES_PATH}/2.nii.gz" COPYONLY)

# The example programs
add_executable (featuresC featuresC.c)
target_link_libraries (featuresC PUBLIC sift3D imutil)

add_executable (registerC registerC.c)
target_link_libraries (registerC PUBLIC reg sift3D imutil)

add_executable (ioC ioC.c)
target_link_libraries (ioC PUBLIC imutil)

# Send all files to the examples subdirectory 
set_target_properties(featuresC registerC ioC
        PROPERTIES
        ARCHIVE_OUTPUT_DIRECTORY ${EXAMPLES_PATH}
        LIBRARY_OUTPUT_DIRECTORY ${EXAMPLES_PATH}
        RUNTIME_OUTPUT_DIRECTORY ${EXAMPLES_PATH}
)


================================================
FILE: examples/featuresC.c
================================================
/* -----------------------------------------------------------------------------
 * featuresC.c
 * -----------------------------------------------------------------------------
 * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.
 * -----------------------------------------------------------------------------
 * Example of extracting and visualizing SIFT3D keypoints and descriptors using
 * the C API.
 */

/* System headers */
#include <stdio.h>

/* SIFT3D headers */
#include "immacros.h"
#include "imutil.h"
#include "sift.h"

/* Example file paths */
const char *im_path = "1.nii.gz";
const char *keys_path = "1_keys.csv.gz";
const char *desc_path = "1_desc.csv.gz";
const char *draw_path = "1_keys.nii.gz";

/* This illustrates how to use SIFT3D within a function, and free all memory
 * afterwards. */
int demo(void) {

	Image im, draw;
        Mat_rm keys;
	SIFT3D sift3d;
	Keypoint_store kp;
	SIFT3D_Descriptor_store desc;

        // Initialize the intermediates
        init_Keypoint_store(&kp);
        init_SIFT3D_Descriptor_store(&desc);
        init_im(&im);
        init_im(&draw);
        if (init_Mat_rm(&keys, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE))
                return 1; 

        if (init_SIFT3D(&sift3d)) {
                cleanup_Mat_rm(&keys);
                return 1;
        }

        // Read the image
        if (im_read(im_path, &im))
                goto demo_quit;

        // Detect keypoints
	if (SIFT3D_detect_keypoints(&sift3d, &im, &kp))
                goto demo_quit;

        // Write the keypoints to a file
        if (write_Keypoint_store(keys_path, &kp))
                goto demo_quit;
        printf("Keypoints written to %s. \n", keys_path);

        // Extract descriptors
        if (SIFT3D_extract_descriptors(&sift3d, &kp, &desc))
                goto demo_quit;

        // Write the descriptors to a file
        if (write_SIFT3D_Descriptor_store(desc_path, &desc))
                goto demo_quit;
        printf("Descriptors written to %s. \n", desc_path);

        // Convert the keypoints to a matrix 
        if (Keypoint_store_to_Mat_rm(&kp, &keys))
                goto demo_quit;

        // Draw the keypoints
        if (draw_points(&keys, SIFT3D_IM_GET_DIMS(&im), 1, &draw))
                goto demo_quit;

        // Write the drawn keypoints to a file
        if (im_write(draw_path, &draw))
                goto demo_quit;
        printf("Keypoints drawn in %s. \n", draw_path);

        // Clean up
        im_free(&im);
        im_free(&draw);
        cleanup_Mat_rm(&keys);
        cleanup_SIFT3D(&sift3d);
        cleanup_Keypoint_store(&kp);
        cleanup_SIFT3D_Descriptor_store(&desc);

        return 0;

demo_quit:
        // Clean up and return an error
        im_free(&im);
        im_free(&draw);
        cleanup_Mat_rm(&keys);
        cleanup_SIFT3D(&sift3d);
        cleanup_Keypoint_store(&kp);
        cleanup_SIFT3D_Descriptor_store(&desc);

        return 1;
}

int main(void) {

        int ret;

        // Do the demo
        ret = demo();

        // Check for errors
        if (ret != 0) {
                fprintf(stderr, "Fatal demo error, code %d. \n", ret);
                return 1;
        }

        return 0;
}


================================================
FILE: examples/featuresMatlab.m
================================================
% featuresMatlab.m
%
% This script shows how to extract SIFT3D features from a volumetric image.
%
% Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.

% Load the image
[im, units] = imRead3D('data/1.nii.gz');

% Detect keypoints
keys = detectSift3D(im, 'units', units);

% Extract descriptors
[desc, coords] = extractSift3D(keys);

% Clear MEX memory
clear mex

================================================
FILE: examples/ioC.c
================================================
/* -----------------------------------------------------------------------------
 * ioC.c
 * -----------------------------------------------------------------------------
 * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.
 * -----------------------------------------------------------------------------
 * Short example of file I/O, converting a NIFTI file to DICOM.
 */

/* System headers */
#include <stdio.h>

/* SIFT3D headers */
#include "imutil.h"

/* Example file paths */
const char in_path[] = "1.nii.gz";
const char out_path[] = "1dicom";

/* This illustrates how to use images within a function, and free all memory
 * afterwards. */
int demo(void) {

	Image im;

        // You must call this before any Image struct can be used
        init_im(&im);

        // Read the NIFTI image as input
        if (im_read(in_path, &im))
                goto demo_quit;

        // Write it to a DICOM series
        if (im_write(out_path, &im))
                goto demo_quit;

        // Clean up
        im_free(&im);

        return 0;

demo_quit:
        // Clean up and return an error
        im_free(&im);
        return 1;
}

int main(void) {

        int ret;

        // Do the demo
        ret = demo();

        // Check for errors
        if (ret != 0) {
                fprintf(stderr, "Fatal demo error, code %d. \n", ret);
                return 1;
        }

        return 0;
}


================================================
FILE: examples/ioMatlab.m
================================================
% ioMatlab
%
% Example of using file IO functions with SIFT3D
%
% Copyright (c) 2015 Blaine Rister et al., see LICENSE for details.

% Load the from a NIFTI file
imNifti = imRead3D('data/1.nii.gz');

% Save it as a multi-slice DICOM file
imWrite3D('1.dcm', imNifti);

================================================
FILE: examples/manualFeaturesMatlab.m
================================================
% manualFeaturesMatlab.m
%
% This script shows how to manually define keypoints in a volumetric image,
% and extract SIFT3D feature descriptors at those locations.
%
% Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.

% Load the image
[im, units] = imRead3D('data/1.nii.gz');

% Define a keypoint
keys = keypoint3D([100 100 20]);

% Assign its orientation
[keys, conf] = orientation3D(keys, im, units);

% Extract a descriptor
[desc, coords] = extractSift3D(keys, im, units);


================================================
FILE: examples/registerC.c
================================================
/* -----------------------------------------------------------------------------
 * registerC.c
 * -----------------------------------------------------------------------------
 * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.
 * -----------------------------------------------------------------------------
 * Example of registering two images using the C API.
 */

/* System headers */
#include <stdio.h>

/* SIFT3D headers */
#include "imutil.h"
#include "sift.h"
#include "reg.h"

/* Example file paths */
const char *ref_path = "1.nii.gz";
const char *src_path = "2.nii.gz";
const char *match_path = "1_2_matches.nii.gz";
const char *warped_path = "2_warped.nii.gz";
const char *affine_path = "1_2_affine.csv.gz";

/* This illustrates how to use Reg_SIFT3D within a function, freeing all memory
 * afterwards. */
int demo(void) {

	Image src, ref, warped;
        Reg_SIFT3D reg;
        Affine affine;

        // Initialize the intermediates
        init_im(&src);
        init_im(&ref);
        init_im(&warped);
        if (init_tform(&affine, AFFINE))
                return 1;

        if (init_Reg_SIFT3D(&reg)) {
                cleanup_tform(&affine);
                return 1;
        }

        // Read the images
        if (im_read(src_path, &src) ||
                im_read(ref_path, &ref))
                goto demo_quit;

        // Set the images
        if (set_src_Reg_SIFT3D(&reg, &src) ||
                set_ref_Reg_SIFT3D(&reg, &ref))
                goto demo_quit;

        // Match features and solve for an affine transformation
        if (register_SIFT3D(&reg, &affine))
                goto demo_quit;

        // Write the transformation to a file 
        if (write_tform(affine_path, &affine))
                goto demo_quit;

        // Warp the source image
        if (im_inv_transform(&affine, &src, LINEAR, SIFT3D_TRUE, &warped))
                goto demo_quit;

        // Write the warped image to a file
        if (im_write(warped_path, &warped))
                goto demo_quit;

        // Clean up
        im_free(&src);
        im_free(&ref);
        im_free(&warped);
        cleanup_Reg_SIFT3D(&reg);
        cleanup_tform(&affine);

        return 0;

demo_quit:
        // Clean up and return an error
        im_free(&src);
        im_free(&ref);
        im_free(&warped);
        cleanup_Reg_SIFT3D(&reg);
        cleanup_tform(&affine);

        return 1;
}

int main(void) {

        int ret;

        // Do the demo
        ret = demo();

        // Check for errors
        if (ret != 0) {
                fprintf(stderr, "Fatal demo error, code %d. \n", ret);
                return 1;
        }

        return 0;
}


================================================
FILE: examples/registerMatlab.m
================================================
% registerMatlab
%
% Example of image registration in Matlab
%
% Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.

% Load the images
[src, srcUnits] = imRead3D('data/1.nii.gz');
[ref, refUnits] = imRead3D('data/2.nii.gz');

% Register
[A, matchSrc, matchRef] = registerSift3D(src, ref, 'srcUnits', ...
    srcUnits, 'refUnits', refUnits);

% Clear MEX memory
clear mex

================================================
FILE: imutil/CMakeLists.txt
================================================
################################################################################
# Copyright (c) 2015-2017 Blaine Rister et al., see LICENSE for details.
################################################################################
# Build file for the image processing utility library.
################################################################################

# Find BLAS
find_package (BLAS QUIET)
if (NOT BLAS_FOUND)
    message (FATAL_ERROR "BLAS not found. Please set the variable "
	    "BLAS_LIBRARIES to the location of the BLAS library on "
	    "your system.")
endif() 

# Find LAPACK
find_package (LAPACK QUIET)
if (NOT LAPACK_FOUND)
    message (FATAL_ERROR "LAPACK not found. Please set the variable "
	    "LAPACK_LIBRARIES to the location of the LAPACK library on your "
	    "system.")
endif ()

# Find DCMTK
find_package (DCMTK QUIET)
if (DCMTK_FOUND)

        # Add platform-specific DCMTK dependencies
        if (WIN32)
                # Add ws2_32 
                list (APPEND DCMTK_LIBRARIES "ws2_32")
        endif ()

        # Get the base DCMTK include dir. Note that DCMTK_DIR is set incorrectly on 
        # Linux, so we must add additional paths
        find_path (DCMTK_BASE_INCLUDE_PARENT_DIR "include/dcmtk"
                PATHS ${DCMTK_DIR} "${DCMTK_config_INCLUDE_DIR}/../../..")
        set (DCMTK_BASE_INCLUDE_DIR "${DCMTK_BASE_INCLUDE_PARENT_DIR}/include" 
                CACHE PATH "DCMTK include directory")

        if (_DCMTK_BASE_INCLUDE_PARENT_DIR STREQUAL
                "DCMTK_BASE_INCLUDE_PARENT_DIR-NOTFOUND")
                message (FATAL_ERROR "Failed to find the DCMTK include "
                        "directory. Please set the variable "
                        "DCMTK_BASE_INCLUDE_DIR to <DCMTK-INSTALL>/include, "
                        "or disable DICOM support by setting WITH_DICOM to "
                        "false.")
        endif ()

        # Add the base dir to the DCMTK include paths
        list(APPEND DCMTK_INCLUDE_DIRS ${DCMTK_BASE_INCLUDE_DIR})

        # Check if there is a configuration file for DCMTK
        find_file(DCMTK_CONFIG_FILE 
                NAMES "cfunix.h" "cfwin32.h"
                PATHS ${DCMTK_config_INCLUDE_DIR}
                NO_CMAKE_PATH
                NO_CMAKE_ENVIRONMENT_PATH
                NO_SYSTEM_ENVIRONMENT_PATH)
        if (DCMTK_CONFIG_FILE STREQUAL "DCMTK_CONFIG_FILE-NOTFOUND")
                set (DCMTK_HAVE_CONFIG_FILE false)
        else ()
                set (DCMTK_HAVE_CONFIG_FILE true)
        endif ()

	message (STATUS "Found DCMTK.")

elseif (WITH_DICOM)
	message (FATAL_ERROR "DCMTK not found. Please set the variable "
                "DCMTK_DIR to the location of DCMTK on your system, or disable"
                "DICOM support by setting WITH_DICOM to false.")
else ()
	message (STATUS "DCMTK not found. Compiling without DICOM support. "
                "To enable DICOM support, set the variable DCMTK_DIR to the "
                "location of DCMTK on your system.")
endif ()
set (WITH_DICOM ${DCMTK_FOUND} CACHE BOOL "Compile DICOM I/O support")
if (WITH_DICOM)
	message (STATUS "Building with DICOM support.")
endif ()

# Optionally find NIFTI 
find_package (NIFTI QUIET)
if (NIFTI_FOUND)
	message (STATUS "Found NIFTI.")
elseif (WITH_NIFTI)
	message (FATAL_ERROR "Failed to find nifticlib. Please set the "
                "variable NIFTI_DIR to the location of DCMTK on your system, "
                "or disable NIFTI support by setting WITH_NIFTI to false.")
else()
	message (STATUS "Failed to find nifticlib. Compiling without NIFTI "
                "support. To enable NIFTI support, set the variable NIFTI_DIR "
                "to the location of NIFTI on your system.")
endif()
set (WITH_NIFTI ${NIFTI_FOUND} CACHE BOOL "Compile NIFTI I/O support")
if (WITH_NIFTI)
	message (STATUS "Building with NIFTI support.")
endif ()

# Find iconv on Mac
if (APPLE)
	find_library (ICONV_LIBRARY NAMES iconv libiconv libiconv-2 c REQUIRED)
endif ()

# Find MinGW dependencies
if (MINGW)
        find_package (MINGW REQUIRED)
endif ()

# Macro to optionally add DCMTK to a target
macro (add_DICOM arg)
        if (WITH_DICOM)
                target_include_directories(${arg} PRIVATE ${DCMTK_INCLUDE_DIRS})
                target_link_libraries (${arg} PRIVATE ${DCMTK_LIBRARIES})
                target_compile_definitions(${arg} PRIVATE "SIFT3D_WITH_DICOM")
                if (DCMTK_HAVE_CONFIG_FILE)
                        target_compile_definitions (${arg} PRIVATE 
                                "HAVE_CONFIG_H")
                endif ()
        endif ()
endmacro ()

# Macro to optionally add NIFTI to a target
macro (add_NIFTI arg)
        if (WITH_NIFTI)
                target_compile_definitions(${arg} PRIVATE "SIFT3D_WITH_NIFTI")
                target_include_directories(${arg} PRIVATE ${NIFTI_INCLUDE_DIRS})
                target_link_libraries (${arg} PRIVATE ${NIFTI_LIBRARIES})
        endif ()
endmacro ()

# Format the compiler definitions
set (IMUTIL_DEFINITIONS "SIFT3D_VERSION_NUMBER=${SIFT3D_VERSION}")

# Check if the compiler uses strndup and strnlen
check_function_exists (strnlen HAVE_STRNLEN)
if (HAVE_STRNLEN)
        list (APPEND IMUTIL_DEFINITIONS "SIFT3D_HAVE_STRNLEN")
endif ()
check_function_exists (strndup HAVE_STRNDUP)
if (HAVE_STRNDUP)
        list (APPEND IMUTIL_DEFINITIONS "SIFT3D_HAVE_STRNDUP")
endif ()

# Compile imutil
add_library (imutil SHARED imutil.c nifti.c dicom.cpp)
target_include_directories (imutil PUBLIC 
                $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
                $<INSTALL_INTERFACE:${INSTALL_INCLUDE_DIR}>
)
target_link_libraries (imutil PRIVATE ${LAPACK_LIBRARIES})
target_compile_definitions (imutil PRIVATE ${IMUTIL_DEFINITIONS})
add_DICOM(imutil)
add_NIFTI(imutil)
install (FILES imtypes.h immacros.h imutil.h kernels.cl
        DESTINATION ${INSTALL_INCLUDE_DIR})

# Link to system libraries
target_link_libraries(imutil PRIVATE ${ZLIB_LIBRARIES} ${M_LIBRARY})
target_include_directories(imutil PRIVATE ${ZLIB_INCLUDE_DIR})
if (APPLE)
	target_link_libraries(imutil PUBLIC ${ICONV_LIBRARY})
endif ()

# Configure the installation
install (TARGETS imutil 
        EXPORT SIFT3D-targets 
        RUNTIME DESTINATION ${INSTALL_BIN_DIR} 
	LIBRARY DESTINATION ${INSTALL_LIB_DIR} 
	ARCHIVE DESTINATION ${INSTALL_LIB_DIR}
)

# OS-specific installation
if (WIN32)

	# Make a list of all external dependencies
	set (DEPS ${LAPACK_LIBRARIES} ${BLAS_LIBRARIES} ${ZLIB_LIBRARIES} 
                ${OpenMP_C_LIBRARIES} ${MINGW_LIBRARIES})
        if (WITH_DICOM)
                list (APPEND DEPS ${DCMTK_LIBRARIES})
        endif ()
        if (WITH_NIFTI)
                list (APPEND DEPS ${NIFTI_LIBRARIES})
        endif ()

	function (get_runtime_deps ARG_DEPS ARG_RUNTIME_DEPS)

		# Process each dependency, adding a runtime dependency if 
		# necessary
		set (${ARG_RUNTIME_DEPS} "")
		foreach (DEP IN LISTS ${ARG_DEPS})

			# Get the file extension	
			get_filename_component (DEP_EXT ${DEP} EXT)		

			# Process shared libraries
			if (DEP_EXT STREQUAL ".dll")
				list (APPEND ${ARG_RUNTIME_DEPS} ${DEP})
			# Process MinGW import libraries
			elseif (DEP_EXT STREQUAL ".dll.a")

				# Extract the filename, parent and grandparent directories
				get_filename_component (DEP_NAME ${DEP} NAME)
				get_filename_component (DEP_DIR ${DEP} DIRECTORY)
				get_filename_component (DEP_DIR_DIR ${DEP_DIR} DIRECTORY)

				# Get the name of the .dll version
				string (REGEX REPLACE ".dll.a$" ".dll" DEP_DLL_NAME ${DEP_NAME})

				# Find the corresponding .dll
				string (REGEX REPLACE ".dll" "_DLL" DEP_DLL_VAR ${DEP_DLL_NAME})
				find_file (${DEP_DLL_VAR} ${DEP_DLL_NAME} 
					PATHS ${DEP_DIR} ${DEP_DIR_DIR}
					PATH_SUFFIXES "bin" "lib")
				if (${DEP_DLL_VAR} STREQUAL "${DEP_DLL_NAME}-NOTFOUND")
					message (FATAL_ERROR 
						"Failed to find runtime dependency ${DEP_DLL_NAME}")
				endif ()

				# The .dll, not the .dll.a, becomes a runtime dependency
				list (APPEND ${ARG_RUNTIME_DEPS} ${${DEP_DLL_VAR}})
			endif ()

		endforeach ()

		# Set the return value
		set (${ARG_RUNTIME_DEPS} ${${ARG_RUNTIME_DEPS}} PARENT_SCOPE)

	endfunction ()

	# Convert dependencies to runtime dependencies
	get_runtime_deps (DEPS RUNTIME_DEPS)

	# Copy the runtime dependencies to the Windows DLL
	file (COPY ${RUNTIME_DEPS} DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})

	# Add the runtime dependencies to the Windows installer
	install (FILES ${RUNTIME_DEPS} DESTINATION ${INSTALL_BIN_DIR})

endif ()

# If Matlab was found, compile a copy for use with Matlab libraries
if (BUILD_Matlab)

        add_library (meximutil SHARED imutil.c nifti.c dicom.cpp)
        target_compile_definitions (meximutil PUBLIC "SIFT3D_MEX")
        target_compile_definitions (meximutil PRIVATE  ${IMUTIL_DEFINITIONS})

        target_include_directories (meximutil PUBLIC 
                $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
                $<INSTALL_INTERFACE:${INSTALL_INCLUDE_DIR}>
        )
        target_include_directories(meximutil PUBLIC ${Matlab_INCLUDE_DIRS})
        target_include_directories (meximutil PRIVATE 
                ${ZLIB_INCLUDE_DIR})
        target_link_libraries (meximutil PUBLIC ${Matlab_LIBRARIES})
        target_link_libraries (meximutil PRIVATE ${Matlab_MWLAPACK_LIBRARY} 
                ${Matlab_MWBLAS_LIBRARY} ${ZLIB_LIBRARIES} ${M_LIBRARY})
        add_DICOM(meximutil)
        add_NIFTI(meximutil)

        set_target_properties (meximutil 
                PROPERTIES 
                ARCHIVE_OUTPUT_DIRECTORY ${BUILD_TOOLBOX_DIR}
                LIBRARY_OUTPUT_DIRECTORY ${BUILD_TOOLBOX_DIR}
                RUNTIME_OUTPUT_DIRECTORY ${BUILD_TOOLBOX_DIR}
        )

        install (TARGETS meximutil 
                RUNTIME DESTINATION ${INSTALL_TOOLBOX_DIR}
                LIBRARY DESTINATION ${INSTALL_TOOLBOX_DIR}
                ARCHIVE DESTINATION ${INSTALL_TOOLBOX_DIR}
        )

        if (WIN32)

		# The toolbox has the same dependencies except for BLAS and LAPACK
		set (TOOLBOX_DEPS ${DEPS})
		list (REMOVE_ITEM TOOLBOX_DEPS 
			${LAPACK_LIBRARIES} ${BLAS_LIBRARIES}
		)

		get_runtime_deps (TOOLBOX_DEPS TOOLBOX_RUNTIME_DEPS)
		file (COPY ${TOOLBOX_RUNTIME_DEPS} DESTINATION 
			${BUILD_TOOLBOX_DIR})
		install (FILES ${TOOLBOX_RUNTIME_DEPS} 
			DESTINATION ${INSTALL_TOOLBOX_DIR})
        endif ()
endif ()

# Add the code snippets
add_subdirectory (templates)


================================================
FILE: imutil/dicom.cpp
================================================
/* -----------------------------------------------------------------------------
 * dicom.cpp 
 * -----------------------------------------------------------------------------
 * Copyright (c) 2015-2017 Blaine Rister et al., see LICENSE for details.
 * -----------------------------------------------------------------------------
 * C-language wrapper for the DCMTK library.
 * -----------------------------------------------------------------------------
 */

/* SIFT3D includes */
#include "imutil.h"
#include "immacros.h"
#include "dicom.h"

#ifndef SIFT3D_WITH_DICOM
/* Return error messages if this was not compiled with DICOM support. */ 

static int dcm_error_message() {
        SIFT3D_ERR("dcm_error_message: SIFT3D was not compiled with DICOM "
                "support!\n");
        return SIFT3D_WRAPPER_NOT_COMPILED;
}

int read_dcm(const char *path, Image *const im) {
        return dcm_error_message();
}

int read_dcm_dir(const char *path, Image *const im) {
        return dcm_error_message();
}

int write_dcm(const char *path, const Image *const im, 
        const Dcm_meta *const meta, const float max_val) {
        return dcm_error_message();
}

int write_dcm_dir(const char *path, const Image *const im, 
        const Dcm_meta *const meta) {
        return dcm_error_message();
}

#else

/*----------------Include the very picky DCMTK----------------*/
#include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */

#define INCLUDE_CSTDIO
#define INCLUDE_CSTRING
#include "dcmtk/ofstd/ofstdinc.h"

#ifdef HAVE_GUSI_H
#include <GUSI.h>
#endif

#include "dcmtk/dcmdata/dctk.h"          /* for various dcmdata headers */
#include "dcmtk/dcmdata/cmdlnarg.h"      /* for prepareCmdLineArgs */
#include "dcmtk/dcmdata/dcuid.h"         /* for dcmtk version name */

#include "dcmtk/ofstd/ofconapp.h"        /* for OFConsoleApplication */
#include "dcmtk/ofstd/ofcmdln.h"         /* for OFCommandLine */

#include "dcmtk/oflog/oflog.h"           /* for OFLogger */

#include "dcmtk/dcmimgle/dcmimage.h"     /* for DicomImage */
#include "dcmtk/dcmimgle/diutils.h"     /* for DIPixel */
#include "dcmtk/dcmimage/diregist.h"     /* include to support color images */
#include "dcmtk/dcmdata/dcrledrg.h"      /* for DcmRLEDecoderRegistration */

#include "dcmtk/dcmjpeg/djdecode.h"      /* for dcmjpeg decoders */
#include "dcmtk/dcmjpeg/dipijpeg.h"      /* for dcmimage JPEG plugin */

#include "dcmtk/dcmjpeg/djencode.h" /* for JPEG encoding */
#include "dcmtk/dcmjpeg/djrplol.h"  /* for DJ_RPLossless */

#include "dcmtk/dcmseg/segment.h" /* Dicom segmentations */

#ifdef WITH_ZLIB
#include <zlib.h>          /* for zlibVersion() */
#endif
/*---------------------------------------------------------*/

/* Other includes */
#include <algorithm>
#include <memory>
#include <vector>
#include <cmath>
#include <cfloat>
#include <cstdlib>
#include <stdint.h>
#include <dirent.h>

/* Macro to call a C++ function and catch any exceptions it throws,
 * returning SIFT3D_FAILURE when an exception is caught. The return value is
 * stored in ret. */
#define CATCH_EXCEPTIONS(ret, tag, fun, ...) \
        try { \
                ret = (fun)( __VA_ARGS__ ); \
        } catch (std::exception &e) { \
                SIFT3D_ERR("%s: %s\n", tag, e.what()); \
                ret = SIFT3D_FAILURE; \
        } catch (...) { \
                SIFT3D_ERR("%s: unexpected exception \n", tag); \
                ret = SIFT3D_FAILURE; \
        } \

/* Format strings for DICOM tags */
static const char *pixelSpacingFmt = "%lf\\%lf"; // Pixel Spacing
static const char *imPosPatientFmt = "%f\\%f\\%f"; // ImagePositionPatient
static const char *imOriPatientFmt = "%f\\%f\\%f\\%f\\%f\\%f"; 
        // ImageOrientationPatient

/* File separator in string form */
const std::string sepStr(1, SIFT3D_FILE_SEP);

/* Dicom parameters */
static const unsigned int dcm_bit_width = 8; // Bits per pixel

/* DICOM metadata defaults */
static const char *default_patient_name = "DefaultSIFT3DPatient";
static const char *default_series_descrip = "Series generated by SIFT3D";
static const char *default_patient_id = "DefaultSIFT3DPatientID";
static const char default_instance_num = 1;

/* Types of DICOMs which necessitate special processing */
enum image_type {
        DSO,
        PET,
        OTHER
};

/* Helper declarations */
class Dicom;
static bool isLittleEndian(void);
static void default_Dcm_meta(Dcm_meta *const meta);
static int load_file(const char *path, DcmFileFormat &fileFormat);
static int read_dcm_cpp(const char *path, Image *const im);
static int read_dcm_img(const Dicom &dicom, Image *const im);
static int read_dso(const char *imDir, Dicom &dso, 
                            Image *const mask);
static int cvec_max_abs(const Cvec *v, float *const val, int *const pos);
static int read_dcm_dir_meta(const char *path, std::vector<Dicom> &dicoms);
static int read_dcm_dir_cpp(const char *path, Image *const im);
static int write_dcm_cpp(const char *path, const Image *const im,
        const Dcm_meta *const meta, const float max_val);
static int write_dcm_dir_cpp(const char *path, const Image *const im,
        const Dcm_meta *const meta);
static void set_meta_defaults(const Dcm_meta *const meta, 
        Dcm_meta *const meta_new);
static int dcm_resize_im(const std::vector<Dicom> &dicoms, Image *const im);
static int write_subvolume(const Dicom &dicom, Image *const main, 
        const int mainOffset, Image *const sub, const int start, 
        const int end);

/* Helper class to store DICOM data. */
class Dicom {
private:
        int axes[2]; // Data axes, e.g. {0, 1} means x, y
        int axisSigns[2]; // Axis signs, negative means go backwards
        std::string filename; // DICOM file name
        std::string classUID;
        std::string seriesUID; // Series UID 
        std::string instanceUID; // Instance UID
        double sortCoord; // Coordinate by which the series is sorted. Usually z
        double ux, uy, uz; // Voxel spacing in real-world coordinates
        int nx, ny, nz, nc; // Image dimensions
        int sortAxis; // The axis corresponding to sortCoord
        bool valid; // Data validity 

public:

        /* Data is initially invalid */
        Dicom() : valid(false) {};

        ~Dicom() {};

        /* Load a file */
        Dicom(const char *filename, const int defaults3D=0);

        /* Get the multiplier to convert a PET image to SUV */
        double PET_SUV_multiplier(void) const;

        /* Get a dimension by index */
        int getDim(const int idx) const {
                switch (idx) {
                case 0:
                        return getNx();
                case 1:
                        return getNy();
                case 2:
                        return getNz();
                }

                return -1;
        }

        /* Get a unit by index */
        int getUnit(const int idx) const {
                switch (idx) {
                case 0:
                        return getUx();
                case 1:
                        return getUy();
                case 2:
                        return getUz();
                }

                return -1;
        }

        /* Get the image axis by index */
        int getAxes(const int idx) const {
                return idx < 0 || idx > 2 ? -1 : axes[idx];
        }

        /* Get the axis sign by index */
        int getAxisSign(const int idx) const {
                return idx < 0 || idx > 2 ? -1 : axisSigns[idx];
        }

        /* Get the x-dimension */
        int getNx(void) const {
                return nx;
        }

        /* Get the y-dimension */
        int getNy(void) const {
                return ny;
        }

        /* Get the z-dimension */
        int getNz(void) const {
                return nz;
        }

        /* Get the number of channels */
        int getNc(void) const {
                return nc;
        } 

        /* Get the sorting axis */
        int getSortAxis(void) const {
                return sortAxis;
        }

        /* Get the sorting coordinate (usually z) */
        double getSortCoord(void) const {
                return sortCoord;
        }

        /* Get the units in the sorting dimension */
        int getSortUnit(void) const {
                return getUnit(getSortAxis());
        }

        /* Get the x-spacing */
        double getUx(void) const {
                return ux;
        }

        /* Get the y-spacing */
        double getUy(void) const {
                return uy;
        }

        /* Get the z-spacing */
        double getUz(void) const {
                return uz;
        }

        /* Check whether or not the data is valid */
        bool isValid(void) const {
                return valid;
        }

        /* Get the file name */
        const char *name(void) const {
                return filename.c_str();
        }

        /* Sort by z position */
        bool operator < (const Dicom &dicom) const {
                return getSortCoord() < dicom.getSortCoord();
        }

        /* Check if another DICOM file is from the same series */
        bool eqSeries(const Dicom &dicom) const {
                return !seriesUID.compare(dicom.seriesUID);
        }

        /* Check for a matching SOPInstanceUID */
        bool eqInstance(const char *uid) const {
                return !instanceUID.compare(uid);
        }

        /* Return an image type, based on the classUID */
        enum image_type getType() const {
                if (!classUID.compare(UID_SegmentationStorage)) {
                        return DSO;
                } else if (
                        !classUID.compare(
                                UID_PositronEmissionTomographyImageStorage) ||
                        !classUID.compare(
                                UID_LegacyConvertedEnhancedPETImageStorage) ||
                        !classUID.compare(UID_EnhancedPETImageStorage)) {
                        return PET;
                } else {
                        return OTHER;
                }
        }
};

/* Read a DICOM file with DCMTK. */
static int load_file(const char *path, DcmFileFormat &fileFormat) {

        // Load the image as a DcmFileFormat 
        OFCondition status = fileFormat.loadFile(path);
        if (status.bad()) {
                SIFT3D_ERR("load_file: failed to read DICOM file %s (%s)\n",
                        path, status.text());
                return SIFT3D_FAILURE;
        }

        return SIFT3D_SUCCESS;
}

/* Get the single maximum component from a Cvec, by absolue value. Returns the 
 * element and its position. Returns SIFT3D_SUCCESS if the maximum is unique,
 * SIFT3D_FAILURE otherwise. */
static int cvec_max_abs(const Cvec *v, float *const val, int *const pos) {

        int k;
        const float vals[] = {v->x, v->y, v->z};

        // Find the maximum
        float maxDiff = 0.f;
        float maxAbsVal = -1;
        for (k = 0; k < 3; k++) {

                const float thisVal = vals[k];
                const float thisAbsVal = fabsf(thisVal);
        
                // Check for maximum. If so, write into val and pos
                if (thisAbsVal < maxAbsVal)
                        continue;
                maxDiff = thisAbsVal - maxAbsVal;
                maxAbsVal = thisAbsVal;
                *val = thisVal;
                *pos = k;
        }

        // Test for unique maxima
        return maxDiff > 1e-2 ? SIFT3D_SUCCESS : SIFT3D_FAILURE;
}

/* Load the data from a DICOM file. If defaults3D is true, substitute default
 * values for 3D positioning information. Use this to load a single file even
 * if key metadata is missing, e.g. for 2D images. */
Dicom::Dicom(const char *path, const int defaults3D) : filename(path), 
        valid(false) {

        // Read the file
        DcmFileFormat fileFormat;
        if (load_file(path, fileFormat))
                return;
        DcmDataset *const data = fileFormat.getDataset();

        // Get the SOPClass UID
        const char *classUIDStr;
        OFCondition status = data->findAndGetString(DCM_SOPClassUID, 
                                                    classUIDStr);
        if (status.bad() || classUIDStr == NULL) {
                SIFT3D_ERR("Dicom.Dicom: failed to get SOPClassUID "
                        "from file %s (%s)\n", path, status.text());
                return;
        }
        classUID = std::string(classUIDStr); 

        // Get the series UID 
        const char *seriesUIDStr;
        status = data->findAndGetString(DCM_SeriesInstanceUID, seriesUIDStr);
        if (status.bad() || seriesUIDStr == NULL) {
                SIFT3D_ERR("Dicom.Dicom: failed to get SeriesInstanceUID "
                        "from file %s (%s)\n", path, status.text());
                return;
        }
        seriesUID = std::string(seriesUIDStr); 

        // Get the instance UID 
        const char *instanceUIDStr;
        status = data->findAndGetString(DCM_SOPInstanceUID, instanceUIDStr);
        if (status.bad() || instanceUIDStr == NULL) {
                SIFT3D_ERR("Dicom.Dicom: failed to get SOPInstanceUID "
                        "from file %s (%s)\n", path, status.text());
                return;
        }
        instanceUID = std::string(instanceUIDStr); 

        // Get the z coordinate
        if (getType() == DSO) {
                // DSOs don't always have coordinates
                sortCoord = -1;

                // DSOs don't always have units
                ux = uy = uz = -1.0;
        } else {
#if 0
                // Read the patient position
                const char *patientPosStr;
                status = data->findAndGetString(DCM_PatientPosition, 
                                                patientPosStr);
                if (status.bad() || patientPosStr == NULL) {
                        SIFT3D_ERR("Dicom.Dicom: failed to get " 
                                "PatientPosition from file %s (%s)\n", path, 
                                status.text());
                        return;
                }

                // Interpret the patient position to give the sign of the z axis
                double zSign;
                switch (patientPosStr[0]) {
                case 'H':
                        zSign = -1.0;
                        break;
                case 'F':
                        zSign = 1.0;
                        break;
                default:
                        SIFT3D_ERR("Dicom.Dicom: unrecognized patient "
                                "position: %d\n", patientPosStr);
                        return;
                }
#else
                //TODO: Is this needed?
                const double zSign = 1.0;
#endif

                // Read the image position patient vector
                const char *imPosPatientStr;
                status = data->findAndGetString(DCM_ImagePositionPatient, 
                        imPosPatientStr);
                if (status.bad() || imPosPatientStr == NULL) {
                        SIFT3D_ERR("Dicom.Dicom: failed to get "
                        "ImagePositionPatient from file %s (%s)\n, defaulting"
                        " to zeros. \n", path, 
                        status.text());
                        if (defaults3D) {
                                imPosPatientStr = "0\\0\\0";
                        } else return;
                }

                // Parse the image position patient vector
                Cvec imPos;
                if (sscanf(imPosPatientStr, imPosPatientFmt, &imPos.x, &imPos.y, 
                        &imPos.z) != 3) {
                        SIFT3D_ERR("Dicom.Dicom: failed to parse "
                                "ImagePositionPatient value %s from file %s\n", 
                                imPosPatientStr, path);
                        if (defaults3D) {
                                imPos = {0, 0, 0};
                        } else return;
                }

                // Read the image orientation patient vector
                const char *imOriPatientStr; 
                status = data->findAndGetString(DCM_ImageOrientationPatient,
                        imOriPatientStr);
                if (status.bad() || imOriPatientStr == NULL) {
                        SIFT3D_ERR("Dicom.Dicom: failed to get "
                        "ImageOrientationPatient from file %s (%s)\n", path, 
                        status.text());
                        return;
                }

                // Parse the image orientation patient vectors
                Cvec imOri1, imOri2;
                if (sscanf(imOriPatientStr, imOriPatientFmt, 
                        &imOri1.x, &imOri1.y, &imOri1.z, &imOri2.x, &imOri2.y,
                        &imOri2.z) != 6) {
                        SIFT3D_ERR("Dicom.Dicom: failed to parse "
                                "ImageOrientationPatient value %s from file %s\n", 
                                imOriPatientStr, path);
                        return;
                }

                // Take the cross-product of orientation vectors to get the 
                // image normal
                Cvec normal; 
                SIFT3D_CVEC_CROSS(&imOri1, &imOri2, &normal);

                // Project the patient position on the image normal, to get the
                // sorting coordinate
                sortCoord = SIFT3D_CVEC_DOT(&imPos, &normal);

                // Get the image axes
                float oriVals[2];
                if (cvec_max_abs(&imOri1, oriVals, axes) || 
                        cvec_max_abs(&imOri2, oriVals + 1, axes + 1)) {
                        SIFT3D_ERR("Dicom.Dicom: unsupported format for "
                                "imageOrientationPatient %s from file %s\n", 
                                imOriPatientStr, path);
                        return;
                }

                // Get the sorting axis
                for (int k = 0; k < 3; k++) {
                        if (axes[0] == k || axes[1] == k)
                                continue;
                        sortAxis = k;
                        break;
                }

                // Compute the signs of the axes
                for (int k = 0; k < 2; k++) {
                        axisSigns[k] = oriVals[k] >= 0 ? 1 : -1;
                }

                // Read the pixel spacing
                double spacing[2];
                const char *pixelSpacingStr;
                status = data->findAndGetString(DCM_PixelSpacing, 
                                                pixelSpacingStr);
                if (status.bad()) {
                        SIFT3D_ERR("Dicom.Dicom: failed to get pixel spacing "
                                "from file %s (%s)\n", path, status.text());
                        return;
                }
                if (sscanf(pixelSpacingStr, pixelSpacingFmt, spacing, 
                        spacing + 1) != 2) {
                        SIFT3D_ERR("Dicom.Dicom: unable to parse pixel "
                                "spacing from file %s \n", path);
                        return;
                }
                if (spacing[0] <= 0.0 || spacing[1] <= 0.0) {
                        SIFT3D_ERR("Dicom.Dicom: file %s has invalid pixel "
                                "spacing [%f, %f]\n", path, ux, uy);
                        return;
                }

                // Convert the pixel spacing into units based on the image axes
                double *units = &ux;
                for (int k = 0; k < 2; k++) {
                       units[axes[k]] = spacing[k]; 
                }

                // Read the slice thickness 
                Float64 sliceThickness;
                status = data->findAndGetFloat64(DCM_SliceThickness, 
                                                 sliceThickness);
                if (!status.good()) {
                        SIFT3D_ERR("Dicom.Dicom: failed to get slice "
                                "thickness from file %s (%s)\n", path, 
                                status.text());
                        return;
                }
                if (sliceThickness <= 0.0) {
                        SIFT3D_ERR("Dicom.Dicom: file %s has invalid slice "
                                "thickness: %f \n", path, uz);
                        return;
                }

                // Use the slice thickness as the units for the perpendicular 
                // axis
                units[sortAxis] = sliceThickness;
        }

        // Load the DicomImage object
        DicomImage dicomImage(path);
        if (dicomImage.getStatus() != EIS_Normal) {
                SIFT3D_ERR("Dicom.Dicom: failed to open image %s (%s)\n",
                        path, 
                        DicomImage::getString(dicomImage.getStatus()));
                return;
        }

        // Check for color images
        if (!dicomImage.isMonochrome()) {
                SIFT3D_ERR("Dicom.Dicom: reading of color DICOM images is "
                        "not supported at this time \n");
                return;
        }
        nc = 1;

        // Read the dimensions
        int *dims = &nx;
        dims[axes[0]] = dicomImage.getWidth();
        dims[axes[1]] = dicomImage.getHeight();
        dims[sortAxis] = dicomImage.getFrameCount();
        if (nx < 1 || ny < 1 || nz < 1) {
                SIFT3D_ERR("Dicom.Dicom: invalid dimensions for file %s "
                        "(%d, %d, %d)\n", path, nx, ny, nz);
                return;
        }

        // Set the window 
        dicomImage.setMinMaxWindow();

        valid = true;
}

/* Check the endianness of the machine. Returns true if the machine is little-
 * endian, false otherwise. */
static bool isLittleEndian(void) {
        volatile uint16_t i = 0x0123;
        return ((uint8_t *) &i)[0] == 0x23;
}

/* Set a Dcm_meta struct to default values. Generates new UIDs. */
static void default_Dcm_meta(Dcm_meta *const meta) {
        meta->patient_name = default_patient_name;
        meta->patient_id = default_patient_id;
        meta->series_descrip = default_series_descrip;
        dcmGenerateUniqueIdentifier(meta->study_uid, SITE_STUDY_UID_ROOT);
        dcmGenerateUniqueIdentifier(meta->series_uid, SITE_SERIES_UID_ROOT);
        dcmGenerateUniqueIdentifier(meta->instance_uid, SITE_INSTANCE_UID_ROOT); 
        meta->instance_num = default_instance_num;
}

/* Parse a time (TM) string from a Dicom file. */ 
int parseTM(const char *const tm, double *const time) {

        // Buffers to hold the sub-strings
        char hh[3];
        char mm[3];

        // Subdivide into the TM into hours, minutes and seconds
        memcpy(hh, tm, 2);
        memcpy(mm, tm + 2, 2);
        hh[2] = mm[2] = '\0';
        const char *const ss = tm + 4;

        // Parse the strings
        errno = 0;
        const int hours = atoi(hh);
        const int minutes = atoi(mm);
        const double seconds = strtod(ss, NULL);
        if (errno) return SIFT3D_FAILURE;

        // Add up the totals
        *time = hours * 60.0 * 60.0 + minutes * 60.0 + seconds;

        return SIFT3D_SUCCESS;
}

/* Get the multiplier to convert a PET scan to SUV values. Returns negative if
 * an error has occured. */
double Dicom::PET_SUV_multiplier(void) const {

        // Read the file
        const char *path = filename.c_str();
        DcmFileFormat fileFormat;
        if (load_file(path, fileFormat))
                return -1.0;
        DcmDataset *const data = fileFormat.getDataset();

        // Read the body weight (0010, 1010)
        Float64 weight;
        OFCondition status = data->findAndGetFloat64(DCM_PatientWeight, 
                weight);
        if (status.bad()) {
                SIFT3D_ERR("Dicom.PET_SUV_multiplier: failed to get "
                        "PatientWeight (0010, 1010) from file %s (%s)\n", 
                        path, status.text());
                return -1.0;
        }

        // Read the radiopharmeceutical dosage (0018, 1074)
        Float64 dosage;
        status = data->findAndGetFloat64(DCM_RadionuclideTotalDose,
                dosage, 0, OFTrue);
        if (status.bad()) {
                SIFT3D_ERR("Dicom.PET_SUV_multiplier: failed to get "
                        "RadionuclideTotalDose (0018, 1074) from file %s "
                        "(%s)\n", path, status.text());
                return -1.0;
        }

        // Read the radiopharmeceutical injection time (0018, 1072)
        // DCM_RadiopharmaceuticalStartTime (TM)
        const char *timeStr;
        status = data->findAndGetString(DCM_RadiopharmaceuticalStartTime, 
                timeStr, OFTrue);
        if (status.bad() || timeStr == NULL) {
                SIFT3D_ERR("Dicom.PET_SUV_multiplier: failed to get "
                        "RadiopharmeceuticalStartTime (0018, 1072) from file %s"
                        " (%s)\n", path, status.text());
                return -1.0;
        }

        // Parse the injection time
        Float64 iv_time;
        if (parseTM(timeStr, &iv_time)) {
                SIFT3D_ERR("Dicom.PET_SUV_multipler: failed to parse "
                "RadiopharmeceuticalStartTime (0018, 1072) value %s from file "
                "%s\n", timeStr, path);
                return -1.0;
        }

        // Read the image acquistion time (0008, 0032)
        status = data->findAndGetString(DCM_AcquisitionTime, timeStr);
        if (status.bad() || timeStr == NULL) {
                SIFT3D_ERR("Dicom.PET_SUV_multiplier: failed to get "
                        "AcquisitionTime (0008, 0032) from file %s"
                        " (%s)\n", path, status.text());
                return -1.0;
        }

        // Parse the image time
        Float64 image_time;
        if (parseTM(timeStr, &image_time)) {
                SIFT3D_ERR("Dicom.PET_SUV_multipler: filed to parse "
                        "AcquisitionTime (0008, 0032) value %s from file %s\n", 
                        timeStr, path);
                return -1.0;
        }

        // Read the radiopharmeceutical half-life (0018, 1075)
        Float64 half_life;
        status = data->findAndGetFloat64(DCM_RadionuclideHalfLife, half_life, 0,
                OFTrue);
        if (status.bad()) {
                SIFT3D_ERR("Dicom.PET_SUV_multiplier: failed to get "
                        "RadionuclideHalfLife (0018, 1075) from file %s (%s)\n",
                        path, status.text());
                return -1.0;
        }

        // Compute the elapsed time
        double elapsed_time = iv_time - image_time; 
        if (elapsed_time < 0) {
                const double day_seconds = 24.0 * 60.0 * 60.0;
                elapsed_time = elapsed_time + day_seconds;
        }

        // Compute the time-adjusted dosage
        const double adjusted_dosage = dosage * 
                pow(2.0, -elapsed_time / half_life);

        // Compute the SUV multiplier
        return weight / adjusted_dosage;
}

/* Read a DICOM file into an Image struct. If the file is a DSO, the 
 * referenced DICOM image must be in the same directory. */
int read_dcm(const char *path, Image *const im) {

        int ret;

        CATCH_EXCEPTIONS(ret, "read_dcm", read_dcm_cpp, path, im);

        return ret;
}

/* Read all of the DICOM files from a directory into an Image struct. Slices 
 * must be ordered alphanumerically, starting with z = 0. */
int read_dcm_dir(const char *path, Image *const im) {

        int ret;

        CATCH_EXCEPTIONS(ret, "read_dcm_dir", read_dcm_dir_cpp, path, im);

        return ret;
}

/* Write an Image struct into a DICOM file. 
 * Inputs: 
 *      path - File name
 *      im - Image data
 *      meta - Dicom metadata (or NULL for default values)
 *      max_val - The maximum value of the image, used for scaling. If set
 *              to a negative number, this functions computes the maximum value
 *              from this image.
 *
 * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise.
 */
int write_dcm(const char *path, const Image *const im, 
        const Dcm_meta *const meta, const float max_val) {

        int ret;

        CATCH_EXCEPTIONS(ret, "write_dcm", write_dcm_cpp, path, im, meta, 
                max_val);

        return ret;
}

/* Write an Image struct into a directory of DICOM files.
 * Inputs: 
 *      path - File name
 *      im - Image data
 *      meta - Dicom metadata (or NULL for default values)
 *
 * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise.
 */
int write_dcm_dir(const char *path, const Image *const im, 
        const Dcm_meta *const meta) {

        int ret;

        CATCH_EXCEPTIONS(ret, "write_dcm_dir", write_dcm_dir_cpp, path, im, 
                meta);

        return ret;
}

/* Helper function to read a DICOM file using C++ */
static int read_dcm_cpp(const char *path, Image *const im) {

        // Read the image metadata
	Dicom dicom(path, 1);
        if (!dicom.isValid())
                return SIFT3D_FAILURE;

        // Check if this is a segmentation or normal image
        if (dicom.getType() == DSO) {
                // Look for images only in the same directory
                char *pathDir = im_get_parent_dir(path);
                int ret = read_dso(pathDir, dicom, im);
                free(pathDir);
                return ret;
         }
       
        // Otherwise directly read the image data 
        return read_dcm_img(dicom, im);
}

/* Helper function to read DICOM image data */
static int read_dcm_img(const Dicom &dicom, Image *const im) {

        int offsets[] = {0, 0, 0};
        int signs[] = {1, 1, 1};
        const void *data;
        const DiMonoPixel *pixels;
	uint32_t shift;
	int x, y, z, depth, k;
        const int bufNBits = 32;

        // Image data strides
        const int y_stride = dicom.getNx(); 
        const int z_stride = dicom.getNx() * dicom.getNy();

	// Initialize JPEG decoders
	DJDecoderRegistration::registerCodecs();

        // Initialize the DicomImage object
        const char *path = dicom.name();
	DicomImage dicomImage(path);
        if (dicomImage.getStatus() != EIS_Normal) {
                SIFT3D_ERR("read_dcm_cpp: failed to open image %s (%s)\n",
                        path, DicomImage::getString(dicomImage.getStatus()));
		goto read_dcm_img_quit;
        }

        // Initialize the image fields
        im->nx = dicom.getNx();
        im->ny = dicom.getNy();
        im->nz = dicom.getNz();
        im->nc = dicom.getNc();
        im->ux = dicom.getUx();
        im->uy = dicom.getUy();
        im->uz = dicom.getUz();

        // Resize the output
        im_default_stride(im);
        if (im_resize(im))
		goto read_dcm_img_quit;

        // Format the offsets and signs for the main volume
        for (k = 0; k < 2; k++) {
                if (dicom.getAxisSign(k) > 0) 
                        continue;
                const int axisIdx = dicom.getAxes(k);
                signs[axisIdx] = -1;
                offsets[axisIdx] = dicom.getDim(axisIdx) - 1;
        }

        // Get the vendor-independent intermediate pixel data
        pixels = (const DiMonoPixel *) dicomImage.getInterData();
        if (pixels == NULL) {
                SIFT3D_ERR("read_dcm_img: failed to get intermediate data for "
                        "%s\n", path);
                goto read_dcm_img_quit;
        }
        depth = pixels->getBits();

        // Macro to copy the data
#define COPY_DATA(type) { \
        SIFT3D_IM_LOOP_START(im, x, y, z) \
                SIFT3D_IM_GET_VOX(im, x * signs[0] + offsets[0], \
                        y * signs[1] + offsets[1], \
                        z * signs[2] + offsets[2], \
                        0) = (float) *((type *) data + x + y * y_stride + \
                                z * z_stride);\
        SIFT3D_IM_LOOP_END \
        }\

        // Choose the appropriate data type and copy the data
        data = pixels->getData(); 
        switch (pixels->getRepresentation()) {
        case EPR_Uint8:
                COPY_DATA(uint8_t)
                break;
        case EPR_Uint16:
                COPY_DATA(uint16_t)
                break;
        case EPR_Uint32:
                COPY_DATA(uint32_t)
                break;
        case EPR_Sint8:
                COPY_DATA(int8_t)
                break;
        case EPR_Sint16:
                COPY_DATA(int16_t)
                break;
        case EPR_Sint32:
                COPY_DATA(int32_t)
                break;
        default:
                SIFT3D_ERR("read_dcm_img: unrecognized pixel representation "
                        "for %s\n", path);
                goto read_dcm_img_quit;
        }
#undef COPY_DATA

#if 0
        // Get the bit depth of the image
        depth = dicomImage.Depth();
        if (depth > bufNBits) 
                SIFT3D_ERR("read_dcm_cpp: buffer is insufficiently wide "
                        "for %d-bit data of image %s \n", depth, path);
		goto read_dcm_img_quit;
        }

        // Get the number of bits by which we need to shift the 32-bit data, to
        // recover the original resolution (DICOM uses Big-endian encoding)
        shift = isLittleEndian() ? static_cast<uint32_t>(bufNBits - depth) : 0;

        // Read each frame
        for (int i = 0; i < im->nz; i++) { 

                int x, y, z;

                const int x_start = 0;
                const int y_start = 0;
                const int z_start = i;
                const int x_end = im->nx - 1;
                const int y_end = im->ny - 1;
                const int z_end = z_start;

                // Get a pointer to the data, rendered as a 32-bit int
                const uint32_t *const frameData = 
                        static_cast<const uint32_t *const>(
                                dicomImage.getOutputData(
                                        static_cast<int>(bufNBits), i));
                if (frameData == NULL) {
                        SIFT3D_ERR("read_dcm_cpp: could not get data from "
                                "image %s frame %d (%s)\n", path, i, 
                                DicomImage::getString(dicomImage.getStatus()));
			goto read_dcm_img_quit;
                }

                // Copy the frame
                SIFT3D_IM_LOOP_LIMITED_START(im, x, y, z, x_start, x_end,
                        y_start, y_end, z_start, z_end)

                        // Get the voxel and shift it to match the original
                        // magnitude 
                        const uint32_t vox =
                                frameData[x + y * im->nx] >> shift;

                        // Convert to float and write to the output image
                        SIFT3D_IM_GET_VOX(im, x, y, z, 0) =
                                static_cast<float>(vox);

                SIFT3D_IM_LOOP_END
        }
#endif

	// Clean up
	DJDecoderRegistration::cleanup();

        // Modality-specific post-processing
        if (dicom.getType() == PET) {

                const double suv_factor = dicom.PET_SUV_multiplier();

                // Check for errors in SUV computation
                if (suv_factor < 0)
                        return SIFT3D_FAILURE;

                // Scale all the values
                SIFT3D_IM_LOOP_START(im, x, y, z)
                        SIFT3D_IM_GET_VOX(im, x, y, z, 0) *= suv_factor;
                SIFT3D_IM_LOOP_END
        }

        return SIFT3D_SUCCESS;

read_dcm_img_quit:
	DJDecoderRegistration::cleanup();
	return SIFT3D_FAILURE;
}

/* Read a DICOM Segmentation Object (DSO) mask. 
 *
 * Parameters:
 *      imDir - The path of the directory containing DICOM image files.
 *      dsoPath - The path of the DSO file.
 *      mask - The image in which to write a binary mask.
 *
 * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise.
 */
static int read_dso(const char *imDir, Dicom &dso, 
                            Image *const mask) {

        // Read the DSO
        DcmFileFormat fileFormat;
        const char *dsoPath = dso.name();
        if (load_file(dso.name(), fileFormat))
                return SIFT3D_FAILURE;
        DcmDataset *const dso_data = fileFormat.getDataset();

        // Initialize the DcmSegmentation object
        OFCondition status;
        OFunique_ptr<DcmSegmentation> dcmSegmentation;
        {
                DcmSegmentation *p;
                status = DcmSegmentation::loadDataset(*dso_data, p);
                dcmSegmentation.reset(p);
        }
        if (status.bad()) {
                SIFT3D_ERR("read_dso: failed to initialize the "
                        "DcmSegmentation object for file %s (%s)\n", dsoPath,
                        status.text());
                return SIFT3D_FAILURE;
        }

        // Ensure we have only one segment
        const int num_segments = dcmSegmentation->getNumberOfSegments();
        if (num_segments != 1) {
                SIFT3D_ERR("read_dso: unsupported number of segments "
                        " in file %s: %d\n", dsoPath, num_segments);
                return SIFT3D_FAILURE;
        }

        // Check how many frames we have
        const int nz = dcmSegmentation->getNumberOfFrames();

        // Get the referenced series items
        OFVector<IODSeriesAndInstanceReferenceMacro::ReferencedSeriesItem*>&
                series_items = dcmSegmentation->getCommonInstanceReference()
                        .getReferencedSeriesItems();

        // Ensure there is only one series
        const int num_series = series_items.size();
        if (num_series != 1) {
                SIFT3D_ERR("read_dso: unsupported number of referenced "
                        "series: %d \n", num_series);
                return SIFT3D_FAILURE;
        }

        // Get the referenced instance items
        OFVector<SOPInstanceReferenceMacro*> &ref_instances =
                series_items[0]->getReferencedInstanceItems();

        // Read the image metadata
        std::vector<Dicom> images;
        if (read_dcm_dir_meta(imDir, images))
                return SIFT3D_FAILURE;

        // Read the mask DICOM image data
        Image maskData;
        init_im(&maskData);
        if (read_dcm_img(dso, &maskData)) {
                im_free(&maskData);
                return SIFT3D_FAILURE;
        }

        // Ensure we have a single frame for each instance
        if (ref_instances.size() != maskData.nz) {
                SIFT3D_ERR("read_dso: DSO has %lu frames but %d segments \n",
                        ref_instances.size(), maskData.nz);
                im_free(&maskData);
                return SIFT3D_FAILURE;
        }

        // Initialize and zero the mask
        if (dcm_resize_im(images, mask)) {
                im_free(&maskData);
                return SIFT3D_FAILURE;
        }
        im_zero(mask);

        // Ensure the x,y dimensions match
        if (mask->nx != maskData.nx || mask->ny != maskData.ny) {
                SIFT3D_ERR("read_dso: Mask has frame dimensions [%d x %d] "
                        "while image volume has dimensions [%d x %d] \n",
                        maskData.nx, maskData.ny, mask->nx, mask->ny);
                im_free(&maskData);
                return SIFT3D_FAILURE;
        }

        // Copy each frame into the mask
        for (auto it = ref_instances.begin(); it != ref_instances.end(); 
                ++it) {

                // Get the SOPInstanceUID
                OFString ref_uid;
                status = (*it)->getReferencedSOPInstanceUID(ref_uid);
                if (status.bad()) {
                        SIFT3D_ERR("read_dso: Failed to get "
                                "ReferencedSOPInstanceUID (%s) \n",
                                status.text());
                        im_free(&maskData);
                        return SIFT3D_FAILURE;
                }
                const char *ref_uid_str = ref_uid.c_str();

                // Check for a matching UID in the images
                auto match = std::find_if(
                        images.begin(), 
                        images.end(), 
                        [ref_uid_str] (Dicom &image) {
                                return image.eqInstance(ref_uid_str);
                        }
                );
                if (match == images.end()) {
                        SIFT3D_ERR("read_dso: No image found with "
                                "SOPInstanceUID %s \n", ref_uid_str);
                        im_free(&maskData);
                        return SIFT3D_FAILURE;
                }

                // Get the z offsets
                const int im_frame_num = std::distance(images.begin(), match);
                const int dso_frame_num = std::distance(ref_instances.begin(), 
                                                        it);

                // Copy the frame into the full mask volume
                if (write_subvolume(*match, mask, im_frame_num, &maskData, 
                        dso_frame_num, dso_frame_num)) {
                        im_free(&maskData);
                        return SIFT3D_FAILURE;
                }
        }

        // Clean up
        im_free(&maskData);
        return SIFT3D_SUCCESS;
}

/* Helper function to read the metadata from a directory of DICOM files */
static int read_dcm_dir_meta(const char *path, std::vector<Dicom> &dicoms) {

        struct stat st;
        DIR *dir;
        struct dirent *ent;

        // Verify that the directory exists
	if (stat(path, &st)) {
                SIFT3D_ERR("read_dcm_dir_cpp: cannot find file %s \n", path);
                return SIFT3D_FAILURE;
	} else if (!S_ISDIR(st.st_mode)) {
                SIFT3D_ERR("read_dcm_dir_cpp: file %s is not a directory \n",
                        path);
                return SIFT3D_FAILURE;
	}

        // Open the directory
        if ((dir = opendir(path)) == NULL) {
                SIFT3D_ERR("read_dcm_dir_cpp: unexpected error opening "
                        "directory %s \n", path);
                return SIFT3D_FAILURE;
        }

        // Get all of the .dcm files in the directory, ignoring DSOs
        dicoms.clear();
        while ((ent = readdir(dir)) != NULL) {

                // Form the full file path
                std::string fullfile(std::string(path) + sepStr + ent->d_name);

                // Check if it is a DICOM file 
                if (im_get_format(fullfile.c_str()) != DICOM)
                        continue;

                // Read the file
                Dicom dicom(fullfile.c_str(), 0);
                if (!dicom.isValid()) {
                        closedir(dir);
                        return SIFT3D_FAILURE;
                }

                // Ignore DSOs
                if (dicom.getType() == DSO)
                        continue;

                // Add the file to the list
                dicoms.push_back(dicom);
        }

        // Release the directory
        closedir(dir);
        
        // Verify that dicom files were found
        if (dicoms.size() == 0) {
                SIFT3D_ERR("read_dcm_dir_cpp: no DICOM files found in %s \n",
                        path);
                return SIFT3D_FAILURE;
        }

        // Sort the slices by their coordinates
        std::sort(dicoms.begin(), dicoms.end()); 

        return SIFT3D_SUCCESS;
}

/* Resize an image to fit a DICOM series. */
static int dcm_resize_im(const std::vector<Dicom> &dicoms, Image *const im) {

        int i, j;

        // Verify that the files are from the same series, and other metadata
        const int num_files = dicoms.size();
        const Dicom &first = dicoms[0];
        const int sortAxis = first.getSortAxis();
        for (int i = 1; i < num_files; i++) {

                const Dicom &dicom = dicoms[i];

                // Verify the SOPSeriesUID
                if (!first.eqSeries(dicom)) {
                        SIFT3D_ERR("read_dcm_dir_cpp: file %s is from a "
                                "different series than file %s \n", 
                                dicom.name(), first.name());
                        return SIFT3D_FAILURE;
                }

                // Verify the sorting axis
                if (dicom.getSortAxis() != sortAxis) {
                        SIFT3D_ERR("read_dcm_dir_cpp: file %s (%d) is sorted "
                                "by a different axis than file %s (%d) \n",
                                dicom.name(), dicom.getSortAxis(), first.name(),
                                sortAxis);
                        return SIFT3D_INCONSISTENT_AXES;
                }
        }

        // Initialize the units to those of the first image
        im->ux = first.getUx(); 
        im->uy = first.getUy();
        im->uz = first.getUz();

        // Compute the spacing between slices
        if (num_files > 1) {

                // Tolerance for spacing discrepancies
                const double tol_spacing = 5E-2;

                // If there are multiple slices, compute the slice spacing by 
                // subtracting the first two images
                const double first_spacing = fabs(first.getSortCoord() - 
                        dicoms[1].getSortCoord());

                // Ensure all the other spacings agree, else throw an error
                for (int i = 0; i < num_files - 1; i++) {

                        const Dicom &prev = dicoms[i];
                        const Dicom &next = dicoms[i + 1];

                        const double spacing = fabs(prev.getSortCoord() - 
                                next.getSortCoord());

                        // Check for duplicates
                        if (spacing == 0.0) {
                                SIFT3D_ERR("read_dcm_dir_cpp: files %s and %s "
                                "have duplicate coordinates in the sorting "
                                "dimension (%d) \n", prev.name(), next.name(),
                                prev.getSortAxis());
                                return SIFT3D_DUPLICATE_SLICES;
                        }

                        // Check the spacing
                        if (fabs(spacing - first_spacing) <= tol_spacing)
                                continue;

                        SIFT3D_ERR("read_dcm_dir_cpp: files %s and %s are "
                                "spaced %fmm apart, which does not follow the "
                                "series spacing of %fmm \n", 
                                prev.name(), next.name(), spacing, 
                                first_spacing);
                        return SIFT3D_UNEVEN_SPACING;
                }

                // Ensure all the slice thicknesses agree, else print a warning
                for (int i = 1; i < num_files; i++) {

                        const Dicom &dicom = dicoms[i];
                        const double thickness = dicom.getSortUnit();

                        if (fabs(first_spacing - thickness) <= tol_spacing)
                                continue; 

                        SIFT3D_ERR("read_dcm_dir_cpp: WARNING--file %s has "
                                "a slice thickness of %fmm, which does not "
                                "agree with the series slice spacing of %fmm\n",
                                dicom.name(), thickness, first_spacing);

                        // No need for multiple warnings
                        break;
                }
               
                SIFT3D_IM_GET_UNITS(im)[sortAxis] = first_spacing;
        }

        // Initialize the output dimensions to those of the first image
        int dims[] = {first.getNx(), first.getNy(), first.getNz()};
        int nc = first.getNc();

        // Verify the dimensions of the other files, counting the total
        // number of slices
        int nSlice = 0;
        for (i = 0; i < num_files; i++) {

                // Get a slice
                const Dicom &dicom = dicoms[i];        

                // Verify the channels
                if (dicom.getNc() != nc) {
                        SIFT3D_ERR("read_dcm_dir_cpp: slice %s "
                                "(%d) does not match the "
                                "channels of slice %s (%d) \n",
                                dicom.name(), dicom.getNc(), 
                                first.name(), nc);
                }

                // Verify the non-sorting dimensions
                for (j = 0; j < 2; j++) {
                        const int firstDim = dims[j];
                        const int sliceDim = dicom.getDim(j);

                        if (sliceDim == firstDim)
                                continue;

                        SIFT3D_ERR("read_dcm_dir_cpp: slice %s "
                                "dimension %d (%d) does not match the "
                                "dimensions of slice %s (%d) \n",
                                dicom.name(), sliceDim,  sortAxis, first.name(),
                                firstDim);
                        return SIFT3D_FAILURE;
                }

                // Count the number of slices
                nSlice += dicom.getDim(sortAxis);
        }

        // Set the dimension of the sorting axis
        dims[sortAxis] = nSlice;

        // Resize the output
        memcpy(SIFT3D_IM_GET_DIMS(im), dims, sizeof(dims));
        im->nc = nc;
        im_default_stride(im);
        if (im_resize(im))
                return SIFT3D_FAILURE;

        return SIFT3D_SUCCESS;
}

/* Copy a Dicom sub-volume into a larger volume. start and end are indices into
 * sub, along the sorting dimension.  */
static int write_subvolume(const Dicom &dicom, Image *const main, 
        const int mainOffset, Image *const sub, const int start, 
        const int end) {

        int mainOffsets[] = {0, 0, 0};
        int starts[] = {0, 0, 0};
        int ends[] = {sub->nx - 1, sub->ny - 1, sub->nz - 1};
        int x, y, z, c, k;
        
        const int sortAxis = dicom.getSortAxis();
        const int mainSortDim = SIFT3D_IM_GET_DIMS(main)[sortAxis];
        const int subSortDim = SIFT3D_IM_GET_DIMS(sub)[sortAxis];

        // Verify the image axis dimensions
        for (k = 0; k < 2; k++) {
                const int axisIdx = dicom.getAxes(k);
                const int mainDim = SIFT3D_IM_GET_DIMS(main)[axisIdx];
                const int subDim = SIFT3D_IM_GET_DIMS(sub)[axisIdx];
                if (mainDim != subDim) {
                        SIFT3D_ERR("write_subvolume: axis %d does not match: "
                                "%d (main) vs %d (sub)", axisIdx, mainDim, 
                                subDim);
                        return SIFT3D_FAILURE;
                }
        }

        // Verify the sorting axis dimensions
        if (start < 0) {
                SIFT3D_ERR("write_subvolume: invalid start %d\n", start);
                return SIFT3D_FAILURE;
        }
        if (end > subSortDim) {
                SIFT3D_ERR("write_subvolume: invalid end %d (maximum %d)\n", 
                        end, subSortDim);
                return SIFT3D_FAILURE;
        }
        if (mainSortDim < mainOffset + end - start) {
                SIFT3D_ERR("write_subvolume: subvolume range (%d, %d) does "
                        "not fit into main volume dimension %d, offset %d, "
                        "axis %d\n", start,  end, mainSortDim, mainOffset, 
                        sortAxis);
                return SIFT3D_FAILURE;
        }

        // Set the starting and ending indices for the sort axis
        starts[sortAxis] = start;
        ends[sortAxis] = end;
        mainOffsets[sortAxis] = mainOffset;

        // Copy the data
        SIFT3D_IM_LOOP_LIMITED_START_C(sub, x, y, z, c, starts[0], ends[0],
                starts[1], ends[1], starts[2], ends[2])
                SIFT3D_IM_GET_VOX(main, x + mainOffsets[0], y + mainOffsets[1],
                        z + mainOffsets[2], c) = 
                        SIFT3D_IM_GET_VOX(sub, x, y, z, c);
        SIFT3D_IM_LOOP_END_C

        return SIFT3D_SUCCESS;
}

/* Helper function to read a directory of DICOM files using C++ */
static int read_dcm_dir_cpp(const char *path, Image *const im) {

        int ret, i, j, sliceCount;

        // Read the DICOM metadata
        std::vector<Dicom> dicoms;
        if (read_dcm_dir_meta(path, dicoms))
                return SIFT3D_FAILURE;

        // Initialize the image volume
        if (ret = dcm_resize_im(dicoms, im))
                return ret;

        // Allocate a temporary image for the slices
        Image slice;
        init_im(&slice);

        // Read the image data
        sliceCount = 0;
        const int num_files = dicoms.size();
        for (i = 0; i < num_files; i++) {

                Dicom dicom = dicoms[i];

                // Read the slice data and write it into the volume
                const int numSlices = dicom.getDim(dicom.getSortAxis());
                if (read_dcm_img(dicom, &slice) ||
                        write_subvolume(dicom, im, sliceCount, &slice, 0, 
                                numSlices - 1)) { 
                        im_free(&slice);
                        return SIFT3D_FAILURE;
                }

                sliceCount += numSlices;
        }
        im_free(&slice);

        return SIFT3D_SUCCESS;
} 

/* Helper function to set meta_new to default values if meta is NULL,
 * otherwise copy meta to meta_new */
static void set_meta_defaults(const Dcm_meta *const meta, 
        Dcm_meta *const meta_new) {
        if (meta == NULL) {
                default_Dcm_meta(meta_new);
        } else {
                *meta_new = *meta;        
        }
}

/* Helper function to write a DICOM file using C++ */
static int write_dcm_cpp(const char *path, const Image *const im,
        const Dcm_meta *const meta, const float max_val) {

#define BUF_LEN 1024
        char buf[BUF_LEN];

        // Ensure the image is monochromatic
        if (im->nc != 1) {
                SIFT3D_ERR("write_dcm_cpp: image %s has %d channels. "
                        "Currently only single-channel images are supported.\n",
                         path, im->nc);
                return SIFT3D_FAILURE;
        }

        // If no metadata was provided, initialize default metadata
        Dcm_meta meta_new;
        set_meta_defaults(meta, &meta_new);

        // Create a new fileformat object
        DcmFileFormat fileFormat;

        // Set the file type to derived
        DcmDataset *const dataset = fileFormat.getDataset();
        OFCondition status = dataset->putAndInsertString(DCM_ImageType, 
                                                         "DERIVED");
        if (status.bad()) {
                std::cerr << "write_dcm_cpp: Failed to set the image type" <<
                        std::endl;
                return SIFT3D_FAILURE;
        }

        // Set the class UID
        dataset->putAndInsertString(DCM_SOPClassUID, 
                UID_CTImageStorage);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the SOPClassUID\n");
                return SIFT3D_FAILURE;
        }

        // Set the photometric interpretation
        const char *photoInterp;
        if (im->nc == 1) {
                photoInterp = "MONOCHROME2";
        } else if (im->nc == 3) {
                photoInterp = "RGB";
        } else {
                SIFT3D_ERR("write_dcm_cpp: failed to determine the "
                        "photometric representation for %d channels \n", 
                        im->nc);
                return SIFT3D_FAILURE;
        }
        dataset->putAndInsertString(DCM_PhotometricInterpretation,
                photoInterp);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the photometric "
                        "interpretation \n");
                return SIFT3D_FAILURE;
        }

        // Set the pixel representation to unsigned
        dataset->putAndInsertUint16(DCM_PixelRepresentation, 0);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the pixel "
                        "representation \n");
                return SIFT3D_FAILURE;
        }

        // Set the number of channels (Samples Per Pixel) and set the planar
        // configuration to interlaced pixels
        assert(SIFT3D_IM_GET_IDX(im, 0, 0, 0, 1) == 
                SIFT3D_IM_GET_IDX(im, 0, 0, 0, 0) + 1);
        snprintf(buf, BUF_LEN, "%d", im->nc);
        dataset->putAndInsertString(DCM_SamplesPerPixel, buf);
        dataset->putAndInsertString(DCM_PlanarConfiguration, "0");

        // Set the bits allocated and stored, in big endian format 
        const unsigned int dcm_high_bit = dcm_bit_width - 1;
        dataset->putAndInsertUint16(DCM_BitsAllocated, dcm_bit_width);
        dataset->putAndInsertUint16(DCM_BitsStored, dcm_bit_width);
        dataset->putAndInsertUint16(DCM_HighBit, dcm_high_bit);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the bit widths \n");
                return SIFT3D_FAILURE;
        }

        // Set the patient name
        status = dataset->putAndInsertString(DCM_PatientName, 
                meta_new.patient_name);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the patient name\n");
                return SIFT3D_FAILURE;
        }

        // Set the patient ID
        status = dataset->putAndInsertString(DCM_PatientID,
                meta_new.patient_id);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the patient ID \n");
                return SIFT3D_FAILURE;
        }

        // Set the study UID
        status = dataset->putAndInsertString(DCM_StudyInstanceUID,
                meta_new.study_uid);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the "
                        "StudyInstanceUID \n");
                return SIFT3D_FAILURE;
        }

        // Set the series UID
        status = dataset->putAndInsertString(DCM_SeriesInstanceUID,
                meta_new.series_uid);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the "
                        "SeriesInstanceUID \n");
                return SIFT3D_FAILURE;
        }

        // Set the series description
        status = dataset->putAndInsertString(DCM_SeriesDescription,
                meta_new.series_descrip);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the series "
                        "description \n");
                return SIFT3D_FAILURE;
        }

        // Set the instance UID
        status = dataset->putAndInsertString(DCM_SOPInstanceUID, 
                meta_new.instance_uid);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: failed to set the "
                        "SOPInstanceUID \n");
                return SIFT3D_FAILURE;
        }

        // Set the dimensions
        OFCondition xstatus = dataset->putAndInsertUint16(DCM_Rows, im->ny); 
        OFCondition ystatus = dataset->putAndInsertUint16(DCM_Columns, im->nx);
        snprintf(buf, BUF_LEN, "%d", im->nz);
        OFCondition zstatus = dataset->putAndInsertString(DCM_NumberOfFrames,
                buf);
        if (xstatus.bad() || ystatus.bad() || zstatus.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the dimensions \n");
                return SIFT3D_FAILURE;
        }

        // Set the instance number
        snprintf(buf, BUF_LEN, "%u", meta_new.instance_num);
        status = dataset->putAndInsertString(DCM_InstanceNumber, buf);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the instance "
                        "number \n");
                return SIFT3D_FAILURE;
        }

        // Set the ImagePositionPatient vector
        const double imPosX = static_cast<double>(im->nx - 1) * im->ux;
        const double imPosY = static_cast<double>(im->ny - 1) * im->uy;
        const double imPosZ = static_cast<double>(meta_new.instance_num) * 
                              im->uz;
        snprintf(buf, BUF_LEN, imPosPatientFmt, imPosX, imPosY, imPosZ);
        status = dataset->putAndInsertString(DCM_ImagePositionPatient, buf);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the "
                        "ImagePositionPatient vector \n");
                return SIFT3D_FAILURE;
        }

        // Set the ImageOrientationPatient vectors
        const float imageOriPatient[] = {1., 0, 0, 0, 1., 0};
        snprintf(buf, BUF_LEN, imOriPatientFmt, 
                imageOriPatient[0], 
                imageOriPatient[1],
                imageOriPatient[2],
                imageOriPatient[3],
                imageOriPatient[4],
                imageOriPatient[5]);
        status = dataset->putAndInsertString(DCM_ImageOrientationPatient, buf);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the "
                        "ImageOrientationPatient vectors \n");
                return SIFT3D_FAILURE;
        }

        // Set the slice location
        snprintf(buf, BUF_LEN, "%f", imPosZ);
        status = dataset->putAndInsertString(DCM_SliceLocation, buf);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the slice "
                        "location \n");
                return SIFT3D_FAILURE;
        }

        // Set the pixel spacing
        snprintf(buf, BUF_LEN, pixelSpacingFmt, im->ux, im->uy);
        status = dataset->putAndInsertString(DCM_PixelSpacing, buf);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the pixel "
                        "spacing \n");
                return SIFT3D_FAILURE;
        }

        // Set the aspect ratio
        snprintf(buf, BUF_LEN, "%f\\%f", im->ux, im->uy);
        status = dataset->putAndInsertString(DCM_PixelAspectRatio, buf);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the pixel aspect "
                        "aspect ratio \n");
                return SIFT3D_FAILURE;
        }

        // Set the slice thickness
        snprintf(buf, BUF_LEN, "%f", im->uz);
        status = dataset->putAndInsertString(DCM_SliceThickness, buf);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the slice "
                                "thickness \n");
                return SIFT3D_FAILURE;
        }

        // Count the number of pixels in the image
        unsigned long numPixels = SIFT3D_IM_GET_DIMS(im)[0];
        for (int i = 1; i < IM_NDIMS; i++) {
                numPixels *= SIFT3D_IM_GET_DIMS(im)[i];
        }

        // Get the image scaling factor
        const float dcm_max_val = static_cast<float>(1 << dcm_bit_width) - 1.0f;
        const float im_max = max_val < 0.0f ? im_max_abs(im) : max_val;
        const float scale = im_max == 0.0f ? 1.0f : dcm_max_val / im_max;

        // Render the data to an 8-bit unsigned integer array
        assert(dcm_bit_width == 8);
        assert(fabsf(dcm_max_val - 255.0f) < FLT_EPSILON);
        uint8_t *pixelData = new uint8_t[numPixels];
        int x, y, z, c;
        SIFT3D_IM_LOOP_START_C(im, x, y, z, c)

                const float vox = SIFT3D_IM_GET_VOX(im, x, y, z, c);

                if (vox < 0.0f) {
                        SIFT3D_ERR("write_dcm_cpp: Image cannot be "
                                "negative \n");
                        return SIFT3D_FAILURE;
                }

                pixelData[c + x + y * im->nx + z * im->nx * im->ny] =
                        static_cast<uint8_t>(vox * scale);
        SIFT3D_IM_LOOP_END_C

        // Write the data
        status = dataset->putAndInsertUint8Array(DCM_PixelData, pixelData, 
                numPixels);
        delete[] pixelData;
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: failed to set the pixel data \n");
                return SIFT3D_FAILURE;
        }

        // Choose the encoding format
#if 0
        DJEncoderRegistration::registerCodecs();
        const E_TransferSyntax xfer = EXS_JPEGProcess14SV1TransferSyntax;
        DJ_RPLossless rp_lossless;
        status = dataset->chooseRepresentation(xfer, &rp_lossless);
#else
        const E_TransferSyntax xfer = EXS_LittleEndianExplicit;
        dataset->chooseRepresentation(xfer, NULL);
#endif
        if (!dataset->canWriteXfer(xfer)) {
                SIFT3D_ERR("write_dcm_cpp: Failed to choose the encoding "
                        "format \n");
                return SIFT3D_FAILURE;
        }

        // Force the media storage UIDs to be re-generated by removing them
        dataset->remove(DCM_MediaStorageSOPClassUID);
        dataset->remove(DCM_MediaStorageSOPInstanceUID);

        // Save the file
        status = fileFormat.saveFile(path, xfer);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: failed to write file %s (%s) \n",
                        path, status.text());
                return SIFT3D_FAILURE;
        }

        return SIFT3D_SUCCESS;
#undef BUF_LEN
}

/* Helper function to write an image to a directory of DICOM files using C++ */
static int write_dcm_dir_cpp(const char *path, const Image *const im,
        const Dcm_meta *const meta) {

        Image slice;

        // Initialize C intermediates
        init_im(&slice);

        // Initialize the metadata to defaults, if it is null 
        Dcm_meta meta_new;
        set_meta_defaults(meta, &meta_new);

        // Get the number of leading zeros for the file names
        const int num_slices = im->nz;
        const int num_zeros = static_cast<int>(ceil(log10(
                static_cast<double>(num_slices))));

        // Form the printf format string for file names
#define BUF_LEN 16
        char format[BUF_LEN];
        snprintf(format, BUF_LEN, "%%0%dd.%s", num_zeros, ext_dcm); 
#undef BUF_LEN

        // Resize the slice buffer
        slice.nx = im->nx; 
        slice.ny = im->ny;
        slice.nz = 1;
        slice.nc = im->nc;
        im_default_stride(&slice);
        if (im_resize(&slice)) {
                im_free(&slice);
                return SIFT3D_FAILURE;
        }

        // Copy the units to the slice
        memcpy(SIFT3D_IM_GET_UNITS(&slice), SIFT3D_IM_GET_UNITS(im), 
                IM_NDIMS * sizeof(double));

        // Get the maximum absolute value of the whole image volume
        const float max_val = im_max_abs(im);

        // Write each slice
        for (int i = 0; i < num_slices; i++) {

                // Form the slice file name
#define BUF_LEN 1024
                char buf[BUF_LEN];
                snprintf(buf, BUF_LEN, format, i);

                // Form the full file path
                std::string fullfile(path + sepStr + buf);

                // Copy the data to the slice
                int x, y, z, c;
                SIFT3D_IM_LOOP_START_C(&slice, x, y, z, c)
                        SIFT3D_IM_GET_VOX(&slice, x, y, z, c) =
                                SIFT3D_IM_GET_VOX(im, x, y, i, c);
                SIFT3D_IM_LOOP_END_C

                // Generate a new SOPInstanceUID
                dcmGenerateUniqueIdentifier(meta_new.instance_uid, 
                        SITE_INSTANCE_UID_ROOT); 

                // Set the instance number
                const unsigned int instance = static_cast<unsigned int>(i + 1);
                meta_new.instance_num = instance;

                // Write the slice to a file
                if (write_dcm(fullfile.c_str(), &slice, &meta_new, max_val)) {
                        im_free(&slice);
                        return SIFT3D_FAILURE;
                }
        }

        // Clean up
        im_free (&slice);

        return SIFT3D_SUCCESS;
}

#endif


================================================
FILE: imutil/dicom.h
================================================
/* -----------------------------------------------------------------------------
 * dicom.h 
 * -----------------------------------------------------------------------------
 * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.
 * -----------------------------------------------------------------------------
 * Internal header file for the DCMTK wrapper.
 * -----------------------------------------------------------------------------
 */

#ifndef _DICOM_H
#define _DICOM_H

#ifdef __cplusplus
extern "C" {
#endif

/* Length of UID buffers */
#define SIFT3D_UID_LEN 1024 

/* Dicom file extension */
const char ext_dcm[] = "dcm";

/* Internal struct to hold limited Dicom metadata */
typedef struct _Dcm_meta {
        const char *patient_name; // Patient name
        const char *patient_id; // Patient ID
        const char *series_descrip; // Series description
        char study_uid[SIFT3D_UID_LEN]; // Study Instance UID
        char series_uid[SIFT3D_UID_LEN]; // Series UID
        char instance_uid[SIFT3D_UID_LEN]; // SOP Instance UID
        int instance_num; // Instance number
} Dcm_meta;

int read_dcm(const char *path, Image *const im);

int read_dcm_dir(const char *path, Image *const im);

int write_dcm(const char *path, const Image *const im, 
        const Dcm_meta *const meta, const float max_val);

int write_dcm_dir(const char *path, const Image *const im, 
        const Dcm_meta *const meta);

#ifdef __cplusplus
}
#endif

#endif


================================================
FILE: imutil/immacros.h
================================================
/* -----------------------------------------------------------------------------
 * immacros.h
 * -----------------------------------------------------------------------------
 * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.
 * -----------------------------------------------------------------------------
 * This header defines preprocessor macros for the imutil library.
 * -----------------------------------------------------------------------------
 */

#include "imtypes.h"

#ifdef SIFT3D_MEX
#include <uchar.h>
#include "mex.h"
#else
#include "stdio.h"
#endif

#ifndef _IMMACROS_H
#define _IMMACROS_H

#ifdef __cplusplus
extern "C" {
#endif

// Print an error message
#ifdef SIFT3D_MEX
#define SIFT3D_ERR(...) \
        mexWarnMsgIdAndTxt("sift3d:internal", __VA_ARGS__)
#else
#define SIFT3D_ERR(...) fprintf(stderr, __VA_ARGS__)
#endif

// Math macros
#define SIFT3D_MIN(x, y) ((x) < (y) ? (x) : (y))
#define SIFT3D_MAX(x, y) ((x) > (y) ? (x) : (y))
#define SIFT3D_AZ_MAX_F (2 * (float) M_PI) // Maximum azimuth
#define SIFT3D_PO_MAX_F ((float) M_PI) // Maximum polar angle

// Compiler flags
#ifdef __GNUC__
#define SIFT3D_IGNORE_UNUSED __attribute__((unused))
#endif

// Get a pointer to the [nx, ny, nz] array of an image
#define SIFT3D_IM_GET_DIMS(im) \
        (&(im)->nx)

// Get a pointer to the [xs, ys, zs] array of an image
#define SIFT3D_IM_GET_STRIDES(im) \
        (&(im)->xs)

// Get a pointer to the [ux, uy, uz] array of an image
#define SIFT3D_IM_GET_UNITS(im) \
        (&(im)->ux)

// Get the index of an [x,y,z] pair in an image 
#define SIFT3D_IM_GET_IDX(im, x, y, z, c) ((size_t) (x) * (im)->xs + \
        (size_t) (y) * (im)->ys + (size_t) (z) * (im)->zs + (size_t) (c))

// Get the value of voxel [x,y,z] in an image 
#define SIFT3D_IM_GET_VOX(im, x, y, z, c) ((im)->data[ \
        SIFT3D_IM_GET_IDX((im), (x), (y), (z), (c))])

// Loop through an image in x, z, y order. Delmit with SIFT3D_IM_LOOP_END
#define SIFT3D_IM_LOOP_START(im, x, y, z) \
	for (z = 0; (z) < (im)->nz; (z)++) {	\
	for ((y) = 0; (y) < (im)->ny; (y)++) {	\
	for ((x) = 0; (x) < (im)->nx; (x)++) {

/* As in SIFT3D_IM_LOOP_START, but also loop through each channel */
#define SIFT3D_IM_LOOP_START_C(im, x, y, z, c) \
        SIFT3D_IM_LOOP_START(im, x, y, z) \
        for ((c) = 0; (c) < (im)->nc; (c)++) {

/* Loop through an image iterating with the (inclusive) x, y, z bounds given.
 * Delimit with SIFT3D_IM_LOOP_END. */
#define SIFT3D_IM_LOOP_LIMITED_START(im, x, y, z, x_start, x_end, \
			      y_start, y_end, z_start, z_end) \
	for (z = z_start; (z) <= z_end; (z)++) { \
	for ((y) = y_start; (y) <= y_end; (y)++) { \
	for ((x) = x_start; (x) <= x_end; (x)++) {		

/* As in SIFT3D_IM_LOOP_LIMITED_START, but also loop through each channel */
#define SIFT3D_IM_LOOP_LIMITED_START_C(im, x, y, z, c, x_start, x_end, \
			      y_start, y_end, z_start, z_end) \
        SIFT3D_IM_LOOP_LIMITED_START(im, x, y, z, x_start, x_end, \
			      y_start, y_end, z_start, z_end) \
        for ((c) = 0; (c) < (im)->nc; (c)++) {
                                

// Delimit an SIFT3D_IM_LOOP_START or SIFT3D_IM_LOOP_LIMITED_START
#define SIFT3D_IM_LOOP_END }}}

// Delimited an SIFT3D_IM_LOOP_START_C or SIFT3D_IM_LOOP_LIMITED_START_C
#define SIFT3D_IM_LOOP_END_C SIFT3D_IM_LOOP_END }

/* Check if a point is within the boundaries of an image */
#define IM_CONTAINS(im, x, y, z) \
        ((x) >= 0 && (y) >= 0 && (z) >= 0 && (x) < (im)->nx && \
         (y) < (im)->ny && (z) < (im)->nz)

/* Take the Cartesian gradient of an image at [x, y, z, c]. The voxel cannot be
 * on the boundary. */
#define SIFT3D_IM_GET_GRAD(im, x, y, z, c, vd) \
		(vd)->x = 0.5f * (SIFT3D_IM_GET_VOX(im, (x) + 1, y, z, c) - \
			   SIFT3D_IM_GET_VOX(im, (x) - 1, y, z, c)); \
		(vd)->y = 0.5f * (SIFT3D_IM_GET_VOX(im, x, (y) + 1, z, c) - \
			   SIFT3D_IM_GET_VOX(im, x, (y) - 1, z, c)); \
		(vd)->z = 0.5f * (SIFT3D_IM_GET_VOX(im, x, y, (z) + 1, c) - \
			   SIFT3D_IM_GET_VOX(im, x, y, (z) - 1, c))

/* Get the Hessian of an image at [x, y, z]. The voxel cannot be on the 
 * boundary. */
#define SIFT3D_IM_GET_HESSIAN(im, x, y, z, c, H, type) \
   /* Dxx */ \
    SIFT3D_MAT_RM_GET(H, 0, 0, type) = (type) (0.25f * \
                                (SIFT3D_IM_GET_VOX(im, x + 1, y, z, c) - \
				 2 * SIFT3D_IM_GET_VOX(im, x, y, z, c) + \
				 SIFT3D_IM_GET_VOX(im, x - 1, y, z, c))); \
    /* Dxy */ \
    SIFT3D_MAT_RM_GET(H, 0, 1, type) = (type) (0.25f * \\
                                (SIFT3D_IM_GET_VOX(im, x + 1, y + 1, z, c) - \
				 SIFT3D_IM_GET_VOX(im, x - 1, y + 1, z, c) + \
				 SIFT3D_IM_GET_VOX(im, x - 1, y - 1, z, c) - \
				 SIFT3D_IM_GET_VOX(im, x + 1, y - 1, z, c))); \
    /* Dxz */ \
    SIFT3D_MAT_RM_GET(H, 0, 2, type) = (type) (0.25f * \
                                (SIFT3D_IM_GET_VOX(im, x + 1, y, z + 1, c) - \
				 SIFT3D_IM_GET_VOX(im, x - 1, y, z + 1, c) + \
				 SIFT3D_IM_GET_VOX(im, x - 1, y, z - 1, c) - \
				 SIFT3D_IM_GET_VOX(im, x + 1, y, z - 1, c))); \
    /* Dyx */ \
    SIFT3D_MAT_RM_GET(H, 1, 0, type) = SIFT3D_MAT_RM_GET(H, 0, 1, type); \
    /* Dyy */ \
    SIFT3D_MAT_RM_GET(H, 1, 1, type) = (type) (0.25f * \
                                (SIFT3D_IM_GET_VOX(im, x, y + 1, z, c) - \
				 2 * SIFT3D_IM_GET_VOX(im, x, y, z, c) + \
				 SIFT3D_IM_GET_VOX(im, x, y - 1, z, c))); \
    /* Dyz */ \
    SIFT3D_MAT_RM_GET(H, 1, 2, type) = (type) (0.25f * \
                                (SIFT3D_IM_GET_VOX(im, x, y + 1, z + 1, c) - \
				 SIFT3D_IM_GET_VOX(im, x, y - 1, z + 1, c) + \
				 SIFT3D_IM_GET_VOX(im, x, y - 1, z - 1, c) - \
				 SIFT3D_IM_GET_VOX(im, x, y + 1, z - 1, c))); \
    /* Dzx */ \
    SIFT3D_MAT_RM_GET(H, 2, 0, type) = SIFT3D_MAT_RM_GET(H, 0, 2, type); \
    /* Dzy */ \
    SIFT3D_MAT_RM_GET(H, 2, 1, type) = SIFT3D_MAT_RM_GET(H, 1, 2, type); \
    /* Dzz */ \
    SIFT3D_MAT_RM_GET(H, 2, 2, type) = (type) (0.25f * \
                                (SIFT3D_IM_GET_VOX(im, x, y, z + 1, c) - \
				 2 * SIFT3D_IM_GET_VOX(im, x, y, z, c) + \
				 SIFT3D_IM_GET_VOX(im, x, y, z - 1, c)))

// Get a pointer to an image struct at pyramid level [o, s]
#define SIFT3D_PYR_IM_GET(pyr, o, s) ((pyr)->levels + \
						((o) - (pyr)->first_octave) * \
						(pyr)->num_levels + ((s) - (pyr)->first_level))

// Get the index of the last octave of a Pyramid struct
#define SIFT3D_PYR_LAST_OCTAVE(pyr) \
        ((pyr)->first_octave + (pyr)->num_octaves - 1)

// Get the index of the last level of a Pyramid struct
#define SIFT3D_PYR_LAST_LEVEL(pyr) \
        ((pyr)->first_level + (pyr)->num_levels - 1)

// Loop through all levels of a given pyramid
#define SIFT3D_PYR_LOOP_START(pyr, o, s) \
	for ((o) = (pyr)->first_octave; (o) <= SIFT3D_PYR_LAST_OCTAVE(pyr); \
                (o)++) { \
	for ((s) = (pyr)->first_level; (s) <= SIFT3D_PYR_LAST_LEVEL(pyr); \
                (s)++) {

// Loop from the specified (inclusive) limits of a given pyramid
#define SIFT3D_PYR_LOOP_LIMITED_START(o, s, o_start, o_end, s_start, s_end) \
	for ((o) = (o_start); (o) <= (o_end); (o)++) {	\
	for ((s) = (s_start); (s) <= (s_end); (s)++) {

// Delimit a SIFT3D_PYR_LOOP
#define SIFT3D_PYR_LOOP_END }}

// Delimit the first level of a SIFT3D_PYR_LOOP
#define SIFT3D_PYR_LOOP_SCALE_END }

// Delimit the second level of a PYR_LOOP
#define SIFT3D_PYR_LOOP_OCTAVE_END }

// Get a pointer to the incremental Gaussian filter for level s
#define SIFT3D_GAUSS_GET(gss, s) \
	((gss)->gauss_octave + (s - (gss)->first_level))

/* Resize a slab. If SIFT3D_SLAB_SIZE is defined, add
* elements in increments of that number. Otherwise,
* use a default of 500. This macro is meant to be
* used whether or not the slab buffer actually needs
* resizing -- it checks for that. */
#ifndef SIFT3D_SLAB_LEN
#define SIFT3D_SLAB_LEN 500
#endif
#define SIFT3D_RESIZE_SLAB(slab, num_new, size) \
{ \
        const size_t slabs_new = ( (size_t) (num_new) + SIFT3D_SLAB_LEN - 1) / \
                        SIFT3D_SLAB_LEN; \
        const size_t size_new = slabs_new * SIFT3D_SLAB_LEN * (size); \
\
	if (size_new != (slab)->buf_size) { \
\
		/* Re-initialize if the new size is 0 */ \
		if (size_new == 0) { \
			cleanup_Slab(slab); \
			init_Slab(slab); \
		/* Else allocate new memory */ \
		} else if (((slab)->buf = SIFT3D_safe_realloc((slab)->buf, \
			size_new)) == NULL) { \
			return SIFT3D_FAILURE; \
		} \
		(slab)->buf_size = size_new; \
	} \
	(slab)->num = (num_new); \
}

// Nested loop through all elements of a matrix
#define SIFT3D_MAT_RM_LOOP_START(mat, row, col) \
	for ((row) = 0; (row) < (mat)->num_rows; (row)++) { \
	for ((col) = 0; (col) < (mat)->num_cols; (col)++) {

// Delmit a MAT_LOOP
#define SIFT3D_MAT_RM_LOOP_END }}

// Delimit the first level of a MAT_LOOP
#define SIFT3D_MAT_RM_LOOP_COL_END }

// Delmit the second level of a MAT_LOOP
#define SIFT3D_MAT_RM_LOOP_ROW_END } 

// Get the index of an element in a dense matrix, in row-major order.
#define SIFT3D_MAT_RM_GET_IDX(mat, row, col) \
        ((col) + (row) * (mat)->num_cols)

// Get an element from a dense matrix in row-major order. Type must
// be "double", "float", or "int."
#define SIFT3D_MAT_RM_GET(mat, row, col, type) ((mat)->u.data_ ## type \
	[SIFT3D_MAT_RM_GET_IDX(mat, row, col)])

/* Execute the macro MACRO, with the first argument set to the type of mat. If
 * there is an error, goto err_label. */
#define SIFT3D_MAT_RM_TYPE_MACRO(mat, err_label, MACRO, ...) \
        switch ((mat)->type) { \
                case SIFT3D_DOUBLE: \
                        MACRO(double, ## __VA_ARGS__) \
                        break; \
                case SIFT3D_FLOAT: \
                        MACRO(float, ## __VA_ARGS__) \
                        break; \
                case SIFT3D_INT: \
                        MACRO(int, ## __VA_ARGS__) \
                        break; \
                default: \
                        SIFT3D_ERR("imutil: unknown matrix type \n"); \
                        goto err_label; \
        } \

// Convert a vector from Cartesian to Spherical coordinates.
#define SIFT3D_CVEC_TO_SVEC(cvec, svec) { \
	(svec)->mag = sqrtf((cvec)->x * (cvec)->x + (cvec)->y * (cvec)->y + \
						(cvec)->z * (cvec)->z); \
	(svec)->az = fmodf(atan2f((cvec)->y, (cvec)->x) + SIFT3D_AZ_MAX_F, \
					  SIFT3D_AZ_MAX_F); \
	(svec)->po = fmodf(acosf((cvec)->z / ((svec)->mag + FLT_EPSILON)), \
		     SIFT3D_PO_MAX_F); \
}

// Convert a vector from Spherical to Cartesian coordinates
#define SIFT3D_SVEC_TO_CVEC(svec, cvec) { \
	(cvec)->x = (svec)->mag * sinf((svec)->po) * cosf((svec)->az); \
	(cvec)->y = (svec)->mag * sinf((svec)->po) * sinf((svec)->az); \
	(cvec)->z = (svec)->mag * cosf((svec)->po); \
}

// Return the L2 norm of a Cartesian coordinate vector
#define SIFT3D_CVEC_L2_NORM(cvec) \
	sqrtf((cvec)->x * (cvec)->x + (cvec)->y * (cvec)->y + \
	(cvec)->z * (cvec)->z)

// Return the square of the  L2 norm of a Cartesian coordinate vector
#define SIFT3D_CVEC_L2_NORM_SQ(cvec) \
	((cvec)->x * (cvec)->x + (cvec)->y * (cvec)->y + \
	(cvec)->z * (cvec)->z)

// Scale a Cartesian coordinate vector by a constant factor
#define SIFT3D_CVEC_SCALE(cvec, a) { \
    (cvec)->x = (cvec)->x * a; \
    (cvec)->y = (cvec)->y * a; \
    (cvec)->z = (cvec)->z * a; \
}

// Operate element-wise on two Cartesian coordinate vectors, cc = ca op cb
#define SIFT3D_CVEC_OP(ca, cb, op, cc) { \
    (cc)->x = (ca)->x op (cb)->x; \
    (cc)->y = (ca)->y op (cb)->y; \
    (cc)->z = (ca)->z op (cb)->z; \
}

// Return the dot product of two Cartesian coordinate 
// vectors
#define SIFT3D_CVEC_DOT(in1, in2) \
	((in1)->x * (in2)->x + (in1)->y * (in2)->y + (in1)->z * (in2)->z)

// Take the cross product of two Cartesian coordinate
// vectors, as out = in1 X in2
#define SIFT3D_CVEC_CROSS(in1, in2, out) { \
	(out)->x = (in1)->y * (in2)->z - (in1)->z * (in2)->y; \
	(out)->y = (in1)->z * (in2)->x - (in1)->x * (in2)->z; \
	(out)->z = (in1)->x * (in2)->y - (in1)->y * (in2)->x; \
} 

// Evaluates to true (nonzero) if im contains cvec, false otherwise
#define SIFT3D_IM_CONTAINS_CVEC(im, cvec) ( \
        (cvec)->x >= 0 || (cvec)->y >= 0 || (cvec)->z >= 0 || \
        (cvec)->x < (float) (im)->nx || \
        (cvec)->y < (float) (im)->ny || \
        (cvec)->z < (float) (im)->nz \
)

/* Computes v_out = mat * v_in. Note that mat must be of FLOAT
 * type, since this is the only type available for vectors. 
 * Also note that mat must be (3 x 3). */
#define SIFT3D_MUL_MAT_RM_CVEC(mat, v_in, v_out) { \
	(v_out)->x = SIFT3D_MAT_RM_GET(mat, 0, 0, float) * (v_in)->x + \
	    	     SIFT3D_MAT_RM_GET(mat, 0, 1, float) * (v_in)->y + \
                     SIFT3D_MAT_RM_GET(mat, 0, 2, float) * (v_in)->z; \
	\
	(v_out)->y = SIFT3D_MAT_RM_GET(mat, 1, 0, float) * (v_in)->x + \
                     SIFT3D_MAT_RM_GET(mat, 1, 1, float) * (v_in)->y + \
                     SIFT3D_MAT_RM_GET(mat, 1, 2, float) * (v_in)->z; \
	\
	(v_out)->z = SIFT3D_MAT_RM_GET(mat, 2, 0, float) * (v_in)->x + \
                     SIFT3D_MAT_RM_GET(mat, 2, 1, float) * (v_in)->y + \
                     SIFT3D_MAT_RM_GET(mat, 2, 2, float) * (v_in)->z; \
}

#ifdef __cplusplus
}
#endif

#endif


===========================================
Download .txt
gitextract_mi4zrg4r/

├── .gitignore
├── CHANGES.md
├── CMakeLists.txt
├── LICENSE
├── README.md
├── SIFT3DConfig.cmake.in
├── SIFT3DConfigVersion.cmake.in
├── cli/
│   ├── CMakeLists.txt
│   ├── denseSift3D.c
│   ├── kpSift3D.c
│   └── regSift3D.c
├── cmake/
│   ├── FindDCMTK.cmake
│   ├── FindMingw.cmake
│   ├── FindNIFTI.cmake
│   ├── FindOpenMP.cmake
│   └── SIFT3DPackage.cmake
├── doc/
│   ├── INSTALL_LINUX.md
│   ├── INSTALL_MAC.md
│   └── INSTALL_WINDOWS.md
├── examples/
│   ├── CMakeLists.txt
│   ├── featuresC.c
│   ├── featuresMatlab.m
│   ├── ioC.c
│   ├── ioMatlab.m
│   ├── manualFeaturesMatlab.m
│   ├── registerC.c
│   └── registerMatlab.m
├── imutil/
│   ├── CMakeLists.txt
│   ├── dicom.cpp
│   ├── dicom.h
│   ├── immacros.h
│   ├── imtypes.h
│   ├── imutil.c
│   ├── imutil.h
│   ├── kernels.cl
│   ├── nifti.c
│   ├── nifti.h
│   └── templates/
│       ├── CMakeLists.txt
│       └── sep_fir_3d.template
├── reg/
│   ├── CMakeLists.txt
│   ├── reg.c
│   └── reg.h
├── sift3d/
│   ├── CMakeLists.txt
│   ├── sift.c
│   └── sift.h
└── wrappers/
    ├── CMakeLists.txt
    └── matlab/
        ├── CMakeLists.txt
        ├── README.md
        ├── Sift3DParser.m
        ├── Sift3DTest.m
        ├── checkUnits3D.m
        ├── detectSift3D.m
        ├── extractSift3D.m
        ├── imRead3D.m
        ├── imWrite3D.m
        ├── keypoint3D.m
        ├── matchSift3D.m
        ├── mexDetectSift3D.c
        ├── mexExtractSift3D.c
        ├── mexImRead3D.c
        ├── mexImWrite3D.c
        ├── mexMatchSift3D.c
        ├── mexOrientation3D.c
        ├── mexRegisterSift3D.c
        ├── mexutil.c
        ├── mexutil.h
        ├── orientation3D.m
        ├── registerSift3D.m
        └── setupSift3D.m
Download .txt
SYMBOL INDEX (337 symbols across 22 files)

FILE: cli/denseSift3D.c
  function err_msg (line 49) | void err_msg(const char *msg) {
  function err_msgu (line 56) | void err_msgu(const char *msg) {
  function main (line 61) | int main(int argc, char **argv) {

FILE: cli/kpSift3D.c
  function err_msg (line 49) | static void err_msg(const char *msg) {
  function err_msgu (line 55) | static void err_msgu(const char *msg) {
  function main (line 61) | int main(int argc, char *argv[]) {

FILE: cli/regSift3D.c
  function print_help (line 41) | static void print_help() {
  function err_msg (line 89) | static void err_msg(const char *msg) {
  function err_msgu (line 95) | static void err_msgu(const char *msg) {
  function main (line 100) | int main(int argc, char *argv[]) {

FILE: examples/featuresC.c
  function demo (line 26) | int demo(void) {
  function main (line 104) | int main(void) {

FILE: examples/ioC.c
  function demo (line 21) | int demo(void) {
  function main (line 47) | int main(void) {

FILE: examples/registerC.c
  function demo (line 26) | int demo(void) {
  function main (line 90) | int main(void) {

FILE: imutil/dicom.cpp
  function dcm_error_message (line 18) | static int dcm_error_message() {
  function read_dcm (line 24) | int read_dcm(const char *path, Image *const im) {
  function read_dcm_dir (line 28) | int read_dcm_dir(const char *path, Image *const im) {
  function write_dcm (line 32) | int write_dcm(const char *path, const Image *const im,
  function write_dcm_dir (line 37) | int write_dcm_dir(const char *path, const Image *const im,
  type image_type (line 125) | enum image_type {
  class Dicom (line 132) | class Dicom
    method Dicom (line 172) | Dicom() : valid(false) {}
    method getDim (line 183) | int getDim(const int idx) const {
    method getUnit (line 197) | int getUnit(const int idx) const {
    method getAxes (line 211) | int getAxes(const int idx) const {
    method getAxisSign (line 216) | int getAxisSign(const int idx) const {
    method getNx (line 221) | int getNx(void) const {
    method getNy (line 226) | int getNy(void) const {
    method getNz (line 231) | int getNz(void) const {
    method getNc (line 236) | int getNc(void) const {
    method getSortAxis (line 241) | int getSortAxis(void) const {
    method getSortCoord (line 246) | double getSortCoord(void) const {
    method getSortUnit (line 251) | int getSortUnit(void) const {
    method getUx (line 256) | double getUx(void) const {
    method getUy (line 261) | double getUy(void) const {
    method getUz (line 266) | double getUz(void) const {
    method isValid (line 271) | bool isValid(void) const {
    method eqSeries (line 286) | bool eqSeries(const Dicom &dicom) const {
    method eqInstance (line 291) | bool eqInstance(const char *uid) const {
    method getType (line 296) | enum image_type getType() const {
  class Dicom (line 155) | class Dicom {
    method Dicom (line 172) | Dicom() : valid(false) {}
    method getDim (line 183) | int getDim(const int idx) const {
    method getUnit (line 197) | int getUnit(const int idx) const {
    method getAxes (line 211) | int getAxes(const int idx) const {
    method getAxisSign (line 216) | int getAxisSign(const int idx) const {
    method getNx (line 221) | int getNx(void) const {
    method getNy (line 226) | int getNy(void) const {
    method getNz (line 231) | int getNz(void) const {
    method getNc (line 236) | int getNc(void) const {
    method getSortAxis (line 241) | int getSortAxis(void) const {
    method getSortCoord (line 246) | double getSortCoord(void) const {
    method getSortUnit (line 251) | int getSortUnit(void) const {
    method getUx (line 256) | double getUx(void) const {
    method getUy (line 261) | double getUy(void) const {
    method getUz (line 266) | double getUz(void) const {
    method isValid (line 271) | bool isValid(void) const {
    method eqSeries (line 286) | bool eqSeries(const Dicom &dicom) const {
    method eqInstance (line 291) | bool eqInstance(const char *uid) const {
    method getType (line 296) | enum image_type getType() const {
  function load_file (line 313) | static int load_file(const char *path, DcmFileFormat &fileFormat) {
  function cvec_max_abs (line 329) | static int cvec_max_abs(const Cvec *v, float *const val, int *const pos) {
  function isLittleEndian (line 602) | static bool isLittleEndian(void) {
  function default_Dcm_meta (line 608) | static void default_Dcm_meta(Dcm_meta *const meta) {
  function parseTM (line 619) | int parseTM(const char *const tm, double *const time) {
  function read_dcm (line 744) | int read_dcm(const char *path, Image *const im) {
  function read_dcm_dir (line 755) | int read_dcm_dir(const char *path, Image *const im) {
  function write_dcm (line 775) | int write_dcm(const char *path, const Image *const im,
  function write_dcm_dir (line 794) | int write_dcm_dir(const char *path, const Image *const im,
  function read_dcm_cpp (line 806) | static int read_dcm_cpp(const char *path, Image *const im) {
  function read_dcm_img (line 827) | static int read_dcm_img(const Dicom &dicom, Image *const im) {
  function read_dso (line 1012) | static int read_dso(const char *imDir, Dicom &dso,
  function read_dcm_dir_meta (line 1152) | static int read_dcm_dir_meta(const char *path, std::vector<Dicom> &dicom...
  function dcm_resize_im (line 1218) | static int dcm_resize_im(const std::vector<Dicom> &dicoms, Image *const ...
  function write_subvolume (line 1371) | static int write_subvolume(const Dicom &dicom, Image *const main,
  function read_dcm_dir_cpp (line 1432) | static int read_dcm_dir_cpp(const char *path, Image *const im) {
  function set_meta_defaults (line 1474) | static void set_meta_defaults(const Dcm_meta *const meta,
  function write_dcm_cpp (line 1484) | static int write_dcm_cpp(const char *path, const Image *const im,
  function write_dcm_dir_cpp (line 1778) | static int write_dcm_dir_cpp(const char *path, const Image *const im,

FILE: imutil/dicom.h
  type Dcm_meta (line 24) | typedef struct _Dcm_meta {

FILE: imutil/imtypes.h
  type cl_uchar (line 54) | typedef unsigned char cl_uchar;
  type cl_device_id (line 55) | typedef int cl_device_id;
  type cl_command_queue (line 56) | typedef int cl_command_queue;
  type cl_device_type (line 57) | typedef int cl_device_type;
  type cl_platform_id (line 58) | typedef int cl_platform_id;
  type cl_context (line 59) | typedef int cl_context;
  type cl_image_format (line 60) | typedef int cl_image_format;
  type cl_mem_flags (line 61) | typedef int cl_mem_flags;
  type cl_kernel (line 62) | typedef int cl_kernel;
  type cl_mem (line 63) | typedef int cl_mem;
  type cl_mem_object_type (line 64) | typedef int cl_mem_object_type;
  type cl_program (line 65) | typedef int cl_program;
  type cl_int (line 66) | typedef int cl_int;
  type cl_bool (line 67) | typedef int cl_bool;
  type cl_uint (line 68) | typedef unsigned int cl_uint;
  type im_format (line 101) | typedef enum _im_format {
  type Mat_rm_type (line 111) | typedef enum _Mat_rm_type {
  type Kernels (line 118) | typedef struct _kernels {
  type CL_data (line 123) | typedef struct _CL_data {
  type Mat_rm (line 136) | typedef struct _Mat_rm {
  type Image (line 156) | typedef struct _Image {
  type Sep_FIR_filter (line 171) | typedef struct _Sep_FIR_filter {
  type Gauss_filter (line 182) | typedef struct _Gauss_filter {
  type GSS_filters (line 190) | typedef struct _GSS_filters {
  type SIFT_cl_kernels (line 200) | typedef struct _SIFT_cl_kernels {
  type Pyramid (line 207) | typedef struct _Pyramid {
  type Svec (line 226) | typedef struct _Svec {
  type Cvec (line 235) | typedef struct _Cvec {
  type Slab (line 244) | typedef struct _Slab {
  type Keypoint (line 253) | typedef struct _Keypoint {
  type Keypoint_store (line 264) | typedef struct _Keypoint_store {
  type Hist (line 274) | typedef struct _Hist {
  type Tri (line 279) | typedef struct _Tri {
  type Mesh (line 285) | typedef struct _Mesh {
  type SIFT3D_Descriptor (line 291) | typedef struct _SIFT3D_Descriptor {
  type SIFT3D_Descriptor_store (line 299) | typedef struct _SIFT3D_Descriptor_store {
  type SIFT3D (line 309) | typedef struct _SIFT3D {
  type tform_type (line 337) | typedef enum _tform_type {
  type interp_type (line 343) | typedef enum _interp_type {
  type Tform_vtable (line 349) | typedef struct _Tform_vtable {
  type Tform (line 368) | typedef struct _Tform {
  type Affine (line 374) | typedef struct _Affine {
  type Tps (line 380) | typedef struct _Tps {
  type Ransac (line 388) | typedef struct _Ransac {

FILE: imutil/imutil.c
  function strnlen (line 58) | size_t strnlen(const char *string, size_t maxlen) {
  type mwSignedIndex (line 159) | typedef mwSignedIndex fortran_int;
  type fortran_int (line 170) | typedef int32_t fortran_int;
  function clFinish_all (line 264) | void clFinish_all()
  function check_cl_error (line 277) | void check_cl_error(int err, const char *msg)
  function SIFT3D_IGNORE_UNUSED (line 294) | SIFT3D_IGNORE_UNUSED
  function init_cl (line 340) | int init_cl(CL_data * user_cl_data, const char *platform_name,
  function SIFT3D_IGNORE_UNUSED (line 504) | SIFT3D_IGNORE_UNUSED
  function init_Mesh (line 549) | void init_Mesh(Mesh * const mesh)
  function cleanup_Mesh (line 557) | void cleanup_Mesh(Mesh * const mesh)
  function convert_Mat_rm (line 567) | int convert_Mat_rm(const Mat_rm * const in, Mat_rm * const out,
  function init_Mat_rm (line 631) | int init_Mat_rm(Mat_rm *const mat, const int num_rows, const int num_cols,
  function init_Mat_rm_p (line 655) | int init_Mat_rm_p(Mat_rm *const mat, const void *const p, const int num_...
  function sprint_type_Mat_rm (line 678) | void sprint_type_Mat_rm(const Mat_rm * const mat, char *const str)
  function concat_Mat_rm (line 702) | int concat_Mat_rm(const Mat_rm * const src1, const Mat_rm * const src2,
  function copy_Mat_rm (line 786) | int copy_Mat_rm(const Mat_rm * const src, Mat_rm * const dst)
  function print_Mat_rm (line 803) | int print_Mat_rm(const Mat_rm * const mat)
  function resize_Mat_rm (line 844) | int resize_Mat_rm(Mat_rm *const mat) {
  function zero_Mat_rm (line 900) | int zero_Mat_rm(Mat_rm *const mat)
  function identity_Mat_rm (line 934) | int identity_Mat_rm(const int n, Mat_rm *const mat) {
  function cleanup_Mat_rm (line 962) | void cleanup_Mat_rm(Mat_rm *mat) {
  function draw_grid (line 973) | int draw_grid(Image * grid, int nx, int ny, int nz, int spacing, int lin...
  function draw_points (line 1012) | int draw_points(const Mat_rm * const in, const int *const dims, int radius,
  function draw_lines (line 1063) | int draw_lines(const Mat_rm * const points1, const Mat_rm * const points2,
  function im_format (line 1166) | im_format im_get_format(const char *path) {
  function im_read (line 1215) | int im_read(const char *path, Image *const im) {
  function im_write (line 1263) | int im_write(const char *path, const Image *const im) {
  function write_Mat_rm (line 1343) | int write_Mat_rm(const char *path, const Mat_rm * const mat)
  function init_im_with_dims (line 1426) | int init_im_with_dims(Image *const im, const int nx, const int ny, const...
  function im_default_stride (line 1453) | void im_default_stride(Image *const im)
  function im_pad (line 1471) | int im_pad(const Image * const im, Image * const pad)
  function im_resize (line 1527) | int im_resize(Image *const im)
  function im_concat (line 1613) | int im_concat(const Image * const src1, const Image * const src2, const ...
  function im_upsample_2x (line 1685) | int im_upsample_2x(const Image *const src, Image *const dst)
  function SIFT3D_IGNORE_UNUSED (line 2397) | SIFT3D_IGNORE_UNUSED
  function convolve_sep_sym (line 2455) | static int convolve_sep_sym(const Image * const src, Image * const dst,
  function im_permute (line 2476) | int im_permute(const Image * const src, const int dim1, const int dim2,
  function im_restride (line 2537) | int im_restride(const Image * const src, const size_t *const strides,
  function init_tform (line 2563) | int init_tform(void *const tform, const tform_type type)
  function init_Affine (line 2585) | int init_Affine(Affine * const affine, const int dim)
  function copy_tform (line 2606) | int copy_tform(const void *const src, void *const dst)
  function copy_Affine (line 2612) | static int copy_Affine(const void *const src, void *const dst)
  function copy_Tps (line 2622) | static int copy_Tps(const void *const src, void *const dst)
  function Affine_set_mat (line 2631) | int Affine_set_mat(const Mat_rm * const mat, Affine * const affine)
  function apply_tform_xyz (line 2642) | void apply_tform_xyz(const void *const tform, const double x_in,
  function apply_Affine_xyz (line 2651) | static void apply_Affine_xyz(const void *const affine, const double x_in,
  function apply_Tps_xyz (line 2676) | static void apply_Tps_xyz(const void *const tps, const double x_in,
  function apply_tform_Mat_rm (line 2733) | int apply_tform_Mat_rm(const void *const tform, const Mat_rm * const mat...
  function apply_Tps_Mat_rm (line 2743) | static int apply_Tps_Mat_rm(const void *const tps, const Mat_rm * const ...
  function tform_type (line 2808) | tform_type tform_get_type(const void *const tform)
  function tform_get_size (line 2814) | size_t tform_get_size(const void *const tform)
  function tform_type_get_size (line 2820) | size_t tform_type_get_size(const tform_type type)
  function Affine_get_size (line 2834) | static size_t Affine_get_size(void)
  function Tps_get_size (line 2840) | static size_t Tps_get_size(void)
  function write_tform (line 2846) | int write_tform(const char *path, const void *const tform)
  function write_Affine (line 2852) | static int write_Affine(const char *path, const void *const tform)
  function write_Tps (line 2861) | static int write_Tps(const char *path, const void *const tform)
  function cleanup_tform (line 2871) | void cleanup_tform(void *const tform)
  function cleanup_Affine (line 2877) | static void cleanup_Affine(void *const affine)
  function cleanup_Tps (line 2886) | static void cleanup_Tps(void *const tps)
  function apply_Affine_Mat_rm (line 2910) | static int apply_Affine_Mat_rm(const void *const affine,
  function mul_Mat_rm (line 2923) | int mul_Mat_rm(const Mat_rm * const mat_in1, const Mat_rm * const mat_in2,
  function eigen_Mat_rm (line 2992) | int eigen_Mat_rm(Mat_rm * A, Mat_rm * Q, Mat_rm * L)
  function solve_Mat_rm (line 3089) | int solve_Mat_rm(const Mat_rm *const A, const Mat_rm *const B,
  function solve_Mat_rm_ls (line 3207) | int solve_Mat_rm_ls(const Mat_rm *const A, const Mat_rm *const B,
  function trace_Mat_rm (line 3301) | int trace_Mat_rm(Mat_rm * mat, void *trace)
  function transpose_Mat_rm (line 3338) | int transpose_Mat_rm(const Mat_rm *const src, Mat_rm *const dst)
  function det_symm_Mat_rm (line 3389) | int det_symm_Mat_rm(Mat_rm * mat, void *det)
  function apply_Sep_FIR_filter (line 3459) | int apply_Sep_FIR_filter(const Image * const src, Image * const dst,
  function init_Sep_FIR_filter (line 3552) | int init_Sep_FIR_filter(Sep_FIR_filter *const f, const int dim, const in...
  function cleanup_Sep_FIR_filter (line 3620) | void cleanup_Sep_FIR_filter(Sep_FIR_filter *const f)
  function init_im (line 3634) | void init_im(Image *const im)
  function init_Gauss_filter (line 3657) | int init_Gauss_filter(Gauss_filter * const gauss, const double sigma,
  function init_Gauss_incremental_filter (line 3713) | int init_Gauss_incremental_filter(Gauss_filter * const gauss,
  function cleanup_Gauss_filter (line 3737) | void cleanup_Gauss_filter(Gauss_filter * gauss)
  function init_GSS_filters (line 3744) | void init_GSS_filters(GSS_filters * const gss)
  function make_gss (line 3752) | int make_gss(GSS_filters * const gss, const Pyramid * const pyr)
  function cleanup_GSS_filters (line 3806) | void cleanup_GSS_filters(GSS_filters * const gss)
  function init_Pyramid (line 3832) | void init_Pyramid(Pyramid * const pyr)
  function resize_Pyramid (line 3858) | int resize_Pyramid(const Image *const im, const int first_level,
  function set_scales_Pyramid (line 3957) | int set_scales_Pyramid(const double sigma0, const double sigma_n,
  function copy_Pyramid (line 3995) | int copy_Pyramid(const Pyramid * const src, Pyramid * const dst)
  function cleanup_Pyramid (line 4051) | void cleanup_Pyramid(Pyramid * const pyr)
  function init_Slab (line 4071) | void init_Slab(Slab *const slab) {
  function cleanup_Slab (line 4078) | void cleanup_Slab(Slab * const slab)
  function write_pyramid (line 4093) | int write_pyramid(const char *path, Pyramid * pyr)
  function err_exit (line 4112) | void err_exit(const char *str)
  function SIFT3D_IGNORE_UNUSED (line 4120) | SIFT3D_IGNORE_UNUSED
  function mkpath (line 4145) | static int mkpath(const char *path, mode_t mode)
  function do_mkdir (line 4182) | static int do_mkdir(const char *path, mode_t mode)
  function cross_mkdir (line 4200) | static int cross_mkdir(const char *path, mode_t mode) {
  function init_Tps (line 4213) | int init_Tps(Tps * tps, int dim, int terms)
  function init_Ransac (line 4238) | void init_Ransac(Ransac *const ran)
  function set_err_thresh_Ransac (line 4245) | int set_err_thresh_Ransac(Ransac *const ran, double err_thresh)
  function set_num_iter_Ransac (line 4260) | int set_num_iter_Ransac(Ransac *const ran, int num_iter)
  function copy_Ransac (line 4274) | int copy_Ransac(const Ransac *const src, Ransac *const dst) {
  function n_choose_k (line 4286) | static int n_choose_k(const int n, const int k, int **ret) {
  function SIFT3D_IGNORE_UNUSED (line 4329) | SIFT3D_IGNORE_UNUSED
  function make_affine_matrix (line 4430) | static int make_affine_matrix(const Mat_rm *const pts_in, const int dim,
  function SIFT3D_IGNORE_UNUSED (line 4458) | SIFT3D_IGNORE_UNUSED
  function Mat_rm (line 4475) | static Mat_rm *extract_ctrl_pts_Tps(Tps * tps)
  function solve_system (line 4490) | static int solve_system(const Mat_rm *const src, const Mat_rm *const ref,
  function tform_err_sq (line 4575) | static double tform_err_sq(const void *const tform, const Mat_rm *const ...
  function ransac (line 4619) | static int ransac(const Mat_rm *const src, const Mat_rm *const ref,
  function resize_Tps (line 4727) | int resize_Tps(Tps * tps, int num_pts, int dim)
  function find_tform_ransac (line 4757) | int find_tform_ransac(const Ransac *const ran, const Mat_rm *const src,
  function parse_gnu (line 4891) | int parse_gnu(const int argc, char *const *argv)
  function print_bug_msg (line 4925) | void print_bug_msg()

FILE: imutil/nifti.c
  function nii_error_message (line 18) | static int nii_error_message() {
  function read_nii (line 24) | int read_nii(const char *path, Image *const im) {
  function write_nii (line 28) | int write_nii(const char *path, const Image *const im) {
  function read_nii (line 51) | int read_nii(const char *path, Image *const im)
  function write_nii (line 170) | int write_nii(const char *path, const Image *const im)

FILE: reg/reg.c
  function im2mm (line 43) | static int im2mm(const Mat_rm *const im, const double *const units,
  function mm2im (line 79) | static int mm2im(const double *const src_units, const double *const ref_...
  function init_Reg_SIFT3D (line 121) | int init_Reg_SIFT3D(Reg_SIFT3D *const reg) {
  function cleanup_Reg_SIFT3D (line 141) | void cleanup_Reg_SIFT3D(Reg_SIFT3D *const reg) {
  function set_nn_thresh_Reg_SIFT3D (line 151) | int set_nn_thresh_Reg_SIFT3D(Reg_SIFT3D *const reg, const double nn_thre...
  function set_Ransac_Reg_SIFT3D (line 164) | int set_Ransac_Reg_SIFT3D(Reg_SIFT3D *const reg, const Ransac *const ran) {
  function set_SIFT3D_Reg_SIFT3D (line 170) | int set_SIFT3D_Reg_SIFT3D(Reg_SIFT3D *const reg, const SIFT3D *const sif...
  function set_im_Reg_SIFT3D (line 183) | static int set_im_Reg_SIFT3D(Reg_SIFT3D *const reg, const Image *const im,
  function set_src_Reg_SIFT3D (line 222) | int set_src_Reg_SIFT3D(Reg_SIFT3D *const reg, const Image *const src) {
  function set_ref_Reg_SIFT3D (line 227) | int set_ref_Reg_SIFT3D(Reg_SIFT3D *const reg, const Image *const ref) {
  function register_SIFT3D (line 239) | int register_SIFT3D(Reg_SIFT3D *const reg, void *const tform) {
  function scale_SIFT3D (line 320) | static void scale_SIFT3D(const double *const factors,
  function register_SIFT3D_resample (line 366) | int register_SIFT3D_resample(Reg_SIFT3D *const reg, const Image *const src,
  function get_matches_Reg_SIFT3D (line 434) | int get_matches_Reg_SIFT3D(const Reg_SIFT3D *const reg, Mat_rm *const ma...

FILE: reg/reg.h
  type Reg_SIFT3D (line 23) | typedef struct _Reg_SIFT3D {

FILE: sift3d/sift.c
  function init_geometry (line 215) | static int init_geometry(SIFT3D *sift3d) {
  function cart2bary (line 335) | static int cart2bary(const Cvec * const cart, const Tri * const tri,
  function init_Keypoint_store (line 399) | void init_Keypoint_store(Keypoint_store *const kp) {
  function init_Keypoint (line 406) | int init_Keypoint(Keypoint *const key) {
  function resize_Keypoint_store (line 417) | int resize_Keypoint_store(Keypoint_store *const kp, const size_t num) {
  function copy_Keypoint (line 439) | int copy_Keypoint(const Keypoint *const src, Keypoint *const dst) {
  function cleanup_Keypoint_store (line 455) | void cleanup_Keypoint_store(Keypoint_store *const kp) {
  function init_SIFT3D_Descriptor_store (line 462) | void init_SIFT3D_Descriptor_store(SIFT3D_Descriptor_store *const desc) {
  function cleanup_SIFT3D_Descriptor_store (line 468) | void cleanup_SIFT3D_Descriptor_store(SIFT3D_Descriptor_store *const desc) {
  function resize_SIFT3D_Descriptor_store (line 476) | static int resize_SIFT3D_Descriptor_store(SIFT3D_Descriptor_store *const...
  function init_cl_SIFT3D (line 495) | static int init_cl_SIFT3D(SIFT3D *sift3d) {
  function set_peak_thresh_SIFT3D (line 514) | int set_peak_thresh_SIFT3D(SIFT3D *const sift3d,
  function set_corner_thresh_SIFT3D (line 527) | int set_corner_thresh_SIFT3D(SIFT3D *const sift3d,
  function set_num_kp_levels_SIFT3D (line 542) | int set_num_kp_levels_SIFT3D(SIFT3D *const sift3d,
  function set_sigma_n_SIFT3D (line 552) | int set_sigma_n_SIFT3D(SIFT3D *const sift3d,
  function set_sigma0_SIFT3D (line 568) | int set_sigma0_SIFT3D(SIFT3D *const sift3d,
  function init_SIFT3D (line 583) | int init_SIFT3D(SIFT3D *sift3d) {
  function copy_SIFT3D (line 629) | int copy_SIFT3D(const SIFT3D *const src, SIFT3D *const dst) {
  function cleanup_SIFT3D (line 659) | void cleanup_SIFT3D(SIFT3D *const sift3d) {
  function argv_remove (line 682) | static int argv_remove(const int argc, char **argv,
  function print_opts_SIFT3D (line 702) | void print_opts_SIFT3D(void) {
  function parse_args_SIFT3D (line 754) | int parse_args_SIFT3D(SIFT3D *const sift3d,
  function set_im_SIFT3D (line 883) | static int set_im_SIFT3D(SIFT3D *const sift3d, const Image *const im) {
  function set_scales_SIFT3D (line 916) | static int set_scales_SIFT3D(SIFT3D *const sift3d, const double sigma0,
  function resize_SIFT3D (line 938) | static int resize_SIFT3D(SIFT3D *const sift3d, const int num_kp_levels) {
  function build_gpyr (line 989) | static int build_gpyr(SIFT3D *sift3d) {
  function build_dog (line 1052) | static int build_dog(SIFT3D *sift3d) {
  function detect_extrema (line 1074) | static int detect_extrema(SIFT3D *sift3d, Keypoint_store *kp) {
  function SIFT3D_IGNORE_UNUSED (line 1215) | SIFT3D_IGNORE_UNUSED
  function refine_Hist (line 1236) | static void refine_Hist(Hist *hist) {
  function assign_orientations (line 1264) | static int assign_orientations(SIFT3D *const sift3d,
  function assign_orientation_thresh (line 1331) | static int assign_orientation_thresh(const Image *const im,
  function assign_eig_ori (line 1354) | static int assign_eig_ori(const Image *const im, const Cvec *const vcenter,
  function SIFT3D_assign_orientations (line 1534) | int SIFT3D_assign_orientations(const SIFT3D *const sift3d,
  function SIFT3D_detect_keypoints (line 1609) | int SIFT3D_detect_keypoints(SIFT3D *const sift3d, const Image *const im,
  function SIFT3D_IGNORE_UNUSED (line 1645) | SIFT3D_IGNORE_UNUSED
  function SIFT3D_desc_acc_interp (line 1687) | void SIFT3D_desc_acc_interp(const SIFT3D * const sift3d,
  function hist_zero (line 1824) | static void hist_zero(Hist *hist) {
  function SIFT3D_have_gpyr (line 1936) | int SIFT3D_have_gpyr(const SIFT3D *const sift3d) {
  function scale_Keypoint (line 1952) | static int scale_Keypoint(const Keypoint *const src,
  function smooth_scale_raw_input (line 1978) | static int smooth_scale_raw_input(const SIFT3D *const sift3d,
  function SIFT3D_extract_descriptors (line 2025) | int SIFT3D_extract_descriptors(SIFT3D *const sift3d,
  function verify_keys (line 2050) | static int verify_keys(const Keypoint_store *const kp, const Image *cons...
  function keypoint2base (line 2094) | static int keypoint2base(const Keypoint *const src, Keypoint *const dst) {
  function SIFT3D_extract_raw_descriptors (line 2131) | int SIFT3D_extract_raw_descriptors(SIFT3D *const sift3d,
  function _SIFT3D_extract_descriptors (line 2207) | static int _SIFT3D_extract_descriptors(SIFT3D *const sift3d,
  function normalize_hist (line 2246) | static void normalize_hist(Hist *const hist) {
  function postproc_Hist (line 2267) | static void postproc_Hist(Hist *const hist, const float norm) {
  function extract_dense_descrip_rotate (line 2295) | static int extract_dense_descrip_rotate(SIFT3D *const sift3d,
  function SIFT3D_extract_dense_descriptors (line 2354) | int SIFT3D_extract_dense_descriptors(SIFT3D *const sift3d,
  function extract_dense_descriptors_no_rotate (line 2429) | static int extract_dense_descriptors_no_rotate(SIFT3D *const sift3d,
  function vox2hist (line 2499) | static void vox2hist(const Image *const im, const int x, const int y,
  function hist2vox (line 2510) | static void hist2vox(Hist *const hist, const Image *const im, const int x,
  function extract_dense_descriptors_rotate (line 2521) | static int extract_dense_descriptors_rotate(SIFT3D *const sift3d,
  function Keypoint_store_to_Mat_rm (line 2597) | int Keypoint_store_to_Mat_rm(const Keypoint_store *const kp, Mat_rm *con...
  function SIFT3D_Descriptor_coords_to_Mat_rm (line 2628) | int SIFT3D_Descriptor_coords_to_Mat_rm(
  function SIFT3D_Descriptor_store_to_Mat_rm (line 2674) | int SIFT3D_Descriptor_store_to_Mat_rm(const SIFT3D_Descriptor_store *con...
  function Mat_rm_to_SIFT3D_Descriptor_store (line 2721) | int Mat_rm_to_SIFT3D_Descriptor_store(const Mat_rm *const mat,
  function SIFT3D_matches_to_Mat_rm (line 2784) | int SIFT3D_matches_to_Mat_rm(SIFT3D_Descriptor_store *d1,
  function SIFT3D_nn_match (line 2840) | int SIFT3D_nn_match(const SIFT3D_Descriptor_store *const d1,
  function match_desc (line 2892) | static int match_desc(const SIFT3D_Descriptor *const desc,
  function draw_matches (line 2990) | int draw_matches(const Image *const left, const Image *const right,
  function write_Keypoint_store (line 3143) | int write_Keypoint_store(const char *path, const Keypoint_store *const k...
  function write_SIFT3D_Descriptor_store (line 3206) | int write_SIFT3D_Descriptor_store(const char *path,

FILE: wrappers/matlab/mexDetectSift3D.c
  function mexFunction (line 16) | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prh...

FILE: wrappers/matlab/mexExtractSift3D.c
  function mexFunction (line 22) | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prh...

FILE: wrappers/matlab/mexImRead3D.c
  function mexFunction (line 15) | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prh...

FILE: wrappers/matlab/mexImWrite3D.c
  function mexFunction (line 15) | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prh...

FILE: wrappers/matlab/mexMatchSift3D.c
  function mexFunction (line 19) | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prh...

FILE: wrappers/matlab/mexOrientation3D.c
  function mexFunction (line 16) | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prh...

FILE: wrappers/matlab/mexRegisterSift3D.c
  function mexFunction (line 17) | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prh...

FILE: wrappers/matlab/mexutil.c
  function init (line 57) | static void init(void) {
  function fini (line 63) | static void fini(void) {
  function err_msg (line 68) | void err_msg(const char *name, const char *msg) {
  function err_msgu (line 78) | void err_msgu(const char *name, const char *msg) {
  function isDouble (line 85) | int isDouble(const mxArray *const mx) {
  function mwIndex (line 91) | mwIndex mxImGetIdx(const mxArray *const mx, const int x, const int y,
  function mxArray (line 105) | mxArray *im2mx(const Image *const im) {
  function mx2im (line 140) | int mx2im(const mxArray *const mx, Image *const im) {
  function mxArray (line 194) | mxArray *units2mx(const Image *const im) {
  function mx2units (line 234) | int mx2units(const mxArray *const mx, Image *const im) {
  function mx2imWithUnits (line 271) | int mx2imWithUnits(const mxArray *const data, const mxArray *const units,
  function mxArray (line 281) | mxArray *mat2mx(const Mat_rm *const mat) {
  function mx2mat (line 326) | int mx2mat(const mxArray *const mx, Mat_rm *const mat) {
  function mxArray (line 379) | mxArray *kp2mx(const Keypoint_store *const kp) {
  function mx2kp (line 445) | int mx2kp(const mxArray *const mx, Keypoint_store *const kp) {
  function mxArray (line 580) | mxArray *desc2mx(const SIFT3D_Descriptor_store *const desc) {
  function mx2desc (line 608) | int mx2desc(const mxArray *const mx, SIFT3D_Descriptor_store *const desc) {
  function mxArray (line 645) | mxArray *array2mx(const double *const array, const size_t len) {
  function mex_SIFT3D_detect_keypoints (line 672) | int mex_SIFT3D_detect_keypoints(const Image *const im,
  function mex_SIFT3D_assign_orientations (line 678) | int mex_SIFT3D_assign_orientations(const Image *const im,
  function mex_SIFT3D_extract_descriptors (line 684) | int mex_SIFT3D_extract_descriptors(const Keypoint_store *const kp,
  function mex_SIFT3D_extract_raw_descriptors (line 690) | int mex_SIFT3D_extract_raw_descriptors(const Image *const im,
  function mexHaveGpyr (line 696) | int mexHaveGpyr(void) {
  function mex_set_opts_SIFT3D (line 711) | int mex_set_opts_SIFT3D(const mxArray *const mx) {
  function mex_set_opts_Reg_SIFT3D (line 774) | int mex_set_opts_Reg_SIFT3D(const mxArray *const mx) {
  function mex_get_nn_thresh_Reg_SIFT3D (line 813) | double mex_get_nn_thresh_Reg_SIFT3D(void) {
  function mex_register_SIFT3D_resample (line 818) | int mex_register_SIFT3D_resample(const Image *const src,
  function mex_set_src_Reg_SIFT3D (line 824) | int mex_set_src_Reg_SIFT3D(const Image *const src) {
  function mex_set_ref_Reg_SIFT3D (line 829) | int mex_set_ref_Reg_SIFT3D(const Image *const ref) {
  function mex_register_SIFT3D (line 834) | int mex_register_SIFT3D(void *const tform) {
  function mex_get_matches_Reg_SIFT3D (line 839) | int mex_get_matches_Reg_SIFT3D(Mat_rm *const match_src,
Condensed preview — 69 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (639K chars).
[
  {
    "path": ".gitignore",
    "chars": 424,
    "preview": "################################################################################\n# Copyright (c) 2015 Blaine Rister et a"
  },
  {
    "path": "CHANGES.md",
    "chars": 4134,
    "preview": "# SIFT3D version history\n\n## 1.0.0\n\n* Initial release\n\n## 1.1.0\n\n* Added DICOM IO\n* Wrapped DICOM and NIFTI IO in C func"
  },
  {
    "path": "CMakeLists.txt",
    "chars": 7744,
    "preview": "################################################################################\n# Copyright (c) 2015-2017 Blaine Rister"
  },
  {
    "path": "LICENSE",
    "chars": 1093,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2015-2016 Blaine Rister et al.\n\nPermission is hereby granted, free of charge, to an"
  },
  {
    "path": "README.md",
    "chars": 3654,
    "preview": "# SIFT3D\n\nCopyright (c) 2015-2019 Blaine Rister et al., see LICENSE for details.\n\nSIFT3D is an analogue of the scale-inv"
  },
  {
    "path": "SIFT3DConfig.cmake.in",
    "chars": 722,
    "preview": "################################################################################\n# Copyright (c) 2015-2016 Blaine Rister"
  },
  {
    "path": "SIFT3DConfigVersion.cmake.in",
    "chars": 768,
    "preview": "################################################################################\n# Copyright (c) 2015-2016 Blaine Rister"
  },
  {
    "path": "cli/CMakeLists.txt",
    "chars": 880,
    "preview": "################################################################################\n# Copyright (c) 2015-2016 Blaine Rister"
  },
  {
    "path": "cli/denseSift3D.c",
    "chars": 5062,
    "preview": "/* -----------------------------------------------------------------------------\n * denseSift3d.c \n * ------------------"
  },
  {
    "path": "cli/kpSift3D.c",
    "chars": 7399,
    "preview": "/* -----------------------------------------------------------------------------\r\n * kpSift3D.c\r\n * --------------------"
  },
  {
    "path": "cli/regSift3D.c",
    "chars": 17077,
    "preview": "/* -----------------------------------------------------------------------------\n * regSift3D.c\n * ---------------------"
  },
  {
    "path": "cmake/FindDCMTK.cmake",
    "chars": 11293,
    "preview": "#.rst:\n# FindDCMTK\n# ---------\n#\n# Find DCMTK libraries and applications\n#\n# The module defines the following variables:"
  },
  {
    "path": "cmake/FindMingw.cmake",
    "chars": 2260,
    "preview": "# FindNIFTI.cmake\n#\n# Finds the MinGW runtime dependencies:\n#\n#   -libgcc\n#   -libstdc++\n#   -libgfortran\n#   -libquadma"
  },
  {
    "path": "cmake/FindNIFTI.cmake",
    "chars": 2188,
    "preview": "# - FindNIFTI.cmake\n#\n# Author: Thomas Proeger\n# Modified by Blaine Rister\n#\n# This cmake find module looks for the head"
  },
  {
    "path": "cmake/FindOpenMP.cmake",
    "chars": 12705,
    "preview": "#.rst:\n# FindOpenMP\n# ----------\n#\n# Finds OpenMP support\n#\n# This module can be used to detect OpenMP support in a comp"
  },
  {
    "path": "cmake/SIFT3DPackage.cmake",
    "chars": 4001,
    "preview": "################################################################################\n# SIFT3DPackage.cmake\n#################"
  },
  {
    "path": "doc/INSTALL_LINUX.md",
    "chars": 2517,
    "preview": "# SIFT3D Linux Installation Instructions\n\nCopyright (c) 2015-2018 Blaine Rister et al., see LICENSE for details.\n\n# Inst"
  },
  {
    "path": "doc/INSTALL_MAC.md",
    "chars": 2247,
    "preview": "# SIFT3D Mac Installation Instructions\n\nCopyright (c) 2015-2017 Blaine Rister et al., see LICENSE for details.\n\n# Instal"
  },
  {
    "path": "doc/INSTALL_WINDOWS.md",
    "chars": 6478,
    "preview": "# SIFT3D Windows Installation Instructions\n\nCopyright (c) 2015-2018 Blaine Rister et al., see LICENSE for details.\n\n## I"
  },
  {
    "path": "examples/CMakeLists.txt",
    "chars": 1524,
    "preview": "################################################################################\n# Copyright (c) 2015 Blaine Rister et a"
  },
  {
    "path": "examples/featuresC.c",
    "chars": 3236,
    "preview": "/* -----------------------------------------------------------------------------\n * featuresC.c\n * ---------------------"
  },
  {
    "path": "examples/featuresMatlab.m",
    "chars": 382,
    "preview": "% featuresMatlab.m\n%\n% This script shows how to extract SIFT3D features from a volumetric image.\n%\n% Copyright (c) 2015-"
  },
  {
    "path": "examples/ioC.c",
    "chars": 1422,
    "preview": "/* -----------------------------------------------------------------------------\n * ioC.c\n * ---------------------------"
  },
  {
    "path": "examples/ioMatlab.m",
    "chars": 266,
    "preview": "% ioMatlab\n%\n% Example of using file IO functions with SIFT3D\n%\n% Copyright (c) 2015 Blaine Rister et al., see LICENSE f"
  },
  {
    "path": "examples/manualFeaturesMatlab.m",
    "chars": 498,
    "preview": "% manualFeaturesMatlab.m\n%\n% This script shows how to manually define keypoints in a volumetric image,\n% and extract SIF"
  },
  {
    "path": "examples/registerC.c",
    "chars": 2702,
    "preview": "/* -----------------------------------------------------------------------------\n * registerC.c\n * ---------------------"
  },
  {
    "path": "examples/registerMatlab.m",
    "chars": 390,
    "preview": "% registerMatlab\n%\n% Example of image registration in Matlab\n%\n% Copyright (c) 2015-2016 Blaine Rister et al., see LICEN"
  },
  {
    "path": "imutil/CMakeLists.txt",
    "chars": 10450,
    "preview": "################################################################################\n# Copyright (c) 2015-2017 Blaine Rister"
  },
  {
    "path": "imutil/dicom.cpp",
    "chars": 67959,
    "preview": "/* -----------------------------------------------------------------------------\n * dicom.cpp \n * ----------------------"
  },
  {
    "path": "imutil/dicom.h",
    "chars": 1478,
    "preview": "/* -----------------------------------------------------------------------------\n * dicom.h \n * ------------------------"
  },
  {
    "path": "imutil/immacros.h",
    "chars": 13109,
    "preview": "/* -----------------------------------------------------------------------------\n * immacros.h\n * ----------------------"
  },
  {
    "path": "imutil/imtypes.h",
    "chars": 10692,
    "preview": "/* -----------------------------------------------------------------------------\r\n * imtypes.h\r\n * ---------------------"
  },
  {
    "path": "imutil/imutil.c",
    "chars": 135889,
    "preview": "/* -----------------------------------------------------------------------------\n * imutil.c\n * ------------------------"
  },
  {
    "path": "imutil/imutil.h",
    "chars": 8225,
    "preview": "/* -----------------------------------------------------------------------------\r\n * imutil.h\r\n * ----------------------"
  },
  {
    "path": "imutil/kernels.cl",
    "chars": 975,
    "preview": "/* -----------------------------------------------------------------------------\n * kernels.cl \n * ---------------------"
  },
  {
    "path": "imutil/nifti.c",
    "chars": 6188,
    "preview": "/* -----------------------------------------------------------------------------\n * nifti.c \n * ------------------------"
  },
  {
    "path": "imutil/nifti.h",
    "chars": 610,
    "preview": "/* -----------------------------------------------------------------------------\n * nifti.h \n * ------------------------"
  },
  {
    "path": "imutil/templates/CMakeLists.txt",
    "chars": 430,
    "preview": "################################################################################\n# Copyright (c) 2015-2016 Blaine Rister"
  },
  {
    "path": "imutil/templates/sep_fir_3d.template",
    "chars": 1063,
    "preview": "/* -----------------------------------------------------------------------------\n * sep_fir_3d.template \n * ------------"
  },
  {
    "path": "reg/CMakeLists.txt",
    "chars": 1803,
    "preview": "################################################################################\n# Copyright (c) 2015-2016 Blaine Rister"
  },
  {
    "path": "reg/reg.c",
    "chars": 14703,
    "preview": "/* -----------------------------------------------------------------------------\n * reg.c\n * ---------------------------"
  },
  {
    "path": "reg/reg.h",
    "chars": 1814,
    "preview": "/* -----------------------------------------------------------------------------\n * reg.h\n * ---------------------------"
  },
  {
    "path": "sift3d/CMakeLists.txt",
    "chars": 1856,
    "preview": "################################################################################\n# Copyright (c) 2015-2016 Blaine Rister"
  },
  {
    "path": "sift3d/sift.c",
    "chars": 106480,
    "preview": "/* -----------------------------------------------------------------------------\n * sift.c\n * --------------------------"
  },
  {
    "path": "sift3d/sift.h",
    "chars": 3912,
    "preview": "/* -----------------------------------------------------------------------------\r\n * sift.h\r\n * ------------------------"
  },
  {
    "path": "wrappers/CMakeLists.txt",
    "chars": 476,
    "preview": "################################################################################\n# Copyright (c) 2015-2017 Blaine Rister"
  },
  {
    "path": "wrappers/matlab/CMakeLists.txt",
    "chars": 2959,
    "preview": "################################################################################\n# Copyright (c) 2015-2016 Blaine Rister"
  },
  {
    "path": "wrappers/matlab/README.md",
    "chars": 2662,
    "preview": "# SIFT3D for Matlab\n\nCopyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n\n## Contents\n\nThis is a Matl"
  },
  {
    "path": "wrappers/matlab/Sift3DParser.m",
    "chars": 3065,
    "preview": "classdef Sift3DParser < inputParser\n    %Sift3DParser helper class to parse SIFT3D options. This class inherits\n    % fr"
  },
  {
    "path": "wrappers/matlab/Sift3DTest.m",
    "chars": 29286,
    "preview": "classdef Sift3DTest < TestCase\n%Sift3DTest a test suite for SIFT3D.\n%\n% To run this test suite, you must install xUnit. "
  },
  {
    "path": "wrappers/matlab/checkUnits3D.m",
    "chars": 876,
    "preview": "function units = checkUnits3D(units, name)\n%checkUnits3D helper function to check the validity of the 'units' argument\n%"
  },
  {
    "path": "wrappers/matlab/detectSift3D.m",
    "chars": 2291,
    "preview": "function keys = detectSift3D(im, varargin)\n%detectSift3D(im, options) Detect Sift3D keypoints in an image.\n%  Arguments:"
  },
  {
    "path": "wrappers/matlab/extractSift3D.m",
    "chars": 3464,
    "preview": "function [desc, coords] = extractSift3D(keys, im, units)\n%extractSift3D(keys, im, units) Extract Sift3D descriptors from"
  },
  {
    "path": "wrappers/matlab/imRead3D.m",
    "chars": 1847,
    "preview": "function [im, units] = imRead3D(path)\n%imRead3D(im) Read a 3D image from a file.\n%  Arguments:\n%    path - The path to t"
  },
  {
    "path": "wrappers/matlab/imWrite3D.m",
    "chars": 1801,
    "preview": "function imWrite3D(path, im, units)\n%imWrite3D(im) Write a 3D image to a file.\n%  Arguments:\n%    path - The path to the"
  },
  {
    "path": "wrappers/matlab/keypoint3D.m",
    "chars": 3531,
    "preview": "function keys = keypoint(coords, scale, ori)\n%keypoint3D(coords, scales, orientations) Create an array of Keypoint \n% st"
  },
  {
    "path": "wrappers/matlab/matchSift3D.m",
    "chars": 2847,
    "preview": "function matches = matchSift3D(desc1, coords1, desc2, coords2, nnThresh)\n%matchSift3D(desc1, desc2)\n% Match SIFT3D desri"
  },
  {
    "path": "wrappers/matlab/mexDetectSift3D.c",
    "chars": 2525,
    "preview": "/* -----------------------------------------------------------------------------\n * mexDetectSift3D.c\n * ---------------"
  },
  {
    "path": "wrappers/matlab/mexExtractSift3D.c",
    "chars": 3931,
    "preview": "/* -----------------------------------------------------------------------------\n * mexExtractSift3D.c\n * --------------"
  },
  {
    "path": "wrappers/matlab/mexImRead3D.c",
    "chars": 3586,
    "preview": "/* -----------------------------------------------------------------------------\n * mexImRead3D.c\n * -------------------"
  },
  {
    "path": "wrappers/matlab/mexImWrite3D.c",
    "chars": 2692,
    "preview": "/* -----------------------------------------------------------------------------\n * mexImWrite3D.c\n * ------------------"
  },
  {
    "path": "wrappers/matlab/mexMatchSift3D.c",
    "chars": 3473,
    "preview": "/* -----------------------------------------------------------------------------\n * mexMatchSift3D.c\n * ----------------"
  },
  {
    "path": "wrappers/matlab/mexOrientation3D.c",
    "chars": 2801,
    "preview": "/* -----------------------------------------------------------------------------\n * mexOrientation3D.c\n * --------------"
  },
  {
    "path": "wrappers/matlab/mexRegisterSift3D.c",
    "chars": 4679,
    "preview": "/* -----------------------------------------------------------------------------\n * mexRegisterSift3D.c\n * -------------"
  },
  {
    "path": "wrappers/matlab/mexutil.c",
    "chars": 26495,
    "preview": "/* -----------------------------------------------------------------------------\n * mexutil.c\n * -----------------------"
  },
  {
    "path": "wrappers/matlab/mexutil.h",
    "chars": 2668,
    "preview": "/* -----------------------------------------------------------------------------\n * mexutil.h\n * -----------------------"
  },
  {
    "path": "wrappers/matlab/orientation3D.m",
    "chars": 1954,
    "preview": "%orientation3D(keys, im, units) Assign 3D orientations to keypoints \n%   in an image. This is not needed if your keypoin"
  },
  {
    "path": "wrappers/matlab/registerSift3D.m",
    "chars": 4634,
    "preview": "function [A, matchSrc, matchRef] = registerSift3D(src, ref, varargin)\n%registerSift3D(src, ref, options) register a pair"
  },
  {
    "path": "wrappers/matlab/setupSift3D.m",
    "chars": 689,
    "preview": "% setupSift3D\n%\n% Run this script to set up the paths for the SIFT3D matlab toolbox. For \n% example, add the following l"
  }
]

About this extraction

This page contains the full source code of the bbrister/SIFT3D GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 69 files (591.7 KB), approximately 164.6k tokens, and a symbol index with 337 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!