[
  {
    "path": ".gitignore",
    "content": "################################################################################\n# Copyright (c) 2015 Blaine Rister et al., see LICENSE for details.\n################################################################################\n# Files to be ignored by git.\n################################################################################\n\n*.o\n*.nii\n*.DS_Store\n*.png\n\nbuild\nbuild/*\ndebug\ndebug/*\nscripts\nscripts/*\n*DEBUG*\n"
  },
  {
    "path": "CHANGES.md",
    "content": "# 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 functions im_read and im_write\n* Added matlab wrappers keypoint3D, imRead3D and imWrite3D\n* Changed C function SIFT3D_Extract_Descriptors to SIFT3D_Extract_Descriptors and SIFT3D_Extract_Raw_Descriptors\n* Fixed various bugs, improving keypoint and descriptor accuracy\n\n## 1.1.1 October 30, 2015\n\n* Performance optimizations\n* Fixes to DICOM slice ordering (im_read, imRead3D)\n* Write more DICOM metadata (im_write, imWrite3D)\n* Corrected Mac build instructions\n\n## 1.2.0 January 5, 2016\n\n* Fixed Mac linking issues\n* Take real-world units into account in SIFT3D keypoints, descriptors\n* Added Matlab wrapper for image registration\n* Optionally resample input images prior to registration (regSift3D, register_SIFT3D_resample)\n* Write more NIFTI metadata (im_write, imWrite3D)\n* Changed C interface for descriptor extraction to disallow providing a custom Gaussian scale-space pyramid (SIFT3D_Extract_Descriptors)\n* Allow arbitrary output sizes in im_inv_transform\n* Minor bug fixes\n\n## 1.3.0 March 28, 2016\n\n* Add parameters for keypoint detection to Matlab interface\n* Add registration parameters to Matlab and CLI\n* Default in Matlab to faster version of image registration that avoids resampling\n* Improved error handling\n* Removed unused, faulty options\n* Print internal error messages to the Matlab command prompt\n\n## 1.3.1 March 31, 2016\n\n* Removed option to set the number of octaves, which was causing bugs \n\n## 1.4.0 May 11, 2016\n\n* Fixed bugs to improve keypoint and registration accuracy, especially concerning rotations\n* Fixed bug that caused crashing on some large images\n* Dramatically reduced memory consumption of keypoint matching\n* Refactored SIFT3D_nn_match_fb into SIFT3D_nn_match, as there is now no reason to prefer forward to forward-backward matching\n* Renamed headers macros.h and types.h to immacros.h and imtypes.h, respectively\n\n## 1.4.1 May 25, 2016\n\n* Removed keypoint refinement step, which did not improve the accuracy.\n\n## 1.4.2 June 15, 2016\n\n* Added multithreading with OpenMP\n* Improved keypoint matching speed\n\n## 1.4.3 July 24, 2016\n\n* Fixed CMake settings to allow linking to CMake targets without dependencies\n* Updated for newer versions of CMake, MinGW\n* Update package dependencies for Ubuntu 16.04\n* Removed POSIX dependencies in header files to allow linking with Visual Studio\n* Added support for reading JPEG-compressed DICOM files\n* Ship both MS (.lib) and MinGW (.dll.a) import libraries on Windows\n* Ship with MinGW runtime libraries on Windows\n* Ship with OpenBLAS on Windows\n\n## 1.4.4 September 13, 2017\n\n* Add support for reading Dicom Segmentation Objects (DSOs)\n* Add the option to compile without DCMTK and nifticlib\n* Reading images no longer scales them (im_read, imRead3D)\n* Read DICOM CT scans in Hounsfield units (im_read, imRead3D)\n* Fix header includes for newer builds of MinGW (TDM-GCC)\n\n## 1.4.5 January 17, 2018\n\n* Fixed a bug in orientation assignment to improve the accuracy of SIFT3D descriptors. Thanks to KinMan for finding this bug.\n  * Change the default value of corner_thresh parameter to 0.4, to get the same number of keypoints as before the fix\n* Add a new Matlab wrapper function to enable matching pre-computed descriptors (matchSift3D.m)\n* 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.\n* Add keypoint octave indices to kpSift3D output. Thanks to v8korb for this suggestion.\n* Refactor RANSAC code for improved clarity and efficiency. Thanks to cslayers for this suggestion.\n\n## 1.4.6 November 12, 2019\n\n* Fix MEX file compilation for Matlab 2018b and newer\n* Improve Nifti-1 image reading to take into account slope and intercept\n* Convert PET scans to SUV\n* 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.\n* Support 4D Nifti files\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "################################################################################\n# Copyright (c) 2015-2017 Blaine Rister et al., see LICENSE for details.\n################################################################################\n# Top-level build file for SIFT3D.\n################################################################################\n\ninclude (ExternalProject)\n\n# Minimum CMake version\ncmake_minimum_required (VERSION 3.3)\n\n# Minimum C++ version\nset (CMAKE_CXX_STANDARD 11)\nset (CMAKE_CXX_STANDARD_REQUIRED ON)\n\n# Convert GNU archives to .lib files in Windows\nif (WIN32)\n\tset (CMAKE_GNUtoMS ON\n\t\tCACHE BOOL \n\t\t\"Convert .dll.a files to .lib\" \n\t)\nendif ()\n\n# Project name\nproject (SIFT3D)\n\n# Project version\nset (SIFT3D_VERSION 1.4.6)\n\n# Default build type\nif (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)\n        message (STATUS  \"Build type not specified. Defaulting to 'Release'.\")\n        set (CMAKE_BUILD_TYPE Release CACHE STRING \"The type of build\" FORCE)\n        set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS \"Debug\" \"Release\")\nendif ()\n\n# Default settings for optional components\nif (WIN32)\n        set (_BUILD_CLI OFF)\nelse ()\n        set (_BUILD_CLI ON)\nendif()\n\n# Optional component variables\nset (BUILD_CLI ${_BUILD_CLI} CACHE BOOL \n        \"If ON, builds the command line interface\")\nset (BUILD_EXAMPLES \"ON\" CACHE BOOL \"If ON, builds the example programs\")\nset (BUILD_PACKAGE \"OFF\" CACHE BOOL \"If ON, builds the package generator\")\n\n# Configurable paths        \nset (INSTALL_LIB_DIR \"lib/sift3d\" CACHE PATH \n        \"Installation directory for libraries\")\nset (INSTALL_BIN_DIR \"bin\" CACHE PATH \n        \"Installation directory for executables\")\nset (INSTALL_INCLUDE_DIR \"include/sift3d\" CACHE PATH \n        \"Installation directory for header files\")\nif (WIN32 AND NOT CYGWIN)\n        set (DEFAULT_INSTALL_CMAKE_DIR \"cmake\")\nelse ()\n        set (DEFAULT_INSTALL_CMAKE_DIR \"lib/cmake/sift3d\")\nendif ()\nset (INSTALL_CMAKE_DIR ${DEFAULT_INSTALL_CMAKE_DIR} CACHE PATH\n        \"Installation directory for CMake files\")\n\n# Internal paths \nlist (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake)\nset (LICENSE_FILE ${CMAKE_CURRENT_LIST_DIR}/LICENSE)\nset (README_FILE ${CMAKE_CURRENT_LIST_DIR}/README.md)\n\n# Configure RPATH options\nset (INSTALL_LIB_PATH \"${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}\")\nset (CMAKE_SKIP_BUILD_RPATH OFF)\nset (CMAKE_BUILD_WITH_INSTALL_RPATH OFF)\nset (CMAKE_INSTALL_RPATH ${INSTALL_LIB_PATH})\nset (CMAKE_INSTALL_RPATH_USE_LINK_PATH ON)\n\n# Output directories\nset (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)\nset (CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)\nset (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)\n\n# The name and location of the wrappers build and install directories\nset (WRAPPERS_DIR \"wrappers\")\nset (BUILD_WRAPPERS_DIR \"${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${WRAPPERS_DIR}\")\nset (INSTALL_WRAPPERS_DIR \"${INSTALL_LIB_DIR}/${WRAPPERS_DIR}\")\n\n# Build flags\nset (DEBUG_FLAGS \"-g -DVERBOSE\")\nset (RELEASE_FLAGS \"-O3 -DNDEBUG\")\n\n# GCC-specific flags\nif (CMAKE_C_COMPILER_ID STREQUAL \"GNU\" OR \n    CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\")\n    set (DEBUG_FLAGS \"${DEBUG_FLAGS} -ggdb3\")\nendif ()\n\n# OS-specific build flags\nif (APPLE)\n        # Enable undefined shared library symbols\n        set (CMAKE_SHARED_LINKER_FLAGS \n\t\t\"${CMAKE_SHARED_LINKER_FLAGS} -undefined dynamic_lookup\")\n\n        # Use rpath\n        set (CMAKE_MACOSX_RPATH ON)\nendif ()\n\nset (CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG} ${DEBUG_FLAGS}\")\nset (CMAKE_CXX_FLAGS_DEBUG \"${CMAKE_CXX_FLAGS_DEBUG} ${DEBUG_FLAGS}\")\nset (CMAKE_C_FLAGS_RELEASE \"${CMAKE_C_FLAGS_RELEASE} ${RELEASE_FLAGS}\")\nset (CMAKE_CXX_FLAGS_RELEASE \"${CMAKE_CXX_FLAGS_RELEASE} ${RELEASE_FLAGS}\")\n\n# Find the system libraries\nif (WIN32)\n        # m is not a separate library in Windows\n        set (M_LIBRARY \"\")\nelse () \n        find_library (M_LIBRARY m)\nendif ()\nfind_package (ZLIB REQUIRED)\n\n# Try to find and use OpenMP\nfind_package (OpenMP)\nif (OPENMP_FOUND)\n        # Nothing extra needs to be done here.\nelseif (WITH_OpenMP)\n        message (FATAL_ERROR \"OpenMP not found. Please provide a compiler that \"\n                \"supports OpenMP, or disable the OpenMP by setting \"\n                \"the variable WITH_OpenMP to false.\")\nelse ()\n        message (STATUS \"Failed to find OpenMP. Compiling without it.\")\nendif ()\nset (WITH_OpenMP ${OPENMP_FOUND} CACHE BOOL \"If ON, parallelizes with OpenMP \"\n        \"in release mode\")\nif (WITH_OpenMP)\n        set (CMAKE_C_FLAGS_RELEASE \"${CMAKE_C_FLAGS_RELEASE} ${OpenMP_C_FLAGS}\")\n        set (CMAKE_CXX_FLAGS_RELEASE \"${CMAKE_CXX_FLAGS_RELEASE} ${OpenMP_CXX_FLAGS}\")\nendif ()\n\n# Look for MATLAB in the default locations\nfind_package (Matlab QUIET)\n\n# If not found, look for MATLAB in OS-specific locations\nif (NOT Matlab_FOUND)\n        if (APPLE)\n                set (Matlab_ROOT_DIR \n                        \"/Applications/Matlab.app\")\n                find_package (Matlab QUIET)\n        endif ()\nendif ()\n\n# Set up Matlab paths\nif (Matlab_FOUND)\n\tmessage (STATUS \"Found Matlab.\")\nelseif (BUILD_Matlab)\n        message (FATAL_ERROR \"Matlab not found. Please set the variable \"\n                \"Matlab_ROOT_DIR to the location of your Matlab installation, \"\n                \"or disable the Matlab toolbox by setting BUILD_Matlab to \"\n                \"false.\")\nelse ()\n        message (STATUS \"Matlab not found. The toolbox will not be built.\")\nendif ()\nset (BUILD_Matlab ${Matlab_FOUND} CACHE BOOL \"If ON, builds the Matlab toolbox\")\nif (BUILD_Matlab)\n        message (STATUS \"Building the Matlab toolbox.\")\n\n        # Set the name and location of the Matlab toolbox\n        set (TOOLBOX_NAME \"matlab\")\n        set (BUILD_TOOLBOX_DIR \"${BUILD_WRAPPERS_DIR}/${TOOLBOX_NAME}\")\n        set (INSTALL_TOOLBOX_DIR \"${INSTALL_WRAPPERS_DIR}/${TOOLBOX_NAME}\"\n                CACHE PATH \"Installation directory for the Matlab toolbox\")\n        set (INSTALL_TOOLBOX_PATH \"${CMAKE_INSTALL_PREFIX}/${INSTALL_TOOLBOX_DIR}\")\n        list (APPEND CMAKE_INSTALL_RPATH ${INSTALL_TOOLBOX_PATH})\n\n        # Get the path of the Matlab libraries\n        get_filename_component (Matlab_LIBRARIES_PATH ${Matlab_MEX_LIBRARY} \n                DIRECTORY)\n\n\t# Check for the MX library, which CMake cannot find on Windows\n\tif (NOT Matlab_MX_LIBRARY_FOUND)\n\n\t\t# Find Matlab's MX library\n\t\tfind_library (Matlab_MX_LIBRARY mx libmx \n                        PATHS ${Matlab_LIBRARIES_PATH}\n                )\n\tendif ()\n\n        # Find the Matlab LAPACK and BLAS libraries\n        find_library (Matlab_MWLAPACK_LIBRARY mwlapack libmwlapack \n                PATHS ${Matlab_LIBRARIES_PATH}\n        )\n        find_library (Matlab_MWBLAS_LIBRARY mwblas libmwblas \n                PATHS ${Matlab_LIBRARIES_PATH}\n        )\nendif ()\n\n# Generate the package configuration files\nset (CMAKE_CONFIG_FILE ${CMAKE_CURRENT_BINARY_DIR}/SIFT3DConfig.cmake)\nset (CMAKE_VERSION_FILE ${CMAKE_CURRENT_BINARY_DIR}/SIFT3DConfigVersion.cmake)\nconfigure_file (SIFT3DConfig.cmake.in\n        ${CMAKE_CONFIG_FILE} @ONLY\n)\nconfigure_file (SIFT3DConfigVersion.cmake.in\n        ${CMAKE_VERSION_FILE} @ONLY)\n\n# Install the package configuration files\ninstall (FILES ${CMAKE_CONFIG_FILE} ${CMAKE_VERSION_FILE} \n        DESTINATION ${INSTALL_CMAKE_DIR})\n\n# Install the targets file\ninstall (EXPORT SIFT3D-targets DESTINATION ${INSTALL_CMAKE_DIR})\n\n# Mandatory source subdirectories\nadd_subdirectory (imutil)\nadd_subdirectory (sift3d)\nadd_subdirectory (reg)\nadd_subdirectory (wrappers)\n\n# Command line interface\nif (BUILD_CLI)\n        add_subdirectory (cli)\nendif ()\n\n# Examples\nif (BUILD_EXAMPLES)\n        add_subdirectory (examples)\nendif ()\n\n# Packager file\nif (BUILD_PACKAGE)\n        include (SIFT3DPackage)\nendif ()\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015-2016 Blaine Rister et al.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "# SIFT3D\n\nCopyright (c) 2015-2019 Blaine Rister et al., see LICENSE for details.\n\nSIFT3D 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.\n\nSIFT3D 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.\n\n## Contents\n\nThis code creates the following executables:\n- kpSift3D - Extract keypoints and descriptors from a single image.\n- regSift3D - Extract matches and a geometric transformation from two images. \n\nand the following libraries:\n- libreg.so - Image registration from SIFT3D features\n- libsift3d.so - Extract and match SIFT3D features\n- libimutil.so - Utility library for image processing, regression and linear algebra. Includes IO functions for DICOM and NIFTI file formats.\n\nIt also contains a Matlab toolbox for calling the library functions from Matlab scripts. See the README in /wrappers/matlab for more information.\n\n## Installation instructions\n\nSee doc/INSTALL_\\<PLATFORM\\>.md for instructions on installing SIFT3D for your specific platform.\n\n## Usage instructions\n\nFor instructions on using the CLI, use the \"--help\" option, e.g. \n        kpSift3D --help\n\nSee /examples for sample programs using the C and Matlab APIs.\n\nThe following sections describe how to link a program to the SIFT3D libraries.\n\n### Linking to SIFT3D libraries with CMake\n\nSIFT3D exports a CMake package to the install directories. Here is an example of compiling a C program with SIFT3D from a CMake list.\n\n        find_package (SIFT3D) # Find SIFT3D\n        add_executable (helloWorld helloWorld.c) # Declare a target\n        target_link_libraries (helloWorld PUBLIC ${SIFT3D_LIBRARIES}) # Link to the SIFT3D libraries\n        if (WIN32) # Find the SIFT3D headers\n            target_include_directories (helloWorld PUBLIC \"${SIFT3D_DIR}/../${SIFT3D_INCLUDE_DIRS}\") \n        else()\n            target_include_directories (helloWorld PUBLIC ${SIFT3D_INCLUDE_DIRS}) \n        endif()\n\n### Linking to SIFT3D libraries without CMake\n\nThe 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.\n\n- libimutil - requires linking to LAPACK, BLAS, and zlib. Linking to DCMTK and nifticlib are optional.\n- libsift3d - requires linking to imutil\n- libreg - requires linking to sift3d and imutil\n\nInformation about the dependencies can be found in the installation instructions.\n\n*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.*\n\n## Contact\n\nPlease contact me at blaine@stanford.edu if you have any questions or concerns.\n\nIf you would like to cite this work, please refer to the following paper:\n\nB. 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.\ndoi: 10.1109/TIP.2017.2722689\n\nThe paper and automatic citations are available [here](http://ieeexplore.ieee.org/document/7967757/citations).\n"
  },
  {
    "path": "SIFT3DConfig.cmake.in",
    "content": "################################################################################\n# Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n################################################################################\n# SIFT3D package configuration file. Defines the following variables:\n#   SIFT3D_INCLUDE_DIRS - Include directories\n#   SIFT3D_LIBRARIES - Library targets\n################################################################################\n\n# Import the targets, if not already imported\nif (NOT TARGET reg)\n        include (${CMAKE_CURRENT_LIST_DIR}/SIFT3D-targets.cmake)\nendif() \n\n# Get the libraries\nset (SIFT3D_LIBRARIES reg sift3D imutil)\nset (SIFT3D_INCLUDE_DIRS @INSTALL_INCLUDE_DIR@)\n"
  },
  {
    "path": "SIFT3DConfigVersion.cmake.in",
    "content": "################################################################################\n# Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n################################################################################\n# SIFT3D package version file.\n################################################################################\n\nset (PACKAGE_VERSION @SIFT3D_VERSION@)\n\n# Check whether the requested PACKAGE_FIND_VERSION is compatible\nif (\"${PACKAGE_VERSION}\" VERSION_LESS \"${PACKAGE_FIND_VERSION}\")\n        set (PACKAGE_VERSION_COMPATIBLE FALSE)\nelse ()\n        set (PACKAGE_VERSION_COMPATIBLE TRUE)\n        if (\"${PACKAGE_VERSION}\" VERSION_EQUAL \"${PACKAGE_FIND_VERSION}\")\n                set (PACKAGE_VERSION_EXACT TRUE)\n        endif ()\nendif ()\n"
  },
  {
    "path": "cli/CMakeLists.txt",
    "content": "################################################################################\n# Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n################################################################################\n# Build file for the command line interfaces.\n################################################################################\n\nadd_executable (denseSift3D denseSift3D.c)\ntarget_link_libraries(denseSift3D PUBLIC sift3D imutil)\ntarget_link_libraries(denseSift3D PRIVATE ${M_LIBRARY})\n\nadd_executable(kpSift3D kpSift3D.c)\ntarget_link_libraries(kpSift3D PUBLIC sift3D imutil)\n\nadd_executable(regSift3D regSift3D.c)\ntarget_link_libraries(regSift3D PUBLIC reg sift3D imutil)\n\ninstall (TARGETS denseSift3D kpSift3D regSift3D\n\t RUNTIME DESTINATION ${INSTALL_BIN_DIR} \n\t LIBRARY DESTINATION ${INSTALL_LIB_DIR} \n\t ARCHIVE DESTINATION ${INSTALL_LIB_DIR})\n"
  },
  {
    "path": "cli/denseSift3D.c",
    "content": "/* -----------------------------------------------------------------------------\n * denseSift3d.c \n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * This file contains a command-line tool to extract dense SIFT3D features from \n * an image.\n * -----------------------------------------------------------------------------\n */\n\n#include <stdio.h>\n#include <string.h>\n#include <math.h>\n#include \"immacros.h\"\n#include \"imutil.h\"\n#include \"sift.h\"\n\n#define BUF_SIZE (1 << 10)\n\n/* The log tag */\nconst char tag[] = \"denseSift3d\";\n\n/* The help message */\nconst char help_msg[] = \n        \"Usage: denseSift3D [input.nii] [descriptors%.nii] \\n\"\n        \"\\n\"\n        \"Extracts a dense gradient histogram image from the input file. The \\n\"\n        \"output is a set of 12 images, each representing a channel or \\n\"\n        \"histogram bin. The last '%' character in the output filename is \\n\"\n        \"replaced by the channel index.\\n\"\n        \"\\n\"\n        \"Supported image formats: \\n\"\n\t\"\t.dcm (DICOM) \\n\"\n        \"\t.nii (nifti-1) \\n\"\n        \"\t.nii.gz (gzip-compressed nifti-1) \\n\"\n\t\"\tdirectory containing .dcm files \\n\"\n        \"\\n\"\n        \"Example: \\n\"\n        \"       denseSift3d in.nii.gz out%.nii.gz \\n\"\n        \"\\n\"\n        \"Upon completion, the output would be the following 12 images: \\n\"\n        \"       -out0.nii.gz \\n\"\n        \"       -out1.nii.gz \\n\"\n        \"            ... \\n\"\n        \"       -out11.nii.gz \\n\"\n        \"\\n\";\n          \n/* Print an error message. */      \nvoid err_msg(const char *msg) {\n        SIFT3D_ERR(\"%s: %s \\n\"\n                \"Use \\\"denseSift3d --help\\\" for more information. \\n\", tag, \n                msg);\n}\n\n/* Report an unexpected error. */\nvoid err_msgu(const char *msg) {\n        err_msg(msg);\n        print_bug_msg();\n}\n\nint main(int argc, char **argv) {\n\n        char out_name[BUF_SIZE], chan_str[BUF_SIZE];\n        Image im, desc, chan;\n        SIFT3D sift3d;\n        char *in_path, *out_path, *marker;\n        size_t len;\n        int c, marker_pos;\n\n        /* Parse the GNU standard options */\n        switch (parse_gnu(argc, argv)) {\n                case SIFT3D_HELP:\n                        puts(help_msg);\n                        return 0;\n                case SIFT3D_VERSION:\n                        return 0;\n        }\n\n        /* Parse the arguments */\n        if (argc < 3) {\n                err_msg(\"Not enough arguments.\");\n                return 1;\n        } else if (argc > 3) {\n                err_msg(\"Too many arguments.\");\n                return 1;\n        }\n        in_path = argv[1];\n        out_path = argv[2];\n\n        /* Initialize data */\n        init_im(&im);\n        init_im(&desc);\n        init_im(&chan);\n        if (init_SIFT3D(&sift3d)) {\n                err_msgu(\"Failed to initialize SIFT3D data.\");\n                return 1;\n        }\n\n        /* Read the image */        \n        if (im_read(in_path, &im)) {\n                \n                char msg[BUF_SIZE];\n\n                snprintf(msg, BUF_SIZE, \"Failed to read input image \\\"%s\\\".\",\n\t\t\tin_path);\n                err_msg(msg);\n                return 1;\n        }\n\n        /* Ensure the output file name has a % character */\n        if ((marker = strrchr(out_path, '%')) == NULL) {\n                err_msg(\"output filename must contain '%'.\");\n                return 1;\n        }\n        marker_pos = marker - out_path;\n\n        /* Get the output file name length */\n        len = strlen(out_path) + (int) ceil(log10((double) im.nc)) - 1;\n        if (len > BUF_SIZE) {\n\n                char msg[BUF_SIZE];\n\n                snprintf(msg, BUF_SIZE, \"Ouput filename cannot exceed %d \"\n\t\t\t\"characters.\", BUF_SIZE);\n                err_msg(msg);\n                return 1;\n        }\n\n        /* Extract the descriptors */\n        if (SIFT3D_extract_dense_descriptors(&sift3d, &im, &desc)) {\n                err_msgu(\"Failed to extract descriptors.\");\n                return 1;\n        }\n        \n\n        /* Write each channel as a separate image */\n        for (c = 0; c < desc.nc; c++) {\n\n                /* Get the channel */\n                if (im_channel(&desc, &chan, c)) {\n                        err_msgu(\"Failed to extract the channel.\");\n                        return 1;\n                }\n\n                /* Form the output file name */\n                out_name[0] = '\\0';\n                snprintf(chan_str, BUF_SIZE, \"%d\", c);\n                strncat(out_name, out_path, marker_pos);\n                strcat(out_name, chan_str);\n                strcat(out_name, marker + 1);\n\n                /* Write the channel */\n                if (im_write(out_name, &chan)) {\n\n                        char msg[BUF_SIZE];\n\n                        snprintf(msg, BUF_SIZE, \"Failed to write output image \"\n\t\t\t\t\"\\\"%s\\\".\", out_name);\n                        err_msg(msg);\n                        return 1;\n                }\n        }\n\n        return 0;\n}\n"
  },
  {
    "path": "cli/kpSift3D.c",
    "content": "/* -----------------------------------------------------------------------------\r\n * kpSift3D.c\r\n * -----------------------------------------------------------------------------\r\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\r\n * -----------------------------------------------------------------------------\r\n * This file contains the CLI to extract SIFT3D keypoints and descriptors from\r\n * a single image. \r\n * -----------------------------------------------------------------------------\r\n */\r\n\r\n#include <stdio.h>\r\n#include <getopt.h>\r\n#include \"immacros.h\"\r\n#include \"imutil.h\"\r\n#include \"sift.h\"\r\n\r\n/* Options */\r\n#define KEYS 'a'\r\n#define DESC 'b'\r\n#define DRAW 'c'\r\n\r\n/* Message buffer size */\r\n#define BUF_SIZE 1024\r\n\r\n/* Help message */\r\nconst char help_msg[] = \r\n        \"Usage: kpSift3D [image.nii] \\n\"\r\n        \"\\n\"\r\n        \"Detects SIFT3D keypoints and extracts their descriptors from an \"\r\n        \"image.\\n\" \r\n        \"\\n\"\r\n        \"Example: \\n\"\r\n        \" kpSift3D --keys keys.csv --desc desc.csv image.nii \\n\"\r\n        \"\\n\"\r\n        \"Output options: \\n\"\r\n        \" --keys [filename] \\n\"\r\n        \"       Specifies the output file name for the keypoints. \\n\"\r\n        \"       Supported file formats: .csv, .csv.gz \\n\"\r\n        \" --desc [filename] \\n\"\r\n        \"       Specifies the output file name for the descriptors. \\n\"\r\n        \"       Supported file formats: .csv, .csv.gz \\n\"\r\n        \" --draw [filename] \\n\"\r\n        \"       Draws the keypoints in image space. \\n\"\r\n        \"       Supported file formats: .dcm, .nii, .nii.gz, directory \\n\"\r\n        \"At least one of the output options must be specified. \\n\"\r\n        \"\\n\";\r\n\r\n/* Print an error message */\r\nstatic void err_msg(const char *msg) {\r\n        SIFT3D_ERR(\"kpSift3D: %s \\n\"\r\n                \"Use \\\"kpSift3D --help\\\" for more information. \\n\", msg);\r\n}\r\n\r\n/* Report an unexpected error. */\r\nstatic void err_msgu(const char *msg) {\r\n        err_msg(msg);\r\n        print_bug_msg();\r\n}\r\n\r\n/* CLI for 3D SIFT */\r\nint main(int argc, char *argv[]) {\r\n\r\n\tImage im;\r\n\tSIFT3D sift3d;\r\n\tKeypoint_store kp;\r\n\tSIFT3D_Descriptor_store desc;\r\n\tchar *im_path, *keys_path, *desc_path, *draw_path;\r\n        int c, num_args;\r\n\r\n        const struct option longopts[] = {\r\n                {\"keys\", required_argument, NULL, KEYS},\r\n                {\"desc\", required_argument, NULL, DESC},\r\n                {\"draw\", required_argument, NULL, DRAW},\r\n                {0, 0, 0, 0}\r\n        };\r\n\r\n        // Parse the GNU standard options\r\n        switch (parse_gnu(argc, argv)) {\r\n                case SIFT3D_HELP:\r\n                        puts(help_msg);\r\n                        print_opts_SIFT3D();\r\n                        return 0;\r\n                case SIFT3D_VERSION:\r\n                        return 0;\r\n                case SIFT3D_FALSE:\r\n                        break;\r\n                default:\r\n                        err_msgu(\"Unexpected return from parse_gnu \\n\");\r\n                        return 1;\r\n        }\r\n\r\n\t// Initialize the SIFT data \r\n\tif (init_SIFT3D(&sift3d)) {\r\n\t\terr_msgu(\"Failed to initialize SIFT data.\");\r\n                return 1;\r\n        }\r\n\r\n        // Parse the SIFT3D options and increment the argument list\r\n        if ((argc = parse_args_SIFT3D(&sift3d, argc, argv, SIFT3D_FALSE)) < 0)\r\n                return 1;\r\n\r\n        // Parse the kpSift3d options\r\n        opterr = 1;\r\n        keys_path = desc_path = draw_path = NULL;\r\n        while ((c = getopt_long(argc, argv, \"\", longopts, NULL)) != -1) {\r\n                switch (c) {\r\n                        case KEYS:\r\n                                keys_path = optarg;\r\n                                break;\r\n                        case DESC:\r\n                                desc_path = optarg;\r\n                                break;\r\n                        case DRAW:\r\n                                draw_path = optarg;\r\n                                break;\r\n                        case '?':\r\n                        default:\r\n                                return 1;\r\n                }\r\n        }\r\n\r\n        // Ensure we have at least one output\r\n        if (keys_path == NULL && desc_path == NULL && draw_path == NULL) {\r\n                err_msg(\"No outputs specified.\");\r\n                return 1;\r\n        }\r\n\r\n        // Parse the required arguments\r\n        num_args = argc - optind;\r\n        if (num_args < 1) {\r\n                err_msg(\"Not enough arguments.\");\r\n                return 1;\r\n        } else if (num_args > 1) {\r\n                err_msg(\"Too many arguments.\");\r\n                return 1;\r\n        }\r\n        im_path = argv[optind];\r\n\r\n\t// Initialize data \r\n\tinit_Keypoint_store(&kp); \r\n\tinit_SIFT3D_Descriptor_store(&desc); \r\n\tinit_im(&im);\r\n\r\n\t// Read the image\r\n\tif (im_read(im_path, &im)) {\r\n\t\terr_msg(\"Could not read image.\");\r\n                return 1;\r\n        }\r\n\r\n\t// Extract keypoints\r\n\tif (SIFT3D_detect_keypoints(&sift3d, &im, &kp)) {\r\n\t\terr_msgu(\"Failed to detect keypoints.\");\r\n                return 1;\r\n        }\r\n\r\n        // Optionally write the keypoints \r\n        if (keys_path != NULL && write_Keypoint_store(keys_path, &kp)) {\r\n\r\n                char msg[BUF_SIZE];\r\n\r\n                snprintf(msg, BUF_SIZE, \"Failed to write the keypoints to \"\r\n\t\t\t\"\\\"%s\\\"\", keys_path);\r\n                err_msg(msg);\r\n                return 1;\r\n        }\r\n\r\n        // Optionally extract descriptors\r\n        if (desc_path != NULL) {\r\n\r\n                // Extract descriptors\r\n\t        if (SIFT3D_extract_descriptors(&sift3d, &kp,&desc)) {\r\n                        err_msgu(\"Failed to extract descriptors.\");\r\n                        return 1;\r\n                }\r\n\r\n                // Write the descriptors\r\n                if (write_SIFT3D_Descriptor_store(desc_path, &desc)) {\r\n\r\n                        char msg[BUF_SIZE];\r\n\r\n                        snprintf(msg, BUF_SIZE, \"Failed to write the \"\r\n\t\t\t\t\"descriptors to \\\"%s\\\"\", desc_path);\r\n                        err_msg(msg);\r\n                        return 1;\r\n                }\r\n        }\r\n\r\n        // Optionally draw the keypoints\r\n        if (draw_path != NULL) {\r\n\r\n                Image draw;\r\n                Mat_rm keys;\r\n\r\n                // Initialize intermediates\r\n                init_im(&draw);\r\n                if (init_Mat_rm(&keys, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE))\r\n                        err_msgu(\"Failed to initialize keys matrix\");\r\n\r\n                // Convert to matrices\r\n                if (Keypoint_store_to_Mat_rm(&kp, &keys)) {\r\n                        err_msgu(\"Failed to convert the keypoints to \"\r\n                                 \"a matrix.\");\r\n                        return 1;\r\n                }\r\n\r\n                // Draw the points\r\n                if (draw_points(&keys, SIFT3D_IM_GET_DIMS(&im), 1, &draw)) {\r\n                        err_msgu(\"Failed to draw the points.\");\r\n                        return 1;\r\n                }\r\n\r\n                // Write the output\r\n                if (im_write(draw_path, &draw)) {\r\n                        \r\n                        char msg[BUF_SIZE];\r\n\r\n                        snprintf(msg, BUF_SIZE, \"Failed to draw the keypoints \"\r\n\t\t\t\t\"to \\\"%s\\\"\", draw_path);\r\n                        err_msg(msg);\r\n                        return 1;\r\n                }\r\n\r\n                // Clean up\r\n                im_free(&draw);\r\n        }\r\n\r\n\treturn 0;\r\n}\r\n"
  },
  {
    "path": "cli/regSift3D.c",
    "content": "/* -----------------------------------------------------------------------------\n * regSift3D.c\n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * This file contains the CLI to detect and match SIFT3D features between a pair\n * of images, and register one image to another.\n * -----------------------------------------------------------------------------\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <getopt.h>\n#include \"immacros.h\"\n#include \"imutil.h\"\n#include \"sift.h\"\n#include \"reg.h\"\n\n/* Option tags */\n#define MATCHES 'a'\n#define TRANSFORM 'b'\n#define WARPED 'c'\n#define CONCAT 'd'\n#define KEYS 'e'\n#define LINES 'f'\n#define NN_THRESH 'g'\n#define ERR_THRESH 'h'\n#define NUM_ITER 'l'\n#define TYPE 'm' \n#define RESAMPLE 'n'\n\n/* Message buffer size */\n#define BUF_SIZE 1024\n\n/* Internal parameters */\nconst interp_type interp = LINEAR; // Interpolation used for the warped image\nconst tform_type type_default = AFFINE; // Default transformation type\n        \n/* Print the help message */\nstatic void print_help() {\n        printf(\n        \"Usage: regSift3D [source.nii] [reference.nii] \\n\"\n        \"\\n\"\n        \"Matches SIFT3D features. \\n\"\n        \"\\n\"\n        \"Supported input formats: \\n\"\n        \" .nii (nifti-1) \\n\"\n        \" .nii.gz (gzip-compressed nifti-1) \\n\"\n        \"\\n\"\n        \"Example: \\n\"\n        \" regSift3D --nn_thresh 0.8 --matches matches.csv src.nii ref.nii \\n\"\n        \"\\n\"\n        \"Output options: \\n\"\n        \" --matches [filename] - The feature matches. \\n\"\n        \"       Supported file formats: .csv, .csv.gz \\n\"\n        \" --transform [filename] - The transformation parameters. \\n\"\n        \"       Supported file formats: .csv, .csv.gz \\n\"\n        \" --warped [filename] -  The warped source image. \\n\"\n        \"       Supported file formats: .dcm, .nii, .nii.gz, directory \\n\"\n        \" --concat [filename] - Concatenated images, with source on the left \\n\"\n        \"       Supported file formats: .dcm, .nii, .nii.gz, directory \\n\"\n        \" --keys [filename] - Keypoints drawn in the concatenated image \\n\"\n        \"       Supported file formats: .dcm, .nii, .nii.gz, directory \\n\"\n        \" --lines [filename] - Lines drawn between matching keypoints \\n\"\n        \"       Supported file formats: .dcm, .nii, .nii.gz, directory \\n\"\n        \"At least one output option must be specified. \\n\"\n        \"\\n\"\n        \"Other options: \\n\"\n        \" --nn_thresh [value] - Matching threshold on the nearest neighbor \\n\"\n        \"       ratio, in the interval (0, 1]. (default: %.2f) \\n\"\n        \" --err_thresh [value] - RANSAC inlier threshold, in the interval \\n\"\n        \"       (0, inf). This is a threshold on the squared Euclidean \\n\"\n        \"       distance in real-world units. (default: %.1f) \\n\"\n        \" --num_iter [value] - Number of RANSAC iterations. (default: %d) \\n\"\n        \" --type [value] - Type of transformation to be applied. \\n\"\n        \"       Supported arguments: \\\"affine\\\" (default: affine) \\n\"\n\t\" --resample - Internally resample the images to have the same \\n\"\n\t\"\tphysical resolution. This is slow. Use it when the images \\n\"\n\t\"\thave very different resolutions, for example registering 5mm \\n\"\n\t\"\tto 1mm slices. \\n\"\n        \"\\n\",\n        SIFT3D_nn_thresh_default, SIFT3D_err_thresh_default, \n        SIFT3D_num_iter_default);\n        print_opts_SIFT3D();\n}\n\n/* Print an error message */\nstatic void err_msg(const char *msg) {\n        SIFT3D_ERR(\"regSift3D: %s \\n\"\n                \"Use \\\"regSift3D --help\\\" for more information. \\n\", msg);\n}\n\n/* Report an unexpected error. */\nstatic void err_msgu(const char *msg) {\n        err_msg(msg);\n        print_bug_msg();\n}\n\nint main(int argc, char *argv[]) {\n\n        Reg_SIFT3D reg;\n        SIFT3D sift3d;\n        Ransac ran;\n        Image src, ref;\n        Mat_rm match_src, match_ref;\n        void *tform, *tform_arg;\n        char *src_path, *ref_path, *warped_path, *match_path, *tform_path,\n                *concat_path, *keys_path, *lines_path;\n        tform_type type;\n        int num_args, c, have_match, have_tform, resample;\n\n        const struct option longopts[] = {\n                {\"matches\", required_argument, NULL, MATCHES},\n                {\"transform\", required_argument, NULL, TRANSFORM},\n                {\"warped\", required_argument, NULL, WARPED},\n                {\"concat\", required_argument, NULL, CONCAT},\n                {\"keys\", required_argument, NULL, KEYS},\n                {\"lines\", required_argument, NULL, LINES},\n                {\"nn_thresh\", required_argument, NULL, NN_THRESH},\n                {\"err_thresh\", required_argument, NULL, ERR_THRESH},\n                {\"num_iter\", required_argument, NULL, NUM_ITER},\n                {\"type\", required_argument, NULL, TYPE},\n\t\t{\"resample\", no_argument, NULL, RESAMPLE},\n                {0, 0, 0, 0}\n        };\n\n        const char str_affine[] = \"affine\";\n\n        // Parse the GNU standard options\n        switch (parse_gnu(argc, argv)) {\n                case SIFT3D_HELP:\n                        print_help();\n                        return 0;\n                case SIFT3D_VERSION:\n                        return 0;\n                case SIFT3D_FALSE:\n                        break;\n                default:\n                        err_msgu(\"Unexpected return from parse_gnu.\");\n                        return 1;\n        }\n\n        // Initialize the data\n        init_im(&src);\n        init_im(&ref);\n        init_Reg_SIFT3D(&reg);\n        init_Ransac(&ran);\n        if (init_SIFT3D(&sift3d) ||\n                init_Mat_rm(&match_src, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE) ||\n                init_Mat_rm(&match_ref, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE)) {\n                err_msgu(\"Failed basic initialization.\");\n                return 1;\n        }\n\n        // Initialize parameters to defaults        \n        type = type_default;\n\n        // Parse the SIFT3D options\n        if ((argc = parse_args_SIFT3D(&sift3d, argc, argv, SIFT3D_FALSE)) < 0)\n                return 1;\n        if (set_SIFT3D_Reg_SIFT3D(&reg, &sift3d) ||\n                set_Ransac_Reg_SIFT3D(&reg, &ran)) {\n                err_msgu(\"Failed to save the SIFT3D or Ransac parameters.\");\n                return 1;\n        }\n\n        // Parse the remaining options \n        opterr = 1;\n        have_match = have_tform = resample = SIFT3D_FALSE;\n        match_path = tform_path = warped_path = concat_path = keys_path =\n                lines_path = NULL;\n        while ((c = getopt_long(argc, argv, \"\", longopts, NULL)) != -1) {\n                switch (c) {\n                case MATCHES:\n                        match_path = optarg;\n                        have_match = SIFT3D_TRUE;\n                        break;\n                case TRANSFORM:\n                        tform_path = optarg;\n                        have_tform = SIFT3D_TRUE;\n                        break;\n                case WARPED:\n                        warped_path = optarg;\n                        have_tform = SIFT3D_TRUE;\n                        break;\n                case CONCAT:\n                        concat_path = optarg;\n                        have_match = SIFT3D_TRUE;\n                        break;\n                case KEYS:\n                        keys_path = optarg;\n                        have_match = SIFT3D_TRUE;\n                        break;\n                case LINES:\n                        lines_path = optarg;\n                        have_match = SIFT3D_TRUE;\n                        break;\n                case NN_THRESH:\n                {\n                        const double nn_thresh = atof(optarg);\n                        if (set_nn_thresh_Reg_SIFT3D(&reg, nn_thresh)) {\n                                err_msg(\"Invalid value for nn_thresh.\");\n                                return 1;\n                        }\n                        break;\n                }\n                case ERR_THRESH:\n                {\n                        const double err_thresh = atof(optarg);\n                        if (set_err_thresh_Ransac(&ran, err_thresh)) {\n                                err_msg(\"Invalid value for err_thresh.\");\n                                return 1;\n                        }\n                        break;\n                }\n                case NUM_ITER:\n                {\n                        const int num_iter = atoi(optarg);\n                        if (set_num_iter_Ransac(&ran, num_iter)) {\n                                err_msg(\"Invalid value for num_iter.\");\n                                return 1;\n                        }\n                        break;\n                }\n                case TYPE:\n                        if (!strcmp(optarg, str_affine)) {\n                                type = AFFINE;\n                        } else {\n\n                                char msg[BUF_SIZE];\n\n                                snprintf(msg, BUF_SIZE,\n                                        \"Unrecognized transformation type: %s\",\n                                        optarg);\n                                err_msg(msg);\n                                return 1;\n                        }\n                        break;\n                case RESAMPLE:\n                        resample = SIFT3D_TRUE;\n                        break;\n                case '?':\n                default:\n                        return 1;\n                }\n        }\n\n        // Ensure that at least one output was specified\n        if (!have_match && !have_tform) {\n                err_msg(\"No outputs were specified.\");\n                return 1;\n        }\n\n        // Parse the required arguments\n        num_args = argc - optind;\n        if (num_args < 2) {\n                err_msg(\"Not enough arguments.\");\n                return 1;\n        } else if (num_args > 2) {\n                err_msg(\"Too many arguments.\");\n                return 1;\n        }\n        src_path = argv[optind];\n        ref_path = argv[optind + 1];\n\n        // Allocate memory for the transformation\n        if ((tform = malloc(tform_type_get_size(type))) == NULL) {\n                err_msg(\"Out of memory.\");\n                return 1;\n        }\n\n        // Initialize the transformation\n        if (init_tform(tform, type))\n                return 1;\n\n        // Read the images\n        if (im_read(src_path, &src)) {\n\n                char msg[BUF_SIZE];\n\n                snprintf(msg, BUF_SIZE, \"Failed to read the source image \"\n\t\t\t\"\\\"%s\\\"\", src_path);\n                err_msg(msg);\n                return 1;\n        }\n        if (im_read(ref_path, &ref)) {\n\n                char msg[BUF_SIZE];\n\n                snprintf(msg, BUF_SIZE, \"Failed to read the reference image \"\n\t\t\t\"\\\"%s\\\"\", ref_path);\n                err_msg(msg);\n                return 1;\n        }\n\n\t// Process the images\t\n\ttform_arg = have_tform ? tform : NULL;\n\tif (resample) {\n\t\t// Optionally register with resampling\n\t\tif (register_SIFT3D_resample(&reg, &src, &ref, LINEAR,\n\t\t\ttform_arg)) {\n\t\t\terr_msg(\"Failed to register the images with \"\n\t\t\t\t \"resampling. \\n\");\n\t\t\treturn 1;\n\t\t}\n\t} else {\n\n\t\t// Set the images\n\t\tif (set_src_Reg_SIFT3D(&reg, &src)) {\n\t\t\terr_msg(\"Failed to set the source image.\");\n\t\t\treturn 1;\n\t\t}\n\t\tif (set_ref_Reg_SIFT3D(&reg, &ref)) {\n\t\t\terr_msg(\"Failed to set the reference image.\");\n\t\t\treturn 1;\n\t\t}\n\n\t\t// Match the features, optionally registering the images \n\t\tif (register_SIFT3D(&reg, tform_arg)) {\n\t\t\terr_msg(\"Failed to register the images.\");\n\t\t\treturn 1;\n\t\t}\n\t}\n\n        // Convert the matches to matrices\n        if (get_matches_Reg_SIFT3D(&reg, &match_src, &match_ref)) {\n                err_msgu(\"Failed to convert matches to coordinates.\");\n                return 1;\n        }\n\n        // Write the outputs\n        if (match_path != NULL) {\n\n                Mat_rm matches;\n\n                // Initialize intermediates\n                init_Mat_rm(&matches, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE);\n\n                // Form a combined matrix for both sets of matches\n                if (concat_Mat_rm(&match_src, &match_ref, &matches, 1)) {\n                        err_msgu(\"Failed to concatenate the matches.\");\n                        return 1;\n                }\n\n                if (write_Mat_rm(match_path, &matches)) {\n\n                        char msg[BUF_SIZE];\n\n                        snprintf(msg, BUF_SIZE, \"Failed to write the matches \"\n\t\t\t\t\"\\\"%s\\\"\", match_path);\n                        err_msg(msg);\n                        return 1;\n                }\n\n                // Clean up\n                cleanup_Mat_rm(&matches);\n        }\n        if (tform_path != NULL && write_tform(tform_path, tform)) {\n\n                char msg[BUF_SIZE];\n\n                snprintf(msg, BUF_SIZE, \"Failed to write the transformation \"\n\t\t\t\"parameters \\\"%s\\\"\", tform_path);\n                err_msg(msg);\n                return 1;\n        }\n\n        // Optionally warp the source image\n        if (warped_path != NULL) {\n\n                Image warped;\n\n                // Initialize intermediates\n                init_im(&warped);\n\n                // Set the output to the reference image size\n                if (im_copy_dims(&ref, &warped)) {\n                        err_msgu(\"Failed to resize the warped image.\");\n                        return 1;\n                }\n\n                // Warp\n                if (im_inv_transform(tform, &src, interp, SIFT3D_FALSE, \n                        &warped)) {\n                        err_msgu(\"Failed to warp the source image.\");\n                        return 1;\n                }\n\n                // Write the warped image\n                if (im_write(warped_path, &warped)) {\n\n                        char msg[BUF_SIZE];\n\n                        snprintf(msg, BUF_SIZE, \"Failed to write the warped \"\n\t\t\t\t\"image \\\"%s\\\"\", warped_path);\n                        err_msg(msg);\n                        return 1;\n                }\n\n                // Clean up\n                im_free(&warped);\n        }\n\n        // Optionally draw the matches\n        if (concat_path != NULL || keys_path != NULL || lines_path != NULL) {\n\n                Image concat, keys, lines;\n                Mat_rm keys_src, keys_ref;\n\n                // Set up the pointers for the images\n                Image *const concat_arg = concat_path == NULL ? NULL : &concat;\n                Image *const keys_arg = keys_path == NULL ? NULL : &keys;\n                Image *const lines_arg = lines_path == NULL ? NULL : &lines;\n\n                // Initialize intermediates\n                init_im(&concat);\n                init_im(&keys);\n                init_im(&lines);\n                if (init_Mat_rm(&keys_src, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE) ||\n                        init_Mat_rm(&keys_ref, 0, 0, SIFT3D_DOUBLE, \n\t\t\t\tSIFT3D_FALSE)) {\n                        err_msgu(\"Failed to initialize keypoint matrices.\");\n                        return 1;\n                }\n\n                // Convert the keypoint coordinates to matrices\n                if (SIFT3D_Descriptor_coords_to_Mat_rm(&reg.desc_src, \n                        &keys_src) ||\n                    SIFT3D_Descriptor_coords_to_Mat_rm(&reg.desc_ref, \n                        &keys_ref)) {\n                        err_msgu(\"Failed to convert the keypoints to \"\n                                 \"matrices.\");\n                        return 1;\n                }\n\n                // Draw the matches\n                if (draw_matches(&src, &ref, &keys_src, &keys_ref, \n                        &match_src, &match_ref, concat_arg, keys_arg,\n                        lines_arg)) {\n                        err_msgu(\"Failed to draw the matches.\");\n                        return 1;\n                }\n\n                // Optionally write a concatenated image\n                if (concat_path != NULL && im_write(concat_path, &concat)) {\n\n                        char msg[BUF_SIZE];\n\n                        snprintf(msg, BUF_SIZE, \"Failed to write the \"\n\t\t\t\t\"concatenated image \\\"%s\\\"\", concat_path);\n                        err_msg(msg);\n                        return 1;\n                }\n\n                // Optionally write the keypoints\n                if (keys_path != NULL && im_write(keys_path, &keys)) {\n\n                        char msg[BUF_SIZE];\n\n                        snprintf(msg, BUF_SIZE, \"Failed to write the keypoint \"\n\t\t\t\t\"image \\\"%s\\\"\", concat_path);\n                        err_msg(msg);\n                        return 1;\n                }\n\n                // Optionally write the matches\n                if (lines_path != NULL && im_write(lines_path, &lines)) {\n\n                        char msg[BUF_SIZE];\n\n                        snprintf(msg, BUF_SIZE, \"Failed to write the line \"\n\t\t\t\t\"image \\\"%s\\\"\", concat_path);\n                        err_msg(msg);\n                        return 1;\n                }\n\n                // Clean up\n                im_free(&concat);\n                im_free(&keys);\n                im_free(&lines);\n        }\n\n\treturn 0;\n}\n"
  },
  {
    "path": "cmake/FindDCMTK.cmake",
    "content": "#.rst:\n# FindDCMTK\n# ---------\n#\n# Find DCMTK libraries and applications\n#\n# The module defines the following variables::\n#\n#  DCMTK_INCLUDE_DIRS  - Directories to include to use DCMTK\n#  DCMTK_LIBRARIES     - Files to link against to use DCMTK\n#  DCMTK_FOUND         - If false, don't try to use DCMTK\n#  DCMTK_DIR           - (optional) Source directory for DCMTK\n#\n# Compatibility\n# ^^^^^^^^^^^^^\n#\n# This module is able to find a version of DCMTK that does or does not export\n# a *DCMTKConfig.cmake* file. It applies a two step process:\n#\n# * Step 1:  Attempt to find DCMTK version providing a *DCMTKConfig.cmake* file.\n# * Step 2:  If step 1 failed, rely on *FindDCMTK.cmake* to set `DCMTK_*` variables details below.\n#\n#\n# `Recent DCMTK\n# <http://git.dcmtk.org/web?p=dcmtk.git;a=commit;h=662ae187c493c6b9a73dd5e3875372cebd0c11fe>`_\n# provides a *DCMTKConfig.cmake* :manual:`package configuration file\n# <cmake-packages(7)>`. To exclusively use the package configuration file\n# (recommended when possible), pass the `NO_MODULE` option to\n# :command:`find_package`. For example, `find_package(DCMTK NO_MODULE)`.\n# This requires official DCMTK snapshot *3.6.1_20140617* or newer.\n#\n#\n# Until all clients update to the more recent DCMTK, build systems will need\n# to support different versions of DCMTK.\n#\n# On any given system, the following combinations of DCMTK versions could be\n# considered:\n#\n# +--------+---------------------+-----------------------+-------------------+\n# |        |   SYSTEM DCMTK      |      LOCAL DCMTK      |     Supported ?   |\n# +--------+---------------------+-----------------------+-------------------+\n# | Case A |   NA                |      [ ] DCMTKConfig  |         YES       |\n# +--------+---------------------+-----------------------+-------------------+\n# | Case B |   NA                |      [X] DCMTKConfig  |         YES       |\n# +--------+---------------------+-----------------------+-------------------+\n# | Case C |   [ ] DCMTKConfig   |      NA               |         YES       |\n# +--------+---------------------+-----------------------+-------------------+\n# | Case D |   [X] DCMTKConfig   |      NA               |         YES       |\n# +--------+---------------------+-----------------------+-------------------+\n# | Case E |   [ ] DCMTKConfig   |      [ ] DCMTKConfig  |         YES (*)   |\n# +--------+---------------------+-----------------------+-------------------+\n# | Case F |   [X] DCMTKConfig   |      [ ] DCMTKConfig  |         NO        |\n# +--------+---------------------+-----------------------+-------------------+\n# | Case G |   [ ] DCMTKConfig   |      [X] DCMTKConfig  |         YES       |\n# +--------+---------------------+-----------------------+-------------------+\n# | Case H |   [X] DCMTKConfig   |      [X] DCMTKConfig  |         YES       |\n# +--------+---------------------+-----------------------+-------------------+\n#\n#  (*) See Troubleshooting section.\n#\n# Legend:\n#\n#   NA ...............: Means that no System or Local DCMTK is available\n#\n#   [ ] DCMTKConfig ..: Means that the version of DCMTK does NOT export a DCMTKConfig.cmake file.\n#\n#   [X] DCMTKConfig ..: Means that the version of DCMTK exports a DCMTKConfig.cmake file.\n#\n#\n# Troubleshooting\n# ^^^^^^^^^^^^^^^\n#\n# What to do if my project finds a different version of DCMTK?\n#\n# Remove DCMTK entry from the CMake cache per :command:`find_package`\n# documentation.\n\n#=============================================================================\n# Copyright 2004-2009 Kitware, Inc.\n# Copyright 2009-2010 Mathieu Malaterre <mathieu.malaterre@gmail.com>\n# Copyright 2010 Thomas Sondergaard <ts@medical-insight.com>\n#\n# Distributed under the OSI-approved BSD License (the \"License\");\n# see accompanying file Copyright.txt for details.\n#\n# This software is distributed WITHOUT ANY WARRANTY; without even the\n# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n# See the License for more information.\n#=============================================================================\n# (To distribute this file outside of CMake, substitute the full\n#  License text for the above reference.)\n\n#\n# Written for VXL by Amitha Perera.\n# Upgraded for GDCM by Mathieu Malaterre.\n# Modified for EasyViz by Thomas Sondergaard.\n#\n\nset(_dcmtk_dir_description \"The directory of DCMTK build or install tree.\")\n\n# Ensure that DCMTK_DIR is set to a reasonable default value\n# so that DCMTK libraries can be found on a standard Unix distribution.\n# It also overwrite the value of DCMTK_DIR after this one has been\n# set by a successful discovery of DCMTK by the unpatched FindDCMTK.cmake module\n# distributed with CMake (as of 0167cea)\nif(NOT DCMTK_DIR OR DCMTK_DIR STREQUAL \"/usr/include/dcmtk\")\n  set(DCMTK_DIR \"/usr\" CACHE PATH ${_dcmtk_dir_description} FORCE)\nendif()\n\nset(_SAVED_DCMTK_DIR ${DCMTK_DIR})\n\n#\n# Step1: Attempt to find a version of DCMTK providing a DCMTKConfig.cmake file.\n#\nif(NOT DCMTK_FIND_QUIETLY)\n  message(STATUS \"Trying to find DCMTK expecting DCMTKConfig.cmake\")\nendif()\nfind_package(DCMTK QUIET NO_MODULE)\nif(DCMTK_FOUND\n    AND NOT \"x\" STREQUAL \"x${DCMTK_LIBRARIES}\"\n    AND NOT \"x\" STREQUAL \"x${DCMTK_INCLUDE_DIRS}\")\n\n  if(NOT DCMTK_FIND_QUIETLY)\n    message(STATUS \"Trying to find DCMTK expecting DCMTKConfig.cmake - ok\")\n  endif()\n  return()\nelse()\n  if(NOT DCMTK_FIND_QUIETLY)\n    message(STATUS \"Trying to find DCMTK expecting DCMTKConfig.cmake - failed\")\n  endif()\nendif()\n\nif(NOT DCMTK_FIND_QUIETLY)\n  message(STATUS \"Trying to find DCMTK relying on FindDCMTK.cmake\")\nendif()\n\n# Restore the value reset by the previous call to 'find_package(DCMTK QUIET NO_MODULE)'\nset(DCMTK_DIR ${_SAVED_DCMTK_DIR} CACHE PATH ${_dcmtk_dir_description} FORCE)\n\n\n#\n# Step2: Attempt to find a version of DCMTK that does NOT provide a DCMTKConfig.cmake file.\n#\n\n# prefer DCMTK_DIR over default system paths like /usr/lib\nif(DCMTK_DIR)\n  set(CMAKE_PREFIX_PATH ${DCMTK_DIR}/lib ${CMAKE_PREFIX_PATH}) # this is given to FIND_LIBRARY or FIND_PATH\nendif()\n\n# Find all libraries, store debug and release separately\nforeach(lib\n    dcmpstat\n    dcmsr\n    dcmsign\n    dcmtls\n    dcmqrdb\n    dcmnet\n    dcmjpeg\n    dcmimage\n    dcmimgle\n    dcmdata\n    oflog\n    ofstd\n    ijg12\n    ijg16\n    ijg8\n    )\n\n  # Find Release libraries\n  find_library(DCMTK_${lib}_LIBRARY_RELEASE\n    ${lib}\n    PATHS\n    ${DCMTK_DIR}/${lib}/libsrc\n    ${DCMTK_DIR}/${lib}/libsrc/Release\n    ${DCMTK_DIR}/${lib}/Release\n    ${DCMTK_DIR}/lib\n    ${DCMTK_DIR}/lib/Release\n    ${DCMTK_DIR}/dcmjpeg/lib${lib}/Release\n    NO_DEFAULT_PATH\n    )\n\n  # Find Debug libraries\n  find_library(DCMTK_${lib}_LIBRARY_DEBUG\n    ${lib}${DCMTK_CMAKE_DEBUG_POSTFIX}\n    PATHS\n    ${DCMTK_DIR}/${lib}/libsrc\n    ${DCMTK_DIR}/${lib}/libsrc/Debug\n    ${DCMTK_DIR}/${lib}/Debug\n    ${DCMTK_DIR}/lib\n    ${DCMTK_DIR}/lib/Debug\n    ${DCMTK_DIR}/dcmjpeg/lib${lib}/Debug\n    NO_DEFAULT_PATH\n    )\n\n  mark_as_advanced(DCMTK_${lib}_LIBRARY_RELEASE)\n  mark_as_advanced(DCMTK_${lib}_LIBRARY_DEBUG)\n\n  # Add libraries to variable according to build type\n  if(DCMTK_${lib}_LIBRARY_RELEASE)\n    list(APPEND DCMTK_LIBRARIES optimized ${DCMTK_${lib}_LIBRARY_RELEASE})\n  endif()\n\n  if(DCMTK_${lib}_LIBRARY_DEBUG)\n    list(APPEND DCMTK_LIBRARIES debug ${DCMTK_${lib}_LIBRARY_DEBUG})\n  endif()\n\nendforeach()\n\nset(CMAKE_THREAD_LIBS_INIT)\nif(DCMTK_oflog_LIBRARY_RELEASE OR DCMTK_oflog_LIBRARY_DEBUG)\n  # Hack - Not having a DCMTKConfig.cmake file to read the settings from, we will attempt to\n  # find the library in all cases.\n  # Ideally, pthread library should be discovered only if DCMTK_WITH_THREADS is enabled.\n  set(CMAKE_THREAD_PREFER_PTHREAD TRUE)\n  find_package(Threads)\nendif()\n\nif(CMAKE_THREAD_LIBS_INIT)\n  list(APPEND DCMTK_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})\nendif()\n\n#\n# SPECIFIC CASE FOR DCMTK BUILD DIR as DCMTK_DIR\n# (as opposed to a DCMTK install dir)\n# Have to find the source directory.\nif(EXISTS ${DCMTK_DIR}/CMakeCache.txt)\n          load_cache(${DCMTK_DIR} READ_WITH_PREFIX \"EXT\"\n          DCMTK_SOURCE_DIR)\n  if(NOT EXISTS ${EXTDCMTK_SOURCE_DIR})\n    message(FATAL_ERROR\n      \"DCMTK build directory references\nnonexistant DCMTK source directory ${EXTDCMTK_SOURCE_DIR}\")\n  endif()\nendif()\n\nset(DCMTK_config_TEST_HEADER osconfig.h)\nset(DCMTK_dcmdata_TEST_HEADER dctypes.h)\nset(DCMTK_dcmimage_TEST_HEADER dicoimg.h)\nset(DCMTK_dcmimgle_TEST_HEADER dcmimage.h)\nset(DCMTK_dcmjpeg_TEST_HEADER djdecode.h)\nset(DCMTK_dcmnet_TEST_HEADER assoc.h)\nset(DCMTK_dcmpstat_TEST_HEADER dcmpstat.h)\nset(DCMTK_dcmqrdb_TEST_HEADER dcmqrdba.h)\nset(DCMTK_dcmsign_TEST_HEADER sicert.h)\nset(DCMTK_dcmsr_TEST_HEADER dsrtree.h)\nset(DCMTK_dcmtls_TEST_HEADER tlslayer.h)\nset(DCMTK_ofstd_TEST_HEADER ofstdinc.h)\nset(DCMTK_oflog_TEST_HEADER oflog.h)\nset(DCMTK_dcmjpls_TEST_HEADER djlsutil.h)\n\nset(DCMTK_INCLUDE_DIR_NAMES)\n\nforeach(dir\n    config\n    dcmdata\n    dcmimage\n    dcmimgle\n    dcmjpeg\n    dcmjpls\n    dcmnet\n    dcmpstat\n    dcmqrdb\n    dcmsign\n    dcmsr\n    dcmtls\n    ofstd\n    oflog)\n  if(EXTDCMTK_SOURCE_DIR)\n    set(SOURCE_DIR_PATH\n      ${EXTDCMTK_SOURCE_DIR}/${dir}/include/dcmtk/${dir})\n  endif()\n  find_path(DCMTK_${dir}_INCLUDE_DIR\n    ${DCMTK_${dir}_TEST_HEADER}\n    PATHS\n    ${DCMTK_DIR}/${dir}/include\n    ${DCMTK_DIR}/${dir}\n    ${DCMTK_DIR}/include/dcmtk/${dir}\n    ${DCMTK_DIR}/${dir}/include/dcmtk/${dir}\n    ${DCMTK_DIR}/include/${dir}\n    ${SOURCE_DIR_PATH}\n    )\n  mark_as_advanced(DCMTK_${dir}_INCLUDE_DIR)\n  list(APPEND DCMTK_INCLUDE_DIR_NAMES DCMTK_${dir}_INCLUDE_DIR)\n\n  if(DCMTK_${dir}_INCLUDE_DIR)\n    # add the 'include' path so eg\n    #include \"dcmtk/dcmimgle/dcmimage.h\"\n    # works\n    get_filename_component(_include ${DCMTK_${dir}_INCLUDE_DIR} PATH)\n    get_filename_component(_include ${_include} PATH)\n    list(APPEND\n      DCMTK_INCLUDE_DIRS\n      ${DCMTK_${dir}_INCLUDE_DIR}\n      ${_include})\n  endif()\nendforeach()\n\nlist(APPEND DCMTK_INCLUDE_DIRS ${DCMTK_DIR}/include)\n\nif(WIN32)\n  list(APPEND DCMTK_LIBRARIES netapi32 wsock32)\nendif()\n\nif(DCMTK_ofstd_INCLUDE_DIR)\n  get_filename_component(DCMTK_dcmtk_INCLUDE_DIR\n    ${DCMTK_ofstd_INCLUDE_DIR}\n    PATH\n    CACHE)\n  list(APPEND DCMTK_INCLUDE_DIRS ${DCMTK_dcmtk_INCLUDE_DIR})\n  mark_as_advanced(DCMTK_dcmtk_INCLUDE_DIR)\nendif()\n\n# Compatibility: This variable is deprecated\nset(DCMTK_INCLUDE_DIR ${DCMTK_INCLUDE_DIRS})\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(DCMTK\n  REQUIRED_VARS ${DCMTK_INCLUDE_DIR_NAMES} DCMTK_LIBRARIES\n  FAIL_MESSAGE \"Please set DCMTK_DIR and re-run configure\")\n\n# Workaround bug in packaging of DCMTK 3.6.0 on Debian.\n# See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=637687\nif(DCMTK_FOUND AND UNIX AND NOT APPLE)\n  include(CheckCXXSourceCompiles)\n  set(CMAKE_REQUIRED_FLAGS )\n  set(CMAKE_REQUIRED_DEFINITIONS )\n  set(CMAKE_REQUIRED_INCLUDES ${DCMTK_INCLUDE_DIRS})\n  set(CMAKE_REQUIRED_LIBRARIES ${DCMTK_LIBRARIES})\n  set(CMAKE_REQUIRED_QUIET ${DCMTK_FIND_QUIETLY})\n  check_cxx_source_compiles(\"#include <dcmtk/config/osconfig.h>\\n#include <dcmtk/ofstd/ofstream.h>\\nint main(int,char*[]){return 0;}\"\n    DCMTK_HAVE_CONFIG_H_OPTIONAL\n    )\n  if(NOT DCMTK_HAVE_CONFIG_H_OPTIONAL)\n    set(DCMTK_DEFINITIONS \"HAVE_CONFIG_H\")\n  endif()\nendif()\n\nif(NOT DCMTK_FIND_QUIETLY)\n  message(STATUS \"Trying to find DCMTK relying on FindDCMTK.cmake - ok\")\nendif()\n"
  },
  {
    "path": "cmake/FindMingw.cmake",
    "content": "# FindNIFTI.cmake\n#\n# Finds the MinGW runtime dependencies:\n#\n#   -libgcc\n#   -libstdc++\n#   -libgfortran\n#   -libquadmath\n#\n# The following variables will be exported:\n#   MINGW_LIBRARIES - The dependencies\n\n# Standard MinGW locations\nset (MINGW_HINTS  \"C:/TDM-GCC/\")\nset (MINGW_SUFFIXES \"bin\")\n\n\n# Get the names of the libraries, depending on 32- or 64-bit\nif (\"${CMAKE_SIZEOF_VOID_P}\" EQUAL \"8\")\n\tmessage(STATUS \"Using 64-bit MinGW...\")\n\tset (MINGW_GCC_LIBRARY_NAME \"libgcc_s_seh_64-1\")\n\tset (MINGW_CXX_LIBRARY_NAME \"libstdc++_64-6\")\n\tset (MINGW_GFORTRAN_LIBRARY_NAME \"libgfortran_64-3\")\n\tset (MINGW_QUADMATH_LIBRARY_NAME \"libquadmath_64-0\")\nelseif (\"${CMAKE_SIZEOF_VOID_P}\" EQUAL \"4\")\n\tmessage(STATUS \"Using 32-bit MinGW...\")\n\tset (MINGW_GCC_LIBRARY_NAME \"libgcc_s_dw2-1\")\n\tset (MINGW_CXX_LIBRARY_NAME \"libstdc++-6\")\n\tset (MINGW_GFORTRAN_LIBRARY_NAME \"libgfortran-3\")\n\tset (MINGW_QUADMATH_LIBRARY_NAME \"libquadmath-0\")\nelse ()\n        message(FATAL_ERROR \"Unrecognized byte width: ${CMAKE_SIZE_OF_VOID_P}\")\nendif ()\n\n\nfind_library (\n\tMINGW_GCC_LIBRARY\n\tNAMES ${MINGW_GCC_LIBRARY_NAME} \n\tHINTS ${MINGW_HINTS}\n\tPATH_SUFFIXES ${MINGW_SUFFIXES} \n\tDOC \"MinGW C standard library\"\n)\n\nfind_library (\n\tMINGW_CXX_LIBRARY\n\tnames ${MINGW_CXX_LIBRARY_NAME}\n\tHINTS ${MINGW_HINTS}\n\tPATH_SUFFIXES ${MINGW_SUFFIXES} \n\tDOC \"MinGW C++ standard library\"\t\n)\n\nfind_library (\n\tMINGW_GFORTRAN_LIBRARY\n\tNAMES ${MINGW_GFORTRAN_LIBRARY_NAME}\n\tHINTS ${MINGW_HINTS}\n\tPATH_SUFFIXES ${MINGW_SUFFIXES}\n\tDOC \"MinGW Fortran standard library\"\n)\n\nfind_library (\n\tMINGW_QUADMATH_LIBRARY\n\tNAMES ${MINGW_QUADMATH_LIBRARY_NAME}\n\tHINTS ${MINGW_HINTS}\n\tPATH_SUFFIXES ${MINGW_SUFFIXES}\n\tDOC \"MinGW quadmath library\"\n)\n\nset (MINGW_LIBRARIES ${MINGW_GCC_LIBRARY} ${MINGW_CXX_LIBRARY} ${MINGW_GFORTRAN_LIBRARY} ${MINGW_QUADMATH_LIBRARY})\n\n# handle the QUIETLY and REQUIRED arguments and set PNG_FOUND to TRUE if\n# all listed variables are TRUE\nINCLUDE(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(MINGW\n\t\"Cannot find package MinGW.\"\n\tMINGW_GCC_LIBRARY\n\tMINGW_CXX_LIBRARY\n\tMINGW_GFORTRAN_LIBRARY\n\tMINGW_QUADMATH_LIBRARY\n)\n\n# these variables are only visible in 'advanced mode'\nMARK_AS_ADVANCED(\n\tMINGW_GCC_LIBRARY\n\tMINGW_CXX_LIBRARY\n\tMINGW_GFORTRAN_LIBRARY\n\tMINGW_QUADMATH_LIBRARY\n)\n"
  },
  {
    "path": "cmake/FindNIFTI.cmake",
    "content": "# - FindNIFTI.cmake\n#\n# Author: Thomas Proeger\n# Modified by Blaine Rister\n#\n# This cmake find module looks for the header files and libraries from the\n# 'libnifti1-dev' package.\n#\n# The following variables will be exported:\n#\n# NIFTI_INCLUDE_DIR - the directory that contains nifti1.h\n# NIFTI_NIFTICDF_LIBRARY - the libnifticdf library file\n# NIFTI_NIFTIIO_LIBRARY - the libniftiio library file\n# NIFTI_ZNZ_LIBRARY - the libznz library file\n# NIFTI_FOUND - TRUE if and only if ALL other variables have\n# correct values.\n#\n\n# INCLUDE directory\nFIND_PATH(NIFTI_INCLUDE_DIRS\nNAMES nifti1.h\nPATHS ${NIFTI_DIR}\nHINTS \"C:/Program Files (x86)/NIFTI\" \"C:/Program Files/NIFTI\"\nPATH_SUFFIXES include nifti include/nifti\nDOC \"The include directory containing nifti1.h\"\n)\n\n# LIBRARY files\nFIND_LIBRARY(NIFTI_NIFTICDF_LIBRARY\nNAMES nifticdf\nPATHS ${NIFTI_DIR}\nHINTS \"C:/Program Files (x86)/NIFTI\" \"C:/Program Files/NIFTI\"\nPATH_SUFFIXES lib\nDOC \"The library file libnifticdf.so\"\n)\nFIND_LIBRARY(NIFTI_NIFTIIO_LIBRARY\nNAMES niftiio\nPATHS ${NIFTI_DIR}\nHINTS \"C:/Program Files (x86)/NIFTI\" \"C:/Program Files/NIFTI\"\nPATH_SUFFIXES lib\nDOC \"The library file libniftiiof.so\"\n)\nFIND_LIBRARY(NIFTI_ZNZ_LIBRARY\nNAMES znz\nPATHS ${NIFTI_DIR}\nHINTS \"C:/Program Files (x86)/NIFTI\" \"C:/Program Files/NIFTI\"\nPATH_SUFFIXES lib\nDOC \"The library file libznz.so\"\n)\n\n# Allow the user to specify the path to the NIFTI installation\nget_filename_component (_NIFTI_DIR ${NIFTI_NIFTIIO_LIBRARY} DIRECTORY)\nset (NIFTI_DIR ${_NIFTI_DIR} \n        CACHE PATH \"The directory containing the NIFTI installation\")\n\n# The combined NIFTI libraries\nset (NIFTI_LIBRARIES ${NIFTI_NIFTICDF_LIBRARY} ${NIFTI_NIFTIIO_LIBRARY} ${NIFTI_ZNZ_LIBRARY})\n\n# handle the QUIETLY and REQUIRED arguments and set PNG_FOUND to TRUE if\n# all listed variables are TRUE\nINCLUDE(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(NIFTI\n\"Cannot find package NIFTI. Did you install the package libnifti1-dev?\"\nNIFTI_INCLUDE_DIRS\nNIFTI_NIFTICDF_LIBRARY\nNIFTI_NIFTIIO_LIBRARY\nNIFTI_ZNZ_LIBRARY\n)\n# these variables are only visible in 'advanced mode'\nMARK_AS_ADVANCED(NIFTI_INCLUDE_DIRS\nNIFTI_NIFTICDF_LIBRARY\nNIFTI_NIFTIIO_LIBRARY\nNIFTI_ZNZ_LIBRARY\n)\n"
  },
  {
    "path": "cmake/FindOpenMP.cmake",
    "content": "#.rst:\n# FindOpenMP\n# ----------\n#\n# Finds OpenMP support\n#\n# This module can be used to detect OpenMP support in a compiler.  If\n# the compiler supports OpenMP, the flags required to compile with\n# OpenMP support are returned in variables for the different languages.\n# The variables may be empty if the compiler does not need a special\n# flag to support OpenMP.\n#\n# The following variables are set:\n#\n# ::\n#\n#    OpenMP_C_FLAGS - flags to add to the C compiler for OpenMP support\n#    OpenMP_C_LIBRARIES - OpenMP C libraries to link against\n#    OpenMP_CXX_FLAGS - flags to add to the CXX compiler for OpenMP support\n#    OpenMP_CXX_LIBRARIES - OpenMP CXX libraries to link against\n#    OpenMP_Fortran_FLAGS - flags to add to the Fortran compiler for OpenMP support\n#    OpenMP_Fortran_LIBRARIES - OpenMP Fortran libraries to link against\n#    OPENMP_FOUND - true if openmp is detected\n#\n# The following internal variables are set, if detected:\n#    OpenMP_C_SPEC_DATE - specification date of OpenMP version of C compiler\n#    OpenMP_CXX_SPEC_DATE - specification date of OpenMP version of CXX compiler\n#    OpenMP_Fortran_SPEC_DATE - specification date of OpenMP version of Fortran compiler\n#\n#\n#\n# Supported compilers can be found at\n# http://openmp.org/wp/openmp-compilers/\n# Specification dates for each version can be found at\n# http://openmp.org/wp/openmp-specifications/\n\n#=============================================================================\n# Copyright 2009 Kitware, Inc.\n# Copyright 2008-2009 André Rigland Brodtkorb <Andre.Brodtkorb@ifi.uio.no>\n# Copyright 2012 Rolf Eike Beer <eike@sf-mail.de>\n# Copyright 2014 Nicolas Bock <nicolasbock@gmail.com>\n#\n# Distributed under the OSI-approved BSD License (the \"License\");\n# see accompanying file Copyright.txt for details.\n#\n# This software is distributed WITHOUT ANY WARRANTY; without even the\n# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n# See the License for more information.\n#=============================================================================\n# (To distribute this file outside of CMake, substitute the full\n#  License text for the above reference.)\n\n\nset(_OPENMP_REQUIRED_VARS)\nset(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET})\nset(CMAKE_REQUIRED_QUIET ${OpenMP_FIND_QUIETLY})\n\nfunction(_OPENMP_FLAG_CANDIDATES LANG)\n  set(OpenMP_FLAG_CANDIDATES\n    #Empty, if compiler automatically accepts openmp\n    \" \"\n    #GNU\n    \"-fopenmp\"\n    #Microsoft Visual Studio\n    \"/openmp\"\n    #Intel windows\n    \"-Qopenmp\"\n    #PathScale, Intel\n    \"-openmp\"\n    #Sun\n    \"-xopenmp\"\n    #HP\n    \"+Oopenmp\"\n    #IBM XL C/c++\n    \"-qsmp\"\n    #Portland Group\n    \"-mp\"\n  )\n\n  set(OMP_FLAG_GNU \"-fopenmp\")\n  set(OMP_FLAG_HP \"+Oopenmp\")\n  if(WIN32)\n    set(OMP_FLAG_Intel \"-Qopenmp\")\n  elseif(CMAKE_${LANG}_COMPILER_ID STREQUAL \"Intel\" AND\n         \"${CMAKE_${LANG}_COMPILER_VERSION}\" VERSION_LESS \"15.0.0.20140528\")\n    set(OMP_FLAG_Intel \"-openmp\")\n  else()\n    set(OMP_FLAG_Intel \"-qopenmp\")\n  endif()\n  set(OMP_FLAG_MSVC \"/openmp\")\n  set(OMP_FLAG_PathScale \"-openmp\")\n  set(OMP_FLAG_PGI \"-mp\")\n  set(OMP_FLAG_SunPro \"-xopenmp\")\n  set(OMP_FLAG_XL \"-qsmp\")\n  set(OMP_FLAG_Cray \" \")\n\n  # Move the flag that matches the compiler to the head of the list,\n  # this is faster and doesn't clutter the output that much. If that\n  # flag doesn't work we will still try all.\n  if(OMP_FLAG_${CMAKE_${LANG}_COMPILER_ID})\n    list(REMOVE_ITEM OpenMP_FLAG_CANDIDATES \"${OMP_FLAG_${CMAKE_${LANG}_COMPILER_ID}}\")\n    list(INSERT OpenMP_FLAG_CANDIDATES 0 \"${OMP_FLAG_${CMAKE_${LANG}_COMPILER_ID}}\")\n  endif()\n\n  set(OpenMP_${LANG}_FLAG_CANDIDATES \"${OpenMP_FLAG_CANDIDATES}\" PARENT_SCOPE)\nendfunction()\n\n# sample openmp source code to test\nset(OpenMP_C_TEST_SOURCE\n\"\n#include <omp.h>\nint main() {\n#ifdef _OPENMP\n  return 0;\n#else\n  breaks_on_purpose\n#endif\n}\n\")\n\n# same in Fortran\nset(OpenMP_Fortran_TEST_SOURCE\n  \"\n      program test\n      use omp_lib\n      integer :: n\n      n = omp_get_num_threads()\n      end program test\n  \"\n  )\n\nset(OpenMP_C_CXX_CHECK_VERSION_SOURCE\n\"\n#include <stdio.h>\n#include <omp.h>\nconst char ompver_str[] = { 'I', 'N', 'F', 'O', ':', 'O', 'p', 'e', 'n', 'M',\n                            'P', '-', 'd', 'a', 't', 'e', '[',\n                            ('0' + ((_OPENMP/100000)%10)),\n                            ('0' + ((_OPENMP/10000)%10)),\n                            ('0' + ((_OPENMP/1000)%10)),\n                            ('0' + ((_OPENMP/100)%10)),\n                            ('0' + ((_OPENMP/10)%10)),\n                            ('0' + ((_OPENMP/1)%10)),\n                            ']', '\\\\0' };\nint main(int argc, char *argv[])\n{\n  printf(\\\"%s\\\\n\\\", ompver_str);\n  return 0;\n}\n\")\n\nset(OpenMP_Fortran_CHECK_VERSION_SOURCE\n\"\n      program omp_ver\n      use omp_lib\n      integer, parameter :: zero = ichar('0')\n      integer, parameter :: ompv = openmp_version\n      character, dimension(24), parameter :: ompver_str =&\n      (/ 'I', 'N', 'F', 'O', ':', 'O', 'p', 'e', 'n', 'M', 'P', '-',&\n         'd', 'a', 't', 'e', '[',&\n         char(zero + mod(ompv/100000, 10)),&\n         char(zero + mod(ompv/10000, 10)),&\n         char(zero + mod(ompv/1000, 10)),&\n         char(zero + mod(ompv/100, 10)),&\n         char(zero + mod(ompv/10, 10)),&\n         char(zero + mod(ompv/1, 10)), ']' /)\n      print *, ompver_str\n      end program omp_ver\n\")\n\nfunction(_OPENMP_GET_SPEC_DATE LANG SPEC_DATE)\n  set(WORK_DIR ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/FindOpenMP)\n  if(\"${LANG}\" STREQUAL \"C\")\n    set(SRC_FILE ${WORK_DIR}/ompver.c)\n    file(WRITE ${SRC_FILE} \"${OpenMP_C_CXX_CHECK_VERSION_SOURCE}\")\n  elseif(\"${LANG}\" STREQUAL \"CXX\")\n    set(SRC_FILE ${WORK_DIR}/ompver.cpp)\n    file(WRITE ${SRC_FILE} \"${OpenMP_C_CXX_CHECK_VERSION_SOURCE}\")\n  else() # (\"${LANG}\" STREQUAL \"Fortran\")\n    set(SRC_FILE ${WORK_DIR}/ompver.f90)\n    file(WRITE ${SRC_FILE} \"${OpenMP_Fortran_CHECK_VERSION_SOURCE}\")\n  endif()\n\n  set(BIN_FILE ${WORK_DIR}/ompver_${LANG}.bin)\n  try_compile(OpenMP_TRY_COMPILE_RESULT ${CMAKE_BINARY_DIR} ${SRC_FILE}\n              COMPILE_DEFINITIONS ${OpenMP_${LANG}_FLAGS}\n              LINK_LIBRARIES \"${OpenMP_${LANG}_LIBRARIES}\"\n              COPY_FILE ${BIN_FILE})\n\n  if(${OpenMP_TRY_COMPILE_RESULT})\n    file(STRINGS ${BIN_FILE} specstr LIMIT_COUNT 1 REGEX \"INFO:OpenMP-date\")\n    set(regex_spec_date \".*INFO:OpenMP-date\\\\[0*([^]]*)\\\\].*\")\n    if(\"${specstr}\" MATCHES \"${regex_spec_date}\")\n      set(${SPEC_DATE} \"${CMAKE_MATCH_1}\" PARENT_SCOPE)\n    endif()\n  endif()\nendfunction()\n\n\n# check c compiler\nif(CMAKE_C_COMPILER_LOADED)\n  # if these are set then do not try to find them again,\n  # by avoiding any try_compiles for the flags\n  if(OpenMP_C_FLAGS)\n    unset(OpenMP_C_FLAG_CANDIDATES)\n  else()\n    _OPENMP_FLAG_CANDIDATES(\"C\")\n    include(CheckCSourceCompiles)\n  endif()\n\n  foreach(FLAG IN LISTS OpenMP_C_FLAG_CANDIDATES)\n    set(SAFE_CMAKE_REQUIRED_FLAGS \"${CMAKE_REQUIRED_FLAGS}\")\n    set(CMAKE_REQUIRED_FLAGS \"${FLAG}\")\n    unset(OpenMP_FLAG_DETECTED CACHE)\n    if(NOT CMAKE_REQUIRED_QUIET)\n      message(STATUS \"Try OpenMP C flag = [${FLAG}]\")\n    endif()\n    check_c_source_compiles(\"${OpenMP_C_TEST_SOURCE}\" OpenMP_FLAG_DETECTED)\n    set(CMAKE_REQUIRED_FLAGS \"${SAFE_CMAKE_REQUIRED_FLAGS}\")\n    if(OpenMP_FLAG_DETECTED)\n      set(OpenMP_C_FLAGS_INTERNAL \"${FLAG}\")\n      break()\n    endif()\n  endforeach()\n\n  set(OpenMP_C_FLAGS \"${OpenMP_C_FLAGS_INTERNAL}\"\n    CACHE STRING \"C compiler flags for OpenMP parallization\")\n\n  list(APPEND _OPENMP_REQUIRED_VARS OpenMP_C_FLAGS)\n  unset(OpenMP_C_FLAG_CANDIDATES)\n\n  if (\"x${CMAKE_C_COMPILER_ID}\" STREQUAL \"xMSVC\")\n  elseif (MINGW)\n    # Get the names of the C libraries, depending on 32- or 64-bit\n    if (\"${CMAKE_SIZEOF_VOID_P}\" EQUAL \"8\")\n        message(STATUS \"Using 64-bit OpenMP...\")\n        set (OpenMP_C_LIBRARY_NAME \"libgomp_64-1\")\n    elseif (\"${CMAKE_SIZEOF_VOID_P}\" EQUAL \"4\")\n        message(STATUS \"Using 32-bit OpenMP...\")\n        set (OpenMP_C_LIBRARY_NAME \"libgomp-1\")\n    else ()\n        message(FATAL_ERROR \"Unrecognized byte width: ${CMAKE_SIZE_OF_VOID_P}\")\n    endif ()\n\n    # Find the OpenMP C library for MinGW\n    find_library(\n        OpenMP_C_LIBRARY\n        NAMES ${OpenMP_C_LIBRARY_NAME}\n        HINTS \"C:/TDM-GCC\"\n        PATH_SUFFIXES \"bin\"\n        DOC \"The OpenMP C library\"\n    )\n  else ()\n    set (OpenMP_C_LIBRARY \"${OpenMP_C_FLAGS}\" CACHE\n      STRING \"C library for OpenMP\")\n    list (APPEND _OPENMP_REQUIRED_VARS OpenMP_C_LIBRARY)\n      set (OpenMP_CXX_LIBRARIES OpenMP_C_LIBRARIES)\n  endif()\n  set (OpenMP_C_LIBRARIES \"${OpenMP_C_LIBRARY}\")\n\n  if (NOT OpenMP_C_SPEC_DATE)\n    _OPENMP_GET_SPEC_DATE(\"C\" OpenMP_C_SPEC_DATE_INTERNAL)\n    set(OpenMP_C_SPEC_DATE \"${OpenMP_C_SPEC_DATE_INTERNAL}\" CACHE\n      INTERNAL \"C compiler's OpenMP specification date\")\n  endif()\nendif()\n\n# check cxx compiler\nif(CMAKE_CXX_COMPILER_LOADED)\n  # if these are set then do not try to find them again,\n  # by avoiding any try_compiles for the flags\n  if(OpenMP_CXX_FLAGS)\n    unset(OpenMP_CXX_FLAG_CANDIDATES)\n  else()\n    _OPENMP_FLAG_CANDIDATES(\"CXX\")\n    include(CheckCXXSourceCompiles)\n\n    # use the same source for CXX as C for now\n    set(OpenMP_CXX_TEST_SOURCE ${OpenMP_C_TEST_SOURCE})\n  endif()\n\n  foreach(FLAG IN LISTS OpenMP_CXX_FLAG_CANDIDATES)\n    set(SAFE_CMAKE_REQUIRED_FLAGS \"${CMAKE_REQUIRED_FLAGS}\")\n    set(CMAKE_REQUIRED_FLAGS \"${FLAG}\")\n    unset(OpenMP_FLAG_DETECTED CACHE)\n    if(NOT CMAKE_REQUIRED_QUIET)\n      message(STATUS \"Try OpenMP CXX flag = [${FLAG}]\")\n    endif()\n    check_cxx_source_compiles(\"${OpenMP_CXX_TEST_SOURCE}\" OpenMP_FLAG_DETECTED)\n    set(CMAKE_REQUIRED_FLAGS \"${SAFE_CMAKE_REQUIRED_FLAGS}\")\n    if(OpenMP_FLAG_DETECTED)\n      set(OpenMP_CXX_FLAGS_INTERNAL \"${FLAG}\")\n      break()\n    endif()\n  endforeach()\n\n  set(OpenMP_CXX_FLAGS \"${OpenMP_CXX_FLAGS_INTERNAL}\"\n    CACHE STRING \"C++ compiler flags for OpenMP parallization\")\n\n  list(APPEND _OPENMP_REQUIRED_VARS OpenMP_CXX_FLAGS)\n  unset(OpenMP_CXX_FLAG_CANDIDATES)\n  unset(OpenMP_CXX_TEST_SOURCE)\n  unset(OpenMP_C_TEST_SOURCE)\n\n  if (NOT \"x${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"xMSVC\")\n    set(OpenMP_CXX_LIBRARY \"${OpenMP_CXX_FLAGS}\" CACHE\n      STRING \"CXX library for OpenMP\")\n    list(APPEND _OPENMP_REQUIRED_VARS OpenMP_CXX_LIBRARY)\n  endif()\n  set(OpenMP_CXX_LIBRARIES \"${OpenMP_CXX_LIBRARY}\")\n\n  if (NOT OpenMP_CXX_SPEC_DATE)\n    _OPENMP_GET_SPEC_DATE(\"CXX\" OpenMP_CXX_SPEC_DATE_INTERNAL)\n    set(OpenMP_CXX_SPEC_DATE \"${OpenMP_CXX_SPEC_DATE_INTERNAL}\" CACHE\n      INTERNAL \"C++ compiler's OpenMP specification date\")\n  endif()\n  unset(OpenMP_C_CXX_CHECK_VERSION_SOURCE)\nendif()\n\n# check Fortran compiler\nif(CMAKE_Fortran_COMPILER_LOADED)\n  # if these are set then do not try to find them again,\n  # by avoiding any try_compiles for the flags\n  if(OpenMP_Fortran_FLAGS)\n    unset(OpenMP_Fortran_FLAG_CANDIDATES)\n  else()\n    _OPENMP_FLAG_CANDIDATES(\"Fortran\")\n    include(CheckFortranSourceCompiles)\n  endif()\n\n  foreach(FLAG IN LISTS OpenMP_Fortran_FLAG_CANDIDATES)\n    set(SAFE_CMAKE_REQUIRED_FLAGS \"${CMAKE_REQUIRED_FLAGS}\")\n    set(CMAKE_REQUIRED_FLAGS \"${FLAG}\")\n    unset(OpenMP_FLAG_DETECTED CACHE)\n    if(NOT CMAKE_REQUIRED_QUIET)\n      message(STATUS \"Try OpenMP Fortran flag = [${FLAG}]\")\n    endif()\n    check_fortran_source_compiles(\"${OpenMP_Fortran_TEST_SOURCE}\" OpenMP_FLAG_DETECTED)\n    set(CMAKE_REQUIRED_FLAGS \"${SAFE_CMAKE_REQUIRED_FLAGS}\")\n    if(OpenMP_FLAG_DETECTED)\n      set(OpenMP_Fortran_FLAGS_INTERNAL \"${FLAG}\")\n      break()\n    endif()\n  endforeach()\n\n  set(OpenMP_Fortran_FLAGS \"${OpenMP_Fortran_FLAGS_INTERNAL}\"\n    CACHE STRING \"Fortran compiler flags for OpenMP parallization\")\n\n  list(APPEND _OPENMP_REQUIRED_VARS OpenMP_Fortran_FLAGS)\n  unset(OpenMP_Fortran_FLAG_CANDIDATES)\n  unset(OpenMP_Fortran_TEST_SOURCE)\n\n  set(OpenMP_Fortran_LIBRARY \"${OpenMP_Fortran_FLAGS}\" CACHE\n    STRING \"Fortran library for OpenMP\")\n  list(APPEND _OPENMP_REQUIRED_VARS OpenMP_Fortran_LIBRARY)\n  set(OpenMP_Fortran_LIBRARIES \"${OpenMP_Fortran_LIBRARY}\")\n\n  if (NOT OpenMP_Fortran_SPEC_DATE)\n    _OPENMP_GET_SPEC_DATE(\"Fortran\" OpenMP_Fortran_SPEC_DATE_INTERNAL)\n    set(OpenMP_Fortran_SPEC_DATE \"${OpenMP_Fortran_SPEC_DATE_INTERNAL}\" CACHE\n      INTERNAL \"Fortran compiler's OpenMP specification date\")\n  endif()\n  unset(OpenMP_Fortran_CHECK_VERSION_SOURCE)\nendif()\n\nset(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE})\n\nif(_OPENMP_REQUIRED_VARS)\n  include(FindPackageHandleStandardArgs)\n\n  find_package_handle_standard_args(OpenMP\n                                    REQUIRED_VARS ${_OPENMP_REQUIRED_VARS})\n\n  mark_as_advanced(${_OPENMP_REQUIRED_VARS})\n\n  unset(_OPENMP_REQUIRED_VARS)\nelse()\n  message(SEND_ERROR \"FindOpenMP requires C or CXX language to be enabled\")\nendif()\n"
  },
  {
    "path": "cmake/SIFT3DPackage.cmake",
    "content": "################################################################################\n# SIFT3DPackage.cmake\n################################################################################\n# Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n################################################################################\n# Build file to populate SIFT3D packages.\n################################################################################\n\n# Set the default package generator based on the OS \nif (WIN32)\n        # NSIS for Windows\n        set (_CPACK_GENERATOR \"NSIS\")\nelseif (APPLE)\n        # Bundle for Mac\n        set (_CPACK_GENERATOR \"BUNDLE\")\nelseif (UNIX)\n        # Default to .tar.gz on Unix-like platforms\n        set (_CPACK_GENERATOR \"TGZ\")\n\n        # Override for specific Linux distributions\n        if (CMAKE_SYSTEM_NAME MATCHES \"Linux\")\n                # Try to read the Linux distribution\n                if (EXISTS \"/etc/issue\")\n                        file (READ \"/etc/issue\" LINUX_ISSUE)\n                        # .deb for Ubuntu and Debian\n                        if (${LINUX_ISSUE} MATCHES \"Ubuntu\" OR \n                                ${LINUX_ISSUE} MATCHES \"Debian\")\n                                set (_CPACK_GENERATOR \"DEB\")\n                        # .rpm for Fedora and OpenSuSE\n                        elseif (${LINUX_ISSUE} MATCHES \"Fedora\" OR\n                                ${LINUX_ISSUE} MATCHES \"SUSE\")\n                                set (_CPACK_GENERATOR \"RPM\")\n                        endif ()\n                endif ()\n        endif ()\nelse ()\n        message (FATAL_ERROR \"Unable to determine the default package generator for this operating system\")\nendif ()\nset (CPACK_GENERATOR ${_CPACK_GENERATOR} CACHE STRING \"The package generation \n        program\")\n\n# Global CPack variables\nset (CPACK_PACKAGE_NAME \"SIFT3D\")\nset (CPACK_PACKAGE_CONTACT \"Blaine Rister blaine@stanford.edu\")\nset (CPACK_RESOURCE_FILE_LICENSE ${LICENSE_FILE})\nset (CPACK_RESOURCE_FILE_README ${README_FILE})\nset (CPACK_PACKAGE_VERSION ${SIFT3D_VERSION})\nset (CPACK_PACKAGE_DESCRIPTION \"Extracts keypoints and descriptors from 3D images. Also contians libraries for image processing and registration. Includes wrappers for Matlab.\")\n\n# Use the CMake install path, unless this is Windows, in which case this would\n# break CPack\nif (NOT WIN32)\n        set (CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})\nendif ()\n\n# Generator-specific CPack variables\nif (CPACK_GENERATOR STREQUAL \"NSIS\")\n\n        # Add the option to append SIFT3D to the path\n        set (CPACK_NSIS_MODIFY_PATH ON)\n\nelseif (CPACK_GENERATOR STREQUAL \"BUNDLE\")\n        message(FATAL_ERROR \"Bundle support not yet implemented\")\n        # TODO Do we need a bundle info file?\nelseif (CPACK_GENERATOR STREQUAL \"DEB\")\n\n        # Set the target platform in Debian terms\n        if (CMAKE_SYSTEM_PROCESSOR STREQUAL \"x86_64\")\n                set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE amd64)\n        elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL \"i686\")\n                set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE i386)\n        else ()\n                set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})\n        endif () \n        message (\"-- Configured Debian package for architecture ${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}\")\n\n        # Set the standard Debian package dependencies\n        set (CPACK_DEBIAN_PACKAGE_DEPENDS \"libc6, zlib1g, libnifti2, libdcmtk5, liblapack3\")\n\n        # Set compiler-specific Debian package dependencies\n        if (CMAKE_C_COMPILER_ID STREQUAL \"GNU\" OR\n               CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\")\n                set (CPACK_DEBIAN_PACKAGE_DEPENDS \n                        \"${CPACK_DEBIAN_PACKAGE_DEPENDS}, libgcc1\")\n        endif () \nelseif (CPACK_GENERATOR STREQUAL \"RPM\")\n        message(FATAL_ERROR \"RPM support not yet implemented\")\n        # TODO Add RPM package dependencies (see debian dependencies)\nendif ()\n\n# Finally include the CPack module\ninclude (CPack)\n"
  },
  {
    "path": "doc/INSTALL_LINUX.md",
    "content": "# SIFT3D Linux Installation Instructions\n\nCopyright (c) 2015-2018 Blaine Rister et al., see LICENSE for details.\n\n# Installing the dependencies\n\nThis program requires the following external libraries:\n- [zlib](http://www.zlib.net/)\n- [LAPACK](http://www.netlib.org/lapack/)\n\nIn addition, the following libraries add optional support for reading and writing DICOM and NIFTI files:\n- [DCMTK](http://dicom.offis.de/dcmtk.php.en)\n- [nifticlib](http://sourceforge.net/projects/niftilib/files/nifticlib/)\n\nThe 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.\n\nOn Ubuntu 16.04, the following command will install all dependencies:\n\n\tsudo apt-get install zlib1g-dev liblapack-dev libdcmtk-dev libnifti-dev\n\n# Installing SIFT3D\n\n## Installing from binaries\n\nYou 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. \n\n## Installing from source\n\nThis program has been successfully compiled and executed on the following Linux platforms:\n- Ubuntu Linux 16.04, using GCC 5.4.0 and CMake 3.5.1.\n\nThis program requires the following tools to compile:\n- [CMake](http://www.cmake.org)\n- A suitable C/C++ compiler, such as GCC or Clang/LLVM.\n\nOn Ubuntu 16.04, the following command will install CMake and GCC:\n\n        sudo apt-get install build-essential cmake\n\nThe following commands will generate Makefiles and use them to compile the binaries in a subdirectory called \"build\":\n\n        cd /path/to/SIFT3D # Go to the directory where you downloaded SIFT3D \n\tmkdir build # Make a directory to store the binaries\n\tcd build\n\tcmake .. # Create the Makefiles\n\tmake # Compile the program\n\nIf for some reason CMake cannot find the dependencies, you can specify the paths manually with the CMake GUI. \n\nUse the following command to install the files:\n\n\tsudo make install\n\n### Packaging\n\nTo 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,\n\n        cmake .. -DBUILD_PACKAGE=ON\n        make package\n\n### Troubleshooting\n\nIf 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.\n\n"
  },
  {
    "path": "doc/INSTALL_MAC.md",
    "content": "# SIFT3D Mac Installation Instructions\n\nCopyright (c) 2015-2017 Blaine Rister et al., see LICENSE for details.\n\n# Installing the dependencies\n\nThis program requires the following external libraries:\n- [zlib](http://www.zlib.net/)\n- [LAPACK](http://www.netlib.org/lapack/)\n\nIn addition, the following libraries add optional support for reading and writing DICOM and NIFTI files:\n- [DCMTK](http://dicom.offis.de/dcmtk.php.en)\n- [nifticlib](http://sourceforge.net/projects/niftilib/files/nifticlib/)\n\nThe 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.\n\nAs 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:\n \n        brew install dcmtk niftilib\n\n# Installing SIFT3D\n\n## Installing from binaries\n\nWe do not currently have binaries for SIFT3D on Mac. You must install from source.\n\n## Installing from source\n\nThis program has been successfully compiled and executed on the following Mac platforms:\n- Mac OSX 10.10.5, using Clang 6.1.0, LLVM 3.6.0 and CMake 3.3.1\n\nThis program requires the following tools to compile:\n- [CMake](http://www.cmake.org)\n- A suitable C/C++ compiler. such as GCC or Clang/LLVM.\n\nClang/LLVM comes pre-installed. Using [Homebrew](http://brew.sh), the following command will install CMake.\n\n        brew install cmake\n\nThe following commands will generate Makefiles and use them to compile the binaries in a subdirectory called \"build\":\n\n        cd /path/to/SIFT3D # Go to the directory where you downloaded SIFT3D \n\tmkdir build # Make a directory to store the binaries\n\tcd build\n\tcmake .. # Create the Makefiles\n\tmake # Compile the program\n\n*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.*\n\nIf CMake cannot find the dependencies, you can specify the paths manually with the CMake GUI.\n\nUse the following command to install the files:\n\n\tsudo make install\n\n"
  },
  {
    "path": "doc/INSTALL_WINDOWS.md",
    "content": "# SIFT3D Windows Installation Instructions\n\nCopyright (c) 2015-2018 Blaine Rister et al., see LICENSE for details.\n\n## Installing from binaries\n\nYou 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.\n\n## Installing from source\n\n*This is a difficult process on Windows, recommended only for advanced users.*\n\nThis program has been successfully compiled and executed on the following Windows platforms:\n* Windows 10 64-bit, using [TDM-GCC](http://tdm-gcc.tdragon.net/) 5.10 and CMake 3.3.1\n* Windows 8 64-bit, using the same\n* Windows 7 64-bit, using the same\n\nIn addition, the compiled C libraries have been linked to on the following platforms:\n* Windows 7 64-bit, using Visual Studio 2012\n\nPlease follow the instructions below to compile and install SIFT3D from source.\n\n1. Install [CMake](http://www.cmake.org).\n\n2. Install MinGW via [TDM-GCC](http://tdm-gcc.tdragon.net/)\n\t1. Download the installer\n\t2. Run the installer and remember to select the GCC packages C, C++, Fortran and OpenMP\n\t\t* OpenMP is for optional multithreading, the rest are mandatory\n\n3. 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.*\n\t1. Download and extract gnumex\n\t2. Run Matlab as an administrator\n\t3. Run the \"gnumex\" program from within Matlab\n\t4. Make sure the required paths are detected\n\t5. Generate the files\n\t6. 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.\n\n4. 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.)*\n\t1. Download the binaries for MinGW and your verison of Windows (lapack.dll, blas.dll)\n\t2. Move lapack.dll and blas.dll to the TDM-GCC/bin directory\n\n5. Install [zlib](http://zlib.net/).\n\t1. Download and extract the most recent version\n\t2. Use the CMake GUI to generate MinGW Makefiles\n\t\t1. Set the source folder to the location of zlib\n\t\t2. Generate -> MinGW Makefiles\n\t3. Compile and install with MinGW\n\t\t1. cd to the build directory\n\t\t2. \"mingw32-make\"\n\t\t3. \"mingw32-make install\" (may require administrator privileges)\n\n6. 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.*\n\t1. Download and extract the newest version\n\t2. Generate MinGW Makefiles with CMake\n\t\t1. Set the source folder to the location of nifticlib\n\t\t2. Generate -> MinGW Makefiles\n                        * You may have to set the variable ZLIB_LIBRARY to the directory where zlib was installed.\n\t3. Compile and install with MinGW\n\t\t1. cd to build directory\n\t\t2. \"mingw32-make\"\n\t\t3. \"mingw32-make install\" (may require administrator privileges)\n\n7. 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.* \n\t1. 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.*\n\t2. Generate MinGW Makefiles with CMake\n\t\t1. Set the source folder to the location of DCMTK\n\t\t2. Generate -> MinGW Makefiles\n\t3. Compile and install with MinGW\n\t\t1. cd to build directory\n\t\t2. \"mingw32-make\"\n\t\t3. \"mingw32-make install\" (may require administrator privileges)\n\n8. Install SIFT3D.\n\t1. Download this repository\n\t2. Generate MinGW Makefiles with CMake\n\t\t1. Navigate to SIFT3D\n\t\t2. Select \"MinGW Makefiles\"\n                3. Configure\n                4. Configuration may fail if CMake cannot find the dependencies. If so, set the appropriate variables and configure again.\n                        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.\n\t\t        2. If configuration fails due to DCMTK, set DCMTK_DIR to the install location of DCMTK.\n\t\t        3. If configuration fails due to NIFTI, set NIFTI_DIR to the install location of nifticlib.\n\t\t5. *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.\n\t\t6. Generate -> MinGW Makefiles\n\t3. Compile and install with MinGW\n\t\t1. cd to the build directory\n\t\t2. \"mingw32-make\"\n\t\t3. \"mingw32-make install\" (may require administrator privileges)\n### Packaging\n\nTo 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,\n\n        mingw32-make package\n\n\n## Caveats\n\nThis program was originally developed for Unix-like platforms, so some features have been disabled in the Windows version. \n\n* 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.\n\n* 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.\n"
  },
  {
    "path": "examples/CMakeLists.txt",
    "content": "################################################################################\n# Copyright (c) 2015 Blaine Rister et al., see LICENSE for details.\n################################################################################\n# Build file for example programs.\n################################################################################\n\n# The data directory\nset (DATA_DIR \"data\")\n\n# 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.\nif (WIN32)\n\tset (EXAMPLES_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})\nelse ()\n\tset (EXAMPLES_DIR \"examples\")\n\tset (EXAMPLES_PATH \"${CMAKE_BINARY_DIR}/${EXAMPLES_DIR}\")\nendif ()\n\n# Copy the example data\nconfigure_file(\"${DATA_DIR}/1.nii.gz\" \"${EXAMPLES_PATH}/1.nii.gz\" COPYONLY)\nconfigure_file(\"${DATA_DIR}/2.nii.gz\" \"${EXAMPLES_PATH}/2.nii.gz\" COPYONLY)\n\n# The example programs\nadd_executable (featuresC featuresC.c)\ntarget_link_libraries (featuresC PUBLIC sift3D imutil)\n\nadd_executable (registerC registerC.c)\ntarget_link_libraries (registerC PUBLIC reg sift3D imutil)\n\nadd_executable (ioC ioC.c)\ntarget_link_libraries (ioC PUBLIC imutil)\n\n# Send all files to the examples subdirectory \nset_target_properties(featuresC registerC ioC\n        PROPERTIES\n        ARCHIVE_OUTPUT_DIRECTORY ${EXAMPLES_PATH}\n        LIBRARY_OUTPUT_DIRECTORY ${EXAMPLES_PATH}\n        RUNTIME_OUTPUT_DIRECTORY ${EXAMPLES_PATH}\n)\n"
  },
  {
    "path": "examples/featuresC.c",
    "content": "/* -----------------------------------------------------------------------------\n * featuresC.c\n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * Example of extracting and visualizing SIFT3D keypoints and descriptors using\n * the C API.\n */\n\n/* System headers */\n#include <stdio.h>\n\n/* SIFT3D headers */\n#include \"immacros.h\"\n#include \"imutil.h\"\n#include \"sift.h\"\n\n/* Example file paths */\nconst char *im_path = \"1.nii.gz\";\nconst char *keys_path = \"1_keys.csv.gz\";\nconst char *desc_path = \"1_desc.csv.gz\";\nconst char *draw_path = \"1_keys.nii.gz\";\n\n/* This illustrates how to use SIFT3D within a function, and free all memory\n * afterwards. */\nint demo(void) {\n\n\tImage im, draw;\n        Mat_rm keys;\n\tSIFT3D sift3d;\n\tKeypoint_store kp;\n\tSIFT3D_Descriptor_store desc;\n\n        // Initialize the intermediates\n        init_Keypoint_store(&kp);\n        init_SIFT3D_Descriptor_store(&desc);\n        init_im(&im);\n        init_im(&draw);\n        if (init_Mat_rm(&keys, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE))\n                return 1; \n\n        if (init_SIFT3D(&sift3d)) {\n                cleanup_Mat_rm(&keys);\n                return 1;\n        }\n\n        // Read the image\n        if (im_read(im_path, &im))\n                goto demo_quit;\n\n        // Detect keypoints\n\tif (SIFT3D_detect_keypoints(&sift3d, &im, &kp))\n                goto demo_quit;\n\n        // Write the keypoints to a file\n        if (write_Keypoint_store(keys_path, &kp))\n                goto demo_quit;\n        printf(\"Keypoints written to %s. \\n\", keys_path);\n\n        // Extract descriptors\n        if (SIFT3D_extract_descriptors(&sift3d, &kp, &desc))\n                goto demo_quit;\n\n        // Write the descriptors to a file\n        if (write_SIFT3D_Descriptor_store(desc_path, &desc))\n                goto demo_quit;\n        printf(\"Descriptors written to %s. \\n\", desc_path);\n\n        // Convert the keypoints to a matrix \n        if (Keypoint_store_to_Mat_rm(&kp, &keys))\n                goto demo_quit;\n\n        // Draw the keypoints\n        if (draw_points(&keys, SIFT3D_IM_GET_DIMS(&im), 1, &draw))\n                goto demo_quit;\n\n        // Write the drawn keypoints to a file\n        if (im_write(draw_path, &draw))\n                goto demo_quit;\n        printf(\"Keypoints drawn in %s. \\n\", draw_path);\n\n        // Clean up\n        im_free(&im);\n        im_free(&draw);\n        cleanup_Mat_rm(&keys);\n        cleanup_SIFT3D(&sift3d);\n        cleanup_Keypoint_store(&kp);\n        cleanup_SIFT3D_Descriptor_store(&desc);\n\n        return 0;\n\ndemo_quit:\n        // Clean up and return an error\n        im_free(&im);\n        im_free(&draw);\n        cleanup_Mat_rm(&keys);\n        cleanup_SIFT3D(&sift3d);\n        cleanup_Keypoint_store(&kp);\n        cleanup_SIFT3D_Descriptor_store(&desc);\n\n        return 1;\n}\n\nint main(void) {\n\n        int ret;\n\n        // Do the demo\n        ret = demo();\n\n        // Check for errors\n        if (ret != 0) {\n                fprintf(stderr, \"Fatal demo error, code %d. \\n\", ret);\n                return 1;\n        }\n\n        return 0;\n}\n"
  },
  {
    "path": "examples/featuresMatlab.m",
    "content": "% featuresMatlab.m\n%\n% This script shows how to extract SIFT3D features from a volumetric image.\n%\n% Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n\n% Load the image\n[im, units] = imRead3D('data/1.nii.gz');\n\n% Detect keypoints\nkeys = detectSift3D(im, 'units', units);\n\n% Extract descriptors\n[desc, coords] = extractSift3D(keys);\n\n% Clear MEX memory\nclear mex"
  },
  {
    "path": "examples/ioC.c",
    "content": "/* -----------------------------------------------------------------------------\n * ioC.c\n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * Short example of file I/O, converting a NIFTI file to DICOM.\n */\n\n/* System headers */\n#include <stdio.h>\n\n/* SIFT3D headers */\n#include \"imutil.h\"\n\n/* Example file paths */\nconst char in_path[] = \"1.nii.gz\";\nconst char out_path[] = \"1dicom\";\n\n/* This illustrates how to use images within a function, and free all memory\n * afterwards. */\nint demo(void) {\n\n\tImage im;\n\n        // You must call this before any Image struct can be used\n        init_im(&im);\n\n        // Read the NIFTI image as input\n        if (im_read(in_path, &im))\n                goto demo_quit;\n\n        // Write it to a DICOM series\n        if (im_write(out_path, &im))\n                goto demo_quit;\n\n        // Clean up\n        im_free(&im);\n\n        return 0;\n\ndemo_quit:\n        // Clean up and return an error\n        im_free(&im);\n        return 1;\n}\n\nint main(void) {\n\n        int ret;\n\n        // Do the demo\n        ret = demo();\n\n        // Check for errors\n        if (ret != 0) {\n                fprintf(stderr, \"Fatal demo error, code %d. \\n\", ret);\n                return 1;\n        }\n\n        return 0;\n}\n"
  },
  {
    "path": "examples/ioMatlab.m",
    "content": "% ioMatlab\n%\n% Example of using file IO functions with SIFT3D\n%\n% Copyright (c) 2015 Blaine Rister et al., see LICENSE for details.\n\n% Load the from a NIFTI file\nimNifti = imRead3D('data/1.nii.gz');\n\n% Save it as a multi-slice DICOM file\nimWrite3D('1.dcm', imNifti);"
  },
  {
    "path": "examples/manualFeaturesMatlab.m",
    "content": "% manualFeaturesMatlab.m\n%\n% This script shows how to manually define keypoints in a volumetric image,\n% and extract SIFT3D feature descriptors at those locations.\n%\n% Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n\n% Load the image\n[im, units] = imRead3D('data/1.nii.gz');\n\n% Define a keypoint\nkeys = keypoint3D([100 100 20]);\n\n% Assign its orientation\n[keys, conf] = orientation3D(keys, im, units);\n\n% Extract a descriptor\n[desc, coords] = extractSift3D(keys, im, units);\n"
  },
  {
    "path": "examples/registerC.c",
    "content": "/* -----------------------------------------------------------------------------\n * registerC.c\n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * Example of registering two images using the C API.\n */\n\n/* System headers */\n#include <stdio.h>\n\n/* SIFT3D headers */\n#include \"imutil.h\"\n#include \"sift.h\"\n#include \"reg.h\"\n\n/* Example file paths */\nconst char *ref_path = \"1.nii.gz\";\nconst char *src_path = \"2.nii.gz\";\nconst char *match_path = \"1_2_matches.nii.gz\";\nconst char *warped_path = \"2_warped.nii.gz\";\nconst char *affine_path = \"1_2_affine.csv.gz\";\n\n/* This illustrates how to use Reg_SIFT3D within a function, freeing all memory\n * afterwards. */\nint demo(void) {\n\n\tImage src, ref, warped;\n        Reg_SIFT3D reg;\n        Affine affine;\n\n        // Initialize the intermediates\n        init_im(&src);\n        init_im(&ref);\n        init_im(&warped);\n        if (init_tform(&affine, AFFINE))\n                return 1;\n\n        if (init_Reg_SIFT3D(&reg)) {\n                cleanup_tform(&affine);\n                return 1;\n        }\n\n        // Read the images\n        if (im_read(src_path, &src) ||\n                im_read(ref_path, &ref))\n                goto demo_quit;\n\n        // Set the images\n        if (set_src_Reg_SIFT3D(&reg, &src) ||\n                set_ref_Reg_SIFT3D(&reg, &ref))\n                goto demo_quit;\n\n        // Match features and solve for an affine transformation\n        if (register_SIFT3D(&reg, &affine))\n                goto demo_quit;\n\n        // Write the transformation to a file \n        if (write_tform(affine_path, &affine))\n                goto demo_quit;\n\n        // Warp the source image\n        if (im_inv_transform(&affine, &src, LINEAR, SIFT3D_TRUE, &warped))\n                goto demo_quit;\n\n        // Write the warped image to a file\n        if (im_write(warped_path, &warped))\n                goto demo_quit;\n\n        // Clean up\n        im_free(&src);\n        im_free(&ref);\n        im_free(&warped);\n        cleanup_Reg_SIFT3D(&reg);\n        cleanup_tform(&affine);\n\n        return 0;\n\ndemo_quit:\n        // Clean up and return an error\n        im_free(&src);\n        im_free(&ref);\n        im_free(&warped);\n        cleanup_Reg_SIFT3D(&reg);\n        cleanup_tform(&affine);\n\n        return 1;\n}\n\nint main(void) {\n\n        int ret;\n\n        // Do the demo\n        ret = demo();\n\n        // Check for errors\n        if (ret != 0) {\n                fprintf(stderr, \"Fatal demo error, code %d. \\n\", ret);\n                return 1;\n        }\n\n        return 0;\n}\n"
  },
  {
    "path": "examples/registerMatlab.m",
    "content": "% registerMatlab\n%\n% Example of image registration in Matlab\n%\n% Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n\n% Load the images\n[src, srcUnits] = imRead3D('data/1.nii.gz');\n[ref, refUnits] = imRead3D('data/2.nii.gz');\n\n% Register\n[A, matchSrc, matchRef] = registerSift3D(src, ref, 'srcUnits', ...\n    srcUnits, 'refUnits', refUnits);\n\n% Clear MEX memory\nclear mex"
  },
  {
    "path": "imutil/CMakeLists.txt",
    "content": "################################################################################\n# Copyright (c) 2015-2017 Blaine Rister et al., see LICENSE for details.\n################################################################################\n# Build file for the image processing utility library.\n################################################################################\n\n# Find BLAS\nfind_package (BLAS QUIET)\nif (NOT BLAS_FOUND)\n    message (FATAL_ERROR \"BLAS not found. Please set the variable \"\n\t    \"BLAS_LIBRARIES to the location of the BLAS library on \"\n\t    \"your system.\")\nendif() \n\n# Find LAPACK\nfind_package (LAPACK QUIET)\nif (NOT LAPACK_FOUND)\n    message (FATAL_ERROR \"LAPACK not found. Please set the variable \"\n\t    \"LAPACK_LIBRARIES to the location of the LAPACK library on your \"\n\t    \"system.\")\nendif ()\n\n# Find DCMTK\nfind_package (DCMTK QUIET)\nif (DCMTK_FOUND)\n\n        # Add platform-specific DCMTK dependencies\n        if (WIN32)\n                # Add ws2_32 \n                list (APPEND DCMTK_LIBRARIES \"ws2_32\")\n        endif ()\n\n        # Get the base DCMTK include dir. Note that DCMTK_DIR is set incorrectly on \n        # Linux, so we must add additional paths\n        find_path (DCMTK_BASE_INCLUDE_PARENT_DIR \"include/dcmtk\"\n                PATHS ${DCMTK_DIR} \"${DCMTK_config_INCLUDE_DIR}/../../..\")\n        set (DCMTK_BASE_INCLUDE_DIR \"${DCMTK_BASE_INCLUDE_PARENT_DIR}/include\" \n                CACHE PATH \"DCMTK include directory\")\n\n        if (_DCMTK_BASE_INCLUDE_PARENT_DIR STREQUAL\n                \"DCMTK_BASE_INCLUDE_PARENT_DIR-NOTFOUND\")\n                message (FATAL_ERROR \"Failed to find the DCMTK include \"\n                        \"directory. Please set the variable \"\n                        \"DCMTK_BASE_INCLUDE_DIR to <DCMTK-INSTALL>/include, \"\n                        \"or disable DICOM support by setting WITH_DICOM to \"\n                        \"false.\")\n        endif ()\n\n        # Add the base dir to the DCMTK include paths\n        list(APPEND DCMTK_INCLUDE_DIRS ${DCMTK_BASE_INCLUDE_DIR})\n\n        # Check if there is a configuration file for DCMTK\n        find_file(DCMTK_CONFIG_FILE \n                NAMES \"cfunix.h\" \"cfwin32.h\"\n                PATHS ${DCMTK_config_INCLUDE_DIR}\n                NO_CMAKE_PATH\n                NO_CMAKE_ENVIRONMENT_PATH\n                NO_SYSTEM_ENVIRONMENT_PATH)\n        if (DCMTK_CONFIG_FILE STREQUAL \"DCMTK_CONFIG_FILE-NOTFOUND\")\n                set (DCMTK_HAVE_CONFIG_FILE false)\n        else ()\n                set (DCMTK_HAVE_CONFIG_FILE true)\n        endif ()\n\n\tmessage (STATUS \"Found DCMTK.\")\n\nelseif (WITH_DICOM)\n\tmessage (FATAL_ERROR \"DCMTK not found. Please set the variable \"\n                \"DCMTK_DIR to the location of DCMTK on your system, or disable\"\n                \"DICOM support by setting WITH_DICOM to false.\")\nelse ()\n\tmessage (STATUS \"DCMTK not found. Compiling without DICOM support. \"\n                \"To enable DICOM support, set the variable DCMTK_DIR to the \"\n                \"location of DCMTK on your system.\")\nendif ()\nset (WITH_DICOM ${DCMTK_FOUND} CACHE BOOL \"Compile DICOM I/O support\")\nif (WITH_DICOM)\n\tmessage (STATUS \"Building with DICOM support.\")\nendif ()\n\n# Optionally find NIFTI \nfind_package (NIFTI QUIET)\nif (NIFTI_FOUND)\n\tmessage (STATUS \"Found NIFTI.\")\nelseif (WITH_NIFTI)\n\tmessage (FATAL_ERROR \"Failed to find nifticlib. Please set the \"\n                \"variable NIFTI_DIR to the location of DCMTK on your system, \"\n                \"or disable NIFTI support by setting WITH_NIFTI to false.\")\nelse()\n\tmessage (STATUS \"Failed to find nifticlib. Compiling without NIFTI \"\n                \"support. To enable NIFTI support, set the variable NIFTI_DIR \"\n                \"to the location of NIFTI on your system.\")\nendif()\nset (WITH_NIFTI ${NIFTI_FOUND} CACHE BOOL \"Compile NIFTI I/O support\")\nif (WITH_NIFTI)\n\tmessage (STATUS \"Building with NIFTI support.\")\nendif ()\n\n# Find iconv on Mac\nif (APPLE)\n\tfind_library (ICONV_LIBRARY NAMES iconv libiconv libiconv-2 c REQUIRED)\nendif ()\n\n# Find MinGW dependencies\nif (MINGW)\n        find_package (MINGW REQUIRED)\nendif ()\n\n# Macro to optionally add DCMTK to a target\nmacro (add_DICOM arg)\n        if (WITH_DICOM)\n                target_include_directories(${arg} PRIVATE ${DCMTK_INCLUDE_DIRS})\n                target_link_libraries (${arg} PRIVATE ${DCMTK_LIBRARIES})\n                target_compile_definitions(${arg} PRIVATE \"SIFT3D_WITH_DICOM\")\n                if (DCMTK_HAVE_CONFIG_FILE)\n                        target_compile_definitions (${arg} PRIVATE \n                                \"HAVE_CONFIG_H\")\n                endif ()\n        endif ()\nendmacro ()\n\n# Macro to optionally add NIFTI to a target\nmacro (add_NIFTI arg)\n        if (WITH_NIFTI)\n                target_compile_definitions(${arg} PRIVATE \"SIFT3D_WITH_NIFTI\")\n                target_include_directories(${arg} PRIVATE ${NIFTI_INCLUDE_DIRS})\n                target_link_libraries (${arg} PRIVATE ${NIFTI_LIBRARIES})\n        endif ()\nendmacro ()\n\n# Format the compiler definitions\nset (IMUTIL_DEFINITIONS \"SIFT3D_VERSION_NUMBER=${SIFT3D_VERSION}\")\n\n# Check if the compiler uses strndup and strnlen\ncheck_function_exists (strnlen HAVE_STRNLEN)\nif (HAVE_STRNLEN)\n        list (APPEND IMUTIL_DEFINITIONS \"SIFT3D_HAVE_STRNLEN\")\nendif ()\ncheck_function_exists (strndup HAVE_STRNDUP)\nif (HAVE_STRNDUP)\n        list (APPEND IMUTIL_DEFINITIONS \"SIFT3D_HAVE_STRNDUP\")\nendif ()\n\n# Compile imutil\nadd_library (imutil SHARED imutil.c nifti.c dicom.cpp)\ntarget_include_directories (imutil PUBLIC \n                $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>\n                $<INSTALL_INTERFACE:${INSTALL_INCLUDE_DIR}>\n)\ntarget_link_libraries (imutil PRIVATE ${LAPACK_LIBRARIES})\ntarget_compile_definitions (imutil PRIVATE ${IMUTIL_DEFINITIONS})\nadd_DICOM(imutil)\nadd_NIFTI(imutil)\ninstall (FILES imtypes.h immacros.h imutil.h kernels.cl\n        DESTINATION ${INSTALL_INCLUDE_DIR})\n\n# Link to system libraries\ntarget_link_libraries(imutil PRIVATE ${ZLIB_LIBRARIES} ${M_LIBRARY})\ntarget_include_directories(imutil PRIVATE ${ZLIB_INCLUDE_DIR})\nif (APPLE)\n\ttarget_link_libraries(imutil PUBLIC ${ICONV_LIBRARY})\nendif ()\n\n# Configure the installation\ninstall (TARGETS imutil \n        EXPORT SIFT3D-targets \n        RUNTIME DESTINATION ${INSTALL_BIN_DIR} \n\tLIBRARY DESTINATION ${INSTALL_LIB_DIR} \n\tARCHIVE DESTINATION ${INSTALL_LIB_DIR}\n)\n\n# OS-specific installation\nif (WIN32)\n\n\t# Make a list of all external dependencies\n\tset (DEPS ${LAPACK_LIBRARIES} ${BLAS_LIBRARIES} ${ZLIB_LIBRARIES} \n                ${OpenMP_C_LIBRARIES} ${MINGW_LIBRARIES})\n        if (WITH_DICOM)\n                list (APPEND DEPS ${DCMTK_LIBRARIES})\n        endif ()\n        if (WITH_NIFTI)\n                list (APPEND DEPS ${NIFTI_LIBRARIES})\n        endif ()\n\n\tfunction (get_runtime_deps ARG_DEPS ARG_RUNTIME_DEPS)\n\n\t\t# Process each dependency, adding a runtime dependency if \n\t\t# necessary\n\t\tset (${ARG_RUNTIME_DEPS} \"\")\n\t\tforeach (DEP IN LISTS ${ARG_DEPS})\n\n\t\t\t# Get the file extension\t\n\t\t\tget_filename_component (DEP_EXT ${DEP} EXT)\t\t\n\n\t\t\t# Process shared libraries\n\t\t\tif (DEP_EXT STREQUAL \".dll\")\n\t\t\t\tlist (APPEND ${ARG_RUNTIME_DEPS} ${DEP})\n\t\t\t# Process MinGW import libraries\n\t\t\telseif (DEP_EXT STREQUAL \".dll.a\")\n\n\t\t\t\t# Extract the filename, parent and grandparent directories\n\t\t\t\tget_filename_component (DEP_NAME ${DEP} NAME)\n\t\t\t\tget_filename_component (DEP_DIR ${DEP} DIRECTORY)\n\t\t\t\tget_filename_component (DEP_DIR_DIR ${DEP_DIR} DIRECTORY)\n\n\t\t\t\t# Get the name of the .dll version\n\t\t\t\tstring (REGEX REPLACE \".dll.a$\" \".dll\" DEP_DLL_NAME ${DEP_NAME})\n\n\t\t\t\t# Find the corresponding .dll\n\t\t\t\tstring (REGEX REPLACE \".dll\" \"_DLL\" DEP_DLL_VAR ${DEP_DLL_NAME})\n\t\t\t\tfind_file (${DEP_DLL_VAR} ${DEP_DLL_NAME} \n\t\t\t\t\tPATHS ${DEP_DIR} ${DEP_DIR_DIR}\n\t\t\t\t\tPATH_SUFFIXES \"bin\" \"lib\")\n\t\t\t\tif (${DEP_DLL_VAR} STREQUAL \"${DEP_DLL_NAME}-NOTFOUND\")\n\t\t\t\t\tmessage (FATAL_ERROR \n\t\t\t\t\t\t\"Failed to find runtime dependency ${DEP_DLL_NAME}\")\n\t\t\t\tendif ()\n\n\t\t\t\t# The .dll, not the .dll.a, becomes a runtime dependency\n\t\t\t\tlist (APPEND ${ARG_RUNTIME_DEPS} ${${DEP_DLL_VAR}})\n\t\t\tendif ()\n\n\t\tendforeach ()\n\n\t\t# Set the return value\n\t\tset (${ARG_RUNTIME_DEPS} ${${ARG_RUNTIME_DEPS}} PARENT_SCOPE)\n\n\tendfunction ()\n\n\t# Convert dependencies to runtime dependencies\n\tget_runtime_deps (DEPS RUNTIME_DEPS)\n\n\t# Copy the runtime dependencies to the Windows DLL\n\tfile (COPY ${RUNTIME_DEPS} DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})\n\n\t# Add the runtime dependencies to the Windows installer\n\tinstall (FILES ${RUNTIME_DEPS} DESTINATION ${INSTALL_BIN_DIR})\n\nendif ()\n\n# If Matlab was found, compile a copy for use with Matlab libraries\nif (BUILD_Matlab)\n\n        add_library (meximutil SHARED imutil.c nifti.c dicom.cpp)\n        target_compile_definitions (meximutil PUBLIC \"SIFT3D_MEX\")\n        target_compile_definitions (meximutil PRIVATE  ${IMUTIL_DEFINITIONS})\n\n        target_include_directories (meximutil PUBLIC \n                $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>\n                $<INSTALL_INTERFACE:${INSTALL_INCLUDE_DIR}>\n        )\n        target_include_directories(meximutil PUBLIC ${Matlab_INCLUDE_DIRS})\n        target_include_directories (meximutil PRIVATE \n                ${ZLIB_INCLUDE_DIR})\n        target_link_libraries (meximutil PUBLIC ${Matlab_LIBRARIES})\n        target_link_libraries (meximutil PRIVATE ${Matlab_MWLAPACK_LIBRARY} \n                ${Matlab_MWBLAS_LIBRARY} ${ZLIB_LIBRARIES} ${M_LIBRARY})\n        add_DICOM(meximutil)\n        add_NIFTI(meximutil)\n\n        set_target_properties (meximutil \n                PROPERTIES \n                ARCHIVE_OUTPUT_DIRECTORY ${BUILD_TOOLBOX_DIR}\n                LIBRARY_OUTPUT_DIRECTORY ${BUILD_TOOLBOX_DIR}\n                RUNTIME_OUTPUT_DIRECTORY ${BUILD_TOOLBOX_DIR}\n        )\n\n        install (TARGETS meximutil \n                RUNTIME DESTINATION ${INSTALL_TOOLBOX_DIR}\n                LIBRARY DESTINATION ${INSTALL_TOOLBOX_DIR}\n                ARCHIVE DESTINATION ${INSTALL_TOOLBOX_DIR}\n        )\n\n        if (WIN32)\n\n\t\t# The toolbox has the same dependencies except for BLAS and LAPACK\n\t\tset (TOOLBOX_DEPS ${DEPS})\n\t\tlist (REMOVE_ITEM TOOLBOX_DEPS \n\t\t\t${LAPACK_LIBRARIES} ${BLAS_LIBRARIES}\n\t\t)\n\n\t\tget_runtime_deps (TOOLBOX_DEPS TOOLBOX_RUNTIME_DEPS)\n\t\tfile (COPY ${TOOLBOX_RUNTIME_DEPS} DESTINATION \n\t\t\t${BUILD_TOOLBOX_DIR})\n\t\tinstall (FILES ${TOOLBOX_RUNTIME_DEPS} \n\t\t\tDESTINATION ${INSTALL_TOOLBOX_DIR})\n        endif ()\nendif ()\n\n# Add the code snippets\nadd_subdirectory (templates)\n"
  },
  {
    "path": "imutil/dicom.cpp",
    "content": "/* -----------------------------------------------------------------------------\n * dicom.cpp \n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2017 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * C-language wrapper for the DCMTK library.\n * -----------------------------------------------------------------------------\n */\n\n/* SIFT3D includes */\n#include \"imutil.h\"\n#include \"immacros.h\"\n#include \"dicom.h\"\n\n#ifndef SIFT3D_WITH_DICOM\n/* Return error messages if this was not compiled with DICOM support. */ \n\nstatic int dcm_error_message() {\n        SIFT3D_ERR(\"dcm_error_message: SIFT3D was not compiled with DICOM \"\n                \"support!\\n\");\n        return SIFT3D_WRAPPER_NOT_COMPILED;\n}\n\nint read_dcm(const char *path, Image *const im) {\n        return dcm_error_message();\n}\n\nint read_dcm_dir(const char *path, Image *const im) {\n        return dcm_error_message();\n}\n\nint write_dcm(const char *path, const Image *const im, \n        const Dcm_meta *const meta, const float max_val) {\n        return dcm_error_message();\n}\n\nint write_dcm_dir(const char *path, const Image *const im, \n        const Dcm_meta *const meta) {\n        return dcm_error_message();\n}\n\n#else\n\n/*----------------Include the very picky DCMTK----------------*/\n#include \"dcmtk/config/osconfig.h\"    /* make sure OS specific configuration is included first */\n\n#define INCLUDE_CSTDIO\n#define INCLUDE_CSTRING\n#include \"dcmtk/ofstd/ofstdinc.h\"\n\n#ifdef HAVE_GUSI_H\n#include <GUSI.h>\n#endif\n\n#include \"dcmtk/dcmdata/dctk.h\"          /* for various dcmdata headers */\n#include \"dcmtk/dcmdata/cmdlnarg.h\"      /* for prepareCmdLineArgs */\n#include \"dcmtk/dcmdata/dcuid.h\"         /* for dcmtk version name */\n\n#include \"dcmtk/ofstd/ofconapp.h\"        /* for OFConsoleApplication */\n#include \"dcmtk/ofstd/ofcmdln.h\"         /* for OFCommandLine */\n\n#include \"dcmtk/oflog/oflog.h\"           /* for OFLogger */\n\n#include \"dcmtk/dcmimgle/dcmimage.h\"     /* for DicomImage */\n#include \"dcmtk/dcmimgle/diutils.h\"     /* for DIPixel */\n#include \"dcmtk/dcmimage/diregist.h\"     /* include to support color images */\n#include \"dcmtk/dcmdata/dcrledrg.h\"      /* for DcmRLEDecoderRegistration */\n\n#include \"dcmtk/dcmjpeg/djdecode.h\"      /* for dcmjpeg decoders */\n#include \"dcmtk/dcmjpeg/dipijpeg.h\"      /* for dcmimage JPEG plugin */\n\n#include \"dcmtk/dcmjpeg/djencode.h\" /* for JPEG encoding */\n#include \"dcmtk/dcmjpeg/djrplol.h\"  /* for DJ_RPLossless */\n\n#include \"dcmtk/dcmseg/segment.h\" /* Dicom segmentations */\n\n#ifdef WITH_ZLIB\n#include <zlib.h>          /* for zlibVersion() */\n#endif\n/*---------------------------------------------------------*/\n\n/* Other includes */\n#include <algorithm>\n#include <memory>\n#include <vector>\n#include <cmath>\n#include <cfloat>\n#include <cstdlib>\n#include <stdint.h>\n#include <dirent.h>\n\n/* Macro to call a C++ function and catch any exceptions it throws,\n * returning SIFT3D_FAILURE when an exception is caught. The return value is\n * stored in ret. */\n#define CATCH_EXCEPTIONS(ret, tag, fun, ...) \\\n        try { \\\n                ret = (fun)( __VA_ARGS__ ); \\\n        } catch (std::exception &e) { \\\n                SIFT3D_ERR(\"%s: %s\\n\", tag, e.what()); \\\n                ret = SIFT3D_FAILURE; \\\n        } catch (...) { \\\n                SIFT3D_ERR(\"%s: unexpected exception \\n\", tag); \\\n                ret = SIFT3D_FAILURE; \\\n        } \\\n\n/* Format strings for DICOM tags */\nstatic const char *pixelSpacingFmt = \"%lf\\\\%lf\"; // Pixel Spacing\nstatic const char *imPosPatientFmt = \"%f\\\\%f\\\\%f\"; // ImagePositionPatient\nstatic const char *imOriPatientFmt = \"%f\\\\%f\\\\%f\\\\%f\\\\%f\\\\%f\"; \n        // ImageOrientationPatient\n\n/* File separator in string form */\nconst std::string sepStr(1, SIFT3D_FILE_SEP);\n\n/* Dicom parameters */\nstatic const unsigned int dcm_bit_width = 8; // Bits per pixel\n\n/* DICOM metadata defaults */\nstatic const char *default_patient_name = \"DefaultSIFT3DPatient\";\nstatic const char *default_series_descrip = \"Series generated by SIFT3D\";\nstatic const char *default_patient_id = \"DefaultSIFT3DPatientID\";\nstatic const char default_instance_num = 1;\n\n/* Types of DICOMs which necessitate special processing */\nenum image_type {\n        DSO,\n        PET,\n        OTHER\n};\n\n/* Helper declarations */\nclass Dicom;\nstatic bool isLittleEndian(void);\nstatic void default_Dcm_meta(Dcm_meta *const meta);\nstatic int load_file(const char *path, DcmFileFormat &fileFormat);\nstatic int read_dcm_cpp(const char *path, Image *const im);\nstatic int read_dcm_img(const Dicom &dicom, Image *const im);\nstatic int read_dso(const char *imDir, Dicom &dso, \n                            Image *const mask);\nstatic int cvec_max_abs(const Cvec *v, float *const val, int *const pos);\nstatic int read_dcm_dir_meta(const char *path, std::vector<Dicom> &dicoms);\nstatic int read_dcm_dir_cpp(const char *path, Image *const im);\nstatic int write_dcm_cpp(const char *path, const Image *const im,\n        const Dcm_meta *const meta, const float max_val);\nstatic int write_dcm_dir_cpp(const char *path, const Image *const im,\n        const Dcm_meta *const meta);\nstatic void set_meta_defaults(const Dcm_meta *const meta, \n        Dcm_meta *const meta_new);\nstatic int dcm_resize_im(const std::vector<Dicom> &dicoms, Image *const im);\nstatic int write_subvolume(const Dicom &dicom, Image *const main, \n        const int mainOffset, Image *const sub, const int start, \n        const int end);\n\n/* Helper class to store DICOM data. */\nclass Dicom {\nprivate:\n        int axes[2]; // Data axes, e.g. {0, 1} means x, y\n        int axisSigns[2]; // Axis signs, negative means go backwards\n        std::string filename; // DICOM file name\n        std::string classUID;\n        std::string seriesUID; // Series UID \n        std::string instanceUID; // Instance UID\n        double sortCoord; // Coordinate by which the series is sorted. Usually z\n        double ux, uy, uz; // Voxel spacing in real-world coordinates\n        int nx, ny, nz, nc; // Image dimensions\n        int sortAxis; // The axis corresponding to sortCoord\n        bool valid; // Data validity \n\npublic:\n\n        /* Data is initially invalid */\n        Dicom() : valid(false) {};\n\n        ~Dicom() {};\n\n        /* Load a file */\n        Dicom(const char *filename, const int defaults3D=0);\n\n        /* Get the multiplier to convert a PET image to SUV */\n        double PET_SUV_multiplier(void) const;\n\n        /* Get a dimension by index */\n        int getDim(const int idx) const {\n                switch (idx) {\n                case 0:\n                        return getNx();\n                case 1:\n                        return getNy();\n                case 2:\n                        return getNz();\n                }\n\n                return -1;\n        }\n\n        /* Get a unit by index */\n        int getUnit(const int idx) const {\n                switch (idx) {\n                case 0:\n                        return getUx();\n                case 1:\n                        return getUy();\n                case 2:\n                        return getUz();\n                }\n\n                return -1;\n        }\n\n        /* Get the image axis by index */\n        int getAxes(const int idx) const {\n                return idx < 0 || idx > 2 ? -1 : axes[idx];\n        }\n\n        /* Get the axis sign by index */\n        int getAxisSign(const int idx) const {\n                return idx < 0 || idx > 2 ? -1 : axisSigns[idx];\n        }\n\n        /* Get the x-dimension */\n        int getNx(void) const {\n                return nx;\n        }\n\n        /* Get the y-dimension */\n        int getNy(void) const {\n                return ny;\n        }\n\n        /* Get the z-dimension */\n        int getNz(void) const {\n                return nz;\n        }\n\n        /* Get the number of channels */\n        int getNc(void) const {\n                return nc;\n        } \n\n        /* Get the sorting axis */\n        int getSortAxis(void) const {\n                return sortAxis;\n        }\n\n        /* Get the sorting coordinate (usually z) */\n        double getSortCoord(void) const {\n                return sortCoord;\n        }\n\n        /* Get the units in the sorting dimension */\n        int getSortUnit(void) const {\n                return getUnit(getSortAxis());\n        }\n\n        /* Get the x-spacing */\n        double getUx(void) const {\n                return ux;\n        }\n\n        /* Get the y-spacing */\n        double getUy(void) const {\n                return uy;\n        }\n\n        /* Get the z-spacing */\n        double getUz(void) const {\n                return uz;\n        }\n\n        /* Check whether or not the data is valid */\n        bool isValid(void) const {\n                return valid;\n        }\n\n        /* Get the file name */\n        const char *name(void) const {\n                return filename.c_str();\n        }\n\n        /* Sort by z position */\n        bool operator < (const Dicom &dicom) const {\n                return getSortCoord() < dicom.getSortCoord();\n        }\n\n        /* Check if another DICOM file is from the same series */\n        bool eqSeries(const Dicom &dicom) const {\n                return !seriesUID.compare(dicom.seriesUID);\n        }\n\n        /* Check for a matching SOPInstanceUID */\n        bool eqInstance(const char *uid) const {\n                return !instanceUID.compare(uid);\n        }\n\n        /* Return an image type, based on the classUID */\n        enum image_type getType() const {\n                if (!classUID.compare(UID_SegmentationStorage)) {\n                        return DSO;\n                } else if (\n                        !classUID.compare(\n                                UID_PositronEmissionTomographyImageStorage) ||\n                        !classUID.compare(\n                                UID_LegacyConvertedEnhancedPETImageStorage) ||\n                        !classUID.compare(UID_EnhancedPETImageStorage)) {\n                        return PET;\n                } else {\n                        return OTHER;\n                }\n        }\n};\n\n/* Read a DICOM file with DCMTK. */\nstatic int load_file(const char *path, DcmFileFormat &fileFormat) {\n\n        // Load the image as a DcmFileFormat \n        OFCondition status = fileFormat.loadFile(path);\n        if (status.bad()) {\n                SIFT3D_ERR(\"load_file: failed to read DICOM file %s (%s)\\n\",\n                        path, status.text());\n                return SIFT3D_FAILURE;\n        }\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Get the single maximum component from a Cvec, by absolue value. Returns the \n * element and its position. Returns SIFT3D_SUCCESS if the maximum is unique,\n * SIFT3D_FAILURE otherwise. */\nstatic int cvec_max_abs(const Cvec *v, float *const val, int *const pos) {\n\n        int k;\n        const float vals[] = {v->x, v->y, v->z};\n\n        // Find the maximum\n        float maxDiff = 0.f;\n        float maxAbsVal = -1;\n        for (k = 0; k < 3; k++) {\n\n                const float thisVal = vals[k];\n                const float thisAbsVal = fabsf(thisVal);\n        \n                // Check for maximum. If so, write into val and pos\n                if (thisAbsVal < maxAbsVal)\n                        continue;\n                maxDiff = thisAbsVal - maxAbsVal;\n                maxAbsVal = thisAbsVal;\n                *val = thisVal;\n                *pos = k;\n        }\n\n        // Test for unique maxima\n        return maxDiff > 1e-2 ? SIFT3D_SUCCESS : SIFT3D_FAILURE;\n}\n\n/* Load the data from a DICOM file. If defaults3D is true, substitute default\n * values for 3D positioning information. Use this to load a single file even\n * if key metadata is missing, e.g. for 2D images. */\nDicom::Dicom(const char *path, const int defaults3D) : filename(path), \n        valid(false) {\n\n        // Read the file\n        DcmFileFormat fileFormat;\n        if (load_file(path, fileFormat))\n                return;\n        DcmDataset *const data = fileFormat.getDataset();\n\n        // Get the SOPClass UID\n        const char *classUIDStr;\n        OFCondition status = data->findAndGetString(DCM_SOPClassUID, \n                                                    classUIDStr);\n        if (status.bad() || classUIDStr == NULL) {\n                SIFT3D_ERR(\"Dicom.Dicom: failed to get SOPClassUID \"\n                        \"from file %s (%s)\\n\", path, status.text());\n                return;\n        }\n        classUID = std::string(classUIDStr); \n\n        // Get the series UID \n        const char *seriesUIDStr;\n        status = data->findAndGetString(DCM_SeriesInstanceUID, seriesUIDStr);\n        if (status.bad() || seriesUIDStr == NULL) {\n                SIFT3D_ERR(\"Dicom.Dicom: failed to get SeriesInstanceUID \"\n                        \"from file %s (%s)\\n\", path, status.text());\n                return;\n        }\n        seriesUID = std::string(seriesUIDStr); \n\n        // Get the instance UID \n        const char *instanceUIDStr;\n        status = data->findAndGetString(DCM_SOPInstanceUID, instanceUIDStr);\n        if (status.bad() || instanceUIDStr == NULL) {\n                SIFT3D_ERR(\"Dicom.Dicom: failed to get SOPInstanceUID \"\n                        \"from file %s (%s)\\n\", path, status.text());\n                return;\n        }\n        instanceUID = std::string(instanceUIDStr); \n\n        // Get the z coordinate\n        if (getType() == DSO) {\n                // DSOs don't always have coordinates\n                sortCoord = -1;\n\n                // DSOs don't always have units\n                ux = uy = uz = -1.0;\n        } else {\n#if 0\n                // Read the patient position\n                const char *patientPosStr;\n                status = data->findAndGetString(DCM_PatientPosition, \n                                                patientPosStr);\n                if (status.bad() || patientPosStr == NULL) {\n                        SIFT3D_ERR(\"Dicom.Dicom: failed to get \" \n                                \"PatientPosition from file %s (%s)\\n\", path, \n                                status.text());\n                        return;\n                }\n\n                // Interpret the patient position to give the sign of the z axis\n                double zSign;\n                switch (patientPosStr[0]) {\n                case 'H':\n                        zSign = -1.0;\n                        break;\n                case 'F':\n                        zSign = 1.0;\n                        break;\n                default:\n                        SIFT3D_ERR(\"Dicom.Dicom: unrecognized patient \"\n                                \"position: %d\\n\", patientPosStr);\n                        return;\n                }\n#else\n                //TODO: Is this needed?\n                const double zSign = 1.0;\n#endif\n\n                // Read the image position patient vector\n                const char *imPosPatientStr;\n                status = data->findAndGetString(DCM_ImagePositionPatient, \n                        imPosPatientStr);\n                if (status.bad() || imPosPatientStr == NULL) {\n                        SIFT3D_ERR(\"Dicom.Dicom: failed to get \"\n                        \"ImagePositionPatient from file %s (%s)\\n, defaulting\"\n                        \" to zeros. \\n\", path, \n                        status.text());\n                        if (defaults3D) {\n                                imPosPatientStr = \"0\\\\0\\\\0\";\n                        } else return;\n                }\n\n                // Parse the image position patient vector\n                Cvec imPos;\n                if (sscanf(imPosPatientStr, imPosPatientFmt, &imPos.x, &imPos.y, \n                        &imPos.z) != 3) {\n                        SIFT3D_ERR(\"Dicom.Dicom: failed to parse \"\n                                \"ImagePositionPatient value %s from file %s\\n\", \n                                imPosPatientStr, path);\n                        if (defaults3D) {\n                                imPos = {0, 0, 0};\n                        } else return;\n                }\n\n                // Read the image orientation patient vector\n                const char *imOriPatientStr; \n                status = data->findAndGetString(DCM_ImageOrientationPatient,\n                        imOriPatientStr);\n                if (status.bad() || imOriPatientStr == NULL) {\n                        SIFT3D_ERR(\"Dicom.Dicom: failed to get \"\n                        \"ImageOrientationPatient from file %s (%s)\\n\", path, \n                        status.text());\n                        return;\n                }\n\n                // Parse the image orientation patient vectors\n                Cvec imOri1, imOri2;\n                if (sscanf(imOriPatientStr, imOriPatientFmt, \n                        &imOri1.x, &imOri1.y, &imOri1.z, &imOri2.x, &imOri2.y,\n                        &imOri2.z) != 6) {\n                        SIFT3D_ERR(\"Dicom.Dicom: failed to parse \"\n                                \"ImageOrientationPatient value %s from file %s\\n\", \n                                imOriPatientStr, path);\n                        return;\n                }\n\n                // Take the cross-product of orientation vectors to get the \n                // image normal\n                Cvec normal; \n                SIFT3D_CVEC_CROSS(&imOri1, &imOri2, &normal);\n\n                // Project the patient position on the image normal, to get the\n                // sorting coordinate\n                sortCoord = SIFT3D_CVEC_DOT(&imPos, &normal);\n\n                // Get the image axes\n                float oriVals[2];\n                if (cvec_max_abs(&imOri1, oriVals, axes) || \n                        cvec_max_abs(&imOri2, oriVals + 1, axes + 1)) {\n                        SIFT3D_ERR(\"Dicom.Dicom: unsupported format for \"\n                                \"imageOrientationPatient %s from file %s\\n\", \n                                imOriPatientStr, path);\n                        return;\n                }\n\n                // Get the sorting axis\n                for (int k = 0; k < 3; k++) {\n                        if (axes[0] == k || axes[1] == k)\n                                continue;\n                        sortAxis = k;\n                        break;\n                }\n\n                // Compute the signs of the axes\n                for (int k = 0; k < 2; k++) {\n                        axisSigns[k] = oriVals[k] >= 0 ? 1 : -1;\n                }\n\n                // Read the pixel spacing\n                double spacing[2];\n                const char *pixelSpacingStr;\n                status = data->findAndGetString(DCM_PixelSpacing, \n                                                pixelSpacingStr);\n                if (status.bad()) {\n                        SIFT3D_ERR(\"Dicom.Dicom: failed to get pixel spacing \"\n                                \"from file %s (%s)\\n\", path, status.text());\n                        return;\n                }\n                if (sscanf(pixelSpacingStr, pixelSpacingFmt, spacing, \n                        spacing + 1) != 2) {\n                        SIFT3D_ERR(\"Dicom.Dicom: unable to parse pixel \"\n                                \"spacing from file %s \\n\", path);\n                        return;\n                }\n                if (spacing[0] <= 0.0 || spacing[1] <= 0.0) {\n                        SIFT3D_ERR(\"Dicom.Dicom: file %s has invalid pixel \"\n                                \"spacing [%f, %f]\\n\", path, ux, uy);\n                        return;\n                }\n\n                // Convert the pixel spacing into units based on the image axes\n                double *units = &ux;\n                for (int k = 0; k < 2; k++) {\n                       units[axes[k]] = spacing[k]; \n                }\n\n                // Read the slice thickness \n                Float64 sliceThickness;\n                status = data->findAndGetFloat64(DCM_SliceThickness, \n                                                 sliceThickness);\n                if (!status.good()) {\n                        SIFT3D_ERR(\"Dicom.Dicom: failed to get slice \"\n                                \"thickness from file %s (%s)\\n\", path, \n                                status.text());\n                        return;\n                }\n                if (sliceThickness <= 0.0) {\n                        SIFT3D_ERR(\"Dicom.Dicom: file %s has invalid slice \"\n                                \"thickness: %f \\n\", path, uz);\n                        return;\n                }\n\n                // Use the slice thickness as the units for the perpendicular \n                // axis\n                units[sortAxis] = sliceThickness;\n        }\n\n        // Load the DicomImage object\n        DicomImage dicomImage(path);\n        if (dicomImage.getStatus() != EIS_Normal) {\n                SIFT3D_ERR(\"Dicom.Dicom: failed to open image %s (%s)\\n\",\n                        path, \n                        DicomImage::getString(dicomImage.getStatus()));\n                return;\n        }\n\n        // Check for color images\n        if (!dicomImage.isMonochrome()) {\n                SIFT3D_ERR(\"Dicom.Dicom: reading of color DICOM images is \"\n                        \"not supported at this time \\n\");\n                return;\n        }\n        nc = 1;\n\n        // Read the dimensions\n        int *dims = &nx;\n        dims[axes[0]] = dicomImage.getWidth();\n        dims[axes[1]] = dicomImage.getHeight();\n        dims[sortAxis] = dicomImage.getFrameCount();\n        if (nx < 1 || ny < 1 || nz < 1) {\n                SIFT3D_ERR(\"Dicom.Dicom: invalid dimensions for file %s \"\n                        \"(%d, %d, %d)\\n\", path, nx, ny, nz);\n                return;\n        }\n\n        // Set the window \n        dicomImage.setMinMaxWindow();\n\n        valid = true;\n}\n\n/* Check the endianness of the machine. Returns true if the machine is little-\n * endian, false otherwise. */\nstatic bool isLittleEndian(void) {\n        volatile uint16_t i = 0x0123;\n        return ((uint8_t *) &i)[0] == 0x23;\n}\n\n/* Set a Dcm_meta struct to default values. Generates new UIDs. */\nstatic void default_Dcm_meta(Dcm_meta *const meta) {\n        meta->patient_name = default_patient_name;\n        meta->patient_id = default_patient_id;\n        meta->series_descrip = default_series_descrip;\n        dcmGenerateUniqueIdentifier(meta->study_uid, SITE_STUDY_UID_ROOT);\n        dcmGenerateUniqueIdentifier(meta->series_uid, SITE_SERIES_UID_ROOT);\n        dcmGenerateUniqueIdentifier(meta->instance_uid, SITE_INSTANCE_UID_ROOT); \n        meta->instance_num = default_instance_num;\n}\n\n/* Parse a time (TM) string from a Dicom file. */ \nint parseTM(const char *const tm, double *const time) {\n\n        // Buffers to hold the sub-strings\n        char hh[3];\n        char mm[3];\n\n        // Subdivide into the TM into hours, minutes and seconds\n        memcpy(hh, tm, 2);\n        memcpy(mm, tm + 2, 2);\n        hh[2] = mm[2] = '\\0';\n        const char *const ss = tm + 4;\n\n        // Parse the strings\n        errno = 0;\n        const int hours = atoi(hh);\n        const int minutes = atoi(mm);\n        const double seconds = strtod(ss, NULL);\n        if (errno) return SIFT3D_FAILURE;\n\n        // Add up the totals\n        *time = hours * 60.0 * 60.0 + minutes * 60.0 + seconds;\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Get the multiplier to convert a PET scan to SUV values. Returns negative if\n * an error has occured. */\ndouble Dicom::PET_SUV_multiplier(void) const {\n\n        // Read the file\n        const char *path = filename.c_str();\n        DcmFileFormat fileFormat;\n        if (load_file(path, fileFormat))\n                return -1.0;\n        DcmDataset *const data = fileFormat.getDataset();\n\n        // Read the body weight (0010, 1010)\n        Float64 weight;\n        OFCondition status = data->findAndGetFloat64(DCM_PatientWeight, \n                weight);\n        if (status.bad()) {\n                SIFT3D_ERR(\"Dicom.PET_SUV_multiplier: failed to get \"\n                        \"PatientWeight (0010, 1010) from file %s (%s)\\n\", \n                        path, status.text());\n                return -1.0;\n        }\n\n        // Read the radiopharmeceutical dosage (0018, 1074)\n        Float64 dosage;\n        status = data->findAndGetFloat64(DCM_RadionuclideTotalDose,\n                dosage, 0, OFTrue);\n        if (status.bad()) {\n                SIFT3D_ERR(\"Dicom.PET_SUV_multiplier: failed to get \"\n                        \"RadionuclideTotalDose (0018, 1074) from file %s \"\n                        \"(%s)\\n\", path, status.text());\n                return -1.0;\n        }\n\n        // Read the radiopharmeceutical injection time (0018, 1072)\n        // DCM_RadiopharmaceuticalStartTime (TM)\n        const char *timeStr;\n        status = data->findAndGetString(DCM_RadiopharmaceuticalStartTime, \n                timeStr, OFTrue);\n        if (status.bad() || timeStr == NULL) {\n                SIFT3D_ERR(\"Dicom.PET_SUV_multiplier: failed to get \"\n                        \"RadiopharmeceuticalStartTime (0018, 1072) from file %s\"\n                        \" (%s)\\n\", path, status.text());\n                return -1.0;\n        }\n\n        // Parse the injection time\n        Float64 iv_time;\n        if (parseTM(timeStr, &iv_time)) {\n                SIFT3D_ERR(\"Dicom.PET_SUV_multipler: failed to parse \"\n                \"RadiopharmeceuticalStartTime (0018, 1072) value %s from file \"\n                \"%s\\n\", timeStr, path);\n                return -1.0;\n        }\n\n        // Read the image acquistion time (0008, 0032)\n        status = data->findAndGetString(DCM_AcquisitionTime, timeStr);\n        if (status.bad() || timeStr == NULL) {\n                SIFT3D_ERR(\"Dicom.PET_SUV_multiplier: failed to get \"\n                        \"AcquisitionTime (0008, 0032) from file %s\"\n                        \" (%s)\\n\", path, status.text());\n                return -1.0;\n        }\n\n        // Parse the image time\n        Float64 image_time;\n        if (parseTM(timeStr, &image_time)) {\n                SIFT3D_ERR(\"Dicom.PET_SUV_multipler: filed to parse \"\n                        \"AcquisitionTime (0008, 0032) value %s from file %s\\n\", \n                        timeStr, path);\n                return -1.0;\n        }\n\n        // Read the radiopharmeceutical half-life (0018, 1075)\n        Float64 half_life;\n        status = data->findAndGetFloat64(DCM_RadionuclideHalfLife, half_life, 0,\n                OFTrue);\n        if (status.bad()) {\n                SIFT3D_ERR(\"Dicom.PET_SUV_multiplier: failed to get \"\n                        \"RadionuclideHalfLife (0018, 1075) from file %s (%s)\\n\",\n                        path, status.text());\n                return -1.0;\n        }\n\n        // Compute the elapsed time\n        double elapsed_time = iv_time - image_time; \n        if (elapsed_time < 0) {\n                const double day_seconds = 24.0 * 60.0 * 60.0;\n                elapsed_time = elapsed_time + day_seconds;\n        }\n\n        // Compute the time-adjusted dosage\n        const double adjusted_dosage = dosage * \n                pow(2.0, -elapsed_time / half_life);\n\n        // Compute the SUV multiplier\n        return weight / adjusted_dosage;\n}\n\n/* Read a DICOM file into an Image struct. If the file is a DSO, the \n * referenced DICOM image must be in the same directory. */\nint read_dcm(const char *path, Image *const im) {\n\n        int ret;\n\n        CATCH_EXCEPTIONS(ret, \"read_dcm\", read_dcm_cpp, path, im);\n\n        return ret;\n}\n\n/* Read all of the DICOM files from a directory into an Image struct. Slices \n * must be ordered alphanumerically, starting with z = 0. */\nint read_dcm_dir(const char *path, Image *const im) {\n\n        int ret;\n\n        CATCH_EXCEPTIONS(ret, \"read_dcm_dir\", read_dcm_dir_cpp, path, im);\n\n        return ret;\n}\n\n/* Write an Image struct into a DICOM file. \n * Inputs: \n *      path - File name\n *      im - Image data\n *      meta - Dicom metadata (or NULL for default values)\n *      max_val - The maximum value of the image, used for scaling. If set\n *              to a negative number, this functions computes the maximum value\n *              from this image.\n *\n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise.\n */\nint write_dcm(const char *path, const Image *const im, \n        const Dcm_meta *const meta, const float max_val) {\n\n        int ret;\n\n        CATCH_EXCEPTIONS(ret, \"write_dcm\", write_dcm_cpp, path, im, meta, \n                max_val);\n\n        return ret;\n}\n\n/* Write an Image struct into a directory of DICOM files.\n * Inputs: \n *      path - File name\n *      im - Image data\n *      meta - Dicom metadata (or NULL for default values)\n *\n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise.\n */\nint write_dcm_dir(const char *path, const Image *const im, \n        const Dcm_meta *const meta) {\n\n        int ret;\n\n        CATCH_EXCEPTIONS(ret, \"write_dcm_dir\", write_dcm_dir_cpp, path, im, \n                meta);\n\n        return ret;\n}\n\n/* Helper function to read a DICOM file using C++ */\nstatic int read_dcm_cpp(const char *path, Image *const im) {\n\n        // Read the image metadata\n\tDicom dicom(path, 1);\n        if (!dicom.isValid())\n                return SIFT3D_FAILURE;\n\n        // Check if this is a segmentation or normal image\n        if (dicom.getType() == DSO) {\n                // Look for images only in the same directory\n                char *pathDir = im_get_parent_dir(path);\n                int ret = read_dso(pathDir, dicom, im);\n                free(pathDir);\n                return ret;\n         }\n       \n        // Otherwise directly read the image data \n        return read_dcm_img(dicom, im);\n}\n\n/* Helper function to read DICOM image data */\nstatic int read_dcm_img(const Dicom &dicom, Image *const im) {\n\n        int offsets[] = {0, 0, 0};\n        int signs[] = {1, 1, 1};\n        const void *data;\n        const DiMonoPixel *pixels;\n\tuint32_t shift;\n\tint x, y, z, depth, k;\n        const int bufNBits = 32;\n\n        // Image data strides\n        const int y_stride = dicom.getNx(); \n        const int z_stride = dicom.getNx() * dicom.getNy();\n\n\t// Initialize JPEG decoders\n\tDJDecoderRegistration::registerCodecs();\n\n        // Initialize the DicomImage object\n        const char *path = dicom.name();\n\tDicomImage dicomImage(path);\n        if (dicomImage.getStatus() != EIS_Normal) {\n                SIFT3D_ERR(\"read_dcm_cpp: failed to open image %s (%s)\\n\",\n                        path, DicomImage::getString(dicomImage.getStatus()));\n\t\tgoto read_dcm_img_quit;\n        }\n\n        // Initialize the image fields\n        im->nx = dicom.getNx();\n        im->ny = dicom.getNy();\n        im->nz = dicom.getNz();\n        im->nc = dicom.getNc();\n        im->ux = dicom.getUx();\n        im->uy = dicom.getUy();\n        im->uz = dicom.getUz();\n\n        // Resize the output\n        im_default_stride(im);\n        if (im_resize(im))\n\t\tgoto read_dcm_img_quit;\n\n        // Format the offsets and signs for the main volume\n        for (k = 0; k < 2; k++) {\n                if (dicom.getAxisSign(k) > 0) \n                        continue;\n                const int axisIdx = dicom.getAxes(k);\n                signs[axisIdx] = -1;\n                offsets[axisIdx] = dicom.getDim(axisIdx) - 1;\n        }\n\n        // Get the vendor-independent intermediate pixel data\n        pixels = (const DiMonoPixel *) dicomImage.getInterData();\n        if (pixels == NULL) {\n                SIFT3D_ERR(\"read_dcm_img: failed to get intermediate data for \"\n                        \"%s\\n\", path);\n                goto read_dcm_img_quit;\n        }\n        depth = pixels->getBits();\n\n        // Macro to copy the data\n#define COPY_DATA(type) { \\\n        SIFT3D_IM_LOOP_START(im, x, y, z) \\\n                SIFT3D_IM_GET_VOX(im, x * signs[0] + offsets[0], \\\n                        y * signs[1] + offsets[1], \\\n                        z * signs[2] + offsets[2], \\\n                        0) = (float) *((type *) data + x + y * y_stride + \\\n                                z * z_stride);\\\n        SIFT3D_IM_LOOP_END \\\n        }\\\n\n        // Choose the appropriate data type and copy the data\n        data = pixels->getData(); \n        switch (pixels->getRepresentation()) {\n        case EPR_Uint8:\n                COPY_DATA(uint8_t)\n                break;\n        case EPR_Uint16:\n                COPY_DATA(uint16_t)\n                break;\n        case EPR_Uint32:\n                COPY_DATA(uint32_t)\n                break;\n        case EPR_Sint8:\n                COPY_DATA(int8_t)\n                break;\n        case EPR_Sint16:\n                COPY_DATA(int16_t)\n                break;\n        case EPR_Sint32:\n                COPY_DATA(int32_t)\n                break;\n        default:\n                SIFT3D_ERR(\"read_dcm_img: unrecognized pixel representation \"\n                        \"for %s\\n\", path);\n                goto read_dcm_img_quit;\n        }\n#undef COPY_DATA\n\n#if 0\n        // Get the bit depth of the image\n        depth = dicomImage.Depth();\n        if (depth > bufNBits) \n                SIFT3D_ERR(\"read_dcm_cpp: buffer is insufficiently wide \"\n                        \"for %d-bit data of image %s \\n\", depth, path);\n\t\tgoto read_dcm_img_quit;\n        }\n\n        // Get the number of bits by which we need to shift the 32-bit data, to\n        // recover the original resolution (DICOM uses Big-endian encoding)\n        shift = isLittleEndian() ? static_cast<uint32_t>(bufNBits - depth) : 0;\n\n        // Read each frame\n        for (int i = 0; i < im->nz; i++) { \n\n                int x, y, z;\n\n                const int x_start = 0;\n                const int y_start = 0;\n                const int z_start = i;\n                const int x_end = im->nx - 1;\n                const int y_end = im->ny - 1;\n                const int z_end = z_start;\n\n                // Get a pointer to the data, rendered as a 32-bit int\n                const uint32_t *const frameData = \n                        static_cast<const uint32_t *const>(\n                                dicomImage.getOutputData(\n                                        static_cast<int>(bufNBits), i));\n                if (frameData == NULL) {\n                        SIFT3D_ERR(\"read_dcm_cpp: could not get data from \"\n                                \"image %s frame %d (%s)\\n\", path, i, \n                                DicomImage::getString(dicomImage.getStatus()));\n\t\t\tgoto read_dcm_img_quit;\n                }\n\n                // Copy the frame\n                SIFT3D_IM_LOOP_LIMITED_START(im, x, y, z, x_start, x_end,\n                        y_start, y_end, z_start, z_end)\n\n                        // Get the voxel and shift it to match the original\n                        // magnitude \n                        const uint32_t vox =\n                                frameData[x + y * im->nx] >> shift;\n\n                        // Convert to float and write to the output image\n                        SIFT3D_IM_GET_VOX(im, x, y, z, 0) =\n                                static_cast<float>(vox);\n\n                SIFT3D_IM_LOOP_END\n        }\n#endif\n\n\t// Clean up\n\tDJDecoderRegistration::cleanup();\n\n        // Modality-specific post-processing\n        if (dicom.getType() == PET) {\n\n                const double suv_factor = dicom.PET_SUV_multiplier();\n\n                // Check for errors in SUV computation\n                if (suv_factor < 0)\n                        return SIFT3D_FAILURE;\n\n                // Scale all the values\n                SIFT3D_IM_LOOP_START(im, x, y, z)\n                        SIFT3D_IM_GET_VOX(im, x, y, z, 0) *= suv_factor;\n                SIFT3D_IM_LOOP_END\n        }\n\n        return SIFT3D_SUCCESS;\n\nread_dcm_img_quit:\n\tDJDecoderRegistration::cleanup();\n\treturn SIFT3D_FAILURE;\n}\n\n/* Read a DICOM Segmentation Object (DSO) mask. \n *\n * Parameters:\n *      imDir - The path of the directory containing DICOM image files.\n *      dsoPath - The path of the DSO file.\n *      mask - The image in which to write a binary mask.\n *\n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise.\n */\nstatic int read_dso(const char *imDir, Dicom &dso, \n                            Image *const mask) {\n\n        // Read the DSO\n        DcmFileFormat fileFormat;\n        const char *dsoPath = dso.name();\n        if (load_file(dso.name(), fileFormat))\n                return SIFT3D_FAILURE;\n        DcmDataset *const dso_data = fileFormat.getDataset();\n\n        // Initialize the DcmSegmentation object\n        OFCondition status;\n        OFunique_ptr<DcmSegmentation> dcmSegmentation;\n        {\n                DcmSegmentation *p;\n                status = DcmSegmentation::loadDataset(*dso_data, p);\n                dcmSegmentation.reset(p);\n        }\n        if (status.bad()) {\n                SIFT3D_ERR(\"read_dso: failed to initialize the \"\n                        \"DcmSegmentation object for file %s (%s)\\n\", dsoPath,\n                        status.text());\n                return SIFT3D_FAILURE;\n        }\n\n        // Ensure we have only one segment\n        const int num_segments = dcmSegmentation->getNumberOfSegments();\n        if (num_segments != 1) {\n                SIFT3D_ERR(\"read_dso: unsupported number of segments \"\n                        \" in file %s: %d\\n\", dsoPath, num_segments);\n                return SIFT3D_FAILURE;\n        }\n\n        // Check how many frames we have\n        const int nz = dcmSegmentation->getNumberOfFrames();\n\n        // Get the referenced series items\n        OFVector<IODSeriesAndInstanceReferenceMacro::ReferencedSeriesItem*>&\n                series_items = dcmSegmentation->getCommonInstanceReference()\n                        .getReferencedSeriesItems();\n\n        // Ensure there is only one series\n        const int num_series = series_items.size();\n        if (num_series != 1) {\n                SIFT3D_ERR(\"read_dso: unsupported number of referenced \"\n                        \"series: %d \\n\", num_series);\n                return SIFT3D_FAILURE;\n        }\n\n        // Get the referenced instance items\n        OFVector<SOPInstanceReferenceMacro*> &ref_instances =\n                series_items[0]->getReferencedInstanceItems();\n\n        // Read the image metadata\n        std::vector<Dicom> images;\n        if (read_dcm_dir_meta(imDir, images))\n                return SIFT3D_FAILURE;\n\n        // Read the mask DICOM image data\n        Image maskData;\n        init_im(&maskData);\n        if (read_dcm_img(dso, &maskData)) {\n                im_free(&maskData);\n                return SIFT3D_FAILURE;\n        }\n\n        // Ensure we have a single frame for each instance\n        if (ref_instances.size() != maskData.nz) {\n                SIFT3D_ERR(\"read_dso: DSO has %lu frames but %d segments \\n\",\n                        ref_instances.size(), maskData.nz);\n                im_free(&maskData);\n                return SIFT3D_FAILURE;\n        }\n\n        // Initialize and zero the mask\n        if (dcm_resize_im(images, mask)) {\n                im_free(&maskData);\n                return SIFT3D_FAILURE;\n        }\n        im_zero(mask);\n\n        // Ensure the x,y dimensions match\n        if (mask->nx != maskData.nx || mask->ny != maskData.ny) {\n                SIFT3D_ERR(\"read_dso: Mask has frame dimensions [%d x %d] \"\n                        \"while image volume has dimensions [%d x %d] \\n\",\n                        maskData.nx, maskData.ny, mask->nx, mask->ny);\n                im_free(&maskData);\n                return SIFT3D_FAILURE;\n        }\n\n        // Copy each frame into the mask\n        for (auto it = ref_instances.begin(); it != ref_instances.end(); \n                ++it) {\n\n                // Get the SOPInstanceUID\n                OFString ref_uid;\n                status = (*it)->getReferencedSOPInstanceUID(ref_uid);\n                if (status.bad()) {\n                        SIFT3D_ERR(\"read_dso: Failed to get \"\n                                \"ReferencedSOPInstanceUID (%s) \\n\",\n                                status.text());\n                        im_free(&maskData);\n                        return SIFT3D_FAILURE;\n                }\n                const char *ref_uid_str = ref_uid.c_str();\n\n                // Check for a matching UID in the images\n                auto match = std::find_if(\n                        images.begin(), \n                        images.end(), \n                        [ref_uid_str] (Dicom &image) {\n                                return image.eqInstance(ref_uid_str);\n                        }\n                );\n                if (match == images.end()) {\n                        SIFT3D_ERR(\"read_dso: No image found with \"\n                                \"SOPInstanceUID %s \\n\", ref_uid_str);\n                        im_free(&maskData);\n                        return SIFT3D_FAILURE;\n                }\n\n                // Get the z offsets\n                const int im_frame_num = std::distance(images.begin(), match);\n                const int dso_frame_num = std::distance(ref_instances.begin(), \n                                                        it);\n\n                // Copy the frame into the full mask volume\n                if (write_subvolume(*match, mask, im_frame_num, &maskData, \n                        dso_frame_num, dso_frame_num)) {\n                        im_free(&maskData);\n                        return SIFT3D_FAILURE;\n                }\n        }\n\n        // Clean up\n        im_free(&maskData);\n        return SIFT3D_SUCCESS;\n}\n\n/* Helper function to read the metadata from a directory of DICOM files */\nstatic int read_dcm_dir_meta(const char *path, std::vector<Dicom> &dicoms) {\n\n        struct stat st;\n        DIR *dir;\n        struct dirent *ent;\n\n        // Verify that the directory exists\n\tif (stat(path, &st)) {\n                SIFT3D_ERR(\"read_dcm_dir_cpp: cannot find file %s \\n\", path);\n                return SIFT3D_FAILURE;\n\t} else if (!S_ISDIR(st.st_mode)) {\n                SIFT3D_ERR(\"read_dcm_dir_cpp: file %s is not a directory \\n\",\n                        path);\n                return SIFT3D_FAILURE;\n\t}\n\n        // Open the directory\n        if ((dir = opendir(path)) == NULL) {\n                SIFT3D_ERR(\"read_dcm_dir_cpp: unexpected error opening \"\n                        \"directory %s \\n\", path);\n                return SIFT3D_FAILURE;\n        }\n\n        // Get all of the .dcm files in the directory, ignoring DSOs\n        dicoms.clear();\n        while ((ent = readdir(dir)) != NULL) {\n\n                // Form the full file path\n                std::string fullfile(std::string(path) + sepStr + ent->d_name);\n\n                // Check if it is a DICOM file \n                if (im_get_format(fullfile.c_str()) != DICOM)\n                        continue;\n\n                // Read the file\n                Dicom dicom(fullfile.c_str(), 0);\n                if (!dicom.isValid()) {\n                        closedir(dir);\n                        return SIFT3D_FAILURE;\n                }\n\n                // Ignore DSOs\n                if (dicom.getType() == DSO)\n                        continue;\n\n                // Add the file to the list\n                dicoms.push_back(dicom);\n        }\n\n        // Release the directory\n        closedir(dir);\n        \n        // Verify that dicom files were found\n        if (dicoms.size() == 0) {\n                SIFT3D_ERR(\"read_dcm_dir_cpp: no DICOM files found in %s \\n\",\n                        path);\n                return SIFT3D_FAILURE;\n        }\n\n        // Sort the slices by their coordinates\n        std::sort(dicoms.begin(), dicoms.end()); \n\n        return SIFT3D_SUCCESS;\n}\n\n/* Resize an image to fit a DICOM series. */\nstatic int dcm_resize_im(const std::vector<Dicom> &dicoms, Image *const im) {\n\n        int i, j;\n\n        // Verify that the files are from the same series, and other metadata\n        const int num_files = dicoms.size();\n        const Dicom &first = dicoms[0];\n        const int sortAxis = first.getSortAxis();\n        for (int i = 1; i < num_files; i++) {\n\n                const Dicom &dicom = dicoms[i];\n\n                // Verify the SOPSeriesUID\n                if (!first.eqSeries(dicom)) {\n                        SIFT3D_ERR(\"read_dcm_dir_cpp: file %s is from a \"\n                                \"different series than file %s \\n\", \n                                dicom.name(), first.name());\n                        return SIFT3D_FAILURE;\n                }\n\n                // Verify the sorting axis\n                if (dicom.getSortAxis() != sortAxis) {\n                        SIFT3D_ERR(\"read_dcm_dir_cpp: file %s (%d) is sorted \"\n                                \"by a different axis than file %s (%d) \\n\",\n                                dicom.name(), dicom.getSortAxis(), first.name(),\n                                sortAxis);\n                        return SIFT3D_INCONSISTENT_AXES;\n                }\n        }\n\n        // Initialize the units to those of the first image\n        im->ux = first.getUx(); \n        im->uy = first.getUy();\n        im->uz = first.getUz();\n\n        // Compute the spacing between slices\n        if (num_files > 1) {\n\n                // Tolerance for spacing discrepancies\n                const double tol_spacing = 5E-2;\n\n                // If there are multiple slices, compute the slice spacing by \n                // subtracting the first two images\n                const double first_spacing = fabs(first.getSortCoord() - \n                        dicoms[1].getSortCoord());\n\n                // Ensure all the other spacings agree, else throw an error\n                for (int i = 0; i < num_files - 1; i++) {\n\n                        const Dicom &prev = dicoms[i];\n                        const Dicom &next = dicoms[i + 1];\n\n                        const double spacing = fabs(prev.getSortCoord() - \n                                next.getSortCoord());\n\n                        // Check for duplicates\n                        if (spacing == 0.0) {\n                                SIFT3D_ERR(\"read_dcm_dir_cpp: files %s and %s \"\n                                \"have duplicate coordinates in the sorting \"\n                                \"dimension (%d) \\n\", prev.name(), next.name(),\n                                prev.getSortAxis());\n                                return SIFT3D_DUPLICATE_SLICES;\n                        }\n\n                        // Check the spacing\n                        if (fabs(spacing - first_spacing) <= tol_spacing)\n                                continue;\n\n                        SIFT3D_ERR(\"read_dcm_dir_cpp: files %s and %s are \"\n                                \"spaced %fmm apart, which does not follow the \"\n                                \"series spacing of %fmm \\n\", \n                                prev.name(), next.name(), spacing, \n                                first_spacing);\n                        return SIFT3D_UNEVEN_SPACING;\n                }\n\n                // Ensure all the slice thicknesses agree, else print a warning\n                for (int i = 1; i < num_files; i++) {\n\n                        const Dicom &dicom = dicoms[i];\n                        const double thickness = dicom.getSortUnit();\n\n                        if (fabs(first_spacing - thickness) <= tol_spacing)\n                                continue; \n\n                        SIFT3D_ERR(\"read_dcm_dir_cpp: WARNING--file %s has \"\n                                \"a slice thickness of %fmm, which does not \"\n                                \"agree with the series slice spacing of %fmm\\n\",\n                                dicom.name(), thickness, first_spacing);\n\n                        // No need for multiple warnings\n                        break;\n                }\n               \n                SIFT3D_IM_GET_UNITS(im)[sortAxis] = first_spacing;\n        }\n\n        // Initialize the output dimensions to those of the first image\n        int dims[] = {first.getNx(), first.getNy(), first.getNz()};\n        int nc = first.getNc();\n\n        // Verify the dimensions of the other files, counting the total\n        // number of slices\n        int nSlice = 0;\n        for (i = 0; i < num_files; i++) {\n\n                // Get a slice\n                const Dicom &dicom = dicoms[i];        \n\n                // Verify the channels\n                if (dicom.getNc() != nc) {\n                        SIFT3D_ERR(\"read_dcm_dir_cpp: slice %s \"\n                                \"(%d) does not match the \"\n                                \"channels of slice %s (%d) \\n\",\n                                dicom.name(), dicom.getNc(), \n                                first.name(), nc);\n                }\n\n                // Verify the non-sorting dimensions\n                for (j = 0; j < 2; j++) {\n                        const int firstDim = dims[j];\n                        const int sliceDim = dicom.getDim(j);\n\n                        if (sliceDim == firstDim)\n                                continue;\n\n                        SIFT3D_ERR(\"read_dcm_dir_cpp: slice %s \"\n                                \"dimension %d (%d) does not match the \"\n                                \"dimensions of slice %s (%d) \\n\",\n                                dicom.name(), sliceDim,  sortAxis, first.name(),\n                                firstDim);\n                        return SIFT3D_FAILURE;\n                }\n\n                // Count the number of slices\n                nSlice += dicom.getDim(sortAxis);\n        }\n\n        // Set the dimension of the sorting axis\n        dims[sortAxis] = nSlice;\n\n        // Resize the output\n        memcpy(SIFT3D_IM_GET_DIMS(im), dims, sizeof(dims));\n        im->nc = nc;\n        im_default_stride(im);\n        if (im_resize(im))\n                return SIFT3D_FAILURE;\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Copy a Dicom sub-volume into a larger volume. start and end are indices into\n * sub, along the sorting dimension.  */\nstatic int write_subvolume(const Dicom &dicom, Image *const main, \n        const int mainOffset, Image *const sub, const int start, \n        const int end) {\n\n        int mainOffsets[] = {0, 0, 0};\n        int starts[] = {0, 0, 0};\n        int ends[] = {sub->nx - 1, sub->ny - 1, sub->nz - 1};\n        int x, y, z, c, k;\n        \n        const int sortAxis = dicom.getSortAxis();\n        const int mainSortDim = SIFT3D_IM_GET_DIMS(main)[sortAxis];\n        const int subSortDim = SIFT3D_IM_GET_DIMS(sub)[sortAxis];\n\n        // Verify the image axis dimensions\n        for (k = 0; k < 2; k++) {\n                const int axisIdx = dicom.getAxes(k);\n                const int mainDim = SIFT3D_IM_GET_DIMS(main)[axisIdx];\n                const int subDim = SIFT3D_IM_GET_DIMS(sub)[axisIdx];\n                if (mainDim != subDim) {\n                        SIFT3D_ERR(\"write_subvolume: axis %d does not match: \"\n                                \"%d (main) vs %d (sub)\", axisIdx, mainDim, \n                                subDim);\n                        return SIFT3D_FAILURE;\n                }\n        }\n\n        // Verify the sorting axis dimensions\n        if (start < 0) {\n                SIFT3D_ERR(\"write_subvolume: invalid start %d\\n\", start);\n                return SIFT3D_FAILURE;\n        }\n        if (end > subSortDim) {\n                SIFT3D_ERR(\"write_subvolume: invalid end %d (maximum %d)\\n\", \n                        end, subSortDim);\n                return SIFT3D_FAILURE;\n        }\n        if (mainSortDim < mainOffset + end - start) {\n                SIFT3D_ERR(\"write_subvolume: subvolume range (%d, %d) does \"\n                        \"not fit into main volume dimension %d, offset %d, \"\n                        \"axis %d\\n\", start,  end, mainSortDim, mainOffset, \n                        sortAxis);\n                return SIFT3D_FAILURE;\n        }\n\n        // Set the starting and ending indices for the sort axis\n        starts[sortAxis] = start;\n        ends[sortAxis] = end;\n        mainOffsets[sortAxis] = mainOffset;\n\n        // Copy the data\n        SIFT3D_IM_LOOP_LIMITED_START_C(sub, x, y, z, c, starts[0], ends[0],\n                starts[1], ends[1], starts[2], ends[2])\n                SIFT3D_IM_GET_VOX(main, x + mainOffsets[0], y + mainOffsets[1],\n                        z + mainOffsets[2], c) = \n                        SIFT3D_IM_GET_VOX(sub, x, y, z, c);\n        SIFT3D_IM_LOOP_END_C\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Helper function to read a directory of DICOM files using C++ */\nstatic int read_dcm_dir_cpp(const char *path, Image *const im) {\n\n        int ret, i, j, sliceCount;\n\n        // Read the DICOM metadata\n        std::vector<Dicom> dicoms;\n        if (read_dcm_dir_meta(path, dicoms))\n                return SIFT3D_FAILURE;\n\n        // Initialize the image volume\n        if (ret = dcm_resize_im(dicoms, im))\n                return ret;\n\n        // Allocate a temporary image for the slices\n        Image slice;\n        init_im(&slice);\n\n        // Read the image data\n        sliceCount = 0;\n        const int num_files = dicoms.size();\n        for (i = 0; i < num_files; i++) {\n\n                Dicom dicom = dicoms[i];\n\n                // Read the slice data and write it into the volume\n                const int numSlices = dicom.getDim(dicom.getSortAxis());\n                if (read_dcm_img(dicom, &slice) ||\n                        write_subvolume(dicom, im, sliceCount, &slice, 0, \n                                numSlices - 1)) { \n                        im_free(&slice);\n                        return SIFT3D_FAILURE;\n                }\n\n                sliceCount += numSlices;\n        }\n        im_free(&slice);\n\n        return SIFT3D_SUCCESS;\n} \n\n/* Helper function to set meta_new to default values if meta is NULL,\n * otherwise copy meta to meta_new */\nstatic void set_meta_defaults(const Dcm_meta *const meta, \n        Dcm_meta *const meta_new) {\n        if (meta == NULL) {\n                default_Dcm_meta(meta_new);\n        } else {\n                *meta_new = *meta;        \n        }\n}\n\n/* Helper function to write a DICOM file using C++ */\nstatic int write_dcm_cpp(const char *path, const Image *const im,\n        const Dcm_meta *const meta, const float max_val) {\n\n#define BUF_LEN 1024\n        char buf[BUF_LEN];\n\n        // Ensure the image is monochromatic\n        if (im->nc != 1) {\n                SIFT3D_ERR(\"write_dcm_cpp: image %s has %d channels. \"\n                        \"Currently only single-channel images are supported.\\n\",\n                         path, im->nc);\n                return SIFT3D_FAILURE;\n        }\n\n        // If no metadata was provided, initialize default metadata\n        Dcm_meta meta_new;\n        set_meta_defaults(meta, &meta_new);\n\n        // Create a new fileformat object\n        DcmFileFormat fileFormat;\n\n        // Set the file type to derived\n        DcmDataset *const dataset = fileFormat.getDataset();\n        OFCondition status = dataset->putAndInsertString(DCM_ImageType, \n                                                         \"DERIVED\");\n        if (status.bad()) {\n                std::cerr << \"write_dcm_cpp: Failed to set the image type\" <<\n                        std::endl;\n                return SIFT3D_FAILURE;\n        }\n\n        // Set the class UID\n        dataset->putAndInsertString(DCM_SOPClassUID, \n                UID_CTImageStorage);\n        if (status.bad()) {\n                SIFT3D_ERR(\"write_dcm_cpp: Failed to set the SOPClassUID\\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Set the photometric interpretation\n        const char *photoInterp;\n        if (im->nc == 1) {\n                photoInterp = \"MONOCHROME2\";\n        } else if (im->nc == 3) {\n                photoInterp = \"RGB\";\n        } else {\n                SIFT3D_ERR(\"write_dcm_cpp: failed to determine the \"\n                        \"photometric representation for %d channels \\n\", \n                        im->nc);\n                return SIFT3D_FAILURE;\n        }\n        dataset->putAndInsertString(DCM_PhotometricInterpretation,\n                photoInterp);\n        if (status.bad()) {\n                SIFT3D_ERR(\"write_dcm_cpp: Failed to set the photometric \"\n                        \"interpretation \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Set the pixel representation to unsigned\n        dataset->putAndInsertUint16(DCM_PixelRepresentation, 0);\n        if (status.bad()) {\n                SIFT3D_ERR(\"write_dcm_cpp: Failed to set the pixel \"\n                        \"representation \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Set the number of channels (Samples Per Pixel) and set the planar\n        // configuration to interlaced pixels\n        assert(SIFT3D_IM_GET_IDX(im, 0, 0, 0, 1) == \n                SIFT3D_IM_GET_IDX(im, 0, 0, 0, 0) + 1);\n        snprintf(buf, BUF_LEN, \"%d\", im->nc);\n        dataset->putAndInsertString(DCM_SamplesPerPixel, buf);\n        dataset->putAndInsertString(DCM_PlanarConfiguration, \"0\");\n\n        // Set the bits allocated and stored, in big endian format \n        const unsigned int dcm_high_bit = dcm_bit_width - 1;\n        dataset->putAndInsertUint16(DCM_BitsAllocated, dcm_bit_width);\n        dataset->putAndInsertUint16(DCM_BitsStored, dcm_bit_width);\n        dataset->putAndInsertUint16(DCM_HighBit, dcm_high_bit);\n        if (status.bad()) {\n                SIFT3D_ERR(\"write_dcm_cpp: Failed to set the bit widths \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Set the patient name\n        status = dataset->putAndInsertString(DCM_PatientName, \n                meta_new.patient_name);\n        if (status.bad()) {\n                SIFT3D_ERR(\"write_dcm_cpp: Failed to set the patient name\\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Set the patient ID\n        status = dataset->putAndInsertString(DCM_PatientID,\n                meta_new.patient_id);\n        if (status.bad()) {\n                SIFT3D_ERR(\"write_dcm_cpp: Failed to set the patient ID \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Set the study UID\n        status = dataset->putAndInsertString(DCM_StudyInstanceUID,\n                meta_new.study_uid);\n        if (status.bad()) {\n                SIFT3D_ERR(\"write_dcm_cpp: Failed to set the \"\n                        \"StudyInstanceUID \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Set the series UID\n        status = dataset->putAndInsertString(DCM_SeriesInstanceUID,\n                meta_new.series_uid);\n        if (status.bad()) {\n                SIFT3D_ERR(\"write_dcm_cpp: Failed to set the \"\n                        \"SeriesInstanceUID \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Set the series description\n        status = dataset->putAndInsertString(DCM_SeriesDescription,\n                meta_new.series_descrip);\n        if (status.bad()) {\n                SIFT3D_ERR(\"write_dcm_cpp: Failed to set the series \"\n                        \"description \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Set the instance UID\n        status = dataset->putAndInsertString(DCM_SOPInstanceUID, \n                meta_new.instance_uid);\n        if (status.bad()) {\n                SIFT3D_ERR(\"write_dcm_cpp: failed to set the \"\n                        \"SOPInstanceUID \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Set the dimensions\n        OFCondition xstatus = dataset->putAndInsertUint16(DCM_Rows, im->ny); \n        OFCondition ystatus = dataset->putAndInsertUint16(DCM_Columns, im->nx);\n        snprintf(buf, BUF_LEN, \"%d\", im->nz);\n        OFCondition zstatus = dataset->putAndInsertString(DCM_NumberOfFrames,\n                buf);\n        if (xstatus.bad() || ystatus.bad() || zstatus.bad()) {\n                SIFT3D_ERR(\"write_dcm_cpp: Failed to set the dimensions \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Set the instance number\n        snprintf(buf, BUF_LEN, \"%u\", meta_new.instance_num);\n        status = dataset->putAndInsertString(DCM_InstanceNumber, buf);\n        if (status.bad()) {\n                SIFT3D_ERR(\"write_dcm_cpp: Failed to set the instance \"\n                        \"number \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Set the ImagePositionPatient vector\n        const double imPosX = static_cast<double>(im->nx - 1) * im->ux;\n        const double imPosY = static_cast<double>(im->ny - 1) * im->uy;\n        const double imPosZ = static_cast<double>(meta_new.instance_num) * \n                              im->uz;\n        snprintf(buf, BUF_LEN, imPosPatientFmt, imPosX, imPosY, imPosZ);\n        status = dataset->putAndInsertString(DCM_ImagePositionPatient, buf);\n        if (status.bad()) {\n                SIFT3D_ERR(\"write_dcm_cpp: Failed to set the \"\n                        \"ImagePositionPatient vector \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Set the ImageOrientationPatient vectors\n        const float imageOriPatient[] = {1., 0, 0, 0, 1., 0};\n        snprintf(buf, BUF_LEN, imOriPatientFmt, \n                imageOriPatient[0], \n                imageOriPatient[1],\n                imageOriPatient[2],\n                imageOriPatient[3],\n                imageOriPatient[4],\n                imageOriPatient[5]);\n        status = dataset->putAndInsertString(DCM_ImageOrientationPatient, buf);\n        if (status.bad()) {\n                SIFT3D_ERR(\"write_dcm_cpp: Failed to set the \"\n                        \"ImageOrientationPatient vectors \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Set the slice location\n        snprintf(buf, BUF_LEN, \"%f\", imPosZ);\n        status = dataset->putAndInsertString(DCM_SliceLocation, buf);\n        if (status.bad()) {\n                SIFT3D_ERR(\"write_dcm_cpp: Failed to set the slice \"\n                        \"location \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Set the pixel spacing\n        snprintf(buf, BUF_LEN, pixelSpacingFmt, im->ux, im->uy);\n        status = dataset->putAndInsertString(DCM_PixelSpacing, buf);\n        if (status.bad()) {\n                SIFT3D_ERR(\"write_dcm_cpp: Failed to set the pixel \"\n                        \"spacing \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Set the aspect ratio\n        snprintf(buf, BUF_LEN, \"%f\\\\%f\", im->ux, im->uy);\n        status = dataset->putAndInsertString(DCM_PixelAspectRatio, buf);\n        if (status.bad()) {\n                SIFT3D_ERR(\"write_dcm_cpp: Failed to set the pixel aspect \"\n                        \"aspect ratio \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Set the slice thickness\n        snprintf(buf, BUF_LEN, \"%f\", im->uz);\n        status = dataset->putAndInsertString(DCM_SliceThickness, buf);\n        if (status.bad()) {\n                SIFT3D_ERR(\"write_dcm_cpp: Failed to set the slice \"\n                                \"thickness \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Count the number of pixels in the image\n        unsigned long numPixels = SIFT3D_IM_GET_DIMS(im)[0];\n        for (int i = 1; i < IM_NDIMS; i++) {\n                numPixels *= SIFT3D_IM_GET_DIMS(im)[i];\n        }\n\n        // Get the image scaling factor\n        const float dcm_max_val = static_cast<float>(1 << dcm_bit_width) - 1.0f;\n        const float im_max = max_val < 0.0f ? im_max_abs(im) : max_val;\n        const float scale = im_max == 0.0f ? 1.0f : dcm_max_val / im_max;\n\n        // Render the data to an 8-bit unsigned integer array\n        assert(dcm_bit_width == 8);\n        assert(fabsf(dcm_max_val - 255.0f) < FLT_EPSILON);\n        uint8_t *pixelData = new uint8_t[numPixels];\n        int x, y, z, c;\n        SIFT3D_IM_LOOP_START_C(im, x, y, z, c)\n\n                const float vox = SIFT3D_IM_GET_VOX(im, x, y, z, c);\n\n                if (vox < 0.0f) {\n                        SIFT3D_ERR(\"write_dcm_cpp: Image cannot be \"\n                                \"negative \\n\");\n                        return SIFT3D_FAILURE;\n                }\n\n                pixelData[c + x + y * im->nx + z * im->nx * im->ny] =\n                        static_cast<uint8_t>(vox * scale);\n        SIFT3D_IM_LOOP_END_C\n\n        // Write the data\n        status = dataset->putAndInsertUint8Array(DCM_PixelData, pixelData, \n                numPixels);\n        delete[] pixelData;\n        if (status.bad()) {\n                SIFT3D_ERR(\"write_dcm_cpp: failed to set the pixel data \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Choose the encoding format\n#if 0\n        DJEncoderRegistration::registerCodecs();\n        const E_TransferSyntax xfer = EXS_JPEGProcess14SV1TransferSyntax;\n        DJ_RPLossless rp_lossless;\n        status = dataset->chooseRepresentation(xfer, &rp_lossless);\n#else\n        const E_TransferSyntax xfer = EXS_LittleEndianExplicit;\n        dataset->chooseRepresentation(xfer, NULL);\n#endif\n        if (!dataset->canWriteXfer(xfer)) {\n                SIFT3D_ERR(\"write_dcm_cpp: Failed to choose the encoding \"\n                        \"format \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Force the media storage UIDs to be re-generated by removing them\n        dataset->remove(DCM_MediaStorageSOPClassUID);\n        dataset->remove(DCM_MediaStorageSOPInstanceUID);\n\n        // Save the file\n        status = fileFormat.saveFile(path, xfer);\n        if (status.bad()) {\n                SIFT3D_ERR(\"write_dcm_cpp: failed to write file %s (%s) \\n\",\n                        path, status.text());\n                return SIFT3D_FAILURE;\n        }\n\n        return SIFT3D_SUCCESS;\n#undef BUF_LEN\n}\n\n/* Helper function to write an image to a directory of DICOM files using C++ */\nstatic int write_dcm_dir_cpp(const char *path, const Image *const im,\n        const Dcm_meta *const meta) {\n\n        Image slice;\n\n        // Initialize C intermediates\n        init_im(&slice);\n\n        // Initialize the metadata to defaults, if it is null \n        Dcm_meta meta_new;\n        set_meta_defaults(meta, &meta_new);\n\n        // Get the number of leading zeros for the file names\n        const int num_slices = im->nz;\n        const int num_zeros = static_cast<int>(ceil(log10(\n                static_cast<double>(num_slices))));\n\n        // Form the printf format string for file names\n#define BUF_LEN 16\n        char format[BUF_LEN];\n        snprintf(format, BUF_LEN, \"%%0%dd.%s\", num_zeros, ext_dcm); \n#undef BUF_LEN\n\n        // Resize the slice buffer\n        slice.nx = im->nx; \n        slice.ny = im->ny;\n        slice.nz = 1;\n        slice.nc = im->nc;\n        im_default_stride(&slice);\n        if (im_resize(&slice)) {\n                im_free(&slice);\n                return SIFT3D_FAILURE;\n        }\n\n        // Copy the units to the slice\n        memcpy(SIFT3D_IM_GET_UNITS(&slice), SIFT3D_IM_GET_UNITS(im), \n                IM_NDIMS * sizeof(double));\n\n        // Get the maximum absolute value of the whole image volume\n        const float max_val = im_max_abs(im);\n\n        // Write each slice\n        for (int i = 0; i < num_slices; i++) {\n\n                // Form the slice file name\n#define BUF_LEN 1024\n                char buf[BUF_LEN];\n                snprintf(buf, BUF_LEN, format, i);\n\n                // Form the full file path\n                std::string fullfile(path + sepStr + buf);\n\n                // Copy the data to the slice\n                int x, y, z, c;\n                SIFT3D_IM_LOOP_START_C(&slice, x, y, z, c)\n                        SIFT3D_IM_GET_VOX(&slice, x, y, z, c) =\n                                SIFT3D_IM_GET_VOX(im, x, y, i, c);\n                SIFT3D_IM_LOOP_END_C\n\n                // Generate a new SOPInstanceUID\n                dcmGenerateUniqueIdentifier(meta_new.instance_uid, \n                        SITE_INSTANCE_UID_ROOT); \n\n                // Set the instance number\n                const unsigned int instance = static_cast<unsigned int>(i + 1);\n                meta_new.instance_num = instance;\n\n                // Write the slice to a file\n                if (write_dcm(fullfile.c_str(), &slice, &meta_new, max_val)) {\n                        im_free(&slice);\n                        return SIFT3D_FAILURE;\n                }\n        }\n\n        // Clean up\n        im_free (&slice);\n\n        return SIFT3D_SUCCESS;\n}\n\n#endif\n"
  },
  {
    "path": "imutil/dicom.h",
    "content": "/* -----------------------------------------------------------------------------\n * dicom.h \n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * Internal header file for the DCMTK wrapper.\n * -----------------------------------------------------------------------------\n */\n\n#ifndef _DICOM_H\n#define _DICOM_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* Length of UID buffers */\n#define SIFT3D_UID_LEN 1024 \n\n/* Dicom file extension */\nconst char ext_dcm[] = \"dcm\";\n\n/* Internal struct to hold limited Dicom metadata */\ntypedef struct _Dcm_meta {\n        const char *patient_name; // Patient name\n        const char *patient_id; // Patient ID\n        const char *series_descrip; // Series description\n        char study_uid[SIFT3D_UID_LEN]; // Study Instance UID\n        char series_uid[SIFT3D_UID_LEN]; // Series UID\n        char instance_uid[SIFT3D_UID_LEN]; // SOP Instance UID\n        int instance_num; // Instance number\n} Dcm_meta;\n\nint read_dcm(const char *path, Image *const im);\n\nint read_dcm_dir(const char *path, Image *const im);\n\nint write_dcm(const char *path, const Image *const im, \n        const Dcm_meta *const meta, const float max_val);\n\nint write_dcm_dir(const char *path, const Image *const im, \n        const Dcm_meta *const meta);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "imutil/immacros.h",
    "content": "/* -----------------------------------------------------------------------------\n * immacros.h\n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * This header defines preprocessor macros for the imutil library.\n * -----------------------------------------------------------------------------\n */\n\n#include \"imtypes.h\"\n\n#ifdef SIFT3D_MEX\n#include <uchar.h>\n#include \"mex.h\"\n#else\n#include \"stdio.h\"\n#endif\n\n#ifndef _IMMACROS_H\n#define _IMMACROS_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n// Print an error message\n#ifdef SIFT3D_MEX\n#define SIFT3D_ERR(...) \\\n        mexWarnMsgIdAndTxt(\"sift3d:internal\", __VA_ARGS__)\n#else\n#define SIFT3D_ERR(...) fprintf(stderr, __VA_ARGS__)\n#endif\n\n// Math macros\n#define SIFT3D_MIN(x, y) ((x) < (y) ? (x) : (y))\n#define SIFT3D_MAX(x, y) ((x) > (y) ? (x) : (y))\n#define SIFT3D_AZ_MAX_F (2 * (float) M_PI) // Maximum azimuth\n#define SIFT3D_PO_MAX_F ((float) M_PI) // Maximum polar angle\n\n// Compiler flags\n#ifdef __GNUC__\n#define SIFT3D_IGNORE_UNUSED __attribute__((unused))\n#endif\n\n// Get a pointer to the [nx, ny, nz] array of an image\n#define SIFT3D_IM_GET_DIMS(im) \\\n        (&(im)->nx)\n\n// Get a pointer to the [xs, ys, zs] array of an image\n#define SIFT3D_IM_GET_STRIDES(im) \\\n        (&(im)->xs)\n\n// Get a pointer to the [ux, uy, uz] array of an image\n#define SIFT3D_IM_GET_UNITS(im) \\\n        (&(im)->ux)\n\n// Get the index of an [x,y,z] pair in an image \n#define SIFT3D_IM_GET_IDX(im, x, y, z, c) ((size_t) (x) * (im)->xs + \\\n        (size_t) (y) * (im)->ys + (size_t) (z) * (im)->zs + (size_t) (c))\n\n// Get the value of voxel [x,y,z] in an image \n#define SIFT3D_IM_GET_VOX(im, x, y, z, c) ((im)->data[ \\\n        SIFT3D_IM_GET_IDX((im), (x), (y), (z), (c))])\n\n// Loop through an image in x, z, y order. Delmit with SIFT3D_IM_LOOP_END\n#define SIFT3D_IM_LOOP_START(im, x, y, z) \\\n\tfor (z = 0; (z) < (im)->nz; (z)++) {\t\\\n\tfor ((y) = 0; (y) < (im)->ny; (y)++) {\t\\\n\tfor ((x) = 0; (x) < (im)->nx; (x)++) {\n\n/* As in SIFT3D_IM_LOOP_START, but also loop through each channel */\n#define SIFT3D_IM_LOOP_START_C(im, x, y, z, c) \\\n        SIFT3D_IM_LOOP_START(im, x, y, z) \\\n        for ((c) = 0; (c) < (im)->nc; (c)++) {\n\n/* Loop through an image iterating with the (inclusive) x, y, z bounds given.\n * Delimit with SIFT3D_IM_LOOP_END. */\n#define SIFT3D_IM_LOOP_LIMITED_START(im, x, y, z, x_start, x_end, \\\n\t\t\t      y_start, y_end, z_start, z_end) \\\n\tfor (z = z_start; (z) <= z_end; (z)++) { \\\n\tfor ((y) = y_start; (y) <= y_end; (y)++) { \\\n\tfor ((x) = x_start; (x) <= x_end; (x)++) {\t\t\n\n/* As in SIFT3D_IM_LOOP_LIMITED_START, but also loop through each channel */\n#define SIFT3D_IM_LOOP_LIMITED_START_C(im, x, y, z, c, x_start, x_end, \\\n\t\t\t      y_start, y_end, z_start, z_end) \\\n        SIFT3D_IM_LOOP_LIMITED_START(im, x, y, z, x_start, x_end, \\\n\t\t\t      y_start, y_end, z_start, z_end) \\\n        for ((c) = 0; (c) < (im)->nc; (c)++) {\n                                \n\n// Delimit an SIFT3D_IM_LOOP_START or SIFT3D_IM_LOOP_LIMITED_START\n#define SIFT3D_IM_LOOP_END }}}\n\n// Delimited an SIFT3D_IM_LOOP_START_C or SIFT3D_IM_LOOP_LIMITED_START_C\n#define SIFT3D_IM_LOOP_END_C SIFT3D_IM_LOOP_END }\n\n/* Check if a point is within the boundaries of an image */\n#define IM_CONTAINS(im, x, y, z) \\\n        ((x) >= 0 && (y) >= 0 && (z) >= 0 && (x) < (im)->nx && \\\n         (y) < (im)->ny && (z) < (im)->nz)\n\n/* Take the Cartesian gradient of an image at [x, y, z, c]. The voxel cannot be\n * on the boundary. */\n#define SIFT3D_IM_GET_GRAD(im, x, y, z, c, vd) \\\n\t\t(vd)->x = 0.5f * (SIFT3D_IM_GET_VOX(im, (x) + 1, y, z, c) - \\\n\t\t\t   SIFT3D_IM_GET_VOX(im, (x) - 1, y, z, c)); \\\n\t\t(vd)->y = 0.5f * (SIFT3D_IM_GET_VOX(im, x, (y) + 1, z, c) - \\\n\t\t\t   SIFT3D_IM_GET_VOX(im, x, (y) - 1, z, c)); \\\n\t\t(vd)->z = 0.5f * (SIFT3D_IM_GET_VOX(im, x, y, (z) + 1, c) - \\\n\t\t\t   SIFT3D_IM_GET_VOX(im, x, y, (z) - 1, c))\n\n/* Get the Hessian of an image at [x, y, z]. The voxel cannot be on the \n * boundary. */\n#define SIFT3D_IM_GET_HESSIAN(im, x, y, z, c, H, type) \\\n   /* Dxx */ \\\n    SIFT3D_MAT_RM_GET(H, 0, 0, type) = (type) (0.25f * \\\n                                (SIFT3D_IM_GET_VOX(im, x + 1, y, z, c) - \\\n\t\t\t\t 2 * SIFT3D_IM_GET_VOX(im, x, y, z, c) + \\\n\t\t\t\t SIFT3D_IM_GET_VOX(im, x - 1, y, z, c))); \\\n    /* Dxy */ \\\n    SIFT3D_MAT_RM_GET(H, 0, 1, type) = (type) (0.25f * \\\\\n                                (SIFT3D_IM_GET_VOX(im, x + 1, y + 1, z, c) - \\\n\t\t\t\t SIFT3D_IM_GET_VOX(im, x - 1, y + 1, z, c) + \\\n\t\t\t\t SIFT3D_IM_GET_VOX(im, x - 1, y - 1, z, c) - \\\n\t\t\t\t SIFT3D_IM_GET_VOX(im, x + 1, y - 1, z, c))); \\\n    /* Dxz */ \\\n    SIFT3D_MAT_RM_GET(H, 0, 2, type) = (type) (0.25f * \\\n                                (SIFT3D_IM_GET_VOX(im, x + 1, y, z + 1, c) - \\\n\t\t\t\t SIFT3D_IM_GET_VOX(im, x - 1, y, z + 1, c) + \\\n\t\t\t\t SIFT3D_IM_GET_VOX(im, x - 1, y, z - 1, c) - \\\n\t\t\t\t SIFT3D_IM_GET_VOX(im, x + 1, y, z - 1, c))); \\\n    /* Dyx */ \\\n    SIFT3D_MAT_RM_GET(H, 1, 0, type) = SIFT3D_MAT_RM_GET(H, 0, 1, type); \\\n    /* Dyy */ \\\n    SIFT3D_MAT_RM_GET(H, 1, 1, type) = (type) (0.25f * \\\n                                (SIFT3D_IM_GET_VOX(im, x, y + 1, z, c) - \\\n\t\t\t\t 2 * SIFT3D_IM_GET_VOX(im, x, y, z, c) + \\\n\t\t\t\t SIFT3D_IM_GET_VOX(im, x, y - 1, z, c))); \\\n    /* Dyz */ \\\n    SIFT3D_MAT_RM_GET(H, 1, 2, type) = (type) (0.25f * \\\n                                (SIFT3D_IM_GET_VOX(im, x, y + 1, z + 1, c) - \\\n\t\t\t\t SIFT3D_IM_GET_VOX(im, x, y - 1, z + 1, c) + \\\n\t\t\t\t SIFT3D_IM_GET_VOX(im, x, y - 1, z - 1, c) - \\\n\t\t\t\t SIFT3D_IM_GET_VOX(im, x, y + 1, z - 1, c))); \\\n    /* Dzx */ \\\n    SIFT3D_MAT_RM_GET(H, 2, 0, type) = SIFT3D_MAT_RM_GET(H, 0, 2, type); \\\n    /* Dzy */ \\\n    SIFT3D_MAT_RM_GET(H, 2, 1, type) = SIFT3D_MAT_RM_GET(H, 1, 2, type); \\\n    /* Dzz */ \\\n    SIFT3D_MAT_RM_GET(H, 2, 2, type) = (type) (0.25f * \\\n                                (SIFT3D_IM_GET_VOX(im, x, y, z + 1, c) - \\\n\t\t\t\t 2 * SIFT3D_IM_GET_VOX(im, x, y, z, c) + \\\n\t\t\t\t SIFT3D_IM_GET_VOX(im, x, y, z - 1, c)))\n\n// Get a pointer to an image struct at pyramid level [o, s]\n#define SIFT3D_PYR_IM_GET(pyr, o, s) ((pyr)->levels + \\\n\t\t\t\t\t\t((o) - (pyr)->first_octave) * \\\n\t\t\t\t\t\t(pyr)->num_levels + ((s) - (pyr)->first_level))\n\n// Get the index of the last octave of a Pyramid struct\n#define SIFT3D_PYR_LAST_OCTAVE(pyr) \\\n        ((pyr)->first_octave + (pyr)->num_octaves - 1)\n\n// Get the index of the last level of a Pyramid struct\n#define SIFT3D_PYR_LAST_LEVEL(pyr) \\\n        ((pyr)->first_level + (pyr)->num_levels - 1)\n\n// Loop through all levels of a given pyramid\n#define SIFT3D_PYR_LOOP_START(pyr, o, s) \\\n\tfor ((o) = (pyr)->first_octave; (o) <= SIFT3D_PYR_LAST_OCTAVE(pyr); \\\n                (o)++) { \\\n\tfor ((s) = (pyr)->first_level; (s) <= SIFT3D_PYR_LAST_LEVEL(pyr); \\\n                (s)++) {\n\n// Loop from the specified (inclusive) limits of a given pyramid\n#define SIFT3D_PYR_LOOP_LIMITED_START(o, s, o_start, o_end, s_start, s_end) \\\n\tfor ((o) = (o_start); (o) <= (o_end); (o)++) {\t\\\n\tfor ((s) = (s_start); (s) <= (s_end); (s)++) {\n\n// Delimit a SIFT3D_PYR_LOOP\n#define SIFT3D_PYR_LOOP_END }}\n\n// Delimit the first level of a SIFT3D_PYR_LOOP\n#define SIFT3D_PYR_LOOP_SCALE_END }\n\n// Delimit the second level of a PYR_LOOP\n#define SIFT3D_PYR_LOOP_OCTAVE_END }\n\n// Get a pointer to the incremental Gaussian filter for level s\n#define SIFT3D_GAUSS_GET(gss, s) \\\n\t((gss)->gauss_octave + (s - (gss)->first_level))\n\n/* Resize a slab. If SIFT3D_SLAB_SIZE is defined, add\n* elements in increments of that number. Otherwise,\n* use a default of 500. This macro is meant to be\n* used whether or not the slab buffer actually needs\n* resizing -- it checks for that. */\n#ifndef SIFT3D_SLAB_LEN\n#define SIFT3D_SLAB_LEN 500\n#endif\n#define SIFT3D_RESIZE_SLAB(slab, num_new, size) \\\n{ \\\n        const size_t slabs_new = ( (size_t) (num_new) + SIFT3D_SLAB_LEN - 1) / \\\n                        SIFT3D_SLAB_LEN; \\\n        const size_t size_new = slabs_new * SIFT3D_SLAB_LEN * (size); \\\n\\\n\tif (size_new != (slab)->buf_size) { \\\n\\\n\t\t/* Re-initialize if the new size is 0 */ \\\n\t\tif (size_new == 0) { \\\n\t\t\tcleanup_Slab(slab); \\\n\t\t\tinit_Slab(slab); \\\n\t\t/* Else allocate new memory */ \\\n\t\t} else if (((slab)->buf = SIFT3D_safe_realloc((slab)->buf, \\\n\t\t\tsize_new)) == NULL) { \\\n\t\t\treturn SIFT3D_FAILURE; \\\n\t\t} \\\n\t\t(slab)->buf_size = size_new; \\\n\t} \\\n\t(slab)->num = (num_new); \\\n}\n\n// Nested loop through all elements of a matrix\n#define SIFT3D_MAT_RM_LOOP_START(mat, row, col) \\\n\tfor ((row) = 0; (row) < (mat)->num_rows; (row)++) { \\\n\tfor ((col) = 0; (col) < (mat)->num_cols; (col)++) {\n\n// Delmit a MAT_LOOP\n#define SIFT3D_MAT_RM_LOOP_END }}\n\n// Delimit the first level of a MAT_LOOP\n#define SIFT3D_MAT_RM_LOOP_COL_END }\n\n// Delmit the second level of a MAT_LOOP\n#define SIFT3D_MAT_RM_LOOP_ROW_END } \n\n// Get the index of an element in a dense matrix, in row-major order.\n#define SIFT3D_MAT_RM_GET_IDX(mat, row, col) \\\n        ((col) + (row) * (mat)->num_cols)\n\n// Get an element from a dense matrix in row-major order. Type must\n// be \"double\", \"float\", or \"int.\"\n#define SIFT3D_MAT_RM_GET(mat, row, col, type) ((mat)->u.data_ ## type \\\n\t[SIFT3D_MAT_RM_GET_IDX(mat, row, col)])\n\n/* Execute the macro MACRO, with the first argument set to the type of mat. If\n * there is an error, goto err_label. */\n#define SIFT3D_MAT_RM_TYPE_MACRO(mat, err_label, MACRO, ...) \\\n        switch ((mat)->type) { \\\n                case SIFT3D_DOUBLE: \\\n                        MACRO(double, ## __VA_ARGS__) \\\n                        break; \\\n                case SIFT3D_FLOAT: \\\n                        MACRO(float, ## __VA_ARGS__) \\\n                        break; \\\n                case SIFT3D_INT: \\\n                        MACRO(int, ## __VA_ARGS__) \\\n                        break; \\\n                default: \\\n                        SIFT3D_ERR(\"imutil: unknown matrix type \\n\"); \\\n                        goto err_label; \\\n        } \\\n\n// Convert a vector from Cartesian to Spherical coordinates.\n#define SIFT3D_CVEC_TO_SVEC(cvec, svec) { \\\n\t(svec)->mag = sqrtf((cvec)->x * (cvec)->x + (cvec)->y * (cvec)->y + \\\n\t\t\t\t\t\t(cvec)->z * (cvec)->z); \\\n\t(svec)->az = fmodf(atan2f((cvec)->y, (cvec)->x) + SIFT3D_AZ_MAX_F, \\\n\t\t\t\t\t  SIFT3D_AZ_MAX_F); \\\n\t(svec)->po = fmodf(acosf((cvec)->z / ((svec)->mag + FLT_EPSILON)), \\\n\t\t     SIFT3D_PO_MAX_F); \\\n}\n\n// Convert a vector from Spherical to Cartesian coordinates\n#define SIFT3D_SVEC_TO_CVEC(svec, cvec) { \\\n\t(cvec)->x = (svec)->mag * sinf((svec)->po) * cosf((svec)->az); \\\n\t(cvec)->y = (svec)->mag * sinf((svec)->po) * sinf((svec)->az); \\\n\t(cvec)->z = (svec)->mag * cosf((svec)->po); \\\n}\n\n// Return the L2 norm of a Cartesian coordinate vector\n#define SIFT3D_CVEC_L2_NORM(cvec) \\\n\tsqrtf((cvec)->x * (cvec)->x + (cvec)->y * (cvec)->y + \\\n\t(cvec)->z * (cvec)->z)\n\n// Return the square of the  L2 norm of a Cartesian coordinate vector\n#define SIFT3D_CVEC_L2_NORM_SQ(cvec) \\\n\t((cvec)->x * (cvec)->x + (cvec)->y * (cvec)->y + \\\n\t(cvec)->z * (cvec)->z)\n\n// Scale a Cartesian coordinate vector by a constant factor\n#define SIFT3D_CVEC_SCALE(cvec, a) { \\\n    (cvec)->x = (cvec)->x * a; \\\n    (cvec)->y = (cvec)->y * a; \\\n    (cvec)->z = (cvec)->z * a; \\\n}\n\n// Operate element-wise on two Cartesian coordinate vectors, cc = ca op cb\n#define SIFT3D_CVEC_OP(ca, cb, op, cc) { \\\n    (cc)->x = (ca)->x op (cb)->x; \\\n    (cc)->y = (ca)->y op (cb)->y; \\\n    (cc)->z = (ca)->z op (cb)->z; \\\n}\n\n// Return the dot product of two Cartesian coordinate \n// vectors\n#define SIFT3D_CVEC_DOT(in1, in2) \\\n\t((in1)->x * (in2)->x + (in1)->y * (in2)->y + (in1)->z * (in2)->z)\n\n// Take the cross product of two Cartesian coordinate\n// vectors, as out = in1 X in2\n#define SIFT3D_CVEC_CROSS(in1, in2, out) { \\\n\t(out)->x = (in1)->y * (in2)->z - (in1)->z * (in2)->y; \\\n\t(out)->y = (in1)->z * (in2)->x - (in1)->x * (in2)->z; \\\n\t(out)->z = (in1)->x * (in2)->y - (in1)->y * (in2)->x; \\\n} \n\n// Evaluates to true (nonzero) if im contains cvec, false otherwise\n#define SIFT3D_IM_CONTAINS_CVEC(im, cvec) ( \\\n        (cvec)->x >= 0 || (cvec)->y >= 0 || (cvec)->z >= 0 || \\\n        (cvec)->x < (float) (im)->nx || \\\n        (cvec)->y < (float) (im)->ny || \\\n        (cvec)->z < (float) (im)->nz \\\n)\n\n/* Computes v_out = mat * v_in. Note that mat must be of FLOAT\n * type, since this is the only type available for vectors. \n * Also note that mat must be (3 x 3). */\n#define SIFT3D_MUL_MAT_RM_CVEC(mat, v_in, v_out) { \\\n\t(v_out)->x = SIFT3D_MAT_RM_GET(mat, 0, 0, float) * (v_in)->x + \\\n\t    \t     SIFT3D_MAT_RM_GET(mat, 0, 1, float) * (v_in)->y + \\\n                     SIFT3D_MAT_RM_GET(mat, 0, 2, float) * (v_in)->z; \\\n\t\\\n\t(v_out)->y = SIFT3D_MAT_RM_GET(mat, 1, 0, float) * (v_in)->x + \\\n                     SIFT3D_MAT_RM_GET(mat, 1, 1, float) * (v_in)->y + \\\n                     SIFT3D_MAT_RM_GET(mat, 1, 2, float) * (v_in)->z; \\\n\t\\\n\t(v_out)->z = SIFT3D_MAT_RM_GET(mat, 2, 0, float) * (v_in)->x + \\\n                     SIFT3D_MAT_RM_GET(mat, 2, 1, float) * (v_in)->y + \\\n                     SIFT3D_MAT_RM_GET(mat, 2, 2, float) * (v_in)->z; \\\n}\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "imutil/imtypes.h",
    "content": "/* -----------------------------------------------------------------------------\r\n * imtypes.h\r\n * -----------------------------------------------------------------------------\r\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\r\n * -----------------------------------------------------------------------------\r\n * This header contains data type definitions.\r\n * -----------------------------------------------------------------------------\r\n */\r\n\r\n#include <time.h>\r\n\r\n#ifndef _IMTYPES_H\r\n#define _IMTYPES_H\r\n\r\n#ifdef __cplusplus\r\nextern \"C\" {\r\n#endif\r\n\r\n// Return codes\r\n#define SIFT3D_SINGULAR 1\r\n#define SIFT3D_SUCCESS 0\r\n#define SIFT3D_FAILURE -1\r\n#define SIFT3D_HELP 1\r\n#define SIFT3D_VERSION 2\r\n\r\n// Truth values\r\n#define SIFT3D_TRUE 1\r\n#define SIFT3D_FALSE 0\r\n\r\n// Platform types\r\n#if _Win16 == 1 || _WIN32 == 1 || _WIN64 == 1 || \\\r\n\tdefined __WIN32__ || defined __TOS_WIN__ || \\\r\n\tdefined __WINDOWS__ && !defined _WINDOWS\r\n#define _WINDOWS\r\n#endif\r\n#if (defined(__MINGW32__) || defined(__MINGW64__)) && defined(_WINDOWS) && \\\r\n         !defined _MINGW_WINDOWS \r\n#define _MINGW_WINDOWS\r\n#endif\r\n\r\n/* OpenCL type definitions. */\r\n#ifdef USE_OPENCL\r\n#ifdef _WINDOWS\r\n#include \"CL\\cl.h\"\r\n#else\r\n#include \"CL/cl.h\"\r\n#endif\r\n#else\r\n\t/* Use dummy macro definitions */\r\n#define CL_SUCCESS 0\r\n#define CL_FAILURE -1\r\n\r\n\t/* Use dummy type definitions. */\r\n\ttypedef unsigned char cl_uchar;\r\n\ttypedef int cl_device_id;\r\n\ttypedef int cl_command_queue;\r\n\ttypedef int cl_device_type;\r\n\ttypedef int cl_platform_id;\r\n\ttypedef int cl_context;\r\n\ttypedef int cl_image_format;\r\n\ttypedef int cl_mem_flags;\r\n\ttypedef int cl_kernel;\r\n\ttypedef int cl_mem;\r\n\ttypedef int cl_mem_object_type;\r\n\ttypedef int cl_program;\r\n\ttypedef int cl_int;\r\n\ttypedef int cl_bool;\r\n\ttypedef unsigned int cl_uint;\r\n#endif\r\n\r\n/* File separator character */\r\n#ifdef _WINDOWS\r\n#define SIFT3D_FILE_SEP '\\\\'\r\n#else\r\n#define SIFT3D_FILE_SEP '/'\r\n#endif\r\n\r\n/* Parameters */\r\n#define NBINS_AZ 8\t\t// Number of bins for azimuthal angles\r\n#define NBINS_PO 4\t\t// Number of bins for polar angles\r\n#define NHIST_PER_DIM 4 // Number of SIFT descriptor histograms per dimension \r\n#define ICOS_HIST\t\t\t// Icosahedral gradient histogram\r\n\r\n/* Constants */\r\n#define IM_NDIMS 3 // Number of dimensions in an Image\r\n#define ICOS_NFACES 20 // Number of faces in an icosahedron\r\n#define ICOS_NVERT 12 // Number of vertices in an icosahedron\r\n\r\n/* Derived constants */\r\n#define DESC_NUM_TOTAL_HIST (NHIST_PER_DIM * NHIST_PER_DIM * NHIST_PER_DIM)\r\n#define DESC_NUMEL (DESC_NUM_TOTAL_HIST * HIST_NUMEL)\r\n\r\n// The number of elements in a gradient histogram\r\n#ifdef ICOS_HIST\r\n#define HIST_NUMEL (ICOS_NVERT)\r\n#else\r\n#define HIST_NUMEL (NBINS_AZ * NBINS_PO)\r\n#endif\r\n\r\n/* Supported image file formats */\r\ntypedef enum _im_format {\r\n        ANALYZE, /* Analyze */\r\n        DICOM, /* DICOM */\r\n        DIRECTORY, /* Directory */\r\n        NIFTI, /* NIFTI-1 */ \r\n        UNKNOWN, /* Not one of the known extensions */\r\n        FILE_ERROR /* Error occurred in determining the format */\r\n} im_format;\r\n\r\n/* Possible data types for matrix elements */ \r\ntypedef enum _Mat_rm_type {\r\n\tSIFT3D_DOUBLE,\r\n\tSIFT3D_FLOAT,\r\n\tSIFT3D_INT\r\n} Mat_rm_type;\r\n\r\n/* Struct to hold OpenCL programs for this library */\r\ntypedef struct _kernels {\r\n\tcl_kernel downsample_2x_3d;\r\n} Kernels;\r\n\r\n/* Struct to hold OpenCL data about the user system */\r\ntypedef struct _CL_data {\r\n\tcl_device_id *devices;\t  // num_devices elements\r\n\tcl_command_queue *queues; // One per device\r\n\tcl_platform_id platform;\r\n\tcl_context context;\r\n\tcl_uint num_devices;\r\n\tcl_image_format image_format;\r\n\tcl_mem_flags mem_flags;\r\n\tKernels kernels;\r\n\tint valid;\t\t// Is this struct valid?\r\n} CL_data;\r\n\r\n/* Struct to hold a dense matrix in row-major order */\r\ntypedef struct _Mat_rm {\r\n\r\n\tunion {\r\n\t\tdouble *data_double;\r\n\t\tfloat  *data_float;\r\n\t\tint *data_int;\r\n\t} u;\r\n\tsize_t size;\t\t// Size of the buffer, in bytes\r\n\tint num_cols;           // Number of columns \r\n\tint num_rows;           // Number of rows\t\r\n        int static_mem;         // Flag for statically-allocated memory\r\n\tMat_rm_type type;       // DOUBLE, FLOAT, or INT\r\n\r\n} Mat_rm;\r\n\r\n/* Struct to hold image data. The image is a rectangular prism, \r\n * where the bottom-left corner is [0 0 0], the x-stride is 1,\r\n * the y-stride is the width in x, and the z-stride is the\r\n * size of an xy plane. For convenience use the macros IM_GET_IDX, \r\n * IM_GET_VOX, and IM_SET_VOX to manipulate this struct. */\r\ntypedef struct _Image {\r\n\r\n\tfloat *data;\t\t// Raster of voxel values ~16MB\r\n\tcl_mem cl_image;\t// Same-sized OpenCL image object\r\n\tdouble s;\t\t// scale-space location\r\n\tsize_t size;\t\t// Total size in pixels\r\n\tint nx, ny, nz;\t\t// Dimensions in x, y, and z\r\n\tdouble ux, uy, uz;\t// Real world dimensions in x, y, and z\r\n        size_t xs, ys, zs;      // Stride in x, y, and z\r\n        int nc;                 // The number of channels\r\n\tint cl_valid;\t\t// If TRUE, cl_image is valid\r\n\r\n} Image;\r\n\r\n/* Holds separable FIR filters and programs to apply them */\r\ntypedef struct _Sep_FIR_filter {\r\n\r\n\tcl_kernel cl_apply_unrolled;\t// unrolled OpenCL program to apply filter\r\n\tfloat *kernel;\t// filter weights\r\n\tint dim;\t// dimensionality, e.g. 3 for MRI\r\n\tint width;\t// number of weights\t\t\t\t\r\n\tint symmetric;\t// enable symmetric optimizations: FALSE or TRUE\r\n\r\n} Sep_FIR_filter;\r\n\r\n/* Holds Gaussian filters */\r\ntypedef struct _Gauss_filter {\r\n\r\n\tdouble sigma;\r\n\tSep_FIR_filter f;\r\n\r\n} Gauss_filter;\r\n\r\n/* Holds Gaussian Scale-Space filters */\r\ntypedef struct _GSS_filters {\r\n\r\n\tGauss_filter first_gauss;\t// Used on the very first blur\r\n\tGauss_filter *gauss_octave;\t// Array of kernels for one octave\r\n\tint num_filters;\t\t// Number of filters for one octave\r\n\tint first_level;                // Index of the first scale level\r\n\r\n} GSS_filters;\r\n\r\n/* Struct to hold miscellaneous SIFT detector OpenCL kernels */\r\ntypedef struct _SIFT_cl_kernels {\r\n\r\n\tcl_kernel downsample_2;\r\n\r\n} SIFT_cl_kernels;\r\n\r\n/* Struct to hold a scale-space image pyramid */\r\ntypedef struct _Pyramid {\r\n\t\r\n\t// Levels in all octaves\r\n\tImage *levels;\t\r\n\r\n\t// Scale-space parameters\r\n\tdouble sigma_n;\r\n\tdouble sigma0;\r\n\tint num_kp_levels;\r\n\r\n\t// Indexing information -- see immacros.h\r\n\tint first_octave;\r\n\tint num_octaves;\r\n\tint first_level;\r\n\tint num_levels;\r\n\r\n} Pyramid;\r\n\r\n/* Struct defining a vector in spherical coordinates */\r\ntypedef struct _Svec {\r\n\r\n\tfloat mag;\t// Magnitude\r\n\tfloat po;\t// Polar angle, [0, pi)\r\n\tfloat az;\t// Azimuth angle, [0, 2pi)\r\n\r\n} Svec;\r\n\r\n/* Struct defining a vector in Cartesian coordinates */\r\ntypedef struct _Cvec {\r\n\r\n\tfloat x;\r\n\tfloat y;\r\n\tfloat z;\r\n\r\n} Cvec;\r\n\r\n/* Slab allocation struct */\r\ntypedef struct _Slab {\r\n\r\n\tvoid *buf;\t\t\t// Buffer\r\n\tsize_t num;\t\t\t// Number of elements currently in buffer\r\n\tsize_t buf_size;\t        // Buffer capactiy, in bytes\r\n\r\n} Slab;\r\n\r\n/* Struct defining a keypoint in 3D space. */\r\ntypedef struct _Keypoint {\r\n\r\n\tfloat r_data[IM_NDIMS * IM_NDIMS];\t// Memory for matrix R, do not use this\r\n\tMat_rm R;\t\t\t\t// Rotation matrix into Keypoint space\r\n\tdouble xd, yd, zd;\t\t\t// sub-pixel x, y, z\r\n\tdouble  sd;\t\t\t\t// absolute scale\r\n\tint o, s;\t\t\t        // pyramid indices \r\n\r\n} Keypoint;\r\n\r\n/* Struct to hold keypoints */\r\ntypedef struct _Keypoint_store {\r\n\t\r\n\tKeypoint *buf;\r\n\tSlab slab;\r\n\tint nx, ny, nz;\t\t// dimensions of first octave\r\n\r\n} Keypoint_store;\r\n\r\n/* Struct defining an orientation histogram in\r\n * spherical coordinates. */\r\ntypedef struct _Hist {\r\n\tfloat bins[HIST_NUMEL];\r\n} Hist;\r\n\r\n/* Triangle */\r\ntypedef struct _Tri {\r\n\tCvec v[3]; // Vertices\r\n\tint idx[3]; // Index of each vertex in the solid\r\n} Tri;\r\n\r\n/* Triangle mesh */\r\ntypedef struct _Mesh {\r\n\tTri *tri; \t// Triangles\r\n\tint num;\t// Number of triangles\r\n} Mesh;\r\n\r\n/* Struct defining a 3D SIFT descriptor */\r\ntypedef struct _SIFT3D_Descriptor {\r\n\r\n\tHist hists[DESC_NUM_TOTAL_HIST]; // Array of orientation histograms\r\n\tdouble xd, yd, zd, sd;\t// sub-pixel [x, y, z], absolute scale\r\n\r\n} SIFT3D_Descriptor;\r\n\r\n/* Struct to hold SIFT3D descriptors */\r\ntypedef struct _SIFT3D_Descriptor_store {\r\n\r\n\tSIFT3D_Descriptor *buf;\r\n\tsize_t num;\r\n\tint nx, ny, nz;\t\t\t// Image dimensions\r\n\r\n} SIFT3D_Descriptor_store;\r\n\r\n/* Struct to hold all parameters and internal data of the \r\n * SIFT3D algorithms */\r\ntypedef struct _SIFT3D {\r\n\r\n        // Triange mesh\r\n\tMesh mesh;\r\n\r\n        // Filters for computing the GSS pyramid\r\n\tGSS_filters gss;\r\n\r\n\t// Other OpenCL kernels\r\n\tSIFT_cl_kernels kernels;\r\n\r\n\t// Gaussian pyramid\r\n\tPyramid gpyr;\r\n\r\n\t// DoG pyramid\r\n\tPyramid dog;\r\n\r\n\t// Image to process\r\n\tImage im;\r\n\r\n\t// Parameters\r\n\tdouble peak_thresh; // Keypoint peak threshold\r\n\tdouble corner_thresh; // Keypoint corner threshold\r\n        int dense_rotate; // If true, dense descriptors are rotation-invariant\r\n\r\n} SIFT3D;\r\n\r\n/* Geometric transformations that can be applied by this library. */\r\ntypedef enum _tform_type {\r\n\tAFFINE,         // Affine (linear + constant)\r\n\tTPS             // Thin-plate spline\t\r\n} tform_type;\r\n\r\n/* Interpolation algorithms that can be used by this library. */\r\ntypedef enum _interp_type {\r\n        LINEAR,         // N-linear interpolation\r\n        LANCZOS2        // Lanczos kernel, a = 2\r\n} interp_type;\r\n\r\n/* Virtual function table for Tform class */\r\ntypedef struct _Tform_vtable {\r\n\r\n        int (*copy)(const void *const, void *const);\r\n\r\n        void (*apply_xyz)(const void *const, const double, const double, \r\n                const double, double *const, double *const, double *const);\r\n\r\n        int (*apply_Mat_rm)(const void *const, const Mat_rm *const, \r\n                Mat_rm *const);\r\n\r\n        size_t (*get_size)(void);\r\n\r\n        int (*write)(const char *, const void *const);\r\n       \r\n        void (*cleanup)(void *const);\r\n\r\n} Tform_vtable;\r\n\r\n/* \"Abstract class\" of transformations */\r\ntypedef struct _Tform {\r\n        tform_type type; // The specific type, e.g. Affine, TPS\r\n        const Tform_vtable *vtable; // Table of virtual functions\r\n} Tform;\r\n\r\n/* Struct to hold an affine transformation */\r\ntypedef struct _Affine {\r\n        Tform tform;    // Abstract parent class\r\n\tMat_rm A;\t// Transformation matrix, x' = Ax\r\n} Affine;\r\n\r\n/* Struct to hold a thin-plate spline */\r\ntypedef struct _Tps {\r\n        Tform tform;       // Abstract parent class\r\n\tMat_rm params;\t// Transformation matrix, dim * number of control point + dim +1\r\n\tMat_rm kp_src;\t// Control point matrix, number of control point * dim\r\n\tint dim; \t// Dimensionality, e.g. 3\r\n} Tps;\r\n\r\n/* Struct to hold RANSAC parameters */\r\ntypedef struct _Ransac {\r\n \tdouble err_thresh; //error threshold for RANSAC inliers\r\n\tint num_iter; //number of RANSAC iterations\r\n} Ransac;\r\n\r\n#ifdef __cplusplus\r\n}\r\n#endif\r\n\r\n#endif\r\n"
  },
  {
    "path": "imutil/imutil.c",
    "content": "/* -----------------------------------------------------------------------------\n * imutil.c\n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2017 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * Miscellaneous utility routines for image processing, linear algebra, and \n * statistical regression. This library completely defines the Image,\n * Mat_rm, and Ransac types, among others, and stands apart from the other\n * source.\n * -----------------------------------------------------------------------------\n */\n\n#include <assert.h>\n#include <getopt.h>\n#include <errno.h>\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <stddef.h>\n#include <float.h>\n#include <zlib.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include \"immacros.h\"\n#include \"imtypes.h\"\n#include \"dicom.h\"\n#include \"nifti.h\"\n#include \"imutil.h\"\n\n/* Check for a version number */\n#if !defined(SIFT3D_VERSION_NUMBER)\n#error imutil.c: Must define the preprocessor macro SIFT3D_VERSION_NUMBER\n#endif\n\n/* Stringify a macro name */\n#define STR(x) #x\n\n/* Stringify the result of a macro expansion */\n#define XSTR(x) STR(x)\n\n/* zlib definitions */\n#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)\n#include <fcntl.h>\n#include <io.h>\n#define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)\n#else\n#define SET_BINARY_MODE(file)\n#endif\n\n/* Implementation parameters */\n//#define SIFT3D_USE_OPENCL // Use OpenCL acceleration\n#define SIFT3D_RANSAC_REFINE\t// Use least-squares refinement in RANSAC\n\n/* Implement strnlen, if it's missing */\n#ifndef SIFT3D_HAVE_STRNLEN\nsize_t strnlen(const char *string, size_t maxlen) {\n  const char *end = memchr (string, '\\0', maxlen);\n  return end ? end - string : maxlen;\n}\n#endif\n\n/* Implement strndup, if it's missing */\n#ifndef SIFT3D_HAVE_STRNDUP\nchar *strndup(const char *s, size_t n) {\n  size_t len = strnlen (s, n);\n  char *new = malloc (len + 1);\n  if (new == NULL)\n    return NULL;\n  new[len] = '\\0';\n  return memcpy (new, s, len);\n}\n#endif\n\n/* SIFT3D version message */\nconst char version_msg[] =\n    \"SIFT3D version \" XSTR(SIFT3D_VERSION_NUMBER)  \" \\n\"\n    \"\\n\"\n    \"Source code available at https://github.com/bbrister/SIFT3D\\n\"\n    \"\\n\"\n    \"Please contact Blaine Rister (blaine@stanford.edu) with questions or \"\n    \"concerns. \\n\";\n\n/* Bug message */\nconst char bug_msg[] =\n    \"SIFT3D has encountered an unexpected error. We would appreciate it \\n\"\n    \"if you would report this issue at the following page: \\n\"\n    \"       https://github.com/bbrister/SIFT3D/issues \\n\";\n\n/* Supported file extensions */\nextern const char ext_dcm[]; // dicom.h\nconst char ext_analyze[] = \"img\";;\nconst char ext_gz[] = \"gz\";\nconst char ext_nii[] = \"nii\";\nconst char ext_dir[] = \"\";\n\n/* Output file permissions */\nconst mode_t out_mode = 0755;\n\n/* Default parameters */\nconst double SIFT3D_err_thresh_default = 5.0;\nconst int SIFT3D_num_iter_default = 500;\n\n/* Declarations for the virtual function implementations */\nstatic int copy_Affine(const void *const src, void *const dst);\nstatic int copy_Tps(const void *const src, void *const dst);\nstatic void apply_Affine_xyz(const void *const affine, const double x_in,\n\t\t\t     const double y_in, const double z_in,\n\t\t\t     double *const x_out, double *const y_out,\n\t\t\t     double *const z_out);\nstatic void apply_Tps_xyz(const void *const tps, const double x_in, \n                          const double y_in, const double z_in, \n                          double *const x_out, double *const y_out, \n                          double *const z_out);\nstatic int apply_Affine_Mat_rm(const void *const affine, \n        const Mat_rm * const mat_in, Mat_rm * const mat_out);\nstatic int apply_Tps_Mat_rm(const void *const tps, const Mat_rm * const mat_in,\n\t\t\t    Mat_rm * const mat_out);\nstatic size_t Affine_get_size(void);\nstatic size_t Tps_get_size(void);\nstatic int write_Affine(const char *path, const void *const tform);\nstatic int write_Tps(const char *path, const void *const tform);\nstatic void cleanup_Affine(void *const affine);\nstatic void cleanup_Tps(void *const tps);\nstatic int mkpath(const char *path, mode_t mode);\n\n/* Virtual function tables */\nconst Tform_vtable Affine_vtable = {\n\tcopy_Affine,\n\tapply_Affine_xyz,\n\tapply_Affine_Mat_rm,\n\tAffine_get_size,\n\twrite_Affine,\n\tcleanup_Affine\n};\n\nconst Tform_vtable Tps_vtable = {\n\tcopy_Tps,\n\tapply_Tps_xyz,\n\tapply_Tps_Mat_rm,\n\tTps_get_size,\n\twrite_Tps,\n\tcleanup_Tps\n};\n\n/* Internal macros */\n#define TFORM_GET_VTABLE(arg) (((Affine *) arg)->tform.vtable)\n#define AFFINE_GET_DIM(affine) ((affine)->A.num_rows)\n\n/* Global data */\nCL_data cl_data;\n\n/* LAPACK declarations */\n#ifdef SIFT3D_MEX\n// Set the integer width to Matlab's defined width\n#include <uchar.h>\n#include \"mex.h\"\ntypedef mwSignedIndex fortran_int;\n#ifdef _WINDOWS\n// Remove underscores from FORTRAN functions\n#define dlange_ dlange\n#define dgecon_ dgecon\n#define dgelss_ dgelss\n#define dgetrf_ dgetrf\n#define dgetrs_ dgetrs\n#define dsyevd_ dsyevd\n#endif\n#else\ntypedef int32_t fortran_int;\n#endif\nextern double dlange_(const char *, const fortran_int *, const fortran_int *,\n\t\t      const double *, const fortran_int *, double *);\nextern void dgecon_(const char *, const fortran_int *, double *,\n\t\t    const fortran_int *, const double *, double *,\n\t\t    double *, fortran_int *, fortran_int *);\n\nextern void dgelss_(const fortran_int *, const fortran_int *,\n\t\t    const fortran_int *, const double *, const fortran_int *,\n\t\t    double *, const fortran_int *, double *, const double *,\n\t\t    fortran_int *, double *, const fortran_int *,\n\t\t    fortran_int *);\n\nextern void dgetrf_(const fortran_int *, const fortran_int *, double *,\n\t\t    const fortran_int *, fortran_int *, fortran_int *);\n\nextern void dgetrs_(const char *, const fortran_int *, const fortran_int *,\n\t\t    const double *, const fortran_int *, fortran_int *,\n\t\t    double *, const fortran_int *, fortran_int *);\n\nextern void dsyevd_(const char *, const char *, const fortran_int *, double *,\n\t\t    const fortran_int *, double *, double *,\n\t\t    const fortran_int *, fortran_int *, const fortran_int *,\n\t\t    fortran_int *);\n\n/* Internal helper routines */\nstatic char *read_file(const char *path);\nstatic int do_mkdir(const char *path, mode_t mode);\nstatic int cross_mkdir(const char *path, mode_t mode);\nstatic double resample_linear(const Image * const in, const double x,\n\t\t\t      const double y, const double z, const int c);\nstatic double resample_lanczos2(const Image * const in, const double x,\n\t\t\t\tconst double y, const double z, const int c);\nstatic double lanczos(double x, double a);\nstatic int check_cl_image_support(cl_context context, cl_mem_flags mem_flags,\n\t\t\t\t  cl_image_format image_format,\n\t\t\t\t  cl_mem_object_type image_type);\nstatic int compile_cl_program_from_source(cl_program * program,\n\t\t\t\t\t  cl_context context,\n\t\t\t\t\t  cl_device_id * devices,\n\t\t\t\t\t  int num_devices, char **src,\n\t\t\t\t\t  int num_str);\nstatic int n_choose_k(const int n, const int k, int **ret);\nstatic int make_spline_matrix(Mat_rm * src, Mat_rm * src_in, Mat_rm * sp_src,\n\t\t\t      int K_terms, int *r, int dim);\nstatic int make_affine_matrix(const Mat_rm *const pts_in, const int dim, \n        Mat_rm *const mat_out);\nstatic Mat_rm *extract_ctrl_pts(void *tform, tform_type type);\nstatic Mat_rm *extract_ctrl_pts_Tps(Tps * tps);\nstatic int solve_system(const Mat_rm *const src, const Mat_rm *const ref, \n        void *const tform);\nstatic double tform_err_sq(const void *const tform, const Mat_rm *const src, \n        const Mat_rm *const ref, const int i);\nstatic int ransac(const Mat_rm *const src, const Mat_rm *const ref, \n        const Ransac *const ran, void *tform, int **const cset, int *const len);\nstatic int convolve_sep(const Image * const src,\n\t\t\tImage * const dst, const Sep_FIR_filter * const f,\n\t\t\tconst int dim, const double unit);\nstatic int convolve_sep_gen(const Image * const src,\n\t\t\tImage * const dst, const Sep_FIR_filter * const f,\n\t\t\tconst int dim, const double unit);\nstatic int convolve_sep_cl(const Image * const src,\n\t\t\tImage * const dst, const Sep_FIR_filter * const f,\n\t\t\tint dim, const double unit);\nstatic int convolve_sep_sym(const Image * const src, Image * const dst,\n\t\t\t    const Sep_FIR_filter * const f, const int dim,\n                            const double unit);\nstatic const char *get_file_name(const char *path);\nstatic const char *get_file_ext(const char *name);\n\n/* Unfinished public routines */\nint init_Tps(Tps * tps, int dim, int terms);\nint resize_Tps(Tps * tps, int num_pts, int dim);\n\n/* As realloc, but frees the underlying pointer and returns NULL on error, or\n * if size is 0 and ptr is non-NULL. */\nvoid *SIFT3D_safe_realloc(void *ptr, size_t size) {\n\n\tvoid *ret;\n\n\t// Call realloc and handle failures\n\tif (size == 0 || (ret = realloc(ptr, size)) == NULL) {\n\t\tif (ptr != NULL) {\n\t\t\tfree(ptr);\n\t\t}\n\t\treturn NULL;\n\t}\n\n\treturn ret;\n\n}\n\n/* Finish all OpenCL command queues. */\nvoid clFinish_all()\n{\n#ifdef SIFT3D_USE_OPENCL\n\tint i;\n\tfor (i = 0; i < cl_data.num_devices; i++) {\n\t\tclFinish(cl_data.queues[i]);\n\t}\n#endif\n}\n\n/* Check the error code and exit on error, unless NDEBUG is\n * defined. If exiting, prints the error type and the cause,\n * given by the msg string. */\nvoid check_cl_error(int err, const char *msg)\n{\n#ifdef NDEBUG\n\treturn;\n#endif\n\tswitch (err) {\n\tcase CL_SUCCESS:\n\t\treturn;\n\tdefault:\n\t\tprintf(\"unknown OpenCL error %d \\n\", err);\n\t}\n\tprintf(\"Exiting due to error in: %s \\n\", msg);\n\texit(1);\n}\n\n/* Returns SIFT3D_SUCCESS if the specified format is supported for this context,\n * or SIFT3D_FAILURE if it is not. */\nSIFT3D_IGNORE_UNUSED\nstatic int check_cl_image_support(cl_context context, cl_mem_flags mem_flags,\n\t\t\t\t  cl_image_format image_format,\n\t\t\t\t  cl_mem_object_type image_type)\n{\n#ifdef SIFT3D_USE_OPENCL\n\tcl_image_format *image_formats;\n\tcl_int err;\n\tcl_uint num_image_formats;\n\tint i, support;\n\n\terr = clGetSupportedImageFormats(context, mem_flags, image_type,\n\t\t\t\t\t 0, NULL, &num_image_formats);\n\tif ((image_formats =\n\t     malloc(num_image_formats * sizeof(cl_image_format))) == NULL)\n\t\treturn SIFT3D_FAILURE;\n\terr |= clGetSupportedImageFormats(context, mem_flags, image_type,\n\t\t\t\t\t  num_image_formats, image_formats,\n\t\t\t\t\t  NULL);\n\tcheck_cl_error(err, \"2D image formats\");\n\tsupport = SIFT3D_FALSE;\n\tfor (i = 0; i < num_image_formats; i++) {\n\t\tif (memcmp\n\t\t    (image_formats + i, &image_format,\n\t\t     sizeof(cl_image_format))) {\n\t\t\tsupport = SIFT3D_TRUE;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn (support == SIFT3D_TRUE) ? SIFT3D_SUCCESS : SIFT3D_FAILURE;\n#else\n\tprintf\n\t    (\"check_cl_image_support: This version was not compiled with OpenCL!\\n\");\n\treturn SIFT3D_FAILURE;\n#endif\n}\n\n/* Initialize the relevant OpenCL data, using the specified device type,\n * memory flags, and image format. If no such devices are found, or these\n * settings are not supported on the device, returns SIFT3D_FAILURE. Returns SIFT3D_SUCCESS\n * when userData is initialized. \n *\n * This library saves a copy of user_cl_data for use with future calls. To change \n * the settings of the library, call init_cl again. */\n\nint init_cl(CL_data * user_cl_data, const char *platform_name,\n\t    cl_device_type device_type, cl_mem_flags mem_flags,\n\t    cl_image_format image_format)\n{\n#ifdef SIFT3D_USE_OPENCL\n\tKernels kernels;\n\tcl_platform_id *platforms;\n\tcl_device_id *devices;\n\tchar *name, *src;\n\tcl_context_properties properties[3];\n\tcl_command_queue *queues;\n\tcl_program program;\n\tcl_platform_id platform;\n\tcl_context context;\n\tcl_uint num_platforms, num_devices;\n\tcl_int err;\n\tcl_bool support;\n\tsize_t size;\n\tint i;\n\n\t// Initialize persistent arrays to NULL\n\tdevices = NULL;\n\tqueues = NULL;\n\tsrc = NULL;\n\n\t// Query the available platforms and select the specified one\n\terr = clGetPlatformIDs(0, NULL, &num_platforms);\n\tif (err != CL_SUCCESS || num_platforms < 1)\n\t\tgoto init_cl_quit;\n\n\tif ((platforms = (cl_platform_id *) malloc(num_platforms *\n\t\t\t\t\t\t   sizeof(cl_platform_id))) ==\n\t    NULL)\n\t\tgoto init_cl_quit;\n\tclGetPlatformIDs(num_platforms, platforms, NULL);\n\tname = NULL;\n\tplatform = (cl_platform_id) NULL;\n\tfor (i = 0; i < num_platforms; i++) {\n\t\terr = clGetPlatformInfo(platforms[i], CL_PLATFORM_NAME, 0,\n\t\t\t\t\tNULL, &size);\n\t\tname = (char *)realloc(name, size * sizeof(char));\n\t\terr = clGetPlatformInfo(platforms[i], CL_PLATFORM_NAME,\n\t\t\t\t\tsize, name, NULL);\n\t\tif (!strcmp(name, platform_name)) {\n\t\t\tplatform = platforms[i];\n\t\t}\n\t}\n\tfree(platforms);\n\tfree(name);\n\n\tif (platform == (cl_platform_id) NULL) {\n\t\tprintf(\"init_cl: Failed to find platform %s \\n\", platform_name);\n\t\tgoto init_cl_quit;\n\t}\n\t// Get the number of devices of the specified type\n\tdevices = NULL;\n\terr = clGetDeviceIDs(platform, device_type, 0, NULL, &num_devices);\n\tif (err != CL_SUCCESS && err != CL_DEVICE_NOT_FOUND) {\n\t\tcheck_cl_error(err, \"Create context\");\n\t} else if (num_devices > 0) {\n\t\tdevices =\n\t\t    (cl_device_id *) malloc(num_devices * sizeof(cl_device_id));\n\t\terr =\n\t\t    clGetDeviceIDs(platform, device_type, num_devices, devices,\n\t\t\t\t   NULL);\n\t\tcheck_cl_error(err, \"Get devices\");\n\t}\n\n\tif (num_devices <= 0 || devices == NULL) {\n\t\tputs(\"init_cl: No OpenCL devices available \\n\");\n\t\tgoto init_cl_quit;\n\t}\n\t//TODO: Multiple GPUs on one context does not seem to work. Maybe try multiple\n\t//      contexts, one per GPU?\n\tnum_devices = 1;\n\n\t// Create the context\n\tproperties[0] = CL_CONTEXT_PLATFORM;\n\tproperties[1] = (cl_context_properties) platform;\n\tproperties[2] = 0;\n\tcontext =\n\t    clCreateContext(properties, num_devices, devices, NULL, NULL, &err);\n\tcheck_cl_error(err, \"Create context \\n\");\n\n\t// Create a command queue on each device \n\tif ((queues = malloc(num_devices * sizeof(cl_command_queue))) == NULL)\n\t\tgoto init_cl_quit;\n\n\tfor (i = 0; i < num_devices; i++) {\n\t\tcl_device_type type;\n\t\terr = clGetDeviceInfo(devices[i], CL_DEVICE_TYPE,\n\t\t\t\t      sizeof(cl_device_type), &type, NULL);\n\t\tif (type == device_type) {\n\t\t\tif ((queues[i] = clCreateCommandQueue(context,\n\t\t\t\t\t\t\t      devices[i], 0,\n\t\t\t\t\t\t\t      NULL)) == NULL) {\n\t\t\t\tgoto init_cl_quit;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Query for support of the desired image and memory format on these devices\n\tfor (i = 0; i < num_devices; i++) {\n\n\t\t// Query for image support\n\t\tsupport = CL_FALSE;\n\t\tclGetDeviceInfo(devices[i], CL_DEVICE_IMAGE_SUPPORT,\n\t\t\t\tsizeof(cl_bool), &support, NULL);\n\t\tif (support != CL_TRUE) {\n\t\t\tprintf\n\t\t\t    (\"init_cl: Images are not supported by device %d \\n\",\n\t\t\t     i);\n\t\t\tgoto init_cl_quit;\n\t\t}\n\t\t// Query for support of the specified image format in both 2D and 3D images\n\t\tcheck_cl_image_support(context, mem_flags, image_format,\n\t\t\t\t       CL_MEM_OBJECT_IMAGE2D);\n\t\tcheck_cl_image_support(context, mem_flags, image_format,\n\t\t\t\t       CL_MEM_OBJECT_IMAGE3D);\n\t}\n\n\t// Load the kernels from a file\n\tif ((src = read_file(KERNELS_PATH)) == NULL) {\n\t\tprintf(\"init_cl: Error reading kernel source file %s\",\n\t\t       KERNELS_PATH);\n\t\tgoto init_cl_quit;\n\t}\n\t// Compile static programs\n\tif (compile_cl_program_from_source\n\t    (&program, context, devices, num_devices, &src, 1))\n\t\tgoto init_cl_quit;\n\tkernels.downsample_2x_3d =\n\t    clCreateKernel(program, \"downsample_2x_3d\", &err);\n\tcheck_cl_error(err, \"init_cl: create kernels\");\n\tclReleaseProgram(program);\n\n\t// Save data to the user and library copies\n\tcl_data.platform = platform;\n\tcl_data.devices = devices;\n\tcl_data.context = context;\n\tcl_data.queues = queues;\n\tcl_data.num_devices = num_devices;\n\tcl_data.image_format = image_format;\n\tcl_data.valid = SIFT3D_TRUE;\n\tcl_data.kernels = kernels;\n\t*user_cl_data = cl_data;\n\treturn SIFT3D_SUCCESS;\n\n init_cl_quit:\n\tif (devices != NULL)\n\t\tfree(devices);\n\tif (queues != NULL)\n\t\tfree(queues);\n\tif (src != NULL)\n\t\tfree(src);\n\treturn SIFT3D_FAILURE;\n#else\n\tprintf(\"init_cl: This version was not compiled with OpenCL!\\n\");\n\treturn SIFT3D_FAILURE;\n#endif\n}\n\n/* Compile a program from the given source strings, writing the program handle into\n * the specified pointer. */\nSIFT3D_IGNORE_UNUSED\nstatic int compile_cl_program_from_source(cl_program * program,\n\t\t\t\t\t  cl_context context,\n\t\t\t\t\t  cl_device_id * devices,\n\t\t\t\t\t  int num_devices, char **src,\n\t\t\t\t\t  int num_str)\n{\n#ifdef SIFT3D_USE_OPENCL\n\tcl_int err;\n\tint i;\n\n\terr = CL_SUCCESS;\n\t*program =\n\t    clCreateProgramWithSource(context, 1, (const char **)src, NULL,\n\t\t\t\t      &err);\n\tif (*program == NULL || err != CL_SUCCESS) {\n\t\tputs(\"Error creating program for static kernels \\n\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n\tif ((err =\n\t     clBuildProgram(*program, 0, NULL, NULL, NULL,\n\t\t\t    NULL)) != CL_SUCCESS) {\n\t\tchar log[1 << 15];\n\t\tputs(\"Error: Failed to build program \\n\");\n\t\tfor (i = 0; i < num_devices; i++) {\n\t\t\tclGetProgramBuildInfo(*program, devices[i],\n\t\t\t\t\t      CL_PROGRAM_BUILD_LOG, sizeof(log),\n\t\t\t\t\t      log, NULL);\n\t\t\tprintf(\"\\n-------Build log for device %d-------\\n %s\",\n\t\t\t       i, log);\n\t\t}\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n\treturn SIFT3D_SUCCESS;\n#else\n\tprintf\n\t    (\"compile_cl_program_from_source: This verison was not compiled with \"\n\t     \"OpenCL!\\n\");\n\treturn SIFT3D_FAILURE;\n#endif\n}\n\n/* Initialize a triangle mesh for first use. This must be called before mesh\n * can be used in any other functions. */\nvoid init_Mesh(Mesh * const mesh)\n{\n\tmesh->tri = NULL;\n\tmesh->num = -1;\n}\n\n/* Release all memory associated with a triangle mesh. mesh cannot be reused\n * before it is reinitialized. */\nvoid cleanup_Mesh(Mesh * const mesh)\n{\n\tfree(mesh->tri);\n}\n\n/* Convert a matrix to a different type. in and out may be the same pointer.\n * \n * This function resizes out.\n * \n * All matrices must be initialized prior to calling this function. */\nint convert_Mat_rm(const Mat_rm * const in, Mat_rm * const out,\n\t\t   const Mat_rm_type type)\n{\n\n\tint i, j;\n\n\t// Resize the output\n\tout->num_rows = in->num_rows;\n\tout->num_cols = in->num_cols;\n\tout->type = type;\n\tif (resize_Mat_rm(out))\n\t\treturn SIFT3D_FAILURE;\n\n#define CONVERT_TYPE(type_in, type_out) \\\n    SIFT3D_MAT_RM_LOOP_START(in, i, j) \\\n    SIFT3D_MAT_RM_GET(out, i, j, type_out) = (type_out) \\\n        SIFT3D_MAT_RM_GET(in, i, j, type_in); \\\n    SIFT3D_MAT_RM_LOOP_END\n\n#define CONVERT_TYPE_OUTER(type_out) \\\n    switch (in->type) { \\\n    case SIFT3D_DOUBLE: \\\n        CONVERT_TYPE(double, type_out) \\\n        break; \\\n    case SIFT3D_FLOAT: \\\n        CONVERT_TYPE(float, type_out) \\\n        break; \\\n    case SIFT3D_INT: \\\n        CONVERT_TYPE(int, type_out) \\\n        break; \\\n    default: \\\n        puts(\"convert_Mat_rm: unknown type of input matrix \\n\"); \\\n        return SIFT3D_FAILURE; \\\n    }\n\n\t// Convert to the specified type\n\tswitch (type) {\n\tcase SIFT3D_DOUBLE:\n\t\tCONVERT_TYPE_OUTER(double) break;\n\tcase SIFT3D_FLOAT:\n\t\tCONVERT_TYPE_OUTER(float) break;\n\tcase SIFT3D_INT:\n\t\tCONVERT_TYPE_OUTER(int) break;\n\tdefault:\n\t\tputs(\"convert_Mat_rm: unknown destination type \\n\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n#undef CONVERT_TYPE_OUTER\n#undef CONVERT_TYPE\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Shortcut function to initalize a matrix.\n * \n * Parameters:\n *      mat - The matrix to be initialized\n *      num_rows - The number of rows\n *      num_cols - The number of columns\n *      type - The data type \n *      set_zero - If true, initializes the elements to zero.\n *\n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise. */\nint init_Mat_rm(Mat_rm *const mat, const int num_rows, const int num_cols,\n                const Mat_rm_type type, const int set_zero) {\n\n        mat->type = type;\n        mat->num_rows = num_rows;\n        mat->num_cols = num_cols;\n        mat->u.data_double = NULL;\n        mat->size = 0;\n        mat->static_mem = SIFT3D_FALSE;\n\n        if (resize_Mat_rm(mat))\n                return SIFT3D_FAILURE;\n        \n        if (set_zero && zero_Mat_rm(mat))\n                return SIFT3D_FAILURE;\n    \n        return SIFT3D_SUCCESS;\n}\n\n/* As init_Mat_rm, but aliases data memory with pointer p. The flag \n * mat->static_mem is set, and the matrix does not need to be freed with \n * cleanup_Mat_rm. But, an error will be thrown if the user attempts to resize\n * the memory. That is, resize_Mat_rm will only return success if the size of \n * the matrix does not change. */ \nint init_Mat_rm_p(Mat_rm *const mat, const void *const p, const int num_rows, \n                  const int num_cols, const Mat_rm_type type, \n                  const int set_zero) {\n\n        // Perform normal initialization\n        if (init_Mat_rm(mat, num_rows, num_cols, type, set_zero))\n                return SIFT3D_FAILURE;\n\n        // Clean up any existing memory\n        cleanup_Mat_rm(mat);\n\n        // Alias with provided memory and set the static flag\n        mat->u.data_double = (double *) p;\n        mat->static_mem = SIFT3D_TRUE;\n\n        // Optionally set to zero \n        if (set_zero && zero_Mat_rm(mat))\n                return SIFT3D_FAILURE;\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Prints the type of mat into the string str. */\nvoid sprint_type_Mat_rm(const Mat_rm * const mat, char *const str)\n{\n\tswitch (mat->type) {\n\tcase SIFT3D_DOUBLE:\n\t\tsprintf(str, \"double\");\n\t\tbreak;\n\tcase SIFT3D_FLOAT:\n\t\tsprintf(str, \"float\");\n\t\tbreak;\n\tcase SIFT3D_INT:\n\t\tsprintf(str, \"int\");\n\t\tbreak;\n\tdefault:\n\t\tsprintf(str, \"<sprint_type_Mat_rm: unknown type>\");\n\t}\n}\n\n/* Concatenate two matrices. If dim = 0, concatenates vertically, i.e. \n *      dst = [src1\n *             src2].\n * If dim = 1, concatenates horizontally, i.e  \n *      dst = [src1 src2]. \n *\n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE on failure. */\nint concat_Mat_rm(const Mat_rm * const src1, const Mat_rm * const src2,\n\t\t    Mat_rm * const dst, const int dim)\n{\n\n        int off[2], dst_dims[2];\n\tint i, j;\n\n        const int dims1[] = {src1->num_rows, src1->num_cols};\n        const int dims2[] = {src2->num_rows, src2->num_cols};\n\n\t// Verify inputs\n\tif (dims1[1 - dim] != dims2[1 - dim]) {\n\t\tSIFT3D_ERR(\"concat_Mat_rm: incompatible dimensions: \"\n\t\t\t\"left: [%d x %d] right: [%d x %d] dim: %d \\n\", \n                        dims1[0], dims1[1], dims2[0], dims2[1], dim);\n\t\treturn SIFT3D_FAILURE;\n\t}\n\tif (src1->type != src2->type) {\n\n\t\tchar type1[1024], type2[1024];\n\n\t\tsprint_type_Mat_rm(src1, type1);\n\t\tsprint_type_Mat_rm(src2, type2);\n\n\t\tSIFT3D_ERR(\"concat_Mat_rm: incompatible types: \"\n\t\t\t\"left: <%s> right: <%s> \\n\", type1, type2);\n\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n        // Compute the destination dimensions\n        for (i = 0; i < 2; i++) {\n                dst_dims[i] = dim == i ? dims1[i] + dims2[i] : dims1[i];\n        }\n\n\t// Resize dst\n\tdst->type = src1->type;\n\tdst->num_rows = dst_dims[0];\n\tdst->num_cols = dst_dims[1];\n\tif (resize_Mat_rm(dst))\n\t\treturn SIFT3D_FAILURE;\n\n        // Compute the offsets\n        for (i = 0; i < 2; i++) {\n                off[i] = dim == i ? dims1[i] : 0;\n        }\n\n#define COPY_DATA(type) \\\n        /* Copy src1 data */ \\\n        SIFT3D_MAT_RM_LOOP_START(src1, i, j) \\\n                SIFT3D_MAT_RM_GET(dst, i, j, type) = \\\n                        SIFT3D_MAT_RM_GET(src1, i, j, type); \\\n        SIFT3D_MAT_RM_LOOP_END \\\n        \\\n        /* Copy src2 data */ \\\n        SIFT3D_MAT_RM_LOOP_START(src2, i, j) \\\n        \\\n                SIFT3D_MAT_RM_GET(dst, i + off[0], j + off[1], type) = \\\n                        SIFT3D_MAT_RM_GET(src2, i, j, type); \\\n        \\\n        SIFT3D_MAT_RM_LOOP_END\n\n\t// Copy the data\n\tswitch (dst->type) {\n\tcase SIFT3D_DOUBLE:\n\t\tCOPY_DATA(double);\n\t\tbreak;\n\tcase SIFT3D_FLOAT:\n\t\tCOPY_DATA(float);\n\t\tbreak;\n\tcase SIFT3D_INT:\n\t\tCOPY_DATA(int);\n\t\tbreak;\n\tdefault:\n\t\tSIFT3D_ERR(\"concat_Mat_rm: unknown type \\n\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n#undef COPY_DATA\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Copies a matrix. dst will be resized. */\nint copy_Mat_rm(const Mat_rm * const src, Mat_rm * const dst)\n{\n\n\t// Resize dst\n\tdst->type = src->type;\n\tdst->num_rows = src->num_rows;\n\tdst->num_cols = src->num_cols;\n\tif (resize_Mat_rm(dst))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Copy the data (use memmove because of static mode)\n\tmemmove(dst->u.data_double, src->u.data_double, src->size);\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Print a matrix to stdout. The matrix must be initialized. */\nint print_Mat_rm(const Mat_rm * const mat)\n{\n\n\tint i, j;\n\n#define PRINT_MAT_RM(type, format) \\\n    SIFT3D_MAT_RM_LOOP_START(mat, i, j) \\\n        printf(\"%\" #format \" \", SIFT3D_MAT_RM_GET(mat, i, j, type)); \\\n        SIFT3D_MAT_RM_LOOP_COL_END \\\n        puts(\"\\n\"); \\\n    SIFT3D_MAT_RM_LOOP_ROW_END\n\n\tswitch (mat->type) {\n\tcase SIFT3D_DOUBLE:\n\t\tPRINT_MAT_RM(double, f) break;\n\tcase SIFT3D_FLOAT:\n\t\tPRINT_MAT_RM(float, f) break;\n\tcase SIFT3D_INT:\n\t\tPRINT_MAT_RM(int, d) break;\n\tdefault:\n\t\tputs(\"print_Mat_rm: unknown type \\n\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n#undef PRINT_MAT_RM\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Re-sizes a matrix. The following fields\n * must already be initialized:\n * -num_rows\n * -num_cols\n * -type\n * -u.data_* (NULL for first use, non-null for resize)\n *\n * The following fields will be modified:\n * -size\n * -u.data_* (Change is not guaranteed)\n * \n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise.\n */\nint resize_Mat_rm(Mat_rm *const mat) {\n\n    size_t type_size, total_size;\n\n    const int num_rows = mat->num_rows;\n    const int num_cols = mat->num_cols;\n    double **const data = &mat->u.data_double;\n    const size_t numel = num_rows * num_cols;\n    const Mat_rm_type type = mat->type;\n\n    // Get the size of the underyling datatype\n    switch (type) {\n        case SIFT3D_DOUBLE:\n            type_size = sizeof(double);\n            break;\n        case SIFT3D_FLOAT:\n            type_size = sizeof(float);\n            break;\n        case SIFT3D_INT:\n            type_size = sizeof(int);\n            break;\n        default:\n            SIFT3D_ERR(\"resize_Mat_rm: unknown type! \\n\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n    // Calculate the new size\n    total_size = type_size * numel;\n\n    // Do nothing if the size has not changed\n    if (total_size == mat->size)\n        return SIFT3D_SUCCESS;\n    mat->size = total_size;\n\n    // Check for static reallocation\n    if (mat->static_mem) {\n        SIFT3D_ERR(\"resize_Mat_rm: illegal re-allocation of static matrix \\n\");\n        return SIFT3D_FAILURE;\n    }\n\n    // Reset if the new size is 0 \n    if (total_size == 0) {\n\tcleanup_Mat_rm(mat);\n\treturn init_Mat_rm(mat, num_rows, num_cols, type, SIFT3D_FALSE);\n    }\n\n    // Re-allocate the memory\n    if ((*data = (double *) SIFT3D_safe_realloc(*data, total_size)) == NULL) {\n        mat->size = 0;\n        return SIFT3D_FAILURE;\n    }\n\n    return SIFT3D_SUCCESS;\n}\n\n/* Set all elements to zero */\nint zero_Mat_rm(Mat_rm *const mat)\n{\n\n\tint i, j;\n\n#define SET_ZERO(type) \\\n    SIFT3D_MAT_RM_LOOP_START(mat, i, j) \\\n        SIFT3D_MAT_RM_GET(mat, i, j, type) = (type) 0; \\\n    SIFT3D_MAT_RM_LOOP_END\n\n\tswitch (mat->type) {\n\tcase SIFT3D_DOUBLE:\n\t\tSET_ZERO(double);\n\t\tbreak;\n\tcase SIFT3D_FLOAT:\n\t\tSET_ZERO(float);\n\t\tbreak;\n\tcase SIFT3D_INT:\n\t\tSET_ZERO(int);\n\t\tbreak;\n\tdefault:\n\t\treturn SIFT3D_FAILURE;\n\t}\n#undef SET_ZERO\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Set a matrix to identity. \n *\n * Parameters:\n *   n: The length of the square matrix. The output will have size [n x n].\n *   mat: The matrix to be set.\n */\nint identity_Mat_rm(const int n, Mat_rm *const mat) {\n\n        int i;\n\n        // Resize the matrix \n        mat->num_rows = mat->num_cols = n;\n        if (resize_Mat_rm(mat))\n                return SIFT3D_FAILURE;\n\n        // Set to identity\n        zero_Mat_rm(mat);\n\n#define SET_IDENTITY(type) \\\n        for (i = 0; i < n; i++) { \\\n                SIFT3D_MAT_RM_GET(mat, i, i, type) = (type) 1; \\\n        }\n\n        SIFT3D_MAT_RM_TYPE_MACRO(mat, identity_Mat_rm_quit, SET_IDENTITY);\n#undef SET_IDENTITY\n\n        return SIFT3D_SUCCESS;\n\nidentity_Mat_rm_quit:\n        return SIFT3D_FAILURE;\n}\n\n/* De-allocate the memory for a Mat_rm struct, unless it was initialized in\n * static mode. */\nvoid cleanup_Mat_rm(Mat_rm *mat) {\n\n    if (mat->u.data_double == NULL)\n        return;\n\n    if (!mat->static_mem)\n        free(mat->u.data_double);\n}\n\n/* Make a grid with the specified spacing between lines and line width. \n * Uses the default stride and initializes grid. */\nint draw_grid(Image * grid, int nx, int ny, int nz, int spacing, int line_width)\n{\n\n\tint x, y, z;\n\n\tconst double line_half_width = (double)line_width / 2.0;\n\n\t// Verify inputs\n\tif (spacing < 2 || line_width < 1 || line_width > spacing)\n\t\treturn SIFT3D_FAILURE;\n\n\tif (init_im_with_dims(grid, nx, ny, nz, 1))\n\t\treturn SIFT3D_FAILURE;\n\n\tSIFT3D_IM_LOOP_START(grid, x, y, z)\n\t    if (x % spacing == 0 || y % spacing == 0 || z % spacing == 0) {\n\t\tint x_draw, y_draw, z_draw, x_start, x_end, y_start,\n\t\t    y_end, z_start, z_end;\n\n\t\t// Draw a line  \n\t\tx_start = SIFT3D_MAX(x - line_half_width, 0);\n\t\ty_start = SIFT3D_MAX(y - line_half_width, 0);\n\t\tz_start = SIFT3D_MAX(z - line_half_width, 0);\n\t\tx_end = SIFT3D_MIN(x + line_half_width + 1, nx - 1);\n\t\ty_end = SIFT3D_MIN(y + line_half_width + 1, ny - 1);\n\t\tz_end = SIFT3D_MIN(z + line_half_width + 1, nz - 1);\n\t\tSIFT3D_IM_LOOP_LIMITED_START(grid, x_draw, y_draw, z_draw,\n\t\t\t\t\t     x_start, x_end,\n\t\t\t\t\t     y_start, y_end, z_start, z_end)\n\t\t    if (abs(x_draw - x) < line_half_width &&\n\t\t\tabs(y_draw - y) < line_half_width &&\n\t\t\tabs(z_draw - z) < line_half_width)\n\t\t\tSIFT3D_IM_GET_VOX(grid, x_draw, y_draw,\n\t\t\t\t\t  z_draw, 0) = 1.0f;\n\tSIFT3D_IM_LOOP_END}\n\tSIFT3D_IM_LOOP_END return SIFT3D_SUCCESS;\n}\n\n/* Draw points in in image*/\nint draw_points(const Mat_rm * const in, const int *const dims, int radius,\n\t\tImage * const out)\n{\n\n\tMat_rm in_i;\n\tint i, x, y, z;\n\n\t// Initialize intermediates\n\tif (init_Mat_rm(&in_i, 0, 0, SIFT3D_INT, SIFT3D_FALSE))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Resize the output\n\tmemcpy(SIFT3D_IM_GET_DIMS(out), dims, IM_NDIMS * sizeof(int));\n\tout->nc = 1;\n\tim_default_stride(out);\n\tif (im_resize(out))\n\t\tgoto draw_points_quit;\n\tim_zero(out);\n\n\t// Convert the input to integer\n\tif (convert_Mat_rm(in, &in_i, SIFT3D_INT))\n\t\tgoto draw_points_quit;\n\n\tfor (i = 0; i < in->num_rows; i++) {\n\t\tconst int cx = SIFT3D_MAT_RM_GET(&in_i, i, 0, int);\n\t\tconst int cy = SIFT3D_MAT_RM_GET(&in_i, i, 1, int);\n\t\tconst int cz = SIFT3D_MAT_RM_GET(&in_i, i, 2, int);\n\t\tconst int x_start = SIFT3D_MAX(cx - radius, 0);\n\t\tconst int y_start = SIFT3D_MAX(cy - radius, 0);\n\t\tconst int z_start = SIFT3D_MAX(cz - radius, 0);\n\t\tconst int x_end = SIFT3D_MIN(cx + radius, dims[0] - 1);\n\t\tconst int y_end = SIFT3D_MIN(cy + radius, dims[1] - 1);\n\t\tconst int z_end = SIFT3D_MIN(cz + radius, dims[2] - 1);\n\n\t\t// Draw the point\n\t\tSIFT3D_IM_LOOP_LIMITED_START(out, x, y, z, x_start, x_end,\n\t\t\t\t\t     y_start, y_end, z_start, z_end)\n\t\t    SIFT3D_IM_GET_VOX(out, x, y, z, 0) = 1.0f;\n\tSIFT3D_IM_LOOP_END}\n\n\t// Clean up\n\tcleanup_Mat_rm(&in_i);\n\treturn SIFT3D_SUCCESS;\n\n draw_points_quit:\n\tcleanup_Mat_rm(&in_i);\n\treturn SIFT3D_FAILURE;\n}\n\n/* Draw lines between two sets of points.\n * TODO currently only does XY plane. Add support for other planes */\nint draw_lines(const Mat_rm * const points1, const Mat_rm * const points2,\n\t       const int *const dims, Image * const out)\n{\n\n\tMat_rm points1_d, points2_d;\n\tdouble xd;\n\tint i, y;\n\n\t// Parameters\n\tconst double line_step = 0.1;\n\n\t// Verify inputs\n\tif (points1->num_rows != points2->num_rows ||\n\t    points1->num_cols != points2->num_cols ||\n\t    points1->num_cols != IM_NDIMS) {\n\t\tputs(\"draw_lines: invalid points dimensions \\n\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n\t// Initialize intermediates\n\tif (init_Mat_rm(&points1_d, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE) ||\n\t    init_Mat_rm(&points2_d, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Resize the output image\n\tmemcpy(SIFT3D_IM_GET_DIMS(out), dims, IM_NDIMS * sizeof(int));\n\tout->nc = 1;\n\tim_default_stride(out);\n\tif (im_resize(out))\n\t\tgoto draw_lines_quit;\n\tim_zero(out);\n\n\t// Convert the inputs to double\n\tif (convert_Mat_rm(points1, &points1_d, SIFT3D_DOUBLE) ||\n\t    convert_Mat_rm(points2, &points2_d, SIFT3D_DOUBLE))\n\t\tgoto draw_lines_quit;\n\n\tfor (i = 0; i < points1->num_rows; i++) {\n\n\t\tconst double p1x = SIFT3D_MAT_RM_GET(&points1_d, i, 0, double);\n\t\tconst double p2x = SIFT3D_MAT_RM_GET(&points2_d, i, 0, double);\n\t\tconst double p1y = SIFT3D_MAT_RM_GET(&points1_d, i, 1, double);\n\t\tconst double p2y = SIFT3D_MAT_RM_GET(&points2_d, i, 1, double);\n\t\tconst double p1z = SIFT3D_MAT_RM_GET(&points1_d, i, 2, double);\n\t\tconst double p2z = SIFT3D_MAT_RM_GET(&points2_d, i, 2, double);\n\n\t\t// Check the bounds\n\t\tif (!IM_CONTAINS(out, p1x, p1y, p1z) ||\n\t\t    !IM_CONTAINS(out, p2x, p2y, p2z))\n\t\t\tcontinue;\n\n\t\t// Get the bounds of the line\n\t\tconst double x_start = SIFT3D_MIN(p1x, p2x) + 0.5;\n\t\tconst double x_end = SIFT3D_MAX(p1x, p2x) + 0.5;\n\t\tconst int zi = (int)p1z;\n\n\t\t// Check if the line is vertical\n\t\tconst int vert = fabs(x_start - x_end) < 1.0;\n\n\t\t// Draw the line\n\t\tif (vert) {\n\n\t\t\tconst int xi = (int)x_start;\n\t\t\tconst int y_start = (int)SIFT3D_MIN(p1y, p2y);\n\t\t\tconst int y_end = (int)SIFT3D_MAX(p1y, p2y);\n\n\t\t\tfor (y = y_start; y <= y_end; y++) {\n\t\t\t\tSIFT3D_IM_GET_VOX(out, xi, y, zi, 0) = 1.0f;\n\t\t\t}\n\n\t\t} else {\n\n\t\t\t// Get the line parameters\n\t\t\tconst double y_slope = p1x < p2x ? (p2y - p1y) /\n\t\t\t    (p2x - p1x) : (p1y - p2y) / (p1x - p2x);\n\t\t\tconst double b = p1y + 0.5 - (p1x + 0.5) * y_slope;\n\n\t\t\tfor (xd = x_start; xd <= x_end; xd += line_step) {\n\n\t\t\t\tconst double yd = y_slope * xd + b;\n\t\t\t\tconst int xi = (int)xd;\n\t\t\t\tconst int yi = (int)yd;\n\n\t\t\t\tif (yi < 0 || yi > dims[1] - 1)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tSIFT3D_IM_GET_VOX(out, xi, yi, zi, 0) = 1.0f;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Clean up\n\tcleanup_Mat_rm(&points1_d);\n\tcleanup_Mat_rm(&points2_d);\n\n\treturn SIFT3D_SUCCESS;\n\n draw_lines_quit:\n\tcleanup_Mat_rm(&points1_d);\n\tcleanup_Mat_rm(&points2_d);\n\treturn SIFT3D_FAILURE;\n}\n\n/* Detect the format of the supplied file name. */\nim_format im_get_format(const char *path) {\n\n        struct stat st;\n        const char *ext;\n\n        // Check if the file exists and is a directory\n        if (stat(path, &st) == 0) {\n                if (S_ISDIR(st.st_mode))\n                        return DIRECTORY;\n        } \n\n        // If not a directory, get the file extension\n        ext = get_file_ext(path);\n\n        // Check the known types\n        if (!strcmp(ext, ext_analyze) || !strcmp(ext, ext_gz) ||\n                !strcmp(ext, ext_nii))\n                return NIFTI;\n\n        if (!strcmp(ext, ext_dcm))\n                return DICOM;\n\n        if (!strcmp(ext, ext_dir))\n                return DIRECTORY;\n\n        // The type was not recognized\n        return UNKNOWN;\n}\n\n/* Read an image from a file. The file extension must match one of the\n * supported formats.\n *\n * Supported formats:\n * - Analyze (.img, .img.gz)\n * - DICOM (.dcm)\n * - Directory of DICOM files\n * - NIFTI-1 (.nii, .nii.gz)\n *\n * Return values:\n * -SIFT3D_SUCCESS - Image successfully read\n * -SIFT3D_FILE_DOES_NOT_EXIST - The file does not exist\n * -SIFT3D_UNSUPPORTED_FILE_TYPE - The file type is not supported\n * -SIFT3D_WRAPPER_NOT_COMPILED - The file type is supported, but the wrapper \n *      library was not compiled.\n * -SIFT3D_UNEVEN_SPACING - The image slices are unevenly spaced.\n * -SIFT3D_INCONSISTENT_AXES  - The image slices have inconsistent axes.\n * -SIFT3D_DUPLICATE_SLICES - There are multiple slices in the same location.\n * -SIFT3D_FAILURE - Other error\n */\nint im_read(const char *path, Image *const im) {\n\n        struct stat st;\n        int ret;\n\n        // Ensure the file exists\n        if (stat(path, &st) != 0) {\n                SIFT3D_ERR(\"im_read: failed to find file %s \\n\", path);\n                return SIFT3D_FILE_DOES_NOT_EXIST;\n        }\n\n        // Get the file format and write the file\n        switch (im_get_format(path)) {\n        case ANALYZE:\n        case NIFTI:\n                ret = read_nii(path, im);\n                break;\n        case DICOM:\n                ret = read_dcm(path, im);\n                break;\n        case DIRECTORY:\n                ret = read_dcm_dir(path, im);\n                break;\n        case FILE_ERROR:\n                ret = SIFT3D_FAILURE;\n                break;\n        case UNKNOWN:\n        default:\n                SIFT3D_ERR(\"im_read: unrecognized file extension \"\n                        \"from file %s \\n\", path);\n                ret = SIFT3D_UNSUPPORTED_FILE_TYPE;\n        }\n\n        return ret;\n}\n\n/* Write an image to a file.\n * \n * Supported formats:\n * -DICOM (.dcm)\n * -Directory of DICOM files\n * -NIFTI (.nii, .nii.gz)\n *\n * Return values:\n * -SIFT3D_SUCCESS - Successfully wrote the image\n * -SIFT3D_UNSUPPORTED_FILE_TYPE - Cannot write this file type\n * -SIFT3D_FAILURE - Other error\n */\nint im_write(const char *path, const Image *const im) {\n\n\t// Create the path\n\tif (mkpath(path, out_mode))\n\t\treturn SIFT3D_FAILURE;\n\n        // Get the file format \n        switch (im_get_format(path)) {\n        case ANALYZE:\n        case NIFTI:\n                return write_nii(path, im);\n        case DICOM:\n                return write_dcm(path, im, NULL, -1.0f);\n        case DIRECTORY:\n\n                // Create the directory\n                if (do_mkdir(path, out_mode)) {\n                        SIFT3D_ERR(\"im_write: failed to create directory \"\n                                \"%s \\n\", path);\n                        return SIFT3D_FAILURE;\n                }\n\n                return write_dcm_dir(path, im, NULL);\n        case UNKNOWN:\n        default:\n                // Otherwise, the file extension was not found\n                SIFT3D_ERR(\"im_write: unrecognized file extension \"\n                        \"from file %s \\n\", path);\n\n                return SIFT3D_UNSUPPORTED_FILE_TYPE;\n        }\n\n        // Unreachable code\n        return SIFT3D_FAILURE;\n}\n\n/* Separate the file name component from its path */\nstatic const char *get_file_name(const char *path) {\n\n        const char *name;\n\n        // Get the last file separator\n        name = strrchr(path, SIFT3D_FILE_SEP);\n        return name == NULL ? path : name;\n}\n\n/* Get the extension of a file name */\nstatic const char *get_file_ext(const char *name)\n{\n\n        const char *dot;\n\n        // Get the file name component\n        name = get_file_name(name);\n\n        // Get the last dot\n\tdot = strrchr(name, '.');\n\n\treturn dot == NULL || dot == name ? \"\" : dot + 1;\n}\n\n/* Get the parent directory of a file. The returned string must later be\n * freed. */\nchar *im_get_parent_dir(const char *path) {\n\n        ptrdiff_t file_pos;\n        char *dirName;\n\n        // Duplicate the path so we can edit it\n        dirName = strndup(path, FILENAME_MAX); \n\n        // Subtract away the file name\n        file_pos = get_file_name(path) - path;\n        if (file_pos > 0)\n                dirName[file_pos] = '\\0';\n       \n        return dirName; \n}\n\n/* Write a matrix to a .csv or .csv.gz file. */\nint write_Mat_rm(const char *path, const Mat_rm * const mat)\n{\n\n\tFILE *file;\n\tgzFile gz;\n\tconst char *ext;\n\tint i, j, compress;\n\n\tconst char *mode = \"w\";\n\n\t// Validate and create the output directory\n\tif (mkpath(path, out_mode))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Get the file extension\n\text = get_file_ext(path);\n\n\t// Check if we need to compress the file\n\tcompress = strcmp(ext, ext_gz) == 0;\n\n\t// Open the file\n\tif (compress) {\n\t\tif ((gz = gzopen(path, mode)) == Z_NULL)\n\t\t\treturn SIFT3D_FAILURE;\n\t} else {\n\t\tif ((file = fopen(path, mode)) == NULL)\n\t\t\treturn SIFT3D_FAILURE;\n\t}\n\n#define WRITE_MAT(mat, format, type) \\\n    SIFT3D_MAT_RM_LOOP_START(mat, i, j) \\\n                const char delim = j < mat->num_cols - 1 ? ',' : '\\n'; \\\n                if (compress) { \\\n                        gzprintf(gz, format, SIFT3D_MAT_RM_GET(mat, i, j, \\\n                                type)); \\\n                        gzputc(gz, delim); \\\n                } else { \\\n                \tfprintf(file, format, SIFT3D_MAT_RM_GET(mat, i, j, \\\n                 \t\ttype)); \\\n                        fputc(delim, file); \\\n                } \\\n    SIFT3D_MAT_RM_LOOP_END\n\n\t// Write the matrix\n\tswitch (mat->type) {\n\tcase SIFT3D_DOUBLE:\n\t\tWRITE_MAT(mat, \"%f\", double); \n\t\tbreak;\n\tcase SIFT3D_FLOAT:\n\t\tWRITE_MAT(mat, \"%f\", float); \n\t\tbreak;\n\tcase SIFT3D_INT:\n\t\tWRITE_MAT(mat, \"%d\", int); \n\t\tbreak;\n\tdefault:\n\t\tgoto write_mat_quit;\n\t}\n#undef WRITE_MAT\n\n\t// Check for errors and finish writing the matrix\n\tif (compress) {\n\t\tif (gzclose(gz) != Z_OK)\n\t\t\tgoto write_mat_quit;\n\t} else {\n\t\tif (ferror(file))\n\t\t\tgoto write_mat_quit;\n\t\tfclose(file);\n\t}\n\n\treturn SIFT3D_SUCCESS;\n\n write_mat_quit:\n\tif (compress) {\n\t\tgzclose(gz);\n\t} else {\n\t\tfclose(file);\n\t}\n\treturn SIFT3D_FAILURE;\n}\n\n/* Shortcut to initialize an image for first-time use.\n * Allocates memory, and assumes the default stride. This\n * function calls init_im and initializes all values to 0. */\nint init_im_with_dims(Image *const im, const int nx, const int ny, const int nz,\n        const int nc)\n{\n\n\tinit_im(im);\n\tim->nx = nx;\n\tim->ny = ny;\n\tim->nz = nz;\n\tim->nc = nc;\n\n\tim_default_stride(im);\n\tif (im_resize(im))\n\t\treturn SIFT3D_FAILURE;\n\n\tim_zero(im);\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Calculate the strides of an image object in the default\n * manner. The following parameters must be initialized:\n * -nx\n * -ny\n * -nz\n * -nc\n * If a dimension is not used, its size should be set\n * to 1. */\nvoid im_default_stride(Image *const im)\n{\n\n        size_t prod;\n\tint i;\n\n\tprod = (size_t) im->nc;\n\tSIFT3D_IM_GET_STRIDES(im)[0] = prod;\n\n\tfor (i = 1; i < IM_NDIMS; i++) {\n\t\tprod *= SIFT3D_IM_GET_DIMS(im)[i - 1];\n\t\tSIFT3D_IM_GET_STRIDES(im)[i] = prod;\n\t}\n}\n\n/* Pads an image to a new size. Prior to calling this function, initialize \n * pad with all dimensions and strides, as in im_resize. Other metadata,\n * such as units, will be copied from im to pad. */\nint im_pad(const Image * const im, Image * const pad)\n{\n\n\tint x, y, z, c;\n\n\tconst int pad_x_end = pad->nx - 1;\n\tconst int pad_y_end = pad->ny - 1;\n\tconst int pad_z_end = pad->nz - 1;\n\n\tconst int data_x_end = SIFT3D_MIN(im->nx - 1, pad_x_end);\n\tconst int data_y_end = SIFT3D_MIN(im->ny - 1, pad_y_end);\n\tconst int data_z_end = SIFT3D_MIN(im->nz - 1, pad_z_end);\n\n        // Copy relevant metadata, omitting dimensions and strides\n        memcpy(SIFT3D_IM_GET_UNITS(pad), SIFT3D_IM_GET_UNITS(im), \n                IM_NDIMS * sizeof(double));\n\n\t// Resize the output \n\tif (im_resize(pad))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Copy the image \n\tSIFT3D_IM_LOOP_LIMITED_START_C(im, x, y, z, c, 0, data_x_end, 0,\n\t\t\t\t       data_y_end, 0, data_z_end)\n\n\t    SIFT3D_IM_GET_VOX(pad, x, y, z, c) =\n\t    SIFT3D_IM_GET_VOX(im, x, y, z, c);\n\n\tSIFT3D_IM_LOOP_END_C\n\t    // Pad the remaining data with zeros\n\t    SIFT3D_IM_LOOP_LIMITED_START_C(im, x, y, z, c, data_x_end,\n\t\t\t\t\t   pad_x_end, data_y_end, pad_y_end,\n\t\t\t\t\t   data_z_end, pad_z_end)\n\n\t    SIFT3D_IM_GET_VOX(pad, x, y, z, c) = 0.0f;\n\n\tSIFT3D_IM_LOOP_END_C return SIFT3D_SUCCESS;\n}\n\n/* Resize an image according to the current nx, ny,\n * and nz. Does not modify scale space information or\n * strides. Prior to calling this function, use init_im(im)\n * and initialize the following fields:\n * -nx\n * -ny\n * -nz\n * -nc\n * -xs (can be set by im_default_stride(im)) \n * -ys (can be set by im_default_stride(im)) \n * -zs (can be set by im_default_stride(im)) \n *\n * All of this initialization can also be done with\n * init_im_with_dims(), which calls this function.\n *\n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise.\n */\nint im_resize(Image *const im)\n{\n\n\tint i;\n\n\t//FIXME: This will not work for strange strides\n\tconst size_t size = im->nx * im->ny * im->nz * im->nc;\n\n\t// Verify inputs\n\tfor (i = 0; i < IM_NDIMS; i++) {\n\n\t\tconst int dim = SIFT3D_IM_GET_DIMS(im)[i];\n\n\t\tif (dim > 0)\n\t\t\tcontinue;\n\n\t\tSIFT3D_ERR(\"im_resize: invalid dimension %d: %d \\n\", i,\n\t\t\tdim);\n\t\treturn SIFT3D_FAILURE;\n\t}\n\tif (im->nc < 1) {\n\t\tSIFT3D_ERR(\"im_resize: invalid number of channels: %d \\n\",\n\t\t\tim->nc);\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n        // Do nothing if the size has not changed\n        if (im->size == size)\n                return SIFT3D_SUCCESS;\n\tim->size = size;\n\n\t// Allocate new memory\n\tim->data = SIFT3D_safe_realloc(im->data, size * sizeof(float));\n\n#ifdef SIFT3D_USE_OPENCL\n\t{\n\t\tcl_int err;\n\t\tint initialized;\n\n\t\tif (cl_data.valid) {\n\t\t\tinitialized = (im->data != NULL);\n\n\t\t\t// Destroy the old image\n\t\t\tif (initialized && im->cl_valid)\n\t\t\t\tclReleaseMemObject(im->cl_image);\n\n\t\t\t// Init an OpenCL image\n\t\t\tif (im->nz > 0) {\n\t\t\t\tim->cl_image = clCreateImage2D(cl_data.context,\n\t\t\t\t\t\t\t       cl_data.\n\t\t\t\t\t\t\t       mem_flags,\n\t\t\t\t\t\t\t       &cl_data.\n\t\t\t\t\t\t\t       image_format,\n\t\t\t\t\t\t\t       im->nx, im->ny,\n\t\t\t\t\t\t\t       im->ys,\n\t\t\t\t\t\t\t       im->data, &err);\n\t\t\t} else {\n\t\t\t\tim->cl_image = clCreateImage3D(cl_data.context,\n\t\t\t\t\t\t\t       cl_data.\n\t\t\t\t\t\t\t       mem_flags,\n\t\t\t\t\t\t\t       &cl_data.\n\t\t\t\t\t\t\t       image_format,\n\t\t\t\t\t\t\t       im->nx, im->ny,\n\t\t\t\t\t\t\t       im->nz,\n\t\t\t\t\t\t\t       im->ys,\n\t\t\t\t\t\t\t       im->zs,\n\t\t\t\t\t\t\t       im->data, &err);\n\t\t\t}\n\n\t\t\tif (err != CL_SUCCESS) {\n\t\t\t\tim->cl_valid = SIFT3D_FALSE;\n\t\t\t\treturn SIFT3D_FAILURE;\n\t\t\t}\n\n\t\t\tim->cl_valid = SIFT3D_TRUE;\n\t\t}\n\t}\n#endif\n\treturn size != 0 && im->data == NULL ? SIFT3D_FAILURE : SIFT3D_SUCCESS;\n}\n\n/* Concatenate two images in dimension dim, so that src1 comes before src2.\n * For example, if dim == 0, the images are horizontally concatenated in x,\n * so that src1 is on the left and src2 is on the right. Resizes dst. \n *\n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise. */\nint im_concat(const Image * const src1, const Image * const src2, const int dim,\n\t      Image * const dst)\n{\n\n\tint off[IM_NDIMS], dims_out[IM_NDIMS];\n\tint i, x, y, z, c;\n\n\tconst int nc = src1->nc;\n\n\t// Verify inputs\n\tfor (i = 0; i < IM_NDIMS; i++) {\n\n\t\tconst int src1d = SIFT3D_IM_GET_DIMS(src1)[i];\n\t\tconst int src2d = SIFT3D_IM_GET_DIMS(src2)[i];\n\n\t\tif (i == dim)\n\t\t\tcontinue;\n\t\tif (src1d != src2d) {\n\t\t\tSIFT3D_ERR(\"im_concat: dimension %d must be \"\n\t\t\t\t\"equal in input images. src1: %d src2: %d \\n\",\n\t\t\t\ti, src1d, src2d);\n\t\t\treturn SIFT3D_FAILURE;\n\t\t}\n\t}\n\tif (src1->nc != src2->nc) {\n\t\tSIFT3D_ERR(\"im_concat: images must have an equal number \"\n\t\t\t\"of channels. src1: %d src2: %d \\n\", src1->nc,\n\t\t\tsrc2->nc);\n\t\treturn SIFT3D_FAILURE;\n\t}\n\t// Get the output dimensions and offsets\n\tfor (i = 0; i < IM_NDIMS; i++) {\n\n\t\tconst int src1d = SIFT3D_IM_GET_DIMS(src1)[i];\n\t\tconst int src2d = SIFT3D_IM_GET_DIMS(src2)[i];\n\n                dims_out[i] = dim == i ? src1d + src2d : src1d;\n                off[i] = dim == i ? src1d : 0;\n\t}\n\n\t// Resize dst \n\tmemcpy(SIFT3D_IM_GET_DIMS(dst), dims_out, IM_NDIMS * sizeof(int));\n\tdst->nc = nc;\n\tim_default_stride(dst);\n\tif (im_resize(dst))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Copy the data from src1 \n\tSIFT3D_IM_LOOP_START_C(src1, x, y, z, c)\n\t    SIFT3D_IM_GET_VOX(dst, x, y, z, c) =\n\t    SIFT3D_IM_GET_VOX(src1, x, y, z, c);\n\tSIFT3D_IM_LOOP_END_C \n\n        // Copy the data from src2\n        SIFT3D_IM_LOOP_START_C(src2, x, y, z, c)\n\n                // Get the destination coordinates with offsets\n                const int x_dst = x + off[0];\n                const int y_dst = y + off[1];\n                const int z_dst = z + off[2];\n\n                // Copy the data from src2\n                SIFT3D_IM_GET_VOX(dst, x_dst, y_dst, z_dst, c) =\n                    SIFT3D_IM_GET_VOX(src2, x, y, z, c);\n\n        SIFT3D_IM_LOOP_END_C \n        \n        return SIFT3D_SUCCESS;\n}\n\n/* Upsample an image by a factor of 2 in each dimension.\n * This function resizes dst and modifies its units. */\nint im_upsample_2x(const Image *const src, Image *const dst)\n{\n\n\tdouble units[IM_NDIMS];\n\tint dims[IM_NDIMS];\n\tint i, x, y, z, c, sx, sy, sz;\n\n\tconst int nc = src->nc;\n\tconst int w = 2;\n\tconst float weight = (float) (1.0 / pow((double) w, IM_NDIMS));\n\n\t// Resize the output \n\tfor (i = 0; i < IM_NDIMS; i++) {\n\t\tdims[i] = SIFT3D_IM_GET_DIMS(src)[i] * 2;\t\n\t\tunits[i] = SIFT3D_IM_GET_UNITS(src)[i] / 2.0;\n\t}\n\tmemcpy(SIFT3D_IM_GET_DIMS(dst), dims, IM_NDIMS * sizeof(int));\n\tmemcpy(SIFT3D_IM_GET_UNITS(dst), units, IM_NDIMS * sizeof(float));\n\tdst->nc = nc;\n\tim_default_stride(dst);\n\tif (im_resize(dst))\n\t\treturn SIFT3D_FAILURE;\n\n\t// TODO: 3-pass (separable) upsample might be faster\n\n\t// Upsample\n\tSIFT3D_IM_LOOP_START_C(dst, x, y, z, c)\n\n\t\tconst int sx_start = x >> 1;\n\t\tconst int sy_start = y >> 1;\n\t\tconst int sz_start = z >> 1;\n\n\t\tconst int sx_end = sx_start + w - 1;\n\t\tconst int sy_end = sy_start + w - 1;\n\t\tconst int sz_end = sz_start + w - 1;\n\n\t\tSIFT3D_IM_GET_VOX(dst, x, y, z, c) = 0;\n\n\t\tSIFT3D_IM_LOOP_LIMITED_START(dst, sx, sy, sz, sx_start,\n\t\t\t\t\t       sx_end, sy_start, sy_end,\n\t\t\t\t\t       sz_start, sz_end)\n\n\t\t\tSIFT3D_IM_GET_VOX(dst, x, y, z, c) += \n\t\t\t\tSIFT3D_IM_GET_VOX(src, sx, sy, sz, c);\n\n\t\tSIFT3D_IM_LOOP_END \n\n\t\tSIFT3D_IM_GET_VOX(dst, x, y, z, c) *= weight;\t\n\n\tSIFT3D_IM_LOOP_END_C\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Downsample an image by a factor of 2 in each dimension.\n * This function initializes dst with the proper \n * dimensions, and allocates memory. */\nint im_downsample_2x(const Image *const src, Image *const dst)\n{\n\n\tint x, y, z, c;\n\n\t// Initialize dst\n\tdst->nx = (int)floor((double)src->nx / 2.0);\n\tdst->ny = (int)floor((double)src->ny / 2.0);\n\tdst->nz = (int)floor((double)src->nz / 2.0);\n\tdst->nc = src->nc;\n\tim_default_stride(dst);\n\tif (im_resize(dst))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Downsample\n\tSIFT3D_IM_LOOP_START_C(dst, x, y, z, c)\n\n\t\tconst int src_x = x << 1;\n\t\tconst int src_y = y << 1;\n\t\tconst int src_z = z << 1;\n\n\t\tSIFT3D_IM_GET_VOX(dst, x, y, z, c) =\n\t\t    SIFT3D_IM_GET_VOX(src, src_x, src_y, src_z, c);\n\tSIFT3D_IM_LOOP_END_C \n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Same as im_downsample_2x, but with OpenCL acceleration. This function DOES NOT\n * read the results back dst->data. Use im_read_back for that. */\nint im_downsample_2x_cl(Image * src, Image * dst)\n{\n#ifdef SIFT3D_USE_OPENCL\n\tsize_t global_work_size[3];\n\tcl_int err, dim;\n\tcl_kernel kernel;\n\n\t// Verify image dimensions\n\tif (src->nx % 2 != 0 || src->ny % 2 != 0 || src->nz % 2 != 0)\n\t\treturn SIFT3D_FAILURE;\n\n\t// Initialize dst dimensions, resized in im_set_kernel_arg\n\tdst->nx = src->nx / 2;\n\tdst->ny = src->ny / 2;\n\tdst->nz = src->nz / 2;\n\tdst->nc = src->nc;\n\tim_default_stride(dst);\n\n\t// Do not have a 2D kernel right now\n\tassert(src->nz > 0);\n\tdim = 3;\n\tglobal_work_size[0] = dst->nx;\n\tglobal_work_size[1] = dst->ny;\n\tglobal_work_size[2] = dst->nz;\n\n\tkernel = cl_data.kernels.downsample_2x_3d;\n\tim_set_kernel_arg(kernel, 0, src);\n\tim_set_kernel_arg(kernel, 1, dst);\n\n\terr =\n\t    clEnqueueNDRangeKernel(cl_data.queues[0], kernel, dim, NULL,\n\t\t\t\t   global_work_size, NULL, 0, NULL, NULL);\n\treturn (int)err;\n#else\n\tprintf\n\t    (\"im_downsample_2x_cl: This version was not compiled with OpenCL!\\n\");\n\treturn SIFT3D_FAILURE;\n#endif\n}\n\n/* Loads the C-accessible data of an image into its OpenCL data. If blocking is set,\n * block the function until the load is complete. */\nint im_load_cl(Image * im, int blocking)\n{\n#ifdef SIFT3D_USE_OPENCL\n\tconst size_t origin[] = { 0, 0, 0 };\n\tconst size_t region[] = { im->nx, im->ny, im->nz };\n\tconst cl_bool cl_blocking = (blocking) ? CL_TRUE : CL_FALSE;\n\treturn clEnqueueWriteImage(cl_data.queues[0], im->cl_image, cl_blocking,\n\t\t\t\t   origin, region, im->ys, im->zs,\n\t\t\t\t   im->data, 0, NULL, NULL);\n#else\n\tprintf(\"im_load_cl: This version was not compiled with OpenCL!\\n\");\n\treturn SIFT3D_FAILURE;\n#endif\n}\n\n/* Reads the OpenCL data of an image back to its C-accessible data. If blocking is set,\n * block the function until the read is complete. */\nint im_read_back(Image * im, int blocking)\n{\n#ifdef SIFT3D_USE_OPENCL\n\tconst size_t origin[] = { 0, 0, 0 };\n\tconst size_t region[] = { im->nx, im->ny, im->nz };\n\tconst cl_bool cl_blocking = (blocking) ? CL_TRUE : CL_FALSE;\n\treturn clEnqueueReadImage(cl_data.queues[0], im->cl_image, cl_blocking,\n\t\t\t\t  origin, region, im->ys, im->zs,\n\t\t\t\t  im->data, 0, NULL, NULL);\n#else\n\tprintf(\"im_read_back: This version was not compiled with OpenCL!\\n\");\n\treturn SIFT3D_FAILURE;\n#endif\n}\n\n/* Updates an Image struct's OpenCL data, if neccessary, and sets it as argument n\n * in the provided kernel. */\nint im_set_kernel_arg(cl_kernel kernel, int n, Image * im)\n{\n#ifdef SIFT3D_USE_OPENCL\n\tcl_int err;\n\n\tif (!im->cl_valid && im_resize(im))\n\t\treturn SIFT3D_FAILURE;\n\terr = clSetKernelArg(kernel, n, sizeof(cl_mem), &im->cl_image);\n\tcheck_cl_error(err, \"im_set_kernel_arg\");\n\n\treturn SIFT3D_SUCCESS;\n#else\n\tprintf\n\t    (\"im_set_kernel_arg: This version was not compiled with OpenCL!\\n\");\n\treturn SIFT3D_FAILURE;\n#endif\n}\n\n/* Copy an image's dimensions and stride into another. \n * This function resizes dst.\n * \n * @param src The source image.\n * @param dst The destination image.\n * @return Returns SIFT3D_SUCCESS or SIFT3D_FAILURE.\n */\nint im_copy_dims(const Image * const src, Image * dst)\n{\n        if (src->data == NULL)\n                return SIFT3D_FAILURE;\n\n\tdst->nx = src->nx;\n\tdst->ny = src->ny;\n\tdst->nz = src->nz;\n\tdst->xs = src->xs;\n\tdst->ys = src->ys;\n\tdst->zs = src->zs;\n\tdst->nc = src->nc;\n        dst->ux = src->ux;\n        dst->uy = src->uy;\n        dst->uz = src->uz;\n\n\treturn im_resize(dst);\n}\n\n/* Copy an image's data into another. This function\n * changes the dimensions and stride of dst,\n * and allocates memory. */\nint im_copy_data(const Image * const src, Image * const dst)\n{\n\n\tint x, y, z, c;\n\n        // Return if src has no data \n        if (src->data == NULL)\n                return SIFT3D_FAILURE;\n\n\t// Return if src and dst are the same\n\tif (dst->data == src->data)\n\t\treturn SIFT3D_SUCCESS;\n\n\t// Resize dst\n\tif (im_copy_dims(src, dst))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Copy data\n        SIFT3D_IM_LOOP_START_C(dst, x, y, z, c)\n                SIFT3D_IM_GET_VOX(dst, x, y, z, c) = \n                        SIFT3D_IM_GET_VOX(src, x, y, z, c);\n        SIFT3D_IM_LOOP_END_C\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Clean up memory for an Image */\nvoid im_free(Image * im)\n{\n\tif (im->data != NULL)\n\t\tfree(im->data);\n}\n\n/* Make a deep copy of a single channel of an image. */\nint im_channel(const Image * const src, Image * const dst,\n\t       const unsigned int chan)\n{\n\n\tint x, y, z;\n\n\tconst int c = chan;\n\n\t// Verify inputs\n\tif (c >= src->nc) {\n\t\tSIFT3D_ERR(\"im_channel: invalid channel: %d, image has \"\n\t\t\t\"%d channels\", c, src->nc);\n\t\treturn SIFT3D_FAILURE;\n\t}\n\t// Resize the output\n\tmemcpy(SIFT3D_IM_GET_DIMS(dst), SIFT3D_IM_GET_DIMS(src), \n                IM_NDIMS * sizeof(int));\n\tdst->nc = 1;\n\tim_default_stride(dst);\n\tif (im_resize(dst))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Copy the channel\n\tSIFT3D_IM_LOOP_START(dst, x, y, z)\n\t    SIFT3D_IM_GET_VOX(dst, x, y, z, 0) =\n\t    SIFT3D_IM_GET_VOX(src, x, y, z, c);\n\tSIFT3D_IM_LOOP_END return SIFT3D_SUCCESS;\n}\n\n/* Find the maximum absolute value of an image */\nfloat im_max_abs(const Image *const im) {\n\n        float max;\n        int x, y, z, c;\n\n\tmax = 0.0f;\n\tSIFT3D_IM_LOOP_START_C(im, x, y, z, c)\n\n\t        const float samp = fabsf(SIFT3D_IM_GET_VOX(im, x, y, z, c));\n                max = SIFT3D_MAX(max, samp);\n\n\tSIFT3D_IM_LOOP_END_C\n\n        return max;\n}\n\n/* Scale an image to the [-1, 1] range, where\n * the largest absolute value is 1. */\nvoid im_scale(const Image *const im)\n{\n\n\tint x, y, z, c;\n\n        // Find the maximum absolute value\n\tconst float max = im_max_abs(im);\n        if (max == 0.0f)\n\t        return;\n\n\t// Divide by the max \n\tSIFT3D_IM_LOOP_START_C(im, x, y, z, c)\n\t        SIFT3D_IM_GET_VOX(im, x, y, z, c) /= max;\n        SIFT3D_IM_LOOP_END_C\n}\n\n/* Subtract src2 from src1, saving the result in\n * dst.\n * Resizes dst. \n */\nint im_subtract(Image * src1, Image * src2, Image * dst)\n{\n\n\tint x, y, z, c;\n\n\t// Verify inputs\n\tif (src1->nx != src2->nx ||\n\t    src1->ny != src2->ny ||\n\t    src1->nz != src2->nz || src1->nc != src2->nc)\n\t\treturn SIFT3D_FAILURE;\n\n\t// Resize the output image\n\tif (im_copy_dims(src1, dst))\n\t\treturn SIFT3D_FAILURE;\n\n\tSIFT3D_IM_LOOP_START_C(dst, x, y, z, c)\n\t    SIFT3D_IM_GET_VOX(dst, x, y, z, c) =\n\t    SIFT3D_IM_GET_VOX(src1, x, y, z, c) -\n\t    SIFT3D_IM_GET_VOX(src2, x, y, z, c);\n\tSIFT3D_IM_LOOP_END_C return SIFT3D_SUCCESS;\n}\n\n/* Zero an image. */\nvoid im_zero(Image * im)\n{\n\n\tint x, y, z, c;\n\n\tSIFT3D_IM_LOOP_START_C(im, x, y, z, c)\n\t    SIFT3D_IM_GET_VOX(im, x, y, z, c) = 0.0f;\nSIFT3D_IM_LOOP_END_C}\n\n/* Transform an image according to the inverse of the provided tform. \n * \n * Paramters:\n *   tform: The transformation. \n *   src: The input image.\n *   interp: The type of interpolation.\n *   resize: If true, resizes the dst to be the same size as src. Otherwise,\n *     uses the dimensions of dst. \n *   dst: The output image.\n *\n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise. */\nint im_inv_transform(const void *const tform, const Image * const src,\n\t\t     const interp_type interp, const int resize, \n                     Image *const dst)\n{\n\tint x, y, z, c;\n\n\t// Optionally resize the output image\n\tif (resize && im_copy_dims(src, dst))\n\t\treturn SIFT3D_FAILURE;\n\n#define IMUTIL_RESAMPLE(arg) \\\n    SIFT3D_IM_LOOP_START(dst, x, y, z) \\\n\\\n\tdouble transx, transy, transz; \\\n\\\n        apply_tform_xyz(tform, (double)x, (double)y, (double)z, \\\n            &transx, &transy, &transz); \\\n                        \\\n                for (c = 0; c < dst->nc; c++) { \\\n                SIFT3D_IM_GET_VOX(dst, x, y, z, c) = resample_ ## arg(src, \\\n                                transx, transy, transz, c); \\\n                } \\\n    SIFT3D_IM_LOOP_END\n\n\t// Transform\n\tswitch (interp) {\n\tcase LINEAR:\n\t\tIMUTIL_RESAMPLE(linear)\n\t\t    break;\n\tcase LANCZOS2:\n\t\tIMUTIL_RESAMPLE(lanczos2)\n\t\t    break;\n\tdefault:\n\t\tSIFT3D_ERR(\"im_inv_transform: unrecognized \"\n\t\t\t\"interpolation type\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n#undef RESAMPLE\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Helper routine for image transformation. Performs trilinear\n * interpolation, setting out-of-bounds voxels to zero. */\nstatic double resample_linear(const Image * const in, const double x,\n\t\t\t      const double y, const double z, const int c)\n{\n\n\t// Detect out-of-bounds\n\tif (x < 0 || x > in->nx - 1 ||\n\t    y < 0 || y > in->ny - 1 || z < 0 || z > in->nz - 1)\n\t\treturn 0.0;\n\n\tint fx = (int)floor(x);\n\tint fy = (int)floor(y);\n\tint fz = (int)floor(z);\n\tint cx = (int)ceil(x);\n\tint cy = (int)ceil(y);\n\tint cz = (int)ceil(z);\n\n\tdouble dist_x = x - fx;\n\tdouble dist_y = y - fy;\n\tdouble dist_z = z - fz;\n\n\tdouble c0 = SIFT3D_IM_GET_VOX(in, fx, fy, fz, c);\n\tdouble c1 = SIFT3D_IM_GET_VOX(in, fx, cy, fz, c);\n\tdouble c2 = SIFT3D_IM_GET_VOX(in, cx, fy, fz, c);\n\tdouble c3 = SIFT3D_IM_GET_VOX(in, cx, cy, fz, c);\n\tdouble c4 = SIFT3D_IM_GET_VOX(in, fx, fy, cz, c);\n\tdouble c5 = SIFT3D_IM_GET_VOX(in, fx, cy, cz, c);\n\tdouble c6 = SIFT3D_IM_GET_VOX(in, cx, fy, cz, c);\n\tdouble c7 = SIFT3D_IM_GET_VOX(in, cx, cy, cz, c);\n\n\tdouble out = c0 * (1.0 - dist_x) * (1.0 - dist_y) * (1.0 - dist_z)\n\t    + c1 * (1.0 - dist_x) * dist_y * (1.0 - dist_z)\n\t    + c2 * dist_x * (1.0 - dist_y) * (1.0 - dist_z)\n\t    + c3 * dist_x * dist_y * (1.0 - dist_z)\n\t    + c4 * (1.0 - dist_x) * (1.0 - dist_y) * dist_z\n\t    + c5 * (1.0 - dist_x) * dist_y * dist_z\n\t    + c6 * dist_x * (1.0 - dist_y) * dist_z\n\t    + c7 * dist_x * dist_y * dist_z;\n\n\treturn out;\n}\n\n/* Helper routine to resample an image at a point, using the Lanczos kernel */\nstatic double resample_lanczos2(const Image * const im, const double x,\n\t\t\t\tconst double y, const double z, const int c)\n{\n\n\tdouble val;\n\tint xs, ys, zs;\n\n\t//TODO: faster separable implementation\n\n\t// Kernel parameter\n\tconst double a = 2;\n\n\t// Check bounds\n\tconst double xMin = 0;\n\tconst double yMin = 0;\n\tconst double zMin = 0;\n\tconst double xMax = im->nx - 1;\n\tconst double yMax = im->ny - 1;\n\tconst double zMax = im->nz - 1;\n\tif (x < xMin || y < yMin || z < zMin ||\n\t    x > xMax || y > yMax || z > zMax)\n\t\treturn 0.0;\n\n\t// Window \n\tconst int x_start = SIFT3D_MAX(floor(x) - a, xMin);\n\tconst int x_end = SIFT3D_MIN(floor(x) + a, xMax);\n\tconst int y_start = SIFT3D_MAX(floor(y) - a, yMin);\n\tconst int y_end = SIFT3D_MIN(floor(y) + a, yMax);\n\tconst int z_start = SIFT3D_MAX(floor(z) - a, zMin);\n\tconst int z_end = SIFT3D_MIN(floor(z) + a, zMax);\n\n\t// Iterate through the window \n\tval = 0.0;\n\tSIFT3D_IM_LOOP_LIMITED_START(in, xs, ys, zs, x_start, x_end, y_start,\n\t\t\t\t     y_end, z_start, z_end)\n\n        // Evalutate the kernel\n\tconst double xw = fabs((double)xs - x) + DBL_EPSILON;\n\tconst double yw = fabs((double)ys - y) + DBL_EPSILON;\n\tconst double zw = fabs((double)zs - z) + DBL_EPSILON;\n\tconst double kernel = lanczos(xw, a) * lanczos(yw, a) * lanczos(zw, a);\n\n\t// Accumulate\n\tval += kernel * SIFT3D_IM_GET_VOX(im, xs, ys, zs, c);\n\n\tSIFT3D_IM_LOOP_END return val;\n}\n\n/* Lanczos kernel function */\nstatic double lanczos(double x, double a)\n{\n\tconst double pi_x = M_PI * x;\n\treturn a * sin(pi_x) * sin(pi_x / a) / (pi_x * pi_x);\n}\n\n/* Resample an image to different units.\n *\n * Parameters:\n *   src: The input image.\n *   units: The new units. \n *   interp: The type of interpolation to use.\n *   dst: The output image. \n *\n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise. */\nint im_resample(const Image *const src, const double *const units, \n\tconst interp_type interp, Image *const dst) {\n\n\tAffine aff;\n\tMat_rm A;\n\tdouble factors[IM_NDIMS];\n\tint i;\n\n\t// Initialize intermediates\n\tif (init_Mat_rm(&A, IM_NDIMS, IM_NDIMS + 1, SIFT3D_DOUBLE, \n\t\tSIFT3D_TRUE) ||\n\t\tinit_Affine(&aff, IM_NDIMS))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Compute the scaling factors\n\tfor (i = 0; i < IM_NDIMS; i++) {\n\t\tfactors[i] = SIFT3D_IM_GET_UNITS(src)[i] / units[i];\n\t}\n\n\t// Set the transformation matrix\n\tfor (i = 0; i < IM_NDIMS; i++) {\n\t\tSIFT3D_MAT_RM_GET(&A, i, i, double) = 1.0 / factors[i];\n\t}\n\tif (Affine_set_mat(&A, &aff))\n\t\tgoto im_resample_quit;\n\n\t// Set the output dimensions\n\tdst->nc = src->nc;\n\tfor (i = 0; i < IM_NDIMS; i++) {\n\t\tSIFT3D_IM_GET_DIMS(dst)[i] = (int) ceil((double) \n                        SIFT3D_IM_GET_DIMS(src)[i] * factors[i]);\n\t}\n\tim_default_stride(dst);\t\n        if (im_resize(dst))\n\t\tgoto im_resample_quit;\n\n\t// Apply the transformation\n\tif (im_inv_transform(&aff, src, interp, SIFT3D_FALSE, dst))\n\t\tgoto im_resample_quit;\n\n        // Set the new output units\n\tmemcpy(SIFT3D_IM_GET_UNITS(dst), units, IM_NDIMS * sizeof(double));\n\n\t// Clean up\n\tcleanup_tform(&aff);\n\tcleanup_Mat_rm(&A);\n\n\treturn SIFT3D_SUCCESS;\n\nim_resample_quit:\n\tcleanup_tform(&aff);\n\tcleanup_Mat_rm(&A);\n\treturn SIFT3D_FAILURE;\n}\n\n/* Horizontally convolves a separable filter with an image, \n * on CPU. Currently only works in 3D.\n * \n * This function chooses among the best variant of convolve_sep* based on\n * compilation options and filter parameters.\n * \n * Parameters: \n * src - input image (initialized)\n * dst - output image (initialized) \n    int x, y, z;\n * f - filter to be applied\n * dim - dimension in which to convolve\n * unit - the spacing of the filter coefficients\n */\nstatic int convolve_sep(const Image * const src,\n\t\t\tImage * const dst, const Sep_FIR_filter * const f,\n\t\t\tconst int dim, const double unit) {\n\n#ifdef SIFT3D_USE_OPENCL\n        return convolve_sep_cl(src, dst, f, dim, unit);\n#else\n\treturn f->symmetric ? \n                convolve_sep_sym(src, dst, f, dim, unit) : \n                convolve_sep_gen(src, dst, f, dim, unit);\n#endif\n}\n\n/* Convolve_sep for general filters */\nstatic int convolve_sep_gen(const Image * const src,\n\t\t\tImage * const dst, const Sep_FIR_filter * const f,\n\t\t\tconst int dim, const double unit)\n{\n\tregister int x, y, z, c, d;\n\n\tregister const int half_width = f->width / 2;\n\tregister const int nx = src->nx;\n\tregister const int ny = src->ny;\n\tregister const int nz = src->nz;\n        register const float conv_eps = 0.1f;\n\tregister const int dim_end = SIFT3D_IM_GET_DIMS(src)[dim] - 1;\n        register const float unit_factor =  unit /\n                SIFT3D_IM_GET_UNITS(src)[dim];\n        register const int unit_half_width = \n                (int) ceilf(half_width * unit_factor);\n        int start[] = {0, 0, 0};\n        int end[] = {nx - 1, ny - 1, nz - 1};\n\n        // Compute starting and ending points for the convolution dimension\n        start[dim] += unit_half_width;\n        end[dim] -= unit_half_width + 1;\n\n\t//TODO: Convert this to convolve_x, which only convolves in x,\n\t// then make a wrapper to restride, transpose, convolve x, and transpose \n\t// back\n\n\t// Resize the output, with the default stride\n        if (im_copy_dims(src, dst))\n                return SIFT3D_FAILURE;\n\tim_default_stride(dst);\n\tif (im_resize(dst))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Initialize the output to zeros\n\tim_zero(dst);\n\n#define SAMP_AND_ACC(src, dst, tap, coords, c) \\\n{ \\\n        float frac; \\\n\\\n        const int idx_lo[] = {(coords)[0], (coords)[1], (coords)[2]}; \\\n        int idx_hi[] = {idx_lo[0], idx_lo[1], idx_lo[2]}; \\\n\\\n        /* Convert the physical coordinates to integer indices*/ \\\n        idx_hi[dim] += 1; \\\n        frac = (coords)[dim] - (float) idx_lo[dim]; \\\n\\\n        /* Sample with linear interpolation */ \\\n        SIFT3D_IM_GET_VOX(dst, x, y, z, c) += (tap) * \\\n                ((1.0f - frac) * \\\n                SIFT3D_IM_GET_VOX(src, idx_lo[0], idx_lo[1], idx_lo[2], c) + \\\n                frac * \\\n                SIFT3D_IM_GET_VOX(src, idx_hi[0], idx_hi[1], idx_hi[2], c)); \\\n}\n\n\t// First pass: process the interior\n#pragma omp parallel for private(x) private(y) private(c)\n\tSIFT3D_IM_LOOP_LIMITED_START_C(dst, x, y, z, c, start[0], end[0], \n                start[1], end[1], start[2], end[2])\n\n                float coords[] = { x, y, z };\n\n                for (d = -half_width; d <= half_width; d++) {\n\n                        const float tap = f->kernel[d + half_width];\n                        const float step = d * unit_factor;\n\n                        // Adjust the sampling coordinates\n                        coords[dim] -= step;\n\n                        // Sample\n                        SAMP_AND_ACC(src, dst, tap, coords, c);\n\n                        // Reset the sampling coordinates\n                        coords[dim] += step;\n                }\n\n\tSIFT3D_IM_LOOP_END_C\n\n        // Second pass: process the boundaries\n#pragma omp parallel for private(x) private(y) private(c)\n        SIFT3D_IM_LOOP_START_C(dst, x, y, z, c)\n\n                const int i_coords[] = { x, y, z };\n\n                // Skip pixels we have already processed\n                if (i_coords[dim] >= start[dim] && i_coords[dim] <= end[dim]) \n                        continue;\n\n                // Process the boundary pixel\n                for (d = -half_width; d <= half_width; d++) {\n\n                        float coords[] = { x, y, z };\n                        const float tap = f->kernel[d + half_width];\n                        const float step = d * unit_factor;\n\n                        // Adjust the sampling coordinates\n                        coords[dim] -= step;\n\n                        // Mirror coordinates\n                        if ((int) coords[dim] < 0) {\n                                coords[dim] = -coords[dim];\n                                assert((int) coords[dim] >= 0);\n                        } else if ((int) coords[dim] >= dim_end) {\n                                coords[dim] = 2.0f * dim_end - coords[dim] -    \n                                        conv_eps;\n                                assert((int) coords[dim] < dim_end);\n                        }\n\n                        // Sample\n                        SAMP_AND_ACC(src, dst, tap, coords, c);\n                }\n\n\tSIFT3D_IM_LOOP_END_C \n\n#undef SAMP_AND_ACC\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Same as convolve_sep, but with OpenCL acceleration. This does NOT\n * read back the results to C-accessible data. Use im_read_back for that. */\nSIFT3D_IGNORE_UNUSED\nstatic int convolve_sep_cl(const Image * const src, Image * const dst,\n\t\t\t   const Sep_FIR_filter * const f, const int dim,\n                           const double unit)\n{\n#ifdef SIFT3D_USE_OPENCL\n\tcl_kernel kernel;\n\tcl_int dx, dy, dz, err;\n\n\tconst size_t global_work_size[] = { src->nx, src->ny, src->nz };\n\n\t// Resize the output, with the default stride\n        if (im_copy_dims(src, dst))\n                return SIFT3D_FAILURE;\n\tim_default_stride(dst);\n\tif (im_resize(dst))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Do not have a 2D kernel right now\n\tif (dim != 3) {\n\t\tprintf(\"convolve_sep_cl: unsupported dimension: %d \\n\", dim);\n\treturn SIFT3D_FAILURE}\n\n\t// Form the dimension offsets\n\tdx = dy = dz = 0;\n\tswitch (dim) {\n\tcase 0:\n\t\tdx = 1;\n\t\tbreak;\n\tcase 1:\n\t\tdy = 1;\n\t\tbreak;\n\tcase 2:\n\t\tdz = 1;\n\t\tbreak;\n\tdefault:\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n\tkernel = f->cl_apply_unrolled;\n\tim_set_kernel_arg(kernel, 0, src);\n\tim_set_kernel_arg(kernel, 1, dst);\n\terr = clSetKernelArg(kernel, 2, sizeof(cl_int), &dx);\n\terr |= clSetKernelArg(kernel, 3, sizeof(cl_int), &dy);\n\terr |= clSetKernelArg(kernel, 4, sizeof(cl_int), &dz);\n\tcheck_cl_error(err, \"convolve_sep_cl: set kernel arg\");\n\n\terr =\n\t    clEnqueueNDRangeKernel(cl_data.queues[0], kernel, dim, NULL,\n\t\t\t\t   global_work_size, NULL, 0, NULL, NULL);\n\treturn (int)err;\n#else\n\tprintf(\"colvolve_sep_cl: This version was not compiled with OpenCL!\\n\");\n\treturn SIFT3D_FAILURE;\n#endif\n}\n\n /* Convolve_sep for symmetric filters. */\nstatic int convolve_sep_sym(const Image * const src, Image * const dst,\n\t\t\t    const Sep_FIR_filter * const f, const int dim,\n                            const double unit)\n{\n\n\t// TODO: Symmetry-specific function\n\treturn convolve_sep_gen(src, dst, f, dim, unit);\n}\n\n/* Permute the dimensions of an image.\n *\n * Arguments: \n * src - input image (initialized)\n * dim1 - input permutation dimension (x = 0, y = 1, z = 2)\n * dim2 - output permutation dimension (x = 0, y = 1, z = 2)\n * dst - output image (initialized)\n * \n * example:\n * im_permute(src, dst, 0, 1) -- permute x with y in src\n *                              and save to dst\n */\nint im_permute(const Image * const src, const int dim1, const int dim2,\n\t\t Image * const dst)\n{\n\tregister int x, y, z, c;\n\n\t// Verify inputs\n\tif (dim1 < 0 || dim2 < 0 || dim1 > 3 || dim2 > 3) {\n\t\tprintf(\"im_permute: invalid dimensions: dim1 %d dim2 %d \\n\",\n\t\t       dim1, dim2);\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n\t// Check for the trivial case\n\tif (dim1 == dim2) {\n\t\treturn im_copy_data(src, dst);\n        }\n\n        // Permute the units\n        memcpy(SIFT3D_IM_GET_UNITS(dst), SIFT3D_IM_GET_UNITS(src), \n                IM_NDIMS * sizeof(double));\n        SIFT3D_IM_GET_UNITS(dst)[dim1] = SIFT3D_IM_GET_UNITS(src)[dim2];\n        SIFT3D_IM_GET_UNITS(dst)[dim2] = SIFT3D_IM_GET_UNITS(src)[dim1];\n\n\t// Resize the output\n\tmemcpy(SIFT3D_IM_GET_DIMS(dst), SIFT3D_IM_GET_DIMS(src), \n                IM_NDIMS * sizeof(int));\n        SIFT3D_IM_GET_DIMS(dst)[dim1] = SIFT3D_IM_GET_DIMS(src)[dim2];\n        SIFT3D_IM_GET_DIMS(dst)[dim2] = SIFT3D_IM_GET_DIMS(src)[dim1];\n\tdst->nc = src->nc;\n\tim_default_stride(dst);\n\tif (im_resize(dst))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Transpose the data\n\tSIFT3D_IM_LOOP_START_C(dst, x, y, z, c)\n\n                int src_coords[] = {x, y, z};\n                int temp;\n\n                // Permute the coordinates\n                temp = src_coords[dim1];\n                src_coords[dim1] = src_coords[dim2];\n                src_coords[dim2] = temp;\n\n                // Copy the datum\n                SIFT3D_IM_GET_VOX(dst, x, y, z, c) = SIFT3D_IM_GET_VOX(src, \n                        src_coords[0], src_coords[1], src_coords[2], c);\n\n\tSIFT3D_IM_LOOP_END_C \n\n        return SIFT3D_SUCCESS;\n}\n\n/* Change an image's stride, preserving its data. \n *\n * Parameters:\n *  -src: The source image. Must be initialized\n *  -strides: an array of length IM_NDIMS specifying the new strides\n *  -dst: The destination image. Must be initialized\n * \n * Return: SIFT3D_SUCCESS (0) on success, nonzero otherwise. */\nint im_restride(const Image * const src, const size_t *const strides,\n\t\tImage * const dst)\n{\n\n\tint x, y, z, c;\n\n\t// Resize the output\n\tmemcpy(SIFT3D_IM_GET_DIMS(dst), SIFT3D_IM_GET_DIMS(src), \n                IM_NDIMS * sizeof(int));\n\tmemcpy(SIFT3D_IM_GET_STRIDES(dst), strides, IM_NDIMS * sizeof(size_t));\n\tdst->nc = src->nc;\n\tif (im_resize(dst))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Copy the data\n\tSIFT3D_IM_LOOP_START_C(dst, x, y, z, c)\n\t    SIFT3D_IM_GET_VOX(dst, x, y, z, c) =\n\t    SIFT3D_IM_GET_VOX(src, x, y, z, c);\n\tSIFT3D_IM_LOOP_END_C \n\n        return SIFT3D_SUCCESS;\n}\n\n/* Initializes a tform to ensure memory safety. \n * Either this or the type-specific version must be called prior to using\n * a tform. */\nint init_tform(void *const tform, const tform_type type)\n{\n\n\tswitch (type) {\n\tcase TPS:\n\t\tputs(\"init_tform: TPS not yet implemented \\n\");\n\t\treturn SIFT3D_FAILURE;\n\tcase AFFINE:\n\t\tif (init_Affine((Affine *) tform, IM_NDIMS))\n\t\t\treturn SIFT3D_FAILURE;\n\t\tbreak;\n\tdefault:\n\t\tputs(\"init_tform: unrecognized type \\n\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Initialize an Affine struct. This initializes\n * all fields, and allocates memory for the inner\n * matrix, initializing it to zero. */\nint init_Affine(Affine * const affine, const int dim)\n{\n\n\t// Verify inputs\n\tif (dim < 2)\n\t\treturn SIFT3D_FAILURE;\n\n\t// Initialize the type\n\taffine->tform.type = AFFINE;\n\n\t// Initialize the vtable\n\taffine->tform.vtable = &Affine_vtable;\n\n\t// Initialize the matrix\n\tif (init_Mat_rm(&affine->A, dim, dim + 1, SIFT3D_DOUBLE, SIFT3D_TRUE))\n\t\treturn SIFT3D_FAILURE;\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Deep copy of a tform. Both src and dst must be initialized. */\nint copy_tform(const void *const src, void *const dst)\n{\n\treturn TFORM_GET_VTABLE(src)->copy(src, dst);\n}\n\n/* Deep copy of one Affine to another. Both must be initialized. */\nstatic int copy_Affine(const void *const src, void *const dst)\n{\n\n\tconst Affine *const srcAff = src;\n\tAffine *const dstAff = dst;\n\n\treturn Affine_set_mat(&srcAff->A, dstAff);\n}\n\n/* Deep copy of one TPS to another. Both must be initialized. */\nstatic int copy_Tps(const void *const src, void *const dst)\n{\n\tSIFT3D_ERR(\"copy_Tps has not yet been implemented!\");\n\treturn SIFT3D_FAILURE;\n}\n\n/* Set an Affine transform to the given matrix.\n * mat is copied. mat must be an n x (n + 1) matrix, where\n * n is the dimensionality of the transformation. */\nint Affine_set_mat(const Mat_rm * const mat, Affine * const affine)\n{\n\n\t// Verify inputs\n\tif (mat->num_cols != mat->num_rows + 1 || mat->num_rows < 2)\n\t\treturn SIFT3D_FAILURE;\n\n\treturn convert_Mat_rm(mat, &affine->A, SIFT3D_DOUBLE);\n}\n\n/* Apply an arbitrary transformation to an [x, y, z] triple. */\nvoid apply_tform_xyz(const void *const tform, const double x_in, \n                     const double y_in, const double z_in, double *const x_out,\n\t\t     double *const y_out, double *const z_out)\n{\n\tTFORM_GET_VTABLE(tform)->apply_xyz(tform, x_in, y_in, z_in,\n\t\t\t\t\t   x_out, y_out, z_out);\n}\n\n/* Apply an Affine transformation to an [x, y, z] triple. */\nstatic void apply_Affine_xyz(const void *const affine, const double x_in,\n\t\t\t     const double y_in, const double z_in,\n\t\t\t     double *const x_out, double *const y_out,\n\t\t\t     double *const z_out)\n{\n\n\tconst Affine *const aff = affine;\n\n\tconst Mat_rm *const A = &aff->A;\n\tassert(AFFINE_GET_DIM(aff) == 3);\n\t*x_out = SIFT3D_MAT_RM_GET(A, 0, 0, double) * x_in +\n\t    SIFT3D_MAT_RM_GET(A, 0, 1, double) * y_in +\n\t    SIFT3D_MAT_RM_GET(A, 0, 2, double) * z_in +\n\t    SIFT3D_MAT_RM_GET(A, 0, 3, double);\n\t*y_out = SIFT3D_MAT_RM_GET(A, 1, 0, double) * x_in +\n\t    SIFT3D_MAT_RM_GET(A, 1, 1, double) * y_in +\n\t    SIFT3D_MAT_RM_GET(A, 1, 2, double) * z_in +\n\t    SIFT3D_MAT_RM_GET(A, 1, 3, double);\n\t*z_out = SIFT3D_MAT_RM_GET(A, 2, 0, double) * x_in +\n\t    SIFT3D_MAT_RM_GET(A, 2, 1, double) * y_in +\n\t    SIFT3D_MAT_RM_GET(A, 2, 2, double) * z_in +\n\t    SIFT3D_MAT_RM_GET(A, 2, 3, double);\n}\n\n/* Apply a thin-plate spline transformation to an [x, y, z] triple. */\nstatic void apply_Tps_xyz(const void *const tps, const double x_in,\n\t\t\t  const double y_in, const double z_in,\n\t\t\t  double *const x_out, double *const y_out,\n\t\t\t  double *const z_out)\n{\n\n\tconst Tps *const t = tps;\n\n\tconst Mat_rm *const params = &t->params;\n\tconst Mat_rm *const kp_src = &t->kp_src;\n\tassert(t->dim == 3);\n\tint n;\n\tint ctrl_pts = kp_src->num_rows;\t//number of control points\n\tdouble x_c, y_c, z_c, r_sq, U;\n\tdouble temp_x = 0.0, temp_y = 0.0, temp_z = 0.0;\n\n\tfor (n = 0; n < ctrl_pts; n++) {\n\t\tx_c = SIFT3D_MAT_RM_GET(kp_src, n, 0, double);\n\t\ty_c = SIFT3D_MAT_RM_GET(kp_src, n, 1, double);\n\t\tz_c = SIFT3D_MAT_RM_GET(kp_src, n, 2, double);\n\t\tr_sq =\n\t\t    (x_in - x_c) * (x_in - x_c) + (y_in - y_c) * (y_in - y_c) +\n\t\t    (z_in - z_c) * (z_in - z_c);\n\t\tif (r_sq == 0) {\n\t\t\tU = 0.0;\n\t\t} else {\n\t\t\tU = r_sq * log(r_sq);\n\t\t}\n\t\ttemp_x += U * SIFT3D_MAT_RM_GET(params, 0, n, double);\n\t\ttemp_y += U * SIFT3D_MAT_RM_GET(params, 1, n, double);\n\t\ttemp_z += U * SIFT3D_MAT_RM_GET(params, 2, n, double);\n\t}\n\n\ttemp_x += SIFT3D_MAT_RM_GET(params, 0, ctrl_pts, double);\n\ttemp_x += SIFT3D_MAT_RM_GET(params, 0, ctrl_pts + 1, double) * x_in;\n\ttemp_x += SIFT3D_MAT_RM_GET(params, 0, ctrl_pts + 2, double) * y_in;\n\ttemp_x += SIFT3D_MAT_RM_GET(params, 0, ctrl_pts + 3, double) * z_in;\n\n\ttemp_y += SIFT3D_MAT_RM_GET(params, 1, ctrl_pts, double);\n\ttemp_y += SIFT3D_MAT_RM_GET(params, 1, ctrl_pts + 1, double) * x_in;\n\ttemp_y += SIFT3D_MAT_RM_GET(params, 1, ctrl_pts + 2, double) * y_in;\n\ttemp_y += SIFT3D_MAT_RM_GET(params, 1, ctrl_pts + 3, double) * z_in;\n\n\ttemp_z += SIFT3D_MAT_RM_GET(params, 2, ctrl_pts, double);\n\ttemp_z += SIFT3D_MAT_RM_GET(params, 2, ctrl_pts + 1, double) * x_in;\n\ttemp_z += SIFT3D_MAT_RM_GET(params, 2, ctrl_pts + 2, double) * y_in;\n\ttemp_z += SIFT3D_MAT_RM_GET(params, 2, ctrl_pts + 3, double) * z_in;\n\n\t//Save results\n\tx_out[0] = temp_x;\n\ty_out[0] = temp_y;\n\tz_out[0] = temp_z;\n\n}\n\n/* Apply an arbitrary transform to a matrix. See apply_Affine_Mat_rm for\n * matrix formats. */\nint apply_tform_Mat_rm(const void *const tform, const Mat_rm * const mat_in,\n\t\t       Mat_rm * const mat_out)\n{\n\treturn TFORM_GET_VTABLE(tform)->apply_Mat_rm(tform, mat_in, mat_out);\n}\n\n/* Apply a spline transformation to a matrix, \n * by multiplication. See apply_Affine_Mat_rm for format of input matrices\n *\n * All matrices must be initialized with init_Mat_rm prior to use. For 3D!*/\nstatic int apply_Tps_Mat_rm(const void *const tps, const Mat_rm * const mat_in,\n\t\t\t    Mat_rm * const mat_out)\n{\n\n\tconst Tps *const t = tps;\n\n\t//Spline transformation matrix is dim * [number of chosen points+dim+1]\n\t//sp_src is [number of chosen points] * dim\n\tconst Mat_rm *const params = &(t->params);\n\tconst Mat_rm *const kp_src = &(t->kp_src);\n\n\tint num_pts = mat_in->num_cols;\t//number of points to be transformed\n\tint ctrl_pts = kp_src->num_rows;\t//number of control points\n\tint m, n, q;\n\tdouble temp, x, y, z, r_sq, x_c, y_c, z_c;\n\tdouble U[ctrl_pts];\n\t//for each point\n\tfor (q = 0; q < num_pts; q++) {\n\t\t//extract the coordinates\n\t\tx = SIFT3D_MAT_RM_GET(mat_in, 0, q, double);\n\t\ty = SIFT3D_MAT_RM_GET(mat_in, 1, q, double);\n\t\tz = SIFT3D_MAT_RM_GET(mat_in, 2, q, double);\n\t\t//Calculate U function for each control point\n\t\tfor (n = 0; n < ctrl_pts; n++) {\n\t\t\tx_c = SIFT3D_MAT_RM_GET(kp_src, n, 0, double);\n\t\t\ty_c = SIFT3D_MAT_RM_GET(kp_src, n, 1, double);\n\t\t\tz_c = SIFT3D_MAT_RM_GET(kp_src, n, 2, double);\n\t\t\tr_sq =\n\t\t\t    (x - x_c) * (x - x_c) + (y - y_c) * (y - y_c) + (z -\n\t\t\t\t\t\t\t\t\t     z_c)\n\t\t\t    * (z - z_c);\n\t\t\tif (r_sq == 0) {\n\t\t\t\tU[n] = 0.0;\n\t\t\t} else {\n\t\t\t\tU[n] = r_sq * log(r_sq);\n\t\t\t}\n\t\t}\n\t\t//for each dimension\n\t\tfor (m = 0; m < 3; m++) {\t//For 3D!\n\t\t\ttemp = 0.0;\n\t\t\tfor (n = 0; n < ctrl_pts; n++) {\n\t\t\t\ttemp +=\n\t\t\t\t    U[n] * SIFT3D_MAT_RM_GET(params, m, n,\n\t\t\t\t\t\t\t     double);\n\t\t\t}\n\t\t\ttemp += SIFT3D_MAT_RM_GET(params, m, ctrl_pts, double);\n\t\t\ttemp +=\n\t\t\t    SIFT3D_MAT_RM_GET(params, m, ctrl_pts + 1,\n\t\t\t\t\t      double) * x;\n\t\t\ttemp +=\n\t\t\t    SIFT3D_MAT_RM_GET(params, m, ctrl_pts + 2,\n\t\t\t\t\t      double) * y;\n\t\t\ttemp +=\n\t\t\t    SIFT3D_MAT_RM_GET(params, m, ctrl_pts + 3,\n\t\t\t\t\t      double) * z;\n\n\t\t\t//Store results\n\t\t\tSIFT3D_MAT_RM_GET(mat_out, m, q, double) = temp;\n\t\t}\n\n\t}\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Get the type of a tform. */\ntform_type tform_get_type(const void *const tform)\n{\n\treturn ((Affine *) tform)->tform.type;\n}\n\n/* Get the size of a tform. */\nsize_t tform_get_size(const void *const tform)\n{\n\treturn TFORM_GET_VTABLE(tform)->get_size();\n}\n\n/* Get the size of a type of tform. */\nsize_t tform_type_get_size(const tform_type type)\n{\n\tswitch (type) {\n\tcase AFFINE:\n\t\treturn Affine_vtable.get_size();\n\tcase TPS:\n\t\treturn Tps_vtable.get_size();\n\tdefault:\n\t\tSIFT3D_ERR(\"tform_type_get_size: unrecognized \" \"type \\n\");\n\t\treturn 0;\n\t}\n}\n\n/* Returns the size of an Affine struct */\nstatic size_t Affine_get_size(void)\n{\n\treturn sizeof(Affine);\n}\n\n/* Returns the size of a Tps struct */\nstatic size_t Tps_get_size(void)\n{\n\treturn sizeof(Tps);\n}\n\n/* Write a tform to a file. */\nint write_tform(const char *path, const void *const tform)\n{\n\treturn TFORM_GET_VTABLE(tform)->write(path, tform);\n}\n\n/* Write an affine transformation to a file. */\nstatic int write_Affine(const char *path, const void *const tform)\n{\n\n\tconst Affine *const affine = tform;\n\n\treturn write_Mat_rm(path, &affine->A);\n}\n\n/* Write a thin-plate spline transformation to a file. */\nstatic int write_Tps(const char *path, const void *const tform)\n{\nSIFT3D_IGNORE_UNUSED\n\tconst Tps *const tps = tform;\n\n\tSIFT3D_ERR(\"write_Tps: this function has not yet been implemented.\");\n\treturn SIFT3D_FAILURE;\n}\n\n/* Free the memory associated with a tform */\nvoid cleanup_tform(void *const tform)\n{\n\tTFORM_GET_VTABLE(tform)->cleanup(tform);\n}\n\n/* Free the memory associated with an Affine transformation. */\nstatic void cleanup_Affine(void *const affine)\n{\n\n\tAffine *const aff = affine;\n\n\tcleanup_Mat_rm(&aff->A);\n}\n\n/* Free the memory assocaited with a thin-plate spline. */\nstatic void cleanup_Tps(void *const tps)\n{\n\n\tTps *const t = tps;\n\n\tcleanup_Mat_rm(&t->params);\n\tcleanup_Mat_rm(&t->kp_src);\n}\n\n/* Apply an Affine transformation to a matrix, by multiplication. The format\n * of Mat_in should be:\n * [x1 x2 ... xN\n *  y1 y2 ... yN\n *      ...\n *  w1 w2 ... wN\n *  1  1  ... 1] \n * \n * mat_out will be resized to the appropriate size. The format will be:\n * [x1' x2' ... xN'\n *  y1' y2' ... yN'\n *      ...\n *  w1' w2' ... wN'] \n *\n * All matrices must be initialized with init_Mat_rm prior to use. */\nstatic int apply_Affine_Mat_rm(const void *const affine, \n        const Mat_rm * const mat_in, Mat_rm * const mat_out)\n{\n\n\tconst Affine *const aff = affine;\n\n\treturn mul_Mat_rm(&aff->A, mat_in, mat_out);\n}\n\n/* Computes mat_in1 * mat_in2 = mat_out. mat_out will be resized\n * the appropriate size.\n *\n * All matrices must be initialized with init_Mat_rm prior to use. */\nint mul_Mat_rm(const Mat_rm * const mat_in1, const Mat_rm * const mat_in2,\n\t       Mat_rm * const mat_out)\n{\n\n\tint i, j, k;\n\n\t// Verify inputs\n\tif (mat_in1->num_cols != mat_in2->num_rows ||\n\t    mat_in1->type != mat_in2->type)\n\t\treturn SIFT3D_FAILURE;\n\n\t// Resize mat_out\n\tmat_out->type = mat_in1->type;\n\tmat_out->num_rows = mat_in1->num_rows;\n\tmat_out->num_cols = mat_in2->num_cols;\n\tif (resize_Mat_rm(mat_out))\n\t\treturn SIFT3D_FAILURE;\n\n#define MAT_RM_MULTIPLY(type) \\\n    SIFT3D_MAT_RM_LOOP_START(mat_out, i, j) \\\n    type acc = 0; \\\n    for (k = 0; k < mat_in1->num_cols; k++) { \\\n    acc += SIFT3D_MAT_RM_GET(mat_in1, i, k, type) * \\\n    SIFT3D_MAT_RM_GET(mat_in2, k, j, type); \\\n    } \\\n    SIFT3D_MAT_RM_GET(mat_out, i, j, type) = acc; \\\n    SIFT3D_MAT_RM_LOOP_END\n\n\t// Row-major multiply\n\tswitch (mat_out->type) {\n\tcase SIFT3D_DOUBLE:\n\t\tMAT_RM_MULTIPLY(double) break;\n\tcase SIFT3D_FLOAT:\n\t\tMAT_RM_MULTIPLY(float) break;\n\tcase SIFT3D_INT:\n\t\tMAT_RM_MULTIPLY(int) break;\n\tdefault:\n\t\tputs(\"mul_Mat_rm: unknown type \\n\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n#undef MAT_RM_MULTIPLY\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Computes the eigendecomposition of a real symmetric matrix, \n * A = Q * diag(L) * Q', where Q is a real orthogonal matrix and L is a real \n * diagonal matrix.\n *\n * A must be an [nxn] matrix. Q is [nxm], where m is in the interval [1, n],\n * depending on the values of A. L is [nx1], where the first m elements are\n * sorted in ascending order. The remaining n - m elements are zero. \n * \n * If Q is NULL, the eigenvectors will not be computed.\n *\n * The eigendecomposition is computed by divide and conquer.\n * \n * This function resizes all non-null outputs and sets their type to double.\n *\n * This function does not ensure that A is symmetric.\n *\n * All matrices must be initialized prior to calling this funciton.\n * All matrices must have type double.\n *\n * Note: This function computes all of the eigenvalues, to a high degree of \n * accuracy. A faster implementation is possible if you do not need high\n * precision, or if you do not need all of the eigenvalues, or if you do not \n * need eigenvalues outside of some interval. \n */\nint eigen_Mat_rm(Mat_rm * A, Mat_rm * Q, Mat_rm * L)\n{\n\n\tMat_rm A_trans;\n\tdouble *work;\n\tfortran_int *iwork;\n\tdouble lwork_ret;\n\tfortran_int info, lwork, liwork;\n\n\tconst char jobz = Q == NULL ? 'N' : 'V';\n\tconst char uplo = 'U';\n\tconst fortran_int n = A->num_cols;\n\tconst fortran_int lda = n;\n\tconst fortran_int lwork_query = -1;\n\tconst fortran_int liwork_query = -1;\n\n\t// Verify inputs\n\tif (A->num_rows != n) {\n\t\tputs(\"eigen_Mat_rm: A be square \\n\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n\tif (A->type != SIFT3D_DOUBLE) {\n\t\tputs(\"eigen_Mat_rm: A must have type double \\n\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n\t// Resize outputs\n\tL->num_rows = n;\n\tL->num_cols = 1;\n\tL->type = SIFT3D_DOUBLE;\n\tif (resize_Mat_rm(L))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Initialize intermediate matrices and buffers\n\twork = NULL;\n\tiwork = NULL;\n\tif (init_Mat_rm(&A_trans, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE))\n\t\tgoto EIGEN_MAT_RM_QUIT;\n\n\t// Copy the input matrix (A = A')\n\tif (copy_Mat_rm(A, &A_trans))\n\t\tgoto EIGEN_MAT_RM_QUIT;\n\n\t// Query for the workspace sizes\n\tdsyevd_(&jobz, &uplo, &n, A_trans.u.data_double, &lda, L->u.data_double,\n\t\t&lwork_ret, &lwork_query, &liwork, &liwork_query, &info);\n\n\tif ((int32_t) info) {\n\t\tprintf\n\t\t    (\"eigen_Mat_rm: LAPACK dsyevd workspace query error code %d\",\n\t\t     info);\n\t\tgoto EIGEN_MAT_RM_QUIT;\n\t}\n\t// Allocate work spaces \n\tlwork = (fortran_int) lwork_ret;\n\tif ((work = (double *)malloc(lwork * sizeof(double))) == NULL ||\n\t    (iwork =\n\t     (fortran_int *) malloc(liwork * sizeof(fortran_int))) == NULL)\n\t\tgoto EIGEN_MAT_RM_QUIT;\n\n\t// Compute the eigendecomposition\n\tdsyevd_(&jobz, &uplo, &n, A_trans.u.data_double, &lda, L->u.data_double,\n\t\twork, &lwork, iwork, &liwork, &info);\n\n\tif ((int32_t) info) {\n\t\tprintf(\"eigen_Mat_rm: LAPACK dsyevd error code %d\", (int) info);\n\t\tgoto EIGEN_MAT_RM_QUIT;\n\t}\n\t// Optionally return the eigenvectors\n\tif (Q != NULL && transpose_Mat_rm(&A_trans, Q))\n\t\tgoto EIGEN_MAT_RM_QUIT;\n\n\tfree(work);\n\tfree(iwork);\n\tcleanup_Mat_rm(&A_trans);\n\treturn SIFT3D_SUCCESS;\n\n EIGEN_MAT_RM_QUIT:\n\tif (work != NULL)\n\t\tfree(work);\n\tif (iwork != NULL)\n\t\tfree(iwork);\n\tcleanup_Mat_rm(&A_trans);\n\treturn SIFT3D_FAILURE;\n}\n\n/* Solves the system AX=B exactly. A must be a square matrix.\n * This function first computes the reciprocal condition number of A.\n * If it is below the parameter \"limit\", it returns SIFT3D_SINGULAR. If limit \n * is less than 0, a default value of 100 * eps is used.\n *\n * The system is solved by LU decomposition.\n * \n * This function returns an error if A and B do not have valid dimensions. \n * This function resizes X to [nx1] and changes the type to match B. \n * All matrices must be initialized prior to calling this function.\n * All matrices must have type double.\n */\nint solve_Mat_rm(const Mat_rm *const A, const Mat_rm *const B, \n        const double limit, Mat_rm *const X)\n{\n\n\tMat_rm A_trans, B_trans;\n\tdouble *work;\n\tfortran_int *ipiv, *iwork;\n\tdouble limit_arg, anorm, rcond;\n\tfortran_int info;\n\n\tconst fortran_int m = A->num_rows;\n\tconst fortran_int n = A->num_cols;\n\tconst fortran_int nrhs = B->num_cols;\n\tconst fortran_int lda = m;\n\tconst fortran_int ldb = B->num_rows;\n\tconst char norm_type = '1';\n\tconst char trans = 'N';\n\n\t// Default parameters\n\tif (limit < 0)\n\t\tlimit_arg = 100.0 * DBL_EPSILON;\n\n\t// Verify inputs\n\tif (m != n || ldb != m) {\n\t\tputs(\"solve_Mat_rm: invalid dimensions! \\n\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n\tif (A->type != SIFT3D_DOUBLE || B->type != SIFT3D_DOUBLE) {\n\t\tputs(\"solve_mat_rm: All matrices must have type double \\n\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n\t// Initialize intermediate matrices and buffers\n\tipiv = NULL;\n\twork = NULL;\n\tiwork = NULL;\n\tif (init_Mat_rm(&A_trans, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE) ||\n\t    init_Mat_rm(&B_trans, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE) ||\n\t    (work = (double *)malloc(n * 4 * sizeof(double))) == NULL ||\n\t    (iwork = (fortran_int *) malloc(n * sizeof(fortran_int))) == NULL ||\n\t    (ipiv = (fortran_int *) calloc(m, sizeof(fortran_int))) == NULL)\n\t\tgoto SOLVE_MAT_RM_QUIT;\n\n\t// Transpose matrices for LAPACK\n\tif (transpose_Mat_rm(A, &A_trans) || transpose_Mat_rm(B, &B_trans))\n\t\tgoto SOLVE_MAT_RM_QUIT;\n\n\t// Compute the L1-norm of A\n\tanorm = dlange_(&norm_type, &m, &n, A_trans.u.data_double, &lda, work);\n\n\t// Compute the LU decomposition of A in place\n\tdgetrf_(&m, &n, A_trans.u.data_double, &lda, ipiv, &info);\n\tif ((int32_t) info < 0) {\n\t\tprintf(\"solve_Mat_rm: LAPACK dgetrf error code %d \\n\", info);\n\t\tgoto SOLVE_MAT_RM_QUIT;\n\t} else if ((int32_t) info > 0) {\n\t\tgoto SOLVE_MAT_RM_SINGULAR;\n\t}\n\t// Compute the reciprocal condition number of A\n\tdgecon_(&norm_type, &n, A_trans.u.data_double, &lda, &anorm, &rcond,\n\t\twork, iwork, &info);\n\tif ((int32_t) info) {\n\t\tprintf(\"solve_Mat_rm: LAPACK dgecon error code %d \\n\", info);\n\t\tgoto SOLVE_MAT_RM_QUIT;\n\t}\n\t// Return if A is singular\n\tif (rcond < limit_arg)\n\t\tgoto SOLVE_MAT_RM_SINGULAR;\n\n\t// Solve the system \n\tdgetrs_(&trans, &n, &nrhs, A_trans.u.data_double, &lda, ipiv,\n\t\tB_trans.u.data_double, &ldb, &info);\n\n\t// Check for errors\n\tif ((int32_t) info) {\n\t\tprintf(\"solve_Mat_rm: LAPACK dgetrs error code %d \\n\", info);\n\t\tgoto SOLVE_MAT_RM_QUIT;\n\t}\n\t// Transpose results\n\tif (transpose_Mat_rm(&B_trans, X))\n\t\tgoto SOLVE_MAT_RM_QUIT;\n\n\tfree(ipiv);\n\tfree(work);\n\tfree(iwork);\n\tcleanup_Mat_rm(&A_trans);\n\tcleanup_Mat_rm(&B_trans);\n\treturn SIFT3D_SUCCESS;\n\n SOLVE_MAT_RM_SINGULAR:\n\tfree(ipiv);\n\tfree(work);\n\tfree(iwork);\n\tcleanup_Mat_rm(&A_trans);\n\tcleanup_Mat_rm(&B_trans);\n\treturn SIFT3D_SINGULAR;\n\n SOLVE_MAT_RM_QUIT:\n\tif (ipiv != NULL)\n\t\tfree(ipiv);\n\tif (work != NULL)\n\t\tfree(work);\n\tif (iwork != NULL)\n\t\tfree(iwork);\n\tcleanup_Mat_rm(&A_trans);\n\tcleanup_Mat_rm(&B_trans);\n\treturn SIFT3D_FAILURE;\n}\n\n/* Solves the system AX=B by least-squares.\n *\n * A least-norm solution is computed using the singular \n * value decomposition. A need not be full-rank.\n * \n * This function returns an error if A and B do not have valid dimensions. \n * This function resizes X to [nx1] and changes the type to match B. \n * All matrices must be initialized prior to calling this funciton.\n * All matrices must have type double.\n */\nint solve_Mat_rm_ls(const Mat_rm *const A, const Mat_rm *const B, \n        Mat_rm *const X)\n{\n\n\tMat_rm A_trans, B_trans;\n\tdouble *s, *work;\n\tdouble lwork_ret;\n\tfortran_int info, rank, lwork;\n\tint i, j;\n\n\tconst double rcond = -1;\n\tconst fortran_int m = A->num_rows;\n\tconst fortran_int n = A->num_cols;\n\tconst fortran_int nrhs = B->num_cols;\n\tconst fortran_int lda = m;\n\tconst fortran_int ldb = B->num_rows;\n\tconst fortran_int lwork_query = -1;\n\n\t// Verify inputs \n\tif (m != ldb) {\n\t\tputs(\"solve_Mat_rm_ls: invalid dimensions \\n\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n\tif (A->type != SIFT3D_DOUBLE || B->type != SIFT3D_DOUBLE) {\n\t\tputs(\"solve_mat_rm_ls: All matrices must have type double \\n\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n\t// Resize the output \n\tX->type = SIFT3D_DOUBLE;\n\tX->num_rows = A->num_cols;\n\tX->num_cols = B->num_cols;\n\tif (resize_Mat_rm(X))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Initialize intermediate matrices and buffers\n\ts = NULL;\n\twork = NULL;\n\tif (init_Mat_rm(&A_trans, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE) ||\n\t    init_Mat_rm(&B_trans, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE) ||\n\t    (s = (double *)calloc(SIFT3D_MAX(m, n), sizeof(double))) == NULL)\n\t\tgoto SOLVE_MAT_RM_LS_QUIT;\n\n\t// Transpose matrices for LAPACK\n\tif (transpose_Mat_rm(A, &A_trans) || transpose_Mat_rm(B, &B_trans))\n\t\tgoto SOLVE_MAT_RM_LS_QUIT;\n\n\t// Get the size of the workspace\n\tdgelss_(&m, &n, &nrhs, A_trans.u.data_double, &lda,\n\t\tB_trans.u.data_double, &ldb, s, &rcond, &rank, &lwork_ret,\n\t\t&lwork_query, &info);\n\tif ((int32_t) info) {\n\t\tprintf\n\t\t    (\"solve_mat_rm: LAPACK dgelss work query error code %d \\n\",\n\t\t     info);\n\t}\n\tlwork = (fortran_int) lwork_ret;\n\n\t// Allocate the workspace\n\tif ((work = (double *)malloc(lwork * sizeof(double))) == NULL)\n\t\tgoto SOLVE_MAT_RM_LS_QUIT;\n\n\t// Solve the system\n\tdgelss_(&m, &n, &nrhs, A_trans.u.data_double, &lda,\n\t\tB_trans.u.data_double, &ldb, s, &rcond, &rank, work, &lwork,\n\t\t&info);\n\tif ((int32_t) info) {\n\t\tprintf(\"solve_mat_rm: LAPACK dgelss error code %d \\n\", info);\n\t\tgoto SOLVE_MAT_RM_LS_QUIT;\n\t}\n\t// Transpose results to the new leading dimension\n\tSIFT3D_MAT_RM_LOOP_START(X, i, j)\n\t    SIFT3D_MAT_RM_GET(X, i, j, double) =\n\t    SIFT3D_MAT_RM_GET(&B_trans, j, i, double);\n\tSIFT3D_MAT_RM_LOOP_END free(s);\n\tfree(work);\n\tcleanup_Mat_rm(&A_trans);\n\tcleanup_Mat_rm(&B_trans);\n\treturn SIFT3D_SUCCESS;\n\n SOLVE_MAT_RM_LS_QUIT:\n\tif (s != NULL)\n\t\tfree(s);\n\tif (work != NULL)\n\t\tfree(work);\n\tcleanup_Mat_rm(&A_trans);\n\tcleanup_Mat_rm(&B_trans);\n\treturn SIFT3D_FAILURE;\n}\n\n/* Computes the trace of a matrix. trace is assumed to be the same type as\n * mat. Returns an error if mat is not square. \n * \n * All matrices must be initialized with init_Mat_rm prior to calling \n * this function. */\nint trace_Mat_rm(Mat_rm * mat, void *trace)\n{\n\n\tint i;\n\n\t// Verify inputs\n\tif (mat->num_rows != mat->num_cols || mat->num_rows < 1) {\n\t\treturn SIFT3D_FAILURE;\n\t}\n#define TRACE_MAT_RM(type) \\\n    {\\\n    type acc = 0; \\\n    for (i = 0; i < mat->num_rows; i++) { \\\n        acc += SIFT3D_MAT_RM_GET(mat, i, i, type); \\\n    } \\\n    *((type *) trace) = acc; \\\n    }\n\n\t// Take the trace\n\tswitch (mat->type) {\n\tcase SIFT3D_DOUBLE:\n\t\tTRACE_MAT_RM(double) break;\n\tcase SIFT3D_FLOAT:\n\t\tTRACE_MAT_RM(float) break;\n\tcase SIFT3D_INT:\n\t\tTRACE_MAT_RM(int) break;\n\tdefault:\n\t\tputs(\"trace_Mat_rm: unknown type \\n\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n#undef TRACE_MAT_RM\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Tranposes a matrix. Resizes dst with the type of src. \n * All matrices must be initialized prior to calling this function. */\nint transpose_Mat_rm(const Mat_rm *const src, Mat_rm *const dst)\n{\n\n\tint i, j;\n\n\t// Verify inputs\n\tif (src->num_rows < 1 || src->num_cols < 1)\n\t\treturn SIFT3D_FAILURE;\n\n\t// Resize the output\n\tdst->type = src->type;\n\tdst->num_rows = src->num_cols;\n\tdst->num_cols = src->num_rows;\n\tif (resize_Mat_rm(dst))\n\t\treturn SIFT3D_FAILURE;\n\n#define TRANSPOSE_MAT_RM(type) \\\n    SIFT3D_MAT_RM_LOOP_START(src, i, j) \\\n    SIFT3D_MAT_RM_GET(dst, j, i, type) = \\\n                SIFT3D_MAT_RM_GET(src, i, j, type); \\\n    SIFT3D_MAT_RM_LOOP_END\n\n\t// Transpose\n\tswitch (src->type) {\n\tcase SIFT3D_DOUBLE:\n\t\tTRANSPOSE_MAT_RM(double);\n\t\tbreak;\n\tcase SIFT3D_FLOAT:\n\t\tTRANSPOSE_MAT_RM(float);\n\t\tbreak;\n\tcase SIFT3D_INT:\n\t\tTRANSPOSE_MAT_RM(int);\n\t\tbreak;\n\tdefault:\n#ifndef NDEBUG\n\t\tputs(\"transpose_Mat_rm: unknown type \\n\");\n#endif\n\t\treturn SIFT3D_FAILURE;\n\t}\n#undef TRANSPOSE_MAT_RM\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Computes the determinant of a symmetric matrix. det is assumed to be the \n * same type as mat. Returns an error if mat is not square. \n * \n * This function does not verify that mat is symmetric.\n * \n * All matrices must be initialized with init_Mat_rm prior to calling \n * this function. */\nint det_symm_Mat_rm(Mat_rm * mat, void *det)\n{\n\n\tMat_rm matd, L;\n\tdouble detd;\n\tint i, j;\n\n\tconst int n = mat->num_cols;\n\n\t// Verify inputs\n\tif (n < 1 || mat->num_rows != n) {\n\t\tputs(\"det_symm_Mat_rm: invalid dimensions \\n\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n\t// Initialize intermediates\n\tif (init_Mat_rm(&matd, 0, 0, mat->type, SIFT3D_FALSE) ||\n\t    init_Mat_rm(&L, n, 1, SIFT3D_DOUBLE, SIFT3D_FALSE))\n\t\tgoto DET_SYMM_QUIT;\n\n\t// Convert the matrix to type double\n\tif (convert_Mat_rm(mat, &matd, SIFT3D_DOUBLE))\n\t\tgoto DET_SYMM_QUIT;\n\n\t// Get the eigendecomposition with LAPACK\n\tif (eigen_Mat_rm(&matd, NULL, &L))\n\t\tgoto DET_SYMM_QUIT;\n\n\t// Take the determinant\n\tdetd = 0.0;\n\tSIFT3D_MAT_RM_LOOP_START(&L, i, j)\n\t    detd += SIFT3D_MAT_RM_GET(&L, i, j, double);\n\tSIFT3D_MAT_RM_LOOP_END\n\t    // Convert the output to the correct type\n\t    switch (mat->type) {\n\tcase SIFT3D_DOUBLE:\n\t\t*((double *)det) = detd;\n\t\tbreak;\n\tcase SIFT3D_FLOAT:\n\t\t*((float *)det) = (float)detd;\n\t\tbreak;\n\tcase SIFT3D_INT:\n\t\t*((int *)det) = (int)detd;\n\t\tbreak;\n\tdefault:\n\t\tputs(\"det_symm_Mat_rm: unknown type \\n\");\n\t\tgoto DET_SYMM_QUIT;\n\t}\n\n\tcleanup_Mat_rm(&matd);\n\tcleanup_Mat_rm(&L);\n\treturn SIFT3D_SUCCESS;\n\n DET_SYMM_QUIT:\n\tcleanup_Mat_rm(&matd);\n\tcleanup_Mat_rm(&L);\n\treturn SIFT3D_FAILURE;\n}\n\n/* Apply a separable filter in multiple dimensions. This function resamples the\n * input to have the same units as f, then resamples the output to the\n * original units.\n *\n * Parameters:\n *  -src: The input image.\n *  -dst: The filtered image.\n *  -f: The filter to apply.\n *  -unit: The physical units of the filter kernel. Use -1.0 for the default,\n *      which is the same units as src.\n *\n * Return: SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise. */\nint apply_Sep_FIR_filter(const Image * const src, Image * const dst,\n\t\t\t Sep_FIR_filter * const f, const double unit)\n{\n\n\tImage temp;\n\tImage *cur_src, *cur_dst;\n\tint i;\n\n        const double unit_default = -1.0;\n\n        // Verify inputs\n        if (unit < 0 && unit != unit_default) {\n                SIFT3D_ERR(\"apply_Sep_FIR_filter: invalid unit: %f, use \"\n                        \"%f for default \\n\", unit, unit_default);\n                return SIFT3D_FAILURE;\n        }\n\n        // Resize the output\n        if (im_copy_dims(src, dst))\n                return SIFT3D_FAILURE; \n\n\t// Allocate temporary storage\n\tinit_im(&temp);\n\tif (im_copy_data(src, &temp))\n\t\tgoto apply_sep_f_quit;\n\n#define SWAP_BUFFERS \\\n    if (cur_dst == &temp) { \\\n    cur_src = &temp; \\\n    cur_dst = dst; \\\n    } else { \\\n    cur_src = dst; \\\n    cur_dst = &temp; \\\n    }\n\n\t// Apply in n dimensions\n\tcur_src = (Image *) src;\n\tcur_dst = &temp;\n\tfor (i = 0; i < IM_NDIMS; i++) {\n\n                // Check for default parameters\n                const double unit_arg = unit == unit_default ?\n                        SIFT3D_IM_GET_UNITS(src)[i] : unit;\n\n#ifdef SIFT3D_USE_OPENCL\n                convolve_sep(cur_src, cur_dst, f, i, unit_arg);\n\t\tSWAP_BUFFERS\n#else\n                // Transpose so that the filter dimension is x\n                if (i != 0) {\n                        if (im_permute(cur_src, 0, i, cur_dst))\n\t\t\t\tgoto apply_sep_f_quit;\n                        SWAP_BUFFERS\n                }\n\n\t\t// Apply the filter\n\t\tconvolve_sep(cur_src, cur_dst, f, 0, unit_arg);\n\t\tSWAP_BUFFERS\n\n                // Transpose back\n                if (i != 0) {\n\t\t\tif (im_permute(cur_src, 0, i, cur_dst))\n\t\t\t\tgoto apply_sep_f_quit;\n                        SWAP_BUFFERS\n\n\t\t}\n#endif\n\t}\n\n\t// Swap back\n\tSWAP_BUFFERS;\n\n#undef SWAP_BUFFERS\n\n\t// Copy result to dst, if necessary\n\tif (cur_dst != dst && im_copy_data(cur_dst, dst))\n\t\tgoto apply_sep_f_quit;\n\n\t// Clean up\n\tim_free(&temp);\n\treturn SIFT3D_SUCCESS;\n\napply_sep_f_quit:\n\tim_free(&temp);\n\treturn SIFT3D_FAILURE;\n}\n\n/* Initialize a separable FIR filter struct with the given parameters. If OpenCL\n * support is enabled and initialized, this creates a program to apply it with\n * separable filters.  \n *\n * Note that the kernel data will be copied, so the user can free it without \n * affecting f. */\nint init_Sep_FIR_filter(Sep_FIR_filter *const f, const int dim, const int width,\n\t\t\tconst float *const kernel, const int symmetric)\n{\n\n        const size_t kernel_size = width * sizeof(float);\n\n        // Save the data\n\tf->dim = dim;\n\tf->width = width;\n\tf->symmetric = symmetric;\n\n        // Allocate the kernel memory\n        if ((f->kernel = (float *) malloc(kernel_size)) == NULL) {\n                SIFT3D_ERR(\"init_Sep_FIT_filter: out of memory! \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Copy the kernel data\n        memcpy(f->kernel, kernel, kernel_size);\n\n#ifdef SIFT3D_USE_OPENCL\n\t{\n\t\tchar src[1 << 15];\n\t\tchar *template;\n\t\tcl_program program;\n\t\tcl_int err;\n\t\tfloat k;\n\t\tint i;\n\n\t\tconst char *path = SEP_FIR_3D_PATH;\n\t\tconst int half_width = f->half_width;\n\n\t\t// Load the template\n\t\tif ((template = read_file(path)) == NULL) {\n\t\t\tprintf(\"init_Sep_FIR_Filter: error reading path %s \\n\",\n\t\t\t       path);\n\t\t\treturn SIFT3D_FAILURE;\n\t\t}\n\t\tsprintf(src, \"%s\\n\", template);\n\n\t\t// Write the unrolled kernel\n\t\tfor (i = -half_width; i < half_width; i++) {\n\t\t\tk = f->kernel[i];\n\t\t\tsprintf(src, \"acc += %.16f * \"\n\t\t\t\t\"read_imagef(src, sampler, center + d_xyz * %d); \\n\",\n\t\t\t\tk, i);\n\t\t}\n\n\t\t// Write the ending\n\t\tsprintf(src,\n\t\t\t\"write_imagef(dst, sampler, (float4) center); \\n } \\n\");\n\n\t\t// Compile the program  \n\t\tif (compile_cl_program_from_source(&program, cl_data.context,\n\t\t\t\t\t\t   cl_data.devices,\n\t\t\t\t\t\t   cl_data.num_devices,\n\t\t\t\t\t\t   (char **)&src, 1))\n\t\t\treturn SIFT3D_FAILURE;\n\t\tf->cl_apply_unrolled =\n\t\t    clCreateKernel(program, \"sep_fir_3d\", &err);\n\t\tcheck_cl_error(err, \"init_Sep_FIR_Filter: create kernel\");\n\t\tclReleaseProgram(program);\n\t}\n#endif\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Free a Sep_FIR_Filter. */\nvoid cleanup_Sep_FIR_filter(Sep_FIR_filter *const f)\n{\n\n\tif (f->kernel != NULL) {\n\t\tfree(f->kernel);\n\t\tf->kernel = NULL;\n\t}\n#ifdef SIFT3D_USE_OPENCL\n\t//TODO release OpenCL program\n#endif\n}\n\n/* Initialize the values of im so that it can be used by the\n * resize function. Does not allocate memory. */\nvoid init_im(Image *const im)\n{\n\tim->data = NULL;\n\tim->cl_valid = SIFT3D_FALSE;\n\n\tim->ux = 1;\n\tim->uy = 1;\n\tim->uz = 1;\n\n\tim->size = 0;\n\tim->s = -1.0;\n\tmemset(SIFT3D_IM_GET_DIMS(im), 0, IM_NDIMS * sizeof(int));\n\tmemset(SIFT3D_IM_GET_STRIDES(im), 0, IM_NDIMS * sizeof(size_t));\n}\n\n/* Initialize a normalized Gaussian filter, of the given sigma.\n * If SIFT3D_GAUSS_WIDTH_FCTR is defined, use that value for\n * the ratio between the width of the filter and sigma. Otherwise,\n * use the default value 3.0 \n */\n#ifndef SIFT3D_GAUSS_WIDTH_FCTR\n#define SIFT3D_GAUSS_WIDTH_FCTR 3.0\n#endif\nint init_Gauss_filter(Gauss_filter * const gauss, const double sigma,\n\t\t      const int dim)\n{\n\n\tfloat *kernel;\n\tdouble x;\n\tfloat acc;\n\tint i;\n\n\tconst int half_width = sigma > 0 ? \n                SIFT3D_MAX((int)ceil(sigma * SIFT3D_GAUSS_WIDTH_FCTR), 1) :\n                1;\n\tconst int width = 2 * half_width + 1;\n\n\t// Initialize intermediates \n\tif ((kernel = (float *) malloc(width * sizeof(float))) == NULL)\n\t\treturn SIFT3D_FAILURE;\n\n\t// Calculate coefficients\n\tacc = 0;\n\tfor (i = 0; i < width; i++) {\n\t\t// distance away from center of filter\n\t\tx = (double)i - half_width;\n\n\t\t// (x / sigma)^2 = x*x / (sigma*sigma)\n\t\tx /= sigma + DBL_EPSILON;\n\n\t\t// exponentiate result\n\t\tkernel[i] = (float)exp(-0.5 * x * x);\n\n\t\t// sum of all kernel elements\n\t\tacc += kernel[i];\n\t}\n\n\t// normalize kernel to sum to 1\n\tfor (i = 0; i < width; i++) {\n\t\tkernel[i] /= acc;\n\t}\n\n\t// Save the filter data \n\tgauss->sigma = sigma;\n\tif (init_Sep_FIR_filter(&gauss->f, dim, width, kernel,\n\t\t\t\t   SIFT3D_TRUE))\n                goto init_Gauss_filter_quit;\n\n        // Clean up\n        free(kernel);\n\n        return SIFT3D_SUCCESS;\n\ninit_Gauss_filter_quit:\n        free(kernel);\n        return SIFT3D_FAILURE;\n}\n\n/* Initialize a Gaussian filter to go from scale s_cur to s_next. */\nint init_Gauss_incremental_filter(Gauss_filter * const gauss,\n\t\t\t\t  const double s_cur, const double s_next,\n\t\t\t\t  const int dim)\n{\n\tdouble sigma;\n\n\tif (s_cur > s_next) {\n                SIFT3D_ERR(\"init_Gauss_incremental_filter: \"\n                                \"s_cur (%f) > s_next (%f) \\n\", s_cur, s_next);\n                return SIFT3D_FAILURE;\n        }\n\tassert(dim > 0);\n\n\t// Compute filter width parameter (sigma)\n\tsigma = sqrt(s_next * s_next - s_cur * s_cur);\n\n\t// Initialize filter kernel\n\tif (init_Gauss_filter(gauss, sigma, dim))\n\t\treturn SIFT3D_FAILURE;\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Free a Gauss_filter */\nvoid cleanup_Gauss_filter(Gauss_filter * gauss)\n{\n\tcleanup_Sep_FIR_filter(&gauss->f);\n}\n\n/* Initialize a GSS filters stuct. This must be called before gss can be\n * used in any other functions. */\nvoid init_GSS_filters(GSS_filters * const gss)\n{\n\tgss->num_filters = -1;\n\tgss->gauss_octave = NULL;\n}\n\n/* Create GSS filters to create the given scale-space \n * pyramid. */\nint make_gss(GSS_filters * const gss, const Pyramid * const pyr)\n{\n\n\tImage *cur, *next;\n\tint o, s;\n\n\tconst int dim = 3;\n\n\tconst int num_filters = pyr->num_levels - 1;\n\tconst int first_level = pyr->first_level;\n\tconst int last_level = SIFT3D_PYR_LAST_LEVEL(pyr);\n\n\t// Verify inputs\n\tif (num_filters < 1) {\n\t\tSIFT3D_ERR(\"make_gss: pyr has only %d levels, must have \"\n\t\t\t\t\"at least 2\", pyr->num_levels);\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n\t// Free all previous data, if any\n\tcleanup_GSS_filters(gss);\n\tinit_GSS_filters(gss);\n\n\t// Copy pyramid parameters\n\tgss->num_filters = num_filters;\n\tgss->first_level = first_level;\n\n\t// Allocate the filter array (num_filters cannot be zero)\n\tif ((gss->gauss_octave = (Gauss_filter *) \n\t\tSIFT3D_safe_realloc(gss->gauss_octave, \n\t\tnum_filters * sizeof(Gauss_filter))) == NULL)\n\t\treturn SIFT3D_FAILURE;\n\n\t// Make the filter for the very first blur\n\tnext = SIFT3D_PYR_IM_GET(pyr, pyr->first_octave, first_level);\n\tif (init_Gauss_incremental_filter(&gss->first_gauss, pyr->sigma_n,\n\t\t\t\t\t  next->s, dim))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Make one octave of filters (num_levels - 1)\n\to = pyr->first_octave;\n\tfor (s = first_level; s < last_level; s++) {\n\t\tcur = SIFT3D_PYR_IM_GET(pyr, o, s);\n\t\tnext = SIFT3D_PYR_IM_GET(pyr, o, s + 1);\n\t\tif (init_Gauss_incremental_filter(SIFT3D_GAUSS_GET(gss, s),\n\t\t\t\t\t\t  cur->s, next->s, dim))\n\t\t\treturn SIFT3D_FAILURE;\n\t}\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Free all memory associated with the GSS filters. gss cannot be reused\n * unless it is reinitialized. */\nvoid cleanup_GSS_filters(GSS_filters * const gss)\n{\n\n\tint i;\n\n\tconst int num_filters = gss->num_filters;\n\n\t// We are done if gss has no filters\n\tif (num_filters < 1)\n\t\treturn;\n\n\t// Free the first filter\n\tcleanup_Gauss_filter(&gss->first_gauss);\n\n\t// Free the octave filters\n\tfor (i = 0; i < num_filters; i++) {\n\t\tGauss_filter *const g = gss->gauss_octave + i;\n\t\tcleanup_Gauss_filter(g);\n\t}\n\n\t// Free the octave filter buffer\n\tfree(gss->gauss_octave);\n}\n\n/* Initialize a Pyramid for use. Must be called before a Pyramid can be used\n * in any other functions. */\nvoid init_Pyramid(Pyramid * const pyr)\n{\n\tpyr->levels = NULL;\n        pyr->first_level = 0;\n\tpyr->num_levels = pyr->num_kp_levels = 0;\n\tpyr->first_octave = 0;\n\tpyr->num_octaves = 0;\n        pyr->sigma0 = pyr->sigma_n = 0.0;\n}\n\n/* Resize a scale-space pyramid according to the size of base image im.\n *\n * Parameters:\n *  -im: An image with the desired dimensions and units at octave 0\n *  -first_level: The index of the first pyramid level per octave\n *  -num_kp_levels: The number of levels per octave in which keypoints are \n *      detected\n *  -num_levels: The total number of levels. Must be greater than or equal to\n *      num_kp_levels.\n *  -first_octave: The index of the first octave (0 is the base)\n *  -num_octaves: The total number of octaves \n *  -sigma0: The scale parameter of level 0, octave 0\n *  -sigma_n: The nominal scale of the image im.\n *  -pyr: The Pyramid to be resized.\n *\n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise. */\nint resize_Pyramid(const Image *const im, const int first_level, \n        const unsigned int num_kp_levels, const unsigned int num_levels,\n        const int first_octave, const unsigned int num_octaves, \n        Pyramid *const pyr) {\n\n        double units[IM_NDIMS];\n        int dims[IM_NDIMS];\n\tdouble factor;\n\tint i, o, s;\n\n\tconst double sigma0 = pyr->sigma0;\n\tconst double sigma_n = pyr->sigma_n;\n        const int old_num_total_levels = pyr->num_levels * pyr->num_octaves;\n\tconst int num_total_levels = num_levels * num_octaves;\n\n        // Verify inputs\n        if (num_levels < num_kp_levels) {\n                SIFT3D_ERR(\"resize_Pyramid: num_levels (%u) < \"\n                        \"num_kp_levels (%d)\", num_levels, num_kp_levels);\n                return SIFT3D_FAILURE;\n        }\n\n        // Store the new parameters\n        pyr->first_level = first_level;\n        pyr->num_kp_levels = num_kp_levels;\n        pyr->first_octave = first_octave;\n        pyr->num_octaves = num_octaves;\n        pyr->num_levels = num_levels;\n\n        // Clean up old levels which are no longer needed \n        for (i = num_total_levels; i < old_num_total_levels; i++) {\n                Image *const level = pyr->levels + i;\n                im_free(level);\n        }\n\n\t// Resize the outer array\n        if (num_total_levels != 0 && \n\t\t((pyr->levels = SIFT3D_safe_realloc(pyr->levels,\n\t\tnum_total_levels * sizeof(Image))) == NULL))\n                return SIFT3D_FAILURE;\n\n\t// We have nothing more to do if there are no levels\n\tif (num_total_levels == 0)\n\t\treturn SIFT3D_SUCCESS;\n\n        // Initalize new levels\n        for (i = old_num_total_levels; i < num_total_levels; i++) {\n                Image *const level = pyr->levels + i;\n                init_im(level);\n        }\n\n        // We have nothing more to do if the image is empty\n        if (im->data == NULL)\n                return SIFT3D_SUCCESS;\n\n\t// Calculate base image dimensions and units\n\tfactor = pow(2.0, -first_octave);\n        for (i = 0; i < IM_NDIMS; i++) {\n                dims[i] = (int) ((double) SIFT3D_IM_GET_DIMS(im)[i] * factor);\n                units[i] = SIFT3D_IM_GET_UNITS(im)[i] * factor;\n        }\n\n\t// Initialize each level separately\n\tSIFT3D_PYR_LOOP_START(pyr, o, s)\n                        // Initialize Image fields\n                        Image *const level = SIFT3D_PYR_IM_GET(pyr, o, s);\n                        memcpy(SIFT3D_IM_GET_DIMS(level), dims, \n                                IM_NDIMS * sizeof(int));\n                        memcpy(SIFT3D_IM_GET_UNITS(level), units, \n                                IM_NDIMS * sizeof(double));\n\t                level->nc = im->nc;\n\t                im_default_stride(level);\n\n                        // Re-size data memory\n                        if (im_resize(level))\n                                return SIFT3D_FAILURE;\n\n\t        SIFT3D_PYR_LOOP_SCALE_END\n\n\t        // Adjust dimensions and recalculate image size\n                for (i = 0; i < IM_NDIMS; i++) {\n                        dims[i] /= 2;\n                        units[i] *= 2;\n                }\n\n\tSIFT3D_PYR_LOOP_OCTAVE_END \n\n        // Set the scales for the new levels\n        return set_scales_Pyramid(pyr->sigma0, pyr->sigma_n, pyr);\n}\n\n/* Set the scale-space parameters on a Pyramid struct. Operates on all levels\n * of the pyramid. This function is called automatically by resize_Pyramid.\n *\n * Parameters:\n *  -sigma0: The scale parameter of level 0, octave 0\n *  -sigma_n: The nominal scale parameter of images being transfomed into\n *      this pyramid struct. \n *  -Pyr: The Pyramid to be modified. */\nint set_scales_Pyramid(const double sigma0, const double sigma_n, \n        Pyramid *const pyr) {\n\n        int o, s;\n\n        const int num_kp_levels = pyr->num_kp_levels;\n        const Image *const first_level = \n                SIFT3D_PYR_IM_GET(pyr, pyr->first_octave, pyr->first_level);\n\n        // Compute the scales of each level\n        SIFT3D_PYR_LOOP_START(pyr, o, s)\n\n                // Compute the scale \n                Image *const level = SIFT3D_PYR_IM_GET(pyr, o, s);\n                const double scale = \n                        sigma0 * pow(2.0, o + (double) s / num_kp_levels);\n\n                // Verify that sigma_n is not too large\n                if (o == pyr->first_octave && s == pyr->first_level && \n                        scale < sigma_n) {\n                        SIFT3D_ERR(\"set_scales_Pyramid: sigma_n too large \"\n                                \"for these settings. Max allowed: %f \\n\", \n                                scale - DBL_EPSILON);\n                        return SIFT3D_FAILURE;\n                }\n\n                // Save the scale\n                level->s = scale;\n        SIFT3D_PYR_LOOP_END\n\n        // Store the parameters\n        pyr->sigma0 = sigma0;\n        pyr->sigma_n = sigma_n;\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Make a deep copy of a pyramid. */\nint copy_Pyramid(const Pyramid * const src, Pyramid * const dst)\n{\n\n        Image dummy;\n        const Image *base;\n\tint o, s, have_levels;\n\n        // Initialize intermediates\n        init_im(&dummy);\n\n        // Set the scale parameters\n        if (set_scales_Pyramid(src->sigma0, src->sigma_n, dst))\n                return SIFT3D_FAILURE;\n\n        // Get the base image\n\tif (src->levels == NULL || src->num_octaves <= 0 ||\n\t    src->num_levels <= 0) {\n                base = &dummy;\n                have_levels = SIFT3D_FALSE;\n        } else {\n                base = src->levels;\n                have_levels = SIFT3D_TRUE;\n        }\n\n        // Resize dst\n        if (resize_Pyramid(base, src->first_level, src->num_kp_levels,\n                src->num_levels, src->first_octave, src->num_octaves, dst))\n                goto copy_Pyramid_failure;\n\n        // We are done if src has no levels\n        if (!have_levels)\n                goto copy_Pyramid_success;\n\n\t// Copy the levels\n\tSIFT3D_PYR_LOOP_START(dst, o, s)\n\n\t\tconst Image *const src_level = SIFT3D_PYR_IM_GET(src, o, s);\n\t\tImage *const dst_level = SIFT3D_PYR_IM_GET(dst, o, s);\n\n\t\tif (src_level->data != NULL && \n                        im_copy_data(src_level, dst_level))\n\t\t\treturn SIFT3D_FAILURE;\n\n\tSIFT3D_PYR_LOOP_END \n\ncopy_Pyramid_success:\n        im_free(&dummy);\n\treturn SIFT3D_SUCCESS;\n\ncopy_Pyramid_failure:\n        im_free(&dummy);\n\treturn SIFT3D_FAILURE;\n}\n\n/* Release all memory associated with a Pyramid. pyr cannot be used again,\n * unless it is reinitialized. */\nvoid cleanup_Pyramid(Pyramid * const pyr)\n{\n\n\tint o, s;\n\n\t// We are done if there are no levels\n\tif (pyr->levels == NULL)\n\t\treturn;\n\n\t// Free the levels\n\tSIFT3D_PYR_LOOP_START(pyr, o, s)\n\t\tImage *const level = SIFT3D_PYR_IM_GET(pyr, o, s);\n\t\tim_free(level);\n\tSIFT3D_PYR_LOOP_END\n\n\t// Free the pyramid level buffer\n\tfree(pyr->levels);\n}\n\n/* Initialize a Slab for first use */\nvoid init_Slab(Slab *const slab) {\n    slab->buf_size = slab->num = 0;\n    slab->buf = NULL;\n}\n\n/* Free all memory associated with a slab. Slab cannot be re-used after \n * calling this function, unless re-initialized. */\nvoid cleanup_Slab(Slab * const slab)\n{\n        if (slab->buf != NULL)\n\t        free(slab->buf);\n}\n\n/* Write the levels of a pyramid to separate files\n * for debugging. The path is prepended to the\n * octave and scale number of each image. \n *\n * File type is inferred from the extension in path.\n *\n * Supported file formats:\n * -NIFTI\n */\nint write_pyramid(const char *path, Pyramid * pyr)\n{\n\n\tchar path_appended[1024];\n\tint o, s;\n\n\t// Validate or create output directory\n\tif (mkpath(path, out_mode))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Save each image a separate file\n\tSIFT3D_PYR_LOOP_START(pyr, o, s)\n\t    sprintf(path_appended, \"%s_o%i_s%i\", path, o, s);\n\tif (write_nii(path_appended, SIFT3D_PYR_IM_GET(pyr, o, s)))\n\t\treturn SIFT3D_FAILURE;\n\tSIFT3D_PYR_LOOP_END return SIFT3D_SUCCESS;\n}\n\n/* Exit and print a message to stdout. */\nvoid err_exit(const char *str)\n{\n\tSIFT3D_ERR(\"Error! Exiting at %s \\n\", str);\n\texit(1);\n}\n\n/* Read a whole ASCII file into a string. Returns NULL\n * on error. */\nSIFT3D_IGNORE_UNUSED\nstatic char *read_file(const char *path)\n{\n\n\tFILE *file;\n\tchar *buf;\n\tsize_t len;\n\n\tif ((file = fopen(path, \"r\")) == NULL) {\n\t\treturn NULL;\n\t}\n\tfseek(file, 0, SEEK_END);\n\tlen = ftell(file);\n\trewind(file);\n\tif (ferror(file) || ((buf = malloc(len)) == NULL))\n\t\treturn NULL;\n\tfread(buf, sizeof(char), len, file);\n\n\treturn ferror(file) ? NULL : buf;\n}\n\n/* Ensure all directories in the given path exist.\n * Thanks to Jonathan Leffler\n * Modifications: Ignore everything after the last '/' \n */\nstatic int mkpath(const char *path, mode_t mode)\n{\n\tchar *pp, *sp, *copypath;\n\tint status;\n\n\tif ((copypath = strndup(path, FILENAME_MAX)) == NULL)\n\t\tstatus = -1;\n\n\t/* Ignore everything after the last '/' */\n\tif ((sp = strrchr(copypath, '/')) != NULL) {\n\t\t*sp = '\\0';\n\t} else {\n\t\t/* If there is no '/', we have nothing to do */\n\t\tfree(copypath);\n\t\treturn SIFT3D_SUCCESS;\n\t}\n\n\tstatus = 0;\n\tpp = copypath;\n\twhile (status == 0 && (sp = strchr(pp, '/')) != NULL) {\n\t\tif (sp != pp) {\n\t\t\t/* Neither root nor double slash in path */\n\t\t\t*sp = '\\0';\n\t\t\tstatus = do_mkdir(copypath, mode);\n\t\t\t*sp = '/';\n\t\t}\n\t\tpp = sp + 1;\n\t}\n\tif (status == 0)\n\t\tstatus = do_mkdir(copypath, mode);\n\n\tfree(copypath);\n\treturn (status);\n}\n\n/* Make a directory if it does not exist.\n * Thanks to Jonathan Leffler */\nstatic int do_mkdir(const char *path, mode_t mode)\n{\n\tstruct stat st;\n\tint status = 0;\n\n\tif (stat(path, &st) != 0) {\n\t\t/* Directory does not exist. EEXIST for race condition */\n\t\tif (cross_mkdir(path, mode) != 0 && errno != EEXIST)\n\t\t\tstatus = -1;\n\t} else if (!S_ISDIR(st.st_mode)) {\n\t\terrno = ENOTDIR;\n\t\tstatus = -1;\n\t}\n\n\treturn (status);\n}\n\n/* Cross-platform mkdir */\nstatic int cross_mkdir(const char *path, mode_t mode) {\n#ifdef _MINGW_WINDOWS\n        return mkdir(path);\n#elif defined( _WINDOWS )\n        return _mkdir(path);\n#else\n        return mkdir(path, mode);\n#endif\n}\n\n/* Initialize a Tps struct. This initializes\n * all fields, and allocates memory for the inner\n * matrix, initializing it to zero. */\nint init_Tps(Tps * tps, int dim, int terms)\n{\n\t// Verify inputs\n\tif (dim < 2)\n\t\treturn SIFT3D_FAILURE;\n\n\t// Initialize the type\n\ttps->tform.type = TPS;\n\n\t// Initialize the vtable\n\ttps->tform.vtable = &Tps_vtable;\n\n\t// Initialize the matrices\n\tif (init_Mat_rm(&tps->params, dim, terms, SIFT3D_DOUBLE, SIFT3D_TRUE))\n\t\treturn SIFT3D_FAILURE;\n\n\tif (init_Mat_rm(&tps->kp_src, terms - dim - 1, dim,\n\t\t\tSIFT3D_DOUBLE, SIFT3D_TRUE))\n\t\treturn SIFT3D_FAILURE;\n\n\ttps->dim = dim;\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Initialize a RANSAC struct with the default parameters */\nvoid init_Ransac(Ransac *const ran)\n{\n\tran->err_thresh = SIFT3D_err_thresh_default;\n\tran->num_iter = SIFT3D_num_iter_default;\n}\n\n/* Set the err_thresh parameter in a Ransac struct, checking for validity. */\nint set_err_thresh_Ransac(Ransac *const ran, double err_thresh)\n{\n\n\tif (err_thresh < 0.0) {\n\t\tSIFT3D_ERR(\"set_err_thresh_Ransac: invalid error \"\n\t\t\t\"threshold: %f \\n\", err_thresh);\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n\tran->err_thresh = err_thresh;\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Set the num_iter parameter in a Ransac struct. */\nint set_num_iter_Ransac(Ransac *const ran, int num_iter)\n{\n        if (num_iter < 1) {\n                SIFT3D_ERR(\"set_num_iter_Ransac: invalid number of \"\n                                \"iterations: %d \\n\", num_iter);\n                return SIFT3D_FAILURE;\n        }\n\n\tran->num_iter = num_iter;\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Copy a Ransac struct from src to dst. */\nint copy_Ransac(const Ransac *const src, Ransac *const dst) {\n        return set_num_iter_Ransac(dst, src->num_iter) ||\n                set_err_thresh_Ransac(dst, src->err_thresh);\n}\n\n/* Returns an array of k integers, (uniformly) randomly chosen from the \n * integers 0 through n - 1.\n *\n * The value of *ret must either be NULL, or a pointer to a previously\n * allocated block. On successful return, *ret contains the k random integers.\n *\n * Returns SIFT3D_SUCCESS on succes, SIFT3D_FAILURE otherwise. */\nstatic int n_choose_k(const int n, const int k, int **ret) {\n\n        int i;\n\n        // Verify inputs\n        if (n < k || k < 1)\n                goto n_choose_k_fail;\n\n        // Allocate the array of n elements\n        if ((*ret = malloc(n * sizeof(int))) == NULL)\n                goto n_choose_k_fail;\n\n        // Initialize the array of indices\n        for (i = 0; i < n; i++) {\n                (*ret)[i] = i;\n        }\n\n        // Randomize the first k indices using Knuth shuffles\n        for (i = 0; i < k; i++) {\n\n                int *const ints = *ret;\n                const int temp = ints[i]; \n                const int rand_idx = i + rand() % (n - i);\n\n                ints[i] = ints[rand_idx];\n                ints[rand_idx] = temp;\n        }\n\n        // Release unused memory\n        if ((*ret = SIFT3D_safe_realloc(*ret, k * sizeof(int))) == NULL)\n                goto n_choose_k_fail;\n\n        return SIFT3D_SUCCESS;\n\nn_choose_k_fail:\n        if (*ret != NULL) {\n                free(*ret);\n                *ret = NULL;\n        }\n        return SIFT3D_FAILURE;\n}\n\n//make the system matrix for spline\nSIFT3D_IGNORE_UNUSED\nstatic int make_spline_matrix(Mat_rm * src, Mat_rm * src_in, Mat_rm * sp_src,\n\t\t\t      int K_terms, int *r, int dim)\n{\n\tint i, d;\n\tdouble x, y, z, x2, y2, z2, r_sq, U;\n\tsrc_in->type = SIFT3D_DOUBLE;\n\tsp_src->type = SIFT3D_DOUBLE;\n\tif (init_Mat_rm\n\t    (src_in, K_terms + dim + 1, K_terms + dim + 1, SIFT3D_DOUBLE,\n\t     SIFT3D_TRUE)) {\n\t\treturn SIFT3D_FAILURE;\n\t}\n\tif (init_Mat_rm(sp_src, K_terms, dim, SIFT3D_DOUBLE, SIFT3D_TRUE)) {\n\t\treturn SIFT3D_FAILURE;\n\t}\n\tfor (i = 0; i < K_terms; i++) {\n\t\t//get the coordinate of current point\n\t\tswitch (dim) {\n\t\tcase 2:\n\t\t\tx = SIFT3D_MAT_RM_GET(src, r[i], 0, double);\n\t\t\ty = SIFT3D_MAT_RM_GET(src, r[i], 1, double);\n\t\t\tbreak;\n\t\tcase 3:\n\t\t\tx = SIFT3D_MAT_RM_GET(src, r[i], 0, double);\n\t\t\ty = SIFT3D_MAT_RM_GET(src, r[i], 1, double);\n\t\t\tz = SIFT3D_MAT_RM_GET(src, r[i], 2, double);\n\t\t\tbreak;\n\t\t}\n\t\tfor (d = 0; d < i; d++) {\n\t\t\t//compute r\n\t\t\tswitch (dim) {\n\t\t\tcase 2:\n\t\t\t\tx2 = SIFT3D_MAT_RM_GET(src, r[d], 0, double);\n\t\t\t\ty2 = SIFT3D_MAT_RM_GET(src, r[d], 1, double);\n\t\t\t\tr_sq =\n\t\t\t\t    (x - x2) * (x - x2) + (y - y2) * (y - y2);\n\t\t\t\tbreak;\n\t\t\tcase 3:\n\t\t\t\tx2 = SIFT3D_MAT_RM_GET(src, r[d], 0, double);\n\t\t\t\ty2 = SIFT3D_MAT_RM_GET(src, r[d], 1, double);\n\t\t\t\tz2 = SIFT3D_MAT_RM_GET(src, r[d], 2, double);\n\t\t\t\tr_sq =\n\t\t\t\t    (x - x2) * (x - x2) + (y - y2) * (y - y2) +\n\t\t\t\t    (z - z2) * (z - z2);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t//compute U\n\t\t\tU = r_sq * log(r_sq);\n\t\t\t//construct K\n\t\t\tSIFT3D_MAT_RM_GET(src_in, i, d, double) = U;\n\t\t\tSIFT3D_MAT_RM_GET(src_in, d, i, double) = U;\n\t\t}\n\t\tSIFT3D_MAT_RM_GET(src_in, i, i, double) = 0.0;\n\t\t//construct P and P'\n\t\tSIFT3D_MAT_RM_GET(src_in, i, K_terms, double) = 1.0;\n\t\tSIFT3D_MAT_RM_GET(src_in, K_terms, i, double) = 1.0;\n\t\tswitch (dim) {\n\t\tcase 2:\n\t\t\tSIFT3D_MAT_RM_GET(src_in, i, K_terms + 1, double) = x;\n\t\t\tSIFT3D_MAT_RM_GET(src_in, i, K_terms + 2, double) = y;\n\t\t\tSIFT3D_MAT_RM_GET(src_in, K_terms + 1, i, double) = x;\n\t\t\tSIFT3D_MAT_RM_GET(src_in, K_terms + 2, i, double) = y;\n\t\t\tbreak;\n\t\tcase 3:\n\t\t\tSIFT3D_MAT_RM_GET(src_in, i, K_terms + 1, double) = x;\n\t\t\tSIFT3D_MAT_RM_GET(src_in, i, K_terms + 2, double) = y;\n\t\t\tSIFT3D_MAT_RM_GET(src_in, i, K_terms + 3, double) = z;\n\t\t\tSIFT3D_MAT_RM_GET(src_in, K_terms + 1, i, double) = x;\n\t\t\tSIFT3D_MAT_RM_GET(src_in, K_terms + 2, i, double) = y;\n\t\t\tSIFT3D_MAT_RM_GET(src_in, K_terms + 3, i, double) = z;\n\t\t\tbreak;\n\t\t}\n\n\t\t//construct sp_src matrix(matrix that stores control points)\n\t\tswitch (dim) {\n\t\tcase 2:\n\t\t\tSIFT3D_MAT_RM_GET(sp_src, i, 0, double) = x;\n\t\t\tSIFT3D_MAT_RM_GET(sp_src, i, 1, double) = y;\n\t\t\tbreak;\n\t\tcase 3:\n\t\t\tSIFT3D_MAT_RM_GET(sp_src, i, 0, double) = x;\n\t\t\tSIFT3D_MAT_RM_GET(sp_src, i, 1, double) = y;\n\t\t\tSIFT3D_MAT_RM_GET(sp_src, i, 2, double) = z;\n\t\t\tbreak;\n\t\t}\n\n\t}\n\n\t//construct O\n\tfor (i = 0; i < dim; i++) {\n\t\tfor (d = 0; d < dim; d++) {\n\t\t\tSIFT3D_MAT_RM_GET(src_in, K_terms + i, K_terms + d,\n\t\t\t\t\t  double) = 0.0;\n\t\t}\n\t}\n\n\treturn SIFT3D_SUCCESS;\n}\n\n//make the system matrix for affine\nstatic int make_affine_matrix(const Mat_rm *const pts_in, const int dim, \n        Mat_rm *const mat_out)\n{\n\n\tint i, j;\n\n\tconst int num_rows = pts_in->num_rows;\n\n\tmat_out->type = SIFT3D_DOUBLE;\n\tmat_out->num_rows = num_rows;\n\tmat_out->num_cols = dim + 1;\n\tif (resize_Mat_rm(mat_out))\n\t\treturn SIFT3D_FAILURE;\n\n\tfor (i = 0; i < num_rows; i++) {\n\n\t\t//Add one row to the matrix\n\t\tfor (j = 0; j < dim; j++) {\n\t\t\tSIFT3D_MAT_RM_GET(mat_out, i, j, double) =\n\t\t\t    SIFT3D_MAT_RM_GET(pts_in, i, j, double);\n\t\t}\n\t\tSIFT3D_MAT_RM_GET(mat_out, i, dim, double) = 1.0;\n\t}\n\n\treturn SIFT3D_SUCCESS;\n}\n\n//extract the control matrix from tform struct (only valid for spline)\nSIFT3D_IGNORE_UNUSED\nstatic Mat_rm *extract_ctrl_pts(void *tform, tform_type type)\n{\n\tMat_rm *T;\n\tTps *tps = (Tps *) tform;\n\tswitch (type) {\n\tcase TPS:\n\t\tT = extract_ctrl_pts_Tps(tps);\n\t\tbreak;\n\tcase AFFINE:\n\t\tbreak;\n\tdefault:\n\t\treturn NULL;\n\t}\n\treturn T;\n}\n\nstatic Mat_rm *extract_ctrl_pts_Tps(Tps * tps)\n{\n\tMat_rm *kp_src = &tps->kp_src;\n\treturn kp_src;\n}\n\n/* Solve for a transformation struct. \n *\n * Paramters:\n *   src - See ransac().\n *   ref - See ransac()\n *   tform - See ransac()\n *\n * Returns SIFT3D_SUCCESS, SIFT3D_SINGULAR, or SIFT3D_FAILURE. See ransac() for\n * interpretation. */\nstatic int solve_system(const Mat_rm *const src, const Mat_rm *const ref, \n        void *const tform)\n{\n\tconst tform_type type = tform_get_type(tform);\n\n\t//Mat_rm *kp_ref;\n\tMat_rm ref_sys, X;\n\tint dim, ret;\n\n\tinit_Mat_rm(&ref_sys, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE);\n\tinit_Mat_rm(&X, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE);\n\n\t//construct source matrix and initialize reference vector\n\tswitch (type) {\n\tcase TPS:\n\t\t//kp_ref = extract_ctrl_pts(tform, type);\n//          make_spline_matrix(ref, &ref_in, kp_ref, num_pts, r, dim);\n\t\tputs(\"solve_system: TPS not yet implemented\");\n\t\tgoto SOLVE_SYSTEM_FAIL;\n\tcase AFFINE:\n                dim = AFFINE_GET_DIM((Affine *const) tform);\n\t\tmake_affine_matrix(ref, dim, &ref_sys);\n\t\tbreak;\n\tdefault:\n\t\tputs(\"solve_system: unknown type\");\n\t\tgoto SOLVE_SYSTEM_FAIL;\n\t}\n\n\t// solve for the coefficients                   \n        ret = ref_sys.num_rows == ref_sys.num_cols ?\n\t\tsolve_Mat_rm(&ref_sys, src, -1.0, &X) :\n\t\tsolve_Mat_rm_ls(&ref_sys, src, &X);\n\n\tswitch (ret) {\n\tcase SIFT3D_SUCCESS:\n\t\tbreak;\n\tcase SIFT3D_SINGULAR:\n\t\tgoto SOLVE_SYSTEM_SINGULAR;\n\tdefault:\n\t\tgoto SOLVE_SYSTEM_FAIL;\n\t}\n\n\t// Save the transformation matrix\n\tswitch (type) {\n\tcase TPS:\n\t\t//TODO\n\t\tgoto SOLVE_SYSTEM_FAIL;\n\tcase AFFINE:\n        {\n\t\tMat_rm X_trans;\n\n\t\tinit_Mat_rm(&X_trans, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE);\n\n\t\tret = transpose_Mat_rm(&X, &X_trans) ||\n\t\t    Affine_set_mat(&X_trans, (Affine *) tform);\n\n\t\tcleanup_Mat_rm(&X_trans);\n\n\t\tif (ret)\n\t\t\tgoto SOLVE_SYSTEM_FAIL;\n\n\t\tbreak;\n\t}\n\tdefault:\n\t\tgoto SOLVE_SYSTEM_FAIL;\n\t}\n\n        // Clean up\n\tcleanup_Mat_rm(&ref_sys);\n\tcleanup_Mat_rm(&X);\n\n\treturn SIFT3D_SUCCESS;\n\n SOLVE_SYSTEM_SINGULAR:\n\tcleanup_Mat_rm(&ref_sys);\n\tcleanup_Mat_rm(&X);\n\treturn SIFT3D_SINGULAR;\n\n SOLVE_SYSTEM_FAIL:\n\tcleanup_Mat_rm(&ref_sys);\n\tcleanup_Mat_rm(&X);\n\treturn SIFT3D_FAILURE;\n}\n\n//Find the SSD error for the i'th point\nstatic double tform_err_sq(const void *const tform, const Mat_rm *const src, \n        const Mat_rm *const ref, const int i)\n{\n\n\tdouble err = 0.0;\n\t//Initialization\n\t//in -- inputs coordinates of source points\n\t//out -- registered points\n\t//r -- reference points (ground truth)\n\tdouble x_in, y_in, z_in, x_r, y_r, z_r, x_out, y_out, z_out;\n\n\t//Find the source point\n\tx_in = SIFT3D_MAT_RM_GET(ref, i, 0, double);\n\ty_in = SIFT3D_MAT_RM_GET(ref, i, 1, double);\n\tz_in = SIFT3D_MAT_RM_GET(ref, i, 2, double);\n\n\t//Register\n\tapply_tform_xyz(tform, x_in, y_in, z_in, &x_out, &y_out, &z_out);\n\n\t//Find the reference point\n\tx_r = SIFT3D_MAT_RM_GET(src, i, 0, double);\n\ty_r = SIFT3D_MAT_RM_GET(src, i, 1, double);\n\tz_r = SIFT3D_MAT_RM_GET(src, i, 2, double);\n\n\t//Find the SSD error\n\terr = (x_r - x_out) * (x_r - x_out) + (y_r - y_out) * (y_r - y_out) +\n\t    (z_r - z_out) * (z_r - z_out);\n\n\t//return the result \n\treturn err;\n}\n\n/* Perform one iteration of RANSAC. \n *\n * Parameters:\n *  src - The source points.\n *  ref - The reference points.\n *  tform - The output transformation. Must be initialized.\n *  cset - An array in which to store the concensus set. The value *cset must\n *         either be NULL, or a pointer to a previously allocated block.\n *  len - A location in which to store the length of the cset. \n *\n * Returns SIFT3D_SUCCESS on success, SIFT3D_SINGULAR if the system is \n * near singular, and SIFT3D_FAILURE otherwise. */\nstatic int ransac(const Mat_rm *const src, const Mat_rm *const ref, \n        const Ransac *const ran, void *tform, int **const cset, int *const len)\n{\n        int *rand_indices;\n\tMat_rm src_rand, ref_rand;\n\tint i, j, num_rand, cset_len;\n\n\tconst double err_thresh = ran->err_thresh;\n\tconst double err_thresh_sq = err_thresh * err_thresh;\n\tconst int num_pts = src->num_rows;\n        const int num_dim = src->num_cols;\n\tconst tform_type type = tform_get_type(tform);\n\n\t// Verify inputs\n\tif (src->type != SIFT3D_DOUBLE || src->type != ref->type) {\n\t\tputs(\"ransac: all matrices must have type double \\n\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n\tif (src->num_rows != ref->num_rows || src->num_cols != ref->num_cols) {\n\t\tputs(\"ransac: src and ref must have the same dimensions \\n\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n        // Get the number of points for this transform\n\tswitch (type) {\n\tcase AFFINE:\n\t\tnum_rand = AFFINE_GET_DIM((Affine *const) tform) + 1;\n\t\tbreak;\n\tdefault:\n\t\tprintf(\"ransac: unknown transformation type \\n\");\n                return SIFT3D_FAILURE;\n\t}\n\n\t// Initialize intermediates\n        rand_indices = NULL;\n\tinit_Mat_rm(&src_rand, num_rand, num_dim, SIFT3D_DOUBLE, SIFT3D_FALSE);\n\tinit_Mat_rm(&ref_rand, num_rand, num_dim, SIFT3D_DOUBLE, SIFT3D_FALSE);\n\n        // Draw random point indices\n        if (n_choose_k(num_pts, num_rand, &rand_indices))\n                goto RANSAC_FAIL;\n\n        // Copy the random points\n\tSIFT3D_MAT_RM_LOOP_START(&src_rand, i, j)\n\n                const int rand_idx = rand_indices[i];\n\n                SIFT3D_MAT_RM_GET(&src_rand, i, j, double) =\n                        SIFT3D_MAT_RM_GET(src, rand_idx, j, double);\n                SIFT3D_MAT_RM_GET(&ref_rand, i, j, double) =\n                        SIFT3D_MAT_RM_GET(ref, rand_idx, j, double);\n\n        SIFT3D_MAT_RM_LOOP_END\n\n        // Fit a transform to the random points\n\tswitch (solve_system(&src_rand, &ref_rand, tform)) {\n\tcase SIFT3D_SUCCESS:\n\t\tbreak;\n\tcase SIFT3D_SINGULAR:\n\t\tgoto RANSAC_SINGULAR;\n\tdefault:\n\t\tgoto RANSAC_FAIL;\n\t}\n\n\t// Extract the consensus set\n\tcset_len = 0;\n\tfor (i = 0; i < num_pts; i++) {\n\n\t\t// Calculate the error\n\t\tconst double err_sq = tform_err_sq(tform, src, ref, i);\n\n\t\t// Reject points below the error threshold\n\t\tif (err_sq > err_thresh_sq)\n\t\t\tcontinue;\n\n\t\t// Add to the consensus set (++cset_len cannot be zero)\n\t\tif ((*cset = SIFT3D_safe_realloc(*cset, \n\t\t\t++cset_len * sizeof(int))) == NULL)\n\t\t\tgoto RANSAC_FAIL;\n\n\t\t(*cset)[cset_len - 1] = i;\n\t}\n\n\t// Return the new length of cset\n\t*len = cset_len;\n\n        if (rand_indices != NULL)\n                free(rand_indices);\n\tcleanup_Mat_rm(&src_rand);\n\tcleanup_Mat_rm(&ref_rand);\n\treturn SIFT3D_SUCCESS;\n\nRANSAC_SINGULAR:\n        if (rand_indices != NULL)\n                free(rand_indices);\n\tcleanup_Mat_rm(&src_rand);\n\tcleanup_Mat_rm(&ref_rand);\n\treturn SIFT3D_SINGULAR;\n\nRANSAC_FAIL:\n        if (rand_indices != NULL)\n                free(rand_indices);\n\tcleanup_Mat_rm(&src_rand);\n\tcleanup_Mat_rm(&ref_rand);\n\treturn SIFT3D_FAILURE;\n}\n\n//Resize spline struct based on number of selected points\nint resize_Tps(Tps * tps, int num_pts, int dim)\n{\n\tMat_rm *params = &(tps->params);\n\tMat_rm *kp_src = &(tps->kp_src);\n\tparams->num_cols = num_pts + dim + 1;\n\tparams->num_rows = dim;\n\tkp_src->num_rows = num_pts;\n\tkp_src->num_cols = dim;\n\tif (resize_Mat_rm(params)) {\n\t\treturn SIFT3D_FAILURE;\n\t}\n\tif (resize_Mat_rm(kp_src)) {\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n\ttps->dim = dim;\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Fit a transformation from ref to src points, using random sample concensus \n * (RANSAC).\n * \n * Parameters:\n *   ran - Struct storing RANSAC parameters.\n *   src - The [mxn] source points.\n *   ref - The [mxn] reference points.\n *   tform - The output transform. Must be initialized with init_from prior to \n *           calling this function. \n *\n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise. */\nint find_tform_ransac(const Ransac *const ran, const Mat_rm *const src, \n        const Mat_rm *const ref, void *const tform)\n{\n\n\tMat_rm ref_cset, src_cset;\n\tvoid *tform_cur;\n\tint *cset, *cset_best;\n\tint i, j, dim, num_terms, ret, len, len_best, min_num_inliers;\n\n\tconst int num_iter = ran->num_iter;\n\tconst int num_pts = src->num_rows;\n\tconst size_t tform_size = tform_get_size(tform);\n\tconst tform_type type = tform_get_type(tform);\n\n\t// Initialize data structures\n\tcset = cset_best = NULL;\n\tlen_best = 0;\n\tif ((tform_cur = malloc(tform_size)) == NULL ||\n\t    init_tform(tform_cur, type) ||\n\t    init_Mat_rm(&src_cset, len_best, IM_NDIMS, SIFT3D_DOUBLE, \n            \tSIFT3D_FALSE) ||\n\t    init_Mat_rm(&ref_cset, len_best, IM_NDIMS, SIFT3D_DOUBLE, \n\t\tSIFT3D_FALSE))\n\t\tgoto find_tform_quit;\n\n\t// initialize type-specific variables\n\tswitch (type) {\n\tcase AFFINE:\n                dim = AFFINE_GET_DIM((Affine *const) tform);\n\t\tnum_terms = dim + 1;\n\t\tmin_num_inliers = 5;\n\t\tbreak;\n\tdefault:\n\t\tputs(\"find_tform_ransac: unsupported transformation \"\n\t\t     \"type \\n\");\n\t\tgoto find_tform_quit;\n\t}\n\n\tif (num_pts < num_terms) {\n\t\tprintf(\"Not enough matched points \\n\");\n\t\tgoto find_tform_quit;\n\t}\n\t// Ransac iterations\n\tfor (i = 0; i < num_iter; i++) {\n\t\tdo {\n\t\t\tret = ransac(src, ref, ran, tform_cur, &cset, &len);\n\t\t} while (ret == SIFT3D_SINGULAR);\n\n\t\tif (ret == SIFT3D_FAILURE)\n\t\t\tgoto find_tform_quit;\n\n\t\tif (len > len_best) {\n\t\t\tlen_best = len;\n\t\t\tif ((cset_best = (int *)SIFT3D_safe_realloc(cset_best,\n\t\t\t\tlen * sizeof(int))) == NULL || \n\t\t\t\tcopy_tform(tform_cur, tform))\n\t\t\t\tgoto find_tform_quit;\n\t\t\tmemcpy(cset_best, cset, len * sizeof(int));\n\t\t}\n\t}\n\n\t// Check if the minimum number of inliers was found\n\tif (len_best < min_num_inliers) {\n\t\tputs(\"find_tform_ransac: No good model was found! \\n\");\n\t\tgoto find_tform_quit; }\n\n\t// Resize the concensus set matrices\n        src_cset.num_rows = ref_cset.num_rows = len_best;\n        if (resize_Mat_rm(&src_cset) || resize_Mat_rm(&ref_cset))\n                goto find_tform_quit;\n\n\t// Extract the concensus set\n\tSIFT3D_MAT_RM_LOOP_START(&src_cset, i, j)\n\n\t        const int idx = cset_best[i];\n\n\t        SIFT3D_MAT_RM_GET(&src_cset, i, j, double) =\n\t                SIFT3D_MAT_RM_GET(src, idx, j, double);\n\t        SIFT3D_MAT_RM_GET(&ref_cset, i, j, double) =\n\t                SIFT3D_MAT_RM_GET(ref, idx, j, double);\n\n\tSIFT3D_MAT_RM_LOOP_END\n\n#ifdef SIFT3D_RANSAC_REFINE\n\t// Refine with least squares\n\tswitch (solve_system(&src_cset, &ref_cset, tform_cur)) {\n\tcase SIFT3D_SUCCESS:\n\t\t// Copy the refined transformation to the output\n\t\tif (copy_tform(tform_cur, tform))\n\t\t\tgoto find_tform_quit;\n\t\tbreak;\n\tcase SIFT3D_SINGULAR:\n\t\t// Stick with the old transformation \n#ifdef VERBOSE\n\t\tprintf(\"find_tform_ransac: warning: least-squares refinement \"\n\t\t       \"abandoned due to numerical precision \\n\");\n#endif\n\t\tbreak;\n\tdefault:\n\t\tgoto find_tform_quit;\n\t}\n#endif\n\n        // Clean up\n\tfree(cset);\n\tfree(cset_best);\n\tcleanup_tform(tform_cur);\n        cleanup_Mat_rm(&ref_cset);\n        cleanup_Mat_rm(&src_cset);\n\tif (tform_cur != NULL)\n\t\tfree(tform_cur);\n\treturn SIFT3D_SUCCESS;\n\nfind_tform_quit:\n        // Clean up and return an error\n\tif (cset != NULL)\n\t\tfree(cset);\n\tif (cset_best != NULL)\n\t\tfree(cset_best);\n\tcleanup_tform(tform_cur);\n\tif (tform_cur != NULL)\n\t\tfree(tform_cur);\n        cleanup_Mat_rm(&ref_cset);\n        cleanup_Mat_rm(&src_cset);\n\treturn SIFT3D_FAILURE;\n}\n\n/* Parse the GNU standard arguments (--version, --help). On return, the\n * getopt state is restored to the original.\n *\n * Return values:\n * -SIFT3D_HELP - \"--help\" was found\n * -SIFT3D_VERSION - \"--version\" was found, and the version message printed\n * -SIFT3D_FALSE - no GNU standard arguments were found */\nint parse_gnu(const int argc, char *const *argv)\n{\n\n\tint c;\n\n\tconst int opterr_start = opterr;\n\n\t// Options\n\tconst struct option longopts[] = {\n\t\t{\"help\", no_argument, NULL, SIFT3D_HELP},\n\t\t{\"version\", no_argument, NULL, SIFT3D_VERSION},\n\t\t{0, 0, 0, 0}\n\t};\n\n\t// Process the arguments\n\topterr = 0;\n\twhile ((c = getopt_long(argc, argv, \"+\", longopts, NULL)) != -1) {\n\t\tswitch (c) {\n\t\tcase SIFT3D_HELP:\n\t\t\treturn SIFT3D_HELP;\n\t\tcase SIFT3D_VERSION:\n\t\t\tputs(version_msg);\n\t\t\treturn SIFT3D_VERSION;\n\t\t}\n\t}\n\n\t// Restore the state\n\toptind = 0;\n\topterr = opterr_start;\n\n\treturn SIFT3D_FALSE;\n}\n\n/* Print the bug message to stderr. */\nvoid print_bug_msg()\n{\n\tSIFT3D_ERR(bug_msg);\n}\n"
  },
  {
    "path": "imutil/imutil.h",
    "content": "/* -----------------------------------------------------------------------------\r\n * imutil.h\r\n * -----------------------------------------------------------------------------\r\n * Copyright (c) 2015-2017 Blaine Rister et al., see LICENSE for details.\r\n * -----------------------------------------------------------------------------\r\n * Public header for imutil.c\r\n * -----------------------------------------------------------------------------\r\n */\r\n\r\n#include \"imtypes.h\"\r\n\r\n#ifndef _IMUTIL_H\r\n#define _IMUTIL_H\r\n\r\n#ifdef __cplusplus\r\nextern \"C\" {\r\n#endif\r\n\r\n/* Extra return codes for this module */\r\n#define SIFT3D_FILE_DOES_NOT_EXIST 1 /* The file does not exist */\r\n#define SIFT3D_UNSUPPORTED_FILE_TYPE 2 /* The file type is not supported */\r\n#define SIFT3D_WRAPPER_NOT_COMPILED 3 /* The file type is supported, but the \r\n                                     * wrapper library was not compiled. */\r\n#define SIFT3D_UNEVEN_SPACING 4 /* The image slices are not evenly spaced. */\r\n#define SIFT3D_INCONSISTENT_AXES 5 /* The image slices have inconsistent \r\n                                    * axes. */\r\n#define SIFT3D_DUPLICATE_SLICES 6 /* Multiple slices in the same location. */\r\n\r\n/* Vendor-specific info */\r\n#define PLATFORM_NAME_NVIDIA \"NVIDIA CUDA\"\r\n\r\n/* Parameters */\r\nconst extern double SIFT3D_err_thresh_default;\r\nconst extern int SIFT3D_num_iter_default;\r\n\r\n/* Externally-visible routines */\r\nvoid *SIFT3D_safe_realloc(void *ptr, size_t size);\r\n\r\nvoid clFinish_all();\r\n\r\nvoid check_cl_error(int err, const char *msg);\r\n\r\nint init_cl(CL_data *user_cl_data, const char *platform_name, \r\n\t\t\tcl_device_type device_type,\tcl_mem_flags mem_flags, \r\n\t\t\tcl_image_format image_format);\r\n\r\nvoid init_Mesh(Mesh * const mesh);\r\n\r\nvoid cleanup_Mesh(Mesh * const mesh);\r\n\r\nint convert_Mat_rm(const Mat_rm *const in, Mat_rm *const out, \r\n        const Mat_rm_type type);\r\n\r\nint init_Mat_rm(Mat_rm *const mat, const int num_rows, const int num_cols,\r\n                const Mat_rm_type type, const int set_zero);\r\n\r\nint init_Mat_rm_p(Mat_rm *const mat, const void *const p, const int num_rows, \r\n                  const int num_cols, const Mat_rm_type type, \r\n                  const int set_zero);\r\n\r\nvoid sprint_type_Mat_rm(const Mat_rm *const mat, char *const str);\r\n\r\nint concat_Mat_rm(const Mat_rm * const src1, const Mat_rm * const src2,\r\n\t\t    Mat_rm * const dst, const int dim);\r\n\r\nint set_Mat_rm_zero(Mat_rm *mat);\r\n\r\nint copy_Mat_rm(const Mat_rm *const src, Mat_rm *const dst);\r\n\r\nint print_Mat_rm(const Mat_rm *const mat);\r\n\r\nint resize_Mat_rm(Mat_rm *const mat); \r\n\r\nint eigen_Mat_rm(Mat_rm *A, Mat_rm *Q, Mat_rm *L);\r\n\r\nint solve_Mat_rm(const Mat_rm *const A, const Mat_rm *const B, \r\n        const double limit, Mat_rm *const X);\r\n\r\nint solve_Mat_rm_ls(const Mat_rm *const A, const Mat_rm *const B, \r\n        Mat_rm *const X);\r\n\r\nint transpose_Mat_rm(const Mat_rm *const src, Mat_rm *const dst);\r\n\r\nint det_symm_Mat_rm(Mat_rm *mat, void *det);\r\n\r\nint zero_Mat_rm(Mat_rm *const mat);\r\n \r\nint identity_Mat_rm(const int n, Mat_rm *const mat);\r\n\r\nvoid cleanup_Mat_rm(Mat_rm *mat);\r\n\r\nint init_tform(void *const tform, const tform_type type);\r\n\r\nint init_Affine(Affine *const affine, const int dim);\r\n\r\nint copy_tform(const void *const src, void *const dst);\r\n\r\nint Affine_set_mat(const Mat_rm *const mat, Affine *const affine);\r\n\r\nvoid apply_tform_xyz(const void *const tform, const double x_in, \r\n                     const double y_in, const double z_in, double *const x_out,\r\n\t\t     double *const y_out, double *const z_out);\r\n\r\nint apply_tform_Mat_rm(const void *const tform, const Mat_rm *const mat_in, \r\n        Mat_rm *const mat_out);\r\n\r\ntform_type tform_get_type(const void *const tform);\r\n\r\nsize_t tform_get_size(const void *const tform);\r\n\r\nsize_t tform_type_get_size(const tform_type type);\r\n\r\nvoid cleanup_tform(void *const tform);\r\n\r\nint write_tform(const char *path, const void *const tform);\r\n\r\nint mul_Mat_rm(const Mat_rm *const mat_in1, const Mat_rm *const mat_in2, \r\n        Mat_rm *const mat_out);\r\n\r\nint draw_grid(Image *grid, int nx, int ny, int nz, int spacing, \r\n\t\t\t\t\t   int line_width);\r\n\r\nint draw_points(const Mat_rm *const in, const int *const dims, int radius, \r\n                Image *const out);\r\n\r\nint draw_lines(const Mat_rm *const points1, const Mat_rm *const points2, \r\n\t       const int *const dims, Image *const out);\r\n\r\nim_format im_get_format(const char *path);\r\n\r\nint im_read(const char *path, Image *const im);\r\n\r\nint im_write(const char *path, const Image *const im);\r\n\r\nchar *im_get_parent_dir(const char *path);\r\n\r\nint write_Mat_rm(const char *path, const Mat_rm *const mat);\r\n\r\nint init_im_with_dims(Image *const im, const int nx, const int ny, const int nz,\r\n                        const int nc);\r\n\r\nint im_load_cl(Image *im, int blocking);\r\n\r\nint im_copy_dims(const Image *const src, Image *dst);\r\n\r\nint im_copy_data(const Image *const src, Image *const dst);\r\n\r\nvoid im_free(Image *im);\r\n\r\nint im_channel(const Image * const src, Image * const dst,\r\n\t       const unsigned int chan);\r\n\r\nint im_downsample_2x(const Image *const src, Image *const dst);\r\n\r\nint im_downsample_2x_cl(Image *src, Image *dst);\r\n\r\nint im_read_back(Image *im, int blocking);\r\n\r\nint im_set_kernel_arg(cl_kernel kernel, int n, Image *im);\r\n\r\nint im_permute(const Image *const src, const int dim1, const int dim2, \r\n\t\t Image *const dst);\r\n\r\nint im_upsample_2x(const Image *const src, Image *const dst);\r\n\r\nint im_pad(const Image *const im, Image *const pad);\r\n\r\nvoid im_default_stride(Image *const im);\r\n\r\nint im_resize(Image *const im);\r\n\r\nint im_concat(const Image *const src1, const Image *const src2, const int dim, \r\n\t      Image *const dst);\r\n\r\nfloat im_max_abs(const Image *const im);\r\n\r\nvoid im_scale(const Image *const im);\r\n\r\nint im_subtract(Image *src1, Image *src2, Image *dst);\r\n\r\nvoid im_zero(Image *im);\r\n\r\nvoid im_Hessian(Image *im, int x, int y, int z, Mat_rm *H);\r\n\r\nint im_inv_transform(const void *const tform, const Image * const src,\r\n\t\t     const interp_type interp, const int resize, \r\n                     Image *const dst);\r\n\r\nint im_resample(const Image *const src, const double *const units, \r\n\tconst interp_type interp, Image *const dst);\r\n\r\nvoid init_im(Image *const im);\r\n\r\nint init_Gauss_filter(Gauss_filter *const gauss, const double sigma, \r\n                      const int dim);\r\n\r\nint init_Gauss_incremental_filter(Gauss_filter *const gauss, \r\n                const double s_cur, const double s_next, const int dim); \r\n\r\nint init_Sep_FIR_filter(Sep_FIR_filter *const f, const int dim, const int width,\r\n\t\t\tconst float *const kernel, const int symmetric);\r\n\r\nint apply_Sep_FIR_filter(const Image *const src, Image *const dst, \r\n        Sep_FIR_filter *const f, const double unit);\r\n\r\nvoid cleanup_Sep_FIR_filter(Sep_FIR_filter *const f);\r\n\r\nvoid cleanup_Gauss_filter(Gauss_filter *gauss);\r\n\r\nvoid init_GSS_filters(GSS_filters *const gss);\r\n\r\nint make_gss(GSS_filters *const gss, const Pyramid *const pyr);\r\n\r\nvoid cleanup_GSS_filters(GSS_filters *const gss);\r\n\r\nvoid init_Pyramid(Pyramid *const pyr);\r\n\r\nint copy_Pyramid(const Pyramid *const src, Pyramid *const dst);\r\n\r\nint resize_Pyramid(const Image *const im, const int first_level, \r\n        const unsigned int num_kp_levels, const unsigned int num_levels,\r\n        const int first_octave, const unsigned int num_octaves, \r\n        Pyramid *const pyr);\r\n\r\nint set_scales_Pyramid(const double sigma0, const double sigma_n, \r\n        Pyramid *const pyr);\r\n\r\nvoid cleanup_Pyramid(Pyramid *const pyr);\r\n\r\nvoid init_Slab(Slab *const slab);\r\n\r\nvoid cleanup_Slab(Slab *const slab);\r\n\r\nint resize_Slab(Slab *slab, int num, size_t size);\r\n\r\nint write_pyramid(const char *path, Pyramid *pyr);\r\n\r\nvoid err_exit(const char *str);\r\n\r\nvoid init_Ransac(Ransac *const ran);\r\n\t\t\t\t\t  \r\nint set_err_thresh_Ransac(Ransac *const ran, double err_thresh);\r\n\r\nint set_num_iter_Ransac(Ransac *const ran, int num_iter);\r\n\r\nint copy_Ransac(const Ransac *const src, Ransac *const dst);\r\n\r\nint find_tform_ransac(const Ransac *const ran, const Mat_rm *const src, \r\n        const Mat_rm *const ref, void *const tform);\r\n\r\nint parse_gnu(const int argc, char *const *argv);\r\n\r\nvoid print_bug_msg();\r\n\r\n#ifdef __cplusplus\r\n}\r\n#endif\r\n\r\n#endif\r\n"
  },
  {
    "path": "imutil/kernels.cl",
    "content": "/* -----------------------------------------------------------------------------\n * kernels.cl \n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * This file contains OpenCL kernels for image processing.\n * -----------------------------------------------------------------------------\n */\n\n#pragma OPENCL EXTENSION cl_khr_3d_image_writes : enable\n\nsampler_t sampler_downsample_2x = CLK_NORMALIZED_COORDS_FALSE |\n\t\t\t\t  CLK_ADDRESS_CLAMP_TO_EDGE |\n\t\t\t\t  CLK_FILTER_NEAEREST;\n\nkernel void downsample_2x_3d(__read_only image3d_t src,\n\t\t\t\t\t\t \t __write_only image3d_t dst) {\n\n\tint x, y, z;\n\tfloat4 out;\n\n\tx = get_global_id(0);\n\ty = get_global_id(1);\n\tz = get_global_id(2);\n\tout = read_imagef(src, sampler_downsample_2x, (int4) (x, y, z, 0) * 2);\n\twrite_imagef(dst, (int4) (x, y, z, 0), out);\n}\n"
  },
  {
    "path": "imutil/nifti.c",
    "content": "/* -----------------------------------------------------------------------------\n * nifti.c \n * -----------------------------------------------------------------------------\n * Copyright (c) 2017 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * Implementation of the nifticlib wrapper for reading and writing NIFTI images.\n * -----------------------------------------------------------------------------\n */\n\n/* SIFT3D includes */ \n#include \"imutil.h\"\n#include \"immacros.h\"\n#include \"nifti.h\"\n\n#ifndef SIFT3D_WITH_NIFTI\n/* Return error messages if this was not compiled with NIFTI support. */ \n\nstatic int nii_error_message() {\n        SIFT3D_ERR(\"nii_error_message: SIFT3D was not compiled with NIFTI \"\n                \"support!\\n\");\n        return SIFT3D_WRAPPER_NOT_COMPILED;\n}\n\nint read_nii(const char *path, Image *const im) {\n        return nii_error_message();\n}\n\nint write_nii(const char *path, const Image *const im) {\n        return nii_error_message();\n}\n\n#else\n\n/* Standard includes */\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n\n/* Nifti includes */\n#include <nifti1_io.h>\n\n/* Macro returning NIFTI data index for the channel dimension */\n#define SIFT3D_NIM_GET_IDX(im, x, y, z, c) ( \\\n        (x) + (y) * (im)->nx + (z) * (im)->nx * (im)->ny + \\\n        (c) * (im)->nx * (im)->ny * (im)->nz )\n\n/* Helper function to read a NIFTI image (.nii, .nii.gz).\n * Prior to calling this function, use init_im(im).\n * This function allocates memory.\n */\nint read_nii(const char *path, Image *const im)\n{\n\n\tnifti_image *nifti;\n        double slope;\n\tint x, y, z, c, i, dim_counter;\n\n        const size_t nim_channel_stride = im->nx * im->ny * im->nz;\n\n\t// Read NIFTI file\n\tif ((nifti = nifti_image_read(path, 1)) == NULL) {\n\t\tSIFT3D_ERR(\"read_nii: failure loading file %s\", path);\n                return SIFT3D_FAILURE;\n\t}\n\n\t// Find the dimensionality of the array, given by the last dimension\n\t// greater than 1. Note that the dimensions begin at dim[1].\n\tfor (dim_counter = nifti->ndim; dim_counter > 0; dim_counter--) {\n\t\tif (nifti->dim[dim_counter] > 1) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n        // Check the dimensionality. 4D is interpreted as a 3D array with\n        // multiple channels.\n\tif (dim_counter > 4) {\n\t\tSIFT3D_ERR(\"read_nii: file %s has unsupported \"\n\t\t\t\"dimensionality %d\\n\", path, dim_counter);\n\t\tgoto read_nii_quit;\n\t}\n\n        // Fill the trailing dimensions with 1\n        for (i = dim_counter; i < IM_NDIMS; i++) {\n                SIFT3D_IM_GET_DIMS(im)[i] = 1;\n        }\n\n\t// Store the real world coordinates\n\tim->ux = nifti->dx;\n\tim->uy = nifti->dy;\n\tim->uz = nifti->dz;\n\n\t// Resize im    \n\tim->nx = nifti->nx;\n\tim->ny = nifti->ny;\n\tim->nz = nifti->nz;\n\tim->nc = dim_counter == 4 ? nifti->nt : 1;\n\tim_default_stride(im);\n\tim_resize(im);\n\n        // Ignore the slope if it's zero. This is an ill-formatted image.\n        slope = nifti->scl_slope;\n        if (slope == 0.0) slope = 1.0;\n\n        // Macro to copy the data for each type\n#define IM_COPY_FROM_TYPE(type) \\\n        SIFT3D_IM_LOOP_START_C(im, x, y, z, c)   \\\n                SIFT3D_IM_GET_VOX(im, x, y, z, c) = (float) ( \\\n                        (double) ((type *) nifti->data)[\\\n                                SIFT3D_NIM_GET_IDX(im, x, y, z, c)] * \\\n                                (double) slope + (double) nifti->scl_inter); \\\n        SIFT3D_IM_LOOP_END_C\n\n\t// Copy the data into im, applying the slope and intercept\n\tswitch (nifti->datatype) {\n\tcase NIFTI_TYPE_UINT8:\n\t\tIM_COPY_FROM_TYPE(uint8_t);\n\t\tbreak;\n\tcase NIFTI_TYPE_INT8:\n\t\tIM_COPY_FROM_TYPE(int8_t);\n\t\tbreak;\n\tcase NIFTI_TYPE_UINT16:\n\t\tIM_COPY_FROM_TYPE(uint16_t);\n\t\tbreak;\n\tcase NIFTI_TYPE_INT16:\n\t\tIM_COPY_FROM_TYPE(int16_t);\n\t\tbreak;\n\tcase NIFTI_TYPE_UINT32:\n\t\tIM_COPY_FROM_TYPE(uint32_t);\n\t\tbreak;\n\tcase NIFTI_TYPE_INT32:\n\t\tIM_COPY_FROM_TYPE(int32_t);\n\t\tbreak;\n\tcase NIFTI_TYPE_UINT64:\n\t\tIM_COPY_FROM_TYPE(uint64_t);\n\t\tbreak;\n\tcase NIFTI_TYPE_INT64:\n\t\tIM_COPY_FROM_TYPE(int64_t);\n\t\tbreak;\n\tcase NIFTI_TYPE_FLOAT32:\n\t\tIM_COPY_FROM_TYPE(float);\n\t\tbreak;\n\tcase NIFTI_TYPE_FLOAT64:\n\t\tIM_COPY_FROM_TYPE(double);\n\t\tbreak;\n\tcase NIFTI_TYPE_FLOAT128:\n\tcase NIFTI_TYPE_COMPLEX128:\n\tcase NIFTI_TYPE_COMPLEX256:\n\tcase NIFTI_TYPE_COMPLEX64:\n\tdefault:\n\t\tSIFT3D_ERR(\"read_nii: unsupported datatype %s \\n\",\n\t\t\tnifti_datatype_string(nifti->datatype));\n                goto read_nii_quit;\n\t}\n#undef IM_COPY_FROM_TYPE\n\n\t// Clean up NIFTI data\n\tnifti_free_extensions(nifti);\n\tnifti_image_free(nifti);\n\n\treturn SIFT3D_SUCCESS;\n\nread_nii_quit:\n        nifti_free_extensions(nifti);\n        nifti_image_free(nifti);\n\treturn SIFT3D_FAILURE;\n}\n\n/* Write a Image to the specified path, in NIFTI format.\n * The path extension must be one of (.nii, .nii.gz). */\nint write_nii(const char *path, const Image *const im)\n{\n\n\tnifti_image *nifti;\n        int x, y, z, c;\n\n        const size_t nim_channel_stride = im->nx * im->ny * im->nz;\n        const int multi_channel = im->nc > 1;\n\tconst int dims[] = {multi_channel ? 4 : 3, \n            im->nx, im->ny, im->nz, multi_channel ? im->nc : 0, 0, 0, 0};\n\n\t// Initialize a nifti struct and allocate memory\n\tif ((nifti = nifti_make_new_nim(dims, DT_FLOAT32, 1)) == NULL)\n\t\tgoto write_nii_quit;\n\n        // Set the slope and intercept to do nothing\n        nifti->scl_slope = 1.0;\n        nifti->scl_inter = 0.0;\n\n        // Copy the units\n        nifti->dx = im->ux;\n        nifti->dy = im->uy;\n        nifti->dz = im->uz;\n        if (multi_channel) \n            nifti->dt = 0.f; // Channels have no size\n\n\t// Copy the data\n        SIFT3D_IM_LOOP_START_C(im, x, y, z, c)\n                ((float *) nifti->data)[SIFT3D_NIM_GET_IDX(im, x, y, z, c)] =\n                        SIFT3D_IM_GET_VOX(im, x, y, z, c);\n        SIFT3D_IM_LOOP_END_C\n\n\tif (nifti_set_filenames(nifti, path, 0, 1))\n\t\tgoto write_nii_quit;\n\n\t// Sanity check\n\tif (!nifti_nim_is_valid(nifti, 1))\n\t\tgoto write_nii_quit;\n\n\tnifti_image_write(nifti);\n\tnifti_free_extensions(nifti);\n\tnifti_image_free(nifti);\n\n\treturn SIFT3D_SUCCESS;\n\n write_nii_quit:\n\tif (nifti != NULL) {\n\t\tnifti_free_extensions(nifti);\n\t\tnifti_image_free(nifti);\n\t}\n\treturn SIFT3D_FAILURE;\n}\n\n#endif\n"
  },
  {
    "path": "imutil/nifti.h",
    "content": "/* -----------------------------------------------------------------------------\n * nifti.h \n * -----------------------------------------------------------------------------\n * Copyright (c) 2017 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * Internal header file for the nifticlib wrapper.\n * -----------------------------------------------------------------------------\n */\n\n#ifndef _NIFTI_H\n#define _NIFTI_H\n\nint read_nii(const char *path, Image *const im);\n\nint write_nii(const char *path, const Image *const im);\n\n#endif\n"
  },
  {
    "path": "imutil/templates/CMakeLists.txt",
    "content": "################################################################################\n# Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n################################################################################\n# Build file for code snippets \n################################################################################\n\ninstall (FILES sep_fir_3d.template DESTINATION ${INSTALL_INCLUDE_DIR}/templates)\n"
  },
  {
    "path": "imutil/templates/sep_fir_3d.template",
    "content": "/* -----------------------------------------------------------------------------\n * sep_fir_3d.template \n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * This file contains an OpenCL C template for a separable FIR filter.\n * -----------------------------------------------------------------------------\n */\n\n#pragma OPENCL EXTENSION cl_khr_3d_image_writes : enable\n\nsampler_t sampler = CLK_NORMALIZED_COORDS_FALSE |\n                    CLK_ADDRESS_CLAMP_TO_EDGE |\n                    CLK_FILTER_NEAEREST;\n\nkernel void sep_fir_3d(__read_only image3d_t src,\n\t \t       __write_only image3d_t dst, const int dx,\n\t\t       const int dy, const int dz) {\n\n\tint4 center, d_xyz;\n\tint x, y, z;\n\tfloat acc;\n\n\t// Form dimension adjustment\n\td_xyz = (int4) (dx, dy, dz, 0);\n\n\tx = get_global_id(0);\n\ty = get_global_id(1);\n\tz = get_global_id(2);\n\tacc = 0.0f;\n\tcenter = (int4) (x, y, z, 0);\n"
  },
  {
    "path": "reg/CMakeLists.txt",
    "content": "################################################################################\n# Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n################################################################################\n# Build file for image registration.\n################################################################################\n\nadd_library (reg SHARED reg.c)\ntarget_link_libraries (reg PUBLIC sift3D imutil)\ntarget_link_libraries (reg PRIVATE ${M_LIBRARY})\ntarget_include_directories (reg PUBLIC\n                $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>\n                $<INSTALL_INTERFACE:${INSTALL_INCLUDE_DIR}>\n)\ninstall (FILES reg.h DESTINATION ${INSTALL_INCLUDE_DIR})\n\ninstall (TARGETS reg EXPORT SIFT3D-targets\n\t RUNTIME DESTINATION ${INSTALL_BIN_DIR} \n\t LIBRARY DESTINATION ${INSTALL_LIB_DIR} \n\t ARCHIVE DESTINATION ${INSTALL_LIB_DIR})\n\n# If Matlab was found, compile a copy for use with matlab wrappers\nif (BUILD_Matlab)\n\n        add_library (mexreg SHARED reg.c)\n        target_link_libraries (mexreg PUBLIC mexsift3D meximutil)\n        target_link_libraries (mexreg PRIVATE ${M_LIBRARY})\n        target_include_directories (mexreg PUBLIC\n                $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>\n                $<INSTALL_INTERFACE:${INSTALL_INCLUDE_DIR}>\n        )\n        set_target_properties (mexreg\n                PROPERTIES \n                ARCHIVE_OUTPUT_DIRECTORY ${BUILD_TOOLBOX_DIR}\n                LIBRARY_OUTPUT_DIRECTORY ${BUILD_TOOLBOX_DIR}\n                RUNTIME_OUTPUT_DIRECTORY ${BUILD_TOOLBOX_DIR}\n        )\n\n        install (TARGETS mexreg\n                RUNTIME DESTINATION ${INSTALL_TOOLBOX_DIR}\n                LIBRARY DESTINATION ${INSTALL_TOOLBOX_DIR}\n                ARCHIVE DESTINATION ${INSTALL_TOOLBOX_DIR}\n        )\nendif ()\n"
  },
  {
    "path": "reg/reg.c",
    "content": "/* -----------------------------------------------------------------------------\n * reg.c\n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * This file contains routines for image registration.\n * -----------------------------------------------------------------------------\n */\n\n#include <stdlib.h>\n#include <string.h>\n#include <math.h>\n#include \"reg.h\"\n#include \"imtypes.h\"\n#include \"immacros.h\"\n#include \"sift.h\"\n#include \"imutil.h\"\n\n/* Stringify a macro */\n#define _SIFT3D_TO_STR(s) #s\n#define SIFT3D_TO_STR(s) _SIFT3D_TO_STR(s)\n\n/* Default parameters */\nconst double SIFT3D_nn_thresh_default = 0.8; // Default matching threshold\n\n/* Internal helper routines */\nstatic void scale_SIFT3D(const double *const factors, \n\tSIFT3D_Descriptor_store *const d);\nstatic int im2mm(const Mat_rm *const im, const double *const units, \n        Mat_rm *const mm);\nstatic int mm2im(const double *const src_units, const double *const ref_units,\n        void *const tform);\n\n/* Convert an [mxIM_NDIMS] coordinate matrix from image space to mm. \n *\n * Parameters:\n *   im: The input coordinate matrix, in image space. \n *   units: An array of length IM_NDIMS giving the units of image space.\n *   mm: The output coordinate matrix, in mm.\n *\n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise.\n */\nstatic int im2mm(const Mat_rm *const im, const double *const units, \n        Mat_rm *const mm) {\n\n        int i, j;\n\n        // Verify inputs\n        if (im->num_cols != IM_NDIMS) {\n                SIFT3D_ERR(\"im2mm: input must have IM_NDIMS columns. \\n\");\n                return SIFT3D_FAILURE;\n        }\n        if (im->type != SIFT3D_DOUBLE) {\n                SIFT3D_ERR(\"im2mm: input must have type double. \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Copy the input \n        if (copy_Mat_rm(im, mm))\n                return SIFT3D_FAILURE;\n\n        // Convert the units\n        SIFT3D_MAT_RM_LOOP_START(mm, i, j)\n                SIFT3D_MAT_RM_GET(mm, i, j, double) *= units[j];\n        SIFT3D_MAT_RM_LOOP_END \n\n        return SIFT3D_SUCCESS;\n}\n\n/* Convert a transformation from mm to image space.\n *\n * Parameters:\n *   tform: The transformation, which shall be modified.\n *   src_units: The units of the source image, array of length IM_NDIMS.\n *   ref_units: As src_units, but for the reference image.\n *\n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise.\n */\nstatic int mm2im(const double *const src_units, const double *const ref_units,\n        void *const tform) {\n\n        const tform_type type = tform_get_type(tform);\n\n        switch (type) {\n        case AFFINE:\n                { \n                        int i, j;\n\n                        Affine *const aff = (Affine *const) tform;\n                        Mat_rm *const A = &aff->A;\n\n                        // Verify the dimensions\n                        if (A->num_rows != IM_NDIMS) {\n                                SIFT3D_ERR(\"mm2im: Invalid transform \"\n                                        \"dimensionality: %d \\n\", A->num_rows);\n                                return SIFT3D_FAILURE;\n                        }\n\n                        // Convert the Affine transformation matrix in-place\n                        SIFT3D_MAT_RM_LOOP_START(A, i, j)\n                                // Invert the input transformation ref->mm\n                                SIFT3D_MAT_RM_GET(A, i, j, double) *= \n                                        j < IM_NDIMS ? ref_units[j] : 1.0;\n\n                                // Invert the output transformation src->mm\n                                SIFT3D_MAT_RM_GET(A, i, j, double) /= \n                                        src_units[i];\n                        SIFT3D_MAT_RM_LOOP_END\n                }\n                break; \n        default:\n                SIFT3D_ERR(\"mm2im: unsupported transform type. \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        return SIFT3D_SUCCESS; \n}\n\n/* Initialize a Reg_SIFT3D struct with the default parameters. This must be\n * called before the struct can be used. */\nint init_Reg_SIFT3D(Reg_SIFT3D *const reg) {\n\n        reg->nn_thresh = SIFT3D_nn_thresh_default;\n\tinit_SIFT3D_Descriptor_store(&reg->desc_src);\n\tinit_SIFT3D_Descriptor_store(&reg->desc_ref);\n\tinit_Ransac(&reg->ran);\n\tif (init_SIFT3D(&reg->sift3d) ||\n                init_Mat_rm(&reg->match_src, 0, 0, SIFT3D_DOUBLE, \n\t\t\tSIFT3D_FALSE) ||\n\t\tinit_Mat_rm(&reg->match_ref, 0, 0, SIFT3D_DOUBLE, \n\t\t\tSIFT3D_FALSE)) {\n                SIFT3D_ERR(\"register_SIFT3D: unexpected error \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Free all memory associated with a Reg_SIFT3D struct. reg cannot be reused\n * unless it is reinitialized. */\nvoid cleanup_Reg_SIFT3D(Reg_SIFT3D *const reg) {\n\n        cleanup_SIFT3D_Descriptor_store(&reg->desc_src);\n        cleanup_SIFT3D_Descriptor_store(&reg->desc_ref);\n        cleanup_SIFT3D(&reg->sift3d); \n        cleanup_Mat_rm(&reg->match_src);\n        cleanup_Mat_rm(&reg->match_ref);\n}\n\n/* Set the matching theshold of a Reg_SIFT3D struct. */\nint set_nn_thresh_Reg_SIFT3D(Reg_SIFT3D *const reg, const double nn_thresh) {\n\n        if (nn_thresh <= 0 || nn_thresh > 1) {\n                SIFT3D_ERR(\"set_nn_thresh_Reg_SIFT3D: invalid threshold: \"\n                        \"%f \\n\", nn_thresh);\n                return SIFT3D_FAILURE;\n        }\n\n        reg->nn_thresh = nn_thresh;\n        return SIFT3D_SUCCESS;\n}\n\n/* Set the Ransac parameters of the Reg_SIFT3D struct. */\nint set_Ransac_Reg_SIFT3D(Reg_SIFT3D *const reg, const Ransac *const ran) {\n        return copy_Ransac(ran, &reg->ran);\n}\n\n/* Set the SIFT3D parameters of the Reg_SIFT3D struct. Makes a deep copy of\n * sift3d, so you are free to modify it after calling this function. */\nint set_SIFT3D_Reg_SIFT3D(Reg_SIFT3D *const reg, const SIFT3D *const sift3d) {\n        return copy_SIFT3D(sift3d, &reg->sift3d);\n}\n\n/* Helper function for set_src_Reg_SIFT3D and set_ref_Reg_SIFT3D.\n * \n * Parameters:\n *   reg - The Reg_SIFT3D struct.\n *   im - The image, either source or reference.\n *   units - The units array in Reg_SIFT3D to be modified.\n *   desc - The descriptor store in Reg_SIFT3D to be modified.\n * \n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise. */\nstatic int set_im_Reg_SIFT3D(Reg_SIFT3D *const reg, const Image *const im,\n        double *const units, SIFT3D_Descriptor_store *const desc) {\n\n        Keypoint_store kp; \n\n        SIFT3D *const sift3d = &reg->sift3d; \n\n        /* Initialize intermediates */ \n        init_Keypoint_store(&kp); \n\n        /* Save the units */ \n        memcpy(units, SIFT3D_IM_GET_UNITS(im), IM_NDIMS * sizeof(double));\n\n        /* Detect keypoints */ \n\tif (SIFT3D_detect_keypoints(sift3d, im, &kp)) { \n\t\tSIFT3D_ERR(\"set_\" SIFT3D_TO_STR(type)  \n                        \"_Reg_SIFT3D: failed to detect keypoints\\n\"); \n                goto set_im_quit; \n        } \n\n        /* Extract descriptors */ \n\tif (SIFT3D_extract_descriptors(sift3d, &kp, desc)) { \n                SIFT3D_ERR(\"set_\" SIFT3D_TO_STR(type) \n                        \"_Reg_SIFT3D: failed to extract descriptors \\n\"); \n                goto set_im_quit; \n        } \n\n        /* Clean up */ \n        cleanup_Keypoint_store(&kp); \n\n        return SIFT3D_SUCCESS; \n\nset_im_quit: \n        cleanup_Keypoint_store(&kp); \n        return SIFT3D_FAILURE; \n} \n\n/* Set the source image. This makes a deep copy of the data, so you are free\n * to modify src after calling this function. */\nint set_src_Reg_SIFT3D(Reg_SIFT3D *const reg, const Image *const src) {\n        return set_im_Reg_SIFT3D(reg, src, reg->src_units, &reg->desc_src);\n}\n\n/* The same as set_source_Reg_SIFT3D, but sets the reference image. */\nint set_ref_Reg_SIFT3D(Reg_SIFT3D *const reg, const Image *const ref) {\n        return set_im_Reg_SIFT3D(reg, ref, reg->ref_units, &reg->desc_ref);\n}\n\n/* Run the registration procedure. \n *\n * Parameters: \n *   reg: The struct holding registration state.\n *   tform: The output transformation. If NULL, this function only performs\n *     feature matching.\n *\n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise. */\nint register_SIFT3D(Reg_SIFT3D *const reg, void *const tform) {\n\n        Mat_rm match_src_mm, match_ref_mm;\n        int *matches;\n        int i, j;\n\n        Ransac *const ran = &reg->ran;\n        Mat_rm *const match_src = &reg->match_src;\n        Mat_rm *const match_ref = &reg->match_ref;\n        const double nn_thresh = reg->nn_thresh;\n        SIFT3D_Descriptor_store *const desc_src = &reg->desc_src;\n        SIFT3D_Descriptor_store *const desc_ref = &reg->desc_ref;\n\n\t// Verify inputs\n\tif (desc_src->num <= 0) {\n\t\tSIFT3D_ERR(\"register_SIFT3D: no source image descriptors \"\n\t\t\t\"are available \\n\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n\tif (desc_ref->num <= 0) {\n\t\tSIFT3D_ERR(\"register_SIFT3D: no reference image \"\n\t\t\t\"descriptors are available \\n\");\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n        // Initialize intermediates\n        matches = NULL;\n        if (init_Mat_rm(&match_src_mm, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE) ||\n\t        init_Mat_rm(&match_ref_mm, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE)) {\n                SIFT3D_ERR(\"register_SIFT3D: failed initialization \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n\t// Match features\n\tif (SIFT3D_nn_match(desc_src, desc_ref, nn_thresh, &matches)) {\n\t\tSIFT3D_ERR(\"register_SIFT3D: failed to match \"\n                        \"descriptors \\n\");\n                goto register_SIFT3D_quit;\n        }\n\n        // Convert matches to coordinate matrices\n\tif (SIFT3D_matches_to_Mat_rm(desc_src, desc_ref, matches,\n\t\t\t\t     match_src, match_ref)) {\n\t\tSIFT3D_ERR(\"register_SIFT3D: failed to extract \"\n                        \"coordinate matrices \\n\");\n                goto register_SIFT3D_quit;\n        }\n\n        // Quit if no tform was provided\n        if (tform == NULL)\n                goto register_SIFT3D_success;\n\n        // Convert the coordinate matrices to real-world units\n        if (im2mm(match_src, reg->src_units, &match_src_mm) ||\n            im2mm(match_ref, reg->ref_units, &match_ref_mm))\n                goto register_SIFT3D_quit;\n\n\t// Find the transformation in real-world units\n\tif (find_tform_ransac(ran, &match_src_mm, &match_ref_mm, tform))\n                goto register_SIFT3D_quit;\n\n        // Convert the transformation back to image space\n        if (mm2im(reg->src_units, reg->ref_units, tform))\n                goto register_SIFT3D_quit;\n\nregister_SIFT3D_success:\n        // Clean up\n        free(matches);\n        cleanup_Mat_rm(&match_src_mm);\n        cleanup_Mat_rm(&match_ref_mm);\n\n\treturn SIFT3D_SUCCESS;\n\nregister_SIFT3D_quit:\n        free(matches);\n        cleanup_Mat_rm(&match_src_mm); \n        cleanup_Mat_rm(&match_ref_mm); \n        return SIFT3D_FAILURE;\n}\n\n/* Helper function to scale the descriptors by the given factors */\nstatic void scale_SIFT3D(const double *const factors, \n\tSIFT3D_Descriptor_store *const d) {\n\n\tdouble det, scale_factor;\n\tint i, j, k;\n\n\t// Compute the determinant of the scaling transformation\n\tdet = 1.0;\n\tfor (i = 0; i < IM_NDIMS; i++) {\n\t\tdet *= factors[i];\t\n\t}\n\n\t// Compute the scale parameter factor from the determinant\n\tscale_factor = pow(det, -1.0 / (double) IM_NDIMS);\n\t\n\t// Scale the descriptors\n\tfor (i = 0; i < d->num; i++) {\n\n\t\tSIFT3D_Descriptor *const desc = d->buf + i;\n\n\t\t// Scale the coordinates\n\t\tdesc->xd *= factors[0];\n\t\tdesc->yd *= factors[1];\n\t\tdesc->zd *= factors[2];\n\n\t\t// Adjust the scale parameter\n\t\tdesc->sd *= scale_factor;\n\t}\n}\n\n/* Like register_SIFT3D, but resamples the input images to have the same\n * physical resolution before extracting features. Use this when registering \n * images with very different resolutions. The results are converted to the\n * original resolution.\n *\n * Parameters:\n *   reg: See register_SIFT3D.\n *   src: The source, or moving image.\n *   ref: The reference, or fixed image.\n *   interp: The type of interpolation to use.\n *   tform: See register_SIFT3D. \n *\n * Note that some fields of the returned keypoints, such as scale and\n * orientation, will not make sense in the new coordinate system.\n *\n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise. */\nint register_SIFT3D_resample(Reg_SIFT3D *const reg, const Image *const src,\n\tconst Image *const ref, const interp_type interp, void *const tform) {\n\n\tdouble units_min[IM_NDIMS], factors_src[IM_NDIMS], \n\t\tfactors_ref[IM_NDIMS];\n\tImage src_interp, ref_interp;\n\tint i;\n\n\t// Check for the trivial case, when src and dst have the same units\n\tif (!memcmp(SIFT3D_IM_GET_UNITS(src), SIFT3D_IM_GET_UNITS(ref), \n\t\tIM_NDIMS * sizeof(double))) {\n\t\treturn set_src_Reg_SIFT3D(reg, src) ||\n\t\t\tset_ref_Reg_SIFT3D(reg, ref) ||\n\t\t\tregister_SIFT3D(reg, tform) ? \n\t\t\tSIFT3D_FAILURE : SIFT3D_SUCCESS;\n\t}\n\n\t// Initalize intermediates\n\tinit_im(&src_interp);\n\tinit_im(&ref_interp);\n\n\t// Compute the new units and scaling factors\n\tfor (i = 0; i < IM_NDIMS; i++) {\n\t\tconst double unit_src = SIFT3D_IM_GET_UNITS(src)[i];\n\t\tconst double unit_ref = SIFT3D_IM_GET_UNITS(ref)[i];\n\n\t\t// Compute the minimum units between the two images\n\t\tunits_min[i] = SIFT3D_MIN(unit_src, unit_ref);\n\n\t\t// Compute the scaling factors between the interpolated\n\t\t// images and the originals\n\t\tfactors_src[i] = units_min[i] / unit_src;\n\t\tfactors_ref[i] = units_min[i] / unit_ref;\n\t}\n\n\t// Resample the images\n\tif (im_resample(src, units_min, interp, &src_interp) ||\n\t\tim_resample(ref, units_min, interp, &ref_interp))\n\t\tgoto register_interp_quit;\n\n\t// Extract features from the interpolated images\n\tif (set_src_Reg_SIFT3D(reg, &src_interp) ||\n\t\tset_ref_Reg_SIFT3D(reg, &ref_interp))\n\t\tgoto register_interp_quit;\n\n\t// Convert the keypoints and descriptors to the original units\n\tscale_SIFT3D(factors_src, &reg->desc_src);\n\tscale_SIFT3D(factors_ref, &reg->desc_ref);\n\n\t// Register the images\n\tif (register_SIFT3D(reg, tform))\n\t\tgoto register_interp_quit;\n\n\t// Clean up\n\tim_free(&src_interp);\t\n\tim_free(&ref_interp);\t\n\n\treturn SIFT3D_SUCCESS;\n\nregister_interp_quit:\n\tim_free(&src_interp);\t\n\tim_free(&ref_interp);\t\n\treturn SIFT3D_FAILURE;\n}\n\n/* Write the coordinates of matching keypoints to the matrices match_src\n * and match_ref. This function uses the keypoints and the matches from\n * the last call to register_SIFT3D() on this Reg_SIFT3D struct. */\nint get_matches_Reg_SIFT3D(const Reg_SIFT3D *const reg, Mat_rm *const match_src,\n        Mat_rm *const match_ref) {\n\n        // Copy the matches\n        return copy_Mat_rm(&reg->match_src, match_src) ||\n                copy_Mat_rm(&reg->match_ref, match_ref);\n}\n\n"
  },
  {
    "path": "reg/reg.h",
    "content": "/* -----------------------------------------------------------------------------\n * reg.h\n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * Public header file for reg.c.\n * -----------------------------------------------------------------------------\n */\n\n#include \"imtypes.h\"\n\n#ifndef _REG_H\n#define _REG_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* Parameters */\nconst extern double SIFT3D_nn_thresh_default; // Default matching threshold\n\n/* Internal data for the SIFT3D + RANSAC registration process */\ntypedef struct _Reg_SIFT3D {\n\n        double src_units[IM_NDIMS], ref_units[IM_NDIMS];\n        SIFT3D sift3d;\n        Ransac ran;\n        SIFT3D_Descriptor_store desc_src, desc_ref;\n        Mat_rm match_src, match_ref;\n        double nn_thresh;\n        int verbose;\n\n} Reg_SIFT3D;\n\nint init_Reg_SIFT3D(Reg_SIFT3D *const reg);\n\nvoid cleanup_Reg_SIFT3D(Reg_SIFT3D *const reg);\n\nint register_SIFT3D(Reg_SIFT3D *const reg, void *const tform);\n\nint register_SIFT3D_resample(Reg_SIFT3D *const reg, const Image *const src,\n\tconst Image *const ref, const interp_type interp, void *const tform);\n\nint set_nn_thresh_Reg_SIFT3D(Reg_SIFT3D *const reg, const double nn_thresh);\n\nint set_Ransac_Reg_SIFT3D(Reg_SIFT3D *const reg, const Ransac *const ran);\n\nint set_SIFT3D_Reg_SIFT3D(Reg_SIFT3D *const reg, const SIFT3D *const sift3d);\n\nint set_src_Reg_SIFT3D(Reg_SIFT3D *const reg, const Image *const src);\n\nint set_ref_Reg_SIFT3D(Reg_SIFT3D *const reg, const Image *const ref);\n\nint get_matches_Reg_SIFT3D(const Reg_SIFT3D *const reg, Mat_rm *const match_src,\n        Mat_rm *const match_ref);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "sift3d/CMakeLists.txt",
    "content": "################################################################################\n# Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n################################################################################\n# Build file for 3D SIFT keypoint detection, description, and matching.\n################################################################################\n\nadd_library (sift3D SHARED sift.c)\ntarget_link_libraries (sift3D PUBLIC imutil)\ntarget_link_libraries (sift3D PRIVATE ${M_LIBRARY})\ntarget_include_directories (sift3D PUBLIC\n                $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>\n                $<INSTALL_INTERFACE:${INSTALL_INCLUDE_DIR}>\n)\ninstall (FILES sift.h DESTINATION ${INSTALL_INCLUDE_DIR})\n\n# If Matlab was found, compile a copy for use with matlab wrappers\nif (BUILD_Matlab)\n\n        add_library (mexsift3D SHARED sift.c)\n        target_include_directories (mexsift3D PUBLIC\n                $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>\n                $<INSTALL_INTERFACE:${INSTALL_INCLUDE_DIR}>\n        )\n        target_link_libraries (mexsift3D PUBLIC meximutil)\n        target_link_libraries (mexsift3D PRIVATE ${M_LIBRARY})\n        set_target_properties (mexsift3D\n                PROPERTIES \n                ARCHIVE_OUTPUT_DIRECTORY ${BUILD_TOOLBOX_DIR}\n                LIBRARY_OUTPUT_DIRECTORY ${BUILD_TOOLBOX_DIR}\n                RUNTIME_OUTPUT_DIRECTORY ${BUILD_TOOLBOX_DIR}\n        )\n        install (TARGETS mexsift3D\n                RUNTIME DESTINATION ${INSTALL_TOOLBOX_DIR}\n                LIBRARY DESTINATION ${INSTALL_TOOLBOX_DIR}\n                ARCHIVE DESTINATION ${INSTALL_TOOLBOX_DIR}\n        )\nendif ()\n\ninstall (TARGETS sift3D EXPORT SIFT3D-targets\n\t RUNTIME DESTINATION ${INSTALL_BIN_DIR} \n\t LIBRARY DESTINATION ${INSTALL_LIB_DIR} \n\t ARCHIVE DESTINATION ${INSTALL_LIB_DIR})\n"
  },
  {
    "path": "sift3d/sift.c",
    "content": "/* -----------------------------------------------------------------------------\n * sift.c\n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * This file contains all routines needed to initialize, delete, \n * and run the SIFT3D detector and descriptor. It also contains routines for\n * matching SIFT3D features and drawing the results.\n * -----------------------------------------------------------------------------\n */\n\n#include <stdlib.h>\n#include <string.h>\n#include <stdarg.h>\n#include <math.h>\n#include <assert.h>\n#include <float.h>\n#include <getopt.h>\n#include \"imtypes.h\"\n#include \"immacros.h\"\n#include \"imutil.h\"\n#include \"sift.h\"\n\n/* Implementation options */\n//#define SIFT3D_ORI_SOLID_ANGLE_WEIGHT // Weight bins by solid angle\n//#define SIFT3D_MATCH_MAX_DIST 0.3 // Maximum distance between matching features \n//#define CUBOID_EXTREMA // Search for extrema in a cuboid region\n\n/* Internal return codes */\n#define REJECT 1\n\n/* Default SIFT3D parameters. These may be overriden by \n * the calling appropriate functions. */\nconst double peak_thresh_default = 0.1; // DoG peak threshold\nconst int num_kp_levels_default = 3; // Number of levels per octave in which keypoints are found\nconst double corner_thresh_default = 0.4; // Minimum corner score\nconst double sigma_n_default = 1.15; // Nominal scale of input data\nconst double sigma0_default = 1.6; // Scale of the base octave\n\n/* SIFT3D option names */\nconst char opt_peak_thresh[] = \"peak_thresh\";\nconst char opt_corner_thresh[] = \"corner_thresh\";\nconst char opt_num_kp_levels[] = \"num_kp_levels\";\nconst char opt_sigma_n[] = \"sigma_n\";\nconst char opt_sigma0[] = \"sigma0\";\n\n/* Internal parameters */\nconst double max_eig_ratio =  0.90;\t// Maximum ratio of eigenvalue magnitudes\nconst double ori_grad_thresh = 1E-10;   // Minimum norm of average gradient\nconst double bary_eps = FLT_EPSILON * 1E1;\t// Error tolerance for barycentric coordinates\nconst double ori_sig_fctr = 1.5;        // Ratio of window parameter to keypoint scale\nconst double ori_rad_fctr =  3.0; // Ratio of window radius to parameter\nconst double desc_sig_fctr = 7.071067812; // See ori_sig_fctr, 5 * sqrt(2)\nconst double desc_rad_fctr = 2.0;  // See ori_rad_fctr\nconst double trunc_thresh = 0.2f * 128.0f / DESC_NUMEL; // Descriptor truncation threshold\n\n/* Internal math constants */\nconst double gr = 1.6180339887; // Golden ratio\n\n/* Get the index of bin j from triangle i */\n#define MESH_GET_IDX(mesh, i, j) \\\n\t((mesh)->tri[i].idx[j])\n\n/* Get bin j from triangle i */\n#define MESH_HIST_GET(mesh, hist, i, j) \\\n\t((hist)->bins[MESH_GET_IDX(mesh, i, j)])\n\n/* Clamp out of bounds polar accesses to the first or last element.\n * Note that the polar histogram is NOT circular. */\n#define HIST_GET_PO(hist, a, p) \\\n\t\t\t ((p) < 0 ? \\\n\t\t\t HIST_GET(hist, ((a) + NBINS_AZ / 2) % NBINS_AZ, 1) : \\\n\t\t\t (p) >= NBINS_PO ? \\\n\t\t \t HIST_GET(hist, ((a) + NBINS_AZ / 2) % NBINS_AZ, \\\n\t\t\t NBINS_PO - 1) : \\\n\t\t     HIST_GET(hist, a, p))\n\n/* Convert out of bounds azimuthal accesses circularly, e.g. -1 goes\n * to NBINS_AZ - 1, NBINS_AZ goes to 0. This algorithm does not work\n * if the indices wrap around more than once. */\n#define HIST_GET_AZ(hist, a, p)\t\\\n\t\t\t HIST_GET_PO(hist, ((a) + NBINS_AZ) % NBINS_AZ, p)\n\n/* Loop through a spherical image region. im and [x, y, z] are defined as\n * above. vcenter is a pointer to a Cvec specifying the center of the window.\n * rad is the radius of the window. vdisp is a pointer to a Cvec storing\n * the displacement from the window center. sqdisp is a float storing the\n * squared Euclidean distance from the window center.\n *\n * Note that the sphere is defined in real-world coordinates, i.e. those\n * with units (1, 1, 1). Thus, rad, sq_dist, and vdisp are defined in these\n * coordinates as well. However, x, y, z, and vcenter are defined in image\n * space.\n *\n * Delimit with IM_LOOP_SPHERE_END. */\n#define IM_LOOP_SPHERE_START(im, x, y, z, vcenter, rad, vdisp, sq_dist) \\\n{ \\\n        const float uxf = (float) (im)->ux; \\\n        const float uyf = (float) (im)->uy; \\\n        const float uzf = (float) (im)->uz; \\\n\tconst int x_start = SIFT3D_MAX(floorf((vcenter)->x - (rad) / uxf), 1); \\\n\tconst int x_end   = SIFT3D_MIN(ceilf((vcenter)->x + (rad) / uxf),  \\\n                im->nx - 2); \\\n\tconst int y_start = SIFT3D_MAX(floorf((vcenter)->y - (rad) / uyf), 1); \\\n\tconst int y_end   = SIFT3D_MIN(ceilf((vcenter)->y + (rad) / uyf), \\\n                im->ny - 2); \\\n\tconst int z_start = SIFT3D_MAX(floorf((vcenter)->z - (rad) / uzf), 1); \\\n\tconst int z_end   = SIFT3D_MIN(ceilf((vcenter)->z + (rad) / uzf), \\\n                im->nz - 2); \\\n\tSIFT3D_IM_LOOP_LIMITED_START(im, x, y, z, x_start, x_end, y_start, \\\n                 y_end, z_start, z_end) \\\n                (vdisp)->x = ((float) x - (vcenter)->x) * uxf; \\\n                (vdisp)->y = ((float) y - (vcenter)->y) * uyf; \\\n                (vdisp)->z = ((float) z - (vcenter)->z) * uzf; \\\n                (sq_dist) = SIFT3D_CVEC_L2_NORM_SQ(vdisp); \\\n                if ((sq_dist) > (rad) * (rad)) \\\n\t                continue; \\\n\n#define IM_LOOP_SPHERE_END SIFT3D_IM_LOOP_END }\n\n// Loop over all bins in a gradient histogram. If ICOS_HIST is defined, p\n// is not referenced\n#ifdef ICOS_HIST\n#define HIST_LOOP_START(a, p) \\\n\tfor ((a) = 0; (a) < HIST_NUMEL; (a)++) { p = p; {\n#else\n#define HIST_LOOP_START(a, p) \\\n\tfor ((p) = 0; (p) < NBINS_PO; (p)++) { \\\n\tfor ((a) = 0; (a) < NBINS_AZ; (a)++) {\n#endif\n\n// Delimit a HIST_LOOP\n#define HIST_LOOP_END }}\n\n// Get an element from a gradient histogram. If ICOS_HIST is defined, p\n// is not referenced\n#ifdef ICOS_HIST\n#define HIST_GET_IDX(a, p) (a)\n#else\n#define HIST_GET_IDX(a, p) ((a) + (p) * NBINS_AZ)\n#endif\n#define HIST_GET(hist, a, p) ((hist)->bins[HIST_GET_IDX(a, p)])\n\n// Get a column index in the matrix representation of a \n// SIFT3D_Descriptor_store struct\n#define DESC_MAT_GET_COL(hist_idx, a, p) \\\n        (((hist_idx) * HIST_NUMEL) + HIST_GET_IDX(a, p) + IM_NDIMS)\n\n// As SIFT3D_IM_GET_GRAD, but with physical units (1, 1, 1)\n#define IM_GET_GRAD_ISO(im, x, y, z, c, vd) { \\\n        SIFT3D_IM_GET_GRAD(im, x, y, z, c, vd); \\\n        (vd)->x *=  1.0f / (float) (im)->ux; \\\n        (vd)->y *= 1.0f / (float) (im)->uy; \\\n        (vd)->z *= 1.0f / (float) (im)->uz; \\\n}\n\n/* Global variables */\nextern CL_data cl_data;\n\n/* Helper routines */\nstatic int init_geometry(SIFT3D *sift3d);\nstatic int set_im_SIFT3D(SIFT3D *const sift3d, const Image *const im);\nstatic int set_scales_SIFT3D(SIFT3D *const sift3d, const double sigma0,\n        const double sigma_n);\nstatic int resize_SIFT3D(SIFT3D *const sift3d, const int num_kp_levels);\nstatic int build_gpyr(SIFT3D *sift3d);\nstatic int build_dog(SIFT3D *dog);\nstatic int detect_extrema(SIFT3D *sift3d, Keypoint_store *kp);\nstatic int assign_orientations(SIFT3D *const sift3d, Keypoint_store *const kp);\nstatic int assign_orientation_thresh(const Image *const im, \n        const Cvec *const vcenter, const double sigma, const double thresh,\n        Mat_rm *const R);\nstatic int assign_eig_ori(const Image *const im, const Cvec *const vcenter,\n                          const double sigma, Mat_rm *const R, \n                          double *const conf);\nstatic int Cvec_to_sbins(const Cvec * const vd, Svec * const bins);\nstatic void refine_Hist(Hist *hist);\nstatic int init_cl_SIFT3D(SIFT3D *sift3d);\nstatic int cart2bary(const Cvec * const cart, const Tri * const tri, \n\t\t      Cvec * const bary, float * const k);\nstatic int scale_Keypoint(const Keypoint *const src, \n        const double *const factors, Keypoint *const dst);\nstatic int smooth_scale_raw_input(const SIFT3D *const sift3d, const Image *const src,\n        Image *const dst);\nstatic int verify_keys(const Keypoint_store *const kp, const Image *const im);\nstatic int keypoint2base(const Keypoint *const src, Keypoint *const dst);\nstatic int _SIFT3D_extract_descriptors(SIFT3D *const sift3d, \n        const Pyramid *const gpyr, const Keypoint_store *const kp, \n        SIFT3D_Descriptor_store *const desc);\nstatic void SIFT3D_desc_acc_interp(const SIFT3D * const sift3d, \n\t\t\t\t   const Cvec * const vbins, \n\t\t\t\t   const Cvec * const grad,\n\t\t\t\t   SIFT3D_Descriptor * const desc);\nstatic int extract_descrip(SIFT3D *const sift3d, const Image *const im,\n\t   const Keypoint *const key, SIFT3D_Descriptor *const desc);\nstatic int argv_remove(const int argc, char **argv, \n                        const unsigned char *processed);\nstatic int extract_dense_descriptors_no_rotate(SIFT3D *const sift3d,\n        const Image *const in, Image *const desc);\nstatic int extract_dense_descriptors_rotate(SIFT3D *const sift3d,\n        const Image *const in, Image *const desc);\nstatic int extract_dense_descrip_rotate(SIFT3D *const sift3d, \n           const Image *const im, const Cvec *const vcenter, \n           const double sigma, const Mat_rm *const R, Hist *const hist);\nstatic void vox2hist(const Image *const im, const int x, const int y,\n        const int z, Hist *const hist);\nstatic void hist2vox(Hist *const hist, const Image *const im, const int x, \n        const int y, const int z);\nstatic int match_desc(const SIFT3D_Descriptor *const desc,\n        const SIFT3D_Descriptor_store *const store, const float nn_thresh);\nstatic int resize_SIFT3D_Descriptor_store(SIFT3D_Descriptor_store *const desc,\n        const int num);\n\n/* Initialize geometry tables. */\nstatic int init_geometry(SIFT3D *sift3d) {\n\n\tMat_rm V, F;\n\tCvec temp1, temp2, temp3, n;\n\tfloat mag;\n\tint i, j;\n\n\tMesh * const mesh = &sift3d->mesh;\n\n\t/* Verices of a regular icosahedron inscribed in the unit sphere. */\n\tconst float vert[] = {  0,  1,  gr,\n\t\t\t        0, -1,  gr,\n\t\t\t        0,  1, -gr,\n\t\t\t        0, -1, -gr,\n\t\t\t        1,  gr,  0,\n\t\t\t       -1,  gr,  0,\n\t\t\t        1, -gr,  0,\n\t\t\t       -1, -gr,  0,\n\t\t\t       gr,   0,  1,\n\t\t\t      -gr,   0,  1,\n\t\t\t       gr,   0, -1, \n\t\t\t      -gr,   0, -1 }; \n\n\t/* Vertex triplets forming the faces of the icosahedron. */\n\tconst float faces[] = {0, 1, 8,\n    \t\t\t       0, 8, 4,\n    \t\t\t       0, 4, 5,\n    \t\t\t       0, 5, 9,\n    \t\t\t       0, 9, 1,\n    \t\t\t       1, 6, 8,\n\t\t\t       8, 6, 10,\n\t\t\t       8, 10, 4,\n\t\t\t       4, 10, 2,\n\t\t\t       4, 2, 5,\n\t\t\t       5, 2, 11,\n\t\t\t       5, 11, 9,\n\t\t\t       9, 11, 7,\n\t\t\t       9, 7, 1,\n\t\t\t       1, 7, 6,\n\t\t\t       3, 6, 7,\n\t\t\t       3, 7, 11,\n\t\t\t       3, 11, 2,\n\t\t\t       3, 2, 10,\n\t\t\t       3, 10, 6};\n\n\t// Initialize matrices\n\tif (init_Mat_rm_p(&V, vert, ICOS_NVERT, 3, SIFT3D_FLOAT, \n\t\tSIFT3D_FALSE) ||\n\t    init_Mat_rm_p(&F, faces, ICOS_NFACES, 3, SIFT3D_FLOAT, \n\t    \tSIFT3D_FALSE))\n\t\treturn SIFT3D_FAILURE;\n\t\t\t    \n\t// Initialize triangle memory\n        init_Mesh(mesh);\n\tif ((mesh->tri = (Tri *) SIFT3D_safe_realloc(mesh->tri, \n                ICOS_NFACES * sizeof(Tri))) == NULL)\n\t\treturn SIFT3D_FAILURE;\n \n\t// Populate the triangle struct for each face\n\tfor (i = 0; i < ICOS_NFACES; i++) {\n\n\t\tTri * const tri = mesh->tri + i;\t\n\t\tCvec * const v = tri->v;\n\n\t\t// Initialize the vertices\n\t\tfor (j = 0; j < 3; j++) {\n\n\t\t\tconst float mag_expected = sqrt(1 + gr * gr);\n\t\t\tint * const idx = tri->idx + j;\n\n\t\t\t*idx = SIFT3D_MAT_RM_GET(&F, i, j, float);\n\n\t\t\t// Initialize the vector\n\t\t\tv[j].x = SIFT3D_MAT_RM_GET(&V, *idx, 0, float);\n\t\t\tv[j].y = SIFT3D_MAT_RM_GET(&V, *idx, 1, float);\n\t\t\tv[j].z = SIFT3D_MAT_RM_GET(&V, *idx, 2, float);\n\n\t\t\t// Normalize to unit length\n\t\t\tmag = SIFT3D_CVEC_L2_NORM(v + j);\n\t\t\tassert(fabsf(mag - mag_expected) < 1E-10);\n\t\t\tSIFT3D_CVEC_SCALE(v + j, 1.0f / mag);\n\t\t}\n\n\t\t// Compute the normal vector at v[0] as  (V2 - V1) X (V1 - V0)\n\t\tSIFT3D_CVEC_OP(v + 2, v + 1, -, &temp1);\n\t\tSIFT3D_CVEC_OP(v + 1, v, -, &temp2);\n\t\tSIFT3D_CVEC_CROSS(&temp1, &temp2, &n);\n\n\t\t// Ensure this vector is facing outward from the origin\n\t\tif (SIFT3D_CVEC_DOT(&n, v) < 0) {\n\t\t\t// Swap two vertices\n\t\t\ttemp1 = v[0];\n\t\t\tv[0] = v[1];\n\t\t\tv[1] = temp1;\n\n\t\t\t// Compute the normal again\n\t\t\tSIFT3D_CVEC_OP(v + 2, v + 1, -, &temp1);\n\t\t\tSIFT3D_CVEC_OP(v + 1, v, -, &temp2);\n\t\t\tSIFT3D_CVEC_CROSS(&temp1, &temp2, &n);\n\t\t}\n\t\tassert(SIFT3D_CVEC_DOT(&n, v) >= 0);\n\n\t\t// Ensure the triangle is equilateral\n\t\tSIFT3D_CVEC_OP(v + 2, v, -, &temp3);\n\t\tassert(fabsf(SIFT3D_CVEC_L2_NORM(&temp1) - \n                        SIFT3D_CVEC_L2_NORM(&temp2)) < 1E-10);\n\t\tassert(fabsf(SIFT3D_CVEC_L2_NORM(&temp1) - \n                        SIFT3D_CVEC_L2_NORM(&temp3)) < 1E-10);\n\t}\t\n\t\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Convert Cartesian coordinates to barycentric. bary is set to all zeros if\n * the problem is unstable. \n *\n * The output value k is the constant by which the ray is multiplied to\n * intersect the supporting plane of the triangle.\n *\n * This code uses the Moller-Trumbore algorithm. */\nstatic int cart2bary(const Cvec * const cart, const Tri * const tri, \n\t\t      Cvec * const bary, float * const k) {\n\n\tCvec e1, e2, t, p, q;\n\tfloat det, det_inv;\n\n\tconst Cvec * const v = tri->v;\n\n\tSIFT3D_CVEC_OP(v + 1, v, -, &e1);\n\tSIFT3D_CVEC_OP(v + 2, v, -, &e2);\n\tSIFT3D_CVEC_CROSS(cart, &e2, &p);\n\tdet = SIFT3D_CVEC_DOT(&e1, &p);\n\n\t// Reject unstable points\n\tif (fabsf(det) < bary_eps) {\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n\tdet_inv = 1.0f / det;\n\n\tt = v[0];\n\tSIFT3D_CVEC_SCALE(&t, -1.0f);\t\n\n\tSIFT3D_CVEC_CROSS(&t, &e1, &q);\n\n\tbary->y = det_inv * SIFT3D_CVEC_DOT(&t, &p);\t\n\tbary->z = det_inv * SIFT3D_CVEC_DOT(cart, &q);\n\tbary->x = 1.0f - bary->y - bary->z;\n\n\t*k = SIFT3D_CVEC_DOT(&e2, &q) * det_inv;\n\n#ifndef NDEBUG\n\tCvec temp1, temp2, temp3;\n        double residual;\n\n        if (isnan(bary->x) || isnan(bary->y) || isnan(bary->z)) {\n                printf(\"cart2bary: invalid bary (%f, %f, %f)\\n\", bary->x, \n                        bary->y, bary->z);\n                //exit(1);\n        }\n\n\t// Verify k * c = bary->x * v1 + bary->y * v2 + bary->z * v3\n\ttemp1 = v[0];\n\ttemp2 = v[1];\n\ttemp3 = v[2];\n\tSIFT3D_CVEC_SCALE(&temp1, bary->x);\n\tSIFT3D_CVEC_SCALE(&temp2, bary->y);\t\n\tSIFT3D_CVEC_SCALE(&temp3, bary->z);\t\n\tSIFT3D_CVEC_OP(&temp1, &temp2, +, &temp1);\n\tSIFT3D_CVEC_OP(&temp1, &temp3, +, &temp1);\n\tSIFT3D_CVEC_SCALE(&temp1, 1.0f / *k);\n\tSIFT3D_CVEC_OP(&temp1, cart, -, &temp1);\n        residual = SIFT3D_CVEC_L2_NORM(&temp1);\n\tif (residual > bary_eps) {\n                printf(\"cart2bary: residual: %f\\n\", residual);\n                exit(1);\n        }\n#endif\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Initialize a Keypoint_store for first use.\n * This does not need to be called to reuse the store\n * for a new image. */\nvoid init_Keypoint_store(Keypoint_store *const kp) {\n\tinit_Slab(&kp->slab);\n\tkp->buf = (Keypoint *) kp->slab.buf;\n}\n\n/* Initialize a Keypoint struct for use. This sets up the internal pointers,\n * and nothing else. If called on a valid Keypoint struct, it has no effect. */\nint init_Keypoint(Keypoint *const key) {\n        // Initialize the orientation matrix with static memory\n        return init_Mat_rm_p(&key->R, key->r_data, IM_NDIMS, IM_NDIMS, \n\t\tSIFT3D_FLOAT, SIFT3D_FALSE);\n}\n\n/* Make room for at least num Keypoint structs in kp. \n * \n * Note: This function must re-initialize some internal data if it was moved. \n * This does not affect the end user, but it affects the implementation of \n * init_Keypoint. */\nint resize_Keypoint_store(Keypoint_store *const kp, const size_t num) {\n\n        void *const buf_old = kp->slab.buf;\n\n        // Resize the internal memory\n\tSIFT3D_RESIZE_SLAB(&kp->slab, num, sizeof(struct _Keypoint));\n\tkp->buf = kp->slab.buf; \n\n        // If the size has changed, re-initialize the keypoints\n        if (buf_old != kp->slab.buf) { \n                int i; \n                for (i = 0; i < kp->slab.num; i++) { \n                        Keypoint *const key = kp->buf + i; \n                        if (init_Keypoint(key)) \n                                return SIFT3D_FAILURE; \n                } \n        } \n\n        return SIFT3D_SUCCESS;\n}\n\n/* Copy one Keypoint struct into another. */\nint copy_Keypoint(const Keypoint *const src, Keypoint *const dst) {\n\n        // Copy the shallow data \n        dst->xd = src->xd;\n        dst->yd = src->yd;\n        dst->zd = src->zd;\n        dst->sd = src->sd;\n        dst->o = src->o;\n        dst->s = src->s;\n\n        // Copy the orienation matrix\n        return copy_Mat_rm(&src->R, &dst->R);\n}\n\n/* Free all memory associated with a Keypoint_store. kp cannot be\n * used after calling this function, unless re-initialized. */\nvoid cleanup_Keypoint_store(Keypoint_store *const kp) {\n        cleanup_Slab(&kp->slab);\n}\n\n/* Initialize a SIFT_Descriptor_store for first use.\n * This does not need to be called to reuse the store\n * for a new image. */\nvoid init_SIFT3D_Descriptor_store(SIFT3D_Descriptor_store *const desc) {\n\tdesc->buf = NULL;\n}\n\n/* Free all memory associated with a SIFT3D_Descriptor_store. desc\n * cannot be used after calling this function, unless re-initialized. */\nvoid cleanup_SIFT3D_Descriptor_store(SIFT3D_Descriptor_store *const desc) {\n        free(desc->buf);\n}\n\n/* Resize a SIFT3D_Descriptor_store to hold n descriptors. Must be initialized\n * prior to calling this function. num must be positive.\n *\n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise. */\nstatic int resize_SIFT3D_Descriptor_store(SIFT3D_Descriptor_store *const desc,\n        const int num) {\n\n        if (num < 1) {\n                SIFT3D_ERR(\"resize_SIFT3D_Descriptor_store: invalid size: %d\",\n                        num);\n                return SIFT3D_FAILURE;\n        }\n\n\tif ((desc->buf = (SIFT3D_Descriptor *) SIFT3D_safe_realloc(desc->buf, \n\t\tnum * sizeof(SIFT3D_Descriptor))) == NULL)\n                return SIFT3D_FAILURE;\n\n\tdesc->num = num;\n        return SIFT3D_SUCCESS;\n}\n\n/* Initializes the OpenCL data for this SIFT3D struct. This\n * increments the reference counts for shared data. */\nstatic int init_cl_SIFT3D(SIFT3D *sift3d) {\n#ifdef SIFT3D_USE_OPENCL\n\tcl_image_format image_format;\n\n\t// Initialize basic OpenCL platform and context info\n\timage_format.image_channel_order = CL_R;\n\timage_format.image_channel_data_type = CL_FLOAT;\n\tif (init_cl(&cl_data, PLATFORM_NAME_NVIDIA, CL_DEVICE_TYPE_GPU,\n \t\t    CL_MEM_READ_WRITE | CL_MEM_ALLOC_HOST_PTR, \n                    image_format))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Load and compile the downsampling kernel\n\n#endif\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Sets the peak threshold, checking that it is in the interval (0, inf) */\nint set_peak_thresh_SIFT3D(SIFT3D *const sift3d,\n                                const double peak_thresh) {\n        if (peak_thresh <= 0.0 || peak_thresh > 1) {\n                SIFT3D_ERR(\"SIFT3D peak_thresh must be in the interval (0, 1]. \"\n                        \"Provided: %f \\n\", peak_thresh);\n                return SIFT3D_FAILURE;\n        }\n\n        sift3d->peak_thresh = peak_thresh;\n        return SIFT3D_SUCCESS;\n}\n\n/* Sets the corner threshold, checking that it is in the interval [0, 1]. */\nint set_corner_thresh_SIFT3D(SIFT3D *const sift3d,\n                                const double corner_thresh) {\n\n        if (corner_thresh < 0.0 || corner_thresh > 1.0) {\n                SIFT3D_ERR(\"SIFT3D corner_thresh must be in the interval \"\n                        \"[0, 1]. Provided: %f \\n\", corner_thresh);\n                return SIFT3D_FAILURE;\n        }\n\n        sift3d->corner_thresh = corner_thresh;\n        return SIFT3D_SUCCESS;\n}\n\n/* Sets the number of levels per octave. This function will resize the\n * internal data. */\nint set_num_kp_levels_SIFT3D(SIFT3D *const sift3d,\n                                const unsigned int num_kp_levels) {\n\n        const Pyramid *const gpyr = &sift3d->gpyr;\n\n        return resize_SIFT3D(sift3d, num_kp_levels);\n}\n\n/* Sets the nominal scale parameter of the input data, checking that it is \n * nonnegative. */\nint set_sigma_n_SIFT3D(SIFT3D *const sift3d,\n                                const double sigma_n) {\n\n        const double sigma0 = sift3d->gpyr.sigma0;\n\n        if (sigma_n < 0.0) {\n                SIFT3D_ERR(\"SIFT3D sigma_n must be nonnegative. Provided: \"\n                        \"%f \\n\", sigma_n);\n                return SIFT3D_FAILURE;\n        }\n\n        return set_scales_SIFT3D(sift3d, sigma0, sigma_n);\n}\n\n/* Sets the scale parameter of the first level of octave 0, checking that it\n * is nonnegative. */\nint set_sigma0_SIFT3D(SIFT3D *const sift3d,\n                                const double sigma0) {\n\n        const double sigma_n = sift3d->gpyr.sigma_n;\n\n        if (sigma0 < 0.0) {\n                SIFT3D_ERR(\"SIFT3D sigma0 must be nonnegative. Provided: \"\n                        \"%f \\n\", sigma0);\n                return SIFT3D_FAILURE; \n        } \n\n        return set_scales_SIFT3D(sift3d, sigma0, sigma_n);\n}\n\n/* Initialize a SIFT3D struct with the default parameters. */\nint init_SIFT3D(SIFT3D *sift3d) {\n\n        Pyramid *const dog = &sift3d->dog;\n        Pyramid *const gpyr = &sift3d->gpyr;\n        GSS_filters *const gss = &sift3d->gss;\n\n\t// Initialize to defaults\n\tconst double peak_thresh = peak_thresh_default;\n\tconst double corner_thresh = corner_thresh_default;\n\tconst int num_kp_levels = num_kp_levels_default;\n\tconst double sigma_n = sigma_n_default;\n\tconst double sigma0 = sigma0_default;\n        const int dense_rotate = SIFT3D_FALSE;\n\n\t// First-time pyramid initialization\n        init_Pyramid(dog);\n        init_Pyramid(gpyr);\n\n        // First-time filter initialization\n        init_GSS_filters(gss);\n\n        // Intialize the geometry tables\n\tif (init_geometry(sift3d))\n\t\treturn SIFT3D_FAILURE;\n\n\t// init static OpenCL programs and contexts, if support is enabled\n\tif (init_cl_SIFT3D(sift3d))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Initialize the image data\n\tinit_im(&sift3d->im);\n\n\t// Save data\n\tdog->first_level = gpyr->first_level = -1;\n        sift3d->dense_rotate = dense_rotate;\n        if (set_sigma_n_SIFT3D(sift3d, sigma_n) ||\n                set_sigma0_SIFT3D(sift3d, sigma0) ||\n                set_peak_thresh_SIFT3D(sift3d, peak_thresh) ||\n                set_corner_thresh_SIFT3D(sift3d, corner_thresh) ||\n                set_num_kp_levels_SIFT3D(sift3d, num_kp_levels))\n                return SIFT3D_FAILURE;\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Make a deep copy of a SIFT3D struct, including all internal images. */\nint copy_SIFT3D(const SIFT3D *const src, SIFT3D *const dst) {\n\n        // Free and re-initialize dst\n        cleanup_SIFT3D(dst);\n        if (init_SIFT3D(dst))\n                return SIFT3D_FAILURE;\n\n        // Copy the parameters\n        set_sigma_n_SIFT3D(dst, src->gpyr.sigma_n); \n        set_sigma0_SIFT3D(dst, src->gpyr.sigma0);\n        if (set_peak_thresh_SIFT3D(dst, src->peak_thresh) ||\n            set_corner_thresh_SIFT3D(dst, src->corner_thresh) ||\n            set_num_kp_levels_SIFT3D(dst, src->gpyr.num_kp_levels))\n                return SIFT3D_FAILURE;\n        dst->dense_rotate = src->dense_rotate;\n\n        // Copy the image, if any\n        if (src->im.data != NULL && set_im_SIFT3D(dst, &src->im))\n                return SIFT3D_FAILURE;\n\n        // Copy the pyramids, if any\n        if (copy_Pyramid(&src->gpyr, &dst->gpyr) ||\n            copy_Pyramid(&src->dog, &dst->dog))\n                return SIFT3D_FAILURE;\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Free all memory associated with a SIFT3D struct. sift3d cannot be reused\n * unless it is reinitialized. */\nvoid cleanup_SIFT3D(SIFT3D *const sift3d) {\n\n\t// Clean up the image copy\n\tim_free(&sift3d->im);\n\n        // Clean up the pyramids\n        cleanup_Pyramid(&sift3d->gpyr);\n        cleanup_Pyramid(&sift3d->dog);\n\n        // Clean up the GSS filters\n        cleanup_GSS_filters(&sift3d->gss);\n\n        // Clean up the triangle mesh \n        cleanup_Mesh(&sift3d->mesh);\n\n#ifdef USE_OPENCL\n        // Clean up the OpenCL kernels\n        cleanup_SIFT3D_cl_kernels(&sift3d->kernels);\n#endif\n}\n\n/* Helper function to remove the processed arguments from argv. \n * Returns the number of remaining arguments. */\nstatic int argv_remove(const int argc, char **argv, \n                        const unsigned char *processed) {\n\n        int i, new_pos;\n\n        // Remove the processed arguments in-place\n        new_pos = 0;\n        for (i = 0; i < argc; i++) {\n                // Skip processed arguments\n                if (processed[i])\n                        continue;\n\n                // Add the unprocessed arguments to the new argv\n                argv[new_pos++] = argv[i];                 \n        }\n\n        return new_pos;\n}\n\n/* Print the options for a SIFT3D struct to stdout. */\nvoid print_opts_SIFT3D(void) {\n\n        printf(\"SIFT3D Options: \\n\"\n               \" --%s [value] \\n\"\n               \"    The smallest allowed absolute DoG value, as a fraction \\n\"\n               \"        of the largest. Must be on the interval (0, 1]. \\n\"\n               \"        (default: %.2f) \\n\" \n               \" --%s [value] \\n\"\n               \"    The smallest allowed corner score, on the interval \\n\"\n               \"        [0, 1]. (default: %.2f) \\n\"\n               \" --%s [value] \\n\"\n               \"    The number of pyramid levels per octave in which \\n\"\n               \"        keypoints are found. Must be a positive integer. \\n\"\n               \"        (default: %d) \\n\"\n               \" --%s [value] \\n\"\n               \"    The nominal scale parameter of the input data, on the \\n\"\n               \"        interval (0, inf). (default: %.2f) \\n\"\n               \" --%s [value] \\n\"\n               \"    The scale parameter of the first level of octave 0, on \\n\"\n               \"        the interval (0, inf). (default: %.2f) \\n\",\n               opt_peak_thresh, peak_thresh_default,\n               opt_corner_thresh, corner_thresh_default,\n               opt_num_kp_levels, num_kp_levels_default,\n               opt_sigma_n, sigma_n_default,\n               opt_sigma0, sigma0_default);\n\n}\n\n/* Set the parameters of a SIFT3D struct from the given command line \n * arguments. The argument SIFT3D must be initialized with\n * init_SIFT3D prior to calling this function. \n *\n * On return, all processed SIFT3D options will be removed from argv.\n * Use argc_ret to get the number of remaining options.\n *\n * Options:\n * --peak_thresh\t - threshold on DoG extrema magnitude (double)\n * --corner_thresh - threshold on edge score (double)\n * --num_kp_levels - number of levels per octave for keypoint\n *    \t\t\tcandidates (int)\n * --sigma_n - base level of blurring assumed in data (double)\n * --sigma0 - level to blur base of pyramid (double)\n *\n * Parameters:\n *      argc - The number of arguments\n *      argv - An array of strings of arguments. All unproccesed arguments are\n *              permuted to the end.\n *      sift3d - The struct to be initialized\n *      check_err - If nonzero, report unrecognized options as errors\n *\n * Return value: \n *       Returns the new number of arguments in argv, or -1 on failure. */\nint parse_args_SIFT3D(SIFT3D *const sift3d,\n        const int argc, char **argv, const int check_err) {\n\n        unsigned char *processed;\n        double dval;\n        int c, err, ival, argc_new;\n\n#define PEAK_THRESH 'a'\n#define CORNER_THRESH 'b'\n#define NUM_KP_LEVELS 'c'\n#define SIGMA_N 'd'\n#define SIGMA0 'e'\n\n        // Options\n        const struct option longopts[] = {\n                {opt_peak_thresh, required_argument, NULL, PEAK_THRESH},\n                {opt_corner_thresh, required_argument, NULL, CORNER_THRESH},\n                {opt_num_kp_levels, required_argument, NULL, NUM_KP_LEVELS},\n                {opt_sigma_n, required_argument, NULL, SIGMA_N},\n                {opt_sigma0, required_argument, NULL, SIGMA0},\n                {0, 0, 0, 0}\n        };\n\n        // Starting getopt variables \n        const int opterr_start = opterr;\n\n        // Set the error checking behavior\n        opterr = check_err;\n\n        // Intialize intermediate data\n        if ((processed = calloc(argc, sizeof(char *))) == NULL) {\n                SIFT3D_ERR(\"parse_args_SIFT3D: out of memory \\n\");\n                return -1;\n        }\n        err = SIFT3D_FALSE;\n\n        // Process the arguments\n        while ((c = getopt_long(argc, argv, \"-\", longopts, NULL)) != -1) {\n\n                const int idx = optind - 1;\n\n                // Convert the value to double and integer\n                if (optarg != NULL) {\n                        dval = atof(optarg);\n                        ival = atoi(optarg);\n                }\n\n                switch (c) {\n                        case PEAK_THRESH:\n                                if (set_peak_thresh_SIFT3D(sift3d, \n                                        dval))\n                                        goto parse_args_quit;\n\n                                processed[idx - 1] = SIFT3D_TRUE;\n                                processed[idx] = SIFT3D_TRUE;\n                                break;\n                        case CORNER_THRESH:\n                                if (set_corner_thresh_SIFT3D(sift3d, \n                                        dval))\n                                        goto parse_args_quit;\n\n                                processed[idx - 1] = SIFT3D_TRUE;\n                                processed[idx] = SIFT3D_TRUE;\n                                break;\n                        case NUM_KP_LEVELS:\n                                // Check for errors                        \n                                if (ival <= 0) {\n                                        SIFT3D_ERR(\"SIFT3D num_kp_levels \"\n                                                \"must be positive. Provided: \"\n                                                \"%d \\n\", ival);\n                                        goto parse_args_quit;\n                                }\n\n                                if (set_num_kp_levels_SIFT3D(sift3d, \n                                        ival))\n                                        goto parse_args_quit;\n\n                                processed[idx - 1] = SIFT3D_TRUE;\n                                processed[idx] = SIFT3D_TRUE;\n                                break;\n                        case SIGMA_N:\n                                set_sigma_n_SIFT3D(sift3d, dval);\n                                processed[idx - 1] = SIFT3D_TRUE;\n                                processed[idx] = SIFT3D_TRUE;\n                                break;\n                        case SIGMA0:\n                                set_sigma0_SIFT3D(sift3d, dval);\n                                processed[idx - 1] = SIFT3D_TRUE;\n                                processed[idx] = SIFT3D_TRUE;\n                                break;\n                        case '?':\n                        default:\n                                if (!check_err)\n                                        continue;\n                                err = SIFT3D_TRUE;\n                }\n        }\n\n#undef PEAK_THRESH\n#undef CORNER_THRESH\n#undef NUM_KP_LEVELS\n#undef SIGMA_N\n#undef SIGMA0\n\n        // Put all unprocessed options at the end\n        argc_new = argv_remove(argc, argv, processed);\n\n        // Return to the default settings\n        opterr = opterr_start;\n\n        // Clean up\n        free(processed);\n\n        // Return an error, if error checking is enabled\n        if (check_err && err)\n                return -1;\n        \n        // Reset the state of getopt\n        optind = 0;\n\n        return argc_new;\n\nparse_args_quit:\n        free(processed);\n        return -1;\n}\n\n/* Helper routine to begin processing a new image. If the dimensions differ\n * from the last one, this function resizes the SIFT3D struct. */\nstatic int set_im_SIFT3D(SIFT3D *const sift3d, const Image *const im) {\n\n        int dims_old[IM_NDIMS];\n        int i;\n\n\tconst float *const data_old = sift3d->im.data;\n        const Pyramid *const gpyr = &sift3d->gpyr;\n        const int first_octave = sift3d->gpyr.first_octave;\n        const int num_kp_levels = gpyr->num_kp_levels;\n\n        // Make a temporary copy the previous image dimensions\n        for (i = 0; i < IM_NDIMS; i++) {\n                dims_old[i] = SIFT3D_IM_GET_DIMS(&sift3d->im)[i];\n        }\n\n        // Make a copy of the input image\n        if (im_copy_data(im, &sift3d->im))\n                return SIFT3D_FAILURE;\n\n        // Scale the input image to [-1, 1]\n        im_scale(&sift3d->im);\n\n        // Resize the internal data, if necessary\n        if ((data_old == NULL || \n                memcmp(dims_old, SIFT3D_IM_GET_DIMS(&sift3d->im), \n                        IM_NDIMS * sizeof(int))) &&\n                resize_SIFT3D(sift3d, num_kp_levels))\n                return SIFT3D_FAILURE;\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Helper function to set the scale parameters for a SIFT3D struct. */\nstatic int set_scales_SIFT3D(SIFT3D *const sift3d, const double sigma0,\n        const double sigma_n) {\n\n        Pyramid *const gpyr = &sift3d->gpyr;\n        Pyramid *const dog = &sift3d->dog;\n        GSS_filters *const gss = &sift3d->gss;\n\n        // Set the scales for the GSS and DOG pyramids\n        if (set_scales_Pyramid(sigma0, sigma_n, gpyr) ||\n                set_scales_Pyramid(sigma0, sigma_n, dog))\n                return SIFT3D_FAILURE;\n\n        // Do nothing more if we have no image\n        if (sift3d->im.data == NULL)\n                return SIFT3D_SUCCESS;\n\n        // Recompute the filters\n\treturn make_gss(gss, gpyr);\n}\n\n/* Resize a SIFT3D struct, allocating temporary storage and recompiling the \n * filters. Does nothing unless set_im_SIFT3D was previously called. */\nstatic int resize_SIFT3D(SIFT3D *const sift3d, const int num_kp_levels) {\n\n        int num_octaves; \n\n        const Image *const im = &sift3d->im;\n        Pyramid *const gpyr = &sift3d->gpyr;\n        Pyramid *const dog = &sift3d->dog;\n\tconst unsigned int num_dog_levels = num_kp_levels + 2;\n\tconst unsigned int num_gpyr_levels = num_dog_levels + 1;\n        const int first_octave = 0;\n        const int first_level = -1;\n\n\t// Compute the meximum allowed number of octaves\n\tif (im->data != NULL) {\n                // The minimum size of a pyramid level is 8 in any dimension\n\t\tconst int last_octave = \n                        (int) log2((double) SIFT3D_MIN(SIFT3D_MIN(im->nx, im->ny), \n                        im->nz)) - 3 - first_octave;\n\n                // Verify octave parameters\n                if (last_octave < first_octave) {\n                        SIFT3D_ERR(\"resize_SIFT3D: input image is too small: \"\n                                \"must have at least 8 voxels in each \"\n                                \"dimension \\n\");\n                        return SIFT3D_FAILURE;\n                }\n\n                num_octaves = last_octave - first_octave + 1;\n\t} else {\n                num_octaves = 0;\n        }\n\n\t// Resize the pyramid\n\tif (resize_Pyramid(im, first_level, num_kp_levels,\n                num_gpyr_levels, first_octave, num_octaves, gpyr) ||\n\t        resize_Pyramid(im, first_level, num_kp_levels, \n                num_dog_levels, first_octave, num_octaves, dog))\n\t\treturn SIFT3D_FAILURE;\n\n        // Do nothing more if we have no image\n        if (im->data == NULL)\n                return SIFT3D_SUCCESS;\n\n\t// Compute the Gaussian filters\n\tif (make_gss(&sift3d->gss, &sift3d->gpyr))\n\t\treturn SIFT3D_FAILURE;\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Build the GSS pyramid on a single CPU thread */\nstatic int build_gpyr(SIFT3D *sift3d) {\n\n        const Image *prev;\n\tSep_FIR_filter *f;\n\tImage *cur;\n\tint o, s;\n\n\tPyramid *const gpyr = &sift3d->gpyr;\n\tconst GSS_filters *const gss = &sift3d->gss;\n\tconst int s_start = gpyr->first_level + 1;\n\tconst int s_end = SIFT3D_PYR_LAST_LEVEL(gpyr);\n\tconst int o_start = gpyr->first_octave;\n\tconst int o_end = SIFT3D_PYR_LAST_OCTAVE(gpyr);\n        const double unit = 1.0;\n\n\t// Build the first image\n\tcur = SIFT3D_PYR_IM_GET(gpyr, o_start, s_start - 1);\n\tprev = &sift3d->im;\n#ifdef SIFT3D_USE_OPENCL\n\tif (im_load_cl(cur, SIFT3D_FALSE))\n\t\treturn SIFT3D_FAILURE;\t\n#endif\n\n\tf = (Sep_FIR_filter *) &gss->first_gauss.f;\n\tif (apply_Sep_FIR_filter(prev, cur, f, unit))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Build the rest of the pyramid\n\tSIFT3D_PYR_LOOP_LIMITED_START(o, s, o_start, o_end, s_start, s_end)\n\t\t\tcur = SIFT3D_PYR_IM_GET(gpyr, o, s);\n\t\t\tprev = SIFT3D_PYR_IM_GET(gpyr, o, s - 1);\n\t\t\tf = &gss->gauss_octave[s].f;\n\t\t\tif (apply_Sep_FIR_filter(prev, cur, f, unit))\n\t\t\t\treturn SIFT3D_FAILURE;\n#ifdef SIFT3D_USE_OPENCL\n\t\t\tif (im_read_back(cur, SIFT3D_FALSE))\n\t\t\t\treturn SIFT3D_FAILURE;\n#endif\n\t\tSIFT3D_PYR_LOOP_SCALE_END\n\t\t// Downsample\n\t\tif (o != o_end) {\n\n                        const int downsample_level = \n                                SIFT3D_MAX(s_end - 2, gpyr->first_level);\n\n\t\t\tprev = SIFT3D_PYR_IM_GET(gpyr, o, downsample_level);\n\t\t\tcur = SIFT3D_PYR_IM_GET(gpyr, o + 1, s_start - 1);\n\n                        assert(fabs(prev->s - cur->s) < FLT_EPSILON);\n\n\t\t\tif (im_downsample_2x(prev, cur))\n\t\t\t\treturn SIFT3D_FAILURE;\n\n\t\t}\n\tSIFT3D_PYR_LOOP_OCTAVE_END\n\n#ifdef SIFT3D_USE_OPENCL\n\tclFinish_all();\n#endif\n\n\treturn SIFT3D_SUCCESS;\n}\n\nstatic int build_dog(SIFT3D *sift3d) {\n\n\tImage *gpyr_cur, *gpyr_next, *dog_level;\n\tint o, s;\n\n\tPyramid *const dog = &sift3d->dog;\n\tPyramid *const gpyr = &sift3d->gpyr;\n\n\tSIFT3D_PYR_LOOP_START(dog, o, s)\n\t\tgpyr_cur = SIFT3D_PYR_IM_GET(gpyr, o, s);\n\t\tgpyr_next = SIFT3D_PYR_IM_GET(gpyr, o, s + 1);\t\t\t\n\t\tdog_level = SIFT3D_PYR_IM_GET(dog, o, s);\n\t\t\n\t\tif (im_subtract(gpyr_cur, gpyr_next, \n\t\t\t\t\t\tdog_level))\n\t\t\treturn SIFT3D_FAILURE;\n\tSIFT3D_PYR_LOOP_END\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Detect local extrema */\nstatic int detect_extrema(SIFT3D *sift3d, Keypoint_store *kp) {\n\n\tImage *cur, *prev, *next;\n\tKeypoint *key;\n\tfloat pcur, dogmax, peak_thresh;\n\tint o, s, x, y, z, x_start, x_end, y_start, y_end, z_start,\n\t\tz_end, num;\n\n\tconst Pyramid *const dog = &sift3d->dog;\n\tconst int o_start = dog->first_octave;\n\tconst int o_end = SIFT3D_PYR_LAST_OCTAVE(dog);\n\tconst int s_start = dog->first_level + 1;\n\tconst int s_end = SIFT3D_PYR_LAST_LEVEL(dog) - 1;\n\n\t// Verify the inputs\n\tif (dog->num_levels < 3) {\n\t\tprintf(\"detect_extrema: Requires at least 3 levels per octave, \"\n\t\t\t   \"provided only %d \\n\", dog->num_levels);\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n\t// Initialize dimensions of keypoint store\n\tcur = SIFT3D_PYR_IM_GET(dog, o_start, s_start);\n\tkp->nx = cur->nx;\n\tkp->ny = cur->ny;\n\tkp->nz = cur->nz;\n\n#define CMP_CUBE(im, x, y, z, CMP, IGNORESELF, val) ( \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x),     (y),     (z) - 1, 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x) - 1, (y),     (z) - 1, 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x) + 1, (y),     (z) - 1, 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x),     (y) - 1, (z) - 1, 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x),     (y) + 1, (z) - 1, 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x) - 1, (y) - 1, (z) - 1, 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x) + 1, (y) - 1, (z) - 1, 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x) - 1, (y) + 1, (z) - 1, 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x) + 1, (y) + 1, (z) - 1, 0) && \\\n\t((val) CMP SIFT3D_IM_GET_VOX( (im), (x),     (y),    (z), 0   ) || \\\n\t    IGNORESELF) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x) - 1, (y),     (z), 0    ) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x) + 1, (y),     (z), 0    ) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x),     (y) - 1, (z), 0    ) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x),     (y) + 1, (z), 0    ) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x) - 1, (y) - 1, (z), 0    ) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x) + 1, (y) - 1, (z), 0    ) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x) - 1, (y) + 1, (z), 0    ) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x) + 1, (y) + 1, (z), 0    ) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x),     (y),     (z) + 1, 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x) - 1, (y),     (z) + 1, 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x) + 1, (y),     (z) + 1, 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x),     (y) - 1, (z) + 1, 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x),     (y) + 1, (z) + 1, 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x) - 1, (y) - 1, (z) + 1, 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x) + 1, (y) - 1, (z) + 1, 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x) - 1, (y) + 1, (z) + 1, 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x) + 1, (y) + 1, (z) + 1, 0) )\n#ifdef CUBOID_EXTREMA\n#define CMP_PREV(im, x, y, z, CMP, val) \\\n        CMP_CUBE(im, x, y, z, CMP, SIFT3D_FALSE, val)\n#define CMP_CUR(im, x, y, z, CMP, val) \\\n        CMP_CUBE(im, x, y, z, CMP, SIFT3D_TRUE, val)\n#define CMP_NEXT(im, x, y, z, CMP, val) \\\n        CMP_CUBE(im, x, y, z, CMP, SIFT3D_FALSE, val)\n#else\n#define CMP_PREV(im, x, y, z, CMP, val) ( \\\n        (val) CMP SIFT3D_IM_GET_VOX( (im), (x), (y), (z), 0) \\\n)\n#define CMP_CUR(im, x, y, z, CMP, val) ( \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x) + 1, (y),     (z), 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x) - 1, (y),     (z), 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x),     (y) + 1, (z), 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x),     (y) - 1, (z), 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x),     (y),     (z) - 1, 0) && \\\n\t(val) CMP SIFT3D_IM_GET_VOX( (im), (x),     (y),     (z) + 1, 0) \\\n)\n#define CMP_NEXT(im, x, y, z, CMP, val) \\\n        CMP_PREV(im, x, y, z, CMP, val)\n#endif\n\n\tnum = 0;\n\tSIFT3D_PYR_LOOP_LIMITED_START(o, s, o_start, o_end, s_start, s_end)  \n\n\t\t// Select current and neighboring levels\n\t\tprev = SIFT3D_PYR_IM_GET(dog, o, s - 1);\n\t\tcur = SIFT3D_PYR_IM_GET(dog, o, s);\n\t\tnext = SIFT3D_PYR_IM_GET(dog, o, s + 1);\n\n\t\t// Find maximum DoG value at this level\n\t\tdogmax = 0.0f;\n\t\tSIFT3D_IM_LOOP_START(cur, x, y, z)\n\t\t\tdogmax = SIFT3D_MAX(dogmax, \n                                fabsf(SIFT3D_IM_GET_VOX(cur, x, y, z, 0)));\n\t\tSIFT3D_IM_LOOP_END\n\n\t\t// Adjust threshold\n\t\tpeak_thresh = sift3d->peak_thresh * dogmax;\n\n\t\t// Loop through all non-boundary pixels\n\t\tx_start = y_start = z_start = 1;\n\t\tx_end = cur->nx - 2;\n\t\ty_end = cur->ny - 2;\n\t\tz_end = cur->nz - 2;\n\t\tSIFT3D_IM_LOOP_LIMITED_START(cur, x, y, z, x_start, x_end, y_start,\n\t\t\t\t\t\t\t  y_end, z_start, z_end)\n\t\t\t// Sample the center value\n\t\t\tpcur = SIFT3D_IM_GET_VOX(cur, x, y, z, 0);\n\n\t\t\t// Apply the peak threshold\n\t\t\tif ((pcur > peak_thresh || pcur < -peak_thresh) && ((\n\t\t\t\t// Compare to the neighbors\n\t\t\t\tCMP_PREV(prev, x, y, z, >, pcur) &&\n\t\t\t\tCMP_CUR(cur, x, y, z, >, pcur) &&\n\t\t\t\tCMP_NEXT(next, x, y, z, >, pcur)\n\t\t\t\t) || (\n\t\t\t\tCMP_PREV(prev, x, y, z, <, pcur) &&\n\t\t\t\tCMP_CUR(cur, x, y, z, <, pcur) &&\n\t\t\t\tCMP_NEXT(next, x, y, z, <, pcur))))\n\t\t\t\t{\n\n                                // Add a keypoint candidate\n                                num++;\n                                if (resize_Keypoint_store(kp, num))\n                                        return SIFT3D_FAILURE;\n                                key = kp->buf + num - 1;\n                                if (init_Keypoint(key))\n                                        return SIFT3D_FAILURE;\n                                key->o = o;\n                                key->s = s;\n                                key->sd = cur->s;\n\t\t\t\tkey->xd = (double) x;\n\t\t\t\tkey->yd = (double) y;\n\t\t\t\tkey->zd = (double) z;\n                        }\n\t\tSIFT3D_IM_LOOP_END\n\tSIFT3D_PYR_LOOP_END\n#undef CMP_NEIGHBORS\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Bin a Cartesian gradient into Spherical gradient bins */\nSIFT3D_IGNORE_UNUSED\nstatic int Cvec_to_sbins(const Cvec * const vd, Svec * const bins) {\n\n\t// Convert to spherical coordinates\n\tSIFT3D_CVEC_TO_SVEC(vd, bins);\n\t//FIXME: Is this needed? SIFT3D_CVEC_TO_SVEC cannot divide by zero\n\tif (bins->mag < FLT_EPSILON * 1E2)\n\t\treturn SIFT3D_FAILURE;\n\n\t// Compute bins\n\tbins->az *= (float) NBINS_AZ / SIFT3D_AZ_MAX_F; \n\tbins->po *= (float) NBINS_PO / SIFT3D_PO_MAX_F;\n\n\tassert(bins->az < NBINS_AZ);\n\tassert(bins->po < NBINS_PO);\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Refine a gradient histogram with optional operations,\n * such as solid angle weighting. */\nstatic void refine_Hist(Hist *hist) {\n\n#ifndef ICOS_HIST\n\n#ifdef SIFT3D_ORI_SOLID_ANGLE_WEIGHT\n\t{\t\n\tfloat po;\n\tint a, p;\n\t// TODO: could accelerate this with a lookup table\t\t\n\n\t// Weight by the solid angle of the bins, ignoring constants\n\tHIST_LOOP_START(a, p)\n\t\tpo = p * po_max_f / NBINS_PO;\n\t\tHIST_GET(hist, a, p) /= cosf(po) - cosf(po + \n\t\t\tpo_max_f / NBINS_PO);\n\tHIST_LOOP_END\n\t}\n#endif\n\n#endif\n\n}\n\n/* Assign rotation matrices to the keypoints. \n * \n * Note that this stage will modify kp, likely\n * rejecting some keypoints as orientationally\n * unstable. */\nstatic int assign_orientations(SIFT3D *const sift3d, \n\t\t\t       Keypoint_store *const kp) {\n\n\tKeypoint *kp_pos;\n\tsize_t num;\n\tint i, err; \n\n\t// Iterate over the keypoints \n        err = SIFT3D_SUCCESS;\n#pragma omp parallel for\n\tfor (i = 0; i < kp->slab.num; i++) {\n\n\t\tKeypoint *const key = kp->buf + i;\n\t\tconst Image *const level = \n                        SIFT3D_PYR_IM_GET(&sift3d->gpyr, key->o, key->s);\n                Mat_rm *const R = &key->R;\n                const Cvec vcenter = {key->xd, key->yd, key->zd};\n                const double sigma = ori_sig_fctr * key->sd;\n\n\t\t// Compute dominant orientations\n                assert(R->u.data_float == key->r_data);\n\t\tswitch (assign_orientation_thresh(level, &vcenter, sigma,\n                                       sift3d->corner_thresh, R)) {\n\t\t\tcase SIFT3D_SUCCESS:\n\t\t\t\t// Continue processing this keypoint\n\t\t\t\tbreak;\n\t\t\tcase REJECT:\n\t\t\t\t// Mark this keypoint as invalid\n                                key->xd = key->yd = key->zd = -1.0;\n                                continue;\n\t\t\tdefault:\n\t\t\t\t// Any other return value is an error\n                                err = SIFT3D_FAILURE;\n                                continue;\n\t\t}\n\t\t\n\t}\n\n        // Check for errors\n        if (err) return err;\n\n        // Rebuild the keypoint buffer in place\n\tkp_pos = kp->buf;\n        for (i = 0; i < kp->slab.num; i++) {\n\n\t\tKeypoint *const key = kp->buf + i;\n\n                // Check if the keypoint is valid\n                if (key->xd < 0.0)\n                        continue;\n\n                // Copy this keypoint to the next available spot\n                if (copy_Keypoint(key, kp_pos))\n                        return SIFT3D_FAILURE;\n               \n                kp_pos++;\n        }\n\n\t// Release unneeded keypoint memory\n\tnum = kp_pos - kp->buf;\n        return resize_Keypoint_store(kp, num);\n}\n\n/* Helper function to call assign_eig_ori, and reject keypoints with\n * confidence below the parameter \"thresh.\" All other parameters are the same.\n * All return values are the same, except REJECT is returned if \n * conf < thresh. */\nstatic int assign_orientation_thresh(const Image *const im, \n        const Cvec *const vcenter, const double sigma, const double thresh,\n        Mat_rm *const R) {\n\n        double conf;\n        int ret;\n\n        ret = assign_eig_ori(im, vcenter, sigma, R, &conf);\n\n        return ret == SIFT3D_SUCCESS ? \n                (conf < thresh ? REJECT : SIFT3D_SUCCESS) : ret;\n}\n\n\n/* Assign an orientation to a point in an image.\n *\n * Parameters:\n *   -im: The image data.\n *   -vcenter: The center of the window, in image space.\n *   -sigma: The scale parameter. The width of the window is a constant\n *      multiple of this.\n *   -R: The place to write the rotation matrix.\n */\nstatic int assign_eig_ori(const Image *const im, const Cvec *const vcenter,\n                          const double sigma, Mat_rm *const R, \n                          double *const conf) {\n\n    Cvec v[2];\n    Mat_rm A, L, Q;\n    Cvec vd_win, vdisp, vr;\n    double d, cos_ang, abs_cos_ang, corner_score;\n    float weight, sq_dist, sgn;\n    int i, x, y, z, m;\n  \n    const double win_radius = sigma * ori_rad_fctr; \n\n    // Verify inputs\n    if (!SIFT3D_IM_CONTAINS_CVEC(im, vcenter)) {\n        SIFT3D_ERR(\"assign_eig_ori: vcenter (%f, %f, %f) lies \"\n                \"outside the boundaries of im [%d x %d x %d] \\n\", \n                vcenter->x, vcenter->y, vcenter->z, im->nx, im->ny, im->nz);\n        return SIFT3D_FAILURE;\n    }\n    if (sigma < 0) {\n        SIFT3D_ERR(\"assign_eig_ori: invalid sigma: %f \\n\", sigma);\n        return SIFT3D_FAILURE;\n    }\n\n    // Initialize the intermediates\n    if (init_Mat_rm(&A, 3, 3, SIFT3D_DOUBLE, SIFT3D_TRUE))\n        return SIFT3D_FAILURE;\n    if (init_Mat_rm(&L, 0, 0, SIFT3D_DOUBLE, SIFT3D_TRUE) ||\n\tinit_Mat_rm(&Q, 0, 0, SIFT3D_DOUBLE, SIFT3D_TRUE))\n\tgoto eig_ori_fail;\n\n    // Resize the output\n    R->num_rows = R->num_cols = IM_NDIMS;\n    R->type = SIFT3D_FLOAT;\n    if (resize_Mat_rm(R))\n        goto eig_ori_fail;\n\n    // Form the structure tensor and window gradient\n    vd_win.x = 0.0f;\n    vd_win.y = 0.0f;\n    vd_win.z = 0.0f;\n    IM_LOOP_SPHERE_START(im, x, y, z, vcenter, win_radius, &vdisp, sq_dist)\n\n        Cvec vd;\n\n\t// Compute Gaussian weighting, ignoring the constant factor\n\tweight = expf(-0.5 * sq_dist / (sigma * sigma));\t\t\n\n\t// Get the gradient\t\n\tIM_GET_GRAD_ISO(im, x, y, z, 0, &vd);\n\n\t// Update the structure tensor\n\tSIFT3D_MAT_RM_GET(&A, 0, 0, double) += (double) vd.x * vd.x * weight;\n\tSIFT3D_MAT_RM_GET(&A, 0, 1, double) += (double) vd.x * vd.y * weight;\n\tSIFT3D_MAT_RM_GET(&A, 0, 2, double) += (double) vd.x * vd.z * weight;\n\tSIFT3D_MAT_RM_GET(&A, 1, 1, double) += (double) vd.y * vd.y * weight;\n\tSIFT3D_MAT_RM_GET(&A, 1, 2, double) += (double) vd.y * vd.z * weight;\n\tSIFT3D_MAT_RM_GET(&A, 2, 2, double) += (double) vd.z * vd.z * weight;\n\n\t// Update the window gradient\n        SIFT3D_CVEC_SCALE(&vd, weight);\n\tSIFT3D_CVEC_OP(&vd_win, &vd, +, &vd_win);\n\n    IM_LOOP_SPHERE_END\n\n    // Fill in the remaining elements\n    SIFT3D_MAT_RM_GET(&A, 1, 0, double) = SIFT3D_MAT_RM_GET(&A, 0, 1, double);\n    SIFT3D_MAT_RM_GET(&A, 2, 0, double) = SIFT3D_MAT_RM_GET(&A, 0, 2, double);\n    SIFT3D_MAT_RM_GET(&A, 2, 1, double) = SIFT3D_MAT_RM_GET(&A, 1, 2, double);\n\n    // Reject keypoints with weak gradient \n    if (SIFT3D_CVEC_L2_NORM_SQ(&vd_win) < (float) ori_grad_thresh) {\n\tgoto eig_ori_reject;\n    } \n\n    // Get the eigendecomposition\n    if (eigen_Mat_rm(&A, &Q, &L))\n\tgoto eig_ori_fail;\n\n    // Ensure we have distinct eigenvalues\n    m = L.num_rows;\n    if (m != 3)\n\tgoto eig_ori_reject;\n\n    // Test the eigenvectors for stability\n    for (i = 0; i < m - 1; i++) {\n\tif (fabs(SIFT3D_MAT_RM_GET(&L, i, 0, double) /\n\t\t SIFT3D_MAT_RM_GET(&L, i + 1, 0, double)) > max_eig_ratio)\n\t    goto eig_ori_reject;\n    }\n\n    // Assign signs to the first n - 1 vectors\n    corner_score = DBL_MAX;\n    for (i = 0; i < m - 1; i++) {\n\n\tconst int eig_idx = m - i - 1;\n\n\t// Get an eigenvector, in descending order\n\tvr.x = (float) SIFT3D_MAT_RM_GET(&Q, 0, eig_idx, double);\n\tvr.y = (float) SIFT3D_MAT_RM_GET(&Q, 1, eig_idx, double);\n\tvr.z = (float) SIFT3D_MAT_RM_GET(&Q, 2, eig_idx, double);\n\n\t// Get the directional derivative\n\td = SIFT3D_CVEC_DOT(&vd_win, &vr);\n\n        // Get the cosine of the angle between the eigenvector and the gradient\n        cos_ang = d / (SIFT3D_CVEC_L2_NORM(&vr) * SIFT3D_CVEC_L2_NORM(&vd_win));\n        abs_cos_ang = fabs(cos_ang);\n\n        // Compute the corner confidence score\n        corner_score = SIFT3D_MIN(corner_score, abs_cos_ang);\n\n\t// Get the sign of the derivative\n        sgn = d > 0.0 ? 1.0f : -1.0f;\n\n\t// Enforce positive directional derivative\n\tSIFT3D_CVEC_SCALE(&vr, sgn);\n\n\t// Add the vector to the rotation matrix\n\tSIFT3D_MAT_RM_GET(R, 0, i, float) = vr.x;\n\tSIFT3D_MAT_RM_GET(R, 1, i, float) = vr.y;\n\tSIFT3D_MAT_RM_GET(R, 2, i, float) = vr.z;\n\n\t// Save this vector for later use\n\tv[i] = vr;\n    }\n\n    // Take the cross product of the first two vectors\n    SIFT3D_CVEC_CROSS(v, v + 1, &vr);\n\n    // Add the last vector\n    SIFT3D_MAT_RM_GET(R, 0, 2, float) = (float) vr.x;\n    SIFT3D_MAT_RM_GET(R, 1, 2, float) = (float) vr.y;\n    SIFT3D_MAT_RM_GET(R, 2, 2, float) = (float) vr.z;\n\n    // Optionally write back the corner score\n    if (conf != NULL)\n        *conf = corner_score;\n\n    cleanup_Mat_rm(&A);\n    cleanup_Mat_rm(&Q);\n    cleanup_Mat_rm(&L);\n    return SIFT3D_SUCCESS; \n\neig_ori_reject:\n    if (conf != NULL)\n        *conf = 0.0;\n    cleanup_Mat_rm(&A);\n    cleanup_Mat_rm(&Q);\n    cleanup_Mat_rm(&L);\n    return REJECT;\n\neig_ori_fail:\n    if (conf != NULL)\n        *conf = 0.0;\n    cleanup_Mat_rm(&A);\n    cleanup_Mat_rm(&Q);\n    cleanup_Mat_rm(&L);\n    return SIFT3D_FAILURE;\n}\n\n/* Assign an orientation to a point in an image.\n *\n * Parameters:\n *   -im: The image data.\n *   -kp: A container holding the keypoints. On successful return, the matrices\n *      \"R\" in these keypoints will be set to the assigned orientations.\n *   -conf: If not NULL, this is a pointer to array where the confidence scores\n *      are written. The scores are normally in the interval [0, 1], where 1 \n *      is the most confident, 0 is the least. A higher score means the assigned\n *      orientation is more likely to be robust. A negative score means the\n *      orientation could not be assigned. \n *\n * If not NULL, this function will re-allocate conf. As such, *conf must either\n * be NULL or a pointer to a previously-allocated array.\n *\n * Return value: \n * SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise.\n */\nint SIFT3D_assign_orientations(const SIFT3D *const sift3d, \n        const Image *const im, Keypoint_store *const kp, double **const conf) {\n\n        Image im_smooth;\n        Keypoint key_base;\n        int i;\n\n        const int num = kp->slab.num;\n\n        // Verify inputs \n        if (verify_keys(kp, im))\n                return SIFT3D_FAILURE;\n\n        // Initialize intermediates\n        init_im(&im_smooth);\n        init_Keypoint(&key_base);\n\n        // Resize conf (num cannot be zero)\n        if ((*conf = SIFT3D_safe_realloc(*conf, num * sizeof(double))) == NULL)\n                goto assign_orientations_quit;\n\n        // Smooth and scale the input\n        if (smooth_scale_raw_input(sift3d, im, &im_smooth))\n                goto assign_orientations_quit;\n\n        // Assign each orientation\n        for (i = 0; i < num; i++) {\n\n                Cvec vcenter;\n                int j;\n\n                Keypoint *const key = kp->buf + i;\n                double *const conf_ret = *conf + i;\n                Mat_rm *const R = &key->R;\n\n                // Convert the keypoint to the base octave\n                if (keypoint2base(key, &key_base))\n                        goto assign_orientations_quit;\n\n                // Convert the keypoint to a vector\n                vcenter.x = key_base.xd;\n                vcenter.y = key_base.yd;\n                vcenter.z = key_base.zd;\n\n                // Assign the orientation\n                switch (assign_eig_ori(&im_smooth, &vcenter, key_base.sd, R, \n                                       conf_ret))\n                {\n                        case SIFT3D_SUCCESS:\n                                break;\n                        case REJECT:\n                                // Set R to identity\n                                if (identity_Mat_rm(IM_NDIMS, R))\n                                        goto assign_orientations_quit;\n                                *conf_ret = -1.0;\n                                break;\n                        default:\n                                // Stop processing if an error occurs\n                                goto assign_orientations_quit;\n                }\n        }\n\n        // Clean up\n        im_free(&im_smooth);\n\n        return SIFT3D_SUCCESS;\n\nassign_orientations_quit:\n        im_free(&im_smooth);\n        return SIFT3D_FAILURE;\n}\n\n/* Detect keypoint locations and orientations. You must initialize\n * the SIFT3D struct, image, and keypoint store with the appropriate\n * functions prior to calling this function. */\nint SIFT3D_detect_keypoints(SIFT3D *const sift3d, const Image *const im,\n\t\t\t    Keypoint_store *const kp) {\n\n        // Verify inputs\n        if (im->nc != 1) {\n                SIFT3D_ERR(\"SIFT3D_detect_keypoints: invalid number \"\n                        \"of image channels: %d -- only single-channel images \"\n                        \"are supported \\n\", im->nc);\n                return SIFT3D_FAILURE;\n        }\n\n        // Set the image       \n        if (set_im_SIFT3D(sift3d, im))\n                return SIFT3D_FAILURE;\n\n\t// Build the GSS pyramid\n\tif (build_gpyr(sift3d))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Build the DoG pyramid\n\tif (build_dog(sift3d))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Detect extrema\n\tif (detect_extrema(sift3d, kp))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Assign orientations\n\tif (assign_orientations(sift3d, kp))\n\t\treturn SIFT3D_FAILURE;\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Get the bin and barycentric coordinates of a vector in the icosahedral \n * histogram. */\nSIFT3D_IGNORE_UNUSED\nstatic int icos_hist_bin(const SIFT3D * const sift3d,\n\t\t\t   const Cvec * const x, Cvec * const bary,\n\t\t\t   int * const bin) { \n\n\tfloat k;\n\tint i;\n\n\tconst Mesh * const mesh = &sift3d->mesh;\n\n\t// Check for very small vectors\n\tif (SIFT3D_CVEC_L2_NORM_SQ(x) < bary_eps)\n\t\treturn SIFT3D_FAILURE;\n\n\t// Iterate through the faces\n\tfor (i = 0; i < ICOS_NFACES; i++) {\n\n\t\tconst Tri * const tri = mesh->tri + i;\n\n\t\t// Convert to barycentric coordinates\n\t\tif (cart2bary(x, tri, bary, &k))\n\t\t\tcontinue;\n\n\t\t// Test for intersection\n\t\tif (bary->x < -bary_eps || bary->y < -bary_eps ||\n\t\t    bary->z < -bary_eps || k < 0)\n\t\t\tcontinue;\n\n\t\t// Save the bin\n\t\t*bin = i;\n\n\t\t// No other triangles will be intersected\n\t\treturn SIFT3D_SUCCESS;\n\t}\t\n\n\t// Unreachable code\n\tassert(SIFT3D_FALSE);\n\treturn SIFT3D_FAILURE;\n}\n\n/* Helper routine to interpolate over the histograms of a\n * SIFT3D descriptor. */\nvoid SIFT3D_desc_acc_interp(const SIFT3D * const sift3d, \n\t\t\t\tconst Cvec * const vbins, \n\t\t\t\tconst Cvec * const grad,\n\t\t\t\tSIFT3D_Descriptor * const desc) {\n\n\tCvec dvbins;\n\tHist *hist;\n\tfloat weight;\n\tint dx, dy, dz, x, y, z;\n\n#ifdef ICOS_HIST\n\tCvec bary;\n\tfloat mag;\n\tint bin;\t\n#else\n\tSvec sbins, dsbins;\n\tint da, dp, a, p;\n#endif\n\n\tconst int y_stride = NHIST_PER_DIM;\n\tconst int z_stride = NHIST_PER_DIM * NHIST_PER_DIM; \n\n\t// Compute difference from integer bin values\n\tdvbins.x = vbins->x - floorf(vbins->x);\n\tdvbins.y = vbins->y - floorf(vbins->y);\n\tdvbins.z = vbins->z - floorf(vbins->z);\n\n\t// Compute the histogram bin\n#ifdef ICOS_HIST\n\tconst Mesh *const mesh = &sift3d->mesh;\n\n\t// Get the index of the intersecting face \n\tif (icos_hist_bin(sift3d, grad, &bary, &bin))\n\t\treturn;\n\t\n\t// Get the magnitude of the vector\n\tmag = SIFT3D_CVEC_L2_NORM(grad);\n\n#else\n\tif (Cvec_to_sbins(grad, &sbins))\n\t\treturn;\n\tdsbins.az = sbins.az - floorf(sbins.az);\n\tdsbins.po = sbins.po - floorf(sbins.po);\n#endif\n\t\n\tfor (dx = 0; dx < 2; dx++) {\n\tfor (dy = 0; dy < 2; dy++) {\n        for (dz = 0; dz < 2; dz++) {\n\n                x = (int) vbins->x + dx;\n                y = (int) vbins->y + dy;\n                z = (int) vbins->z + dz;\n\n                // Check boundaries\n                if (x < 0 || x >= NHIST_PER_DIM ||\n                        y < 0 || y >= NHIST_PER_DIM ||\n                        z < 0 || z >= NHIST_PER_DIM)\n                        continue;\n\n                // Get the histogram\n                hist = desc->hists + x + y * y_stride + \n                           z * z_stride;\t\n\n                assert(x + y * y_stride + z * z_stride < DESC_NUM_TOTAL_HIST);\n\n                // Get the spatial interpolation weight\n                weight = ((dx == 0) ? (1.0f - dvbins.x) : dvbins.x) *\n                        ((dy == 0) ? (1.0f - dvbins.y) : dvbins.y) *\n                        ((dz == 0) ? (1.0f - dvbins.z) : dvbins.z);\n\n                /* Add the value into the histogram */\n#ifdef ICOS_HIST\n                assert(HIST_NUMEL == ICOS_NVERT);\n                assert(bin >= 0 && bin < ICOS_NFACES);\n\n                // Interpolate over three vertices\n                MESH_HIST_GET(mesh, hist, bin, 0) += mag * weight * bary.x;\n                MESH_HIST_GET(mesh, hist, bin, 1) += mag * weight * bary.y;\n                MESH_HIST_GET(mesh, hist, bin, 2) += mag * weight * bary.z; \n#else\n                // Iterate over all angles\n                for (dp = 0; dp < 2; dp ++) {\n                for (da = 0; da < 2; da ++) {\n\n                        a = ((int) sbins.az + da) % NBINS_AZ;\n                        p = (int) sbins.po + dp;\n                        if (p >= NBINS_PO) {\n                                // See HIST_GET_PO\n                                a = (a + NBINS_AZ / 2) % NBINS_AZ;\n                                p = NBINS_PO - 1;\n                        }\n\t\t\n                        assert(a >= 0);\n                        assert(a < NBINS_AZ);\n                        assert(p >= 0);\n                        assert(p < NBINS_PO);\n\n                        HIST_GET(hist, a, p) += sbins.mag * weight *\n                                ((da == 0) ? (1.0f - dsbins.az) : dsbins.az) *\n                                ((dp == 0) ? (1.0f - dsbins.po) : dsbins.po);\n                }}\n#endif\n\t}}}\n\n}\n\n/* Normalize a descriptor */\nstatic void normalize_desc(SIFT3D_Descriptor * const desc) {\n\n\tdouble norm; \n\tint i, a, p;\n\n\tnorm = 0.0;\n\tfor (i = 0; i < DESC_NUM_TOTAL_HIST; i++) { \n\n                const Hist *const hist = desc->hists + i;\n\n\t\tHIST_LOOP_START(a, p) \n\t\t\tconst float el = HIST_GET(hist, a, p);\n\t\t\tnorm += (double) el * el;\n\t\tHIST_LOOP_END \n\t}\n\n\tnorm = sqrt(norm) + DBL_EPSILON; \n\n\tfor (i = 0; i < DESC_NUM_TOTAL_HIST; i++) {\n\n                Hist *const hist = desc->hists + i;\n\t\tconst float norm_inv = 1.0f / norm; \n\n\t\tHIST_LOOP_START(a, p) \n\t\t\tHIST_GET(hist, a, p) *= norm_inv; \n\t\tHIST_LOOP_END \n\t}\n}\n\n/* Set a histogram to zero */\nstatic void hist_zero(Hist *hist) {\n\n        int a, p;\n\n        HIST_LOOP_START(a, p)\n                HIST_GET(hist, a, p) = 0.0f;\n        HIST_LOOP_END\n}\n\n/* Helper routine to extract a single SIFT3D descriptor */\nstatic int extract_descrip(SIFT3D *const sift3d, const Image *const im,\n\t   const Keypoint *const key, SIFT3D_Descriptor *const desc) {\n\n        float buf[IM_NDIMS * IM_NDIMS];\n        Mat_rm Rt;\n\tCvec vcenter, vim, vkp, vbins, grad, grad_rot;\n\tHist *hist;\n\tfloat weight, sq_dist;\n\tint i, x, y, z, a, p;\n\n\t// Compute basic parameters \n        const float sigma = key->sd * desc_sig_fctr;\n\tconst float win_radius = desc_rad_fctr * sigma;\n\tconst float desc_half_width = win_radius / sqrt(2);\n\tconst float desc_width = 2.0f * desc_half_width;\n        const float desc_hist_width = desc_width / NHIST_PER_DIM;\n\tconst float desc_bin_fctr = 1.0f / desc_hist_width;\n\tconst double coord_factor = ldexp(1.0, key->o);\n\n        // Invert the rotation matrix\n        if (init_Mat_rm_p(&Rt, buf, IM_NDIMS, IM_NDIMS, SIFT3D_FLOAT, \n\t\tSIFT3D_FALSE) ||\n                transpose_Mat_rm(&key->R, &Rt))\n                return SIFT3D_FAILURE;\n\n\t// Zero the descriptor\n\tfor (i = 0; i < DESC_NUM_TOTAL_HIST; i++) {\n\t\thist = desc->hists + i;\n                hist_zero(hist);\n\t}\n\n\t// Iterate over a sphere window in real-world coordinates \n\tvcenter.x = key->xd;\n\tvcenter.y = key->yd;\n\tvcenter.z = key->zd;\n\tIM_LOOP_SPHERE_START(im, x, y, z, &vcenter, win_radius, &vim, sq_dist)\n\n\t\t// Rotate to keypoint space\n\t\tSIFT3D_MUL_MAT_RM_CVEC(&Rt, &vim, &vkp);\t\t\n\n\t\t// Compute spatial bins\n\t\tvbins.x = (vkp.x + desc_half_width) * desc_bin_fctr;\n\t\tvbins.y = (vkp.y + desc_half_width) * desc_bin_fctr;\n\t\tvbins.z = (vkp.z + desc_half_width) * desc_bin_fctr;\n\n\t\t// Reject points outside the rectangular descriptor \n\t\tif (vbins.x < 0 || vbins.y < 0 || vbins.z < 0 ||\n\t\t\tvbins.x >= (float) NHIST_PER_DIM ||\n\t\t\tvbins.y >= (float) NHIST_PER_DIM ||\n\t\t\tvbins.z >= (float) NHIST_PER_DIM)\n\t\t\tcontinue;\n\n\t\t// Take the gradient\n\t\tIM_GET_GRAD_ISO(im, x, y, z, 0, &grad);\n\n\t\t// Apply a Gaussian window\n\t\tweight = expf(-0.5f * sq_dist / (sigma * sigma));\n\t\tSIFT3D_CVEC_SCALE(&grad, weight);\n\n                // Rotate the gradient to keypoint space\n\t\tSIFT3D_MUL_MAT_RM_CVEC(&Rt, &grad, &grad_rot);\n\n\t\t// Finally, accumulate bins by 5x linear interpolation\n\t\tSIFT3D_desc_acc_interp(sift3d, &vbins, &grad_rot, desc);\n\tIM_LOOP_SPHERE_END\n\n\t// Histogram refinement steps\n\tfor (i = 0; i < DESC_NUM_TOTAL_HIST; i++) {\n\t\trefine_Hist(&desc->hists[i]);\n\t}\n\n\t// Normalize the descriptor\n\tnormalize_desc(desc);\n\n\t// Truncate\n\tfor (i = 0; i < DESC_NUM_TOTAL_HIST; i++) {\n\t\thist = desc->hists + i;\n\t\tHIST_LOOP_START(a, p)\n\t\t\tHIST_GET(hist, a, p) = SIFT3D_MIN(HIST_GET(hist, a, p), \n\t\t\t\t\t\t   (float) trunc_thresh);\n\t\tHIST_LOOP_END\n\t}\n\n\t// Normalize again\n\tnormalize_desc(desc);\n\n\t// Save the descriptor location in the original image\n\t// coordinates\n\tdesc->xd = key->xd * coord_factor;\n\tdesc->yd = key->yd * coord_factor;\n\tdesc->zd = key->zd * coord_factor;\n\tdesc->sd = key->sd;\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Check if the Gaussian scale-space pyramid in a SIFT3D struct is valid. This\n * shall return SIFT3D_TRUE if the struct was initialized, and \n * SIFT3D_detect_keypoints has been successfully called on it since \n * initialization. \n *\n * Note: sift3d must be initialized before calling this function. */\nint SIFT3D_have_gpyr(const SIFT3D *const sift3d) {\n\n        const Pyramid *const gpyr = &sift3d->gpyr;\n\n        return gpyr->levels != NULL && gpyr->num_levels != 0 && \n                gpyr->num_octaves != 0;\n}\n\n/* Helper function to scale keypoint coordinates.\n *\n * Parameters:\n *  -src: The source keypoints.\n *  -factors: An array of length IM_NDIMS specifying the scaling factors.\n *  -dst: The scaled keypoints.\n *\n * Return: SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise. */\nstatic int scale_Keypoint(const Keypoint *const src, \n        const double *const factors, Keypoint *const dst) {\n\n        // Copy the source keypoint\n        if (copy_Keypoint(src, dst)) {\n                SIFT3D_ERR(\"scale_Keypoint: failed to convert keypoints \\n\");\n                return SIFT3D_FAILURE;\n        }\n      \n        // Scale the coordinates \n        dst->xd *= factors[0];\n        dst->yd *= factors[1];\n        dst->zd *= factors[2];\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Helper function to smooth and scale a \"raw\" input image, as if it were \n * processed via SIFT3D_detect_keypoints.\n *\n * Parameters:\n *  -sift3d: Stores the parameters sigma_n and sigma0.\n *  -src: The input \"raw\" image.\n *  -dst: The output, smoothed image.\n *\n * Return: SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise. */\nstatic int smooth_scale_raw_input(const SIFT3D *const sift3d, \n        const Image *const src, Image *const dst) {\n\n        Gauss_filter gauss;\n\n        const double sigma_n = sift3d->gpyr.sigma_n;\n        const double sigma0 = sift3d->gpyr.sigma0;\n        const double unit = 1.0;\n\n        // Initialize the smoothing filter        \n        if (init_Gauss_incremental_filter(&gauss, sigma_n, sigma0, IM_NDIMS))\n                return SIFT3D_FAILURE;\n\n        // Smooth the input\n        if (apply_Sep_FIR_filter(src, dst, &gauss.f, unit))\n                goto smooth_scale_raw_input_quit;\n\n        // Scale the input to [-1, 1]\n        im_scale(dst);\n\n        // Clean up\n        cleanup_Gauss_filter(&gauss);\n        \n        return SIFT3D_SUCCESS;\n\nsmooth_scale_raw_input_quit:\n        cleanup_Gauss_filter(&gauss);\n        return SIFT3D_FAILURE;\n}\n\n/* Extract SIFT3D descriptors from a list of keypoints. Uses the Gaussian\n * scale-space pyramid from the previous call to SIFT3D_detect_keypoints on\n * this SIFT3D struct. To extract from an image, see \n * SIFT3D_extract_raw_descriptors. \n *\n * Note: To check if SIFT3D_detect_keypoints has been called on this struct,\n * use SIFT3D_have_gpyr.\n *\n * Parameters:\n *  sift3d - (initialized) struct defining the algorithm parameters. Must have\n *      been used in some previous call to SIFT3D_detect_keypoints.\n *  kp - keypoint list populated by a feature detector \n *  desc - (initialized) struct to hold the descriptors\n *\n * Return value:\n *  Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise.\n */\nint SIFT3D_extract_descriptors(SIFT3D *const sift3d, \n        const Keypoint_store *const kp, \n        SIFT3D_Descriptor_store *const desc) {\n\n\t// Verify inputs\n\tif (verify_keys(kp, &sift3d->im))\n\t\treturn SIFT3D_FAILURE;\n\n        // Check if a Gaussian scale-space pyramid is available for processing\n        if (!SIFT3D_have_gpyr(sift3d)) {\n                SIFT3D_ERR(\"SIFT3D_extract_descriptors: no Gaussian pyramid is \"\n                        \"available. Make sure SIFT3D_detect_keypoints was \"\n                        \"called prior to calling this function. \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Extract features\n        if (_SIFT3D_extract_descriptors(sift3d, &sift3d->gpyr, kp, desc))\n                return SIFT3D_FAILURE;\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Verify that keypoints kp are valid in image im. Returns SIFT3D_SUCCESS if\n * valid, SIFT3D_FAILURE otherwise. */\nstatic int verify_keys(const Keypoint_store *const kp, const Image *const im) {\n\n        int i;\n\n\tconst int num = kp->slab.num;\n\n\t// Check the number of keypoints\n\tif (num < 1) {\n\t\tSIFT3D_ERR(\"verify_keys: invalid number of keypoints: \"\n\t\t\t\t\"%d \\n\", num);\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n\t// Check each keypoint\n        for (i = 0; i < num; i++) {\n\n                const Keypoint *key = kp->buf + i;\n\n                const double octave_factor = ldexp(1.0, key->o);\n\n                if (key->xd < 0 ||\n                        key->yd < 0 ||\n                        key->zd < 0 ||\n                        key->xd * octave_factor >= (double) im->nx || \n                        key->yd * octave_factor >= (double) im->ny || \n                        key->zd * octave_factor >= (double) im->nz) {\n                        SIFT3D_ERR(\"verify_keys: keypoint %d (%f, %f, %f) \"\n                                \"octave %d exceeds image dimensions \"\n                                \"(%d, %d, %d) \\n\", i, key->xd, key->yd, key->zd,\n                                key->o, im->nx, im->ny, im->nz);\n                        return SIFT3D_FAILURE; \n                }\n\n                if (key->sd <= 0) {\n                        SIFT3D_ERR(\"verify_keys: keypoint %d has invalid \"\n                                \"scale %f \\n\", i, key->sd);\n                        return SIFT3D_FAILURE;\n                }\n        }\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Convert the keypoint src to the equivalent one at octave 0, stored in dst. */\nstatic int keypoint2base(const Keypoint *const src, Keypoint *const dst) {\n\n        double base_factors[IM_NDIMS];\n        int j;\n\n        const double octave_factor = ldexp(1.0, src->o);\n\n        // Convert the factors to the base octave\n        for (j = 0; j < IM_NDIMS; j++) {\n                base_factors[j] = octave_factor;\n        }\n\n        // Scale the keypoint\n        if (scale_Keypoint(src, base_factors, dst))\n                return SIFT3D_FAILURE;\n\n        // Assign the keypoint the base octave and scale level\n        dst->o = 0;\n        dst->s = 0;\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Extract SIFT3D descriptors from a list of keypoints and \n * an image. To extract from a Gaussian scale-space pyramid, see\n * SIFT3D_extract_descriptors. \n *\n * Parameters:\n *  sift3d - (initialized) struct defining the algorithm parameters\n *  im - Pointer to an Image struct. A copy of the image is smoothed from \n *      sift3d->sigma_n to sift3d->sigma0 prior to descriptor extraction.\n *  kp - keypoint list populated by a feature detector \n *  desc - (initialized) struct to hold the descriptors\n *\n * Return value:\n *  Returns  SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise.\n */\nint SIFT3D_extract_raw_descriptors(SIFT3D *const sift3d, \n        const Image *const im, const Keypoint_store *const kp, \n        SIFT3D_Descriptor_store *const desc) {\n\n        Keypoint_store kp_base;\n        Pyramid pyr;\n        Image *level;\n        int i;\n\n        const int first_octave = 0;\n        const int first_level = 0;\n        const int num_kp_levels = 1;\n        const int num_levels = 1;\n        const int num_octaves = 1;\n        const double sigma0 = sift3d->gpyr.sigma0;\n        const double sigma_n = sift3d->gpyr.sigma_n;\n\n        // Verify inputs\n        if (verify_keys(kp, im))\n                return SIFT3D_FAILURE;\n\n        // Initialize intermediates\n        init_Pyramid(&pyr);\n        init_Keypoint_store(&kp_base);\n\n        // Initialize the image pyramid\n        set_scales_Pyramid(sigma0, sigma_n, &pyr);\n        if (resize_Pyramid(im, first_level, num_kp_levels, num_levels,\n                first_octave, num_octaves, &pyr))\n                goto extract_raw_descriptors_quit;\n\n        // Smooth the input image, storing the result in the pyramid\n        level = SIFT3D_PYR_IM_GET(&pyr, first_octave, first_level); \n        if (smooth_scale_raw_input(sift3d, im, level))\n                goto extract_raw_descriptors_quit;\n\n        // Allocate a temporary copy of the keypoints\n        if (resize_Keypoint_store(&kp_base, kp->slab.num))\n                goto extract_raw_descriptors_quit;\n\n        // Convert keypoints to the base scale and octave\n        for (i = 0; i < kp->slab.num; i++) {\n\n                const Keypoint *const src = kp->buf + i;\n                Keypoint *const dst = kp_base.buf + i; \n\n                if (keypoint2base(src, dst))\n                        goto extract_raw_descriptors_quit;\n        }\n\n        // Extract the descriptors\n        if (_SIFT3D_extract_descriptors(sift3d, &pyr, &kp_base, desc))\n                goto extract_raw_descriptors_quit;\n\n        // Clean up\n        cleanup_Pyramid(&pyr);\n        cleanup_Keypoint_store(&kp_base);\n\n        return SIFT3D_SUCCESS;\n\nextract_raw_descriptors_quit:\n        cleanup_Pyramid(&pyr);\n        cleanup_Keypoint_store(&kp_base);\n        return SIFT3D_FAILURE;\n}\n\n/* Helper funciton to extract SIFT3D descriptors from a list of keypoints and \n * an image. Called by SIFT3D_extract_descriptors and \n * SIFT3D_extract_raw_descriptors.\n *\n * parameters:\n *  sift3d - (initialized) struct defining the algorithm parameters\n *  gpyr - A Gaussian Scale-Space pyramid containing the image data\n *  kp - keypoint list populated by a feature detector \n *  desc - (initialized) struct to hold the descriptors\n *  use_gpyr - see im for details */\nstatic int _SIFT3D_extract_descriptors(SIFT3D *const sift3d, \n        const Pyramid *const gpyr, const Keypoint_store *const kp, \n        SIFT3D_Descriptor_store *const desc) {\n\n\tint i, ret;\n\n\tconst Image *const first_level = \n                SIFT3D_PYR_IM_GET(gpyr, gpyr->first_octave, gpyr->first_level);\n\n\tconst int num = kp->slab.num;\n\n\t// Initialize the metadata \n\tdesc->nx = first_level->nx;\t\n\tdesc->ny = first_level->ny;\t\n\tdesc->nz = first_level->nz;\t\n\n\t// Resize the descriptor store\n        if (resize_SIFT3D_Descriptor_store(desc, num))\n                return SIFT3D_FAILURE;\n\n        // Extract the descriptors\n        ret = SIFT3D_SUCCESS;\n#pragma omp parallel for\n\tfor (i = 0; i < desc->num; i++) {\n\n                const Keypoint *const key = kp->buf + i;\n\t\tSIFT3D_Descriptor *const descrip = desc->buf + i;\n\t\tconst Image *const level = \n                        SIFT3D_PYR_IM_GET(gpyr, key->o, key->s);\n\n\t\tif (extract_descrip(sift3d, level, key, descrip)) {\n                        ret = SIFT3D_FAILURE;\n                }\n\t}\t\n\n\treturn ret;\n}\n\n/* L2-normalize a histogram */\nstatic void normalize_hist(Hist *const hist) {\n\n        double norm;\n        float norm_inv;\n        int a, p;\n\n        norm = 0.0;\n        HIST_LOOP_START(a, p)\n                const float el = HIST_GET(hist, a, p);\n                norm += (double) el * el;\n        HIST_LOOP_END\n\n        norm = sqrt(norm) + DBL_EPSILON;\n        norm_inv = 1.0f / norm; \n\n        HIST_LOOP_START(a, p)\n                 HIST_GET(hist, a, p) *= norm_inv; \n        HIST_LOOP_END \n}\n\n/* Dense gradient histogram postprocessing steps */\nstatic void postproc_Hist(Hist *const hist, const float norm) {\n\n        int a, p;\n\n        const float hist_trunc = trunc_thresh * DESC_NUMEL / HIST_NUMEL;\n\n\t// Histogram refinement steps\n\trefine_Hist(hist);\n\n\t// Normalize the descriptor\n\tnormalize_hist(hist);\n\n\t// Truncate\n\tHIST_LOOP_START(a, p)\n\t\tHIST_GET(hist, a, p) = SIFT3D_MIN(HIST_GET(hist, a, p), \n\t\t\t\t\t   hist_trunc);\n\tHIST_LOOP_END\n\n\t// Normalize again\n\tnormalize_hist(hist);\n\n        // Convert to the desired norm\n        HIST_LOOP_START(a, p)\n                HIST_GET(hist, a, p) *= norm;\n        HIST_LOOP_END\n}\n\n/* Helper routine to extract a single SIFT3D histogram, with rotation. */\nstatic int extract_dense_descrip_rotate(SIFT3D *const sift3d, \n           const Image *const im, const Cvec *const vcenter, \n           const double sigma, const Mat_rm *const R, Hist *const hist) {\n\n        float buf[IM_NDIMS * IM_NDIMS];\n        Mat_rm Rt;\n\tCvec grad, grad_rot, bary, vim;\n\tfloat sq_dist, mag, weight;\nSIFT3D_IGNORE_UNUSED\n\tint a, p, x, y, z, bin;\n\n        const Mesh *const mesh = &sift3d->mesh;\n\tconst float win_radius = desc_rad_fctr * sigma;\n\n        // Invert the rotation matrix\n        if (init_Mat_rm_p(&Rt, buf, IM_NDIMS, IM_NDIMS, SIFT3D_FLOAT, \n\t\tSIFT3D_FALSE) ||\n                transpose_Mat_rm(R, &Rt))\n                return SIFT3D_FAILURE;\n\n\t// Zero the descriptor\n        hist_zero(hist);\n\n\t// Iterate over a sphere window in real-world coordinates\n\tIM_LOOP_SPHERE_START(im, x, y, z, vcenter, win_radius, &vim, sq_dist)\n\n\t\t// Take the gradient and rotate\n\t\tIM_GET_GRAD_ISO(im, x, y, z, 0, &grad);\n\t\tSIFT3D_MUL_MAT_RM_CVEC(&Rt, &grad, &grad_rot);\n\n                // Get the index of the intersecting face\n                if (icos_hist_bin(sift3d, &grad_rot, &bary, &bin))\n                        continue;\n\n                // Get the magnitude of the vector\n                mag = SIFT3D_CVEC_L2_NORM(&grad);\n\n\t\t// Get the Gaussian window weight\n\t\tweight = expf(-0.5f * sq_dist / (sigma * sigma));\n\n                // Interpolate over three vertices\n                MESH_HIST_GET(mesh, hist, bin, 0) += mag * weight * bary.x;\n                MESH_HIST_GET(mesh, hist, bin, 1) += mag * weight * bary.y;\n                MESH_HIST_GET(mesh, hist, bin, 2) += mag * weight * bary.z;\n\n\tIM_LOOP_SPHERE_END\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Get a descriptor with a single histogram at each voxel of an image.\n * The result is an image with HIST_NUMEL channels, where each channel is a\n * bin of the histogram.\n *\n * Parameters:\n * -sift3d Stores the algorithm parameters.\n * -in The input image.\n * -desc The output image of descriptors.\n */\nint SIFT3D_extract_dense_descriptors(SIFT3D *const sift3d, \n        const Image *const in, Image *const desc) {\n\n        int (*extract_fun)(SIFT3D *const, const Image *const, Image *const);\n        Image in_smooth;\n        int x, y, z;\n\n        // Verify inputs\n        if (in->nc != 1) {\n                SIFT3D_ERR(\"SIFT3D_extract_dense_descriptors: invalid \"\n                        \"number of channels: %d. This function only supports \"\n                        \"single-channel images. \\n\", in->nc);\n                return SIFT3D_FAILURE;\n        }\n\n        // Select the appropriate subroutine\n        extract_fun = sift3d->dense_rotate ? \n                extract_dense_descriptors_rotate :\n                extract_dense_descriptors_no_rotate;\n\n        // Resize the output image\n        memcpy(SIFT3D_IM_GET_DIMS(desc), SIFT3D_IM_GET_DIMS(in), \n                IM_NDIMS * sizeof(int));\n        desc->nc = HIST_NUMEL;\n        im_default_stride(desc);\n        if (im_resize(desc))\n                return SIFT3D_FAILURE;\n\n        // Intialize intermediates\n        init_im(&in_smooth);\n\n        //TODO: Interpolate to be isotropic\n\n        // Smooth and scale the input image\n        if (smooth_scale_raw_input(sift3d, in, &in_smooth))\n                goto extract_dense_quit;\n\n        // Extract the descriptors\n        if (extract_fun(sift3d, &in_smooth, desc))\n                return SIFT3D_FAILURE;\n\n        // Post-process the descriptors\n        SIFT3D_IM_LOOP_START(desc, x, y, z)\n\n                Hist hist;\n\n                // Get the image intensity at this voxel \n                const float val = SIFT3D_IM_GET_VOX(in, x, y, z, 0);\n\n                // Copy to a Hist\n                vox2hist(desc, x, y, z, &hist);\n\n                // Post-process\n                postproc_Hist(&hist, val);\n\n                // Copy back to the image\n                hist2vox(&hist, desc, x, y, z);\n\n        SIFT3D_IM_LOOP_END\n\n        // TODO transform back to original space\n\n        // Clean up\n        im_free(&in_smooth);\n\n        return SIFT3D_SUCCESS;\n\nextract_dense_quit:\n        im_free(&in_smooth);\n        return SIFT3D_FAILURE;\n}\n\n/* Helper function for SIFT3D_extract_dense_descriptors, without rotation \n * invariance. This function is much faster than its rotation-invariant \n * counterpart because histogram bins are pre-computed. */\nstatic int extract_dense_descriptors_no_rotate(SIFT3D *const sift3d,\n        const Image *const in, Image *const desc) {\n\n        Image temp; \n        Gauss_filter gauss;\n\tCvec grad, bary;\n        int x, y, z, bin;\n\n        const int x_start = 1;\n        const int y_start = 1;\n        const int z_start = 1;\n        const int x_end = in->nx - 2;\n        const int y_end = in->ny - 2;\n        const int z_end = in->nz - 2;\n\n        Mesh * const mesh = &sift3d->mesh;\n        const double sigma_win = sift3d->gpyr.sigma0 * desc_sig_fctr / \n                                 NHIST_PER_DIM;\n        const double unit = 1.0;\n\n        // Initialize the intermediate image\n        init_im(&temp);\n        if (im_copy_dims(desc, &temp))\n                return SIFT3D_FAILURE;\n\n        // Initialize the filter\n        if (init_Gauss_filter(&gauss, sigma_win, 3)) {\n                im_free(&temp);\n                return SIFT3D_FAILURE;\n        }\n\n        // Initialize the descriptors for each voxel\n        im_zero(&temp);\n        SIFT3D_IM_LOOP_LIMITED_START(in, x, y, z, x_start, x_end, y_start, \n                y_end, z_start, z_end)\n\n                // Take the gradient\n\t\tIM_GET_GRAD_ISO(in, x, y, z, 0, &grad);\n\n                // Get the index of the intersecting face\n                if (icos_hist_bin(sift3d, &grad, &bary, &bin))\n                        continue;\n\n                // Initialize each vertex\n                SIFT3D_IM_GET_VOX(&temp, x, y, z, MESH_GET_IDX(mesh, bin, 0)) = \n                        bary.x;\n                SIFT3D_IM_GET_VOX(&temp, x, y, z, MESH_GET_IDX(mesh, bin, 1)) = \n                        bary.y;\n                SIFT3D_IM_GET_VOX(&temp, x, y, z, MESH_GET_IDX(mesh, bin, 2)) = \n                        bary.z;\n\n        SIFT3D_IM_LOOP_END\n\n        // Filter the descriptors\n\tif (apply_Sep_FIR_filter(&temp, desc, &gauss.f, unit))\n                goto dense_extract_quit;\n\n        // Clean up\n        im_free(&temp);\n        cleanup_Gauss_filter(&gauss);\n\n        return SIFT3D_SUCCESS;\n\ndense_extract_quit:\n        im_free(&temp);\n        cleanup_Gauss_filter(&gauss);\n        return SIFT3D_FAILURE;\n}\n\n/* Copy a voxel to a Hist. Does no bounds checking. */\nstatic void vox2hist(const Image *const im, const int x, const int y,\n        const int z, Hist *const hist) {\n\n        int c;\n\n        for (c = 0; c < HIST_NUMEL; c++) {\n                hist->bins[c] = SIFT3D_IM_GET_VOX(im, x, y, z, c);\n        }\n}\n\n/* Copy a Hist to a voxel. Does no bounds checking. */\nstatic void hist2vox(Hist *const hist, const Image *const im, const int x, \n        const int y, const int z) {\n\n        int c;\n        \n        for (c = 0; c < HIST_NUMEL; c++) {\n                SIFT3D_IM_GET_VOX(im, x, y, z, c) = hist->bins[c];\n        }\n}\n\n/* As in extract_dense_descrip, but with rotation invariance */\nstatic int extract_dense_descriptors_rotate(SIFT3D *const sift3d,\n        const Image *const in, Image *const desc) {\n\n        Hist hist;\n        Mat_rm R, Id;\n        Mat_rm *ori;\n        int i, x, y, z;\n\n        // Initialize the identity matrix\n        if (init_Mat_rm(&Id, 3, 3, SIFT3D_FLOAT, SIFT3D_TRUE)) {\n                return SIFT3D_FAILURE;\n        }\n        for (i = 0; i < 3; i++) {       \n                SIFT3D_MAT_RM_GET(&Id, i, i, float) = 1.0f;\n        }\n\n        // Initialize the rotation matrix\n        if (init_Mat_rm(&R, 3, 3, SIFT3D_FLOAT, SIFT3D_TRUE)) {\n                cleanup_Mat_rm(&Id);\n                return SIFT3D_FAILURE;\n        }\n\n        // Iterate over each voxel\n        SIFT3D_IM_LOOP_START(in, x, y, z)\n\n                const Cvec vcenter = {x, y, z}; \n\n                const double ori_sigma = sift3d->gpyr.sigma0 * ori_sig_fctr;\n                const double desc_sigma = sift3d->gpyr.sigma0 * \n                        desc_sig_fctr / NHIST_PER_DIM;\n\n                // Attempt to assign an orientation\n                switch (assign_orientation_thresh(in, &vcenter, ori_sigma,\n                                      sift3d->corner_thresh, &R)) {\n                        case SIFT3D_SUCCESS:\n                                // Use the assigned orientation\n                                ori = &R;\n                                break;\n                        case REJECT:\n                                // Default to identity\n                                ori = &Id;\n                                break;\n                        default:\n                                // Unexpected error\n                                goto dense_rotate_quit;\n                }\n\n                // Extract the descriptor\n                if (extract_dense_descrip_rotate(sift3d, in, &vcenter, \n                        desc_sigma, ori, &hist))\n                        goto dense_rotate_quit;\n\n                // Copy the descriptor to the image channels\n                hist2vox(&hist, desc, x, y, z);\n\n        SIFT3D_IM_LOOP_END\n\n        // Clean up\n        cleanup_Mat_rm(&R);\n        cleanup_Mat_rm(&Id);\n        return SIFT3D_SUCCESS;\n\ndense_rotate_quit:\n        // Clean up and return an error condition \n        cleanup_Mat_rm(&R);\n        cleanup_Mat_rm(&Id);\n        return SIFT3D_FAILURE;\n}\n\n/* Convert a keypoint store to a matrix. \n * Output format:\n *  [x1 y1 z1]\n *  |   ...  |\n *  [xn yn zn] \n * \n * mat must be initialized. */\nint Keypoint_store_to_Mat_rm(const Keypoint_store *const kp, Mat_rm *const mat) {\n\n    int i;\n\n    const int num = kp->slab.num;\n\n    // Resize mat\n    mat->num_rows = num;\n    mat->num_cols = IM_NDIMS;\n    mat->type = SIFT3D_DOUBLE;\n    if (resize_Mat_rm(mat))\n\treturn SIFT3D_FAILURE;\n\n    // Build the matrix\n    for (i = 0; i < num; i++) {\n\n        const Keypoint *const key = kp->buf + i;\n\n        // Adjust the coordinates to the base octave\n        const double coord_factor = ldexp(1.0, key->o);\n\n\tSIFT3D_MAT_RM_GET(mat, i, 0, double) = coord_factor * key->xd;\n\tSIFT3D_MAT_RM_GET(mat, i, 1, double) = coord_factor * key->yd;\n\tSIFT3D_MAT_RM_GET(mat, i, 2, double) = coord_factor * key->zd;\n    }\n\n    return SIFT3D_SUCCESS;\n}\n\n/* Convert SIFT3D desriptors to a coordinate matrix. This function has the same\n * output format as Keypoint_store_to_Mat_rm */\nint SIFT3D_Descriptor_coords_to_Mat_rm(\n        const SIFT3D_Descriptor_store *const store, \n        Mat_rm *const mat) {\n\n        int i;\n\n\tconst int num_rows = store->num;\n        const int num_cols = IM_NDIMS;\n\n\t// Verify inputs\n\tif (num_rows < 1) {\n\t\tprintf(\"SIFT3D_Descriptor_coords_to_Mat_rm: invalid number of \"\n\t\t       \"descriptors: %d \\n\", num_rows);\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n        // Resize the output\n        mat->type = SIFT3D_DOUBLE;\n        mat->num_rows = num_rows;\n        mat->num_cols = num_cols;\n        if (resize_Mat_rm(mat))\n                return SIFT3D_FAILURE;\n\n        // Copy the data\n        for (i = 0; i < num_rows; i++) {\n\n\t\tconst SIFT3D_Descriptor *const desc = store->buf + i;\n\n                SIFT3D_MAT_RM_GET(mat, i, 0, double) = desc->xd;\n                SIFT3D_MAT_RM_GET(mat, i, 1, double) = desc->yd;\n                SIFT3D_MAT_RM_GET(mat, i, 2, double) = desc->zd;\n        }\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Convert SIFT3D descriptors to a matrix.\n *\n * Output format:\n *  [x y z el0 el1 ... el767]\n * Each row is a feature descriptor. [x y z] are the coordinates in image\n * space, and [el0 el1 ... el767 are the 768 dimensions of the descriptor.\n *\n * mat must be initialized prior to calling this function. mat will be resized.\n * The type of mat will be changed to float.\n */\nint SIFT3D_Descriptor_store_to_Mat_rm(const SIFT3D_Descriptor_store *const store, \n\t\t\t\t      Mat_rm *const mat) {\n\tint i, j, a, p;\n\n\tconst int num_rows = store->num;\n\tconst int num_cols = IM_NDIMS + DESC_NUMEL;\n\n\t// Verify inputs\n\tif (num_rows < 1) {\n\t\tprintf(\"SIFT3D_Descriptor_store_to_Mat_rm: invalid number of \"\n\t\t       \"descriptors: %d \\n\", num_rows);\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n\t// Resize inputs\n\tmat->type = SIFT3D_FLOAT;\n\tmat->num_rows = num_rows;\n\tmat->num_cols = num_cols;\n\tif (resize_Mat_rm(mat))\n\t\treturn SIFT3D_FAILURE;\n\n\t// Copy the data\n\tfor (i = 0; i < num_rows; i++) {\n\n\t\tconst SIFT3D_Descriptor *const desc = store->buf + i;\n\n\t\t// Copy the coordinates\n\t\tSIFT3D_MAT_RM_GET(mat, i, 0, float) = (float) desc->xd;\n\t\tSIFT3D_MAT_RM_GET(mat, i, 1, float) = (float) desc->yd;\n\t\tSIFT3D_MAT_RM_GET(mat, i, 2, float) = (float) desc->zd;\n\n\t\t// Copy the feature vector\n\t\tfor (j = 0; j < DESC_NUM_TOTAL_HIST; j++) {\n\t\t\tconst Hist *const hist = desc->hists + j;\n\t\t\tHIST_LOOP_START(a, p)\n                                const int col = DESC_MAT_GET_COL(j, a, p);\n\t\t\t\tSIFT3D_MAT_RM_GET(mat, i, col, float) = \n\t\t\t\t\tHIST_GET(hist, a, p);\n\t\t\tHIST_LOOP_END\n\t\t}\n\t}\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Convert a Mat_rm to a descriptor store. See \n * SIFT3D_Descriptor_store_to_Mat_rm for the input format. */\nint Mat_rm_to_SIFT3D_Descriptor_store(const Mat_rm *const mat, \n\t\t\t\t      SIFT3D_Descriptor_store *const store) {\n\n\tint i, j, a, p;\n\n\tconst int num_rows = mat->num_rows;\n\tconst int num_cols = mat->num_cols;\n\n\t// Verify inputs\n\tif (num_rows < 1 || num_cols != IM_NDIMS + DESC_NUMEL) {\n\t\tSIFT3D_ERR(\"Mat_rm_to_SIFT3D_Descriptor_store: invalid matrix \"\n\t\t       \"dimensions: [%d X %d] \\n\", num_rows, num_cols);\n\t\treturn SIFT3D_FAILURE;\n\t}\n        if (mat->type != SIFT3D_FLOAT) {\n\t\tSIFT3D_ERR(\"Mat_rm_to_SIFT3D_Descriptor_store: matrix must \"\n                        \"have type SIFT3D_FLOAT\");\n\t\treturn SIFT3D_FAILURE;\n        }\n\n        /* Resize the descriptor store */\n        if (resize_SIFT3D_Descriptor_store(store, num_rows))\n                return SIFT3D_FAILURE;\n\n\t// Copy the data\n\tfor (i = 0; i < num_rows; i++) {\n\n\t\tSIFT3D_Descriptor *const desc = store->buf + i;\n\n\t\t// Copy the coordinates\n\t\tdesc->xd = SIFT3D_MAT_RM_GET(mat, i, 0, float);\n\t\tdesc->yd = SIFT3D_MAT_RM_GET(mat, i, 1, float);\n\t\tdesc->zd = SIFT3D_MAT_RM_GET(mat, i, 2, float);\n                desc->sd = sigma0_default;\n\n\t\t// Copy the feature vector\n\t\tfor (j = 0; j < DESC_NUM_TOTAL_HIST; j++) {\n\t\t\tHist *const hist = desc->hists + j;\n\t\t\tHIST_LOOP_START(a, p)\n                                const int col = DESC_MAT_GET_COL(j, a, p);\n\t\t\t\tHIST_GET(hist, a, p) = \n                                        SIFT3D_MAT_RM_GET(mat, i, col, float);\n\t\t\tHIST_LOOP_END\n\t\t}\n\t}\n\t\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Convert a list of matches to matrices of point coordinates.\n * Only valid matches will be included in the output matrices.\n *\n * The format of \"matches\" is specified in SIFT3D_nn_match.\n *\n * All matrices must be initialized prior to calling this function.\n *\n * Output format:\n *  m x 3 matrices [x11 y11 z11] [x21 y21 z21]\n * \t\t   |x12 y12 z12| |x22 y22 z22|\n *\t\t        ...\t      ...\n * \t\t   [x1N y1N z1N] [x2N y2N z2N] \n *\n * Where points on corresponding rows are matches. */\nint SIFT3D_matches_to_Mat_rm(SIFT3D_Descriptor_store *d1,\n\t\t\t     SIFT3D_Descriptor_store *d2,\n\t\t\t     const int *const matches,\n\t\t\t     Mat_rm *const match1, \n\t\t\t     Mat_rm *const match2) {\n    int i, num_matches;\n\n    const int num = d1->num;\n\n    // Resize matrices \n    match1->num_rows = match2->num_rows = d1->num;\n    match1->num_cols = match2->num_cols = 3;\n    match1->type = match2->type = SIFT3D_DOUBLE;\n    if (resize_Mat_rm(match1) || resize_Mat_rm(match2))\n\t    return SIFT3D_FAILURE;\n\n    // Populate the matrices\n    num_matches = 0;\n    for (i = 0; i < num; i++) {\n\n\t    const SIFT3D_Descriptor *const desc1 = d1->buf + i;\n\t    const SIFT3D_Descriptor *const desc2 = d2->buf + matches[i];\n\n\t    if (matches[i] == -1)\n\t\t    continue;\n\n\t    // Save the match\n\t    SIFT3D_MAT_RM_GET(match1, num_matches, 0, double) = desc1->xd; \n\t    SIFT3D_MAT_RM_GET(match1, num_matches, 1, double) = desc1->yd; \n\t    SIFT3D_MAT_RM_GET(match1, num_matches, 2, double) = desc1->zd; \n\t    SIFT3D_MAT_RM_GET(match2, num_matches, 0, double) = desc2->xd; \n\t    SIFT3D_MAT_RM_GET(match2, num_matches, 1, double) = desc2->yd; \n\t    SIFT3D_MAT_RM_GET(match2, num_matches, 2, double) = desc2->zd; \n\t    num_matches++;\n    }\n\n    // Release extra memory\n    match1->num_rows = match2->num_rows = num_matches;\n    if (resize_Mat_rm(match1) || resize_Mat_rm(match2))\n\t    return SIFT3D_FAILURE;\n    \n    return SIFT3D_SUCCESS;\n}\n\n/* Perform nearest neighbor matching on two sets of \n * SIFT descriptors.\n *\n * This function will reallocate *matches. As such, *matches must be either\n * NULL or a pointer to previously-allocated array. Upon successful exit,\n * *matches is an array of size d1->num.\n * \n * On return, the ith element of matches contains the index in d2 of the match\n * corresponding to the ith descriptor in d1, or -1 if no match was found.\n *\n * You might consider using SIFT3D_matches_to_Mat_rm to convert the matches to\n * coordinate matrices. */\nint SIFT3D_nn_match(const SIFT3D_Descriptor_store *const d1,\n\t\t    const SIFT3D_Descriptor_store *const d2,\n\t\t    const float nn_thresh, int **const matches) {\n\n\tint i;\n\n\tconst int num = d1->num;\n\n        // Verify inputs\n\tif (num < 1) {\n\t\tSIFT3D_ERR(\"_SIFT3D_nn_match: invalid number of \"\n\t\t\t\"descriptors in d1: %d \\n\", num);\n\t\treturn SIFT3D_FAILURE;\n\t}\n\n\t// Resize the matches array (num cannot be zero)\n\tif ((*matches = (int *) SIFT3D_safe_realloc(*matches, \n\t\tnum * sizeof(int))) == NULL) {\n\t    SIFT3D_ERR(\"_SIFT3D_nn_match: out of memory! \\n\");\n\t    return SIFT3D_FAILURE;\n\t}\n\n\tfor (i = 0; i < d1->num; i++) {\n\t    // Mark -1 to signal there is no match\n\t    (*matches)[i] = -1;\n\t}\n\t\n\t// Exhaustive search for matches\n#pragma omp parallel for\n\tfor (i = 0; i < num; i++) {\n\n                const SIFT3D_Descriptor *const desc1 = d1->buf + i;\n                int *const match = *matches + i;\n\n                // Forward matching pass\n                *match = match_desc(desc1, d2, nn_thresh);\n\n                // We are done if there was no match\n                if (*match < 0)\n                        continue;\n\n                // Check for forward-backward consistency\n                if (match_desc(d2->buf + *match, d1, nn_thresh) != i) {\n                        *match = -1;\n                }\n        }\n\n\treturn SIFT3D_SUCCESS;\n}\n\n/* Helper function to match desc against the descriptors in store. Returns the\n * index of the match, or -1 if none was found. */\nstatic int match_desc(const SIFT3D_Descriptor *const desc,\n        const SIFT3D_Descriptor_store *const store, const float nn_thresh) {\n\n\tconst SIFT3D_Descriptor *desc_best;\n        double ssd_best, ssd_nearest;\n        int i;\n\n#ifdef SIFT3D_MATCH_MAX_DIST\n        Cvec dims, dmatch;\n        double dist_match;\n\t\t\t\t\n        // Compute spatial distance rejection threshold\n        dims.x = (float) store->nx;\t\n        dims.y = (float) store->ny;\t\n        dims.z = (float) store->nz;\t\n        const double diag = SIFT3D_CVEC_L2_NORM(&dims);\t\n        const double dist_thresh = diag * SIFT3D_MATCH_MAX_DIST;\n#endif\n\n        // Linear search for the best and second-best SSD matches \n        ssd_best = ssd_nearest = DBL_MAX;\n        desc_best = NULL;\n        for (i = 0; i < store->num; i++) { \n\n                double ssd;\n                int j;\n\n                const SIFT3D_Descriptor *const desc2 = store->buf + i;\n\n                // Compute the SSD of the two descriptors\n                ssd = 0.0;\n                for (j = 0; j < DESC_NUM_TOTAL_HIST; j++) {\n\n                        int a, p;\n\n                        const Hist *const hist1 = desc->hists + j;\n                        const Hist *const hist2 = desc2->hists + j;\n\n                        HIST_LOOP_START(a, p)\n                                const double diff = \n                                        (double) HIST_GET(hist1, a, p) -\n                                        (double) HIST_GET(hist2, a, p);\n                                        ssd += diff * diff;\n                        HIST_LOOP_END\n\n                        // Early termination\n                        if (ssd > ssd_nearest)\n                                break;\n                }\n\n                // Compare to the best matches\n                if (ssd < ssd_best) {\n                        desc_best = desc2; \n                        ssd_nearest = ssd_best;\n                        ssd_best = ssd;\n                } else  {\n                        ssd_nearest = SIFT3D_MIN(ssd_nearest, ssd);\n                }\n        }\n\n        // Reject a match if the nearest neighbor is too close\n        if (ssd_best / ssd_nearest > nn_thresh * nn_thresh)\n                        return -1;\n\n#ifdef SIFT3D_MATCH_MAX_DIST\n        // Compute the spatial distance of the match\n        dmatch.x = (float) desc_best->xd - desc1->xd; \n        dmatch.y = (float) desc_best->yd - desc1->yd; \n        dmatch.z = (float) desc_best->zd - desc1->zd; \n        dist_match = (double) SIFT3D_CVEC_L2_NORM(&dmatch);\n\n        // Reject matches of great distance\n        if (dist_match > dist_thresh)\n                return -1;\n#endif\n        // The match was a success\n        return desc_best - store->buf;\n}\n\t\t\t\n/* Draw the matches. \n * \n * Inputs:\n * -left - lefthand-side image\n * -right - righthand-side image\n * -keys_left - Keypoints from \"left\" (can be NULL if keys is NULL) \n * -keys_right - Keypoints from \"right\" (can be NULL if keys is NULL)\n * -match_left - Matches from \"left\" (can be NULL if lines is NULL)\n * -match_right - Matches from \"right\" (can be NULL if lines is NULL)\n * \n * Outputs:\n * -concat - Concatenated image (NULL if not needed)\n * -keys - Keypoints from concat (NULL is not needed)\n * -lines - Lines between matching keypoints in concat (NULL if not needed)\n * It is an error if all outputs are NULL.\n *\n * Return:\n * SIFT3D_SUCCESS or SIFT3D_FAILURE\n */\nint draw_matches(const Image *const left, const Image *const right,\n                 const Mat_rm *const keys_left, const Mat_rm *const keys_right,\n\t\t const Mat_rm *const match_left, \n                 const Mat_rm *const match_right, Image *const concat, \n                 Image *const keys, Image *const lines) {\n\n        Image concat_temp, left_padded, right_padded;\n\tMat_rm keys_right_draw, keys_left_draw, keys_draw, match_right_draw;\n\tint i;\n\n        const double right_pad = (double) left->nx;\n        const int ny_pad = SIFT3D_MAX(right->ny, left->ny);\n\tconst int nz_pad = SIFT3D_MAX(right->nz, left->nz);\n\n        // Choose which image to use for concatenation \n        Image *const concat_arg = concat == NULL ? &concat_temp : concat;\n\n        // Verify inputs\n        if (concat == NULL && keys == NULL && lines == NULL) {\n                SIFT3D_ERR(\"draw_matches: all outputs are NULL \\n\");\n                return SIFT3D_FAILURE;\n        }\n        if (keys_left == NULL && keys != NULL) {\n                SIFT3D_ERR(\"draw_matches: keys_left is NULL but keys is \"\n                        \"not \\n\");\n                return SIFT3D_FAILURE;\n        }\n        if (keys_right == NULL && keys != NULL) {\n                SIFT3D_ERR(\"draw_matches: keys_right is NULL but keys is \"\n                        \"not \\n\");\n                return SIFT3D_FAILURE;\n        }\n        if (match_left == NULL && lines != NULL) {\n                SIFT3D_ERR(\"draw_matches: match_left is NULL but lines \"\n                        \"is not \\n\");\n                return SIFT3D_FAILURE;\n        }\n        if (match_right == NULL && lines != NULL) {\n                SIFT3D_ERR(\"draw_matches: match_right is NULL but lines \"\n                        \"is not \\n\");\n                return SIFT3D_FAILURE;\n        }\n\n\t// Initialize intermediates\t\t\n        init_im(&concat_temp);\n        init_im(&left_padded);\n        init_im(&right_padded);\n        if (init_Mat_rm(&keys_right_draw, 0, 0, SIFT3D_DOUBLE, \n\t\tSIFT3D_FALSE) ||\n\t        init_Mat_rm(&match_right_draw, 0, 0, SIFT3D_DOUBLE, \n\t\t\tSIFT3D_FALSE) ||\n                init_Mat_rm(&keys_left_draw, 0, 0, SIFT3D_DOUBLE, \n\t\t\tSIFT3D_FALSE) ||\n                init_Mat_rm(&keys_draw, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE))\n\t        return SIFT3D_FAILURE;\n\n        // Pad the images to be the same in all dimensions but x\n\tif (init_im_with_dims(&right_padded, right->nx, ny_pad, nz_pad, 1) || \n\t        init_im_with_dims(&left_padded, left->nx, ny_pad, nz_pad, 1) ||\n\t   \tim_pad(right, &right_padded) || \n\t    \tim_pad(left, &left_padded)) {\n                SIFT3D_ERR(\"draw_matches: unable to pad images \\n\");\n                return SIFT3D_FAILURE;\n\t}\n\n\t// Draw a concatenated image\n\tif (im_concat(&left_padded, &right_padded, 0, concat_arg)) {\n                SIFT3D_ERR(\"draw_matches: Could not concatenate the \"\n                        \"images \\n\");\n                goto draw_matches_quit;\n        }\n\n        // Optionally draw the keypoints\n        if (keys != NULL) { \n\n                // Convert inputs to double\n                if (convert_Mat_rm(keys_right, &keys_right_draw, \n\t\t\tSIFT3D_DOUBLE) ||\n                        convert_Mat_rm(keys_left, &keys_left_draw, \n\t\t\t\tSIFT3D_DOUBLE))\n                        goto draw_matches_quit;\n       \n                // Pad the x-coordinate \n                for (i = 0; i < keys_right->num_rows; i++) {\n                        SIFT3D_MAT_RM_GET(&keys_right_draw, i, 0, double) += \n                                right_pad; \n                }\n\n                // Concatenate the points\n                if (concat_Mat_rm(&keys_left_draw, &keys_right_draw,\n                        &keys_draw, 0))\n                        goto draw_matches_quit;\n\n                // Draw the points\n                if (draw_points(&keys_draw, SIFT3D_IM_GET_DIMS(concat_arg), \n                        1, keys))\n                        goto draw_matches_quit;\n        }\n\n\t// Optionally draw the lines \n        if (lines != NULL) {\n\n                // Convert input to double\n                if (convert_Mat_rm(match_right, &match_right_draw, \n\t\t\tSIFT3D_DOUBLE))\n                        goto draw_matches_quit;\n\n                // Pad the x-coordinate \n                for (i = 0; i < match_right->num_rows; i++) {\n                        SIFT3D_MAT_RM_GET(&match_right_draw, i, 0, double) += \n                                right_pad;\n                }\n\n                // Draw the lines\n                if (draw_lines(match_left, &match_right_draw, \n                        SIFT3D_IM_GET_DIMS(concat_arg), lines))\n                        goto draw_matches_quit;\n        }\n\n        // Clean up\n        im_free(&concat_temp);\n        im_free(&left_padded);\n        im_free(&right_padded); \n        cleanup_Mat_rm(&keys_right_draw); \n        cleanup_Mat_rm(&keys_left_draw); \n        cleanup_Mat_rm(&keys_draw); \n        cleanup_Mat_rm(&match_right_draw); \n\treturn SIFT3D_SUCCESS;\n\ndraw_matches_quit:\n        im_free(&concat_temp);\n        im_free(&left_padded);\n        im_free(&right_padded); \n        cleanup_Mat_rm(&keys_right_draw);\n        cleanup_Mat_rm(&keys_left_draw); \n        cleanup_Mat_rm(&keys_draw); \n        cleanup_Mat_rm(&match_right_draw);\n        return SIFT3D_FAILURE;\n}\n\n/* Write a Keypoint_store to a text file. The keypoints are stored in a matrix\n * (.csv, .csv.gz), where each keypoint is a row. The elements of each row are\n * as follows:\n *\n * x y z o s ori11 ori12 ... orinn\n *\n * x - the x-coordinate\n * y - the y-coordinate\n * z - the z-coordinate\n * o - the pyramid octave. To convert to image coordinates, multiply x,y,z by \n *      pow(2, o)\n * s - the scale coordinate\n * ori(ij) - the ith row, jth column of the orientation matrix */\nint write_Keypoint_store(const char *path, const Keypoint_store *const kp) {\n\n        Mat_rm mat;\n\tint i, i_R, j_R;\n\n        // Keypoint data format constants\n        const int kp_x = 0; // column of x-coordinate\n        const int kp_y = 1; // column of y-coordinate\n        const int kp_z = 2; // column of z-coordinate\n        const int kp_o = 3; // column of octave index\n        const int kp_s = 4; // column of s-coordinate\n        const int kp_ori = 5; // first column of the orientation matrix\n        const int ori_numel = IM_NDIMS * IM_NDIMS; // Number of orientation \n                // elements\n        const int num_rows = kp->slab.num;\n        const int num_cols = kp_ori + ori_numel;\n\n        // Initialize the matrix\n        if (init_Mat_rm(&mat, num_rows, num_cols, SIFT3D_DOUBLE, \n\t\tSIFT3D_FALSE))\n                return SIFT3D_FAILURE;\n       \n        // Write the keypoints \n        for (i = 0; i < num_rows; i++) {\n\n                const Keypoint *const key = kp->buf + i;\n                const Mat_rm *const R = &key->R;\n\n                // Write the coordinates \n                SIFT3D_MAT_RM_GET(&mat, i, kp_x, double) = key->xd;\n                SIFT3D_MAT_RM_GET(&mat, i, kp_y, double) = key->yd; \n                SIFT3D_MAT_RM_GET(&mat, i, kp_z, double) = key->zd; \n                SIFT3D_MAT_RM_GET(&mat, i, kp_o, double) = key->o; \n                SIFT3D_MAT_RM_GET(&mat, i, kp_s, double) = key->sd;\n\n                // Write the orientation matrix\n                SIFT3D_MAT_RM_LOOP_START(R, i_R, j_R)\n\n                        const int kp_idx = kp_ori + \n                                SIFT3D_MAT_RM_GET_IDX(R, i_R, j_R);\n        \n                        SIFT3D_MAT_RM_GET(&mat, i, kp_idx, double) = \n                                (double) SIFT3D_MAT_RM_GET(R, i_R, j_R, float);\n\n                SIFT3D_MAT_RM_LOOP_END\n        }\n\n        // Write the matrix \n        if (write_Mat_rm(path, &mat))\n                goto write_kp_quit;\n\n        // Clean up\n        cleanup_Mat_rm(&mat);\n\n        return SIFT3D_SUCCESS;\n\nwrite_kp_quit:\n        cleanup_Mat_rm(&mat);\n        return SIFT3D_FAILURE;\n}\n\n/* Write SIFT3D descriptors to a text file.\n * See SIFT3D_Descriptor_store_to_Mat_rm for the file format. */\nint write_SIFT3D_Descriptor_store(const char *path, \n        const SIFT3D_Descriptor_store *const desc) {\n\n        Mat_rm mat;\n\n        // Initialize the matrix\n        if (init_Mat_rm(&mat, 0, 0, SIFT3D_FLOAT, SIFT3D_FALSE))\n                return SIFT3D_FAILURE;\n     \n        // Write the data into the matrix \n        if (SIFT3D_Descriptor_store_to_Mat_rm(desc, &mat))\n                goto write_desc_quit;\n\n        // Write the matrix to the file\n        if (write_Mat_rm(path, &mat))\n                goto write_desc_quit;\n\n        // Clean up\n        cleanup_Mat_rm(&mat);\n        return SIFT3D_SUCCESS;\n\nwrite_desc_quit:\n        cleanup_Mat_rm(&mat);\n        return SIFT3D_FAILURE;\n}\n\n"
  },
  {
    "path": "sift3d/sift.h",
    "content": "/* -----------------------------------------------------------------------------\r\n * sift.h\r\n * -----------------------------------------------------------------------------\r\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\r\n * -----------------------------------------------------------------------------\r\n * Public header for sift.c\r\n * -----------------------------------------------------------------------------\r\n */\r\n\r\n#include \"imtypes.h\"\r\n\r\n#ifndef _SIFT_H\r\n#define _SIFT_H\r\n\r\n#ifdef __cplusplus\r\nextern \"C\" {\r\n#endif\r\n\r\nvoid init_Keypoint_store(Keypoint_store *const kp);\r\n\r\nint init_Keypoint(Keypoint *const key);\r\n\r\nint resize_Keypoint_store(Keypoint_store *const kp, const size_t num);\r\n\r\nint copy_Keypoint(const Keypoint *const src, Keypoint *const dst);\r\n\r\nvoid cleanup_Keypoint_store(Keypoint_store *const kp);\r\n\r\nvoid init_SIFT3D_Descriptor_store(SIFT3D_Descriptor_store *const desc);\r\n\r\nvoid cleanup_SIFT3D_Descriptor_store(SIFT3D_Descriptor_store *const desc);\r\n\r\nint set_peak_thresh_SIFT3D(SIFT3D *const sift3d,\r\n                                const double peak_thresh);\r\n\r\nint set_corner_thresh_SIFT3D(SIFT3D *const sift3d,\r\n                                const double corner_thresh);\r\n\r\nint set_num_kp_levels_SIFT3D(SIFT3D *const sift3d,\r\n                                const unsigned int num_kp_levels);\r\n\r\nint set_sigma_n_SIFT3D(SIFT3D *const sift3d,\r\n                                const double sigma_n);\r\n\r\nint set_sigma0_SIFT3D(SIFT3D *const sift3d,\r\n                                const double sigma_n);\r\n\r\nint init_SIFT3D(SIFT3D *sift3d);\r\n\r\nint copy_SIFT3D(const SIFT3D *const src, SIFT3D *const dst);\r\n\r\nvoid cleanup_SIFT3D(SIFT3D *const sift3d);\r\n\r\nvoid print_opts_SIFT3D(void);\r\n\r\nint parse_args_SIFT3D(SIFT3D *const sift3d,\r\n        const int argc, char **argv, const int check_err);\r\n\r\nint SIFT3D_assign_orientations(const SIFT3D *const sift3d, \r\n        const Image *const im, Keypoint_store *const kp, double **const conf);\r\n\r\nint SIFT3D_detect_keypoints(SIFT3D *const sift3d, const Image *const im,\r\n\t\t\t    Keypoint_store *const kp);\r\n\r\nint SIFT3D_have_gpyr(const SIFT3D *const sift3d);\r\n\r\nint SIFT3D_extract_descriptors(SIFT3D *const sift3d, \r\n        const Keypoint_store *const kp, \r\n        SIFT3D_Descriptor_store *const desc);\r\n\r\nint SIFT3D_extract_raw_descriptors(SIFT3D *const sift3d, \r\n        const Image *const im, const Keypoint_store *const kp, \r\n        SIFT3D_Descriptor_store *const desc);\r\n\r\nint SIFT3D_extract_dense_descriptors(SIFT3D *const sift3d, \r\n        const Image *const in, Image *const desc);\r\n\r\nint SIFT3D_nn_match(const SIFT3D_Descriptor_store *const d1,\r\n\t\t    const SIFT3D_Descriptor_store *const d2,\r\n\t\t    const float nn_thresh, int **const matches);\r\n\r\nint Keypoint_store_to_Mat_rm(const Keypoint_store *const kp, Mat_rm *const mat);\r\n\r\nint SIFT3D_Descriptor_coords_to_Mat_rm(\r\n        const SIFT3D_Descriptor_store *const store, \r\n        Mat_rm *const mat);\r\n\r\nint SIFT3D_Descriptor_store_to_Mat_rm(const SIFT3D_Descriptor_store *const store, \r\n\t\t\t\t      Mat_rm *const mat);\r\n\r\nint Mat_rm_to_SIFT3D_Descriptor_store(const Mat_rm *const mat, \r\n\t\t\t\t      SIFT3D_Descriptor_store *const store);\r\n\r\nint SIFT3D_matches_to_Mat_rm(SIFT3D_Descriptor_store *d1,\r\n\t\t\t     SIFT3D_Descriptor_store *d2,\r\n\t\t\t     const int *const matches,\r\n\t\t\t     Mat_rm *const match1, \r\n\t\t\t     Mat_rm *const match2);\r\n\r\nint draw_matches(const Image *const left, const Image *const right,\r\n                 const Mat_rm *const keys_left, const Mat_rm *const keys_right,\r\n\t\t const Mat_rm *const match_left, const Mat_rm *const match_right,\r\n\t\t Image *const concat, Image *const keys, Image *const lines);\r\n\r\nint write_Keypoint_store(const char *path, const Keypoint_store *const kp);\r\n\r\nint write_SIFT3D_Descriptor_store(const char *path, \r\n        const SIFT3D_Descriptor_store *const desc);\r\n\r\n#ifdef __cplusplus\r\n}\r\n#endif\r\n\r\n#endif\r\n"
  },
  {
    "path": "wrappers/CMakeLists.txt",
    "content": "################################################################################\n# Copyright (c) 2015-2017 Blaine Rister et al., see LICENSE for details.\n################################################################################\n# Build file for the wrappers for other programming languages.\n################################################################################\n\n# Subdirectories for each language\nif (BUILD_Matlab)\n        add_subdirectory (matlab)\nendif ()\n"
  },
  {
    "path": "wrappers/matlab/CMakeLists.txt",
    "content": "################################################################################\n# Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n################################################################################\n# Build file for the Matlab wrapper toolbox.\n################################################################################\n\n# There is an issue with MEX compilation for Matlab 2018b that requires a newer \n# CMake.\ncmake_minimum_required (VERSION 3.15)\n\n# Enumerate the Matlab scripts for testing and installation\nset (INSTALL_SCRIPTS setupSift3D.m imRead3D.m imWrite3D.m detectSift3D.m\n        extractSift3D.m orientation3D.m keypoint3D.m registerSift3D.m \n        matchSift3D.m checkUnits3D.m Sift3DParser.m)\nset (TEST_SCRIPTS Sift3DTest.m)\n\n# Build the mex utility library\nadd_library (mexutil SHARED mexutil.c)\ntarget_include_directories (mexutil PUBLIC ${Matlab_INCLUDE_DIRS})\ntarget_include_directories (mexutil PUBLIC\n                $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>\n                $<INSTALL_INTERFACE:${INSTALL_INCLUDE_DIR}>\n)\ntarget_link_libraries (mexutil PUBLIC mexreg mexsift3D meximutil\n        ${Matlab_LIBRARIES})\n\n# Build the mex files\nmatlab_add_mex (NAME mexImRead3D\n        SRC mexImRead3D.c\n        LINK_TO mexutil ${Matlab_LIBRARIES}\n)\n\nmatlab_add_mex (NAME mexImWrite3D\n        SRC mexImWrite3D.c\n        LINK_TO mexutil ${Matlab_LIBRARIES}\n)\n\nmatlab_add_mex (NAME mexDetectSift3D \n        SRC mexDetectSift3D.c \n        LINK_TO mexutil ${Matlab_LIBRARIES}\n)\n\nmatlab_add_mex (NAME mexOrientation3D\n        SRC mexOrientation3D.c\n        LINK_TO mexutil ${Matlab_LIBRARIES}\n)\n\nmatlab_add_mex (NAME mexExtractSift3D\n        SRC mexExtractSift3D.c\n        LINK_TO mexutil ${Matlab_LIBRARIES}\n)\n\nmatlab_add_mex (NAME mexRegisterSift3D\n        SRC mexRegisterSift3D.c\n        LINK_TO mexutil ${Matlab_LIBRARIES}\n)\n\nmatlab_add_mex (NAME mexMatchSift3D\n        SRC mexMatchSift3D.c\n        LINK_TO mexutil ${Matlab_LIBRARIES}\n)\n\n# Enumerate the binary targets\nset (BINARIES mexutil mexImRead3D mexImWrite3D mexDetectSift3D \n        mexOrientation3D mexExtractSift3D mexRegisterSift3D mexMatchSift3D)\n\n# Configure the build destination\nset_target_properties (${BINARIES}\n        PROPERTIES \n        ARCHIVE_OUTPUT_DIRECTORY ${BUILD_TOOLBOX_DIR}\n        LIBRARY_OUTPUT_DIRECTORY ${BUILD_TOOLBOX_DIR}\n        RUNTIME_OUTPUT_DIRECTORY ${BUILD_TOOLBOX_DIR}\n)\n\n# Copy the scripts and test suite to the build tree\nforeach (FILENAME IN LISTS INSTALL_SCRIPTS TEST_SCRIPTS)\n        configure_file (${FILENAME} \"${BUILD_TOOLBOX_DIR}/${FILENAME}\" COPYONLY)\nendforeach ()\n\n# Install the matlab scripts\ninstall (FILES ${INSTALL_SCRIPTS} \n        DESTINATION ${INSTALL_TOOLBOX_DIR}\n)\n\n# Install the libraries and mex files\ninstall (TARGETS ${BINARIES}\n        RUNTIME DESTINATION ${INSTALL_TOOLBOX_DIR}\n        LIBRARY DESTINATION ${INSTALL_TOOLBOX_DIR}\n        ARCHIVE DESTINATION ${INSTALL_TOOLBOX_DIR}\n)\n"
  },
  {
    "path": "wrappers/matlab/README.md",
    "content": "# SIFT3D for Matlab\n\nCopyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n\n## Contents\n\nThis is a Matlab toolbox wrapping SIFT3D. It contains the following functions:\n- detectSift3D.m - Detect SIFT3D keypoints from a 3-dimensional array.\n- extractSift3D.m - Extract SIFT3D descriptors from a 3-dimensional array.\n- keypoint3D.m - Create SIFT3D keypoints from user-supplied coordinates.\n- orientation3D.m - Assign 3D orientations to user-supplied keypoints.\n- registerSift3D.m - Register images using SIFT3D keypoints and descriptors.\n- imRead3D.m - Read 2D and 3D images in DICOM and NIFTI formats.\n- imWrite3D.m - Write 2D and 3D images in DICOM and NIFTI formats.\n\n## Installation instructions\n\nIf you installed SIFT3D from binaries, a Matlab toolbox is included in the lib/wrappers/matlab subdirectory of your installation. If you compiled SIFT3D from source, the toolbox will be built only if Matlab was detected in your system.\n\nTo use the toolbox, simply add it to your Matlab path. This can be accomplished by adding the following line to your startup.m file:\n\n        run('/path/to/sift3d/lib/wrappers/matlab/setupSift3D')\n\nwhere /path/to/sift3d is the path to your SIFT3D installation. If you do not have a startup.m file, you can simply run this command prior to calling any SIFT3D functions.\n\n### Relocating the toolbox\n\nWe do not recommend moving the toolbox shared libraries (.so, .dylib, .dll). If you do, the operating system may not be able to find them when it tries to load the mex files. A better solution is to install SIFT3D in the place you want the toolbox to reside.\n\n### Troubleshooting\n\nWhen compiling from source, CMake might fail to find Matlab on your system, especially on Mac OSX. In that case, you should see \"Matlab not found\" after running the cmake command. You can fix this by manually specifying the location of Matlab in the variable Matlab_ROOT_DIR. For example,\n\n        cmake .. -DMatlab_ROOT_DIR=/path/to/MATLAB\n\nor on Mac OSX,\n\n        cmake .. -DMatlab_ROOT_DIR=/path/to/Matlab.app\n\nwhere /path/to/MATLAB is the location of your Matlab installation.\n\n## Usage instructions\n\nFor instructions on using the Matlab functions, use the help pages, e.g.\n\n        help detectSift3D\n\nSee /examples for sample programs.\n\n## Advanced features\n\nThis toolbox also includes a test suite, Sift3DTest.m. The test suite is found only in the source distribution, not the binary distributions, and must be run from the build tree. It requires [xUnit](http://www.mathworks.com/matlabcentral/fileexchange/22846-matlab-xunit-test-framework) to run. You can run the test suite with the following command:\n\n        runtests\n"
  },
  {
    "path": "wrappers/matlab/Sift3DParser.m",
    "content": "classdef Sift3DParser < inputParser\n    %Sift3DParser helper class to parse SIFT3D options. This class inherits\n    % from inputParser. Call its 'parseAndVerify' method to parse the input \n    % string. The results will be stored in the 'Results' field. Additional\n    % options can be added just as in inputParser.\n    \n    properties (SetAccess = private)\n        % Option names\n        peakThreshStr = 'peakThresh';\n        cornerThreshStr = 'cornerThresh';\n        numKpLevelsStr = 'numKpLevels';\n        sigmaNStr = 'sigmaN';\n        sigma0Str = 'sigma0';\n    end\n    \n    methods\n        \n        % Constructor\n        function self = Sift3DParser\n            \n            % Call the parent constructor\n            self = self@inputParser;\n            \n            % Add the SIFT3D options\n            self.addParamValue(self.peakThreshStr, [])\n            self.addParamValue(self.cornerThreshStr, [])\n            self.addParamValue(self.numKpLevelsStr, [])\n            self.addParamValue(self.sigmaNStr, [])\n            self.addParamValue(self.sigma0Str, [])\n        end\n        \n        % Verify and retrieve the SIFT3D options\n        function optStruct = parseAndVerify(self, varargin)\n            \n            % Parse the input\n            self.parse(varargin{:})\n              \n            % Retrieve the results\n            peakThresh = self.Results.peakThresh;\n            cornerThresh = self.Results.cornerThresh;\n            numKpLevels = self.Results.numKpLevels;\n            sigmaN = self.Results.sigmaN;\n            sigma0 = self.Results.sigma0;\n            \n            % Verify the results\n            if ~isempty(peakThresh)\n                validateattributes(peakThresh, {'numeric'}, ...\n                    {'real', 'positive', 'scalar', '<=', 1}, 'peakThresh')\n            end\n            if ~isempty(cornerThresh)\n                validateattributes(cornerThresh, {'numeric'}, ...\n                    {'real', 'nonnegative', 'scalar', '<=', 1}, ...\n                    'cornerThresh')\n            end\n            if ~isempty(numKpLevels)\n                validateattributes(numKpLevels, {'numeric'}, ...\n                    {'real', 'integer', 'scalar', 'positive'}, ...\n                    'numKpLevels')\n            end\n            if ~isempty(sigmaN)\n                validateattributes(sigmaN, {'numeric'}, ...\n                    {'real', 'positive', 'scalar'}, 'sigmaN')\n            end\n            if ~isempty(sigma0)\n                validateattributes(sigma0, {'numeric'}, ...\n                    {'real', 'positive', 'scalar'}, 'sigma0')\n            end\n            if sigmaN >= sigma0\n                error('Cannot have sigmaN >= sigma0')\n            end            \n            \n            % Collect the options in a struct\n            optStruct = struct( ...\n                self.peakThreshStr, peakThresh, ...\n                self.cornerThreshStr, cornerThresh, ...\n                self.numKpLevelsStr, numKpLevels, ...\n                self.sigmaNStr, sigmaN, ...\n                self.sigma0Str, sigma0);\n        end\n    end\nend"
  },
  {
    "path": "wrappers/matlab/Sift3DTest.m",
    "content": "classdef Sift3DTest < TestCase\n%Sift3DTest a test suite for SIFT3D.\n%\n% To run this test suite, you must install xUnit. As of January\n% 12th, 2017, xUnit is available at:\n% http://www.mathworks.com/matlabcentral/fileexchange/47302-xunit4\n%\n% Run the tests with the following command:\n%   runxunit\n%\n% This test suite can only be run from the build tree.\n%\n% Copyright (c) 2015-2017 Blaine Rister et al., see LICENSE for details.\n\n    properties (SetAccess = private)\n        cd\n        buildDir\n        binDir\n        examplesDir\n        im1Name\n        im2Name\n        dataName\n        kpCmd\n        regCmd\n        tolText\n        fullTest\n    end\n    \n    methods\n        \n        % Constructor\n        function self = Sift3DTest(name)\n            \n            % Call the parent constructor\n            self = self@TestCase(name);\n            \n            % Current directory\n            self.cd = fileparts(mfilename('fullpath'));\n            \n            % Build directory\n            self.buildDir = fullfile(self.cd, '..', '..', '..');\n            \n            % Binary directory\n            self.binDir = fullfile(self.buildDir, 'bin');\n            \n            % Examples directory\n            if ispc\n                self.examplesDir = self.binDir;\n            else\n                self.examplesDir = fullfile(self.buildDir, 'examples');\n            end\n            \n            % Image file names\n            self.im1Name = fullfile(self.examplesDir, '1.nii.gz');\n            self.im2Name = fullfile(self.examplesDir, '2.nii.gz');\n            \n            % Keypoints command name\n            self.kpCmd = fullfile(self.binDir, 'kpSift3D');\n            \n            % Registration command name\n            self.regCmd = fullfile(self.binDir, 'regSift3D');\n            \n            % Error tolerance for text output\n            self.tolText = 0.01;\n            \n            % Run the tests on real data (slow)\n            self.fullTest = true;\n            \n        end\n        \n        % Test keypoint detection against the CLI version\n        function detectCliTest(self)\n            \n            if ~self.fullTest || ispc\n                return\n            end\n                \n            % Output file name\n            kpCliName = 'kpCli.csv';\n            \n            % Detect keypoints using the command line interface\n            status = runCmd([self.kpCmd ' --keys ' kpCliName ' ' ...\n                self.im1Name]);\n            assertEqual(status, 0);\n            \n            % Load the CLI keypoints\n            kpCli = csvread(kpCliName);\n            \n            % Load the image data\n            [im1, units1] = imRead3D(self.im1Name);\n            \n            % Detect keypoints using matlab\n            keys = detectSift3D(im1, 'units', units1);\n            \n            % Check the dimensions\n            assertEqual(size(kpCli, 1), length(keys));\n            assertEqual(size(kpCli, 2), numel(keys(1).coords) + ...\n                numel(keys(1).octave) + numel(keys(1).scale) + ...\n                numel(keys(1).ori));\n            \n            % Compare the two\n            for i = 1 : length(keys)\n                \n                mKey = keys(i);\n                cliKey = kpCli(i, :);\n                \n                % Check the coordinates\n                assertElementsAlmostEqual(mKey.coords, cliKey(1:3), ...\n                    'absolute', self.tolText);\n                \n                % Check the octave\n                assertEqual(mKey.octave, cliKey(4));\n                \n                % Check the scale\n                assertElementsAlmostEqual(mKey.scale, cliKey(5), ...\n                    'absolute', self.tolText);\n                \n                % Check the orientation\n                assertElementsAlmostEqual(mKey.ori, ...\n                    reshape(cliKey(6:end), size(mKey.ori))', ...\n                    'absolute', self.tolText);\n            end\n            \n            % Clean up\n            delete(kpCliName);\n        end\n        \n        % Test descriptor extraction against the CLI version\n        function extractCliTest(self)\n            \n            if ~self.fullTest || ispc\n                return\n            end\n                \n                % Output file name\n                descCliName = 'descCli.csv';\n                \n                % Extract descriptors using the command line interface\n                status = runCmd([self.kpCmd ' --desc ' descCliName ' ' ...\n                    self.im1Name]);\n                assertEqual(status, 0);\n                \n                % Read the results\n                descCli = csvread(descCliName);\n                \n                % Load the image data\n                [im1, units1] = imRead3D(self.im1Name);\n                \n                % Extract descriptors using matlab\n                keys = detectSift3D(im1, 'units', units1);\n                [desc, coords] = extractSift3D(keys);\n                \n                % Check the dimensions\n                assertEqual(size(desc, 1), size(coords, 1));\n                assertEqual(size(descCli, 1), size(desc, 1));\n                assertEqual(size(descCli, 2), size(desc, 2) + size(coords, 2));\n                \n                % Compare the two\n                for i = 1 : length(keys)\n                    \n                    cliDescrip = descCli(i, :);\n                    \n                    % Check the coordinates\n                    assertElementsAlmostEqual(cliDescrip(1 : 3), ...\n                        coords(i, :), 'absolute', self.tolText);\n                    \n                    % Check the descriptor\n                    assertElementsAlmostEqual(cliDescrip(4 : end), ...\n                        desc(i, :), 'absolute', self.tolText);\n                end\n                \n                % Clean up\n                delete(descCliName);\n            \n        end\n        \n        % Test that \"raw\" image descriptors are close to those extracted\n        % from a Gaussian scale-space pyramid\n        function rawDescriptorTest(self)\n            \n            if ~self.fullTest\n                return\n            end\n                \n            % Load the image data\n            [im1, units1] = imRead3D(self.im1Name);\n            \n            % Detect keypoints\n            keys = detectSift3D(im1, 'units', units1);\n            \n            % Extract descriptors using the pyramid\n            [descPyr, coordsPyr] = extractSift3D(keys);\n            \n            % Extract raw descriptors\n            [descRaw, coordsRaw] = extractSift3D(keys, im1, units1);\n            \n            % Check the results\n            assertElementsAlmostEqual(coordsPyr, coordsRaw);\n            assertElementsAlmostEqual(descPyr, descRaw, 'absolute', 0.2);\n            \n        end\n        \n        % Test that \"raw\" keypoint orientations are close to those\n        % extracted from a Gaussian scale-space pyramid\n        function rawOrientationTest(self)\n           if ~self.fullTest\n              return \n           end\n           \n           % Load the image data\n           [im, units] = imRead3D(self.im1Name);\n           \n           % Detect keypoints\n           keys = detectSift3D(im, 'units', units);\n           \n           % Assign orientations to those same keypoints\n           keysRaw = orientation3D(keys, im, units);\n           \n           % Check the dimensions\n           assertEqual(size(keys), size(keysRaw));\n           \n           % Create a basis vector\n           u = zeros(length(keys(1).ori), 1);\n           u(1) = 1;\n           \n           % Check the results\n           ang = zeros(length(keys), 1);\n           for i = 1 : length(keys)\n               key = keys(i);\n               keyRaw = keysRaw(i);\n               \n               % Rotate the basis vector\n               uRotKey = key.ori * u;\n               uRotKeyRaw = keyRaw.ori * u;\n               \n               % Compute the angle between the rotated vectors\n               ang(i) = acos(abs(dot(uRotKey, uRotKeyRaw)));\n           end\n           \n           % Test the median angle\n           assertElementsAlmostEqual(median(ang), 0, 'absolute', pi / 8);\n        end\n        \n        % Test that detected keypoints are valid\n        function detectValidTest(self)\n            if ~self.fullTest\n                return\n            end\n            \n            % Load the image data\n            [im, units] = imRead3D(self.im1Name);\n            \n            % Extract keypoints\n            keys = detectSift3D(im, 'units', units);\n            \n            % Check the keypoints\n            for i = 1 : length(keys)\n                \n                key = keys(i);\n            \n                % Check containment in the original image\n                baseCoords = key.coords * pow2(-key.octave);\n                assertTrue(all(baseCoords >= 0));\n                assertTrue(all(baseCoords < size(im)));\n                \n                % Check orthogonality of the rotation matrix\n                assertElementsAlmostEqual(key.ori * key.ori', ...\n                    eye(length(key.ori)), 'absolute', 1E-3);\n                \n                % Check determinant of the rotation matrix\n                assertElementsAlmostEqual(det(key.ori), 1, 'absolute', ...\n                    1E-3);\n            end\n        end\n        \n        % Test registering an image against the CLI version\n        function regCliTest(self)\n            if ~self.fullTest || ispc\n                return\n            end\n            \n            % Output file names\n            matchesName = 'matches.csv';\n            transformName = 'transform.csv';\n            \n            % Register with the CLI\n            status = runCmd([self.regCmd ' --matches ' matchesName ...\n                ' --transform ' transformName ' ' self.im1Name ' ' ...\n                self.im2Name]);\n            assertEqual(status, 0);\n            \n            % Read the results\n            matchesCli = csvread(matchesName);\n            transformCli = csvread(transformName);\n            \n            % Convert the results to Matlab's format\n            matchSrcCli = matchesCli(:, 1 : 3);\n            matchRefCli = matchesCli(:, 4 : end);\n            \n            % Load the images\n            [im1, units1] = imRead3D(self.im1Name);\n            [im2, units2] = imRead3D(self.im2Name);\n            \n            % Register with Matlab\n            [A, matchSrc, matchRef] = registerSift3D(im1, im2, ...\n                'srcUnits', units1, 'refUnits', units2);\n            \n            % Check the dimensions\n            assertEqual(size(A), size(transformCli));\n            assertEqual(size(matchSrc), size(matchSrcCli));\n            assertEqual(size(matchRef), size(matchRefCli));\n            \n            % Check the matches (the only error is conversion to text)\n            assertElementsAlmostEqual(matchSrc, matchSrcCli, ...\n                'absolute', self.tolText);\n            assertElementsAlmostEqual(matchRef, matchRefCli, ...\n                'absolute', self.tolText);\n            \n            % Check the transformation (discrepancies introduced by \n            % randomized regression)\n            assertElementsAlmostEqual(A(:, 1 : 3), ...\n                transformCli(:, 1 : 3), 'absolute', 5E-2);\n            assertElementsAlmostEqual(A(:, end), transformCli(:, end), ...\n                'absolute', 5);\n            \n            % Clean up\n            delete(matchesName);\n            delete(transformName);\n        end\n        \n        % Test anisotropic registration\n        function regAnisoTest(self)\n            \n            if ~self.fullTest\n                return\n            end\n            \n            % Load the image\n            [im, units] = imRead3D(self.im1Name);\n            \n            % Remove half of the slices of the image\n            imAniso = im(:, :, 1 : 2 : end);\n            unitsAniso = [units(1) units(2) units(3) * 2];\n            \n            % Register the original to the anisotropic image\n            A = registerSift3D(im, imAniso, 'srcUnits', units, ...\n                'refUnits', unitsAniso, 'resample', true);\n            \n            % Form the reference (ground truth) transformation\n            refA = [eye(3) zeros(3, 1)];\n            refA(3, 3) = 2;\n            \n            % Check the transformation\n            assertElementsAlmostEqual(A(:, 1 : 3), refA(:, 1 : 3), ...\n                'absolute', 5E-2);\n            assertElementsAlmostEqual(A(:, end), refA(:, end), ...\n                'absolute', 5);\n        end\n        \n        % Test matching descriptors against the C version\n        function matchTest(self)\n            \n            if ~self.fullTest\n                return\n            end\n            \n            % Load the images\n            [im1, units1] = imRead3D(self.im1Name);\n            [im2, units2] = imRead3D(self.im2Name);\n            \n            % Register with the C version and get the matches\n            [~, match1, match2] = registerSift3D(im1, im2, ...\n                'srcUnits', units1, 'refUnits', units2);\n            \n            % Extract descriptors from each image\n            keys = detectSift3D(im1, 'units', units1);\n            [desc1, coords1] = extractSift3D(keys);\n            keys = detectSift3D(im2, 'units', units2);\n            [desc2, coords2] = extractSift3D(keys);\n            \n            % Match with the Matlab version and convert to coordinates\n            matches = matchSift3D(desc1, coords1, desc2, coords2);\n            match1M = coords1(matches(:, 1), :);\n            match2M = coords2(matches(:, 2), :);\n            \n            assertElementsAlmostEqual(match1M, match1, 'relative', 1E-3);\n            assertElementsAlmostEqual(match2M, match2, 'relative', 1E-3);\n            \n        end\n        \n        % Test registration with invalid matching threshold\n        function regInvalidMatchTest(self)\n            \n           % Load the images\n           im1 = imRead3D(self.im1Name);\n           im2 = imRead3D(self.im2Name);\n            \n           threwErr = false;\n           try \n               A = registerSift3D(im1, im2, 'nnThresh', 2);\n           catch ME\n               threwErr = true;\n           end\n           assertTrue(threwErr);\n        end\n        \n        % Test registration with invalid error threshold\n        function regInvalidErrTest(self)\n            \n            % Load the images\n            im1 = imRead3D(self.im1Name);\n            im2 = imRead3D(self.im2Name);\n            \n            threwErr = false;\n            try\n                A = registerSift3D(im1, im2, 'errThresh', -1);\n            catch ME\n                threwErr = true;\n            end\n            assertTrue(threwErr);\n        end\n        \n        % Test registration with invalid number of iterations\n        function regInvalidIterTest(self)\n            \n            % Load the images\n            im1 = imRead3D(self.im1Name);\n            im2 = imRead3D(self.im2Name);\n            \n            threwErr = false;\n            try\n                A = registerSift3D(im1, im2, 'numIter', 0);\n            catch ME\n                threwErr = true;\n            end\n            assertTrue(threwErr);\n        end\n        \n        % Test reading and writing a NIFTI image\n        function niftiIOTest(self)\n            \n            % The temporary file name\n            imName = 'temp.nii.gz';\n            \n            % Make random image data\n            imWritten = rand(10, 15, 20);\n            \n            % Write the image as a NIFTI file\n            imWrite3D(imName, imWritten);\n            \n            % Read the image back\n            imRead = imRead3D(imName);\n            \n            % Clean up\n            delete(imName);\n            \n            % Ensure the results are identical\n            assertElementsAlmostEqual(imWritten, imRead, 'relative', 1E-3);\n        end\n        \n        % Test reading and writing a DICOM image\n        function dicomIOTest(self)\n            \n            % The temporary file name\n            imName = 'temp.dcm';\n            \n            % Remove any past instances of this file\n            if exist(imName, 'file')\n                delete(imName)\n            end\n            \n            % Make random image data, scaled to the range [0, 1]\n            imWritten = rand(10, 15, 20);\n            imWritten = imWritten / max(imWritten(:));\n            \n            % Write the image as a DICOM file\n            imWrite3D(imName, imWritten);\n            \n            % Read the image back and scale it\n            imRead = imRead3D(imName);\n            imRead = imRead / max(imRead(:));\n            \n            % Clean up\n            delete(imName);\n            \n            % Ensure the results are identical\n            assertElementsAlmostEqual(imWritten, imRead, 'absolute', 1E-2);\n        end\n        \n        % Test reading and writing a directory of DICOM images\n        function dirIOTest(self)\n            \n            % The temporary file name\n            dirName = 'temp';\n            \n            % Make random image data, scaled to the range [0, 1]\n            imWritten = rand(10, 15, 20);\n            imWritten = imWritten / max(imWritten(:));\n            \n            % Write the image as a DICOM file\n            imWrite3D(dirName, imWritten);\n            \n            % Read the image back and scale it\n            imRead = imRead3D(dirName);\n            imRead = imRead / max(imRead(:));\n            \n            % Clean up\n            rmdir(dirName, 's');\n            \n            % Ensure the results are identical\n            assertElementsAlmostEqual(imWritten, imRead, 'absolute', 1E-2);\n        end\n        \n        % Test reading and writing a 2D NIFTI image\n        function nifti2DTest(self)\n            % The temporary file name\n            imName = 'temp.nii.gz';\n            \n            % Make random image data\n            imWritten = rand(20, 15);\n            \n            % Write the image as a NIFTI file\n            imWrite3D(imName, imWritten);\n            \n            % Read the image back\n            imRead = imRead3D(imName);\n            \n            % Clean up\n            delete(imName);\n            \n            % Ensure the results are identical\n            assertElementsAlmostEqual(imWritten, imRead, 'relative', 1E-3);\n        end\n        \n        % Test reading and writing a 2D DICOM image\n        function dicom2DTest(self)\n            \n            % The temporary file name\n            imName = 'temp.dcm';\n            \n            % Make random image data, scaled to the range [0, 1]\n            imWritten = rand(20, 15);\n            imWritten = imWritten / max(imWritten(:));\n            \n            % Write the image as a DICOM file\n            imWrite3D(imName, imWritten);\n            \n            % Read the image back and scale it\n            imRead = imRead3D(imName);\n            imRead = imRead / max(imRead(:));\n            \n            % Clean up\n            delete(imName);\n            \n            % Ensure the results are identical\n            assertElementsAlmostEqual(imWritten, imRead, 'absolute', 1E-2);\n        end\n        \n        % Test reading and writing units from a NIFTI image\n        function niftiUnitsTest(self)\n            \n            % The temporary file name\n            imName = 'temp.nii.gz';\n            \n            % Make random image data\n            imWritten = rand(10, 15, 20);\n            \n            % Make random units\n            unitsWritten = rand(3, 1);\n            \n            % Write the image as a NIFTI file\n            imWrite3D(imName, imWritten, unitsWritten);\n            \n            % Read the units back\n            [~, unitsRead] = imRead3D(imName);\n            \n            % Clean up\n            delete(imName);\n            \n            % Ensure the results are identical\n            assertElementsAlmostEqual(unitsWritten, unitsRead, ...\n                'relative', 1E-3);\n        end\n        \n        % Test reading and writing units from a DICOM image\n        function dicomUnitsTest(self)\n            \n            % The temporary file name\n            imName = 'temp.dcm';\n            \n            % Make random image data\n            imWritten = rand(10, 15, 20);\n            \n            % Make random units\n            unitsWritten = rand(3, 1);\n            \n            % Write the image as a NIFTI file\n            imWrite3D(imName, imWritten, unitsWritten);\n            \n            % Read the units back\n            [~, unitsRead] = imRead3D(imName);\n            \n            % Clean up\n            delete(imName);\n            \n            % Ensure the results are identical\n            assertElementsAlmostEqual(unitsWritten, unitsRead, ...\n                'relative', 1E-3);\n        end\n        \n        % Test reading and writing units from a directory of DICOM images\n        function dirUnitsTest(self)\n            \n            % The temporary file name\n            imName = 'temp';\n            \n            % Make random image data\n            imWritten = rand(10, 15, 20);\n            \n            % Make random units\n            unitsWritten = rand(3, 1);\n            \n            % Write the image as a NIFTI file\n            imWrite3D(imName, imWritten, unitsWritten);\n            \n            % Read the units back\n            [~, unitsRead] = imRead3D(imName);\n            \n            % Clean up\n            rmdir(imName, 's');\n            \n            % Ensure the results are identical\n            assertElementsAlmostEqual(unitsWritten, unitsRead, ...\n                'relative', 1E-3);\n        end\n        \n        % Test reading and writing units from a 2D DICOM image\n        function units2DTest(self)\n            \n            % The temporary file name\n            imName = 'temp.dcm';\n            \n            % Make random image data\n            imWritten = rand(20, 15);\n            \n            % Make random units\n            unitsWritten = rand(2, 1);\n            \n            % Write the image as a NIFTI file\n            imWrite3D(imName, imWritten, unitsWritten);\n            \n            % Read the image back\n            [~, unitsRead] = imRead3D(imName);\n            \n            % Remove the trailing units\n            unitsRead = unitsRead(1 : length(unitsWritten));\n            \n            % Clean up\n            delete(imName);\n            \n            % Ensure the results are identical\n            assertElementsAlmostEqual(unitsWritten, unitsRead, ...\n                'relative', 1E-3);\n        end\n        \n        % Test switching the parameter order in imWrite3D\n        function writeSwappedParamsTest(self)\n            \n            % Make a random image\n            im = rand(10, 10, 10);\n            \n            % Try to write it, with parameters exchanged\n            threwErr = false;\n            try\n                imWrite3D(im, 'im.nii.gz');\n            catch ME\n                threwErr = true;\n            end\n            assertTrue(threwErr);\n        end\n        \n        % Test reading an invalid filetype\n        function readInvalidTypeTest(self)\n            \n            % Temporary file name\n            tempFileName = 'temp.mat';\n            \n            % Make a temporary file\n            threwErr = false;\n            save(tempFileName);\n            \n            % Try to read it\n            try\n                im = imRead3D(tempFileName);\n            catch ME\n                threwErr = true;\n            end\n            \n            % Clean up\n            delete(tempFileName);\n            \n            assertTrue(threwErr);\n        end\n        \n        % Test writing an invalid filetype\n        function writeInvalidTypeTest(self)\n            im = rand(10, 10, 10, 10);\n            threwErr = false;\n            try\n                imWrite3D(im, 'bogus.txt')\n            catch ME\n                threwErr = true;\n            end\n            assertTrue(threwErr);\n        end\n        \n        % Test reading from a nonexistent file\n        function imReadNonexistentTest(self)\n            \n            threwErr = false;\n            try\n                im = imRead3D('nonexistent.nii.gz');\n            catch ME\n                threwErr = true;\n            end\n            assertTrue(threwErr);\n        end\n        \n        % Test writing negative units\n        function negativeUnitsTest(self)\n            \n            % Invalid units\n            units = [1 1 -1];\n            \n            % Fake image\n            im = zeros(10, 10, 10);\n            \n            threwErr = false;\n            try\n                imWrite3D('fake.nii.gz', im, units);\n            catch ME\n                threwErr = true;\n            end\n            assertTrue(threwErr);\n        end\n        \n        % Test writing zero-valued units\n        function zeroUnitsTest(self)\n            \n            % Invalid units\n            units = [1 0 1];\n            \n            % Fake image\n            im = zeros(10, 10, 10);\n            \n            threwErr = false;\n            try\n                imWrite3D('fake.nii.gz', im, units);\n            catch ME\n                threwErr = true;\n            end\n            assertTrue(threwErr);\n        end\n        \n        % Test making valid keypoints\n        function keypointValidTest(self)\n            % Make the keypoints\n            coords = [1 1 1; 2 2 2];\n            scale = [1 2];\n            ori = repmat(rotMat(1), [1 1 length(scale)]);\n            keys = keypoint3D(coords, scale, ori);\n            \n            % Test the results\n            for i = 1 : length(keys)\n                key = keys(i);\n                assertEqual(key.coords, coords(i, :));\n                assertEqual(key.scale, scale(i));\n                assertEqual(key.ori, ori(:, :, i));\n            end\n        end\n        \n        % Test making keypoints with invalid coordinate dimensions\n        function keypointInvalidCoordTest(self)\n            % Make the keypoints\n            coords = [1 1 1 1];\n            \n            % Test the results\n            threwErr = false;\n            try\n                keypoint3D(coords);\n            catch ME\n                threwErr = true;\n            end\n            assertTrue(threwErr);\n        end\n        \n        % Test making keypoints with invalid scale dimensions\n        function keypointInvalidScaleTest(self)\n            % Make the keypoints\n            coords = [1 1 1; 2 2 2];\n            scale = [1 1; 2 2];\n            \n            % Test the results\n            threwErr = false;\n            try\n                keypoint3D(coords, scale);\n            catch ME\n                threwErr = true;\n            end\n            assertTrue(threwErr);\n        end\n        \n        % Test making keypoints with invalid rotation matrix dimensions\n        function keypointInvalidRotDimsTest(self)\n            % Make the keypoints\n            coords = [1 1 1; 2 2 2];\n            ori = repmat(ones(3, 4), [1 1 size(coords, 1)]);\n            \n            % Test the results\n            threwErr = false;\n            try\n                keypoint3D(coords, [], ori);\n            catch ME\n                threwErr = true;\n            end\n            assertTrue(threwErr);\n        end\n        \n        % Test making a keypoint with a reflection matrix\n        function keypointReflectTest(self)\n            % Make the keypoints\n            coords = [1 1 1];\n            ori = eye(3);\n            ori(1, 1) = -1;\n            \n            % Test the results\n            threwErr = false;\n            try\n                keypoint3D(coords, [], ori);\n            catch ME\n                threwErr = true;\n            end\n            assertTrue(threwErr);\n        end\n        \n        % Test making a keypoint with a non-orthogonal matrix\n        function keypointOrthTest(self)\n            % Make the keypoints\n            coords = [1 1 1];\n            ori = rand(3);\n            \n            % Ensure that the determinant is equal to one, so it is\n            % orthogonality and not reflection which causes an error\n            detOri = det(ori);\n            factor = sign(detOri) * abs(detOri) ^ (-1 / length(ori));\n            ori = ori * factor;\n            assert(abs(det(ori) - 1) < eps * 1E2);\n            \n            % Test the results\n            threwErr = false;\n            try\n                keypoint3D(coords, [], ori);\n            catch ME\n                threwErr = true;\n            end\n            assertTrue(threwErr);\n        end\n    end\nend\n\nfunction status = runCmd(cmd)\n%runCmd helper function to run a command in the default system environment,\n% without Matlab's changes to the environment variables\n\n% The CLI is not supported on Windows\nif ispc\n    warning(['The command-line interface is not supported in the ' ...\n        'Windows version of SIFT3D'])\nend\n\n% Strip the LD_LIBRARY_PATH environment variable of Matlab\n% directories\nldPathVar = 'LD_LIBRARY_PATH';\noldLdPath = getenv(ldPathVar);\nnewLdPath = regexprep(oldLdPath, '[^:]*MATLAB[^:]*:*', '', 'ignorecase');\nsetenv(ldPathVar, newLdPath);\n\n% Run the command\nstatus = system(cmd);\n\n% Return the environment to its previous state\nsetenv(ldPathVar, oldLdPath);\n\nend\n\nfunction R = rotMat(theta)\n%rotMat Helper function to make a 3D rotation matrix, rotating by angle\n% theta in the XY plane\nR = eye(3);\nR(1, 1) = cos(theta);\nR(1, 2) = -sin(theta);\nR(2, 1) = sin(theta);\nR(2, 2) = cos(theta);\nend\n"
  },
  {
    "path": "wrappers/matlab/checkUnits3D.m",
    "content": "function units = checkUnits3D(units, name)\n%checkUnits3D helper function to check the validity of the 'units' argument\n% to various SIFT3D functions. Returns a properly formatted version of \n% units, with missing values set to 1.\n%\n% The optional 'name' argument is used in error messages.\n\n% Default arguments\nif nargin < 1 || isempty(units)\n    units = ones(3, 1);\nend\nif nargin < 2 || isempty(name)\n    name = 'units';\nend\n\n% Number of image dimensions\nndim = 3;\n\n% Validate units\nvalidateattributes(units, {'numeric'}, {'real', 'vector', 'positive'}, ...\n    name)\nif length(units) > ndim \n    error([name ' must be a [3x1] real numeric vector'])\nend\n\n% Convert to double\nunits = double(units);\n\n% Default for missing values\nif length(units) < ndim\n    units(length(units) + 1 : ndim) = 1;\nend\n\n% Transpose column vectors\nif size(units, 2) > 1\n    units = units';\nend\n\nend"
  },
  {
    "path": "wrappers/matlab/detectSift3D.m",
    "content": "function keys = detectSift3D(im, varargin)\n%detectSift3D(im, options) Detect Sift3D keypoints in an image.\n%  Arguments:\n%    im - An [MxNxP] array, where voxels are indexed in (x, y, z) order.\n%\n%  Options:\n%    units - See imRead3D. If units are specified, the detected\n%       keypoints are isotropic even when im is not.\n%    peakThresh - The smallest allowed absolute DoG value, as a fraction\n%       of the largest. Must be in the interval (0, 1]. (default: 0.10)\n%    cornerThresh - The smalled allowed corner score, on the interval\n%       [0, 1]. (default: 0.5)\n%    numKpLevels - The number of pyramid levels per octave in which\n%       keypoints are found. Must be a positive integer. (default: 3)\n%    sigmaN - The nominal scale parameter of the input data, on the\n%       interval (0, inf). (default: 1.15)\n%    sigma0 - The scale parameter of the first level of octave 0, in the\n%       interval (0, inf). (default: 1.6)\n%\n%  Return values:\n%    keys - A [Qx1] array of keypoint structs. See keypoint3D.m for the\n%      struct definition.\n%\n%  Note: This function will reserve memory that persists after it has\n%  finished. To release all SIFT3D memory, use 'clear mex'.\n%\n%  Keypoint coordinates are defined in the space of their pyramid level.\n%  To convert them to Matlab indices in the input image, use the following\n%  transformation:\n%      idx = key.coords * pow2(-key.octave) + 1\n%\n%  Examples:\n%      im = rand(50, 50, 50);\n%      keys = detectSift3D(im);\n%\n%      [im, units] = imRead3D('someFile.dcm');\n%      keys = detectSift3D(im, 'units', units);\n%\n%  See also:\n%    extractSift3D, imRead3D, imWrite3D, keypoint3D, setupSift3D\n%\n% Copyright (c) 2015-2018 Blaine Rister et al., see LICENSE for details.\n\n% Option names\nunitsStr = 'units';\n\n% Parse options\nparser = Sift3DParser;\nparser.addParamValue(unitsStr, [])\noptStruct = parser.parseAndVerify(varargin{:});\nunits = parser.Results.units;\n\n% Verify inputs\nnarginchk(1, inf)\nif isempty(im)\n    error('im is empty')\nend\nif ndims(im) ~= 3\n    error(['im must have 3 dimensions, detected ' num2str(ndims(im))]);\nend\nunits = checkUnits3D(units);\n\n% Scale and convert the image to single precision\nim = single(im);\nim = im / (max(im(:)) + eps);\n\n% Detect features\nkeys = mexDetectSift3D(im, units, optStruct);\n\nend\n\n"
  },
  {
    "path": "wrappers/matlab/extractSift3D.m",
    "content": "function [desc, coords] = extractSift3D(keys, im, units)\n%extractSift3D(keys, im, units) Extract Sift3D descriptors from keypoints.\n%  Arguments:\n%    keys - An array of n keypoint structs. See keypoint3D.m for the\n%      format.\n%    im - (Optional) An [MxNxP] array, where voxels are indexed in\n%      (x, y, z) order. If im is empty or not provided, this function uses \n%      the Gaussian scale-space pyramid from the most recent call to \n%      detectSift3D. (Note: this data is overwritten by calls to\n%      registerSift3D.)\n%    units - (Optional) See imRead3D. If units are specified, the extracted\n%       descriptors are isotropic even when im is not.\n%\n%  Return values:\n%    desc - An [n x 768] array of descriptors. The ith row is a descriptor\n%      corresponding to keys(i).\n%    coords - An [n x 3] array of descriptor coordinates, defined in the\n%      space of the input image (see the description of the \"im\" argument).\n%      Note these coordinates are 0-indexed, so you need to add 1 to get the\n%      equivalent Matlab index. (See detectSift3D.m for more explanation.)\n%\n%  Examples:\n%    % Extract without units\n%    im = rand(50, 50, 50);\n%    keys = detectSift3D(im);\n%    [desc, coords] = extractSift3D(keys);\n%\n%    % Extract with units\n%    [im, units] = imRead3D('someFile.dcm');\n%    keys = detectSift3D(im, units);\n%    [desc, coords] = extractSift3D(keys);\n%\n%    % Extract from manually-defined keypoints\n%    [im, units] = imRead3D('someFile.dcm');\n%    keys = keypoint3D([0 0 0]);\n%    keys = orientation3D(keys, im, units);\n%    [desc, coords] = extractSift3D(keys, im, units);\n%\n%  See also:\n%    detectSift3D, imRead3D, keypoint3D, orientation3D, setupSift3D\n%\n% Copyright (c) 2015-2018 Blaine Rister et al., see LICENSE for details.\n\n% Required field dimensions\ncoordsSizeReq = [1 3];\noriSizeReq = [3 3];\n\n% The number of descriptor elements\ndescNumel = 768;\n\n% Default parameters\nif nargin < 2\n    im = [];\nend\n\nif nargin < 3\n    units = [];\nend\n\n% Verify inputs\nif nargin < 1\n    error('Not enough arguments')\nend\n\nif ~isa(keys, 'struct')\n   error('keys must be a struct array') \nend\n\n% Do nothing if we have no keypoints\nif isempty(keys)\n   warning('keys is empty')\n   desc = [];\n   coords = [];\n   return\nend\n\ncoordsSize = size(keys(1).coords);\nif any(coordsSize ~= coordsSizeReq)\n    error(['keys.coords must have size [' num2str(coordsSizeReq) '].' ...\n        'Detected size: [' num2str(coordsSize) ']']);\nend\n\nif ~isa(keys(1).coords, 'double')\n    error('keys.coords must have type double');\nend\n\nif ~isscalar(keys(1).scale)\n   error('keys.scale must be a scalar'); \nend\n\nif ~isa(keys(1).scale, 'double')\n    error('keys.scale must have type double');\nend\n\noriSize = size(keys(1).ori);\nif any(oriSize ~= oriSizeReq)\n    error(['keys.ori must have size [' num2str(oriSizeReq) '].' ...\n        'Detected size: [' num2str(oriSize) ']']);\nend\n\nif ~isa(keys(1).ori, 'double')\n    error('keys.ori must have type double');\nend\n\nif nargin < 2\n    im = [];\nend\n\n% Convert to single precision\nim = single(im);\n\n% Verify and scale the input image, if any\nif (~isempty(im))\n    if (ndims(im) ~= 3)\n        error(['im must have 3 dimensions, detected ' num2str(ndims(im))]);\n    end\n    \n    im = im / (max(im(:)) + eps);\nend\n\nunits = checkUnits3D(units);\n\n% Detect features\nret = mexExtractSift3D(keys, im, units);\n\n% Splice the outputs\ncoords = ret(:, 1 : 3);\ndesc = ret(:, 4 : end);\n\nassert(size(desc, 2) == descNumel);\n\nend\n\n"
  },
  {
    "path": "wrappers/matlab/imRead3D.m",
    "content": "function [im, units] = imRead3D(path)\n%imRead3D(im) Read a 3D image from a file.\n%  Arguments:\n%    path - The path to the file.\n%\n%  Supported file formats:\n%    NIFTI (.nii, .nii.gz)\n%    Analyze (.img, .img.gz)   \n%    DICOM or DICOM Segmentation Object (.dcm)\n%    Directory of DICOM files (no extension)\n%\n%  Return values:\n%    im - An [MxNxPxC] array containing the image data. The last dimension \n%       denotes the channels of the [MxNxP] image. The voxels are indexed \n%       in (x, y, z, c) order, where c is the channel index and (x, y, z) \n%       are the spatial coordinates. NOTE: This is different from the order in\n%       which Matlab usually stores 3D images, where the x-axis is the second\n%       dimension. You must transpose each XY plane (axial slice) before \n%       viewing or manipulating it with Matlab's image processing functions.\n%    units - A [3x1] vector of the (x, y, z) real-world units for the\n%       voxels in im.\n%\n%  Examples:\n%      [im, units] = imRead3D('image.nii.gz'); % NIFTI\n%      [im, units] = imRead3D('image.dcm'); % Multi-slice DICOM or DSO\n%      [im, units] = imRead3D('image'); % Directory of DICOM slices\n%\n%  Notes:\n%   When possible, the image values will be in a vendor-neutral format. For \n%   example, a DICOM CT scan will be converted to Hounsfield units.\n%\n%   When reading a DICOM Segmentation Object (DSO), the program assumes\n%   that the DICOM images to which the DSO refers are located in the same\n%   directory as the DSO.\n%\n%  See also:\n%    imWrite3D, detectSift3D, extractSift3D, setupSift3D\n%\n% Copyright (c) 2015-2017 Blaine Rister et al., see LICENSE for details.\n\n% Verify inputs\nif nargin < 1 || isempty(path)\n    error('Not enough arguments')\nend\n\nif ~exist(path, 'file')\n    error('File does not exist')\nend\n\n% Read the image\n[im, units] = mexImRead3D(path);\n\nend\n\n"
  },
  {
    "path": "wrappers/matlab/imWrite3D.m",
    "content": "function imWrite3D(path, im, units)\n%imWrite3D(im) Write a 3D image to a file.\n%  Arguments:\n%    path - The path to the file.\n%    im - An [MxNxPxC] array containing the image data. See imRead3D.\n%    units - (Optional) See imRead3D. Missing values default to 1.\n%\n%  Supported file formats:\n%    NIFTI (.nii, .nii.gz)\n%    Analyze (.img, .img.gz)   \n%    DICOM (.dcm)\n%    Directory of DICOM files (no extension)\n%\n%  Examples:\n%    imWrite3D('image.nii.gz', im); % NIFTI\n%    imWrite3D('image.dcm', im); % Multi-slice DICOM\n%    imWrite3D('image', im); % Directory of DICOM slices\n%    imWrite3D('image.dcm', im, [1 1 2]) % Anisotropic, multi-slice DICOM  \n%\n%  Notes:\n%    When writing a DICOM file, the images values will be scaled and\n%    rounded to 8 unsigned bits.\n%\n%  See also:\n%    imRead3D, detectSift3D, extractSift3D, setupSift3D\n%\n% Copyright (c) 2015-2017 Blaine Rister et al., see LICENSE for details.\n\n% Default parameters\nif nargin < 3 || isempty(units)\n   units = ones(3, 1); \nend\n\n% Verify inputs\nif nargin < 1 || isempty(path)\n    error('path not specified')\nend\n\nif ~isa(path, 'char')\n    error('path must be a string')\nend\n\nif nargin < 2 || isempty(im)\n    error('im not specified')\nend\n\nif ndims(im) > 4\n   error(['im has invalid dimensionality: ' num2str(ndims(im))]) \nend\n\nunits = checkUnits3D(units);\n\n% Convert the image to single-precision\nim = single(im);\n\n% Check if this is a .gz file being written on Windows\nwinGz = false;\n[pathstr, name, ext] = fileparts(path);\nif strcmp(ext, '.gz') && ispc   \n    % Strip .gz from the path\n    winGz = true;\n    path = fullfile(pathstr, name);\nend\n\n% Write the image\nmexImWrite3D(path, im, units);\n\n% On Windows, compress the file from within Matlab. Otherwise, it will\n% crash.\nif winGz\n    gzip(path)\n    delete(path)\nend\n\nend\n"
  },
  {
    "path": "wrappers/matlab/keypoint3D.m",
    "content": "function keys = keypoint(coords, scale, ori)\n%keypoint3D(coords, scales, orientations) Create an array of Keypoint \n% structs.\n%\n% Inputs:\n%   coords - [Mx3] matrix of locations, where each row is an [x, y, z]\n%     coordinate triple, 0-indexed (required)\n%   scale - [Mx1] vector of nonnegative scale parameters (default: 1.6)\n%   ori - [3x3xM] array, where ori(:, :, i) is the [3x3] rotation matrix \n%   of the ith keypoint (default: identity matrix)\n%\n% Leaving an argument empty results in setting that value to the default.\n%\n% Return values:\n%   keys - an [Mx1] array of keypoint structs. Each struct has the following\n%      fields:\n%      key.coords - The [x y z] coordinates, 0-indexed.\n%      key.scale - The scale coordinate.\n%      key.ori - A [3x3] rotation matrix representing the 3D orientation.\n%      key.octave - The pyramid octave index.\n%      key.level - The pyramid level index within that octave.\n%\n% Example:\n%   coords = [1 1 1; 2 2 2];\n%   scale = [1 2];\n%   ori = repmat(eye(3), [1 1 2]);\n%   keys = keypoint3D(coords, scale, ori);\n%\n% See also:\n%   orientation3D, extractSift3D, setupSift3D\n%\n% Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n\n% Default parameters\nn = 3;\nscaleDefault = 1.6;\noriDefault = eye(n);\n\n% Error tolerance\ntol = 1E3 * eps;\n\n% Verify coords\nif nargin < 1 || isempty(coords)\n    error('Coords argument must be specified')\nelseif ~isa(coords, 'double')\n    error('Coords argument must be of type double')\nelseif ~isreal(coords)\n    error('Coords argument must be real-valued')\nend\n\nif size(coords, 2) ~= n || ~ismatrix(coords)\n   error(['coords argument has invalid dimensions [' ...\n        num2str(size(coords)) '] must be [mx3]'])\nend\nm = size(coords, 1);\n\n% Verify scale\nif nargin < 2 || isempty(scale)\n    scale = repmat(scaleDefault, [m 1]);\nelseif isequal(size(scale), [1 m])\n        scale = scale';\nelseif ~isequal(size(scale), [m 1])\n    error(['scale argument has invalid dimensions [' ...\n        num2str(size(scale)) '] must be [mx1]']);\nelseif ~isa(scale, 'double')\n    error('scale argument must be of type double')\nelseif ~isreal(scale)\n    error('scale argument must be real-valued')\nelseif any(scale <= 0)\n    error('scale argument must be positive')\nend\n\n% Verify orientation\nif nargin < 3 || isempty(ori)\n    ori = repmat(oriDefault, [1 1 m]);\nelseif ndims(ori) == 2 && ~isequal(size(ori), [n n]) || ...\n    ndims(ori) == 3 && ~isequal(size(ori), [n n m])\n    nStr = num2str(n);\n    error(['ori argument has invalid dimensions [' num2str(size(ori)) ...\n        '], must be [' nStr 'x' nStr 'xm]'])\nelseif ~isa(ori, 'double')\n    error('scale argument must be of type double')\nelseif ~isreal(scale)\n    error('scale argument must be real-valued')\nelse\n    % Verify rotation matrices\n    for i = 1 : m\n        \n        R = ori(:, :, i);\n        \n        % Verify determinant\n        d = det(R);\n        if (abs(d - 1) > tol)\n            error(['det(ori(:, :, ' num2str(i) ')) = ' num2str(d) ...\n              ', must be equal or near to 1'])\n        end\n        \n        % Verify orthogonality\n        RRt = R * R';\n        if (abs(RRt - eye(size(R))) > tol)\n            error(['ori(:, :, ' num2str(i) ')) must be orthogonal'])\n        end\n    end \nend\n\n% Create default octave and level arrays\noctave = 0;\nlevel = 0;\n\n% Create the struct\nkeys = struct('coords', num2cell(coords, 2), 'scale', ...\n    num2cell(scale, 2), 'ori', reshape(num2cell(ori, [1 2]), [m 1]), ...\n    'octave', num2cell(octave, 2), 'level', num2cell(level, 2));\n\nend\n"
  },
  {
    "path": "wrappers/matlab/matchSift3D.m",
    "content": "function matches = matchSift3D(desc1, coords1, desc2, coords2, nnThresh)\n%matchSift3D(desc1, desc2)\n% Match SIFT3D desriptors from a pair of images. \n%\n%  Arguments:\n%    desc1: Descriptors returned from extractSift3D, for the first image.\n%    coords1: Coordinates returned from extractSift3D, for the second \n%      image. (Default: zeros)\n%    desc2: Like desc1, but for the second image.\n%    coords2: Like coords1, but for the second image. (Default: zeros)\n%    nnThresh: The matching threshold, in the interval (0, 1]. A higher \n%      value means fewer matches will be returned. (Default: 0.8) \n%\n%  Return values:\n%    matches - An [m x 2] array, where matches(m, 1) indexes a row of\n%      desc1, and matches(m, 2) indexes a row of desc2. The descriptors\n%      given by these\n%\n%  If either of coords1, coords2 are empty, default values will be used.\n%\n%  Example:\n%    % Get descriptors from the first image\n%    [im1, units1] = imRead3D('someFile.dcm');\n%    keys1 = detectSift3D(im1, units1);\n%    [desc1, coords1] = extractSift3D(keys1);\n%    \n%    % Get descriptors from the second image\n%    [im2, units2] = imRead3D('someOtherFile.dcm');\n%    keys2 = detectSift3D(im2, units2);\n%    [desc2, coords2] = extractSift3D(keys2);   \n%\n%    % Match the descriptors\n%    matches = matchSift3D(desc1, coords1, desc2, coords2, 0.8);\n%\n%  See also:\n%    imRead3D, imWrite3D, registerSift3D, setupSift3D\n\n% Supply defaults\nif isempty(coords1)\n    coords1 = zeros(size(desc1, 1), 3);\nend\nif nargin < 4 || isempty(coords2)\n    coords2 = zeros(size(desc2, 1), 3);\nend\nif nargin < 5\n   nnThresh = []; \nend\n\n% Verify inputs\nnarginchk(3, 4)\nvalidateDesc('desc1', desc1, 'coords1', coords1)\nvalidateDesc('desc2', desc2, 'coords2', coords2)\nif ~isempty(nnThresh)\n    validateattributes(nnThresh, {'numeric'}, ...\n        {'real', 'scalar', '>=', 0, '<', 1}, 'nnThresh')\nend\n\n% Format the descriptors as a C-style matrix\ndesc1f = formatDesc(desc1, coords1);\ndesc2f = formatDesc(desc2, coords2);\n\n% Match the descriptors\nmatchIdx = mexMatchSift3D(desc1f, desc2f, nnThresh);\n\n% Convert the matches to indices in each array\nvalidMatches = matchIdx >= 0;\nmatches = [find(validMatches), matchIdx(validMatches) + 1];\n\nend\n\nfunction validateDesc(descName, desc, coordsName, coords)\n% Verify the descriptors and coordinates, throwing errors if invalid.\n\nvalidateattributes(desc, {'numeric'}, {'real', '2d', 'ncols', 768}, ...\n    descName)\nvalidateattributes(coords, {'numeric'}, {'real', '2d', 'ncols', 3}, ...\n    coordsName)\n\nif size(desc, 1) ~= size(coords, 1)\n    error([descName ' has size ' size(desc) ', but ' coordsName ' has ' ...\n        'size ' size(coords) '. Number of rows must be equal.'])\nend\n\nend\n\nfunction ret = formatDesc(desc, coords)\n% Combine the coordinates and descriptors and convert to double\n    ret = double([coords, desc]);\nend\n"
  },
  {
    "path": "wrappers/matlab/mexDetectSift3D.c",
    "content": "/* -----------------------------------------------------------------------------\n * mexDetectSift3D.c\n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * Mex interface to detect SIFT3D keypoints.\n * -----------------------------------------------------------------------------\n */\n\n#include \"imutil.h\"\n#include \"sift.h\"\n#include \"immacros.h\"\n#include \"mexutil.h\"\n#include \"mex.h\"\n\nvoid mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {\n\n        const mxArray *mxIm, *mxUnits, *mxOpts;\n        Image im;\n        Keypoint_store kp;\n        const char *errName, *errMsg;\n\n/* Clean up and print an error */\n#define CLEAN_AND_QUIT(name, msg, expected) { \\\n                im_free(&im); \\\n                cleanup_Keypoint_store(&kp); \\\n                if (expected) { \\\n                        err_msg(name, msg); \\\n                } else { \\\n                        err_msgu(name, msg); \\\n                } \\\n        }\n\n\t// Verify the number of inputs\n\tif (nrhs != 3)\n                err_msg(\"main:numInputs\", \"This function takes 3 inputs.\");\n\n        // Verify the number of outputs\n        if (nlhs > 1) \n                err_msg(\"main:numOutputs\", \"This function takes 1 output.\");\n\n        // Assign the inputs\n        mxIm = prhs[0];\n        mxUnits = prhs[1];\n        mxOpts = prhs[2];\n\n        // Set the options\n        if (mex_set_opts_SIFT3D(mxOpts))\n                CLEAN_AND_QUIT(\"main:setOpts\", \"Failed to set the options.\", \n                        SIFT3D_FALSE);\n\n        // Initialize intermediates\n        init_Keypoint_store(&kp);\n        init_im(&im);\n\n        // Copy the transposed image\n        if (mx2imWithUnits(mxIm, mxUnits, &im))\n                CLEAN_AND_QUIT(\"main:copyIm\", \"Failed to convert the input \"\n                        \"image\", SIFT3D_TRUE);\n\n        // Detect keypoints\n\tif (mex_SIFT3D_detect_keypoints(&im, &kp))\n                CLEAN_AND_QUIT(\"main:detect\", \"Failed to detect keypoints\", \n                        SIFT3D_TRUE);\n\n        // Convert the output to a MATLAB array of structs\n        if ((plhs[0] = kp2mx(&kp)) == NULL)\n                CLEAN_AND_QUIT(\"main:convertToStructs\", \"Failed to convert \"\n                        \"keypoints to structs\", SIFT3D_FALSE);\n\n        // Clean up\n        im_free(&im);\n        cleanup_Keypoint_store(&kp);\n\n#undef CLEAN_AND_QUIT\n}\n\n"
  },
  {
    "path": "wrappers/matlab/mexExtractSift3D.c",
    "content": "/* -----------------------------------------------------------------------------\n * mexExtractSift3D.c\n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * Mex interface to extract SIFT3D descriptors.\n * -----------------------------------------------------------------------------\n */\n\n#include \"imutil.h\"\n#include \"sift.h\"\n#include \"mexutil.h\"\n#include \"mex.h\"\n\n/* Entry point. \n * Output format:\n * [x y z el0 el1 ... el767] (see sift.c:SIFT3D_Descriptor_store_to_Mat_rm)\n *\n * Note that the matlab function does some postprocessing to present the data\n * in a different output format.\n */\nvoid mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {\n\n        const mxArray *mxKp, *mxIm, *mxUnits;\n        const char *errMsg;\n        Image im;\n        Keypoint_store kp;\n        SIFT3D_Descriptor_store desc;\n        int i;\n\n/* Clean up and print an error */\n#define CLEAN_AND_QUIT(name, msg, expected) { \\\n                im_free(&im); \\\n                cleanup_Keypoint_store(&kp); \\\n                cleanup_SIFT3D_Descriptor_store(&desc); \\\n                if (expected) { \\\n                        err_msg(name, msg); \\\n                } else { \\\n                        err_msgu(name, msg); \\\n                } \\\n        }\n\n\t// Verify the number of inputs\n\tif (nrhs != 3)\n                err_msgu(\"main:numInputs\", \"This function takes 3 inputs.\");\n\n        // Verify the number of outputs\n        if (nlhs > 1) \n                err_msgu(\"main:numOutputs\", \"This function takes 1 output.\");\n\n        // Assign inputs\n        mxKp = prhs[0];\n        mxIm = prhs[1];\n        mxUnits = prhs[2];\n\n        // Initialize intermediates\n        init_im(&im); \n        init_Keypoint_store(&kp);\n        init_SIFT3D_Descriptor_store(&desc);\n\n        // Convert the keypoints\n        if (mx2kp(mxKp, &kp))\n                CLEAN_AND_QUIT(\"main:convertKp\", \"Failed to convert keypoints\",\n                        SIFT3D_TRUE);\n\n        // Process the image and extract descriptors\n        if (!mxIsEmpty(mxIm)) {\n\n                // Convert the input to an Image struct\n                if (mx2imWithUnits(mxIm, mxUnits, &im))\n                        CLEAN_AND_QUIT(\"main:convertIm\", \n                                        \"Failed to convert image\", \n                                        SIFT3D_TRUE);\n\n                // Extract raw descriptors\n                if (mex_SIFT3D_extract_raw_descriptors(&im, &kp, &desc))\n                        CLEAN_AND_QUIT(\"main:extractRaw\", \n                                \"Failed to extract raw descriptors\",\n                                SIFT3D_TRUE);\n        } else {\n\n                // Attempt to retrieve the Gaussian pyramid\n                if (!mexHaveGpyr)\n                        CLEAN_AND_QUIT(\"main:haveGpyr\",\n                                \"Failed to get the Gaussian pyramid. Must \"\n                                \"call detectSift3D before this function can \"\n                                \"be called without the im argument.\", \n                                SIFT3D_TRUE);\n\n                // Extract descriptors from the pyramid\n                if (mex_SIFT3D_extract_descriptors(&kp, &desc))\n                        CLEAN_AND_QUIT(\"main:extractPyramid\", \n                                \"Failed to extract pyramid descriptors\",\n                                SIFT3D_FALSE);\n        }\n\n\n        // Convert the descriptors to an output matrix\n        if ((plhs[0] = desc2mx(&desc)) == NULL)\n                CLEAN_AND_QUIT(\"main:createOutput\", \n                        \"Failed to convert descriptors\", SIFT3D_FALSE);\n\n        // Clean up\n        im_free(&im);\n        cleanup_Keypoint_store(&kp);\n        cleanup_SIFT3D_Descriptor_store(&desc);\n\n#undef CLEAN_AND_QUIT\n}\n"
  },
  {
    "path": "wrappers/matlab/mexImRead3D.c",
    "content": "/* -----------------------------------------------------------------------------\n * mexImRead3D.c\n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2017 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * Mex interface to read 3D images.\n * -----------------------------------------------------------------------------\n */\n\n#include \"imutil.h\"\n#include \"mexutil.h\"\n#include \"mex.h\"\n#include \"matrix.h\"\n\nvoid mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {\n\n        Image im;\n        const mxArray *mxPath, *mxIm;\n        const char *path;\n\n/* Clean up and print an error */\n#define CLEAN_AND_QUIT(name, msg, expected) { \\\n                im_free(&im); \\\n                if (expected) { \\\n                        err_msg(name, msg); \\\n                } else { \\\n                        err_msgu(name, msg); \\\n                } \\\n        }\n\n\t// Verify the number of inputs\n\tif (nrhs != 1)\n                err_msgu(\"main:numInputs\", \"This function takes 1 input.\");\n\n        // Verify the number of outputs\n        if (nlhs > 2) \n                err_msgu(\"main:numOutputs\", \"This function takes 2 outputs.\");\n\n        // Assign the inputs\n        mxPath = prhs[0];\n\n        // Initialize intermediates\n        init_im(&im);\n\n        // Get the path string\n        if ((path = mxArrayToString(mxPath)) == NULL)\n                CLEAN_AND_QUIT(\"main:getPath\", \"Failed to convert the input \"\n                        \"to a string\", SIFT3D_FALSE);\n\n        // Load the image\n        switch (im_read(path, &im)) {\n                case SIFT3D_SUCCESS:\n                        break;\n                case SIFT3D_FILE_DOES_NOT_EXIST:\n                        CLEAN_AND_QUIT(\"main:dne\", \"File does not exist\", \n                                SIFT3D_TRUE);\n                case SIFT3D_UNSUPPORTED_FILE_TYPE:\n                        CLEAN_AND_QUIT(\"main:unsupportedType\", \"Unsupported file \"\n                                \"type\", SIFT3D_TRUE);\n                case SIFT3D_WRAPPER_NOT_COMPILED:\n                        CLEAN_AND_QUIT(\"main:notCompiled\", \"Recompile SIFT3D \"\n                                \"with support for this file type\", SIFT3D_TRUE);\n                case SIFT3D_UNEVEN_SPACING:\n                        CLEAN_AND_QUIT(\"main:unevenSpacing\", \"The series has \"\n                        \"uneven slice spacing\", SIFT3D_TRUE);\n                case SIFT3D_INCONSISTENT_AXES:\n                        CLEAN_AND_QUIT(\"main:inconsistentAxes\", \"The series \"\n                        \"has inconsistent slice axes\", SIFT3D_TRUE);\n                case SIFT3D_DUPLICATE_SLICES:\n                        CLEAN_AND_QUIT(\"main:duplicateSlices\", \"The series \"\n                        \"contains slices in duplicate locations\", SIFT3D_TRUE);\n                default:\n                        CLEAN_AND_QUIT(\"main:unexpected\", \"Unexpected error \"\n                                \"reading the image\", SIFT3D_TRUE);\n        }\n\n        // Convert the output image to a MATLAB array\n        if ((plhs[0] = im2mx(&im)) == NULL)\n                CLEAN_AND_QUIT(\"main:im2mx\", \"Failed to convert image to an \"\n                        \"mxArray\", SIFT3D_FALSE);\n\n        // Convert the output units to a MATLAB array\n        if ((plhs[1] = units2mx(&im)) == NULL)\n                CLEAN_AND_QUIT(\"main:im2mx\", \"Failed to convert units to an \"\n                        \"mxArray\", SIFT3D_FALSE);\n\n        // Clean up\n        im_free(&im);\n\n#undef CLEAN_AND_QUIT\n}\n\n"
  },
  {
    "path": "wrappers/matlab/mexImWrite3D.c",
    "content": "/* -----------------------------------------------------------------------------\n * mexImWrite3D.c\n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * Mex interface to write 3D images.\n * -----------------------------------------------------------------------------\n */\n\n#include \"imutil.h\"\n#include \"mexutil.h\"\n#include \"mex.h\"\n#include \"matrix.h\"\n\nvoid mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {\n\n        Image im;\n        const mxArray *mxPath, *mxIm, *mxUnits;\n        const char *path;\n\n/* Clean up and print an error */\n#define CLEAN_AND_QUIT(name, msg, expected) { \\\n                im_free(&im); \\\n                if (expected) { \\\n                        err_msg(name, msg); \\\n                } else { \\\n                        err_msgu(name, msg); \\\n                } \\\n        }\n\n\t// Verify the number of inputs\n\tif (nrhs != 3)\n                err_msgu(\"main:numInputs\", \"This function takes 3 inputs.\");\n\n        // Verify the number of outputs\n        if (nlhs != 0) \n                err_msgu(\"main:numOutputs\", \"This function takes no outputs.\");\n\n        // Assign the inputs\n        mxPath = prhs[0];\n        mxIm = prhs[1];\n        mxUnits = prhs[2];\n\n        // Initialize intermediates\n        init_im(&im);\n\n        // Get the path string\n        if ((path = mxArrayToString(mxPath)) == NULL)\n                CLEAN_AND_QUIT(\"main:getPath\", \"Failed to convert the input \"\n                        \"to a string\", SIFT3D_FALSE);\n\n        // Convert the image to the internal format\n        if (mx2imWithUnits(mxIm, mxUnits, &im))\n                CLEAN_AND_QUIT(\"main:mx2im\", \"Failed to convert the input \"\n                        \"image to the internal format\", SIFT3D_TRUE);\n\n        // Write the image\n        switch (im_write(path, &im)) {\n                case SIFT3D_SUCCESS:\n                        break;\n                case SIFT3D_UNSUPPORTED_FILE_TYPE:\n                        CLEAN_AND_QUIT(\"main:unsupportedType\", \"Unsupported file \"\n                                \"type\", SIFT3D_TRUE);\n                case SIFT3D_WRAPPER_NOT_COMPILED:\n                        CLEAN_AND_QUIT(\"main:notCompiled\", \"Recompile SIFT3D \"\n                                \"with support for this file type\", SIFT3D_TRUE);\n                default:\n                        CLEAN_AND_QUIT(\"main:unexpected\", \"Unexpected error \"\n                                \"writing the image\", SIFT3D_TRUE);\n        }\n\n        // Clean up\n        im_free(&im);\n\n#undef CLEAN_AND_QUIT\n}\n\n"
  },
  {
    "path": "wrappers/matlab/mexMatchSift3D.c",
    "content": "/* -----------------------------------------------------------------------------\n * mexMatchSift3D.c\n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2017 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * Mex interface to register images from SIFT3D keypoints and descriptors.\n * -----------------------------------------------------------------------------\n */\n\n#include <stdlib.h>\n#include \"imutil.h\"\n#include \"immacros.h\"\n#include \"sift.h\"\n#include \"reg.h\"\n#include \"mexutil.h\"\n#include \"mex.h\"\n#include \"matrix.h\"\n\nvoid mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {\n\n        const mxArray *mxDesc1, *mxDesc2, *mxNnThresh;\n        const mwSize *mxDims;\n        mxArray *mxMatches;\n        double *mxMatchesData;\n        int *matches;\n        SIFT3D_Descriptor_store desc1, desc2;\n        double nn_thresh;\n        int i;\n\n/* Clean up and print an error */\n#define CLEAN_AND_QUIT(name, msg, expected) { \\\n                cleanup_SIFT3D_Descriptor_store(&desc1); \\\n                cleanup_SIFT3D_Descriptor_store(&desc2); \\\n                if (matches != NULL) { \\\n                        free(matches);  \\\n                } \\\n                if (expected) { \\\n                        err_msg(name, msg); \\\n                } else { \\\n                        err_msgu(name, msg); \\\n                } \\\n        }\n\n\t// Verify the number of inputs\n\tif (nrhs != 3)\n                err_msg(\"main:numInputs\", \"This function takes 3 inputs.\");\n\n        // Verify the number of outputs\n        if (nlhs > 1)\n                err_msg(\"main:numOutputs\", \"This function takes 1 output.\");\n\n        // Assign the inputs\n        mxDesc1 = prhs[0];\n        mxDesc2 = prhs[1];\n        mxNnThresh = prhs[2];\n\n        // Get the matching threshold, if one was provided\n        nn_thresh = mxIsEmpty(mxNnThresh) ? SIFT3D_nn_thresh_default :\n                mxGetScalar(mxNnThresh);\n\n        // Initialize intermediates\n        matches = NULL;\n\tinit_SIFT3D_Descriptor_store(&desc1);\n\tinit_SIFT3D_Descriptor_store(&desc2);\n\n        // Convert the inputs to descriptor stores\n        if (mx2desc(mxDesc1, &desc1))\n                CLEAN_AND_QUIT(\"main:convert1\", \"Failed to convert desc1\",\n                        SIFT3D_TRUE);\n        if (mx2desc(mxDesc2, &desc2))\n                CLEAN_AND_QUIT(\"main:convert2\", \"Failed to convert desc2\",\n                        SIFT3D_TRUE);\n\n        // Match descriptors\n\tif (SIFT3D_nn_match(&desc1, &desc2, nn_thresh, &matches))\n                CLEAN_AND_QUIT(\"main:match\", \"Failed to match descriptors\",\n                        SIFT3D_FALSE);\n\n        // Create a matrix for the output\n        if ((mxMatches = mxCreateDoubleMatrix(desc1.num, 1, mxREAL)) == NULL)\n                CLEAN_AND_QUIT(\"main:createOutput\", \"Failed to create output\",\n                        SIFT3D_TRUE);\n        plhs[0] = mxMatches;\n\n        // Get the data\n        if ((mxMatchesData = mxGetData(mxMatches)) == NULL)\n                CLEAN_AND_QUIT(\"main:convertOut\", \"Failed to get output data\",\n                        SIFT3D_FALSE);\n\n        // Copy the output\n        for (i = 0; i < desc1.num; i++) {\n                mxMatchesData[i] = (double) matches[i];\n        }\n\n        // Clean up\n        cleanup_SIFT3D_Descriptor_store(&desc1);\n        cleanup_SIFT3D_Descriptor_store(&desc2);\n        free(matches);\n}\n"
  },
  {
    "path": "wrappers/matlab/mexOrientation3D.c",
    "content": "/* -----------------------------------------------------------------------------\n * mexOrientation3D.c\n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * Mex interface to assign 3D orientation.\n * -----------------------------------------------------------------------------\n */\n\n#include \"imutil.h\"\n#include \"sift.h\"\n#include \"immacros.h\"\n#include \"mexutil.h\"\n#include \"mex.h\"\n\nvoid mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {\n\n        const mxArray *mxKp, *mxIm, *mxUnits;\n        Image im;\n        Keypoint_store kp;\n        const char *errName, *errMsg;\n        double *conf;\n\n/* Clean up and print an error */\n#define CLEAN_AND_QUIT(name, msg, expected) { \\\n                im_free(&im); \\\n                cleanup_Keypoint_store(&kp); \\\n                if (expected) { \\\n                        err_msg(name, msg); \\\n                } else { \\\n                        err_msgu(name, msg); \\\n                } \\\n        }\n\n\t// Verify the number of inputs\n\tif (nrhs != 3)\n                err_msg(\"main:numInputs\", \"This function takes 3 inputs.\");\n\n        // Verify the number of outputs\n        if (nlhs > 2) \n                err_msg(\"main:numOutputs\", \"This function takes 2 outputs.\");\n\n        // Assign the inputs\n        mxKp = prhs[0];\n        mxIm = prhs[1];\n        mxUnits = prhs[2];\n\n        // Initialize intermediates\n        init_im(&im);\n        init_Keypoint_store(&kp);\n\n        // Convert the keypoints \n        if (mx2kp(mxKp, &kp))\n                CLEAN_AND_QUIT(\"main:convertKey\", \"failed to convert the \"\n                        \"input keypoints\", SIFT3D_TRUE);\n\n        // Convert the image\n        if (mx2imWithUnits(mxIm, mxUnits, &im))\n                CLEAN_AND_QUIT(\"main:copyIm\", \"Failed to convert the input \"\n                        \"image\", SIFT3D_TRUE);\n\n        // Assign the orientations\n        conf = NULL;\n        if (mex_SIFT3D_assign_orientations(&im, &kp, &conf))\n                CLEAN_AND_QUIT(\"main:assignOrientations\", \"Failed to assign \"\n                        \"the orientations\", SIFT3D_TRUE);\n\n        // Convert the output keypoints\n        if ((plhs[0] = kp2mx(&kp)) == NULL)\n                CLEAN_AND_QUIT(\"main:convertR\", \"Failed to convert R\", \n                        SIFT3D_FALSE);\n\n        // Convert the output confidence to a MATLAB array\n        if ((plhs[1] = array2mx(conf, kp.slab.num)) == NULL)\n                CLEAN_AND_QUIT(\"main:convertConf\", \"Failed to convert conf\",\n                        SIFT3D_FALSE);\n\n        // Clean up\n        im_free(&im);\n        cleanup_Keypoint_store(&kp);\n\n#undef CLEAN_AND_QUIT\n}\n\n"
  },
  {
    "path": "wrappers/matlab/mexRegisterSift3D.c",
    "content": "/* -----------------------------------------------------------------------------\n * mexRegisterSift3D.c\n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * Mex interface to register images from SIFT3D keypoints and descriptors.\n * -----------------------------------------------------------------------------\n */\n\n#include \"imutil.h\"\n#include \"reg.h\"\n#include \"immacros.h\"\n#include \"mexutil.h\"\n#include \"mex.h\"\n#include \"matrix.h\"\n\nvoid mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {\n\n        const mxArray *mxSrc, *mxRef, *mxSrcUnits, *mxRefUnits, *mxResample,\n                *mxRegOpts, *mxSIFT3DOpts;\n        Image src, ref;\n        Affine aff;\n        Mat_rm match_src, match_ref;\n        int ret;\n\n/* Clean up and print an error */\n#define CLEAN_AND_QUIT(name, msg, expected) { \\\n                im_free(&src); \\\n                im_free(&ref); \\\n                cleanup_tform(&aff); \\\n                cleanup_Mat_rm(&match_src); \\\n                cleanup_Mat_rm(&match_ref); \\\n                if (expected) { \\\n                        err_msg(name, msg); \\\n                } else { \\\n                        err_msgu(name, msg); \\\n                } \\\n        }\n\n\t// Verify the number of inputs\n\tif (nrhs != 7)\n                err_msg(\"main:numInputs\", \"This function takes 7 inputs.\");\n\n        // Verify the number of outputs\n        if (nlhs > 3)\n                err_msg(\"main:numOutputs\", \"This function takes 3 outputs.\");\n\n        // Assign the inputs\n        mxSrc = prhs[0];\n        mxRef = prhs[1];\n        mxSrcUnits = prhs[2];\n        mxRefUnits = prhs[3];\n        mxResample = prhs[4];\n        mxRegOpts = prhs[5];\n        mxSIFT3DOpts = prhs[6];\n\n        // Verify the resampling option\n        if (!mxIsLogicalScalar(mxResample))\n                err_msg(\"main:resample\", \"Argument 'resample' must be a \"\n                                         \"logical scalar.\");\n\n        // Initialize intermediates\n        if (init_Affine(&aff, IM_NDIMS) ||\n                init_Mat_rm(&match_src, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE) ||\n                init_Mat_rm(&match_ref, 0, 0, SIFT3D_DOUBLE, SIFT3D_FALSE))\n                err_msgu(\"main:init\", \"Failed to initialize intermediates\");\n        init_im(&src);\n        init_im(&ref);\n\n        // Convert the inputs to images\n        if (mx2imWithUnits(mxSrc, mxSrcUnits, &src))\n                CLEAN_AND_QUIT(\"main:convertSrc\", \"Failed to convert the \"\n                        \"source image.\", SIFT3D_TRUE);\n        if (mx2imWithUnits(mxRef, mxRefUnits, &ref))\n                CLEAN_AND_QUIT(\"main:convertRef\", \"Failed to convert the \"\n                        \"reference image.\", SIFT3D_TRUE);\n\n        // Set the options\n        if (mex_set_opts_Reg_SIFT3D(mxRegOpts))\n                CLEAN_AND_QUIT(\"main:setOpts\", \"Failed to set the registration \"\n                                \"options.\", SIFT3D_FALSE);\n        if (mex_set_opts_SIFT3D(mxSIFT3DOpts))\n                CLEAN_AND_QUIT(\"main:setOpts\", \"Failed to set the SIFT3D \"\n                                \"options.\", SIFT3D_FALSE);\n\n        // Register the images\n        if (mxIsLogicalScalarTrue(mxResample)) {\n                ret = mex_register_SIFT3D_resample(&src, &ref, LINEAR, &aff);\n        } else {\n                ret = mex_set_src_Reg_SIFT3D(&src) ||\n                        mex_set_ref_Reg_SIFT3D(&ref) ||\n                        mex_register_SIFT3D(&aff) ?\n                        SIFT3D_FAILURE : SIFT3D_SUCCESS;\n        }\n\n        // Handle registration errors\n        if (ret) {\n                CLEAN_AND_QUIT(\"main:reg\", \"Failed to register the images. \"\n                        \"Possibly no good transformation was found.\", \n                        SIFT3D_TRUE);\n        }\n\n        // Retrieve the matches\n        if (mex_get_matches_Reg_SIFT3D(&match_src, &match_ref))\n                CLEAN_AND_QUIT(\"main:getMatches\", \"Failed to retrieve the \"\n                        \"matches.\", SIFT3D_FALSE);\n\n        // Convert the outputs to mxArrays\n        if ((plhs[0] = mat2mx(&aff.A)) == NULL ||\n                (plhs[1] = mat2mx(&match_src)) == NULL ||\n                (plhs[2] = mat2mx(&match_ref)) == NULL)\n                CLEAN_AND_QUIT(\"main:convertA\", \"Failed to convert \"\n                        \"outputs.\", SIFT3D_FALSE);\n\n        // Clean up\n        im_free(&src);\n        im_free(&ref); \n        cleanup_tform(&aff); \n        cleanup_Mat_rm(&match_src); \n        cleanup_Mat_rm(&match_ref); \n\n#undef CLEAN_AND_QUIT        \n}\n\n"
  },
  {
    "path": "wrappers/matlab/mexutil.c",
    "content": "/* -----------------------------------------------------------------------------\n * mexutil.c\n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * Mex utility library for SIFT3D.\n * -----------------------------------------------------------------------------\n */\n\n/* Standard headers */\n#include <stddef.h>\n#include <math.h>\n\n/* SIFT3D headers */\n#include \"imtypes.h\"\n#include \"immacros.h\"\n#include \"imutil.h\"\n#include \"sift.h\"\n#include \"reg.h\"\n#include \"mexutil.h\"\n\n/* Matlab headers */\n#include <uchar.h>\n#include \"mex.h\"\n#include \"matrix.h\"\n\n/* The number of dimensions in mxArrays representing images */\n#define MX_IM_NDIMS (IM_NDIMS + 1) \n\n/* Keypoint struct information */\n#define COORDS_NAME \"coords\"\n#define SCALE_NAME \"scale\"\n#define ORI_NAME \"ori\"\n#define OCTAVE_NAME \"octave\"\n#define LEVEL_NAME \"level\"\nconst char *fieldNames[] = {\n        COORDS_NAME,\n        SCALE_NAME,\n        ORI_NAME,\n        OCTAVE_NAME,\n        LEVEL_NAME \n};\nconst mwSize kpNDims = 1;\nconst int kpNFields = sizeof(fieldNames) / sizeof(char *);\n\n/* Global state */\nReg_SIFT3D reg;\n\n/* Error message tag */\nconst char *tag = \"sift3D\";\n\n/* Static helper functions */\nstatic void init(void) __attribute__((constructor));\nstatic void fini(void) __attribute__((destructor));\n\n/* Library initialization */\nstatic void init(void) {\n        if (init_Reg_SIFT3D(&reg))\n                err_msgu(\"main:initSift\", \"Failed to initialize SIFT3D\");\n}\n\n/* Library cleanup */\nstatic void fini(void) {\n        cleanup_Reg_SIFT3D(&reg);\n}\n\n/* Print an error message. */\nvoid err_msg(const char *name, const char *msg) {\n\n        char id[1024];\n\n        sprintf(id, \"%s:%s\", tag, name);\n\n        mexErrMsgIdAndTxt(id, msg);\n}\n\n/* Print an unexpected error. */\nvoid err_msgu(const char *name, const char *msg) {\n        err_msg(name, msg);\n        print_bug_msg();\n}\n\n/* Returns SIFT3D_TRUE if the input is a real-valued double-precision floating\n * point array, aka \"double\" in C. */\nint isDouble(const mxArray *const mx) {\n        return mxIsDouble(mx) && !mxIsComplex(mx);\n}\n\n/* Returns the index in an mxArray of the voxel at the coordinates (x, y, z)\n * and channel c */\nmwIndex mxImGetIdx(const mxArray *const mx, const int x, const int y, \n        const int z, const int c) {\n\n        const mwIndex subs[] = {x, y, z, c};\n        const mwSize nSubs = sizeof(subs) / sizeof(mwIndex);\n        assert(nSubs == MX_IM_NDIMS);\n\n        return mxCalcSingleSubscript(mx, nSubs, subs);\n}\n\n/* Convert an Image to an mxArray. The output will have IM_NDIMS + 1 dimensions\n * and type double. The final dimension denotes the image channels. \n *\n * Returns a pointer to the array, or NULL if an error has occurred. */\nmxArray *im2mx(const Image *const im) {\n    \n        mwSize dims[MX_IM_NDIMS]; \n        mxArray *mx; \n        double *mxData; \n        int i, x, y, z, c;\n\n        // Initialize the dimensions\n        for (i = 0; i < IM_NDIMS; i++) {\n                dims[i] = SIFT3D_IM_GET_DIMS(im)[i];\n        }\n        dims[IM_NDIMS] = im->nc;\n\n        // Create an array\n        if ((mx = mxCreateNumericArray(MX_IM_NDIMS, dims, mxDOUBLE_CLASS, \n                mxREAL)) == NULL)\n                return NULL;\n\n        // Get the data\n        if ((mxData = mxGetData(mx)) == NULL)\n                return NULL;\n\n        // Transpose and copy the data\n        SIFT3D_IM_LOOP_START_C(im, x, y, z, c)\n\n                const mwIndex idx = mxImGetIdx(mx, x, y, z, c); \n                mxData[idx] = (double) SIFT3D_IM_GET_VOX(im, x, y, z, c);\n\n        SIFT3D_IM_LOOP_END_C\n\n        return mx;\n}\n\n/* Convert an mxArray to an Image. mx must have at most IM_NDIMS + 1 \n * dimensions and be of type single (float). */\nint mx2im(const mxArray *const mx, Image *const im) {\n\n        const mwSize *mxDims;\n        float *mxData;\n        mwIndex mxNDims;\n        int i, x, y, z, c, mxNSpaceDims;\n\n        // Verify inputs\n        mxNDims = (int) mxGetNumberOfDimensions(mx);\n\tif (mxNDims > MX_IM_NDIMS || !mxIsSingle(mx) || mxIsComplex(mx))\n                return SIFT3D_FAILURE;\n\n        // Copy the spatial dimensions\n        mxNSpaceDims = SIFT3D_MIN((int) mxNDims, MX_IM_NDIMS - 1);\n        mxDims = mxGetDimensions(mx);\n        for (i = 0; i < mxNSpaceDims; i++) {\n                SIFT3D_IM_GET_DIMS(im)[i] = (int) mxDims[i];\n        }\n\n        // Pad the unfilled dimensions with 1\n        for (i = mxNSpaceDims; i < MX_IM_NDIMS - 1; i++) {\n                SIFT3D_IM_GET_DIMS(im)[i] = 1; \n        } \n\n        // Copy the number of channels, defaulting to 1\n        im->nc = mxNDims == MX_IM_NDIMS ? (int) mxDims[MX_IM_NDIMS - 1] : 1;\n\n        // Resize the output                \n        im_default_stride(im);\n        if (im_resize(im))\n                return SIFT3D_FAILURE;\n\n        // Get the data\n        if ((mxData = mxGetData(mx)) == NULL)\n                return SIFT3D_FAILURE;\n\n        // Transpose and copy the data\n        SIFT3D_IM_LOOP_START_C(im, x, y, z, c)\n\n                mwIndex idx;\n\n                idx = mxImGetIdx(mx, x, y, z, c);\n\n                SIFT3D_IM_GET_VOX(im, x, y, z, c) = mxData[idx];\n\n        SIFT3D_IM_LOOP_END_C\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Returns an mxArray representing the units of image im.\n *\n * Parameters:\n * Return: the array, or NULL on failure. */\nmxArray *units2mx(const Image *const im) {\n\n        mxArray *mx;\n        double *mxData;\n        int i;\n\n        // Create an array\n        if ((mx = mxCreateDoubleMatrix(IM_NDIMS, 1, mxREAL)) == NULL)\n                return NULL;\n\n        // Get the data\n        if ((mxData = mxGetData(mx)) == NULL)\n                goto units2mx_quit;\n\n        // Copy the data from im\n        for (i = 0; i < IM_NDIMS; i++) {\n\n                mwIndex idx;\n\n                const mwIndex subs[] = {i, 0};\n                const mwIndex nSubs = sizeof(subs) / sizeof(mwIndex);\n\n                idx = mxCalcSingleSubscript(mx, nSubs, subs);\n                mxData[idx] = SIFT3D_IM_GET_UNITS(im)[i];\n        }\n\n        return mx;\n\nunits2mx_quit:\n        mxDestroyArray(mx);\n        return NULL;\n}\n\n/* Set the units of an image to the data stored in an mxArray,\n *\n * Parameters:\n *  -mx: An array of IM_NDIMS dimensions, type double.\n *  -im: The Image struct to which the units are written.\n *\n * Return: SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise. */\nint mx2units(const mxArray *const mx, Image *const im) {\n\n        const mwSize *mxDims;\n        double *mxData;\n        mwIndex mxNDims;\n        int i;\n\n        // Verify inputs\n        mxNDims = (int) mxGetNumberOfDimensions(mx);\n\tif (mxNDims != 2 || !isDouble(mx))\n                return SIFT3D_FAILURE;\n\n        // Verify the dimensions\n        mxDims = mxGetDimensions(mx);\n        if (mxDims[0] != IM_NDIMS || mxDims[1] != 1)\n                return SIFT3D_FAILURE; \n\n        // Get the data\n        if ((mxData = mxGetData(mx)) == NULL)\n                return SIFT3D_FAILURE;\n\n        // Copy the data to im\n        for (i = 0; i < IM_NDIMS; i++) {\n\n                mwIndex idx;\n\n                const mwIndex subs[] = {i, 0};\n                const mwIndex nSubs = sizeof(subs) / sizeof(mwIndex);\n\n                idx = mxCalcSingleSubscript(mx, nSubs, subs);\n                SIFT3D_IM_GET_UNITS(im)[i] = mxData[idx];\n        }\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Wrapper around mx2im and mx2units. */\nint mx2imWithUnits(const mxArray *const data, const mxArray *const units,\n        Image *const im) {\n        return mx2im(data, im) || mx2units(units, im);\n}\n\n/* Convert a Mat_rm struct to an mxArray. Returns the array, or NULL on\n * failure. The returned array has type double, regardless of the input array\n * type. \n *\n * Returns the array, or NULL on failure. */\nmxArray *mat2mx(const Mat_rm *const mat) {\n\n        mxArray *mx;\n        double *mxData;\n        int i, j;\n\n        const int rows = mat->num_rows;\n        const int cols = mat->num_cols;\n\n        // Create an array\n        if ((mx = mxCreateDoubleMatrix(rows, cols, mxREAL)) == NULL)\n                return NULL;\n\n        // Get the data\n        if ((mxData = mxGetData(mx)) == NULL)\n                goto mat2mx_quit;\n\n#define TRANSPOSE_AND_COPY(type) \\\n        SIFT3D_MAT_RM_LOOP_START(mat, i, j) \\\n        \\\n                mwIndex idx; \\\n        \\\n                const mwIndex subs[] = {i, j}; \\\n        \\\n                idx = mxCalcSingleSubscript(mx, 2, subs); \\\n                mxData[idx] = (double) SIFT3D_MAT_RM_GET(mat, i, j, type); \\\n        \\\n        SIFT3D_MAT_RM_LOOP_END\n\n        // Transpose and copy the data \n        SIFT3D_MAT_RM_TYPE_MACRO(mat, mat2mx_quit, TRANSPOSE_AND_COPY);\n\n#undef TRANSPOSE_AND_COPY\n\n        return mx;\n\nmat2mx_quit:\n        mxDestroyArray(mx);\n        return NULL;\n}\n\n/* Convert an mxArray to a Mat_rm. mx must have type double. The type of mat\n * is preserved.\n * \n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise. */ \nint mx2mat(const mxArray *const mx, Mat_rm *const mat) {\n\n        const mwSize *mxDims;\n        double *data;\n        int i, j;\n\n        // Verify inputs\n        if (!isDouble(mx)) {\n                SIFT3D_ERR(\"mx2mat: mx must have type double\");\n                return SIFT3D_FAILURE;\n        }\n\n        if (mxGetNumberOfDimensions(mx) != 2) {\n                SIFT3D_ERR(\"mx2mat: mx must be a matrix\");\n                return SIFT3D_FAILURE;\n        }\n\n        // Resize the output\n        mxDims = mxGetDimensions(mx);\n        mat->num_rows = (int) mxDims[0];\n        mat->num_cols = (int) mxDims[1];\n        if (resize_Mat_rm(mat))\n                return SIFT3D_FAILURE;\n\n        // Get the data\n        if ((data = mxGetData(mx)) == NULL)\n                return SIFT3D_FAILURE;\n\n#define COPY_DATA(type) \\\n        SIFT3D_MAT_RM_LOOP_START(mat, i, j) \\\n\\\n                mwIndex idx; \\\n\\\n                const mwIndex subs[] = {i, j}; \\\n\\\n                idx = mxCalcSingleSubscript(mx, 2, subs); \\\n                SIFT3D_MAT_RM_GET(mat, i, j, type) = (type) data[idx]; \\\n\\\n        SIFT3D_MAT_RM_LOOP_END\n\n        // Copy the transposed data\n        SIFT3D_MAT_RM_TYPE_MACRO(mat, mx2mat_quit, COPY_DATA);\n\n#undef COPY_DATA\n\n        return SIFT3D_SUCCESS;\n\nmx2mat_quit:\n        return SIFT3D_FAILURE;\n}\n\n/* Convert from a Keypoint_store to an array of MATLAB keypoint structs. \n * Returns the array of keypoints, or NULL on failure. */\nmxArray *kp2mx(const Keypoint_store *const kp) {\n\n        mxArray *mxKp;\n        int i, coordsNum, scaleNum, oriNum, octaveNum, levelNum;\n\n        const mwSize numKp = (mwSize) kp->slab.num;\n\n\t// Make an array of structs for the output \n\tmxKp = mxCreateStructArray(kpNDims, &numKp, kpNFields, \n                                            fieldNames);\n        if (mxKp == NULL)\n                return NULL;\n\n        // Get the field indices in the structs\n        if ((coordsNum = mxGetFieldNumber(mxKp, COORDS_NAME)) < 0 ||\n                (scaleNum = mxGetFieldNumber(mxKp, SCALE_NAME)) < 0 ||\n                (oriNum = mxGetFieldNumber(mxKp, ORI_NAME)) < 0 ||\n                (octaveNum = mxGetFieldNumber(mxKp, OCTAVE_NAME)) < 0 || \n                (levelNum = mxGetFieldNumber(mxKp, LEVEL_NAME)) < 0)\n                return NULL;\n\n        // Write the keypoints to the output\n        for (i = 0; i < kp->slab.num; i++) {\n\n                mxArray *mxCoords, *mxScale, *mxOri, *mxOctave, *mxLevel;\n                double *coords;\n\n                const Keypoint *const key = kp->buf + i;\n\n                // Initialize the coordinate array\n                if ((mxCoords = \n                        mxCreateDoubleMatrix(1, IM_NDIMS, mxREAL)) == NULL)\n                        return NULL;\n\n                // Copy the coordinates \n                coords = mxGetData(mxCoords); \n                coords[0] = key->xd;\n                coords[1] = key->yd;\n                coords[2] = key->zd;\n\n                // Copy the transposed orientation\n                if ((mxOri = mat2mx(&key->R)) == NULL)\n                        return NULL;\n\n                // Copy the scale \n                mxScale = mxCreateDoubleScalar(key->sd);\n\n                // Copy the octave index\n                mxOctave = mxCreateDoubleScalar((double) key->o); \n\n                // Copy the level index\n                mxLevel = mxCreateDoubleScalar((double) key->s);\n                \n                // Set the struct fields\n                mxSetFieldByNumber(mxKp, i, coordsNum, mxCoords);\n                mxSetFieldByNumber(mxKp, i, scaleNum, mxScale);\n                mxSetFieldByNumber(mxKp, i, oriNum, mxOri);\n                mxSetFieldByNumber(mxKp, i, octaveNum, mxOctave);\n                mxSetFieldByNumber(mxKp, i, levelNum, mxLevel);\n        }\n\n        return mxKp;\n}\n\n/* Convert an array of MATLAB keypoint structs to a Keypoint_store. mx must be\n * a vector, and each struct element must have type double. */\nint mx2kp(const mxArray *const mx, Keypoint_store *const kp) {\n\n        const mwSize *mxDims, *coordsDims, *oriDims;\n        mxArray *mxCoords, *mxScale, *mxOri, *mxOctave, *mxLevel;\n        mwSize numKp, kpRows, kpCols;\n        int i, coordsNum, scaleNum, oriNum, octaveNum, levelNum;\n\n        // Verify the number of dimensions\n        if (mxGetNumberOfDimensions(mx) != 2)\n                return SIFT3D_FAILURE;\n\n        // Parse the input dimensions\n        mxDims = mxGetDimensions(mx);\n        kpRows = mxDims[0];\n        kpCols = mxDims[1];\n        if (kpRows == 1)\n                numKp = kpCols; \n        else if (kpCols == 1)\n                numKp = kpRows;\n        else\n                return SIFT3D_FAILURE; \n\n        // Verify the struct size \n        if (!mxIsStruct(mx) || mxGetNumberOfFields(mx) != kpNFields)\n                return SIFT3D_FAILURE;\n\n        // Get the field indices\n        if ((coordsNum = mxGetFieldNumber(mx, COORDS_NAME)) < 0 ||\n                (scaleNum = mxGetFieldNumber(mx, SCALE_NAME)) < 0 ||\n                (oriNum = mxGetFieldNumber(mx, ORI_NAME)) < 0 ||\n                (octaveNum = mxGetFieldNumber(mx, OCTAVE_NAME)) < 0 || \n                (levelNum = mxGetFieldNumber(mx, LEVEL_NAME)) < 0)\n                return SIFT3D_FAILURE;\n\n        // Get the fields of the first keypoint\n        if ((mxCoords = mxGetFieldByNumber(mx, 0, coordsNum)) == NULL ||\n                (mxScale = mxGetFieldByNumber(mx, 0, scaleNum)) == NULL ||\n                (mxOri = mxGetFieldByNumber(mx, 0, oriNum)) == NULL ||\n                (mxOctave = mxGetFieldByNumber(mx, 0, octaveNum)) == NULL ||\n                (mxLevel = mxGetFieldByNumber(mx, 0, levelNum)) == NULL)\n                return SIFT3D_FAILURE;\n\n        // Verify the type\n        if (!isDouble(mxCoords) ||\n                !isDouble(mxScale) ||\n                !isDouble(mxOri) ||\n                !isDouble(mxOctave) ||\n                !isDouble(mxLevel))\n                return SIFT3D_FAILURE;\n\n        // Verify the number of dimensions\n        if (mxGetNumberOfDimensions(mxCoords) != 2 ||\n                mxGetNumberOfDimensions(mxScale) != 2 ||\n                mxGetNumberOfDimensions(mxOri) != 2 ||\n                mxGetNumberOfDimensions(mxOctave) != 2 ||\n                mxGetNumberOfDimensions(mxLevel) != 2)\n                return SIFT3D_FAILURE;\n\n        // Verify the scalars\n        if (!mxIsScalar(mxScale) ||\n                !mxIsScalar(mxOctave) ||\n                !mxIsScalar(mxLevel))\n                return SIFT3D_FAILURE;\n\n        // Verify the coordinate vector dimensions \n        coordsDims = mxGetDimensions(mxCoords);\n        if ((coordsDims[0] != 1 && coordsDims[1] != 1) ||\n                coordsDims[0] * coordsDims[1] != IM_NDIMS)\n                return SIFT3D_FAILURE;\n\n\n        // Verify the orientation matrix dimensions\n        oriDims = mxGetDimensions(mxOri);\n        if (oriDims[0] != IM_NDIMS || oriDims[1] != IM_NDIMS)\n                return SIFT3D_FAILURE;\n\n        // Allocate space in the keypoint store\n        if (resize_Keypoint_store(kp, (size_t) numKp))\n                return SIFT3D_FAILURE;\n\n        // Copy the data\n        for (i = 0; i < (int) numKp; i++) {\n\n                double *coordsData;\n\n                Keypoint *const key = kp->buf + i;\n                const mwIndex idx = (mwIndex) i;\n\n                // Get the matrices\n                if ((mxCoords = mxGetFieldByNumber(mx, idx, coordsNum)) == \n                                NULL ||\n                        (mxScale = mxGetFieldByNumber(mx, idx, scaleNum)) == \n                                NULL ||\n                        (mxOri = mxGetFieldByNumber(mx, idx, oriNum)) == \n                                NULL ||\n                        (mxOctave = mxGetFieldByNumber(mx, idx, octaveNum)) == \n                                NULL ||\n                        (mxLevel = mxGetFieldByNumber(mx, idx, levelNum)) == \n                                NULL)\n                        return SIFT3D_FAILURE;\n\n\n                // Copy the scalars\n                key->sd = mxGetScalar(mxScale);\n                key->o = (int) mxGetScalar(mxOctave);\n                key->s = (int) mxGetScalar(mxLevel);\n\n                // Get the coordinate data\n                if ((coordsData = mxGetData(mxCoords)) == NULL)\n                        return SIFT3D_FAILURE;\n\n                // Copy the coordinate vector\n                key->xd = coordsData[0];\n                key->yd = coordsData[1];\n                key->zd = coordsData[2];\n\n\t\t// Initialize the orientation matrix\n\t\tif (init_Mat_rm_p(&key->R, key->r_data, IM_NDIMS, IM_NDIMS, \n                        SIFT3D_FLOAT, SIFT3D_FALSE))\n\t\t\treturn SIFT3D_FAILURE;\n\n                // Copy the orientation matrix\n                if (mx2mat(mxOri, &key->R))\n                        return SIFT3D_FAILURE;\n        }\n\n        return SIFT3D_SUCCESS;\n}\n\n/* Converts a SIFT3D_Descriptor_store to an mxArray.\n *\n * Output format:\n * [x y z el0 el1 ... el767] (see sift.c:SIFT3D_Descriptor_store_to_Mat_rm)\n *\n * Returns a pointer to the array, or NULL on failure. */\nmxArray *desc2mx(const SIFT3D_Descriptor_store *const desc) {\n\n        mxArray *mx;\n        Mat_rm mat;\n\n        // Initialize intermediates\n        if (init_Mat_rm(&mat, 0, 0, SIFT3D_FLOAT, SIFT3D_FALSE))\n                return NULL;\n\n        // Convert desc to a matrix\n        if (SIFT3D_Descriptor_store_to_Mat_rm(desc, &mat))\n                goto desc2mx_quit;\n\n        // Convert the matrix to an mxArray\n        if ((mx = mat2mx(&mat)) == NULL)\n                goto desc2mx_quit;\n\n        // Clean up\n        cleanup_Mat_rm(&mat);\n\n        return mx;\n\ndesc2mx_quit:\n        cleanup_Mat_rm(&mat);\n        return NULL;\n} \n\n/* Converts a pair of mxArrays to a SIFT3D_Descriptor_store. */\nint mx2desc(const mxArray *const mx, SIFT3D_Descriptor_store *const desc) {\n\n        Mat_rm mat;\n\n        // Initialize intermediates\n        if (init_Mat_rm(&mat, 0, 0, SIFT3D_FLOAT, SIFT3D_FALSE))\n                return SIFT3D_FAILURE;\n\n        // Convert the mxArrays to a matrix\n        if (mx2mat(mx, &mat)) {\n                SIFT3D_ERR(\"mx2desc: Failed to convert mx to mat\");\n                goto mx2desc_quit;\n        }\n\n        // Convert the matrix to a SIFT3D_Descriptor_store\n        if (Mat_rm_to_SIFT3D_Descriptor_store(&mat, desc)) {\n                SIFT3D_ERR(\"mx2desc: Failed to convert mat to desc\");\n                goto mx2desc_quit;\n        }\n\n        // Clean up\n        cleanup_Mat_rm(&mat);\n\n        return SIFT3D_SUCCESS;\n\nmx2desc_quit:\n        cleanup_Mat_rm(&mat);\n        return SIFT3D_FAILURE;\n}\n\n/* Convert an array of doubles to an mxArray. \n *\n * Parameters:\n *   array: The array.\n *   num: The number of elements.\n *\n * Returns a pointer to the mxArray, or NULL on failure. */\nmxArray *array2mx(const double *const array, const size_t len) {\n\n        mxArray *mx;\n        double *mxData; \n        size_t i;\n\n        const mwSize dims[] = {len}; \n        const int ndim = 1; \n\n        // Create the mxArray\n        if ((mx = mxCreateNumericArray(ndim, dims, mxDOUBLE_CLASS, \n                mxREAL)) == NULL)\n                return NULL;\n\n        // Get a pointer to the array's data\n        if ((mxData = mxGetData(mx)) == NULL)\n                return NULL;\n\n        // Copy the data\n        for (i = 0; i < len; i++) {\n                mxData[i] = array[i];\n        }\n\n        return mx;\n}\n\n/* Wrapper for SIFT3D_detect_keypoints. */\nint mex_SIFT3D_detect_keypoints(const Image *const im, \n        Keypoint_store *const kp) {\n        return SIFT3D_detect_keypoints(&reg.sift3d, im, kp);\n}\n\n/* Wrapper for SIFT3D_assign_orientations. */\nint mex_SIFT3D_assign_orientations(const Image *const im, \n        Keypoint_store *const kp, double **const conf) {\n        return SIFT3D_assign_orientations(&reg.sift3d, im, kp, conf);\n}\n\n/* Wrapper for SIFT3D_extract_descriptors. */\nint mex_SIFT3D_extract_descriptors(const Keypoint_store *const kp, \n        SIFT3D_Descriptor_store *const desc) {\n        return SIFT3D_extract_descriptors(&reg.sift3d, kp, desc);\n}\n\n/* Wrapper for SIFT3D_extract_raw_descriptors. */\nint mex_SIFT3D_extract_raw_descriptors(const Image *const im, \n        const Keypoint_store *const kp, SIFT3D_Descriptor_store *const desc) {\n        return SIFT3D_extract_raw_descriptors(&reg.sift3d, im, kp, desc);\n}\n\n/* Wrapper for SIFT3D_have_gpyr. */\nint mexHaveGpyr(void) {\n        return SIFT3D_have_gpyr(&reg.sift3d);\n}\n\n/* Wrapper to set the options for the SIFT3D struct. The argument mx shall be\n * a struct with the following fields:\n *      -peakThresh\n *      -cornerThresh\n *      -numKpLevels\n *      -sigmaN\n *      -sigma0\n *\n * Any other fields are ignored. Empty fields are replaced with the defaults. \n *\n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise. */\nint mex_set_opts_SIFT3D(const mxArray *const mx) {\n\n        SIFT3D sift3d;\n        const mxArray *mxFirstOctave, *mxPeakThresh, *mxCornerThresh, \n                *mxNumKpLevels, *mxSigmaN, *mxSigma0;\n\n        // Verify inputs\n        if (mxIsEmpty(mx) || !mxIsStruct(mx))\n                return SIFT3D_FAILURE;\n\n        // Get the option arrays\n        if ((mxPeakThresh = mxGetField(mx, 0, \"peakThresh\")) == NULL ||\n                (mxCornerThresh = mxGetField(mx, 0, \"cornerThresh\")) == NULL ||\n                (mxNumKpLevels = mxGetField(mx, 0, \"numKpLevels\")) == NULL ||\n                (mxSigmaN = mxGetField(mx, 0, \"sigmaN\")) == NULL ||\n                (mxSigma0 = mxGetField(mx, 0, \"sigma0\")) == NULL)\n                return SIFT3D_FAILURE;\n\n        // Initialize intermediates\n        if (init_SIFT3D(&sift3d))\n                return SIFT3D_FAILURE;\n\n        // Set the non-empty options in our new SIFT3D struct\n        if (!mxIsEmpty(mxPeakThresh) && \n                set_peak_thresh_SIFT3D(&sift3d, mxGetScalar(mxPeakThresh)))\n                goto mex_set_opts_SIFT3D_quit;\n        if (!mxIsEmpty(mxCornerThresh) &&\n                set_corner_thresh_SIFT3D(&sift3d, mxGetScalar(mxCornerThresh)))\n                goto mex_set_opts_SIFT3D_quit;\n        if (!mxIsEmpty(mxNumKpLevels) &&\n                set_num_kp_levels_SIFT3D(&sift3d, \n                (int) mxGetScalar(mxNumKpLevels)))\n                goto mex_set_opts_SIFT3D_quit;\n        if (!mxIsEmpty(mxSigmaN) &&\n                set_sigma_n_SIFT3D(&sift3d, mxGetScalar(mxSigmaN)))\n                goto mex_set_opts_SIFT3D_quit;\n        if (!mxIsEmpty(mxSigma0) &&\n                set_sigma0_SIFT3D(&sift3d, mxGetScalar(mxSigma0)))\n                goto mex_set_opts_SIFT3D_quit;\n\n        // Set the options in the Reg_SIFT3D struct\n        if (set_SIFT3D_Reg_SIFT3D(&reg, &sift3d))\n                goto mex_set_opts_SIFT3D_quit;\n\n        // Clean up\n        cleanup_SIFT3D(&sift3d);\n\n        return SIFT3D_SUCCESS;\n\nmex_set_opts_SIFT3D_quit:\n        cleanup_SIFT3D(&sift3d);\n        return SIFT3D_FAILURE;\n}\n\n/* Wrapper to set the options for the Reg_SIFT3D struct. The argument mx shall\n * be a struct with the following fields:\n *   -numIter\n *   -errThresh\n *   -nnThresh\n *\n * Any other fields are ignored. Empty fields are replaced with the defaults.\n *\n * Returns SIFT3D_SUCCESS on success, SIFT3D_FAILURE otherwise. */\nint mex_set_opts_Reg_SIFT3D(const mxArray *const mx) {\n\n        Ransac ran;\n        const mxArray *mxNumIter, *mxErrThresh, *mxNnThresh;\n        double nn_thresh;\n\n        // Verify inputs\n        if (mxIsEmpty(mx) || !mxIsStruct(mx))\n                return SIFT3D_FAILURE;\n        \n        // Initialize options to the defaults\n        nn_thresh = SIFT3D_nn_thresh_default;\n        init_Ransac(&ran);\n\n        // Get the option arrays\n        if ((mxNumIter = mxGetField(mx, 0, \"numIter\")) == NULL ||\n                (mxErrThresh = mxGetField(mx, 0, \"errThresh\")) == NULL ||\n                (mxNnThresh = mxGetField(mx, 0, \"nnThresh\")) == NULL)\n                return SIFT3D_FAILURE;\n\n        // Get the non-empty options \n        if (!mxIsEmpty(mxNumIter) &&\n                set_num_iter_Ransac(&ran, (int) mxGetScalar(mxNumIter))) {\n                return SIFT3D_FAILURE;\n        } \n        if (!mxIsEmpty(mxErrThresh) && \n                set_err_thresh_Ransac(&ran, mxGetScalar(mxErrThresh))) {\n                return SIFT3D_FAILURE;\n        } \n        if (!mxIsEmpty(mxNnThresh)) {\n                nn_thresh = mxGetScalar(mxNnThresh);\n        }\n\n        // Set the options\n        return set_Ransac_Reg_SIFT3D(&reg, &ran) ||\n            set_nn_thresh_Reg_SIFT3D(&reg, nn_thresh);\n}\n\n/* Get the current value of nn_thresh from the Reg_SIFT3D struct. */\ndouble mex_get_nn_thresh_Reg_SIFT3D(void) {\n        return reg.nn_thresh;\n}\n\n/* Wrapper for register_SIFT3D_resample */\nint mex_register_SIFT3D_resample(const Image *const src, \n        const Image *const ref, const interp_type interp, void *const tform) {\n        return register_SIFT3D_resample(&reg, src, ref, interp, tform); \n}\n\n/* Wrapper for set_src_Reg_SIFT3D */\nint mex_set_src_Reg_SIFT3D(const Image *const src) {\n        return set_src_Reg_SIFT3D(&reg, src); \n}\n\n/* Wrapper for set_ref_Reg_SIFT3D */\nint mex_set_ref_Reg_SIFT3D(const Image *const ref) {\n        return set_ref_Reg_SIFT3D(&reg, ref); \n}\n\n/* Wrapper for register_SIFT3D */\nint mex_register_SIFT3D(void *const tform) {\n        return register_SIFT3D(&reg, tform);\n}\n\n/* Wrapper for get_matches_Reg_SIFT3D */\nint mex_get_matches_Reg_SIFT3D(Mat_rm *const match_src, \n        Mat_rm *const match_ref) {\n        return get_matches_Reg_SIFT3D(&reg, match_src, match_ref);\n}\n"
  },
  {
    "path": "wrappers/matlab/mexutil.h",
    "content": "/* -----------------------------------------------------------------------------\n * mexutil.h\n * -----------------------------------------------------------------------------\n * Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n * -----------------------------------------------------------------------------\n * Public header for SIFT3D mex utility library.\n * -----------------------------------------------------------------------------\n */\n\n#include <uchar.h>\n#include \"mex.h\"\n#include \"imtypes.h\"\n\n#ifndef _MEXUTIL_H\n#define _MEXUTIL_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid err_msg(const char *name, const char *msg);\n\nvoid err_msgu(const char *name, const char *msg);\n\nint isDouble(const mxArray *const mx);\n\nmwIndex mxImGetIdx(const mxArray *const mx, const int x, const int y, \n        const int z, const int c);\n\nmxArray *im2mx(const Image *const im);\n\nint mx2im(const mxArray *const mx, Image *const im);\n\nmxArray *units2mx(const Image *const im);\n\nint mx2units(const mxArray *const mx, Image *const im);\n\nint mx2imWithUnits(const mxArray *const data, const mxArray *const units,\n        Image *const im);\n\nmxArray *mat2mx(const Mat_rm *const mat);\n\nint mx2mat(const mxArray *const mx, Mat_rm *const mat);\n\nmxArray *kp2mx(const Keypoint_store *const);\n\nint mx2kp(const mxArray *const mx, Keypoint_store *const);\n\nmxArray *desc2mx(const SIFT3D_Descriptor_store *const desc);\n\nint mx2desc(const mxArray *const mx, SIFT3D_Descriptor_store *const desc);\n\nmxArray *array2mx(const double *const array, const size_t len);\n\nint mex_SIFT3D_detect_keypoints(const Image *const im, \n        Keypoint_store *const kp);\n\nint mex_SIFT3D_assign_orientations(const Image *const im, \n        Keypoint_store *const kp, double **const conf);\n\nint mex_SIFT3D_extract_descriptors(const Keypoint_store *const kp, \n        SIFT3D_Descriptor_store *const desc);\n\nint mex_SIFT3D_extract_raw_descriptors(const Image *const im, \n        const Keypoint_store *const kp, SIFT3D_Descriptor_store *const desc);\n\nint mexHaveGpyr(void);\n\nint mex_set_opts_SIFT3D(const mxArray *const mx);\n\nint mex_set_opts_Reg_SIFT3D(const mxArray *const mx);\n\nint mex_set_nn_thresh_Reg_SIFT3D(const double nn_thresh);\n\ndouble mex_get_nn_thresh_Reg_SIFT3D(void);\n\nint mex_register_SIFT3D_resample(const Image *const src, \n        const Image *const ref, const interp_type interp, void *const tform);\n\nint mex_set_src_Reg_SIFT3D(const Image *const src);\n\nint mex_set_ref_Reg_SIFT3D(const Image *const ref);\n\nint mex_register_SIFT3D(void *const tform);\n\nint mex_get_matches_Reg_SIFT3D(Mat_rm *const match_src, \n        Mat_rm *const match_ref);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "wrappers/matlab/orientation3D.m",
    "content": "%orientation3D(keys, im, units) Assign 3D orientations to keypoints \n%   in an image. This is not needed if your keypoints were detected with\n%   detectSift3D, but may be useful if your keypoints were defined\n%   manually with keypoint3D.\n%\n%  Arguments:\n%    keys - [Mx1] array of keypoint structs. See keypoint3D.m.\n%    im - An [MxNxP] array, where voxels are indexed in (x, y, z) order.\n%    units - (Optional) See imRead3D. (Default: [1 1 1])\n%\n%  Return values:\n%    keys - The same as the input, but with the \"ori\" fields filled with\n%       rotation matrices assigned based on the data in im.\n%    conf - An [Mx1] vector of confidence scores. The scores are in the \n%       interval [0, 1], where 1 is the most confident, 0 is the least. A \n%       higher score means the assigned orientation is more likely to be \n%       robust. A negative score means the orientation could not be assigned.\n%\n%  Note: For some data, this function cannot find a stable orientation, in\n%  which case it throws a warning and returns an identity matrix R. The conf\n%  score for these elements is negative.\n%\n%  Example:\n%       im = rand(20, 20, 20);\n%       keys = keypoint3D([10 10 10]);\n%       [keys, conf] = orientation3D(keys, im);\n%\n%  See also:\n%       keypoint3D, extractSift3D, imRead3D, setupSift3D\n%\n%  Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\nfunction [R, conf] = orientation3D(keys, im, units)\n\n% Verify inputs\nif isempty(im)\n    error('im is empty')\nend\n\nif ndims(im) ~= 3\n    error(['im must have 3 dimensions, detected ' num2str(ndims(im))]);\nend\n\nif nargin < 3 || isempty(units)\n        units = [];\nend\n\nunits = checkUnits3D(units);\n\n% Scale and convert the image to single precision\nim = single(im);\nim = im / max(im(:));\n\n% Assign the orientations\n[R, conf] = mexOrientation3D(keys, im, units);\n\n% Check the validity\nif any(conf) < 0\n    warning('Failed to assign a stable orientation to some keypoints')\nend\n\nend\n"
  },
  {
    "path": "wrappers/matlab/registerSift3D.m",
    "content": "function [A, matchSrc, matchRef] = registerSift3D(src, ref, varargin)\n%registerSift3D(src, ref, options) register a pair of\n%  images using SIFT3D keypoints. Detects keypoints, extracts and matches\n%  descriptors, and then fits an affine transformation using the RANSAC\n%  algorithm.\n%\n%  Arguments:\n%    src, ref - A pair of [MxNxP] arrays, where voxels are indexed in\n%      (x, y, z) order. src is the \"source\" or \"moving\" image, and ref is\n%      the \"reference\" or \"fixed\" image.\n%\n%  Options:\n%    srcUnits, refUnits - The physical units for src and ref,\n%      respectively. See imRead3D for the format. Units should be provided\n%      when attempting to register images of different resolutions, i.e.\n%      5mm slices to 1mm slices. (Default: [1 1 1])\n%    nnThresh - The matching threshold, in the interval (0, 1]. See\n%      matchSift3D for details. (Default: 0.8)\n%    errThresh - The RANSAC inlier threshold, in the interval (0, inf).\n%       This is a threshold on the squared Euclidean distance in real-world\n%       units. (Default: 5.0)\n%    numIter - The number of RANSAC iterations. (Default: 500)\n%    resample - If true, resamples src and ref to have the same resolution\n%       prior to registration. This is slow. Use it when the inputs have\n%       vastly different units. (Default: false)\n%\n%  SIFT3D options:\n%    This function also accepts the following options controlling keypoint\n%    detection:\n%      peakThresh\n%      cornerThresh\n%      numKpLevels\n%      sigmaN\n%      sigma0\n%\n%    See detectSift3D.m for the meaning and format of these options. \n%\n%  Return values:\n%    A - A [4x3] matrix giving an affine transformation from the\n%      coordinates of ref to those of src. The transformation can be\n%      applied as follows:\n%          [xt yt zt]' = A * [x y z 1]';\n%    matchSrc, matchRef - A pair of [3xQ] arrays giving the matched\n%      keypoint locations in src and ref, respectively. Each row is an\n%      (x, y, z) coordinate, so that the ith row of matchSrc matches the\n%      ith row of matchRef. These matches may contain outliers.\n%\n%  Note: This function will reserve memory that persists after it has\n%  finished. To release all SIFT3D memory, use 'clear mex'.\n%\n%  Examples:\n%    % Load the images\n%    [src, srcUnits] = imRead3D('src.dcm');\n%    [ref, refUnits] = imRead3D('ref.dcm');\n%\n%    % Register with units\n%    [A, matchSrc, matchRef] = registerSift3D(src, ref, 'srcUnits', ...\n%       srcUnits, 'refUnits', refUnits, 'resample', true);\n%\n%    % Register without units\n%    [A, matchSrc, matchRef] = registerSift3D(src, ref);\n%\n%\n%  See also:\n%    imRead3D, imWrite3D, matchSift3D, setupSift3D\n%\n%  Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n\n% Option names\nsrcUnitsStr = 'srcUnits';\nrefUnitsStr = 'refUnits';\nnumIterStr = 'numIter';\nnnThreshStr = 'nnThresh';\nerrThreshStr = 'errThresh';\nresampleStr = 'resample';\n\n% Parse options\nparser = Sift3DParser;\nparser.addParamValue(srcUnitsStr, [])\nparser.addParamValue(refUnitsStr, [])\nparser.addParamValue(numIterStr, [])\nparser.addParamValue(nnThreshStr, [])\nparser.addParamValue(errThreshStr, [])\nparser.addParamValue(resampleStr, false)\nsift3DOpts = parser.parseAndVerify(varargin{:});\nsrcUnits = parser.Results.srcUnits;\nrefUnits = parser.Results.refUnits;\nnumIter = parser.Results.numIter;\nnnThresh = parser.Results.nnThresh;\nerrThresh = parser.Results.errThresh;\nresample = parser.Results.resample;\n\n% Verify inputs\nnarginchk(2, inf)\nif isempty(src)\n    error('src is empty')\nend\nif isempty(ref)\n    error('ref is empty')\nend\nsrcUnits = checkUnits3D(srcUnits, 'srcUnits');\nrefUnits = checkUnits3D(refUnits, 'refUnits');\nif ~isempty(numIter)\n    validateattributes(numIter, {'numeric'}, ...\n        {'real', 'scalar', '>', 0}, 'numIter')\nend\nif ~isempty(nnThresh)\n    validateattributes(nnThresh, {'numeric'}, ...\n        {'real', 'scalar', '>=', 0, '<', 1}, 'nnThresh')\nend\nif ~isempty(errThresh)\n    validateattributes(errThresh, {'numeric'}, ...\n        {'real', 'scalar', '>', 0}, 'errThresh')\nend\nvalidateattributes(resample, {'logical'}, {'scalar'}, 'resample')\n\n% Convert the images to single precision and scale to [0, 1]\nsrc = imFormat(src);\nref = imFormat(ref);\n\n% Collect the options in a struct\nregOpts = struct(numIterStr, numIter, ...\n    nnThreshStr, nnThresh, ...\n    errThreshStr, errThresh);\n\n% Register\n[A, matchSrc, matchRef] = mexRegisterSift3D(src, ref, srcUnits, ...\n    refUnits, resample, regOpts, sift3DOpts);\nend\n\n% Helper function to convert images to single precision and scale to [0,1]\nfunction im = imFormat(im)\nim = single(im);\nim = im / (max(im(:)) + eps);\nend\n"
  },
  {
    "path": "wrappers/matlab/setupSift3D.m",
    "content": "% setupSift3D\n%\n% Run this script to set up the paths for the SIFT3D matlab toolbox. For \n% example, add the following line to your startup.m file:\n% \n%   run('/path/to/sift3d/lib/wrappers/matlab/setupSift3D.m')\n%\n% where /path/to/sift3d/ is the path to the your SIFT3D installation.\n%\n% See also:\n%   imRead3D, imWrite3D, detectSift3D, extractSift3D, keypoint3D\n%\n% Copyright (c) 2015-2016 Blaine Rister et al., see LICENSE for details.\n\n% Get the full path to this file\nfilename = mfilename('fullpath');\n\n% Extract the toolbox directory\ntoolboxname = fileparts(filename);\n\n% Add the toolbox to the path\naddpath(genpath(toolboxname));\n\n% Clean up the workspace\nclear filename toolboxname\n"
  }
]