Compare View

switch
from
...
to
 
Commits (292)
Showing 127 changed files   Show diff stats

Too many changes.

To preserve performance only 100 of 127 files are displayed.

cmake/FindGLEW.cmake
  1 +# Copyright (c) 2012-2016 DreamWorks Animation LLC
  2 +#
  3 +# All rights reserved. This software is distributed under the
  4 +# Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
  5 +#
  6 +# Redistributions of source code must retain the above copyright
  7 +# and license notice and the following restrictions and disclaimer.
  8 +#
  9 +# * Neither the name of DreamWorks Animation nor the names of
  10 +# its contributors may be used to endorse or promote products derived
  11 +# from this software without specific prior written permission.
  12 +#
  13 +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  14 +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  15 +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  16 +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  17 +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
  18 +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  19 +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  20 +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  21 +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  22 +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  23 +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 +# IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
  25 +# LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
1 # 26 #
2 -# Windows users: define the GLEW_PATH environment variable to point  
3 -# to the root glew directory, which contains:  
4 -# lib/Release/Win32/glew32.lib AND/OR lib/Release/x64/glew32.lib  
5 -# include/GL/glew.h  
6 27
7 -#Try to find GLEW library and include path.  
8 -# Once done this will define 28 +#-*-cmake-*-
  29 +# - Find GLEW
  30 +#
  31 +# Author : Nicholas Yue yue.nicholas@gmail.com
  32 +#
  33 +# This auxiliary CMake file helps in find the GLEW headers and libraries
9 # 34 #
10 -# GLEW_FOUND  
11 -# GLEW_INCLUDE_DIR  
12 -# GLEW_LIBRARY  
13 -# 35 +# GLEW_FOUND set if Glew is found.
  36 +# GLEW_INCLUDE_DIR GLEW's include directory
  37 +# GLEW_glew_LIBRARY GLEW libraries
  38 +# GLEW_glewmx_LIBRARY GLEWmx libraries (Mulitple Rendering Context)
  39 +
  40 +FIND_PACKAGE ( PackageHandleStandardArgs )
  41 +
  42 +FIND_PATH( GLEW_LOCATION include/GL/glew.h
  43 + "$ENV{GLEW_ROOT}"
  44 + NO_DEFAULT_PATH
  45 + NO_SYSTEM_ENVIRONMENT_PATH
  46 + )
  47 +
  48 +FIND_PACKAGE_HANDLE_STANDARD_ARGS ( GLEW
  49 + REQUIRED_VARS GLEW_LOCATION
  50 + )
  51 +
  52 +IF ( GLEW_LOCATION )
  53 +
  54 + SET( GLEW_INCLUDE_DIR "${GLEW_LOCATION}/include" CACHE STRING "GLEW include path")
14 55
15 -IF (WIN32)  
16 - FIND_PATH( GLEW_INCLUDE_DIR GL/glew.h  
17 - $ENV{GLEW_PATH}/include  
18 - $ENV{PROGRAMFILES}/GLEW/include  
19 - ${PROJECT_SOURCE_DIR}/src/nvgl/glew/include  
20 - DOC "The directory where GL/glew.h resides")  
21 - if( CMAKE_SIZEOF_VOID_P EQUAL 8 )  
22 - FIND_LIBRARY( GLEW_LIBRARY  
23 - NAMES glew GLEW glew32 glew32s  
24 - PATHS  
25 - $ENV{GLEW_PATH}/lib/Release/x64  
26 - $ENV{PROGRAMFILES}/GLEW/lib  
27 - ${PROJECT_SOURCE_DIR}/src/nvgl/glew/bin  
28 - ${PROJECT_SOURCE_DIR}/src/nvgl/glew/lib  
29 - DOC "The GLEW library")  
30 - else( CMAKE_SIZEOF_VOID_P EQUAL 8 )  
31 - FIND_LIBRARY( GLEW_LIBRARY  
32 - NAMES glew GLEW glew32 glew32s  
33 - PATHS  
34 - $ENV{GLEW_PATH}/lib/Release/Win32  
35 - $ENV{PROGRAMFILES}/GLEW/lib  
36 - ${PROJECT_SOURCE_DIR}/src/nvgl/glew/bin  
37 - ${PROJECT_SOURCE_DIR}/src/nvgl/glew/lib  
38 - DOC "The GLEW library")  
39 - endif( CMAKE_SIZEOF_VOID_P EQUAL 8 )  
40 -ELSE (WIN32)  
41 - FIND_PATH( GLEW_INCLUDE_DIR GL/glew.h  
42 - /usr/include  
43 - /usr/local/include  
44 - /sw/include  
45 - /opt/local/include  
46 - DOC "The directory where GL/glew.h resides")  
47 - FIND_LIBRARY( GLEW_LIBRARY  
48 - NAMES GLEW glew  
49 - PATHS  
50 - /usr/lib64  
51 - /usr/lib  
52 - /usr/local/lib64  
53 - /usr/local/lib  
54 - /sw/lib  
55 - /opt/local/lib  
56 - DOC "The GLEW library")  
57 -ENDIF (WIN32) 56 + SET ( ORIGINAL_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
  57 + IF (GLEW_USE_STATIC_LIBS)
  58 + IF (APPLE)
  59 + SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
  60 + FIND_LIBRARY ( GLEW_LIBRARY_PATH GLEW PATHS ${GLEW_LOCATION}/lib
  61 + NO_DEFAULT_PATH
  62 + NO_SYSTEM_ENVIRONMENT_PATH
  63 + )
  64 + FIND_LIBRARY ( GLEWmx_LIBRARY_PATH GLEWmx PATHS ${GLEW_LOCATION}/lib
  65 + NO_DEFAULT_PATH
  66 + NO_SYSTEM_ENVIRONMENT_PATH
  67 + )
  68 + # MESSAGE ( "APPLE STATIC" )
  69 + # MESSAGE ( "GLEW_LIBRARY_PATH = " ${GLEW_LIBRARY_PATH} )
  70 + ELSEIF (WIN32)
  71 + # Link library
  72 + SET(CMAKE_FIND_LIBRARY_SUFFIXES ".lib")
  73 + FIND_LIBRARY ( GLEW_LIBRARY_PATH GLEW32S PATHS ${GLEW_LOCATION}/lib )
  74 + FIND_LIBRARY ( GLEWmx_LIBRARY_PATH GLEW32MXS PATHS ${GLEW_LOCATION}/lib )
  75 + ELSE (APPLE)
  76 + SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
  77 + FIND_LIBRARY ( GLEW_LIBRARY_PATH GLEW PATHS ${GLEW_LOCATION}/lib
  78 + NO_DEFAULT_PATH
  79 + NO_SYSTEM_ENVIRONMENT_PATH
  80 + )
  81 + FIND_LIBRARY ( GLEWmx_LIBRARY_PATH GLEWmx PATHS ${GLEW_LOCATION}/lib
  82 + NO_DEFAULT_PATH
  83 + NO_SYSTEM_ENVIRONMENT_PATH
  84 + )
  85 + # MESSAGE ( "LINUX STATIC" )
  86 + # MESSAGE ( "GLEW_LIBRARY_PATH = " ${GLEW_LIBRARY_PATH} )
  87 + ENDIF (APPLE)
  88 + ELSE ()
  89 + IF (APPLE)
  90 + SET(CMAKE_FIND_LIBRARY_SUFFIXES ".dylib")
  91 + FIND_LIBRARY ( GLEW_LIBRARY_PATH GLEW PATHS ${GLEW_LOCATION}/lib )
  92 + FIND_LIBRARY ( GLEWmx_LIBRARY_PATH GLEWmx PATHS ${GLEW_LOCATION}/lib )
  93 + ELSEIF (WIN32)
  94 + # Link library
  95 + SET(CMAKE_FIND_LIBRARY_SUFFIXES ".lib")
  96 + FIND_LIBRARY ( GLEW_LIBRARY_PATH GLEW32 PATHS ${GLEW_LOCATION}/lib )
  97 + FIND_LIBRARY ( GLEWmx_LIBRARY_PATH GLEW32mx PATHS ${GLEW_LOCATION}/lib )
  98 + # Load library
  99 + SET(CMAKE_FIND_LIBRARY_SUFFIXES ".dll")
  100 + FIND_LIBRARY ( GLEW_DLL_PATH GLEW32 PATHS ${GLEW_LOCATION}/bin
  101 + NO_DEFAULT_PATH
  102 + NO_SYSTEM_ENVIRONMENT_PATH
  103 + )
  104 + FIND_LIBRARY ( GLEWmx_DLL_PATH GLEW32mx PATHS ${GLEW_LOCATION}/bin
  105 + NO_DEFAULT_PATH
  106 + NO_SYSTEM_ENVIRONMENT_PATH
  107 + )
  108 + ELSE (APPLE)
  109 + # Unices
  110 + FIND_LIBRARY ( GLEW_LIBRARY_PATH GLEW PATHS ${GLEW_LOCATION}/lib
  111 + NO_DEFAULT_PATH
  112 + NO_SYSTEM_ENVIRONMENT_PATH
  113 + )
  114 + FIND_LIBRARY ( GLEWmx_LIBRARY_PATH GLEWmx PATHS ${GLEW_LOCATION}/lib
  115 + NO_DEFAULT_PATH
  116 + NO_SYSTEM_ENVIRONMENT_PATH
  117 + )
  118 + ENDIF (APPLE)
  119 + ENDIF ()
  120 + # MUST reset
  121 + SET(CMAKE_FIND_LIBRARY_SUFFIXES ${ORIGINAL_CMAKE_FIND_LIBRARY_SUFFIXES})
58 122
59 -IF (GLEW_INCLUDE_DIR)  
60 - SET( GLEW_FOUND 1 CACHE STRING "Set to 1 if GLEW is found, 0 otherwise")  
61 -ELSE (GLEW_INCLUDE_DIR)  
62 - SET( GLEW_FOUND 0 CACHE STRING "Set to 1 if GLEW is found, 0 otherwise")  
63 -ENDIF (GLEW_INCLUDE_DIR) 123 + SET( GLEW_GLEW_LIBRARY ${GLEW_LIBRARY_PATH} CACHE STRING "GLEW library")
  124 + SET( GLEW_GLEWmx_LIBRARY ${GLEWmx_LIBRARY_PATH} CACHE STRING "GLEWmx library")
64 125
65 -MARK_AS_ADVANCED(  
66 - GLEW_FOUND  
67 - GLEW_INCLUDE_DIR  
68 - GLEW_LIBRARY  
69 -)  
70 \ No newline at end of file 126 \ No newline at end of file
  127 +ENDIF ()
cmake/FindSTIM.cmake
1 -include(FindPackageHandleStandardArgs)  
2 -  
3 -set(STIM_INCLUDE_DIR $ENV{STIMLIB_PATH})  
4 -  
5 -find_package_handle_standard_args(STIM DEFAULT_MSG STIM_INCLUDE_DIR)  
6 -  
7 -if(STIM_FOUND)  
8 - set(STIM_INCLUDE_DIRS ${STIM_INCLUDE_DIR})  
9 -endif()  
10 \ No newline at end of file 1 \ No newline at end of file
  2 +# finds the STIM library (downloads it if it isn't present)
  3 +# set STIMLIB_PATH to the directory containing the stim subdirectory (the stim repository)
  4 +
  5 +include(FindPackageHandleStandardArgs)
  6 +
  7 +set(STIM_INCLUDE_DIR $ENV{STIMLIB_PATH})
  8 +
  9 +find_package_handle_standard_args(STIM DEFAULT_MSG STIM_INCLUDE_DIR)
  10 +
  11 +if(STIM_FOUND)
  12 + set(STIM_INCLUDE_DIRS ${STIM_INCLUDE_DIR})
  13 +elseif(STIM_FOUND)
  14 + #if the STIM library isn't found, download it
  15 + #file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/stimlib) #remove the stimlib directory if it exists
  16 + #set(STIM_GIT "https://git.stim.ee.uh.edu/codebase/stimlib.git")
  17 + #execute_process(COMMAND git clone --depth 1 ${STIM_GIT} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
  18 + #set(STIM_INCLUDE_DIRS "${CMAKE_BINARY_DIR}/stimlib" CACHE TYPE PATH)
  19 + message("STIM library not found. Set the STIMLIB_PATH environment variable to the STIMLIB location.")
  20 + message("STIMLIB can be found here: https://git.stim.ee.uh.edu/codebase/stimlib")
  21 +endif(STIM_FOUND)
  22 +
  23 +find_package_handle_standard_args(STIM DEFAULT_MSG STIM_INCLUDE_DIR)
matlab/bsq2tensorflow.m 0 → 100644
  1 +function T = bsq2tensorflow(I, n)
  2 +
  3 + sx = size(I, 1);
  4 + sy = size(I, 2) / n; %get the size of the tensor along Y
  5 + sb = size(I, 3);
  6 +
  7 + T = zeros(sx * sy * sb, n); %allocate space for the output matrix
  8 + for i = 0:n-1
  9 + ti = I(:, i * sy + 1 : i * sy + sy, :);
  10 + T(:, i+1) = ti(:);
  11 + end
  12 +end
  13 +
  14 +
0 \ No newline at end of file 15 \ No newline at end of file
matlab/cls_ConfusionMatrix.m 0 → 100644
  1 +function M = cls_ConfusionMatrix(GT, T)
  2 +
  3 +%calculate the classes (unique elements in the GT array)
  4 +C = unique(GT);
  5 +nc = length(C); %calculate the number of classes
  6 +M = zeros(nc); %allocate space for the confusion matrix
  7 +
  8 +%for each class
  9 +for ci = 1:nc
  10 + for cj = 1:nc
  11 + M(ci, cj) = nnz((GT == C(ci)) .* (T == C(cj)));
  12 + end
  13 +end
0 \ No newline at end of file 14 \ No newline at end of file
matlab/cls_MeanClassFeatures.m 0 → 100644
  1 +function S = cls_MeanClassFeatures(F, T)
  2 +%Calculates the mean set of features for each class given the feature matrix F and targets T
  3 +
  4 +C = unique(T); %get the class IDs
  5 +nc = length(C);
  6 +
  7 +S = zeros(nc, size(F, 2)); %allocate space for the mean feature vectors
  8 +for c = 1:nc %for each class
  9 + S(c, :) = mean(F(T == C(c), :)); %calculate the mean feature vector for class c
  10 +end
  11 +
  12 +S = S';
0 \ No newline at end of file 13 \ No newline at end of file
matlab/cls_PlotConfusionMatrix.m 0 → 100644
  1 +function cls_PlotConfusionMatrix(M)
  2 +
  3 +
  4 +%normalize each row by its column
  5 +sum_cols = repmat(sum(M, 1), size(M, 1), 1);
  6 +Mc = M ./ sum_cols;
  7 +subplot(2, 1, 1),
  8 +bar(Mc');
  9 +
  10 +sum_rows = repmat(sum(M, 2), 1, size(M, 2));
  11 +Mr = M ./ sum_rows;
  12 +subplot(2, 1, 2),
  13 +bar(Mr);
0 \ No newline at end of file 14 \ No newline at end of file
matlab/enviSaveRawcamille.m 0 → 100644
  1 +%saves an ENVI file without any manipulation, assumes (X, Y, S)
  2 +% enviSaveRaw(M, filename, headername,wavenumber data filename)
  3 +function enviSaveRawcamille(M, filename, camilleheader,wavenumberfilename)
  4 +
  5 +%if a header isn't provided, assume it's just the filename
  6 +% with '.hdr' added to the end
  7 +if nargin == 2
  8 + camilleheader = [filename '.hdr'];
  9 +end
  10 +%load the wavenumbers
  11 +if nargin == 4
  12 + wn = csvread([wavenumberfilename '.dat']);
  13 +end
  14 +
  15 +%open a file for writing
  16 +fid = fopen(filename, 'w');
  17 +
  18 +%write the data to disk
  19 +fwrite(fid, M, class(M));
  20 +
  21 +%close the file
  22 +fclose(fid);
  23 +
  24 +%open a header file for writing
  25 +fid = fopen(camilleheader, 'w');
  26 +fprintf(fid, 'ENVI\n');
  27 +fprintf(fid, 'description = {}\n');
  28 +fprintf(fid, 'samples = %d\n', size(M, 1));
  29 +fprintf(fid, 'lines = %d\n', size(M, 2));
  30 +fprintf(fid, 'bands = %d\n', size(M, 3));
  31 +fprintf(fid, 'header offset = 0\n');
  32 +fprintf(fid, 'file type = ENVI Standard\n');
  33 +
  34 +%get a string representing the matlab data type
  35 +matlab_real = isreal(M);
  36 +
  37 +if(isa(M, 'uchar'))
  38 + envi_type = 1;
  39 +elseif(isa(M, 'short'))
  40 + envi_type = 2;
  41 +elseif(isa(M, 'int'))
  42 + envi_type = 3;
  43 +elseif(isa(M, 'single') && matlab_real)
  44 + envi_type = 4;
  45 +elseif(isa(M, 'double') && matlab_real)
  46 + envi_type = 5;
  47 +elseif(isa(M, 'single') && ~matlab_real)
  48 + envi_type = 6;
  49 +elseif(isa(M, 'double') && ~matlab_real)
  50 + envi_type = 9;
  51 +elseif(isa(M, 'ushort'))
  52 + envi_type = 12;
  53 +elseif(isa(M, 'ulong'))
  54 + envi_type = 13;
  55 +elseif(isa(M, 'int64'))
  56 + envi_type = 14;
  57 +elseif(isa(M, 'uint64'))
  58 + envi_type = 15;
  59 +end
  60 +
  61 +fprintf(fid, 'data type = %d\n', envi_type);
  62 +
  63 +fprintf(fid, 'interleave = bsq\n');
  64 +fprintf(fid, 'sensor type = Unknown\n');
  65 +fprintf(fid, 'byte order = 0\n');
  66 +fprintf(fid, 'x start = 0\n');
  67 +fprintf(fid, 'y start = 0\n');
  68 +fprintf(fid, 'wavelength units = Unknown\n');
  69 +fprintf(fid, 'z plot titles = {Unknown, Unknown}\n');
  70 +fprintf(fid, 'pixel size = {1, 1, units=Meters}\n');
  71 +
  72 +% print the wavelengths for each band
  73 +fprintf(fid, 'wavelength = {\n');
  74 +for i= 1:size(wn,2)-1
  75 + fprintf(fid, '%8.3f ,\n',wn(i));
  76 +end
  77 +fprintf(fid, '%8.3f \n',wn(end));
  78 +
  79 +fprintf(fid, '}');
  80 +fclose(fid);
0 \ No newline at end of file 81 \ No newline at end of file
matlab/hyperRaman.m 0 → 100644
  1 +function [im]=hyperRaman(filemask)
  2 +
  3 + filelist = dir(filemask);
  4 +
  5 + %get a list of date numbers
  6 + datenums = cell2mat({filelist.datenum});
  7 +
  8 + %sort the file order based on acquisition time
  9 + [~, id] = sort(datenums);
  10 +
  11 + %get the number of files
  12 + T = length(id); %size of the image along transverse direction X, number of steps when scanned
  13 +
  14 + %load the first file to determine the spectral and Y-axis size
  15 + temp = readspe(filelist(1).name);
  16 + L = size(temp, 1); %size of the image along longitudinal direction Y (laser line)
  17 + B = size(temp, 2); %number of bands in the image
  18 +
  19 + %create the cube
  20 + outvar = zeros(L, T, B);
  21 +
  22 + %for each line
  23 + for x = 1:T
  24 +
  25 + %read a SPE file
  26 + img = readspe(filelist(id(x)).name);
  27 + outvar(:,x,:)= permute(img, [1 3 2]);
  28 + end
  29 + im=outvar;
  30 + figure
  31 + imagesc(outvar(:,:,150)), colorbar, axis normal, axis equal off
  32 +
  33 +% subplot(1,3,1),imagesc(outvar(:,:,151)), colorbar
  34 +% subplot(1,3,2),imagesc(outvar(:,:,175)), colorbar
  35 +% subplot(1,3,3),imagesc(outvar(:,:,291)), colorbar
  36 + colormap inferno;
matlab/inferno.m 0 → 100644
  1 +function [cm_data]=inferno(m)
  2 +
  3 +cm = [[ 1.46159096e-03, 4.66127766e-04, 1.38655200e-02],
  4 + [ 2.26726368e-03, 1.26992553e-03, 1.85703520e-02],
  5 + [ 3.29899092e-03, 2.24934863e-03, 2.42390508e-02],
  6 + [ 4.54690615e-03, 3.39180156e-03, 3.09092475e-02],
  7 + [ 6.00552565e-03, 4.69194561e-03, 3.85578980e-02],
  8 + [ 7.67578856e-03, 6.13611626e-03, 4.68360336e-02],
  9 + [ 9.56051094e-03, 7.71344131e-03, 5.51430756e-02],
  10 + [ 1.16634769e-02, 9.41675403e-03, 6.34598080e-02],
  11 + [ 1.39950388e-02, 1.12247138e-02, 7.18616890e-02],
  12 + [ 1.65605595e-02, 1.31362262e-02, 8.02817951e-02],
  13 + [ 1.93732295e-02, 1.51325789e-02, 8.87668094e-02],
  14 + [ 2.24468865e-02, 1.71991484e-02, 9.73274383e-02],
  15 + [ 2.57927373e-02, 1.93306298e-02, 1.05929835e-01],
  16 + [ 2.94324251e-02, 2.15030771e-02, 1.14621328e-01],
  17 + [ 3.33852235e-02, 2.37024271e-02, 1.23397286e-01],
  18 + [ 3.76684211e-02, 2.59207864e-02, 1.32232108e-01],
  19 + [ 4.22525554e-02, 2.81385015e-02, 1.41140519e-01],
  20 + [ 4.69146287e-02, 3.03236129e-02, 1.50163867e-01],
  21 + [ 5.16437624e-02, 3.24736172e-02, 1.59254277e-01],
  22 + [ 5.64491009e-02, 3.45691867e-02, 1.68413539e-01],
  23 + [ 6.13397200e-02, 3.65900213e-02, 1.77642172e-01],
  24 + [ 6.63312620e-02, 3.85036268e-02, 1.86961588e-01],
  25 + [ 7.14289181e-02, 4.02939095e-02, 1.96353558e-01],
  26 + [ 7.66367560e-02, 4.19053329e-02, 2.05798788e-01],
  27 + [ 8.19620773e-02, 4.33278666e-02, 2.15289113e-01],
  28 + [ 8.74113897e-02, 4.45561662e-02, 2.24813479e-01],
  29 + [ 9.29901526e-02, 4.55829503e-02, 2.34357604e-01],
  30 + [ 9.87024972e-02, 4.64018731e-02, 2.43903700e-01],
  31 + [ 1.04550936e-01, 4.70080541e-02, 2.53430300e-01],
  32 + [ 1.10536084e-01, 4.73986708e-02, 2.62912235e-01],
  33 + [ 1.16656423e-01, 4.75735920e-02, 2.72320803e-01],
  34 + [ 1.22908126e-01, 4.75360183e-02, 2.81624170e-01],
  35 + [ 1.29284984e-01, 4.72930838e-02, 2.90788012e-01],
  36 + [ 1.35778450e-01, 4.68563678e-02, 2.99776404e-01],
  37 + [ 1.42377819e-01, 4.62422566e-02, 3.08552910e-01],
  38 + [ 1.49072957e-01, 4.54676444e-02, 3.17085139e-01],
  39 + [ 1.55849711e-01, 4.45588056e-02, 3.25338414e-01],
  40 + [ 1.62688939e-01, 4.35542881e-02, 3.33276678e-01],
  41 + [ 1.69575148e-01, 4.24893149e-02, 3.40874188e-01],
  42 + [ 1.76493202e-01, 4.14017089e-02, 3.48110606e-01],
  43 + [ 1.83428775e-01, 4.03288858e-02, 3.54971391e-01],
  44 + [ 1.90367453e-01, 3.93088888e-02, 3.61446945e-01],
  45 + [ 1.97297425e-01, 3.84001825e-02, 3.67534629e-01],
  46 + [ 2.04209298e-01, 3.76322609e-02, 3.73237557e-01],
  47 + [ 2.11095463e-01, 3.70296488e-02, 3.78563264e-01],
  48 + [ 2.17948648e-01, 3.66146049e-02, 3.83522415e-01],
  49 + [ 2.24762908e-01, 3.64049901e-02, 3.88128944e-01],
  50 + [ 2.31538148e-01, 3.64052511e-02, 3.92400150e-01],
  51 + [ 2.38272961e-01, 3.66209949e-02, 3.96353388e-01],
  52 + [ 2.44966911e-01, 3.70545017e-02, 4.00006615e-01],
  53 + [ 2.51620354e-01, 3.77052832e-02, 4.03377897e-01],
  54 + [ 2.58234265e-01, 3.85706153e-02, 4.06485031e-01],
  55 + [ 2.64809649e-01, 3.96468666e-02, 4.09345373e-01],
  56 + [ 2.71346664e-01, 4.09215821e-02, 4.11976086e-01],
  57 + [ 2.77849829e-01, 4.23528741e-02, 4.14392106e-01],
  58 + [ 2.84321318e-01, 4.39325787e-02, 4.16607861e-01],
  59 + [ 2.90763373e-01, 4.56437598e-02, 4.18636756e-01],
  60 + [ 2.97178251e-01, 4.74700293e-02, 4.20491164e-01],
  61 + [ 3.03568182e-01, 4.93958927e-02, 4.22182449e-01],
  62 + [ 3.09935342e-01, 5.14069729e-02, 4.23720999e-01],
  63 + [ 3.16281835e-01, 5.34901321e-02, 4.25116277e-01],
  64 + [ 3.22609671e-01, 5.56335178e-02, 4.26376869e-01],
  65 + [ 3.28920763e-01, 5.78265505e-02, 4.27510546e-01],
  66 + [ 3.35216916e-01, 6.00598734e-02, 4.28524320e-01],
  67 + [ 3.41499828e-01, 6.23252772e-02, 4.29424503e-01],
  68 + [ 3.47771086e-01, 6.46156100e-02, 4.30216765e-01],
  69 + [ 3.54032169e-01, 6.69246832e-02, 4.30906186e-01],
  70 + [ 3.60284449e-01, 6.92471753e-02, 4.31497309e-01],
  71 + [ 3.66529195e-01, 7.15785403e-02, 4.31994185e-01],
  72 + [ 3.72767575e-01, 7.39149211e-02, 4.32400419e-01],
  73 + [ 3.79000659e-01, 7.62530701e-02, 4.32719214e-01],
  74 + [ 3.85228383e-01, 7.85914864e-02, 4.32954973e-01],
  75 + [ 3.91452659e-01, 8.09267058e-02, 4.33108763e-01],
  76 + [ 3.97674379e-01, 8.32568129e-02, 4.33182647e-01],
  77 + [ 4.03894278e-01, 8.55803445e-02, 4.33178526e-01],
  78 + [ 4.10113015e-01, 8.78961593e-02, 4.33098056e-01],
  79 + [ 4.16331169e-01, 9.02033992e-02, 4.32942678e-01],
  80 + [ 4.22549249e-01, 9.25014543e-02, 4.32713635e-01],
  81 + [ 4.28767696e-01, 9.47899342e-02, 4.32411996e-01],
  82 + [ 4.34986885e-01, 9.70686417e-02, 4.32038673e-01],
  83 + [ 4.41207124e-01, 9.93375510e-02, 4.31594438e-01],
  84 + [ 4.47428382e-01, 1.01597079e-01, 4.31080497e-01],
  85 + [ 4.53650614e-01, 1.03847716e-01, 4.30497898e-01],
  86 + [ 4.59874623e-01, 1.06089165e-01, 4.29845789e-01],
  87 + [ 4.66100494e-01, 1.08321923e-01, 4.29124507e-01],
  88 + [ 4.72328255e-01, 1.10546584e-01, 4.28334320e-01],
  89 + [ 4.78557889e-01, 1.12763831e-01, 4.27475431e-01],
  90 + [ 4.84789325e-01, 1.14974430e-01, 4.26547991e-01],
  91 + [ 4.91022448e-01, 1.17179219e-01, 4.25552106e-01],
  92 + [ 4.97257069e-01, 1.19379132e-01, 4.24487908e-01],
  93 + [ 5.03492698e-01, 1.21575414e-01, 4.23356110e-01],
  94 + [ 5.09729541e-01, 1.23768654e-01, 4.22155676e-01],
  95 + [ 5.15967304e-01, 1.25959947e-01, 4.20886594e-01],
  96 + [ 5.22205646e-01, 1.28150439e-01, 4.19548848e-01],
  97 + [ 5.28444192e-01, 1.30341324e-01, 4.18142411e-01],
  98 + [ 5.34682523e-01, 1.32533845e-01, 4.16667258e-01],
  99 + [ 5.40920186e-01, 1.34729286e-01, 4.15123366e-01],
  100 + [ 5.47156706e-01, 1.36928959e-01, 4.13510662e-01],
  101 + [ 5.53391649e-01, 1.39134147e-01, 4.11828882e-01],
  102 + [ 5.59624442e-01, 1.41346265e-01, 4.10078028e-01],
  103 + [ 5.65854477e-01, 1.43566769e-01, 4.08258132e-01],
  104 + [ 5.72081108e-01, 1.45797150e-01, 4.06369246e-01],
  105 + [ 5.78303656e-01, 1.48038934e-01, 4.04411444e-01],
  106 + [ 5.84521407e-01, 1.50293679e-01, 4.02384829e-01],
  107 + [ 5.90733615e-01, 1.52562977e-01, 4.00289528e-01],
  108 + [ 5.96939751e-01, 1.54848232e-01, 3.98124897e-01],
  109 + [ 6.03138930e-01, 1.57151161e-01, 3.95891308e-01],
  110 + [ 6.09330184e-01, 1.59473549e-01, 3.93589349e-01],
  111 + [ 6.15512627e-01, 1.61817111e-01, 3.91219295e-01],
  112 + [ 6.21685340e-01, 1.64183582e-01, 3.88781456e-01],
  113 + [ 6.27847374e-01, 1.66574724e-01, 3.86276180e-01],
  114 + [ 6.33997746e-01, 1.68992314e-01, 3.83703854e-01],
  115 + [ 6.40135447e-01, 1.71438150e-01, 3.81064906e-01],
  116 + [ 6.46259648e-01, 1.73913876e-01, 3.78358969e-01],
  117 + [ 6.52369348e-01, 1.76421271e-01, 3.75586209e-01],
  118 + [ 6.58463166e-01, 1.78962399e-01, 3.72748214e-01],
  119 + [ 6.64539964e-01, 1.81539111e-01, 3.69845599e-01],
  120 + [ 6.70598572e-01, 1.84153268e-01, 3.66879025e-01],
  121 + [ 6.76637795e-01, 1.86806728e-01, 3.63849195e-01],
  122 + [ 6.82656407e-01, 1.89501352e-01, 3.60756856e-01],
  123 + [ 6.88653158e-01, 1.92238994e-01, 3.57602797e-01],
  124 + [ 6.94626769e-01, 1.95021500e-01, 3.54387853e-01],
  125 + [ 7.00575937e-01, 1.97850703e-01, 3.51112900e-01],
  126 + [ 7.06499709e-01, 2.00728196e-01, 3.47776863e-01],
  127 + [ 7.12396345e-01, 2.03656029e-01, 3.44382594e-01],
  128 + [ 7.18264447e-01, 2.06635993e-01, 3.40931208e-01],
  129 + [ 7.24102613e-01, 2.09669834e-01, 3.37423766e-01],
  130 + [ 7.29909422e-01, 2.12759270e-01, 3.33861367e-01],
  131 + [ 7.35683432e-01, 2.15905976e-01, 3.30245147e-01],
  132 + [ 7.41423185e-01, 2.19111589e-01, 3.26576275e-01],
  133 + [ 7.47127207e-01, 2.22377697e-01, 3.22855952e-01],
  134 + [ 7.52794009e-01, 2.25705837e-01, 3.19085410e-01],
  135 + [ 7.58422090e-01, 2.29097492e-01, 3.15265910e-01],
  136 + [ 7.64009940e-01, 2.32554083e-01, 3.11398734e-01],
  137 + [ 7.69556038e-01, 2.36076967e-01, 3.07485188e-01],
  138 + [ 7.75058888e-01, 2.39667435e-01, 3.03526312e-01],
  139 + [ 7.80517023e-01, 2.43326720e-01, 2.99522665e-01],
  140 + [ 7.85928794e-01, 2.47055968e-01, 2.95476756e-01],
  141 + [ 7.91292674e-01, 2.50856232e-01, 2.91389943e-01],
  142 + [ 7.96607144e-01, 2.54728485e-01, 2.87263585e-01],
  143 + [ 8.01870689e-01, 2.58673610e-01, 2.83099033e-01],
  144 + [ 8.07081807e-01, 2.62692401e-01, 2.78897629e-01],
  145 + [ 8.12239008e-01, 2.66785558e-01, 2.74660698e-01],
  146 + [ 8.17340818e-01, 2.70953688e-01, 2.70389545e-01],
  147 + [ 8.22385784e-01, 2.75197300e-01, 2.66085445e-01],
  148 + [ 8.27372474e-01, 2.79516805e-01, 2.61749643e-01],
  149 + [ 8.32299481e-01, 2.83912516e-01, 2.57383341e-01],
  150 + [ 8.37165425e-01, 2.88384647e-01, 2.52987700e-01],
  151 + [ 8.41968959e-01, 2.92933312e-01, 2.48563825e-01],
  152 + [ 8.46708768e-01, 2.97558528e-01, 2.44112767e-01],
  153 + [ 8.51383572e-01, 3.02260213e-01, 2.39635512e-01],
  154 + [ 8.55992130e-01, 3.07038188e-01, 2.35132978e-01],
  155 + [ 8.60533241e-01, 3.11892183e-01, 2.30606009e-01],
  156 + [ 8.65005747e-01, 3.16821833e-01, 2.26055368e-01],
  157 + [ 8.69408534e-01, 3.21826685e-01, 2.21481734e-01],
  158 + [ 8.73740530e-01, 3.26906201e-01, 2.16885699e-01],
  159 + [ 8.78000715e-01, 3.32059760e-01, 2.12267762e-01],
  160 + [ 8.82188112e-01, 3.37286663e-01, 2.07628326e-01],
  161 + [ 8.86301795e-01, 3.42586137e-01, 2.02967696e-01],
  162 + [ 8.90340885e-01, 3.47957340e-01, 1.98286080e-01],
  163 + [ 8.94304553e-01, 3.53399363e-01, 1.93583583e-01],
  164 + [ 8.98192017e-01, 3.58911240e-01, 1.88860212e-01],
  165 + [ 9.02002544e-01, 3.64491949e-01, 1.84115876e-01],
  166 + [ 9.05735448e-01, 3.70140419e-01, 1.79350388e-01],
  167 + [ 9.09390090e-01, 3.75855533e-01, 1.74563472e-01],
  168 + [ 9.12965874e-01, 3.81636138e-01, 1.69754764e-01],
  169 + [ 9.16462251e-01, 3.87481044e-01, 1.64923826e-01],
  170 + [ 9.19878710e-01, 3.93389034e-01, 1.60070152e-01],
  171 + [ 9.23214783e-01, 3.99358867e-01, 1.55193185e-01],
  172 + [ 9.26470039e-01, 4.05389282e-01, 1.50292329e-01],
  173 + [ 9.29644083e-01, 4.11479007e-01, 1.45366973e-01],
  174 + [ 9.32736555e-01, 4.17626756e-01, 1.40416519e-01],
  175 + [ 9.35747126e-01, 4.23831237e-01, 1.35440416e-01],
  176 + [ 9.38675494e-01, 4.30091162e-01, 1.30438175e-01],
  177 + [ 9.41521384e-01, 4.36405243e-01, 1.25409440e-01],
  178 + [ 9.44284543e-01, 4.42772199e-01, 1.20354038e-01],
  179 + [ 9.46964741e-01, 4.49190757e-01, 1.15272059e-01],
  180 + [ 9.49561766e-01, 4.55659658e-01, 1.10163947e-01],
  181 + [ 9.52075421e-01, 4.62177656e-01, 1.05030614e-01],
  182 + [ 9.54505523e-01, 4.68743522e-01, 9.98735931e-02],
  183 + [ 9.56851903e-01, 4.75356048e-01, 9.46952268e-02],
  184 + [ 9.59114397e-01, 4.82014044e-01, 8.94989073e-02],
  185 + [ 9.61292850e-01, 4.88716345e-01, 8.42893891e-02],
  186 + [ 9.63387110e-01, 4.95461806e-01, 7.90731907e-02],
  187 + [ 9.65397031e-01, 5.02249309e-01, 7.38591143e-02],
  188 + [ 9.67322465e-01, 5.09077761e-01, 6.86589199e-02],
  189 + [ 9.69163264e-01, 5.15946092e-01, 6.34881971e-02],
  190 + [ 9.70919277e-01, 5.22853259e-01, 5.83674890e-02],
  191 + [ 9.72590351e-01, 5.29798246e-01, 5.33237243e-02],
  192 + [ 9.74176327e-01, 5.36780059e-01, 4.83920090e-02],
  193 + [ 9.75677038e-01, 5.43797733e-01, 4.36177922e-02],
  194 + [ 9.77092313e-01, 5.50850323e-01, 3.90500131e-02],
  195 + [ 9.78421971e-01, 5.57936911e-01, 3.49306227e-02],
  196 + [ 9.79665824e-01, 5.65056600e-01, 3.14091591e-02],
  197 + [ 9.80823673e-01, 5.72208516e-01, 2.85075931e-02],
  198 + [ 9.81895311e-01, 5.79391803e-01, 2.62497353e-02],
  199 + [ 9.82880522e-01, 5.86605627e-01, 2.46613416e-02],
  200 + [ 9.83779081e-01, 5.93849168e-01, 2.37702263e-02],
  201 + [ 9.84590755e-01, 6.01121626e-01, 2.36063833e-02],
  202 + [ 9.85315301e-01, 6.08422211e-01, 2.42021174e-02],
  203 + [ 9.85952471e-01, 6.15750147e-01, 2.55921853e-02],
  204 + [ 9.86502013e-01, 6.23104667e-01, 2.78139496e-02],
  205 + [ 9.86963670e-01, 6.30485011e-01, 3.09075459e-02],
  206 + [ 9.87337182e-01, 6.37890424e-01, 3.49160639e-02],
  207 + [ 9.87622296e-01, 6.45320152e-01, 3.98857472e-02],
  208 + [ 9.87818759e-01, 6.52773439e-01, 4.55808037e-02],
  209 + [ 9.87926330e-01, 6.60249526e-01, 5.17503867e-02],
  210 + [ 9.87944783e-01, 6.67747641e-01, 5.83286889e-02],
  211 + [ 9.87873910e-01, 6.75267000e-01, 6.52570167e-02],
  212 + [ 9.87713535e-01, 6.82806802e-01, 7.24892330e-02],
  213 + [ 9.87463516e-01, 6.90366218e-01, 7.99897176e-02],
  214 + [ 9.87123759e-01, 6.97944391e-01, 8.77314215e-02],
  215 + [ 9.86694229e-01, 7.05540424e-01, 9.56941797e-02],
  216 + [ 9.86174970e-01, 7.13153375e-01, 1.03863324e-01],
  217 + [ 9.85565739e-01, 7.20782460e-01, 1.12228756e-01],
  218 + [ 9.84865203e-01, 7.28427497e-01, 1.20784651e-01],
  219 + [ 9.84075129e-01, 7.36086521e-01, 1.29526579e-01],
  220 + [ 9.83195992e-01, 7.43758326e-01, 1.38453063e-01],
  221 + [ 9.82228463e-01, 7.51441596e-01, 1.47564573e-01],
  222 + [ 9.81173457e-01, 7.59134892e-01, 1.56863224e-01],
  223 + [ 9.80032178e-01, 7.66836624e-01, 1.66352544e-01],
  224 + [ 9.78806183e-01, 7.74545028e-01, 1.76037298e-01],
  225 + [ 9.77497453e-01, 7.82258138e-01, 1.85923357e-01],
  226 + [ 9.76108474e-01, 7.89973753e-01, 1.96017589e-01],
  227 + [ 9.74637842e-01, 7.97691563e-01, 2.06331925e-01],
  228 + [ 9.73087939e-01, 8.05409333e-01, 2.16876839e-01],
  229 + [ 9.71467822e-01, 8.13121725e-01, 2.27658046e-01],
  230 + [ 9.69783146e-01, 8.20825143e-01, 2.38685942e-01],
  231 + [ 9.68040817e-01, 8.28515491e-01, 2.49971582e-01],
  232 + [ 9.66242589e-01, 8.36190976e-01, 2.61533898e-01],
  233 + [ 9.64393924e-01, 8.43848069e-01, 2.73391112e-01],
  234 + [ 9.62516656e-01, 8.51476340e-01, 2.85545675e-01],
  235 + [ 9.60625545e-01, 8.59068716e-01, 2.98010219e-01],
  236 + [ 9.58720088e-01, 8.66624355e-01, 3.10820466e-01],
  237 + [ 9.56834075e-01, 8.74128569e-01, 3.23973947e-01],
  238 + [ 9.54997177e-01, 8.81568926e-01, 3.37475479e-01],
  239 + [ 9.53215092e-01, 8.88942277e-01, 3.51368713e-01],
  240 + [ 9.51546225e-01, 8.96225909e-01, 3.65627005e-01],
  241 + [ 9.50018481e-01, 9.03409063e-01, 3.80271225e-01],
  242 + [ 9.48683391e-01, 9.10472964e-01, 3.95289169e-01],
  243 + [ 9.47594362e-01, 9.17399053e-01, 4.10665194e-01],
  244 + [ 9.46809163e-01, 9.24168246e-01, 4.26373236e-01],
  245 + [ 9.46391536e-01, 9.30760752e-01, 4.42367495e-01],
  246 + [ 9.46402951e-01, 9.37158971e-01, 4.58591507e-01],
  247 + [ 9.46902568e-01, 9.43347775e-01, 4.74969778e-01],
  248 + [ 9.47936825e-01, 9.49317522e-01, 4.91426053e-01],
  249 + [ 9.49544830e-01, 9.55062900e-01, 5.07859649e-01],
  250 + [ 9.51740304e-01, 9.60586693e-01, 5.24203026e-01],
  251 + [ 9.54529281e-01, 9.65895868e-01, 5.40360752e-01],
  252 + [ 9.57896053e-01, 9.71003330e-01, 5.56275090e-01],
  253 + [ 9.61812020e-01, 9.75924241e-01, 5.71925382e-01],
  254 + [ 9.66248822e-01, 9.80678193e-01, 5.87205773e-01],
  255 + [ 9.71161622e-01, 9.85282161e-01, 6.02154330e-01],
  256 + [ 9.76510983e-01, 9.89753437e-01, 6.16760413e-01],
  257 + [ 9.82257307e-01, 9.94108844e-01, 6.31017009e-01],
  258 + [ 9.88362068e-01, 9.98364143e-01, 6.44924005e-01]];
  259 +if nargin < 1
  260 + cm_data = cm;
  261 +else
  262 + hsv=rgb2hsv(cm);
  263 + hsv(144:end,1)=hsv(144:end,1)+1; % hardcoded
  264 + cm_data=interp1(linspace(0,1,size(cm,1)),hsv,linspace(0,1,m));
  265 + cm_data(cm_data(:,1)>1,1)=cm_data(cm_data(:,1)>1,1)-1;
  266 + cm_data=hsv2rgb(cm_data);
  267 +
  268 +end
  269 +end
0 \ No newline at end of file 270 \ No newline at end of file
matlab/magma.m 0 → 100644
  1 +function [cm_data]=magma(m)
  2 +
  3 +cm = [[ 1.46159096e-03, 4.66127766e-04, 1.38655200e-02],
  4 + [ 2.25764007e-03, 1.29495431e-03, 1.83311461e-02],
  5 + [ 3.27943222e-03, 2.30452991e-03, 2.37083291e-02],
  6 + [ 4.51230222e-03, 3.49037666e-03, 2.99647059e-02],
  7 + [ 5.94976987e-03, 4.84285000e-03, 3.71296695e-02],
  8 + [ 7.58798550e-03, 6.35613622e-03, 4.49730774e-02],
  9 + [ 9.42604390e-03, 8.02185006e-03, 5.28443561e-02],
  10 + [ 1.14654337e-02, 9.82831486e-03, 6.07496380e-02],
  11 + [ 1.37075706e-02, 1.17705913e-02, 6.86665843e-02],
  12 + [ 1.61557566e-02, 1.38404966e-02, 7.66026660e-02],
  13 + [ 1.88153670e-02, 1.60262753e-02, 8.45844897e-02],
  14 + [ 2.16919340e-02, 1.83201254e-02, 9.26101050e-02],
  15 + [ 2.47917814e-02, 2.07147875e-02, 1.00675555e-01],
  16 + [ 2.81228154e-02, 2.32009284e-02, 1.08786954e-01],
  17 + [ 3.16955304e-02, 2.57651161e-02, 1.16964722e-01],
  18 + [ 3.55204468e-02, 2.83974570e-02, 1.25209396e-01],
  19 + [ 3.96084872e-02, 3.10895652e-02, 1.33515085e-01],
  20 + [ 4.38295350e-02, 3.38299885e-02, 1.41886249e-01],
  21 + [ 4.80616391e-02, 3.66066101e-02, 1.50326989e-01],
  22 + [ 5.23204388e-02, 3.94066020e-02, 1.58841025e-01],
  23 + [ 5.66148978e-02, 4.21598925e-02, 1.67445592e-01],
  24 + [ 6.09493930e-02, 4.47944924e-02, 1.76128834e-01],
  25 + [ 6.53301801e-02, 4.73177796e-02, 1.84891506e-01],
  26 + [ 6.97637296e-02, 4.97264666e-02, 1.93735088e-01],
  27 + [ 7.42565152e-02, 5.20167766e-02, 2.02660374e-01],
  28 + [ 7.88150034e-02, 5.41844801e-02, 2.11667355e-01],
  29 + [ 8.34456313e-02, 5.62249365e-02, 2.20755099e-01],
  30 + [ 8.81547730e-02, 5.81331465e-02, 2.29921611e-01],
  31 + [ 9.29486914e-02, 5.99038167e-02, 2.39163669e-01],
  32 + [ 9.78334770e-02, 6.15314414e-02, 2.48476662e-01],
  33 + [ 1.02814972e-01, 6.30104053e-02, 2.57854400e-01],
  34 + [ 1.07898679e-01, 6.43351102e-02, 2.67288933e-01],
  35 + [ 1.13094451e-01, 6.54920358e-02, 2.76783978e-01],
  36 + [ 1.18405035e-01, 6.64791593e-02, 2.86320656e-01],
  37 + [ 1.23832651e-01, 6.72946449e-02, 2.95879431e-01],
  38 + [ 1.29380192e-01, 6.79349264e-02, 3.05442931e-01],
  39 + [ 1.35053322e-01, 6.83912798e-02, 3.14999890e-01],
  40 + [ 1.40857952e-01, 6.86540710e-02, 3.24537640e-01],
  41 + [ 1.46785234e-01, 6.87382323e-02, 3.34011109e-01],
  42 + [ 1.52839217e-01, 6.86368599e-02, 3.43404450e-01],
  43 + [ 1.59017511e-01, 6.83540225e-02, 3.52688028e-01],
  44 + [ 1.65308131e-01, 6.79108689e-02, 3.61816426e-01],
  45 + [ 1.71713033e-01, 6.73053260e-02, 3.70770827e-01],
  46 + [ 1.78211730e-01, 6.65758073e-02, 3.79497161e-01],
  47 + [ 1.84800877e-01, 6.57324381e-02, 3.87972507e-01],
  48 + [ 1.91459745e-01, 6.48183312e-02, 3.96151969e-01],
  49 + [ 1.98176877e-01, 6.38624166e-02, 4.04008953e-01],
  50 + [ 2.04934882e-01, 6.29066192e-02, 4.11514273e-01],
  51 + [ 2.11718061e-01, 6.19917876e-02, 4.18646741e-01],
  52 + [ 2.18511590e-01, 6.11584918e-02, 4.25391816e-01],
  53 + [ 2.25302032e-01, 6.04451843e-02, 4.31741767e-01],
  54 + [ 2.32076515e-01, 5.98886855e-02, 4.37694665e-01],
  55 + [ 2.38825991e-01, 5.95170384e-02, 4.43255999e-01],
  56 + [ 2.45543175e-01, 5.93524384e-02, 4.48435938e-01],
  57 + [ 2.52220252e-01, 5.94147119e-02, 4.53247729e-01],
  58 + [ 2.58857304e-01, 5.97055998e-02, 4.57709924e-01],
  59 + [ 2.65446744e-01, 6.02368754e-02, 4.61840297e-01],
  60 + [ 2.71994089e-01, 6.09935552e-02, 4.65660375e-01],
  61 + [ 2.78493300e-01, 6.19778136e-02, 4.69190328e-01],
  62 + [ 2.84951097e-01, 6.31676261e-02, 4.72450879e-01],
  63 + [ 2.91365817e-01, 6.45534486e-02, 4.75462193e-01],
  64 + [ 2.97740413e-01, 6.61170432e-02, 4.78243482e-01],
  65 + [ 3.04080941e-01, 6.78353452e-02, 4.80811572e-01],
  66 + [ 3.10382027e-01, 6.97024767e-02, 4.83186340e-01],
  67 + [ 3.16654235e-01, 7.16895272e-02, 4.85380429e-01],
  68 + [ 3.22899126e-01, 7.37819504e-02, 4.87408399e-01],
  69 + [ 3.29114038e-01, 7.59715081e-02, 4.89286796e-01],
  70 + [ 3.35307503e-01, 7.82361045e-02, 4.91024144e-01],
  71 + [ 3.41481725e-01, 8.05635079e-02, 4.92631321e-01],
  72 + [ 3.47635742e-01, 8.29463512e-02, 4.94120923e-01],
  73 + [ 3.53773161e-01, 8.53726329e-02, 4.95501096e-01],
  74 + [ 3.59897941e-01, 8.78311772e-02, 4.96778331e-01],
  75 + [ 3.66011928e-01, 9.03143031e-02, 4.97959963e-01],
  76 + [ 3.72116205e-01, 9.28159917e-02, 4.99053326e-01],
  77 + [ 3.78210547e-01, 9.53322947e-02, 5.00066568e-01],
  78 + [ 3.84299445e-01, 9.78549106e-02, 5.01001964e-01],
  79 + [ 3.90384361e-01, 1.00379466e-01, 5.01864236e-01],
  80 + [ 3.96466670e-01, 1.02902194e-01, 5.02657590e-01],
  81 + [ 4.02547663e-01, 1.05419865e-01, 5.03385761e-01],
  82 + [ 4.08628505e-01, 1.07929771e-01, 5.04052118e-01],
  83 + [ 4.14708664e-01, 1.10431177e-01, 5.04661843e-01],
  84 + [ 4.20791157e-01, 1.12920210e-01, 5.05214935e-01],
  85 + [ 4.26876965e-01, 1.15395258e-01, 5.05713602e-01],
  86 + [ 4.32967001e-01, 1.17854987e-01, 5.06159754e-01],
  87 + [ 4.39062114e-01, 1.20298314e-01, 5.06555026e-01],
  88 + [ 4.45163096e-01, 1.22724371e-01, 5.06900806e-01],
  89 + [ 4.51270678e-01, 1.25132484e-01, 5.07198258e-01],
  90 + [ 4.57385535e-01, 1.27522145e-01, 5.07448336e-01],
  91 + [ 4.63508291e-01, 1.29892998e-01, 5.07651812e-01],
  92 + [ 4.69639514e-01, 1.32244819e-01, 5.07809282e-01],
  93 + [ 4.75779723e-01, 1.34577500e-01, 5.07921193e-01],
  94 + [ 4.81928997e-01, 1.36891390e-01, 5.07988509e-01],
  95 + [ 4.88088169e-01, 1.39186217e-01, 5.08010737e-01],
  96 + [ 4.94257673e-01, 1.41462106e-01, 5.07987836e-01],
  97 + [ 5.00437834e-01, 1.43719323e-01, 5.07919772e-01],
  98 + [ 5.06628929e-01, 1.45958202e-01, 5.07806420e-01],
  99 + [ 5.12831195e-01, 1.48179144e-01, 5.07647570e-01],
  100 + [ 5.19044825e-01, 1.50382611e-01, 5.07442938e-01],
  101 + [ 5.25269968e-01, 1.52569121e-01, 5.07192172e-01],
  102 + [ 5.31506735e-01, 1.54739247e-01, 5.06894860e-01],
  103 + [ 5.37755194e-01, 1.56893613e-01, 5.06550538e-01],
  104 + [ 5.44015371e-01, 1.59032895e-01, 5.06158696e-01],
  105 + [ 5.50287252e-01, 1.61157816e-01, 5.05718782e-01],
  106 + [ 5.56570783e-01, 1.63269149e-01, 5.05230210e-01],
  107 + [ 5.62865867e-01, 1.65367714e-01, 5.04692365e-01],
  108 + [ 5.69172368e-01, 1.67454379e-01, 5.04104606e-01],
  109 + [ 5.75490107e-01, 1.69530062e-01, 5.03466273e-01],
  110 + [ 5.81818864e-01, 1.71595728e-01, 5.02776690e-01],
  111 + [ 5.88158375e-01, 1.73652392e-01, 5.02035167e-01],
  112 + [ 5.94508337e-01, 1.75701122e-01, 5.01241011e-01],
  113 + [ 6.00868399e-01, 1.77743036e-01, 5.00393522e-01],
  114 + [ 6.07238169e-01, 1.79779309e-01, 4.99491999e-01],
  115 + [ 6.13617209e-01, 1.81811170e-01, 4.98535746e-01],
  116 + [ 6.20005032e-01, 1.83839907e-01, 4.97524075e-01],
  117 + [ 6.26401108e-01, 1.85866869e-01, 4.96456304e-01],
  118 + [ 6.32804854e-01, 1.87893468e-01, 4.95331769e-01],
  119 + [ 6.39215638e-01, 1.89921182e-01, 4.94149821e-01],
  120 + [ 6.45632778e-01, 1.91951556e-01, 4.92909832e-01],
  121 + [ 6.52055535e-01, 1.93986210e-01, 4.91611196e-01],
  122 + [ 6.58483116e-01, 1.96026835e-01, 4.90253338e-01],
  123 + [ 6.64914668e-01, 1.98075202e-01, 4.88835712e-01],
  124 + [ 6.71349279e-01, 2.00133166e-01, 4.87357807e-01],
  125 + [ 6.77785975e-01, 2.02202663e-01, 4.85819154e-01],
  126 + [ 6.84223712e-01, 2.04285721e-01, 4.84219325e-01],
  127 + [ 6.90661380e-01, 2.06384461e-01, 4.82557941e-01],
  128 + [ 6.97097796e-01, 2.08501100e-01, 4.80834678e-01],
  129 + [ 7.03531700e-01, 2.10637956e-01, 4.79049270e-01],
  130 + [ 7.09961888e-01, 2.12797337e-01, 4.77201121e-01],
  131 + [ 7.16387038e-01, 2.14981693e-01, 4.75289780e-01],
  132 + [ 7.22805451e-01, 2.17193831e-01, 4.73315708e-01],
  133 + [ 7.29215521e-01, 2.19436516e-01, 4.71278924e-01],
  134 + [ 7.35615545e-01, 2.21712634e-01, 4.69179541e-01],
  135 + [ 7.42003713e-01, 2.24025196e-01, 4.67017774e-01],
  136 + [ 7.48378107e-01, 2.26377345e-01, 4.64793954e-01],
  137 + [ 7.54736692e-01, 2.28772352e-01, 4.62508534e-01],
  138 + [ 7.61077312e-01, 2.31213625e-01, 4.60162106e-01],
  139 + [ 7.67397681e-01, 2.33704708e-01, 4.57755411e-01],
  140 + [ 7.73695380e-01, 2.36249283e-01, 4.55289354e-01],
  141 + [ 7.79967847e-01, 2.38851170e-01, 4.52765022e-01],
  142 + [ 7.86212372e-01, 2.41514325e-01, 4.50183695e-01],
  143 + [ 7.92426972e-01, 2.44242250e-01, 4.47543155e-01],
  144 + [ 7.98607760e-01, 2.47039798e-01, 4.44848441e-01],
  145 + [ 8.04751511e-01, 2.49911350e-01, 4.42101615e-01],
  146 + [ 8.10854841e-01, 2.52861399e-01, 4.39304963e-01],
  147 + [ 8.16914186e-01, 2.55894550e-01, 4.36461074e-01],
  148 + [ 8.22925797e-01, 2.59015505e-01, 4.33572874e-01],
  149 + [ 8.28885740e-01, 2.62229049e-01, 4.30643647e-01],
  150 + [ 8.34790818e-01, 2.65539703e-01, 4.27671352e-01],
  151 + [ 8.40635680e-01, 2.68952874e-01, 4.24665620e-01],
  152 + [ 8.46415804e-01, 2.72473491e-01, 4.21631064e-01],
  153 + [ 8.52126490e-01, 2.76106469e-01, 4.18572767e-01],
  154 + [ 8.57762870e-01, 2.79856666e-01, 4.15496319e-01],
  155 + [ 8.63320397e-01, 2.83729003e-01, 4.12402889e-01],
  156 + [ 8.68793368e-01, 2.87728205e-01, 4.09303002e-01],
  157 + [ 8.74176342e-01, 2.91858679e-01, 4.06205397e-01],
  158 + [ 8.79463944e-01, 2.96124596e-01, 4.03118034e-01],
  159 + [ 8.84650824e-01, 3.00530090e-01, 4.00047060e-01],
  160 + [ 8.89731418e-01, 3.05078817e-01, 3.97001559e-01],
  161 + [ 8.94700194e-01, 3.09773445e-01, 3.93994634e-01],
  162 + [ 8.99551884e-01, 3.14616425e-01, 3.91036674e-01],
  163 + [ 9.04281297e-01, 3.19609981e-01, 3.88136889e-01],
  164 + [ 9.08883524e-01, 3.24755126e-01, 3.85308008e-01],
  165 + [ 9.13354091e-01, 3.30051947e-01, 3.82563414e-01],
  166 + [ 9.17688852e-01, 3.35500068e-01, 3.79915138e-01],
  167 + [ 9.21884187e-01, 3.41098112e-01, 3.77375977e-01],
  168 + [ 9.25937102e-01, 3.46843685e-01, 3.74959077e-01],
  169 + [ 9.29845090e-01, 3.52733817e-01, 3.72676513e-01],
  170 + [ 9.33606454e-01, 3.58764377e-01, 3.70540883e-01],
  171 + [ 9.37220874e-01, 3.64929312e-01, 3.68566525e-01],
  172 + [ 9.40687443e-01, 3.71224168e-01, 3.66761699e-01],
  173 + [ 9.44006448e-01, 3.77642889e-01, 3.65136328e-01],
  174 + [ 9.47179528e-01, 3.84177874e-01, 3.63701130e-01],
  175 + [ 9.50210150e-01, 3.90819546e-01, 3.62467694e-01],
  176 + [ 9.53099077e-01, 3.97562894e-01, 3.61438431e-01],
  177 + [ 9.55849237e-01, 4.04400213e-01, 3.60619076e-01],
  178 + [ 9.58464079e-01, 4.11323666e-01, 3.60014232e-01],
  179 + [ 9.60949221e-01, 4.18323245e-01, 3.59629789e-01],
  180 + [ 9.63310281e-01, 4.25389724e-01, 3.59469020e-01],
  181 + [ 9.65549351e-01, 4.32518707e-01, 3.59529151e-01],
  182 + [ 9.67671128e-01, 4.39702976e-01, 3.59810172e-01],
  183 + [ 9.69680441e-01, 4.46935635e-01, 3.60311120e-01],
  184 + [ 9.71582181e-01, 4.54210170e-01, 3.61030156e-01],
  185 + [ 9.73381238e-01, 4.61520484e-01, 3.61964652e-01],
  186 + [ 9.75082439e-01, 4.68860936e-01, 3.63111292e-01],
  187 + [ 9.76690494e-01, 4.76226350e-01, 3.64466162e-01],
  188 + [ 9.78209957e-01, 4.83612031e-01, 3.66024854e-01],
  189 + [ 9.79645181e-01, 4.91013764e-01, 3.67782559e-01],
  190 + [ 9.81000291e-01, 4.98427800e-01, 3.69734157e-01],
  191 + [ 9.82279159e-01, 5.05850848e-01, 3.71874301e-01],
  192 + [ 9.83485387e-01, 5.13280054e-01, 3.74197501e-01],
  193 + [ 9.84622298e-01, 5.20712972e-01, 3.76698186e-01],
  194 + [ 9.85692925e-01, 5.28147545e-01, 3.79370774e-01],
  195 + [ 9.86700017e-01, 5.35582070e-01, 3.82209724e-01],
  196 + [ 9.87646038e-01, 5.43015173e-01, 3.85209578e-01],
  197 + [ 9.88533173e-01, 5.50445778e-01, 3.88365009e-01],
  198 + [ 9.89363341e-01, 5.57873075e-01, 3.91670846e-01],
  199 + [ 9.90138201e-01, 5.65296495e-01, 3.95122099e-01],
  200 + [ 9.90871208e-01, 5.72706259e-01, 3.98713971e-01],
  201 + [ 9.91558165e-01, 5.80106828e-01, 4.02441058e-01],
  202 + [ 9.92195728e-01, 5.87501706e-01, 4.06298792e-01],
  203 + [ 9.92784669e-01, 5.94891088e-01, 4.10282976e-01],
  204 + [ 9.93325561e-01, 6.02275297e-01, 4.14389658e-01],
  205 + [ 9.93834412e-01, 6.09643540e-01, 4.18613221e-01],
  206 + [ 9.94308514e-01, 6.16998953e-01, 4.22949672e-01],
  207 + [ 9.94737698e-01, 6.24349657e-01, 4.27396771e-01],
  208 + [ 9.95121854e-01, 6.31696376e-01, 4.31951492e-01],
  209 + [ 9.95480469e-01, 6.39026596e-01, 4.36607159e-01],
  210 + [ 9.95809924e-01, 6.46343897e-01, 4.41360951e-01],
  211 + [ 9.96095703e-01, 6.53658756e-01, 4.46213021e-01],
  212 + [ 9.96341406e-01, 6.60969379e-01, 4.51160201e-01],
  213 + [ 9.96579803e-01, 6.68255621e-01, 4.56191814e-01],
  214 + [ 9.96774784e-01, 6.75541484e-01, 4.61314158e-01],
  215 + [ 9.96925427e-01, 6.82827953e-01, 4.66525689e-01],
  216 + [ 9.97077185e-01, 6.90087897e-01, 4.71811461e-01],
  217 + [ 9.97186253e-01, 6.97348991e-01, 4.77181727e-01],
  218 + [ 9.97253982e-01, 7.04610791e-01, 4.82634651e-01],
  219 + [ 9.97325180e-01, 7.11847714e-01, 4.88154375e-01],
  220 + [ 9.97350983e-01, 7.19089119e-01, 4.93754665e-01],
  221 + [ 9.97350583e-01, 7.26324415e-01, 4.99427972e-01],
  222 + [ 9.97341259e-01, 7.33544671e-01, 5.05166839e-01],
  223 + [ 9.97284689e-01, 7.40771893e-01, 5.10983331e-01],
  224 + [ 9.97228367e-01, 7.47980563e-01, 5.16859378e-01],
  225 + [ 9.97138480e-01, 7.55189852e-01, 5.22805996e-01],
  226 + [ 9.97019342e-01, 7.62397883e-01, 5.28820775e-01],
  227 + [ 9.96898254e-01, 7.69590975e-01, 5.34892341e-01],
  228 + [ 9.96726862e-01, 7.76794860e-01, 5.41038571e-01],
  229 + [ 9.96570645e-01, 7.83976508e-01, 5.47232992e-01],
  230 + [ 9.96369065e-01, 7.91167346e-01, 5.53498939e-01],
  231 + [ 9.96162309e-01, 7.98347709e-01, 5.59819643e-01],
  232 + [ 9.95932448e-01, 8.05527126e-01, 5.66201824e-01],
  233 + [ 9.95680107e-01, 8.12705773e-01, 5.72644795e-01],
  234 + [ 9.95423973e-01, 8.19875302e-01, 5.79140130e-01],
  235 + [ 9.95131288e-01, 8.27051773e-01, 5.85701463e-01],
  236 + [ 9.94851089e-01, 8.34212826e-01, 5.92307093e-01],
  237 + [ 9.94523666e-01, 8.41386618e-01, 5.98982818e-01],
  238 + [ 9.94221900e-01, 8.48540474e-01, 6.05695903e-01],
  239 + [ 9.93865767e-01, 8.55711038e-01, 6.12481798e-01],
  240 + [ 9.93545285e-01, 8.62858846e-01, 6.19299300e-01],
  241 + [ 9.93169558e-01, 8.70024467e-01, 6.26189463e-01],
  242 + [ 9.92830963e-01, 8.77168404e-01, 6.33109148e-01],
  243 + [ 9.92439881e-01, 8.84329694e-01, 6.40099465e-01],
  244 + [ 9.92089454e-01, 8.91469549e-01, 6.47116021e-01],
  245 + [ 9.91687744e-01, 8.98627050e-01, 6.54201544e-01],
  246 + [ 9.91331929e-01, 9.05762748e-01, 6.61308839e-01],
  247 + [ 9.90929685e-01, 9.12915010e-01, 6.68481201e-01],
  248 + [ 9.90569914e-01, 9.20048699e-01, 6.75674592e-01],
  249 + [ 9.90174637e-01, 9.27195612e-01, 6.82925602e-01],
  250 + [ 9.89814839e-01, 9.34328540e-01, 6.90198194e-01],
  251 + [ 9.89433736e-01, 9.41470354e-01, 6.97518628e-01],
  252 + [ 9.89077438e-01, 9.48604077e-01, 7.04862519e-01],
  253 + [ 9.88717064e-01, 9.55741520e-01, 7.12242232e-01],
  254 + [ 9.88367028e-01, 9.62878026e-01, 7.19648627e-01],
  255 + [ 9.88032885e-01, 9.70012413e-01, 7.27076773e-01],
  256 + [ 9.87690702e-01, 9.77154231e-01, 7.34536205e-01],
  257 + [ 9.87386827e-01, 9.84287561e-01, 7.42001547e-01],
  258 + [ 9.87052509e-01, 9.91437853e-01, 7.49504188e-01]];
  259 +
  260 +
  261 +if nargin < 1
  262 + cm_data = cm;
  263 +else
  264 + hsv=rgb2hsv(cm);
  265 + hsv(170:end,1)=hsv(170:end,1)+1; % hardcoded
  266 + cm_data=interp1(linspace(0,1,size(cm,1)),hsv,linspace(0,1,m));
  267 + cm_data(cm_data(:,1)>1,1)=cm_data(cm_data(:,1)>1,1)-1;
  268 + cm_data=hsv2rgb(cm_data);
  269 +
  270 +end
  271 +end
0 \ No newline at end of file 272 \ No newline at end of file
matlab/plasma.m 0 → 100644
  1 +function cm_data=plasma(m)
  2 +
  3 +cm = [[ 5.03832136e-02, 2.98028976e-02, 5.27974883e-01],
  4 + [ 6.35363639e-02, 2.84259729e-02, 5.33123681e-01],
  5 + [ 7.53531234e-02, 2.72063728e-02, 5.38007001e-01],
  6 + [ 8.62217979e-02, 2.61253206e-02, 5.42657691e-01],
  7 + [ 9.63786097e-02, 2.51650976e-02, 5.47103487e-01],
  8 + [ 1.05979704e-01, 2.43092436e-02, 5.51367851e-01],
  9 + [ 1.15123641e-01, 2.35562500e-02, 5.55467728e-01],
  10 + [ 1.23902903e-01, 2.28781011e-02, 5.59423480e-01],
  11 + [ 1.32380720e-01, 2.22583774e-02, 5.63250116e-01],
  12 + [ 1.40603076e-01, 2.16866674e-02, 5.66959485e-01],
  13 + [ 1.48606527e-01, 2.11535876e-02, 5.70561711e-01],
  14 + [ 1.56420649e-01, 2.06507174e-02, 5.74065446e-01],
  15 + [ 1.64069722e-01, 2.01705326e-02, 5.77478074e-01],
  16 + [ 1.71573925e-01, 1.97063415e-02, 5.80805890e-01],
  17 + [ 1.78950212e-01, 1.92522243e-02, 5.84054243e-01],
  18 + [ 1.86212958e-01, 1.88029767e-02, 5.87227661e-01],
  19 + [ 1.93374449e-01, 1.83540593e-02, 5.90329954e-01],
  20 + [ 2.00445260e-01, 1.79015512e-02, 5.93364304e-01],
  21 + [ 2.07434551e-01, 1.74421086e-02, 5.96333341e-01],
  22 + [ 2.14350298e-01, 1.69729276e-02, 5.99239207e-01],
  23 + [ 2.21196750e-01, 1.64970484e-02, 6.02083323e-01],
  24 + [ 2.27982971e-01, 1.60071509e-02, 6.04867403e-01],
  25 + [ 2.34714537e-01, 1.55015065e-02, 6.07592438e-01],
  26 + [ 2.41396253e-01, 1.49791041e-02, 6.10259089e-01],
  27 + [ 2.48032377e-01, 1.44393586e-02, 6.12867743e-01],
  28 + [ 2.54626690e-01, 1.38820918e-02, 6.15418537e-01],
  29 + [ 2.61182562e-01, 1.33075156e-02, 6.17911385e-01],
  30 + [ 2.67702993e-01, 1.27162163e-02, 6.20345997e-01],
  31 + [ 2.74190665e-01, 1.21091423e-02, 6.22721903e-01],
  32 + [ 2.80647969e-01, 1.14875915e-02, 6.25038468e-01],
  33 + [ 2.87076059e-01, 1.08554862e-02, 6.27294975e-01],
  34 + [ 2.93477695e-01, 1.02128849e-02, 6.29490490e-01],
  35 + [ 2.99855122e-01, 9.56079551e-03, 6.31623923e-01],
  36 + [ 3.06209825e-01, 8.90185346e-03, 6.33694102e-01],
  37 + [ 3.12543124e-01, 8.23900704e-03, 6.35699759e-01],
  38 + [ 3.18856183e-01, 7.57551051e-03, 6.37639537e-01],
  39 + [ 3.25150025e-01, 6.91491734e-03, 6.39512001e-01],
  40 + [ 3.31425547e-01, 6.26107379e-03, 6.41315649e-01],
  41 + [ 3.37683446e-01, 5.61830889e-03, 6.43048936e-01],
  42 + [ 3.43924591e-01, 4.99053080e-03, 6.44710195e-01],
  43 + [ 3.50149699e-01, 4.38202557e-03, 6.46297711e-01],
  44 + [ 3.56359209e-01, 3.79781761e-03, 6.47809772e-01],
  45 + [ 3.62553473e-01, 3.24319591e-03, 6.49244641e-01],
  46 + [ 3.68732762e-01, 2.72370721e-03, 6.50600561e-01],
  47 + [ 3.74897270e-01, 2.24514897e-03, 6.51875762e-01],
  48 + [ 3.81047116e-01, 1.81356205e-03, 6.53068467e-01],
  49 + [ 3.87182639e-01, 1.43446923e-03, 6.54176761e-01],
  50 + [ 3.93304010e-01, 1.11388259e-03, 6.55198755e-01],
  51 + [ 3.99410821e-01, 8.59420809e-04, 6.56132835e-01],
  52 + [ 4.05502914e-01, 6.78091517e-04, 6.56977276e-01],
  53 + [ 4.11580082e-01, 5.77101735e-04, 6.57730380e-01],
  54 + [ 4.17642063e-01, 5.63847476e-04, 6.58390492e-01],
  55 + [ 4.23688549e-01, 6.45902780e-04, 6.58956004e-01],
  56 + [ 4.29719186e-01, 8.31008207e-04, 6.59425363e-01],
  57 + [ 4.35733575e-01, 1.12705875e-03, 6.59797077e-01],
  58 + [ 4.41732123e-01, 1.53984779e-03, 6.60069009e-01],
  59 + [ 4.47713600e-01, 2.07954744e-03, 6.60240367e-01],
  60 + [ 4.53677394e-01, 2.75470302e-03, 6.60309966e-01],
  61 + [ 4.59622938e-01, 3.57374415e-03, 6.60276655e-01],
  62 + [ 4.65549631e-01, 4.54518084e-03, 6.60139383e-01],
  63 + [ 4.71456847e-01, 5.67758762e-03, 6.59897210e-01],
  64 + [ 4.77343929e-01, 6.97958743e-03, 6.59549311e-01],
  65 + [ 4.83210198e-01, 8.45983494e-03, 6.59094989e-01],
  66 + [ 4.89054951e-01, 1.01269996e-02, 6.58533677e-01],
  67 + [ 4.94877466e-01, 1.19897486e-02, 6.57864946e-01],
  68 + [ 5.00677687e-01, 1.40550640e-02, 6.57087561e-01],
  69 + [ 5.06454143e-01, 1.63333443e-02, 6.56202294e-01],
  70 + [ 5.12206035e-01, 1.88332232e-02, 6.55209222e-01],
  71 + [ 5.17932580e-01, 2.15631918e-02, 6.54108545e-01],
  72 + [ 5.23632990e-01, 2.45316468e-02, 6.52900629e-01],
  73 + [ 5.29306474e-01, 2.77468735e-02, 6.51586010e-01],
  74 + [ 5.34952244e-01, 3.12170300e-02, 6.50165396e-01],
  75 + [ 5.40569510e-01, 3.49501310e-02, 6.48639668e-01],
  76 + [ 5.46157494e-01, 3.89540334e-02, 6.47009884e-01],
  77 + [ 5.51715423e-01, 4.31364795e-02, 6.45277275e-01],
  78 + [ 5.57242538e-01, 4.73307585e-02, 6.43443250e-01],
  79 + [ 5.62738096e-01, 5.15448092e-02, 6.41509389e-01],
  80 + [ 5.68201372e-01, 5.57776706e-02, 6.39477440e-01],
  81 + [ 5.73631859e-01, 6.00281369e-02, 6.37348841e-01],
  82 + [ 5.79028682e-01, 6.42955547e-02, 6.35126108e-01],
  83 + [ 5.84391137e-01, 6.85790261e-02, 6.32811608e-01],
  84 + [ 5.89718606e-01, 7.28775875e-02, 6.30407727e-01],
  85 + [ 5.95010505e-01, 7.71902878e-02, 6.27916992e-01],
  86 + [ 6.00266283e-01, 8.15161895e-02, 6.25342058e-01],
  87 + [ 6.05485428e-01, 8.58543713e-02, 6.22685703e-01],
  88 + [ 6.10667469e-01, 9.02039303e-02, 6.19950811e-01],
  89 + [ 6.15811974e-01, 9.45639838e-02, 6.17140367e-01],
  90 + [ 6.20918555e-01, 9.89336721e-02, 6.14257440e-01],
  91 + [ 6.25986869e-01, 1.03312160e-01, 6.11305174e-01],
  92 + [ 6.31016615e-01, 1.07698641e-01, 6.08286774e-01],
  93 + [ 6.36007543e-01, 1.12092335e-01, 6.05205491e-01],
  94 + [ 6.40959444e-01, 1.16492495e-01, 6.02064611e-01],
  95 + [ 6.45872158e-01, 1.20898405e-01, 5.98867442e-01],
  96 + [ 6.50745571e-01, 1.25309384e-01, 5.95617300e-01],
  97 + [ 6.55579615e-01, 1.29724785e-01, 5.92317494e-01],
  98 + [ 6.60374266e-01, 1.34143997e-01, 5.88971318e-01],
  99 + [ 6.65129493e-01, 1.38566428e-01, 5.85582301e-01],
  100 + [ 6.69845385e-01, 1.42991540e-01, 5.82153572e-01],
  101 + [ 6.74522060e-01, 1.47418835e-01, 5.78688247e-01],
  102 + [ 6.79159664e-01, 1.51847851e-01, 5.75189431e-01],
  103 + [ 6.83758384e-01, 1.56278163e-01, 5.71660158e-01],
  104 + [ 6.88318440e-01, 1.60709387e-01, 5.68103380e-01],
  105 + [ 6.92840088e-01, 1.65141174e-01, 5.64521958e-01],
  106 + [ 6.97323615e-01, 1.69573215e-01, 5.60918659e-01],
  107 + [ 7.01769334e-01, 1.74005236e-01, 5.57296144e-01],
  108 + [ 7.06177590e-01, 1.78437000e-01, 5.53656970e-01],
  109 + [ 7.10548747e-01, 1.82868306e-01, 5.50003579e-01],
  110 + [ 7.14883195e-01, 1.87298986e-01, 5.46338299e-01],
  111 + [ 7.19181339e-01, 1.91728906e-01, 5.42663338e-01],
  112 + [ 7.23443604e-01, 1.96157962e-01, 5.38980786e-01],
  113 + [ 7.27670428e-01, 2.00586086e-01, 5.35292612e-01],
  114 + [ 7.31862231e-01, 2.05013174e-01, 5.31600995e-01],
  115 + [ 7.36019424e-01, 2.09439071e-01, 5.27908434e-01],
  116 + [ 7.40142557e-01, 2.13863965e-01, 5.24215533e-01],
  117 + [ 7.44232102e-01, 2.18287899e-01, 5.20523766e-01],
  118 + [ 7.48288533e-01, 2.22710942e-01, 5.16834495e-01],
  119 + [ 7.52312321e-01, 2.27133187e-01, 5.13148963e-01],
  120 + [ 7.56303937e-01, 2.31554749e-01, 5.09468305e-01],
  121 + [ 7.60263849e-01, 2.35975765e-01, 5.05793543e-01],
  122 + [ 7.64192516e-01, 2.40396394e-01, 5.02125599e-01],
  123 + [ 7.68090391e-01, 2.44816813e-01, 4.98465290e-01],
  124 + [ 7.71957916e-01, 2.49237220e-01, 4.94813338e-01],
  125 + [ 7.75795522e-01, 2.53657797e-01, 4.91170517e-01],
  126 + [ 7.79603614e-01, 2.58078397e-01, 4.87539124e-01],
  127 + [ 7.83382636e-01, 2.62499662e-01, 4.83917732e-01],
  128 + [ 7.87132978e-01, 2.66921859e-01, 4.80306702e-01],
  129 + [ 7.90855015e-01, 2.71345267e-01, 4.76706319e-01],
  130 + [ 7.94549101e-01, 2.75770179e-01, 4.73116798e-01],
  131 + [ 7.98215577e-01, 2.80196901e-01, 4.69538286e-01],
  132 + [ 8.01854758e-01, 2.84625750e-01, 4.65970871e-01],
  133 + [ 8.05466945e-01, 2.89057057e-01, 4.62414580e-01],
  134 + [ 8.09052419e-01, 2.93491117e-01, 4.58869577e-01],
  135 + [ 8.12611506e-01, 2.97927865e-01, 4.55337565e-01],
  136 + [ 8.16144382e-01, 3.02368130e-01, 4.51816385e-01],
  137 + [ 8.19651255e-01, 3.06812282e-01, 4.48305861e-01],
  138 + [ 8.23132309e-01, 3.11260703e-01, 4.44805781e-01],
  139 + [ 8.26587706e-01, 3.15713782e-01, 4.41315901e-01],
  140 + [ 8.30017584e-01, 3.20171913e-01, 4.37835947e-01],
  141 + [ 8.33422053e-01, 3.24635499e-01, 4.34365616e-01],
  142 + [ 8.36801237e-01, 3.29104836e-01, 4.30905052e-01],
  143 + [ 8.40155276e-01, 3.33580106e-01, 4.27454836e-01],
  144 + [ 8.43484103e-01, 3.38062109e-01, 4.24013059e-01],
  145 + [ 8.46787726e-01, 3.42551272e-01, 4.20579333e-01],
  146 + [ 8.50066132e-01, 3.47048028e-01, 4.17153264e-01],
  147 + [ 8.53319279e-01, 3.51552815e-01, 4.13734445e-01],
  148 + [ 8.56547103e-01, 3.56066072e-01, 4.10322469e-01],
  149 + [ 8.59749520e-01, 3.60588229e-01, 4.06916975e-01],
  150 + [ 8.62926559e-01, 3.65119408e-01, 4.03518809e-01],
  151 + [ 8.66077920e-01, 3.69660446e-01, 4.00126027e-01],
  152 + [ 8.69203436e-01, 3.74211795e-01, 3.96738211e-01],
  153 + [ 8.72302917e-01, 3.78773910e-01, 3.93354947e-01],
  154 + [ 8.75376149e-01, 3.83347243e-01, 3.89975832e-01],
  155 + [ 8.78422895e-01, 3.87932249e-01, 3.86600468e-01],
  156 + [ 8.81442916e-01, 3.92529339e-01, 3.83228622e-01],
  157 + [ 8.84435982e-01, 3.97138877e-01, 3.79860246e-01],
  158 + [ 8.87401682e-01, 4.01761511e-01, 3.76494232e-01],
  159 + [ 8.90339687e-01, 4.06397694e-01, 3.73130228e-01],
  160 + [ 8.93249647e-01, 4.11047871e-01, 3.69767893e-01],
  161 + [ 8.96131191e-01, 4.15712489e-01, 3.66406907e-01],
  162 + [ 8.98983931e-01, 4.20391986e-01, 3.63046965e-01],
  163 + [ 9.01807455e-01, 4.25086807e-01, 3.59687758e-01],
  164 + [ 9.04601295e-01, 4.29797442e-01, 3.56328796e-01],
  165 + [ 9.07364995e-01, 4.34524335e-01, 3.52969777e-01],
  166 + [ 9.10098088e-01, 4.39267908e-01, 3.49610469e-01],
  167 + [ 9.12800095e-01, 4.44028574e-01, 3.46250656e-01],
  168 + [ 9.15470518e-01, 4.48806744e-01, 3.42890148e-01],
  169 + [ 9.18108848e-01, 4.53602818e-01, 3.39528771e-01],
  170 + [ 9.20714383e-01, 4.58417420e-01, 3.36165582e-01],
  171 + [ 9.23286660e-01, 4.63250828e-01, 3.32800827e-01],
  172 + [ 9.25825146e-01, 4.68103387e-01, 3.29434512e-01],
  173 + [ 9.28329275e-01, 4.72975465e-01, 3.26066550e-01],
  174 + [ 9.30798469e-01, 4.77867420e-01, 3.22696876e-01],
  175 + [ 9.33232140e-01, 4.82779603e-01, 3.19325444e-01],
  176 + [ 9.35629684e-01, 4.87712357e-01, 3.15952211e-01],
  177 + [ 9.37990034e-01, 4.92666544e-01, 3.12575440e-01],
  178 + [ 9.40312939e-01, 4.97642038e-01, 3.09196628e-01],
  179 + [ 9.42597771e-01, 5.02639147e-01, 3.05815824e-01],
  180 + [ 9.44843893e-01, 5.07658169e-01, 3.02433101e-01],
  181 + [ 9.47050662e-01, 5.12699390e-01, 2.99048555e-01],
  182 + [ 9.49217427e-01, 5.17763087e-01, 2.95662308e-01],
  183 + [ 9.51343530e-01, 5.22849522e-01, 2.92274506e-01],
  184 + [ 9.53427725e-01, 5.27959550e-01, 2.88883445e-01],
  185 + [ 9.55469640e-01, 5.33093083e-01, 2.85490391e-01],
  186 + [ 9.57468770e-01, 5.38250172e-01, 2.82096149e-01],
  187 + [ 9.59424430e-01, 5.43431038e-01, 2.78700990e-01],
  188 + [ 9.61335930e-01, 5.48635890e-01, 2.75305214e-01],
  189 + [ 9.63202573e-01, 5.53864931e-01, 2.71909159e-01],
  190 + [ 9.65023656e-01, 5.59118349e-01, 2.68513200e-01],
  191 + [ 9.66798470e-01, 5.64396327e-01, 2.65117752e-01],
  192 + [ 9.68525639e-01, 5.69699633e-01, 2.61721488e-01],
  193 + [ 9.70204593e-01, 5.75028270e-01, 2.58325424e-01],
  194 + [ 9.71835007e-01, 5.80382015e-01, 2.54931256e-01],
  195 + [ 9.73416145e-01, 5.85761012e-01, 2.51539615e-01],
  196 + [ 9.74947262e-01, 5.91165394e-01, 2.48151200e-01],
  197 + [ 9.76427606e-01, 5.96595287e-01, 2.44766775e-01],
  198 + [ 9.77856416e-01, 6.02050811e-01, 2.41387186e-01],
  199 + [ 9.79232922e-01, 6.07532077e-01, 2.38013359e-01],
  200 + [ 9.80556344e-01, 6.13039190e-01, 2.34646316e-01],
  201 + [ 9.81825890e-01, 6.18572250e-01, 2.31287178e-01],
  202 + [ 9.83040742e-01, 6.24131362e-01, 2.27937141e-01],
  203 + [ 9.84198924e-01, 6.29717516e-01, 2.24595006e-01],
  204 + [ 9.85300760e-01, 6.35329876e-01, 2.21264889e-01],
  205 + [ 9.86345421e-01, 6.40968508e-01, 2.17948456e-01],
  206 + [ 9.87332067e-01, 6.46633475e-01, 2.14647532e-01],
  207 + [ 9.88259846e-01, 6.52324832e-01, 2.11364122e-01],
  208 + [ 9.89127893e-01, 6.58042630e-01, 2.08100426e-01],
  209 + [ 9.89935328e-01, 6.63786914e-01, 2.04858855e-01],
  210 + [ 9.90681261e-01, 6.69557720e-01, 2.01642049e-01],
  211 + [ 9.91364787e-01, 6.75355082e-01, 1.98452900e-01],
  212 + [ 9.91984990e-01, 6.81179025e-01, 1.95294567e-01],
  213 + [ 9.92540939e-01, 6.87029567e-01, 1.92170500e-01],
  214 + [ 9.93031693e-01, 6.92906719e-01, 1.89084459e-01],
  215 + [ 9.93456302e-01, 6.98810484e-01, 1.86040537e-01],
  216 + [ 9.93813802e-01, 7.04740854e-01, 1.83043180e-01],
  217 + [ 9.94103226e-01, 7.10697814e-01, 1.80097207e-01],
  218 + [ 9.94323596e-01, 7.16681336e-01, 1.77207826e-01],
  219 + [ 9.94473934e-01, 7.22691379e-01, 1.74380656e-01],
  220 + [ 9.94553260e-01, 7.28727890e-01, 1.71621733e-01],
  221 + [ 9.94560594e-01, 7.34790799e-01, 1.68937522e-01],
  222 + [ 9.94494964e-01, 7.40880020e-01, 1.66334918e-01],
  223 + [ 9.94355411e-01, 7.46995448e-01, 1.63821243e-01],
  224 + [ 9.94140989e-01, 7.53136955e-01, 1.61404226e-01],
  225 + [ 9.93850778e-01, 7.59304390e-01, 1.59091984e-01],
  226 + [ 9.93482190e-01, 7.65498551e-01, 1.56890625e-01],
  227 + [ 9.93033251e-01, 7.71719833e-01, 1.54807583e-01],
  228 + [ 9.92505214e-01, 7.77966775e-01, 1.52854862e-01],
  229 + [ 9.91897270e-01, 7.84239120e-01, 1.51041581e-01],
  230 + [ 9.91208680e-01, 7.90536569e-01, 1.49376885e-01],
  231 + [ 9.90438793e-01, 7.96858775e-01, 1.47869810e-01],
  232 + [ 9.89587065e-01, 8.03205337e-01, 1.46529128e-01],
  233 + [ 9.88647741e-01, 8.09578605e-01, 1.45357284e-01],
  234 + [ 9.87620557e-01, 8.15977942e-01, 1.44362644e-01],
  235 + [ 9.86509366e-01, 8.22400620e-01, 1.43556679e-01],
  236 + [ 9.85314198e-01, 8.28845980e-01, 1.42945116e-01],
  237 + [ 9.84031139e-01, 8.35315360e-01, 1.42528388e-01],
  238 + [ 9.82652820e-01, 8.41811730e-01, 1.42302653e-01],
  239 + [ 9.81190389e-01, 8.48328902e-01, 1.42278607e-01],
  240 + [ 9.79643637e-01, 8.54866468e-01, 1.42453425e-01],
  241 + [ 9.77994918e-01, 8.61432314e-01, 1.42808191e-01],
  242 + [ 9.76264977e-01, 8.68015998e-01, 1.43350944e-01],
  243 + [ 9.74443038e-01, 8.74622194e-01, 1.44061156e-01],
  244 + [ 9.72530009e-01, 8.81250063e-01, 1.44922913e-01],
  245 + [ 9.70532932e-01, 8.87896125e-01, 1.45918663e-01],
  246 + [ 9.68443477e-01, 8.94563989e-01, 1.47014438e-01],
  247 + [ 9.66271225e-01, 9.01249365e-01, 1.48179639e-01],
  248 + [ 9.64021057e-01, 9.07950379e-01, 1.49370428e-01],
  249 + [ 9.61681481e-01, 9.14672479e-01, 1.50520343e-01],
  250 + [ 9.59275646e-01, 9.21406537e-01, 1.51566019e-01],
  251 + [ 9.56808068e-01, 9.28152065e-01, 1.52409489e-01],
  252 + [ 9.54286813e-01, 9.34907730e-01, 1.52921158e-01],
  253 + [ 9.51726083e-01, 9.41670605e-01, 1.52925363e-01],
  254 + [ 9.49150533e-01, 9.48434900e-01, 1.52177604e-01],
  255 + [ 9.46602270e-01, 9.55189860e-01, 1.50327944e-01],
  256 + [ 9.44151742e-01, 9.61916487e-01, 1.46860789e-01],
  257 + [ 9.41896120e-01, 9.68589814e-01, 1.40955606e-01],
  258 + [ 9.40015097e-01, 9.75158357e-01, 1.31325517e-01]];
  259 +
  260 +if nargin < 1
  261 + cm_data = cm;
  262 +else
  263 + hsv=rgb2hsv(cm);
  264 + hsv(153:end,1)=hsv(153:end,1)+1; % hardcoded
  265 + cm_data=interp1(linspace(0,1,size(cm,1)),hsv,linspace(0,1,m));
  266 + cm_data(cm_data(:,1)>1,1)=cm_data(cm_data(:,1)>1,1)-1;
  267 + cm_data=hsv2rgb(cm_data);
  268 +
  269 +end
  270 +end
0 \ No newline at end of file 271 \ No newline at end of file
matlab/spe2envicamille.m 0 → 100644
  1 +function spe2envicamille(filemask, outfile,wavenumberfilename)
  2 +
  3 + filelist = dir(filemask);
  4 +
  5 + %get a list of date numbers
  6 + datenums = cell2mat({filelist.datenum});
  7 +
  8 + %sort the file order based on acquisition time
  9 + [~, id] = sort(datenums);
  10 +
  11 + %get the number of files
  12 + Y = length(id); %size of the image along Y
  13 +
  14 + %load the first file to determine the spectral and X-axis size
  15 + temp = readspe(filelist(1).name);
  16 + X = size(temp, 1); %size of the image along X
  17 + B = size(temp, 2); %number of bands in the image
  18 +
  19 + %create the cube
  20 + I = zeros(X, Y, B);
  21 +
  22 + %for each line
  23 + for y = 1:Y
  24 +
  25 + %read a SPE file
  26 + img = readspe(filelist(id(y)).name);
  27 +
  28 + I(:, y, :) = permute(img, [1 3 2]);
  29 + end
  30 +
  31 + enviSaveRawcamille(single(I), outfile, [outfile '.hdr'],wavenumberfilename);
  32 +
  33 +
  34 +
matlab/stim_images2matrix.m 0 → 100644
  1 +function S = stim_images2matrix(filemask)
  2 +%This function loads a set of images as a 3D matrix. Color images are
  3 +%converted to grayscale when loaded, so the resulting matrix is always 3D
  4 +%with size X x Y x Z, where:
  5 +% X is the size of the images along the X axis
  6 +% Y is the size of the images along the Y axis
  7 +% Z is the number of images
  8 +%
  9 +% all images are assumed to be the same size (though they do not have to
  10 +% be the same file format or number of bits per pixel
  11 +
  12 + files = dir(filemask);
  13 +
  14 + %figure out the file size
  15 + I = imread([files(1).folder '/' files(1).name]);
  16 + X = size(I, 1);
  17 + Y = size(I, 2);
  18 + Z = length(files);
  19 +
  20 + S = zeros(X, Y, Z, 'uint8');
  21 +
  22 + h = waitbar(0, ['Loading ' num2str(Z) ' images...']);
  23 + for i = 1:Z
  24 + I = rgb2gray(imread([files(1).folder '/' files(1).name]));
  25 + S(:, :, i) = I;
  26 + waitbar(i/Z, h);
  27 + end
  28 + close(h);
  29 +end
  30 +
  31 +
matlab/viridis.m 0 → 100644
  1 +function cm_data=viridis(m)
  2 +cm = [[ 0.26700401, 0.00487433, 0.32941519],
  3 + [ 0.26851048, 0.00960483, 0.33542652],
  4 + [ 0.26994384, 0.01462494, 0.34137895],
  5 + [ 0.27130489, 0.01994186, 0.34726862],
  6 + [ 0.27259384, 0.02556309, 0.35309303],
  7 + [ 0.27380934, 0.03149748, 0.35885256],
  8 + [ 0.27495242, 0.03775181, 0.36454323],
  9 + [ 0.27602238, 0.04416723, 0.37016418],
  10 + [ 0.2770184 , 0.05034437, 0.37571452],
  11 + [ 0.27794143, 0.05632444, 0.38119074],
  12 + [ 0.27879067, 0.06214536, 0.38659204],
  13 + [ 0.2795655 , 0.06783587, 0.39191723],
  14 + [ 0.28026658, 0.07341724, 0.39716349],
  15 + [ 0.28089358, 0.07890703, 0.40232944],
  16 + [ 0.28144581, 0.0843197 , 0.40741404],
  17 + [ 0.28192358, 0.08966622, 0.41241521],
  18 + [ 0.28232739, 0.09495545, 0.41733086],
  19 + [ 0.28265633, 0.10019576, 0.42216032],
  20 + [ 0.28291049, 0.10539345, 0.42690202],
  21 + [ 0.28309095, 0.11055307, 0.43155375],
  22 + [ 0.28319704, 0.11567966, 0.43611482],
  23 + [ 0.28322882, 0.12077701, 0.44058404],
  24 + [ 0.28318684, 0.12584799, 0.44496 ],
  25 + [ 0.283072 , 0.13089477, 0.44924127],
  26 + [ 0.28288389, 0.13592005, 0.45342734],
  27 + [ 0.28262297, 0.14092556, 0.45751726],
  28 + [ 0.28229037, 0.14591233, 0.46150995],
  29 + [ 0.28188676, 0.15088147, 0.46540474],
  30 + [ 0.28141228, 0.15583425, 0.46920128],
  31 + [ 0.28086773, 0.16077132, 0.47289909],
  32 + [ 0.28025468, 0.16569272, 0.47649762],
  33 + [ 0.27957399, 0.17059884, 0.47999675],
  34 + [ 0.27882618, 0.1754902 , 0.48339654],
  35 + [ 0.27801236, 0.18036684, 0.48669702],
  36 + [ 0.27713437, 0.18522836, 0.48989831],
  37 + [ 0.27619376, 0.19007447, 0.49300074],
  38 + [ 0.27519116, 0.1949054 , 0.49600488],
  39 + [ 0.27412802, 0.19972086, 0.49891131],
  40 + [ 0.27300596, 0.20452049, 0.50172076],
  41 + [ 0.27182812, 0.20930306, 0.50443413],
  42 + [ 0.27059473, 0.21406899, 0.50705243],
  43 + [ 0.26930756, 0.21881782, 0.50957678],
  44 + [ 0.26796846, 0.22354911, 0.5120084 ],
  45 + [ 0.26657984, 0.2282621 , 0.5143487 ],
  46 + [ 0.2651445 , 0.23295593, 0.5165993 ],
  47 + [ 0.2636632 , 0.23763078, 0.51876163],
  48 + [ 0.26213801, 0.24228619, 0.52083736],
  49 + [ 0.26057103, 0.2469217 , 0.52282822],
  50 + [ 0.25896451, 0.25153685, 0.52473609],
  51 + [ 0.25732244, 0.2561304 , 0.52656332],
  52 + [ 0.25564519, 0.26070284, 0.52831152],
  53 + [ 0.25393498, 0.26525384, 0.52998273],
  54 + [ 0.25219404, 0.26978306, 0.53157905],
  55 + [ 0.25042462, 0.27429024, 0.53310261],
  56 + [ 0.24862899, 0.27877509, 0.53455561],
  57 + [ 0.2468114 , 0.28323662, 0.53594093],
  58 + [ 0.24497208, 0.28767547, 0.53726018],
  59 + [ 0.24311324, 0.29209154, 0.53851561],
  60 + [ 0.24123708, 0.29648471, 0.53970946],
  61 + [ 0.23934575, 0.30085494, 0.54084398],
  62 + [ 0.23744138, 0.30520222, 0.5419214 ],
  63 + [ 0.23552606, 0.30952657, 0.54294396],
  64 + [ 0.23360277, 0.31382773, 0.54391424],
  65 + [ 0.2316735 , 0.3181058 , 0.54483444],
  66 + [ 0.22973926, 0.32236127, 0.54570633],
  67 + [ 0.22780192, 0.32659432, 0.546532 ],
  68 + [ 0.2258633 , 0.33080515, 0.54731353],
  69 + [ 0.22392515, 0.334994 , 0.54805291],
  70 + [ 0.22198915, 0.33916114, 0.54875211],
  71 + [ 0.22005691, 0.34330688, 0.54941304],
  72 + [ 0.21812995, 0.34743154, 0.55003755],
  73 + [ 0.21620971, 0.35153548, 0.55062743],
  74 + [ 0.21429757, 0.35561907, 0.5511844 ],
  75 + [ 0.21239477, 0.35968273, 0.55171011],
  76 + [ 0.2105031 , 0.36372671, 0.55220646],
  77 + [ 0.20862342, 0.36775151, 0.55267486],
  78 + [ 0.20675628, 0.37175775, 0.55311653],
  79 + [ 0.20490257, 0.37574589, 0.55353282],
  80 + [ 0.20306309, 0.37971644, 0.55392505],
  81 + [ 0.20123854, 0.38366989, 0.55429441],
  82 + [ 0.1994295 , 0.38760678, 0.55464205],
  83 + [ 0.1976365 , 0.39152762, 0.55496905],
  84 + [ 0.19585993, 0.39543297, 0.55527637],
  85 + [ 0.19410009, 0.39932336, 0.55556494],
  86 + [ 0.19235719, 0.40319934, 0.55583559],
  87 + [ 0.19063135, 0.40706148, 0.55608907],
  88 + [ 0.18892259, 0.41091033, 0.55632606],
  89 + [ 0.18723083, 0.41474645, 0.55654717],
  90 + [ 0.18555593, 0.4185704 , 0.55675292],
  91 + [ 0.18389763, 0.42238275, 0.55694377],
  92 + [ 0.18225561, 0.42618405, 0.5571201 ],
  93 + [ 0.18062949, 0.42997486, 0.55728221],
  94 + [ 0.17901879, 0.43375572, 0.55743035],
  95 + [ 0.17742298, 0.4375272 , 0.55756466],
  96 + [ 0.17584148, 0.44128981, 0.55768526],
  97 + [ 0.17427363, 0.4450441 , 0.55779216],
  98 + [ 0.17271876, 0.4487906 , 0.55788532],
  99 + [ 0.17117615, 0.4525298 , 0.55796464],
  100 + [ 0.16964573, 0.45626209, 0.55803034],
  101 + [ 0.16812641, 0.45998802, 0.55808199],
  102 + [ 0.1666171 , 0.46370813, 0.55811913],
  103 + [ 0.16511703, 0.4674229 , 0.55814141],
  104 + [ 0.16362543, 0.47113278, 0.55814842],
  105 + [ 0.16214155, 0.47483821, 0.55813967],
  106 + [ 0.16066467, 0.47853961, 0.55811466],
  107 + [ 0.15919413, 0.4822374 , 0.5580728 ],
  108 + [ 0.15772933, 0.48593197, 0.55801347],
  109 + [ 0.15626973, 0.4896237 , 0.557936 ],
  110 + [ 0.15481488, 0.49331293, 0.55783967],
  111 + [ 0.15336445, 0.49700003, 0.55772371],
  112 + [ 0.1519182 , 0.50068529, 0.55758733],
  113 + [ 0.15047605, 0.50436904, 0.55742968],
  114 + [ 0.14903918, 0.50805136, 0.5572505 ],
  115 + [ 0.14760731, 0.51173263, 0.55704861],
  116 + [ 0.14618026, 0.51541316, 0.55682271],
  117 + [ 0.14475863, 0.51909319, 0.55657181],
  118 + [ 0.14334327, 0.52277292, 0.55629491],
  119 + [ 0.14193527, 0.52645254, 0.55599097],
  120 + [ 0.14053599, 0.53013219, 0.55565893],
  121 + [ 0.13914708, 0.53381201, 0.55529773],
  122 + [ 0.13777048, 0.53749213, 0.55490625],
  123 + [ 0.1364085 , 0.54117264, 0.55448339],
  124 + [ 0.13506561, 0.54485335, 0.55402906],
  125 + [ 0.13374299, 0.54853458, 0.55354108],
  126 + [ 0.13244401, 0.55221637, 0.55301828],
  127 + [ 0.13117249, 0.55589872, 0.55245948],
  128 + [ 0.1299327 , 0.55958162, 0.55186354],
  129 + [ 0.12872938, 0.56326503, 0.55122927],
  130 + [ 0.12756771, 0.56694891, 0.55055551],
  131 + [ 0.12645338, 0.57063316, 0.5498411 ],
  132 + [ 0.12539383, 0.57431754, 0.54908564],
  133 + [ 0.12439474, 0.57800205, 0.5482874 ],
  134 + [ 0.12346281, 0.58168661, 0.54744498],
  135 + [ 0.12260562, 0.58537105, 0.54655722],
  136 + [ 0.12183122, 0.58905521, 0.54562298],
  137 + [ 0.12114807, 0.59273889, 0.54464114],
  138 + [ 0.12056501, 0.59642187, 0.54361058],
  139 + [ 0.12009154, 0.60010387, 0.54253043],
  140 + [ 0.11973756, 0.60378459, 0.54139999],
  141 + [ 0.11951163, 0.60746388, 0.54021751],
  142 + [ 0.11942341, 0.61114146, 0.53898192],
  143 + [ 0.11948255, 0.61481702, 0.53769219],
  144 + [ 0.11969858, 0.61849025, 0.53634733],
  145 + [ 0.12008079, 0.62216081, 0.53494633],
  146 + [ 0.12063824, 0.62582833, 0.53348834],
  147 + [ 0.12137972, 0.62949242, 0.53197275],
  148 + [ 0.12231244, 0.63315277, 0.53039808],
  149 + [ 0.12344358, 0.63680899, 0.52876343],
  150 + [ 0.12477953, 0.64046069, 0.52706792],
  151 + [ 0.12632581, 0.64410744, 0.52531069],
  152 + [ 0.12808703, 0.64774881, 0.52349092],
  153 + [ 0.13006688, 0.65138436, 0.52160791],
  154 + [ 0.13226797, 0.65501363, 0.51966086],
  155 + [ 0.13469183, 0.65863619, 0.5176488 ],
  156 + [ 0.13733921, 0.66225157, 0.51557101],
  157 + [ 0.14020991, 0.66585927, 0.5134268 ],
  158 + [ 0.14330291, 0.66945881, 0.51121549],
  159 + [ 0.1466164 , 0.67304968, 0.50893644],
  160 + [ 0.15014782, 0.67663139, 0.5065889 ],
  161 + [ 0.15389405, 0.68020343, 0.50417217],
  162 + [ 0.15785146, 0.68376525, 0.50168574],
  163 + [ 0.16201598, 0.68731632, 0.49912906],
  164 + [ 0.1663832 , 0.69085611, 0.49650163],
  165 + [ 0.1709484 , 0.69438405, 0.49380294],
  166 + [ 0.17570671, 0.6978996 , 0.49103252],
  167 + [ 0.18065314, 0.70140222, 0.48818938],
  168 + [ 0.18578266, 0.70489133, 0.48527326],
  169 + [ 0.19109018, 0.70836635, 0.48228395],
  170 + [ 0.19657063, 0.71182668, 0.47922108],
  171 + [ 0.20221902, 0.71527175, 0.47608431],
  172 + [ 0.20803045, 0.71870095, 0.4728733 ],
  173 + [ 0.21400015, 0.72211371, 0.46958774],
  174 + [ 0.22012381, 0.72550945, 0.46622638],
  175 + [ 0.2263969 , 0.72888753, 0.46278934],
  176 + [ 0.23281498, 0.73224735, 0.45927675],
  177 + [ 0.2393739 , 0.73558828, 0.45568838],
  178 + [ 0.24606968, 0.73890972, 0.45202405],
  179 + [ 0.25289851, 0.74221104, 0.44828355],
  180 + [ 0.25985676, 0.74549162, 0.44446673],
  181 + [ 0.26694127, 0.74875084, 0.44057284],
  182 + [ 0.27414922, 0.75198807, 0.4366009 ],
  183 + [ 0.28147681, 0.75520266, 0.43255207],
  184 + [ 0.28892102, 0.75839399, 0.42842626],
  185 + [ 0.29647899, 0.76156142, 0.42422341],
  186 + [ 0.30414796, 0.76470433, 0.41994346],
  187 + [ 0.31192534, 0.76782207, 0.41558638],
  188 + [ 0.3198086 , 0.77091403, 0.41115215],
  189 + [ 0.3277958 , 0.77397953, 0.40664011],
  190 + [ 0.33588539, 0.7770179 , 0.40204917],
  191 + [ 0.34407411, 0.78002855, 0.39738103],
  192 + [ 0.35235985, 0.78301086, 0.39263579],
  193 + [ 0.36074053, 0.78596419, 0.38781353],
  194 + [ 0.3692142 , 0.78888793, 0.38291438],
  195 + [ 0.37777892, 0.79178146, 0.3779385 ],
  196 + [ 0.38643282, 0.79464415, 0.37288606],
  197 + [ 0.39517408, 0.79747541, 0.36775726],
  198 + [ 0.40400101, 0.80027461, 0.36255223],
  199 + [ 0.4129135 , 0.80304099, 0.35726893],
  200 + [ 0.42190813, 0.80577412, 0.35191009],
  201 + [ 0.43098317, 0.80847343, 0.34647607],
  202 + [ 0.44013691, 0.81113836, 0.3409673 ],
  203 + [ 0.44936763, 0.81376835, 0.33538426],
  204 + [ 0.45867362, 0.81636288, 0.32972749],
  205 + [ 0.46805314, 0.81892143, 0.32399761],
  206 + [ 0.47750446, 0.82144351, 0.31819529],
  207 + [ 0.4870258 , 0.82392862, 0.31232133],
  208 + [ 0.49661536, 0.82637633, 0.30637661],
  209 + [ 0.5062713 , 0.82878621, 0.30036211],
  210 + [ 0.51599182, 0.83115784, 0.29427888],
  211 + [ 0.52577622, 0.83349064, 0.2881265 ],
  212 + [ 0.5356211 , 0.83578452, 0.28190832],
  213 + [ 0.5455244 , 0.83803918, 0.27562602],
  214 + [ 0.55548397, 0.84025437, 0.26928147],
  215 + [ 0.5654976 , 0.8424299 , 0.26287683],
  216 + [ 0.57556297, 0.84456561, 0.25641457],
  217 + [ 0.58567772, 0.84666139, 0.24989748],
  218 + [ 0.59583934, 0.84871722, 0.24332878],
  219 + [ 0.60604528, 0.8507331 , 0.23671214],
  220 + [ 0.61629283, 0.85270912, 0.23005179],
  221 + [ 0.62657923, 0.85464543, 0.22335258],
  222 + [ 0.63690157, 0.85654226, 0.21662012],
  223 + [ 0.64725685, 0.85839991, 0.20986086],
  224 + [ 0.65764197, 0.86021878, 0.20308229],
  225 + [ 0.66805369, 0.86199932, 0.19629307],
  226 + [ 0.67848868, 0.86374211, 0.18950326],
  227 + [ 0.68894351, 0.86544779, 0.18272455],
  228 + [ 0.69941463, 0.86711711, 0.17597055],
  229 + [ 0.70989842, 0.86875092, 0.16925712],
  230 + [ 0.72039115, 0.87035015, 0.16260273],
  231 + [ 0.73088902, 0.87191584, 0.15602894],
  232 + [ 0.74138803, 0.87344918, 0.14956101],
  233 + [ 0.75188414, 0.87495143, 0.14322828],
  234 + [ 0.76237342, 0.87642392, 0.13706449],
  235 + [ 0.77285183, 0.87786808, 0.13110864],
  236 + [ 0.78331535, 0.87928545, 0.12540538],
  237 + [ 0.79375994, 0.88067763, 0.12000532],
  238 + [ 0.80418159, 0.88204632, 0.11496505],
  239 + [ 0.81457634, 0.88339329, 0.11034678],
  240 + [ 0.82494028, 0.88472036, 0.10621724],
  241 + [ 0.83526959, 0.88602943, 0.1026459 ],
  242 + [ 0.84556056, 0.88732243, 0.09970219],
  243 + [ 0.8558096 , 0.88860134, 0.09745186],
  244 + [ 0.86601325, 0.88986815, 0.09595277],
  245 + [ 0.87616824, 0.89112487, 0.09525046],
  246 + [ 0.88627146, 0.89237353, 0.09537439],
  247 + [ 0.89632002, 0.89361614, 0.09633538],
  248 + [ 0.90631121, 0.89485467, 0.09812496],
  249 + [ 0.91624212, 0.89609127, 0.1007168 ],
  250 + [ 0.92610579, 0.89732977, 0.10407067],
  251 + [ 0.93590444, 0.8985704 , 0.10813094],
  252 + [ 0.94563626, 0.899815 , 0.11283773],
  253 + [ 0.95529972, 0.90106534, 0.11812832],
  254 + [ 0.96489353, 0.90232311, 0.12394051],
  255 + [ 0.97441665, 0.90358991, 0.13021494],
  256 + [ 0.98386829, 0.90486726, 0.13689671],
  257 + [ 0.99324789, 0.90615657, 0.1439362 ]];
  258 +
  259 +if nargin < 1
  260 + cm_data = cm;
  261 +else
  262 + hsv=rgb2hsv(cm);
  263 + cm_data=interp1(linspace(0,1,size(cm,1)),hsv,linspace(0,1,m));
  264 + cm_data=hsv2rgb(cm_data);
  265 +
  266 +end
  267 +end
0 \ No newline at end of file 268 \ No newline at end of file
matlab/wavenumber.dat 0 → 100644
  1 +128.23,131.48,134.74,138,141.25,144.5,147.75,151,154.25,157.5,160.74,163.98,167.23,170.47,173.71,176.95,180.18,183.42,186.65,189.88,193.11,196.34,199.57,202.8,206.02,209.25,212.47,215.69,218.91,222.13,225.34,228.56,231.77,234.98,238.19,241.4,244.61,247.82,251.02,254.23,257.43,260.63,263.83,267.03,270.22,273.42,276.61,279.81,283,286.19,289.37,292.56,295.75,298.93,302.11,305.29,308.47,311.65,314.83,318,321.18,324.35,327.52,330.69,333.86,337.02,340.19,343.35,346.51,349.68,352.84,355.99,359.15,362.31,365.46,368.61,371.76,374.91,378.06,381.21,384.35,387.5,390.64,393.78,396.92,400.06,403.19,406.33,409.46,412.6,415.73,418.86,421.99,425.11,428.24,431.36,434.48,437.61,440.72,443.84,446.96,450.08,453.19,456.3,459.41,462.52,465.63,468.74,471.84,474.95,478.05,481.15,484.25,487.35,490.45,493.54,496.64,499.73,502.82,505.91,509,512.08,515.17,518.25,521.34,524.42,527.5,530.58,533.65,536.73,539.8,542.88,545.95,549.02,552.08,555.15,558.22,561.28,564.34,567.41,570.47,573.52,576.58,579.64,582.69,585.74,588.79,591.84,594.89,597.94,600.99,604.03,607.07,610.11,613.15,616.19,619.23,622.26,625.3,628.33,631.36,634.39,637.42,640.45,643.47,646.5,649.52,652.54,655.56,658.58,661.6,664.61,667.63,670.64,673.65,676.66,679.67,682.68,685.68,688.69,691.69,694.69,697.69,700.69,703.69,706.68,709.68,712.67,715.66,718.65,721.64,724.63,727.62,730.6,733.58,736.56,739.54,742.52,745.5,748.48,751.45,754.42,757.39,760.36,763.33,766.3,769.27,772.23,775.19,778.16,781.12,784.07,787.03,789.99,792.94,795.9,798.85,801.8,804.75,807.69,810.64,813.58,816.53,819.47,822.41,825.35,828.28,831.22,834.16,837.09,840.02,842.95,845.88,848.81,851.73,854.66,857.58,860.5,863.42,866.34,869.26,872.17,875.09,878,880.91,883.82,886.73,889.64,892.55,895.45,898.35,901.26,904.16,907.06,909.95,912.85,915.74,918.64,921.53,924.42,927.31,930.2,933.08,935.97,938.85,941.73,944.61,947.49,950.37,953.24,956.12,958.99,961.86,964.74,967.6,970.47,973.34,976.2,979.07,981.93,984.79,987.65,990.5,993.36,996.22,999.07,1001.9,1004.8,1007.6,1010.5,1013.3,1016.2,1019,1021.8,1024.7,1027.5,1030.4,1033.2,1036,1038.9,1041.7,1044.5,1047.4,1050.2,1053,1055.8,1058.7,1061.5,1064.3,1067.1,1069.9,1072.8,1075.6,1078.4,1081.2,1084,1086.8,1089.6,1092.4,1095.2,1098,1100.8,1103.6,1106.4,1109.2,1112,1114.8,1117.6,1120.4,1123.2,1126,1128.8,1131.6,1134.4,1137.1,1139.9,1142.7,1145.5,1148.3,1151,1153.8,1156.6,1159.4,1162.1,1164.9,1167.7,1170.4,1173.2,1176,1178.7,1181.5,1184.2,1187,1189.8,1192.5,1195.3,1198,1200.8,1203.5,1206.3,1209,1211.8,1214.5,1217.2,1220,1222.7,1225.5,1228.2,1230.9,1233.7,1236.4,1239.1,1241.9,1244.6,1247.3,1250,1252.8,1255.5,1258.2,1260.9,1263.6,1266.4,1269.1,1271.8,1274.5,1277.2,1279.9,1282.6,1285.3,1288,1290.7,1293.4,1296.1,1298.8,1301.5,1304.2,1306.9,1309.6,1312.3,1315,1317.7,1320.4,1323.1,1325.8,1328.4,1331.1,1333.8,1336.5,1339.2,1341.8,1344.5,1347.2,1349.9,1352.5,1355.2,1357.9,1360.5,1363.2,1365.9,1368.5,1371.2,1373.9,1376.5,1379.2,1381.8,1384.5,1387.1,1389.8,1392.4,1395.1,1397.7,1400.4,1403,1405.7,1408.3,1410.9,1413.6,1416.2,1418.9,1421.5,1424.1,1426.7,1429.4,1432,1434.6,1437.3,1439.9,1442.5,1445.1,1447.7,1450.4,1453,1455.6,1458.2,1460.8,1463.4,1466,1468.7,1471.3,1473.9,1476.5,1479.1,1481.7,1484.3,1486.9,1489.5,1492.1,1494.7,1497.3,1499.8,1502.4,1505,1507.6,1510.2,1512.8,1515.4,1517.9,1520.5,1523.1,1525.7,1528.3,1530.8,1533.4,1536,1538.5,1541.1,1543.7,1546.2,1548.8,1551.4,1553.9,1556.5,1559.1,1561.6,1564.2,1566.7,1569.3,1571.8,1574.4,1576.9,1579.5,1582,1584.6,1587.1,1589.6,1592.2,1594.7,1597.3,1599.8,1602.3,1604.9,1607.4,1609.9,1612.5,1615,1617.5,1620,1622.6,1625.1,1627.6,1630.1,1632.6,1635.1,1637.7,1640.2,1642.7,1645.2,1647.7,1650.2,1652.7,1655.2,1657.7,1660.2,1662.7,1665.2,1667.7,1670.2,1672.7,1675.2,1677.7,1680.2,1682.7,1685.2,1687.6,1690.1,1692.6,1695.1,1697.6,1700.1,1702.5,1705,1707.5,1710,1712.4,1714.9,1717.4,1719.8,1722.3,1724.8,1727.2,1729.7,1732.1,1734.6,1737.1,1739.5,1742,1744.4,1746.9,1749.3,1751.8,1754.2,1756.7,1759.1,1761.6,1764,1766.4,1768.9,1771.3,1773.8,1776.2,1778.6,1781.1,1783.5,1785.9,1788.3,1790.8,1793.2,1795.6,1798,1800.5,1802.9,1805.3,1807.7,1810.1,1812.5,1815,1817.4,1819.8,1822.2,1824.6,1827,1829.4,1831.8,1834.2,1836.6,1839,1841.4,1843.8,1846.2,1848.6,1851,1853.3,1855.7,1858.1,1860.5,1862.9,1865.3,1867.7,1870,1872.4,1874.8,1877.2,1879.5,1881.9,1884.3,1886.6,1889,1891.4,1893.7,1896.1,1898.5,1900.8,1903.2,1905.5,1907.9,1910.3,1912.6,1915,1917.3,1919.7,1922,1924.4,1926.7,1929,1931.4,1933.7,1936.1,1938.4,1940.7,1943.1,1945.4,1947.7,1950.1,1952.4,1954.7,1957.1,1959.4,1961.7,1964,1966.3,1968.7,1971,1973.3,1975.6,1977.9,1980.2,1982.6,1984.9,1987.2,1989.5,1991.8,1994.1,1996.4,1998.7,2001,2003.3,2005.6,2007.9,2010.2,2012.5,2014.8,2017,2019.3,2021.6,2023.9,2026.2,2028.5,2030.7,2033,2035.3,2037.6,2039.9,2042.1,2044.4,2046.7,2048.9,2051.2,2053.5,2055.7,2058,2060.3,2062.5,2064.8,2067,2069.3,2071.6,2073.8,2076.1,2078.3,2080.6,2082.8,2085.1,2087.3,2089.5,2091.8,2094,2096.3,2098.5,2100.7,2103,2105.2,2107.4,2109.7,2111.9,2114.1,2116.4,2118.6,2120.8,2123,2125.3,2127.5,2129.7,2131.9,2134.1,2136.3,2138.6,2140.8,2143,2145.2,2147.4,2149.6,2151.8,2154,2156.2,2158.4,2160.6,2162.8,2165,2167.2,2169.4,2171.6,2173.8,2175.9,2178.1,2180.3,2182.5,2184.7,2186.9,2189,2191.2,2193.4,2195.6,2197.7,2199.9,2202.1,2204.3,2206.4,2208.6,2210.8,2212.9,2215.1,2217.3,2219.4,2221.6,2223.7,2225.9,2228,2230.2,2232.3,2234.5,2236.6,2238.8,2240.9,2243.1,2245.2,2247.4,2249.5,2251.6,2253.8,2255.9,2258,2260.2,2262.3,2264.4,2266.6,2268.7,2270.8,2272.9,2275.1,2277.2,2279.3,2281.4,2283.5,2285.7,2287.8,2289.9,2292,2294.1,2296.2,2298.3,2300.4,2302.5,2304.6,2306.7,2308.8,2310.9,2313,2315.1,2317.2,2319.3,2321.4,2323.5,2325.6,2327.7,2329.8,2331.8,2333.9,2336,2338.1,2340.2,2342.2,2344.3,2346.4,2348.5,2350.5,2352.6,2354.7,2356.7,2358.8,2360.9,2362.9,2365,2367.1,2369.1,2371.2,2373.2,2375.3,2377.3,2379.4,2381.4,2383.5,2385.5,2387.6,2389.6,2391.7,2393.7,2395.7,2397.8,2399.8,2401.9,2403.9,2405.9,2408,2410,2412,2414,2416.1,2418.1,2420.1,2422.1,2424.2,2426.2,2428.2,2430.2,2432.2,2434.2,2436.2,2438.3,2440.3,2442.3,2444.3,2446.3,2448.3,2450.3,2452.3,2454.3,2456.3,2458.3,2460.3,2462.3,2464.3,2466.3,2468.2,2470.2,2472.2,2474.2,2476.2,2478.2,2480.1,2482.1,2484.1,2486.1,2488,2490,2492,2494,2495.9,2497.9,2499.9,2501.8,2503.8,2505.8,2507.7,2509.7,2511.6,2513.6,2515.5,2517.5,2519.4,2521.4,2523.3,2525.3,2527.2,2529.2,2531.1,2533.1,2535,2537,2538.9,2540.8,2542.8,2544.7,2546.6,2548.6,2550.5,2552.4,2554.3,2556.3,2558.2,2560.1,2562,2563.9,2565.9,2567.8,2569.7,2571.6,2573.5,2575.4,2577.3,2579.2,2581.2,2583.1,2585,2586.9,2588.8,2590.7,2592.6,2594.5,2596.4,2598.2,2600.1,2602,2603.9,2605.8,2607.7,2609.6,2611.5,2613.3,2615.2,2617.1,2619,2620.9,2622.7,2624.6,2626.5,2628.3,2630.2,2632.1,2634,2635.8,2637.7,2639.5,2641.4,2643.3,2645.1,2647,2648.8,2650.7,2652.5,2654.4,2656.2,2658.1,2659.9,2661.8,2663.6,2665.5,2667.3,2669.1,2671,2672.8,2674.7,2676.5,2678.3,2680.1,2682,2683.8,2685.6,2687.5,2689.3,2691.1,2692.9,2694.7,2696.6,2698.4,2700.2,2702,2703.8,2705.6,2707.4,2709.2,2711.1,2712.9,2714.7,2716.5,2718.3,2720.1,2721.9,2723.7,2725.5,2727.2,2729,2730.8,2732.6,2734.4,2736.2,2738,2739.8,2741.5,2743.3,2745.1,2746.9,2748.7,2750.4,2752.2,2754,2755.8,2757.5,2759.3,2761.1,2762.8,2764.6,2766.3,2768.1,2769.9,2771.6,2773.4,2775.1,2776.9,2778.6,2780.4,2782.1,2783.9,2785.6,2787.4,2789.1,2790.9,2792.6,2794.4,2796.1,2797.8,2799.6,2801.3,2803,2804.8,2806.5,2808.2,2809.9,2811.7,2813.4,2815.1,2816.8,2818.6,2820.3,2822,2823.7,2825.4,2827.1,2828.8,2830.6,2832.3,2834,2835.7,2837.4,2839.1,2840.8,2842.5,2844.2,2845.9,2847.6,2849.3,2851,2852.7,2854.3,2856,2857.7,2859.4,2861.1,2862.8,2864.5,2866.1,2867.8,2869.5,2871.2,2872.8,2874.5,2876.2,2877.8,2879.5,2881.2,2882.8,2884.5,2886.2,2887.8,2889.5,2891.2,2892.8,2894.5,2896.1,2897.8,2899.4,2901.1,2902.7,2904.4,2906,2907.7,2909.3,2910.9,2912.6,2914.2,2915.9,2917.5,2919.1,2920.8,2922.4,2924,2925.7,2927.3,2928.9,2930.5,2932.2,2933.8,2935.4,2937,2938.6,2940.2,2941.9,2943.5,2945.1,2946.7,2948.3,2949.9,2951.5,2953.1,2954.7,2956.3,2957.9,2959.5,2961.1,2962.7,2964.3,2965.9,2967.5,2969.1,2970.7,2972.3,2973.8,2975.4,2977,2978.6,2980.2,2981.8,2983.3,2984.9,2986.5,2988.1,2989.6,2991.2,2992.8,2994.3,2995.9,2997.5,2999,3000.6,3002.1,3003.7,3005.3,3006.8,3008.4,3009.9,3011.5,3013,3014.6,3016.1,3017.7,3019.2,3020.8,3022.3,3023.8,3025.4,3026.9,3028.5,3030,3031.5,3033.1,3034.6,3036.1,3037.6,3039.2,3040.7,3042.2,3043.7,3045.3,3046.8,3048.3,3049.8,3051.3,3052.8,3054.3,3055.9,3057.4,3058.9,3060.4,3061.9,3063.4,3064.9,3066.4,3067.9,3069.4,3070.9,3072.4,3073.9,3075.4,3076.8,3078.3,3079.8,3081.3,3082.8,3084.3,3085.8,3087.2,3088.7,3090.2,3091.7,3093.1,3094.6,3096.1,3097.6,3099,3100.5,3102,3103.4,3104.9,3106.4,3107.8,3109.3,3110.7,3112.2,3113.6,3115.1,3116.6,3118,3119.5,3120.9,3122.3,3123.8,3125.2,3126.7,3128.1,3129.6,3131,3132.4,3133.9,3135.3,3136.7,3138.2,3139.6,3141,3142.4,3143.9,3145.3,3146.7,3148.1,3149.6,3151,3152.4,3153.8,3155.2,3156.6,3158,3159.4,3160.9,3162.3,3163.7,3165.1,3166.5,3167.9,3169.3,3170.7,3172.1,3173.5,3174.9,3176.2,3177.6,3179,3180.4,3181.8,3183.2,3184.6,3185.9,3187.3,3188.7,3190.1,3191.5,3192.8,3194.2,3195.6,3197,3198.3,3199.7,3201.1,3202.4,3203.8,3205.1,3206.5,3207.9,3209.2,3210.6,3211.9,3213.3,3214.6
python/classify.py 0 → 100644
  1 +# -*- coding: utf-8 -*-
  2 +"""
  3 +Created on Sun Jul 23 16:04:33 2017
  4 +
  5 +@author: david
  6 +"""
  7 +
  8 +import numpy
  9 +import colorsys
  10 +import sklearn
  11 +import sklearn.metrics
  12 +import scipy
  13 +import scipy.misc
  14 +import envi
  15 +import hyperspectral
  16 +import random
  17 +import progressbar
  18 +import matplotlib.pyplot as plt
  19 +
  20 +#generate N qualitative colors and return the value for color c
  21 +def qualcolor(c, N):
  22 + dN = numpy.ceil(numpy.sqrt(N)).astype(numpy.int32)
  23 + h = c/N
  24 +
  25 + sp = c/N * 2 * numpy.pi * dN + numpy.pi/2
  26 + s = numpy.sin(sp) * 0.25 + 0.75
  27 +
  28 + vp = c/N * 2 * numpy.pi * dN
  29 + v = numpy.sin(vp) * 0.25 + 0.75
  30 +
  31 + rgb = numpy.array(colorsys.hsv_to_rgb(h, s, v))
  32 + return rgb * 255
  33 +
  34 +#generate a 2D color class map using a stack of binary class images
  35 +#input: C is a C x Y x X binary image
  36 +#output: an RGB color image with a unique color for each class
  37 +def class2color(C):
  38 +
  39 + #determine the number of classes
  40 + nc = C.shape[0]
  41 +
  42 + s = C.shape[1:]
  43 + s = numpy.append(s, 3)
  44 +
  45 + #generate an RGB image
  46 + RGB = numpy.zeros(s, dtype=numpy.ubyte)
  47 +
  48 + #for each class
  49 + for c in range(0, nc):
  50 + color = qualcolor(c, nc)
  51 + RGB[C[c, ...], :] = color
  52 +
  53 + return RGB
  54 +
  55 +#create a function that loads a set of class images as a stack of binary masks
  56 +#input: list of class image names
  57 +#output: C x Y x X binary image specifying class/pixel membership
  58 +#example: image2class(("class_coll.bmp", "class_epith.bmp"))
  59 +def filenames2class(masks):
  60 + #get num of mask file names
  61 + num_masks = len(masks)
  62 +
  63 + if num_masks == 0:
  64 + print("ERROR: mask filenames not provided")
  65 + print("Usage example: image2class(('class_coll.bmp', 'class_epith.bmp'))")
  66 + return
  67 +
  68 + classimages = []
  69 + bar = progressbar.ProgressBar(max_value=num_masks)
  70 + for m in range(0, num_masks):
  71 + img = scipy.misc.imread(masks[m], flatten=True).astype(numpy.bool)
  72 + classimages.append(img)
  73 + bar.update(m+1)
  74 +
  75 + result = numpy.stack(classimages)
  76 + sum_images = numpy.sum(result.astype(numpy.uint32), 0)
  77 +
  78 + #identify and remove redundant pixels
  79 + bad_idx = sum_images > 1
  80 + result[:, bad_idx] = 0
  81 +
  82 + return result
  83 +
  84 +
  85 +#create a class mask stack from an C x Y x X probability image
  86 +#input: C x Y x X image giving the probability P(c |x,y)
  87 +#output: C x Y x X binary class image
  88 +def prob2class(prob_image):
  89 + class_image = numpy.zeros(prob_image.shape, dtype=numpy.bool)
  90 + #get nonzero indices
  91 + nnz_idx = numpy.transpose(numpy.nonzero(numpy.sum(prob_image, axis=0)))
  92 +
  93 + #set pixel corresponding to max probability to 1
  94 + for idx in nnz_idx:
  95 + idx_max_prob = numpy.argmax(prob_image[:, idx[0], idx[1]])
  96 + class_image[idx_max_prob, idx[0], idx[1]] = 1
  97 +
  98 + return class_image
  99 +
  100 +#calculate an ROC curve given a probability image and mask of "True" values
  101 +#input:
  102 +# P is a Y x X probability image specifying P(c | x,y)
  103 +# t_vals is a Y x X binary image specifying points where x,y = c
  104 +# mask is a mask specifying all pixels to be considered (positives and negatives)
  105 +# use this mask to limit analysis to regions of the image that have been classified
  106 +#output: fpr, tpr, thresholds
  107 +# fpr is the false-positive rate (x-axis of an ROC curve)
  108 +# tpr is the true-positive rate (y-axis of an ROC curve)
  109 +# thresholds stores the threshold associated with each point on the ROC curve
  110 +#
  111 +#note: the AUC can be calculated as auc = sklearn.metrics.auc(fpr, tpr)
  112 +def prob2roc(P, t_vals, mask=[]):
  113 +
  114 + if not P.shape == t_vals.shape:
  115 + print("ERROR: the probability and mask images must be the same shape")
  116 + return
  117 +
  118 + #if a mask image isn't provided, create one for the entire image
  119 + if mask == []:
  120 + mask = numpy.ones(t_vals.shape, dtype=numpy.bool)
  121 +
  122 + #create masks for the positive and negative probability scores
  123 + mask_p = t_vals
  124 + mask_n = mask - mask * t_vals
  125 +
  126 + #calculate the indices for the positive and negative scores
  127 + idx_p = numpy.nonzero(mask_p)
  128 + idx_n = numpy.nonzero(mask_n)
  129 +
  130 + Pp = P[idx_p]
  131 + Pn = P[idx_n]
  132 +
  133 + Lp = numpy.ones((Pp.shape), dtype=numpy.bool)
  134 + Ln = numpy.zeros((Pn.shape), dtype=numpy.bool)
  135 +
  136 + scores = numpy.concatenate((Pp, Pn))
  137 + labels = numpy.concatenate((Lp, Ln))
  138 +
  139 + return sklearn.metrics.roc_curve(labels, scores)
  140 +
  141 +#convert a label image to a C x Y x X class image
  142 +def label2class(L, background=[]):
  143 + unique = numpy.unique(L)
  144 +
  145 + if not background == []: #if a background value is specified
  146 + unique = numpy.delete(unique, numpy.nonzero(unique == background)) #remove it from the label array
  147 + s = L.shape
  148 + s = numpy.append(numpy.array((len(unique))), s)
  149 + C = numpy.zeros(s, dtype=numpy.bool)
  150 + for i in range(0, len(unique)):
  151 + C[i, :, :] = L == unique[i]
  152 + return C
  153 +
  154 +#randomizes a given mask to include a subset of n pixels in the original
  155 +def random_mask(M, n):
  156 + idx = numpy.flatnonzero(M)
  157 + new_idx = numpy.random.permutation(idx)
  158 + new_mask = numpy.zeros(M.shape, dtype=numpy.bool)
  159 + new_mask[numpy.unravel_index(new_idx[0:n], new_mask.shape)] = True
  160 + return new_mask
  161 +
  162 +#perform classification of an ENVI image using batch processing
  163 +# input: E is the ENVI object (file is assumed to be loaded)
  164 +# C is a classifier - anything in sklearn should work
  165 +# batch is the batch size
  166 +def envi_batch_predict(E, C, batch=10000):
  167 +
  168 + Fv = E.loadbatch(batch)
  169 + i = 0
  170 + Tv = []
  171 + plt.ion()
  172 + bar = progressbar.ProgressBar(max_value=numpy.count_nonzero(E.mask))
  173 + while not Fv == []:
  174 + Fv = numpy.nan_to_num(Fv) #remove infinite values
  175 + if i == 0:
  176 + Tv = C.predict(Fv.transpose())
  177 + else:
  178 + Tv = numpy.concatenate((Tv, C.predict(Fv.transpose()).transpose()), 0)
  179 + tempmask = E.batchmask()
  180 + Lv = hyperspectral.unsift2(Tv, tempmask)
  181 + Cv = label2class(Lv.squeeze(), background=0)
  182 + RGB = class2color(Cv)
  183 + plt.imshow(RGB)
  184 + plt.pause(0.05)
  185 + Fv = E.loadbatch(batch)
  186 + i = i + 1
  187 + bar.update(len(Tv))
python/digitalstain.py 0 → 100644
  1 +# -*- coding: utf-8 -*-
  2 +"""
  3 +Created on Tue Jul 25 16:28:37 2017
  4 +
  5 +@author: david
  6 +"""
  7 +
  8 +import hyperspectral
  9 +import envi
  10 +import classify
  11 +import numpy
  12 +import scipy
  13 +import scipy.misc
  14 +import sklearn
  15 +import sklearn.naive_bayes
  16 +import sklearn.neural_network
  17 +import glob
  18 +import matplotlib.pyplot as plt
  19 +import random
  20 +
  21 +def generate_stain(envifile, stainfile, maskfile="", trainmask="", N=5000, batch_size=10000, validate=True):
  22 + if trainmask == "":
  23 + E = envi.envi(envifile)
  24 + else:
  25 + mask = scipy.misc.imread(trainmask, flatten=True)
  26 + E = envi.envi(envifile, mask=mask)
  27 +
  28 + mask = classify.random_mask(E.mask, N)
  29 + scipy.misc.imsave("random.bmp", mask)
  30 +
  31 + Ft = E.loadmask(mask).transpose()
  32 +
  33 + stain = numpy.rollaxis(scipy.misc.imread(stainfile), 2)
  34 + Tt = hyperspectral.sift2(stain, mask).transpose()
  35 +
  36 + print("Training MLPRegressor...")
  37 + CLASS = sklearn.neural_network.MLPRegressor(solver='lbfgs', alpha=1e-5, hidden_layer_sizes=(), random_state=1, verbose=True)
  38 + CLASS.fit(Ft, Tt)
  39 +
  40 + if validate == False:
  41 + return CLASS
  42 +
  43 + print("Validating Stain...")
  44 + plt.ion()
  45 + if not maskfile == "":
  46 + E.close() #close the ENVI file
  47 + mask = scipy.misc.imread(maskfile, flatten=True)
  48 + print(numpy.count_nonzero(mask))
  49 + E = envi.envi(envifile, mask=mask)
  50 +
  51 + Fv = E.loadbatch(batch_size) #load the first batch
  52 + n = 0
  53 + while not Fv == []: #loop until an empty batch is returned
  54 + if n == 0:
  55 + Tv = CLASS.predict(Fv.transpose()).transpose()
  56 + else:
  57 + Tv = numpy.append(Tv, CLASS.predict(Fv.transpose()).transpose(), 1) #append the predicted labels from this batch to those of previous batches
  58 + COLORS = hyperspectral.unsift2(Tv, E.batchmask()) #convert the matrix of class labels to a 2D array
  59 + RGB = numpy.rollaxis(COLORS, 0, 3).astype(numpy.ubyte)
  60 + plt.imshow(RGB) #display it
  61 + plt.pause(0.05)
  62 + Fv = E.loadbatch(batch_size) #load the next batch
  63 + n = n + 1
  64 + return CLASS, RGB
python/envi.py 0 → 100644
  1 +# -*- coding: utf-8 -*-
  2 +"""
  3 +Created on Fri Jul 21 20:18:01 2017
  4 +
  5 +@author: david
  6 +"""
  7 +
  8 +import os
  9 +import numpy
  10 +import scipy
  11 +import matplotlib.pyplot as plt
  12 +import progressbar
  13 +import sys
  14 +
  15 +class envi_header:
  16 + def __init__(self, filename = ""):
  17 + if filename != "":
  18 + self.load(filename)
  19 + else:
  20 + self.initialize()
  21 +
  22 + #initialization function
  23 + def initialize(self):
  24 + self.samples = int(0)
  25 + self.lines = int(0)
  26 + self.bands = int(0)
  27 + self.header_offset = int(0)
  28 + self.data_type = int(4)
  29 + self.interleave = "bsq"
  30 + self.sensor_type = "Unknown"
  31 + self.byte_order = int(0)
  32 + self.x_start = int(0)
  33 + self.y_start = int(0)
  34 + self.z_plot_titles = "Unknown, Unknown"
  35 + self.pixel_size = [float(0), float(0)]
  36 + self.pixel_size_units = "Meters"
  37 + self.wavelength_units = "Wavenumber"
  38 + self.description = "no description"
  39 + self.band_names = []
  40 + self.wavelength = []
  41 +
  42 + #convert an ENVI data_type value to a numpy data type
  43 + def get_numpy_type(self, val):
  44 + if val == 1:
  45 + return numpy.byte
  46 + elif val == 2:
  47 + return numpy.int16
  48 + elif val == 3:
  49 + return numpy.int32
  50 + elif val == 4:
  51 + return numpy.float32
  52 + elif val == 5:
  53 + return numpy.float64
  54 + elif val == 6:
  55 + return numpy.complex64
  56 + elif val == 9:
  57 + return numpy.complex128
  58 + elif val == 12:
  59 + return numpy.uint16
  60 + elif val == 13:
  61 + return numpy.uint32
  62 + elif val == 14:
  63 + return numpy.int64
  64 + elif val == 15:
  65 + return numpy.uint64
  66 +
  67 + def get_envi_type(self, val):
  68 + if val == numpy.byte:
  69 + return 1
  70 + elif val == numpy.int16:
  71 + return 2
  72 + elif val == numpy.int32:
  73 + return 3
  74 + elif val == numpy.float32:
  75 + return 4
  76 + elif val == numpy.float64:
  77 + return 5
  78 + elif val == numpy.complex64:
  79 + return 6
  80 + elif val == numpy.complex128:
  81 + return 9
  82 + elif val == numpy.uint16:
  83 + return 12
  84 + elif val == numpy.uint32:
  85 + return 13
  86 + elif val == numpy.int64:
  87 + return 14
  88 + elif val == numpy.uint64:
  89 + return 15
  90 +
  91 + def load(self, fname):
  92 + f = open(fname)
  93 + l = f.readlines()
  94 + if l[0].strip() != "ENVI":
  95 + print("ERROR: not an ENVI file")
  96 + return
  97 + li = 1
  98 + while li < len(l):
  99 + #t = l[li].split() #split the line into tokens
  100 + #t = map(str.strip, t) #strip all of the tokens in the token list
  101 +
  102 + #handle the simple conditions
  103 + #if l[li].startswith("file type"):
  104 + # if not l[li].strip().endswith("ENVI Standard"):
  105 + # print("ERROR: unsupported ENVI file format: " + l[li].strip())
  106 + # return
  107 + if l[li].startswith("samples"):
  108 + self.samples = int(l[li].split()[-1])
  109 + elif l[li].startswith("lines"):
  110 + self.lines = int(l[li].split()[-1])
  111 + elif l[li].startswith("bands"):
  112 + self.bands = int(l[li].split()[-1])
  113 + elif l[li].startswith("header offset"):
  114 + self.header_offset = int(l[li].split()[-1])
  115 + elif l[li].startswith("data type"):
  116 + self.data_type = self.get_numpy_type(int(l[li].split()[-1]))
  117 + elif l[li].startswith("interleave"):
  118 + self.interleave = l[li].split()[-1].strip()
  119 + elif l[li].startswith("sensor type"):
  120 + self.sensor_type = l[li].split()[-1].strip()
  121 + elif l[li].startswith("byte order"):
  122 + self.byte_order = int(l[li].split()[-1])
  123 + elif l[li].startswith("x start"):
  124 + self.x_start = int(l[li].split()[-1])
  125 + elif l[li].startswith("y start"):
  126 + self.y_start = int(l[li].split()[-1])
  127 + elif l[li].startswith("z plot titles"):
  128 + i0 = l[li].rindex('{')
  129 + i1 = l[li].rindex('}')
  130 + self.z_plot_titles = l[li][i0 + 1 : i1]
  131 + elif l[li].startswith("pixel size"):
  132 + i0 = l[li].rindex('{')
  133 + i1 = l[li].rindex('}')
  134 + s = l[li][i0 + 1 : i1].split(',')
  135 + self.pixel_size = [float(s[0]), float(s[1])]
  136 + self.pixel_size_units = s[2][s[2].rindex('=') + 1:].strip()
  137 + elif l[li].startswith("wavelength units"):
  138 + self.wavelength_units = l[li].split()[-1].strip()
  139 +
  140 + #handle the complicated conditions
  141 + elif l[li].startswith("description"):
  142 + desc = [l[li]]
  143 + '''
  144 + while l[li].strip()[-1] != '}': #will fail if l[li].strip() is empty
  145 + li += 1
  146 + desc.append(l[li])
  147 + '''
  148 + while True:
  149 + if l[li].strip():
  150 + if l[li].strip()[-1] == '}':
  151 + break
  152 + li += 1
  153 + desc.append(l[li])
  154 +
  155 + desc = ''.join(list(map(str.strip, desc))) #strip all white space from the string list
  156 + i0 = desc.rindex('{')
  157 + i1 = desc.rindex('}')
  158 + self.description = desc[i0 + 1 : i1]
  159 +
  160 + elif l[li].startswith("band names"):
  161 + names = [l[li]]
  162 + while l[li].strip()[-1] != '}':
  163 + li += 1
  164 + names.append(l[li])
  165 + names = ''.join(list(map(str.strip, names))) #strip all white space from the string list
  166 + i0 = names.rindex('{')
  167 + i1 = names.rindex('}')
  168 + names = names[i0 + 1 : i1]
  169 + self.band_names = list(map(str.strip, names.split(',')))
  170 + elif l[li].startswith("wavelength"):
  171 + waves = [l[li]]
  172 + while l[li].strip()[-1] != '}':
  173 + li += 1
  174 + waves.append(l[li])
  175 + waves = ''.join(list(map(str.strip, waves))) #strip all white space from the string list
  176 + i0 = waves.rindex('{')
  177 + i1 = waves.rindex('}')
  178 + waves = waves[i0 + 1 : i1]
  179 + self.wavelength = list(map(float, waves.split(',')))
  180 +
  181 + li += 1
  182 +
  183 + f.close()
  184 +
  185 + #save an ENVI header
  186 + def save(self, fname):
  187 + f = open(fname, "w")
  188 + f.write("ENVI\n")
  189 + f.write("description = {" + self.description + "}" + "\n")
  190 + f.write("samples = " + str(self.samples) + "\n")
  191 + f.write("lines = " + str(self.lines) + "\n")
  192 + f.write("bands = " + str(self.bands) + "\n")
  193 + f.write("header offset = " + str(self.header_offset) + "\n")
  194 + f.write("file type = ENVI Standard" + "\n")
  195 + f.write("data type = " + str(self.get_envi_type(self.type)) + "\n")
  196 + f.write("interleave = " + self.interleave + "\n")
  197 + f.write("sensor type = " + self.sensor_type + "\n")
  198 + f.write("byte order = " + str(self.byte_order) + "\n")
  199 + f.write("x start = " + str(self.x_start) + "\n")
  200 + f.write("y start = " + str(self.y_start) + "\n")
  201 + f.write("wavelength units = " + self.wavelength_units + "\n")
  202 + f.write("z plot titles = {" + self.z_plot_titles + "}" + "\n")
  203 +
  204 + f.close()
  205 +
  206 + #sets the properties of the header to match those of the input array
  207 + def set(self, A):
  208 + self.type = A.dtype
  209 + self.samples = A.shape[2]
  210 + self.lines = A.shape[1]
  211 + self.bands = A.shape[0]
  212 +
  213 +
  214 +class envi:
  215 + def __init__(self, filename, headername = "", mask = []):
  216 + self.open(filename, headername)
  217 + if mask == []:
  218 + self.mask = numpy.ones((self.header.lines, self.header.samples), dtype=numpy.bool)
  219 + elif type(mask) == numpy.ndarray:
  220 + self.mask = mask
  221 + else:
  222 + print("ERROR: unrecognized mask format - expecting a boolean array")
  223 + self.idx = 0 #initialize the batch IDX to 0 for batch reading
  224 +
  225 + def open(self, filename, headername = ""):
  226 + if headername == "":
  227 + headername = filename + ".hdr"
  228 +
  229 + if not os.path.isfile(filename):
  230 + print("ERROR: " + filename + " not found")
  231 + return
  232 + if not os.path.isfile(headername):
  233 + print("ERROR: " + headername + " not found")
  234 + return
  235 +
  236 + #open the file
  237 + self.header = envi_header(headername)
  238 + self.file = open(filename, "rb")
  239 +
  240 + def loadall(self):
  241 + X = self.header.samples
  242 + Y = self.header.lines
  243 + B = self.header.bands
  244 +
  245 + #load the data
  246 + D = numpy.fromfile(self.file, dtype=self.header.data_type)
  247 +
  248 + if self.header.interleave == "bsq":
  249 + return numpy.reshape(D, (B, Y, X))
  250 + #return numpy.swapaxes(D, 0, 2)
  251 + elif self.header.interleave == "bip":
  252 + D = numpy.reshape(D, (Y, X, B))
  253 + return numpy.rollaxis(D, 2)
  254 + elif self.header.interleave == "bil":
  255 + D = numpy.reshape(D, (Y, B, X))
  256 + return numpy.rollaxis(D, 1)
  257 +
  258 + #loads all of the pixels where mask != 0 and returns them as a matrix
  259 + def loadmask(self, mask):
  260 + X = self.header.samples
  261 + Y = self.header.lines
  262 + B = self.header.bands
  263 +
  264 + P = numpy.count_nonzero(mask) #count the number of zeros in the mask file
  265 + M = numpy.zeros((B, P), dtype=self.header.data_type)
  266 + type_bytes = numpy.dtype(self.header.data_type).itemsize
  267 +
  268 + prev_pos = self.file.tell()
  269 + self.file.seek(0)
  270 + if self.header.interleave == "bip":
  271 + spectrum = numpy.zeros(B, dtype=self.header.data_type)
  272 + flatmask = numpy.reshape(mask, (X * Y))
  273 + i = numpy.flatnonzero(flatmask)
  274 + bar = progressbar.ProgressBar(max_value = P)
  275 + for p in range(0, P):
  276 + self.file.seek(i[p] * B * type_bytes)
  277 + self.file.readinto(spectrum)
  278 + M[:, p] = spectrum
  279 + bar.update(p+1)
  280 + elif self.header.interleave == "bsq":
  281 + band = numpy.zeros(mask.shape, dtype=self.header.data_type)
  282 + i = numpy.nonzero(mask)
  283 + bar = progressbar.ProgressBar(max_value=B)
  284 + for b in range(0, B):
  285 + self.file.seek(b * X * Y * type_bytes)
  286 + self.file.readinto(band)
  287 + M[b, :] = band[i]
  288 + bar.update(b+1)
  289 + elif self.header.interleave == "bil":
  290 + plane = numpy.zeros((B, X), dtype=self.header.data_type)
  291 + p = 0
  292 + bar = progressbar.ProgressBar(max_value=Y)
  293 + for l in range(0, Y):
  294 + i = numpy.flatnonzero(mask[l, :])
  295 + self.file.readinto(plane)
  296 + M[:, p:p+i.shape[0]] = plane[:, i]
  297 + p = p + i.shape[0]
  298 + bar.update(l+1)
  299 + self.file.seek(prev_pos)
  300 + return M
  301 +
  302 + def loadband(self, n):
  303 + X = self.header.samples
  304 + Y = self.header.lines
  305 + B = self.header.bands
  306 +
  307 + band = numpy.zeros((Y, X), dtype=self.header.data_type)
  308 + type_bytes = numpy.dtype(self.header.data_type).itemsize
  309 +
  310 + prev_pos = self.file.tell()
  311 + if self.header.interleave == "bsq":
  312 + self.file.seek(n * X * Y * type_bytes)
  313 + self.file.readinto(band)
  314 + self.file.seek(prev_pos)
  315 + return band
  316 +
  317 + #create a set of feature/target pairs for classification
  318 + #input: envi file object, stack of class masks C x Y x X
  319 + #output: feature matrix (features x pixels), target matrix (1 x pixels)
  320 + #example: generate_training(("class_coll.bmp", "class_epith.bmp"), (1, 2))
  321 + # verify verify that there are no NaN or Inf values
  322 + def loadtrain(self, classimages, verify=True):
  323 +
  324 + # get number of classes
  325 + C = classimages.shape[0]
  326 +
  327 + F = []
  328 + T = []
  329 + for c in range(0, C):
  330 + print("\nLoading class " + str(c+1) + "...")
  331 + f = self.loadmask(classimages[c, :, :]) #load the feature matrix for class c
  332 + t = numpy.ones((f.shape[1])) * (c+1) #generate a target array
  333 + F.append(f)
  334 + T.append(t)
  335 +
  336 + return numpy.nan_to_num(numpy.concatenate(F, 1).transpose()), numpy.concatenate(T)
  337 +
  338 + #read a batch of data based on the mask
  339 + def loadbatch(self, npixels):
  340 + i = numpy.flatnonzero(self.mask) #get the indices of valid pixels
  341 + if len(i) == self.idx: #if all of the pixels have been read, return an empyt array
  342 + return []
  343 + npixels = min(npixels, len(i) - self.idx) #if there aren't enough pixels, change the batch size
  344 + B = self.header.bands
  345 +
  346 + batch = numpy.zeros((B, npixels), dtype=self.header.data_type) #allocate space for the batch
  347 + pixel = numpy.zeros((B), dtype=self.header.data_type) #allocate space for a single pixel
  348 + type_bytes = numpy.dtype(self.header.data_type).itemsize #calculate the size of a single value
  349 + if self.header.interleave == "bip":
  350 + for n in range(0, npixels): #for each pixel in the batch
  351 + self.file.seek(i[self.idx] * B * type_bytes) #seek to the current pixel in the file
  352 + self.file.readinto(pixel) #read a single pixel
  353 + batch[:, n] = pixel #save the pixel into the batch matrix
  354 + self.idx = self.idx + 1
  355 + return batch
  356 + elif self.header.interleave == "bsq":
  357 + print("ERROR: BSQ batch loading isn't implemented yet!")
  358 + elif self.header.interleave == "bil":
  359 + print("ERROR: BIL batch loading isn't implemented yet!")
  360 +
  361 + #returns the current batch index
  362 + def getidx(self):
  363 + return self.idx
  364 +
  365 + #returns an image of the pixels that have been read using batch loading
  366 + def batchmask(self):
  367 + #allocate a new mask
  368 + outmask = numpy.zeros(self.mask.shape, dtype=numpy.bool)
  369 +
  370 + #zero out any unclassified pixels
  371 + idx = self.getidx()
  372 + i = numpy.nonzero(self.mask)
  373 + outmask[i[0][0:idx], i[1][0:idx]] = self.mask[i[0][0:idx], i[1][0:idx]]
  374 + return outmask
  375 +
  376 + def close(self):
  377 + self.file.close()
  378 +
  379 + def __del__(self):
  380 + self.file.close()
  381 +
  382 +#saves an array as an ENVI file
  383 +def save_envi(A, fname):
  384 +
  385 + #create and save a header file
  386 + header = envi_header();
  387 + header.set(A)
  388 + header.save(fname + ".hdr")
  389 +
  390 + #save the raw data
  391 + file = open(fname, "wb")
  392 + file.write(bytearray(A))
  393 + file.close()
0 \ No newline at end of file 394 \ No newline at end of file
python/example.py 0 → 100644
  1 +import numpy
  2 +import classify
  3 +import matplotlib.pyplot as plt
  4 +from envi import envi
  5 +
  6 +mask_path = '/home/sberisha/data/masks/'
  7 +mask_stack = classify.image2class(mask_path + "class_blood.png", mask_path + "class_coll.png", mask_path + "class_epith.png",
  8 + mask_path + "class_lymph.png", mask_path + "class_necrosis.png")
  9 +
  10 +color_image = classify.classcolor2(mask_stack)
  11 +plt.imshow(color_image)
  12 +
  13 +data_path ='/home/sberisha/data/cnn/brc961-nfp8/envi/'
  14 +
  15 +feature_matrix, target_matrix = classify.generate_training(data_path + 'brc961-nfp8-project-br1003', mask_stack)
  16 +
  17 +prob_path = '/home/sberisha/data/'
  18 +prob_envi= envi(prob_path + "cnn-response")
  19 +prob_image = prob_envi.loadall()
  20 +
  21 +class_image = classify.prob2class(prob_image)
  22 +plt.imshow(class_image[4,:,:])
0 \ No newline at end of file 23 \ No newline at end of file
python/hyperspectral.py 0 → 100644
  1 +# -*- coding: utf-8 -*-
  2 +"""
  3 +Created on Sun Jul 23 13:52:22 2017
  4 +
  5 +@author: david
  6 +"""
  7 +import numpy
  8 +
  9 +#sift a 2D hyperspectral image into a PxB matrix where P is the number of pixels and B is the number of bands
  10 +def sift2(I, mask = []):
  11 +
  12 + #get the shape of the input array
  13 + S = I.shape
  14 +
  15 + #convert that array into a 1D matrix
  16 + M = numpy.reshape(I, (S[0], S[1] * S[2]))
  17 +
  18 + #gif no mask is provided, just return all pixels
  19 + if mask == []:
  20 + return M
  21 +
  22 + #if a mask is provided, only return pixels corresponding to that mask
  23 + flatmask = numpy.reshape(mask, (S[1] * S[2]))
  24 + i = numpy.flatnonzero(flatmask) #get the nonzero indices
  25 + return M[:, i] #return pixels corresponding to the masked values
  26 +
  27 +def unsift2(M, mask):
  28 +
  29 + #get the size of the input matrix
  30 + S = M.shape
  31 +
  32 + #count the number of nonzero values in the mask
  33 + nnz = numpy.count_nonzero(mask)
  34 +
  35 + #the number of masked values should be the same as the number of pixels in the input matrix
  36 + if len(S) == 1:
  37 + if not S[0] == nnz:
  38 + print("ERROR: expected " + str(nnz) + " pixels based on the mask but there are " + str(S[0]) + " in the matrix.")
  39 + elif not S[1] == nnz:
  40 + print("ERROR: expected " + str(nnz) + " pixels based on the mask but there are " + str(S[1]) + " in the matrix.")
  41 +
  42 +
  43 + i = numpy.nonzero(mask)
  44 +
  45 + if len(S) == 1:
  46 + I = numpy.zeros((1, mask.shape[0], mask.shape[1]), dtype=M.dtype)
  47 + else:
  48 + I = numpy.zeros((M.shape[0], mask.shape[0], mask.shape[1]), dtype=M.dtype)
  49 + I[:, i[0], i[1]] = M
  50 + return I
  51 +
  52 +#create a function that sifts a color image
  53 +#input: image name, mask
0 \ No newline at end of file 54 \ No newline at end of file
python/network.py 0 → 100644
  1 +# -*- coding: utf-8 -*-
  2 +"""
  3 +Created on Sat Sep 16 16:34:49 2017
  4 +
  5 +@author: pavel
  6 +"""
  7 +
  8 +import struct
  9 +import numpy as np
  10 +import scipy as sp
  11 +import networkx as nx
  12 +import matplotlib.pyplot as plt
  13 +import math
  14 +import time
  15 +import spharmonics
  16 +
  17 +'''
  18 + Definition of the Node class
  19 + Duplicate of the node class in network
  20 + Stores the physical position, outgoing edges list and incoming edges list.
  21 +'''
  22 +class Node:
  23 + def __init__(self, point, outgoing, incoming):
  24 + self.p = point
  25 + self.o = outgoing
  26 + self.i = incoming
  27 +
  28 +# def p():
  29 +# return self.p
  30 +
  31 +'''
  32 + Definition of the Fiber class.
  33 + Duplicate of the Node class in network
  34 + Stores the starting vertex, the ending vertex, the points array and the radius array
  35 +'''
  36 +class Fiber:
  37 +
  38 +
  39 + def __init__ (self, p1, p2, pois, rads):
  40 + self.v0 = p1
  41 + self.v1 = p2
  42 + self.points = pois
  43 + self.radii = rads
  44 +
  45 + '''
  46 + return the length of the fiber.
  47 + '''
  48 + def length(self):
  49 + length = 0
  50 + for i in range(len(self.points)-1):
  51 + length = length + math.sqrt(pow(self.points[i][0]- self.points[i+1][0],2) + pow(self.points[i][1]- self.points[i+1][1],2) + pow(self.points[i][2]- self.points[i+1][2],2))
  52 +
  53 + return length
  54 +
  55 + '''
  56 + returns the turtuosity of the fiber.
  57 + '''
  58 + def turtuosity(self):
  59 + turtuosity = 0
  60 + distance = math.sqrt(math.pow(self.points[0][0]- self.points[len(self.points)-1][0],2) + math.pow(self.points[0][1]- self.points[len(self.points)-1][1],2) + math.pow(self.points[0][2]- self.points[len(self.points)-1][2],2))
  61 + turtuosity = self.length()/distance
  62 + #print(turtuosity)
  63 +
  64 + return turtuosity
  65 +
  66 + '''
  67 + returns the volume of the fiber.
  68 + '''
  69 + def volume(self):
  70 + volume = 0
  71 + for i in range(len(self.points)-1):
  72 + volume = volume + 1.0/3.0 * math.pi * (math.pow(self.radii[i],2) + math.pow(self.radii[i+1],2) + self.radii[i]*self.radii[i+1]) * math.sqrt(math.pow(self.points[i][0]- self.points[i+1][0],2) + math.pow(self.points[i][1]- self.points[i+1][1],2) + math.pow(self.points[i][2]- self.points[i+1][2],2))
  73 +
  74 + #print(volume)
  75 + return volume
  76 +
  77 +class NWT:
  78 +
  79 + '''
  80 + Writes the header given and open file descripion, number of verticies and number of edges.
  81 + '''
  82 + def writeHeader(open_file, numVerts, numEdges):
  83 + txt = "nwtFileFormat fileid(14B), desc(58B), #vertices(4B), #edges(4B): bindata"
  84 + b = bytearray()
  85 + b.extend(txt.encode())
  86 + open_file.write(b)
  87 + open_file.write(struct.pack('i', numVerts))
  88 + open_file.write(struct.pack('i', numEdges))
  89 +
  90 +
  91 + '''
  92 + Writes a single vertex to a file.
  93 + '''
  94 + def writeVertex(open_file, vertex):
  95 + open_file.write(struct.pack('<f',vertex.p[0]))
  96 + open_file.write(struct.pack('<f',vertex.p[1]))
  97 + open_file.write(struct.pack('<f',vertex.p[2]))
  98 + open_file.write(struct.pack('i', len(vertex.o)))
  99 + open_file.write(struct.pack('i', len(vertex.i)))
  100 + for j in range(len(vertex.o)):
  101 + open_file.write(struct.pack('i',vertex.o[j]))
  102 +
  103 + for j in range(len(vertex.i)):
  104 + open_file.write(struct.pack('i', vertex.i[j]))
  105 +
  106 + return
  107 +
  108 + '''
  109 + Writes a single fiber to a file.
  110 + '''
  111 + def writeFiber(open_file, edge):
  112 + open_file.write(struct.pack('i',edge.v0))
  113 + open_file.write(struct.pack('i',edge.v1))
  114 + open_file.write(struct.pack('i',len(edge.points)))
  115 + for j in range(len(edge.points)):
  116 + open_file.write(struct.pack('<f', edge.points[j][0]))
  117 + open_file.write(struct.pack('<f', edge.points[j][1]))
  118 + open_file.write(struct.pack('<f', edge.points[j][2]))
  119 + open_file.write(struct.pack('<f', edge.radii[j]))
  120 +
  121 + return
  122 +
  123 + '''
  124 + Writes the entire network to a file in str given the vertices array and the edges array.
  125 + '''
  126 + def exportNWT(str, vertices, edges):
  127 + with open(str, "wb") as file:
  128 + NWT.writeHeader(file, len(vertices), len(edges))
  129 + for i in range(len(vertices)):
  130 + NWT.writeVertex(file, vertices[i])
  131 +
  132 + for i in range(len(edges)):
  133 + NWT.writeFiber(file, edges[i])
  134 +
  135 + return
  136 +
  137 +
  138 + '''
  139 + Reads a single vertex from an open file and returns a node Object.
  140 + '''
  141 + def readVertex(open_file):
  142 + points = np.tile(0., 3)
  143 + bytes = open_file.read(4)
  144 + points[0] = struct.unpack('f', bytes)[0]
  145 + bytes = open_file.read(4)
  146 + points[1] = struct.unpack('f', bytes)[0]
  147 + bytes = open_file.read(4)
  148 + points[2] = struct.unpack('f', bytes)[0]
  149 + bytes = open_file.read(4)
  150 +
  151 + numO = int.from_bytes(bytes, byteorder='little')
  152 + outgoing = np.tile(0, numO)
  153 + bts = open_file.read(4)
  154 + numI = int.from_bytes(bts, byteorder='little')
  155 + incoming = np.tile(0, numI)
  156 + for j in range(numO):
  157 + bytes = open_file.read(4)
  158 + outgoing[j] = int.from_bytes(bytes, byteorder='little')
  159 +
  160 + for j in range(numI):
  161 + bytes = open_file.read(4)
  162 + incoming[j] = int.from_bytes(bytes, byteorder='little')
  163 +
  164 + node = Node(points, outgoing, incoming)
  165 + return node
  166 +
  167 +
  168 + '''
  169 + Reads a single fiber from an open file and returns a Fiber object .
  170 + '''
  171 + def readFiber(open_file):
  172 + bytes = open_file.read(4)
  173 + vtx0 = int.from_bytes(bytes, byteorder = 'little')
  174 + bytes = open_file.read(4)
  175 + vtx1 = int.from_bytes(bytes, byteorder = 'little')
  176 + bytes = open_file.read(4)
  177 + numVerts = int.from_bytes(bytes, byteorder = 'little')
  178 + pts = []
  179 + rads = []
  180 +
  181 + for j in range(numVerts):
  182 + point = np.tile(0., 3)
  183 + bytes = open_file.read(4)
  184 + point[0] = struct.unpack('f', bytes)[0]
  185 + bytes = open_file.read(4)
  186 + point[1] = struct.unpack('f', bytes)[0]
  187 + bytes = open_file.read(4)
  188 + point[2] = struct.unpack('f', bytes)[0]
  189 + bytes = open_file.read(4)
  190 + radius = struct.unpack('f', bytes)[0]
  191 + pts.append(point)
  192 + rads.append(radius)
  193 +
  194 + F = Fiber(vtx0, vtx1, pts, rads)
  195 +
  196 + return F
  197 +
  198 + '''
  199 + Imports a NWT file at location str.
  200 + Returns a list of Nodes objects and a list of Fiber objects.
  201 + '''
  202 +
  203 +class Network:
  204 +
  205 + def __init__(self, filename, clock=False):
  206 + if clock:
  207 + start_time = time.time()
  208 +
  209 + with open(filename, "rb") as file:
  210 + header = file.read(72)
  211 + bytes = file.read(4)
  212 + numVertex = int.from_bytes(bytes, byteorder='little')
  213 + bytes = file.read(4)
  214 + numEdges = int.from_bytes(bytes, byteorder='little')
  215 +
  216 + self.N = []
  217 + self.F = []
  218 + for i in range(numVertex):
  219 + node = NWT.readVertex(file)
  220 + self.N.append(node)
  221 +
  222 + for i in range(numEdges):
  223 + edge = NWT.readFiber(file)
  224 + self.F.append(edge)
  225 + if clock:
  226 + print("Network initialization: " + str(time.time() - start_time) + "s")
  227 +
  228 + '''
  229 + Creates a graph from a list of nodes and a list of edges.
  230 + Uses edge length as weight.
  231 + Returns a NetworkX Object.
  232 + '''
  233 +# def createLengthGraph(self):
  234 +# G = nx.Graph()
  235 +# for i in range(len(self.nodeList)):
  236 +# G.add_node(i, p=V[i].p)
  237 +# for i in range(len(self.edgeList)):
  238 +# G.add_edge(self.edgeList[i].v0, self.edgeList[i].v1, weight = E[i].length())
  239 +#
  240 +# return G
  241 +# '''
  242 +# Creates a graph from a list of nodes and a list of edges.
  243 +# Uses edge turtuosity as weight.
  244 +# Returns a NetworkX Object.
  245 +# '''
  246 +# def createTortuosityGraph(nodeList, edgeList):
  247 +# G = nx.Graph()
  248 +# for i in range(len(nodeList)):
  249 +# G.add_node(i, p=V[i].p)
  250 +# for i in range(len(edgeList)):
  251 +# G.add_edge(edgeList[i].v0, edgeList[i].v1, weight = E[i].turtuosity())
  252 +#
  253 +# return G
  254 +
  255 +# '''
  256 +# Creates a graph from a list of nodes and a list of edges.
  257 +# Uses edge volume as weight.
  258 +# Returns a NetworkX Object.
  259 +# '''
  260 +# def createVolumeGraph(nodeList, edgeList):
  261 +# G = nx.Graph()
  262 +# for i in range(len(nodeList)):
  263 +# G.add_node(i, p=V[i].p)
  264 +# for i in range(len(edgeList)):
  265 +# G.add_edge(edgeList[i].v0, edgeList[i].v1, weight = E[i].volume())
  266 +#
  267 +# return G
  268 +#'''
  269 +#Returns the positions dictionary for the Circular layout.
  270 +#'''
  271 +#def getCircularLayout(graph, dim, radius):
  272 +# return nx.circular_layout(graph, dim, radius)
  273 +#
  274 +#'''
  275 +#Return the positions dictionary for the Spring layout.
  276 +#'''
  277 +#def getSpringLayout(graph, pos, iterations, scale):
  278 +# return nx.spring_layout(graph, 2, None, pos, iterations, 'weight', scale, None)
  279 +#
  280 +#'''
  281 +#Draws the graph.
  282 +#'''
  283 +#def drawGraph(graph, pos):
  284 +# nx.draw(graph, pos)
  285 +# return
  286 +
  287 + def aabb(self):
  288 +
  289 + lower = self.N[0].p.copy()
  290 + upper = lower.copy()
  291 + for i in self.N:
  292 + for c in range(len(lower)):
  293 + if lower[c] > i.p[c]:
  294 + lower[c] = i.p[c]
  295 + if upper[c] < i.p[c]:
  296 + upper[c] = i.p[c]
  297 + return lower, upper
  298 +
  299 + #calculate the distance field at a given resolution
  300 + # R (triple) resolution along each dimension
  301 + def distancefield(self, R=(100, 100, 100)):
  302 +
  303 + #get a list of all node positions in the network
  304 + P = []
  305 + for e in self.F:
  306 + for p in e.points:
  307 + P.append(p)
  308 +
  309 + #turn that list into a Numpy array so that we can create a KD tree
  310 + P = np.array(P)
  311 +
  312 + #generate a KD-Tree out of the network point array
  313 + tree = sp.spatial.cKDTree(P)
  314 +
  315 + plt.scatter(P[:, 0], P[:, 1])
  316 +
  317 + #specify the resolution of the ouput grid
  318 + R = (200, 200, 200)
  319 +
  320 + #generate a meshgrid of the appropriate size and resolution to surround the network
  321 + lower, upper = self.aabb(self.N, self.F) #get the space occupied by the network
  322 + x = np.linspace(lower[0], upper[0], R[0]) #get the grid points for uniform sampling of this space
  323 + y = np.linspace(lower[1], upper[1], R[1])
  324 + z = np.linspace(lower[2], upper[2], R[2])
  325 + X, Y, Z = np.meshgrid(x, y, z)
  326 + #Z = 150 * numpy.ones(X.shape)
  327 +
  328 +
  329 + Q = np.stack((X, Y, Z), 3)
  330 +
  331 +
  332 + D, I = tree.query(Q)
  333 +
  334 + return D
  335 +
  336 + #returns the number of points in the network
  337 + def npoints(self):
  338 + n = 0 #initialize the counter to zero
  339 + for f in self.F: #for each fiber
  340 + n = n + len(f.points) - 2 #count the number of points in the fiber - ignoring the end points
  341 + n = n + len(self.N) #add the number of nodes (shared points) to the node count
  342 + return n #return the number of nodes
  343 +
  344 + #returns all of the points in the network
  345 + def points(self):
  346 + k = self.npoints()
  347 + P = np.zeros((3, k)) #allocate space for the point list
  348 +
  349 + idx = 0
  350 + for f in self.F: #for each fiber in the network
  351 + for ip in range(1, len(f.points)-1): #for each point in the network
  352 + P[:, idx] = f.points[ip] #store the point in the raw point list
  353 + idx = idx + 1
  354 + return P #return the point array
  355 +
  356 + #returns the number of linear segments in the network
  357 + def nsegments(self):
  358 + n = 0 #initialize the segment counter to 0
  359 + for f in self.F: #for each fiber
  360 + n = n + len(f.points) - 1 #calculate the number of line segments in the fiber (points - 1)
  361 + return n #return the number of line segments
  362 +
  363 + #return a list of line segments representing the network
  364 + def segments(self, dtype=np.float32):
  365 + k = self.nsegments() #get the number of line segments
  366 + start = np.zeros((k, 3),dtype=dtype) #start points for the line segments
  367 + end = np.zeros((k, 3), dtype=dtype) #end points for the line segments
  368 +
  369 + idx = 0 #initialize the index counter to zero
  370 + for f in self.F: #for each fiber in the network
  371 + for ip in range(0, len(f.points)-1): #for each point in the network
  372 + start[idx, :] = f.points[ip] #store the point in the raw point list
  373 + idx = idx + 1
  374 +
  375 + idx = 0
  376 + for f in self.F: #for each fiber in the network
  377 + for ip in range(1, len(f.points)): #for each point in the network
  378 + end[idx, :] = f.points[ip] #store the point in the raw point list
  379 + idx = idx + 1
  380 +
  381 + return start, end
  382 +
  383 + #function returns the fiber associated with a given 1D line segment index
  384 + def segment2fiber(self, idx):
  385 + i = 0
  386 + for f in range(len(self.F)): #for each fiber in the network
  387 + i = i + len(self.F[f].points)-1 #add the number of points in the fiber to i
  388 + if i > idx: #if we encounter idx in this fiber
  389 + return self.F[f].points, f #return the fiber associated with idx and the index into the fiber array
  390 +
  391 + def vectors(self, clock=False, dtype=np.float32):
  392 + if clock:
  393 + start_time = time.time()
  394 + start, end = self.segments(dtype) #retrieve all of the line segments
  395 + v = end - start #calculate the resulting vectors
  396 + l = np.sqrt(v[:, 0]**2 + v[:,1]**2 + v[:,2]**2) #calculate the fiber lengths
  397 + z = l==0 #look for any zero values
  398 + nz = z.sum()
  399 + if nz > 0:
  400 + print("WARNING: " + str(nz) + " line segment(s) of length zero were found in the network and will be removed" )
  401 +
  402 + if clock:
  403 + print("Network::vectors: " + str(time.time() - start_time) + "s")
  404 +
  405 + return np.delete(v, np.where(z), 0)
  406 +
  407 + #scale all values in the network by tuple S = (sx, sy, sz)
  408 + def scale(self, S):
  409 + for f in self.F:
  410 + for p in f.points:
  411 + p[0] = p[0] * S[0]
  412 + p[1] = p[1] * S[1]
  413 + p[2] = p[2] * S[2]
  414 +
  415 + for n in self.N:
  416 + n.p[0] = n.p[0] * S[0]
  417 + n.p[1] = n.p[1] * S[1]
  418 + n.p[2] = n.p[2] * S[2]
  419 +
  420 +
  421 + #calculate the adjacency weighting function for the network given a set of vectors X = (x, y, z) and weight exponent k
  422 + def adjacencyweight(self, P, k=200, length_threshold = 25, dtype=np.float32):
  423 + V = self.vectors(dtype) #get the vectors representing each segment
  424 + #V = V[0:n_vectors, :]
  425 + L = np.expand_dims(np.sqrt((V**2).sum(1)), 1) #calculate the length of each vector
  426 +
  427 + outliers = L > length_threshold #remove outliers based on the length_threshold
  428 + V = np.delete(V, np.where(outliers), 0)
  429 + L = np.delete(L, np.where(outliers))
  430 + V = V/L[:,None] #normalize the vectors
  431 +
  432 + P = np.stack(spharmonics.sph2cart(1, P[0], P[1]), P[0].ndim)
  433 + PV = P[...,None,:] * V
  434 + cos_alpha = PV.sum(PV.ndim-1)
  435 + W = np.abs(cos_alpha) ** k
  436 +
  437 + return W, L
  438 +
  439 +
python/network.pyc 0 → 100644
No preview for this file type
python/network_dpy.py 0 → 100644
  1 +# -*- coding: utf-8 -*-
  2 +"""
  3 +Created on Sat Jan 19 2018
  4 +
  5 +@author: Jiabing
  6 +"""
  7 +
  8 +import struct
  9 +import numpy as np
  10 +import scipy as sp
  11 +import networkx as nx
  12 +import matplotlib.pyplot as plt
  13 +import math
  14 +
  15 +class Point:
  16 + def __init__(self, x, y, z, radius):
  17 + self.x = x
  18 + self.y = y
  19 + self.z = z
  20 + self.r = radius
  21 +
  22 +
  23 +class Fiber:
  24 + def __init__(self, fiber_idx,point_idx):
  25 + self.fidx = fiber_idx
  26 + self.pidx = point_idx
  27 +
  28 +
  29 +
  30 +class Node:
  31 + def __init__(self, point_idx, fidx):
  32 + self.pidx= point_idx
  33 + self.fidx = fidx
  34 +
  35 +
  36 +#class NWT:
  37 +
  38 +#class network(point, Fiber, Node):
0 \ No newline at end of file 39 \ No newline at end of file
python/phantom.py 0 → 100644
  1 +# -*- coding: utf-8 -*-
  2 +"""
  3 +Created on Thu May 18 15:56:20 2017
  4 +
  5 +@author: david
  6 +"""
  7 +
  8 +import numpy as np
  9 +import scipy as sp
  10 +import scipy.misc
  11 +import scipy.ndimage
  12 +import matplotlib.pyplot as plt
  13 +import os
  14 +
  15 +#calculate a 3D image from a 2D binary mask
  16 +def binary(infile, sigma=2):
  17 +
  18 + I = sp.misc.imread(infile).astype(np.bool)
  19 +
  20 + #if the image has more than one channel, just keep the first one
  21 + if(I.ndim == 3):
  22 + I = I[:, :, 0]
  23 +
  24 + L = []
  25 + while np.count_nonzero(I) != 0:
  26 + L.append(I)
  27 + I = sp.ndimage.binary_erosion(I)
  28 +
  29 + #create a 3D image representing the new stack
  30 + S = np.zeros( (I.shape[0], I.shape[1], len(L) * 2 - 1) )
  31 +
  32 + #for each image in the list
  33 + for i in range(0, len(L)):
  34 + if(i == 0):
  35 + S[:, :, len(L) - 1] = L[0]
  36 + else:
  37 + S[:, :, len(L) - 1 + i] = L[i]
  38 + S[:, :, len(L) - 1 - i] = L[i]
  39 +
  40 + S = sp.ndimage.filters.gaussian_filter(S, sigma)
  41 + return S
  42 +
  43 +#generate a 3D image stack from a 2D binary mask
  44 +def binary_stack(infile, outdir, sigma=2):
  45 + outfile_base = os.path.basename(infile)
  46 + outfile_prefix, outfile_ext = os.path.splitext(outfile_base)
  47 +
  48 + S = binary(infile, sigma)
  49 +
  50 + zcount = len(str(S.shape[2]))
  51 + for f in range(0, S.shape[2]):
  52 + fname = outdir + "/" + outfile_prefix + str(f).zfill(zcount) + outfile_ext
  53 +
  54 + sp.misc.imsave(fname, S[:, :, f])
  55 +
  56 +
  57 +
  58 +
python/spharmonics.py 0 → 100644
  1 +# -*- coding: utf-8 -*-
  2 +"""
  3 +Created on Mon Dec 18 16:31:36 2017
  4 +
  5 +@author: david
  6 +"""
  7 +
  8 +import numpy
  9 +import scipy
  10 +import matplotlib.pyplot as plt
  11 +from matplotlib import cm, colors
  12 +from mpl_toolkits.mplot3d import Axes3D
  13 +import math
  14 +import time
  15 +
  16 +
  17 +def sph2cart(r, theta, phi):
  18 + x = r * numpy.cos(theta) * numpy.sin(phi)
  19 + y = r * numpy.sin(theta) * numpy.sin(phi)
  20 + z = r * numpy.cos(phi)
  21 +
  22 + return x, y, z
  23 +
  24 +def cart2sph(x, y, z):
  25 + r = numpy.sqrt(x**2+y**2+z**2)
  26 + theta = numpy.arctan2(y,x)
  27 + phi = numpy.arccos(z/r)
  28 + #if(x == 0):
  29 + # phi = 0
  30 + #else:
  31 + # phi = numpy.arccos(z/r)
  32 + #phi = numpy.arctan2(numpy.sqrt(x**2 + y**2), z)
  33 + return r, theta, phi
  34 +
  35 +
  36 +def sh(theta, phi, l, m):
  37 +
  38 + if m < 0:
  39 + return numpy.sqrt(2) * (-1)**m * scipy.special.sph_harm(abs(m), l, theta, phi).imag
  40 + elif m > 0:
  41 + return numpy.sqrt(2) * (-1)**m * scipy.special.sph_harm(m, l, theta, phi).real
  42 + else:
  43 + return scipy.special.sph_harm(0, l, theta, phi).real
  44 +
  45 +#calculate a spherical harmonic value from a set of coefficients and coordinates P = (theta, phi)
  46 +def sh_coeff(P, C):
  47 +
  48 + s = numpy.zeros(P[0].shape)
  49 + c = range(len(C))
  50 +
  51 + for c in range(len(C)):
  52 + l, m = i2lm(c) #get the 2D indices
  53 + s = s + C[c] * sh(P[0], P[1], l, m)
  54 +
  55 + return s
  56 +
  57 +#plot a spherical harmonic function on a sphere using N points
  58 +def sh_plot(C, N):
  59 + phi = numpy.linspace(0, numpy.pi, N)
  60 + theta = numpy.linspace(0, 2*numpy.pi, N)
  61 + phi, theta = numpy.meshgrid(phi, theta)
  62 +
  63 + # The Cartesian coordinates of the unit sphere
  64 + x = numpy.sin(phi) * numpy.cos(theta)
  65 + y = numpy.sin(phi) * numpy.sin(theta)
  66 + z = numpy.cos(phi)
  67 +
  68 + # Calculate the spherical harmonic Y(l,m) and normalize to [0,1]
  69 + fcolors = sh_coeff(theta, phi, C)
  70 + fmax, fmin = fcolors.max(), fcolors.min()
  71 + fcolors = (fcolors - fmin)/(fmax - fmin)
  72 +
  73 + # Set the aspect ratio to 1 so our sphere looks spherical
  74 + fig = plt.figure(figsize=plt.figaspect(1.))
  75 + ax = fig.add_subplot(111, projection='3d')
  76 + ax.plot_surface(x, y, z, rstride=1, cstride=1, facecolors=cm.seismic(fcolors))
  77 + # Turn off the axis planes
  78 + ax.set_axis_off()
  79 + plt.show()
  80 +
  81 +def i2lm(i):
  82 + l = numpy.floor(numpy.sqrt(i))
  83 + m = i - l *(l + 1)
  84 + return l, m
  85 +
  86 +def lm2i(l, m):
  87 + return l * (l+1) + m
  88 +
  89 +#generates a set of spherical harmonic coefficients from samples using linear least squares
  90 +def linfit(P, s, nc, clock=False):
  91 + if clock:
  92 + start_time = time.time()
  93 +
  94 + #allocate space for the matrix and RHS values
  95 + A = numpy.zeros((nc, nc))
  96 + b = numpy.zeros(nc)
  97 +
  98 + #calculate each of the matrix coefficients
  99 + #(see SH technical report in the vascular_viz repository)
  100 + for i in range(nc):
  101 + li, mi = i2lm(i)
  102 + yi = sh(P[0], P[1], li, mi)
  103 + for j in range(nc):
  104 + lj, mj = i2lm(j)
  105 + yj = sh(P[0], P[1], lj, mj)
  106 + A[i, j] = numpy.sum(yi * yj)
  107 + b[i] = numpy.sum(yi * s) #calculate the RHS value
  108 +
  109 + #calculate the RHS values
  110 + #for j in range(nc):
  111 + # lj, mj = i2lm(j)
  112 + # yj = sh(theta, phi, lj, mj)
  113 + # b[j] = numpy.sum(yj * s)
  114 +
  115 + if clock:
  116 + print("SH::linfit:matrix "+str(time.time() - start_time)+"s")
  117 + #solve the system of linear equations
  118 + R = numpy.linalg.solve(A, b)
  119 +
  120 + if clock:
  121 + print("SH::linfit:solution "+str(time.time() - start_time)+"s")
  122 + return R
  123 +
  124 +#generate a scatter plot in 3D using spherical coordinates
  125 +def scatterplot3d(P):
  126 + r, theta, phi = P
  127 + #convert all of the samples to cartesian coordinates
  128 + X, Y, Z = sph2cart(r, theta, phi)
  129 +
  130 + fig = plt.figure()
  131 + ax = fig.add_subplot(111, projection='3d')
  132 + ax.scatter(X, Y, Z)
  133 + plt.show()
  134 +
  135 +
python/structen.py 0 → 100644
  1 +# -*- coding: utf-8 -*-
  2 +"""
  3 +Created on Sun Mar 12 21:54:40 2017
  4 +
  5 +@author: david
  6 +"""
  7 +
  8 +import numpy as np
  9 +import scipy as sp
  10 +import scipy.ndimage
  11 +import progressbar
  12 +import glob
  13 +
  14 +def st2(I, s=1, dtype=np.float32):
  15 +
  16 + #calculates the 2D structure tensor for an image using a gaussian window with standard deviation s
  17 +
  18 + #calculate the gradient
  19 + dI = np.gradient(I.astype(dtype))
  20 +
  21 + #calculate the dimensions of the tensor field
  22 + field_dims = [dI[0].shape[0], dI[0].shape[1], 3]
  23 +
  24 + #allocate space for the tensor field
  25 + Tg = np.zeros(field_dims, dtype=dtype)
  26 +
  27 + #calculate the gradient components of the tensor
  28 + ti = 0
  29 + for i in range(2):
  30 + for j in range(i + 1):
  31 + Tg[:, :, ti] = dI[j] * dI[i]
  32 + ti = ti + 1
  33 +
  34 + #if the user does not want a blurred field
  35 + if(s == 0):
  36 + return Tg
  37 +
  38 + #blur the tensor field
  39 + T = np.zeros(field_dims, dtype=dtype)
  40 +
  41 + for i in range(3):
  42 + T[:, :, i] = scipy.ndimage.filters.gaussian_filter(Tg[:, :, i], [s, s])
  43 +
  44 +
  45 + return T
  46 +
  47 +def st3(I, s=1, v=[1, 1, 1], dtype=np.float32):
  48 + #calculate the structure tensor field for the 3D input image I given the window size s and voxel size v
  49 + #check the format for the window size
  50 +
  51 + v = np.array(v)
  52 + print("\nCalculating gradient...")
  53 + dI = np.gradient(I.astype(dtype), v[0], v[1], v[2])
  54 + #calculate the dimensions of the tensor field
  55 + field_dims = [dI[0].shape[0], dI[0].shape[1], dI[0].shape[2], 6]
  56 +
  57 + #allocate space for the tensor field
  58 + Tg = np.zeros(field_dims, dtype=np.float32)
  59 +
  60 + #calculate the gradient components of the tensor
  61 + ti = 0
  62 + print("Calculating tensor components...")
  63 + bar = progressbar.ProgressBar()
  64 + bar.max_value = 6
  65 + for i in range(3):
  66 + for j in range(i + 1):
  67 + Tg[:, :, :, ti] = dI[j] * dI[i]
  68 + ti = ti + 1
  69 + bar.update(ti)
  70 +
  71 + #blur the tensor field
  72 + T = np.zeros(field_dims, dtype=np.float32)
  73 +
  74 + print("\nConvolving tensor field...")
  75 + bar = progressbar.ProgressBar()
  76 + bar.max_value = 6
  77 + sigma = s / v
  78 + print(sigma)
  79 + for i in range(6):
  80 + T[:, :, :, i] = scipy.ndimage.filters.gaussian_filter(Tg[:, :, :, i], sigma)
  81 + bar.update(i+1)
  82 +
  83 + return T
  84 +
  85 +def st(I, s=1):
  86 + if I.ndim == 3:
  87 + return st3(I, s)
  88 + elif I.ndim == 2:
  89 + return st2(I, s)
  90 + else:
  91 + print("Image must be 2D or 3D")
  92 + return
  93 +
  94 +
  95 +
  96 +def sym2mat(T):
  97 + #Calculate the full symmetric matrix from a list of lower triangular elements.
  98 + #The lower triangular components in the input field T are assumed to be the
  99 + # final dimension of the input matrix.
  100 +
  101 + # | 1 2 4 7 |
  102 + # | 0 3 5 8 |
  103 + # | 0 0 6 9 |
  104 + # | 0 0 0 10 |
  105 +
  106 + in_s = T.shape
  107 +
  108 + #get the number of tensor elements
  109 + n = in_s[T.ndim - 1]
  110 +
  111 + #calculate the dimension of the symmetric matrix
  112 + d = int(0.5 * (np.sqrt(8. * n + 1.) - 1.))
  113 +
  114 + #calculate the dimensions of the output field
  115 + out_s = list(in_s)[:-1] + [d] + [d]
  116 +
  117 + #allocate space for the output field
  118 + R = np.zeros(out_s)
  119 +
  120 + ni = 0
  121 + for i in range(d):
  122 + for j in range(i + 1):
  123 + R[..., i, j] = T[..., ni]
  124 + if i != j:
  125 + R[..., j, i] = T[..., ni]
  126 + ni = ni + 1
  127 +
  128 + return R
  129 +
  130 +def vec(S, vector=0):
  131 +
  132 + if(S.ndim != 3):
  133 + print("ERROR: a 2D slice is expected")
  134 + return
  135 +
  136 + #convert the field to a full rank-2 tensor
  137 + T = sym2mat(S);
  138 + del(S)
  139 +
  140 + #calculate the eigenvectors and eigenvalues
  141 + l, v = np.linalg.eig(T)
  142 +
  143 + #get the dimension of the tensor field
  144 + d = T.shape[2]
  145 +
  146 + #allocate space for the vector field
  147 + V = np.zeros([T.shape[0], T.shape[1], 3])
  148 +
  149 + #arrange the indices for each pixel from smallest to largest eigenvalue
  150 + idx = l.argsort()
  151 +
  152 + for di in range(d):
  153 + b = idx[:, :, -1-vector] == di
  154 + V[b, 0:d] = v[b, :, di]
  155 +
  156 + return V
  157 +
  158 +def loadstack(filemask):
  159 + #Load an image stack as a 3D grayscale array
  160 +
  161 + #get a list of all files matching the given mask
  162 + files = [file for file in glob.glob(filemask)]
  163 +
  164 + #calculate the size of the output stack
  165 + I = scipy.misc.imread(files[0])
  166 + X = I.shape[0]
  167 + Y = I.shape[1]
  168 + Z = len(files)
  169 +
  170 + #allocate space for the image stack
  171 + M = np.zeros([X, Y, Z]).astype('float32')
  172 +
  173 + #create a progress bar
  174 + bar = progressbar.ProgressBar()
  175 + bar.max_value = Z
  176 +
  177 + #for each file
  178 + for z in range(Z):
  179 + #load the file and save it to the image stack
  180 + M[:, :, z] = scipy.misc.imread(files[z], flatten="True").astype('float32')
  181 + bar.update(z+1)
  182 + return M
  183 +
  184 +#calculate the anisotropy of a structure tensor given the tensor field S
  185 +def anisotropy3(S):
  186 +
  187 + Sf = sym2mat(S)
  188 +
  189 + #calculate the eigenvectors and eigenvalues
  190 + l, v = np.linalg.eig(Sf)
  191 +
  192 + #store the sorted eigenvalues
  193 + ls = np.sort(l)
  194 + l0 = ls[:, :, 0]
  195 + l1 = ls[:, :, 1]
  196 + l2 = ls[:, :, 2]
  197 +
  198 + #calculate the linear anisotropy
  199 + Cl = (l2 - l1)/(l2 + l1 + l0)
  200 +
  201 + #calculate the planar anisotropy
  202 + Cp = 2 * (l1 - l0) / (l2 + l1 + l0)
  203 +
  204 + #calculate the spherical anisotropy
  205 + Cs = 3 * l0 / (l2 + l1 + l0)
  206 +
  207 + #calculate the fractional anisotropy
  208 + l_hat = (l0 + l1 + l2)/3
  209 + fa_num = (l2 - l_hat) ** 2 + (l1 - l_hat) ** 2 + (l0 - l_hat) ** 2;
  210 + fa_den = l0 ** 2 + l1 ** 2 + l2 ** 2
  211 + FA = np.sqrt(3./2.) * np.sqrt(fa_num) / np.sqrt(fa_den)
  212 +
  213 + return FA, Cl, Cp, Cs
  214 +
  215 +#calculate the fractional anisotropy
  216 +def fa(S):
  217 + Sf = sym2mat(S)
  218 +
  219 + #calculate the eigenvectors and eigenvalues
  220 + l, v = np.linalg.eig(Sf)
  221 +
  222 + #store the sorted eigenvalues
  223 + ls = np.sort(l)
  224 + l0 = ls[:, :, 0]
  225 + l1 = ls[:, :, 1]
  226 +
  227 + #if this is a 2D tensor, calculate and return the coherence
  228 + if(S.shape[2] == 3):
  229 + C = ((l0 - l1) / (l0 + l1)) ** 2
  230 + return C
  231 +
  232 + #if this is a 3D tensor
  233 + elif(S.shape[2] == 6):
  234 + l2 = ls[:, :, 2]
  235 +
  236 + #calculate the fractional anisotropy
  237 + l_hat = (l0 + l1 + l2)/3
  238 + fa_num = (l2 - l_hat) ** 2 + (l1 - l_hat) ** 2 + (l0 - l_hat) ** 2;
  239 + fa_den = l0 ** 2 + l1 ** 2 + l2 ** 2
  240 + FA = np.sqrt(3./2.) * np.sqrt(fa_num) / np.sqrt(fa_den)
  241 + return FA
  242 +
  243 +#calculate the specified eigenvalue for the tensor field
  244 +def eigenval(S, ev):
  245 + Sf = sym2mat(S)
  246 +
  247 + #calculate the eigenvectors and eigenvalues
  248 + l, v = np.linalg.eig(Sf)
  249 +
  250 + #store the sorted eigenvalues
  251 + ls = np.sort(l)
  252 + evals = ls[:, :, ev]
  253 +
  254 + return evals
  255 +
  256 +def amira(filename, T):
  257 + #generates a tensor field that can be imported into Amira
  258 +
  259 + # 0 dx dx ----> 0
  260 + # 1 dx dy ----> 1
  261 + # 2 dy dy ----> 3
  262 + # 3 dx dz ----> 2
  263 + # 4 dy dz ----> 4
  264 + # 5 dz dz ----> 5
  265 +
  266 + #swap the 2nd and 3rd tensor components
  267 + A = np.copy(T)
  268 + A[..., 3] = T[..., 2]
  269 + A[..., 2] = T[..., 3]
  270 +
  271 + #roll the tensor axis so that it is the leading component
  272 + #A = numpy.rollaxis(A, A.ndim - 1)
  273 + A.tofile(filename)
  274 + print("\n", A.shape)
  275 +
  276 +def resample3(T, s=2):
  277 + #resample a tensor field by an integer factor s
  278 + #This function first convolves the field with a box filter and then
  279 + # re-samples to create a smaller field
  280 +
  281 + #check the format for the window size
  282 + if type(s) is not list:
  283 + s = [s] * 3
  284 + elif len(s) == 1:
  285 + s = s * 3
  286 + elif len(s) == 2:
  287 + s.insert(1, s[0])
  288 + s = np.array(s)
  289 +
  290 + bar = progressbar.ProgressBar()
  291 + bar.max_value = T.shape[3]
  292 +
  293 + #blur with a uniform box filter of size r
  294 + for t in range(T.shape[3]):
  295 + T[..., t] = scipy.ndimage.filters.uniform_filter(T[..., t], 2 * s)
  296 + bar.update(t+1)
  297 +
  298 + #resample at a rate of r
  299 + R = T[::s[0], ::s[1], ::s[2], :]
  300 + return R
  301 +
  302 +def color3(prefix, T, vector='largest', aniso=True):
  303 + #Saves a stack of color images corresponding to the eigenvector and optionally scaled by anisotropy
  304 +
  305 + bar = progressbar.ProgressBar()
  306 + bar.max_value = T.shape[2]
  307 +
  308 + #for each z-axis slice
  309 + for z in range(T.shape[2]):
  310 + S = T[:, :, z, :] #get the slice
  311 + V = st2vec(S, vector='smallest') #calculate the vector
  312 + C = np.absolute(V) #calculate the absolute value
  313 +
  314 + if aniso == True: #if the image is scaled by anisotropy
  315 + FA, Cl, Cp, Cs = anisotropy(S) #calculate the anisotropy of the slice
  316 + if vector == 'largest':
  317 + A = Cl
  318 + elif vector == 'smallest':
  319 + A = Cp
  320 + else: #otherwise just scale by 1
  321 + A = np.ones(T.shape[0], T.shape[1])
  322 + image = C * np.expand_dims(A, 3)
  323 +
  324 + filename = prefix + str(z).zfill(4) + ".bmp"
  325 + scipy.misc.imsave(filename, image)
  326 + bar.update(z + 1)
  327 +
  328 +def st2stack(T, outfile, **kwargs):
  329 + eigenvector = False #initialize the colormap flags to false
  330 + aniso_color = False
  331 + aniso_alpha = False
  332 + #image = False
  333 + aniso_pwr = 1
  334 + cimage_pwr = 1
  335 + aimage_pwr = 1
  336 + anisostretch = 1 #set the contrast stretch parameter
  337 + alpha_channel = False
  338 + alpha_image = False
  339 + color_image = False
  340 +
  341 + for k,v in kwargs.items(): #for each argument
  342 + if(k == "ev"): #if the user wants a colormap based on an eigenvector
  343 + eigenvector = True #set the eigenvector flag to true
  344 + ev = v #save the desired eigenvector
  345 + if(k == "aniso"): #if the user wants to factor in anisotropy
  346 + aniso = True #set the anisotropy flag to true
  347 + aniso_channel = v #save the anisotropy channel
  348 + if(k == "aniso_color"):
  349 + aniso_color = v
  350 + if(k == "aniso_alpha"):
  351 + aniso_alpha = v
  352 + if(k == "apwr"): #if the user wants to amplify the anisotropy
  353 + aniso_pwr = v #set the anisotropy exponent
  354 + if(k == "cipwr"): #if the user specifies the image power
  355 + cimage_pwr = v
  356 + if(k == "aipwr"):
  357 + aimage_pwr = v
  358 + if(k == "alphaimage"):
  359 + Ia = v
  360 + alpha_image = True
  361 + if(k == "colorimage"):
  362 + Ic = v
  363 + color_image = True
  364 + if(k == "anisostretch"):
  365 + anisostretch = v
  366 + if(k == "alpha"):
  367 + alpha_channel = v
  368 +
  369 + bar = progressbar.ProgressBar()
  370 + bar.max_value = T.shape[2]
  371 + for i in range(0, T.shape[2]):
  372 + #for i in range(0, 50):
  373 +
  374 + if(alpha_image or alpha_channel):
  375 + img = np.ones([T.shape[0], T.shape[1], 4])
  376 + else:
  377 + img = np.ones([T.shape[0], T.shape[1], 3])
  378 + if(eigenvector):
  379 + V = st2vec(T[:, :, i], ev) #get the vector field for slice i corresponding to eigenvector ev
  380 + img[:, :, 0:3] = V #update the image with the vector field information
  381 + if(aniso): #if the user is requesting anisotropy be incorporated into the image
  382 + FA, Cl, Cp, Cs = anisotropy(T[:, :, i]) #calculate the anisotropy of the tensors in slice i
  383 + if(aniso_channel == "fa"):
  384 + A = FA
  385 + elif(aniso_channel == "l"):
  386 + A = Cl
  387 + elif(aniso_channel == "p"):
  388 + A = Cp
  389 + else:
  390 + A = Cs
  391 + if(aniso_alpha):
  392 + print("rendering anisotropy to the alpha channel")
  393 + img[:, :, 3] = A ** aniso_pwr * anisostretch
  394 + if(aniso_color):
  395 + print("rendering anisotropy to the color channel")
  396 + img[:, :, 0:3] = img[:, :, 0:3] * np.expand_dims(A ** aniso_pwr, 3) * anisostretch
  397 + if(alpha_image):
  398 + img[:, :, 3] = Ia[:, :, i]/255 ** aimage_pwr
  399 + if(color_image):
  400 + img[:, :, 0:3] = img[:, :, 0:3] * (np.expand_dims(Ic[:, :, i], 3)/255) ** cimage_pwr #multiply the vector field by the image intensity
  401 + #outname = outfile + str(i).zfill(3) + ".bmp" #get the file name to be saved
  402 + outname = outfile.replace("*", str(i).zfill(3))
  403 +
  404 + sp.misc.imsave(outname, np.ndarray.astype(np.abs(img)*255, "uint8")) #save the output image
  405 + bar.update(i+1)
  406 +
  407 +
  408 +#this function takes a 3D image and turns it into a stack of color images based on the structure tensor
  409 +def img2stack(I, outfile, **kwargs):
  410 +
  411 + vs = [1, 1, 1] #set the default voxel size to 1
  412 + w = 5
  413 +
  414 + for k,v in kwargs.items(): #for each argument
  415 + if(k == "voxelsize"): #if the voxel size is specified
  416 + if(len(v) == 1): #if the user just specifies one value
  417 + vs = [v] * 3 #assume that the voxels are isotropic, create a list of 3 v's
  418 + elif(len(v) == 2): #if the user specifies two values
  419 + vs[0] = v[0] #assume that the voxels are isotropic along (x, y) and anisotropic along z
  420 + vs[1] = v[0]
  421 + vs[2] = v[1]
  422 + elif(len(v) == 3):
  423 + vs = v
  424 + if(k == "window"): #if the user specifies a window size
  425 + w = v
  426 +
  427 + T = st3(I, w, vs) #calculate the structure tensor
  428 +
  429 + st2stack(T, outfile, **kwargs)
  430 +
  431 +def stack2stack(infile_mask, outfile, **kwargs):
  432 +
  433 + I = loadstack(infile_mask) #load the file mask
  434 + for k,v in kwargs.items(): #for each argument
  435 + if(k == "ipwr"):
  436 + img = I
  437 +
  438 + img2stack(I, outfile, image=img, **kwargs) #call the function to convert the image to an output ST stack
0 \ No newline at end of file 439 \ No newline at end of file
stim/biomodels/cellset.h
  1 +/*
  2 +Copyright <2017> <David Mayerich>
  3 +
  4 +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  5 +
  6 +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  7 +
  8 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  9 +*/
  10 +
1 #ifndef STIM_CELLSET_H 11 #ifndef STIM_CELLSET_H
2 #define STIM_CELLSET_H 12 #define STIM_CELLSET_H
3 13
@@ -117,7 +127,7 @@ public: @@ -117,7 +127,7 @@ public:
117 } 127 }
118 128
119 /// Return the maximum value of a field in this cell set 129 /// Return the maximum value of a field in this cell set
120 - double max(std::string field){ 130 + double maximum(std::string field){
121 size_t idx = fields[field]; //get the field index 131 size_t idx = fields[field]; //get the field index
122 size_t ncells = cells.size(); //get the total number of cells 132 size_t ncells = cells.size(); //get the total number of cells
123 double maxval, val; //stores the current and maximum values 133 double maxval, val; //stores the current and maximum values
@@ -130,7 +140,7 @@ public: @@ -130,7 +140,7 @@ public:
130 } 140 }
131 141
132 /// Return the maximum value of a field in this cell set 142 /// Return the maximum value of a field in this cell set
133 - double min(std::string field){ 143 + double minimum(std::string field){
134 size_t idx = fields[field]; //get the field index 144 size_t idx = fields[field]; //get the field index
135 size_t ncells = cells.size(); //get the total number of cells 145 size_t ncells = cells.size(); //get the total number of cells
136 double minval, val; //stores the current and maximum values 146 double minval, val; //stores the current and maximum values
stim/biomodels/centerline.h
  1 +/*
  2 +Copyright <2017> <David Mayerich>
  3 +
  4 +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  5 +
  6 +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  7 +
  8 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  9 +*/
  10 +
1 #ifndef STIM_CENTERLINE_H 11 #ifndef STIM_CENTERLINE_H
2 #define STIM_CENTERLINE_H 12 #define STIM_CENTERLINE_H
3 13
4 #include <vector> 14 #include <vector>
5 #include <stim/math/vec3.h> 15 #include <stim/math/vec3.h>
6 -//#include <ANN/ANN.h> 16 +#include <stim/structures/kdtree.cuh>
7 17
8 namespace stim{ 18 namespace stim{
9 19
@@ -12,195 +22,499 @@ namespace stim{ @@ -12,195 +22,499 @@ namespace stim{
12 * class to describe an interconnected (often biological) network. 22 * class to describe an interconnected (often biological) network.
13 */ 23 */
14 template<typename T> 24 template<typename T>
15 -class centerline{ 25 +class centerline : public std::vector< stim::vec3<T> >{
16 26
17 protected: 27 protected:
18 - unsigned int N; //number of points in the fiber  
19 - double **c; //centerline (array of double pointers)  
20 -// ANNkd_tree* kdt; //kd-tree stores all points in the fiber for fast searching  
21 -  
22 - /// Initialize an empty fiber  
23 - void init()  
24 - {  
25 - N=0;  
26 - c=NULL;  
27 -// kdt = NULL;  
28 - }  
29 28
30 - /// Initialize a fiber with N centerline points (all located at [0, 0, 0] with radius 0)  
31 - void init(unsigned int n)  
32 - { 29 + std::vector<T> L; //stores the integrated length along the fiber (used for parameterization)
33 30
34 - N = n; //set the number of points  
35 -// kdt = NULL;  
36 - c = (double**) malloc(sizeof(double*) * N); //allocate the array pointer 31 + ///Return the normalized direction vector at point i (average of the incoming and outgoing directions)
  32 + vec3<T> d(size_t i) {
  33 + if (size() <= 1) return vec3<T>(0, 0, 0); //if there is insufficient information to calculate the direction, return a null vector
  34 + if (size() == 2) return (at(1) - at(0)).norm(); //if there are only two points, the direction vector at both is the direction of the line segment
  35 + if (i == 0) return (at(1) - at(0)).norm(); //the first direction vector is oriented towards the first line segment
  36 + if (i == size() - 1) return (at(size() - 1) - at(size() - 2)).norm(); //the last direction vector is oriented towards the last line segment
37 37
38 - for(unsigned int i = 0; i < N; i++) //allocate space for each point  
39 - c[i] = (double*) malloc(sizeof(double) * 3); 38 + //all other direction vectors are the average direction of the two joined line segments
  39 + vec3<T> a = at(i) - at(i - 1);
  40 + vec3<T> b = at(i + 1) - at(i);
  41 + vec3<T> ab = a.norm() + b.norm();
  42 + return ab.norm();
40 } 43 }
41 44
42 - /// Copies an existing fiber to the current fiber  
43 -  
44 - /// @param cpy stores the new copy of the fiber  
45 - void copy( const stim::centerline<T>& cpy, bool kd = 0){  
46 -  
47 - ///allocate space for the new fiber  
48 - init(cpy.N);  
49 -  
50 - ///copy the points  
51 - for(unsigned int i = 0; i < N; i++){  
52 - for(unsigned int d = 0; d < 3; d++) //for each dimension  
53 - c[i][d] = cpy.c[i][d]; //copy the coordinate 45 + //initializes the integrated length vector to make parameterization easier, starting with index idx (all previous indices are assumed to be correct)
  46 + void update_L(size_t start = 0) {
  47 + L.resize(size()); //allocate space for the L array
  48 + if (start == 0) {
  49 + L[0] = 0; //initialize the length value for the first point to zero (0)
  50 + start++;
54 } 51 }
55 -// if(kd)  
56 -// gen_kdtree(); //generate the kd tree for the new fiber  
57 - }  
58 52
59 - /// generate a KD tree for points on fiber  
60 -// void gen_kdtree()  
61 -// {  
62 -// int n_data = N; //create an array of data points  
63 -// ANNpointArray pts = (ANNpointArray)c; //cast the centerline list to an ANNpointArray  
64 -// kdt = new ANNkd_tree(pts, n_data, 3); //build a KD tree  
65 -// }  
66 -  
67 - /// find distance between two points  
68 - double dist(double* p0, double* p1){  
69 -  
70 - double sum = 0; // initialize variables  
71 - float v;  
72 - for(unsigned int d = 0; d < 3; d++)  
73 - {  
74 - v = p1[d] - p0[d];  
75 - sum +=v * v; 53 + stim::vec3<T> d;
  54 + for (size_t i = start; i < size(); i++) { //for each line segment in the centerline
  55 + d = at(i) - at(i - 1);
  56 + L[i] = L[i - 1] + d.len(); //calculate the running length total
76 } 57 }
77 - return sqrt(sum);  
78 } 58 }
79 59
80 - /// This function retreives the index for the fiber point closest to q  
81 -  
82 - /// @param q is a reference point used to find the closest point on the fiber center line  
83 -// unsigned int ann( stim::vec<double> q ){  
84 -// ANNidxArray idx = new ANNidx[1]; //variable used to hold the nearest point  
85 -// ANNdistArray sq_dist = new ANNdist[1]; //variable used to hold the squared distance to the nearest point  
86 -// kdt->annkSearch(q.data(), 1, idx, sq_dist); //search the KD tree for the nearest neighbor  
87 -// return *idx;  
88 -// } 60 + void init() {
  61 + if (size() == 0) return; //return if there aren't any points
  62 + update_L();
  63 + }
89 64
90 /// Returns a stim::vec representing the point at index i 65 /// Returns a stim::vec representing the point at index i
91 66
92 /// @param i is an index of the desired centerline point 67 /// @param i is an index of the desired centerline point
93 stim::vec<T> get_vec(unsigned i){ 68 stim::vec<T> get_vec(unsigned i){
94 - stim::vec3<T> r;  
95 - r.resize(3);  
96 - r[0] = c[i][0];  
97 - r[1] = c[i][1];  
98 - r[2] = c[i][2]; 69 + return std::vector< stim::vec3<T> >::at(i);
  70 + }
  71 +
  72 + ///finds the index of the point closest to the length l on the lower bound.
  73 + ///binary search.
  74 + size_t findIdx(T l) {
  75 + for (size_t i = 1; i < L.size(); i++) { //for each point in the centerline
  76 + if (L[i] > l) return i - 1; //if we have passed the desired length value, return i-1
  77 + }
  78 + return L.size() - 1;
  79 + /*size_t i = L.size() / 2;
  80 + size_t max = L.size() - 1;
  81 + size_t min = 0;
  82 + while (i < L.size() - 1){
  83 + if (l < L[i]) {
  84 + max = i;
  85 + i = min + (max - min) / 2;
  86 + }
  87 + else if (L[i] <= l && L[i + 1] >= l) {
  88 + break;
  89 + }
  90 + else {
  91 + min = i;
  92 + i = min + (max - min) / 2;
  93 + }
  94 + }
  95 + return i;*/
  96 + }
99 97
100 - return r; 98 + ///Returns a position vector at the given length into the fiber (based on the pvalue).
  99 + ///Interpolates the radius along the line.
  100 + ///@param l: the location of the in the cylinder.
  101 + ///@param idx: integer location of the point closest to l but prior to it.
  102 + stim::vec3<T> p(T l, int idx) {
  103 + T rat = (l - L[idx]) / (L[idx + 1] - L[idx]);
  104 + stim::vec3<T> v1 = at(idx);
  105 + stim::vec3<T> v2 = at(idx + 1);
  106 + return(v1 + (v2 - v1)*rat);
101 } 107 }
102 108
103 109
104 public: 110 public:
105 111
106 - centerline(){ 112 + using std::vector< stim::vec3<T> >::at;
  113 + using std::vector< stim::vec3<T> >::size;
  114 +
  115 + centerline() : std::vector< stim::vec3<T> >() {
  116 + init();
  117 + }
  118 + centerline(size_t n) : std::vector< stim::vec3<T> >(n){
  119 + init();
  120 + }
  121 + centerline(std::vector<stim::vec3<T> > pos) :
  122 + std::vector<stim::vec3<T> > (pos)
  123 + {
107 init(); 124 init();
108 } 125 }
  126 +
  127 + //overload the push_back function to update the length vector
  128 + void push_back(stim::vec3<T> p) {
  129 + std::vector< stim::vec3<T> >::push_back(p);
  130 + update_L(size() - 1);
  131 + }
  132 +
  133 + ///Returns a position vector at the given p-value (p value ranges from 0 to 1).
  134 + ///interpolates the position along the line.
  135 + ///@param pvalue: the location of the in the cylinder, from 0 (beginning to 1).
  136 + stim::vec3<T> p(T pvalue) {
  137 + if (pvalue <= 0.0) return at(0); //return the first element
  138 + if (pvalue >= 1.0) return back(); //return the last element
109 139
110 - /// Copy constructor  
111 - centerline(const stim::centerline<T> &obj){  
112 - copy(obj); 140 + T l = pvalue*L[L.size() - 1];
  141 + int idx = findIdx(l);
  142 + return p(l, idx);
113 } 143 }
114 144
115 - //temp constructor for graph visualization  
116 - centerline(int n)  
117 - {  
118 - init(n); 145 + ///Update centerline internal parameters (currently the L vector)
  146 + void update() {
  147 + init();
  148 + }
  149 + ///Return the length of the entire centerline
  150 + T length() {
  151 + return L.back();
119 } 152 }
120 153
121 - /// Constructor takes a list of stim::vec points, the radius at each point is set to zero  
122 - centerline(std::vector< stim::vec<T> > p, bool kd = 0){  
123 - init(p.size()); //initialize the fiber  
124 154
125 - //for each point, set the centerline position and radius  
126 - for(unsigned int i = 0; i < N; i++){ 155 + /// stitch two centerlines
  156 + ///@param c1, c2: two centerlines
  157 + ///@param sigma: sample rate
  158 + static std::vector< stim::centerline<T> > stitch(stim::centerline<T> c1, stim::centerline<T> c2 = stim::centerline<T>()) {
  159 +
  160 + std::vector< stim::centerline<T> > result;
  161 + stim::centerline<T> new_centerline;
  162 + stim::vec3<T> new_vertex;
  163 +
  164 + // if only one centerline, stitch itself!
  165 + if (c2.size() == 0) {
  166 + size_t num = c1.size();
  167 + size_t id = 100000; // store the downsample start position
  168 + T threshold;
  169 + if (num < 4) { // if the number of vertex is less than 4, do nothing
  170 + result.push_back(c1);
  171 + return result;
  172 + }
  173 + else {
  174 + // test geometry start vertex
  175 + stim::vec3<T> v1 = c1[1] - c1[0]; // vector from c1[0] to c1[1]
  176 + for (size_t p = 2; p < num; p++) { // 90° standard???
  177 + stim::vec3<T> v2 = c1[p] - c1[0];
  178 + float cosine = v2.dot(v1);
  179 + if (cosine < 0) {
  180 + id = p;
  181 + threshold = v2.len();
  182 + break;
  183 + }
  184 + }
  185 + if (id != 100000) { // find a downsample position on the centerline
  186 + T* c;
  187 + c = (T*)malloc(sizeof(T) * (num - id) * 3);
  188 + for (size_t p = id; p < num; p++) {
  189 + for (size_t d = 0; d < 3; d++) {
  190 + c[p * 3 + d] = c1[p][d];
  191 + }
  192 + }
  193 + stim::kdtree<T, 3> kdt;
  194 + kdt.create(c, num - id, 5); // create tree
127 195
128 - //set the centerline position  
129 - for(unsigned int d = 0; d < 3; d++)  
130 - c[i][d] = (double) p[i][d]; 196 + T* query = (T*)malloc(sizeof(T) * 3);
  197 + for (size_t d = 0; d < 3; d++)
  198 + query[d] = c1[0][d];
  199 + size_t index;
  200 + T dist;
131 201
132 - //set the radius  
133 - }  
134 - //generate a kd tree  
135 -// if(kd)  
136 -// gen_kdtree();  
137 - } 202 + kdt.search(query, 1, &index, &dist);
138 203
139 - /// constructor takes a list of points  
140 - centerline(std::vector< stim::vec3< T > > pos, bool kd = 0){  
141 - init(pos.size()); //initialize the fiber 204 + free(query);
  205 + free(c);
142 206
143 - //for each point, set the centerline position and radius  
144 - for(unsigned int i = 0; i < N; i++){  
145 - //set the centerline position  
146 - for(unsigned int d = 0; d < 3; d++)  
147 - c[i][d] = (double) pos[i][d];  
148 - //set the radius 207 + if (dist > threshold) {
  208 + result.push_back(c1);
  209 + }
  210 + else {
  211 + // the loop part
  212 + new_vertex = c1[index];
  213 + new_centerline.push_back(new_vertex);
  214 + for (size_t p = 0; p < index + 1; p++) {
  215 + new_vertex = c1[p];
  216 + new_centerline.push_back(new_vertex);
  217 + }
  218 + result.push_back(new_centerline);
  219 + new_centerline.clear();
  220 +
  221 + // the tail part
  222 + for (size_t p = index; p < num; p++) {
  223 + new_vertex = c1[p];
  224 + new_centerline.push_back(new_vertex);
  225 + }
  226 + result.push_back(new_centerline);
  227 + }
  228 + }
  229 + else { // there is one potential problem that two positions have to be stitched
  230 + // test geometry end vertex
  231 + stim::vec3<T> v1 = c1[num - 2] - c1[num - 1];
  232 + for (size_t p = num - 2; p > 0; p--) { // 90° standard
  233 + stim::vec3<T> v2 = c1[p - 1] - c1[num - 1];
  234 + float cosine = v2.dot(v1);
  235 + if (cosine < 0) {
  236 + id = p;
  237 + threshold = v2.len();
  238 + break;
  239 + }
  240 + }
  241 + if (id != 100000) { // find a downsample position
  242 + T* c;
  243 + c = (T*)malloc(sizeof(T) * (id + 1) * 3);
  244 + for (size_t p = 0; p < id + 1; p++) {
  245 + for (size_t d = 0; d < 3; d++) {
  246 + c[p * 3 + d] = c1[p][d];
  247 + }
  248 + }
  249 + stim::kdtree<T, 3> kdt;
  250 + kdt.create(c, id + 1, 5); // create tree
  251 +
  252 + T* query = (T*)malloc(sizeof(T) * 1 * 3);
  253 + for (size_t d = 0; d < 3; d++)
  254 + query[d] = c1[num - 1][d];
  255 + size_t index;
  256 + T dist;
  257 +
  258 + kdt.search(query, 1, &index, &dist);
  259 +
  260 + free(query);
  261 + free(c);
  262 +
  263 + if (dist > threshold) {
  264 + result.push_back(c1);
  265 + }
  266 + else {
  267 + // the tail part
  268 + for (size_t p = 0; p < index + 1; p++) {
  269 + new_vertex = c1[p];
  270 + new_centerline.push_back(new_vertex);
  271 + }
  272 + result.push_back(new_centerline);
  273 + new_centerline.clear();
  274 +
  275 + // the loop part
  276 + for (size_t p = index; p < num; p++) {
  277 + new_vertex = c1[p];
  278 + new_centerline.push_back(new_vertex);
  279 + }
  280 + new_vertex = c1[index];
  281 + new_centerline.push_back(new_vertex);
  282 + result.push_back(new_centerline);
  283 + }
  284 + }
  285 + else { // no stitch position
  286 + result.push_back(c1);
  287 + }
  288 + }
  289 + }
149 } 290 }
150 291
151 - //generate a kd tree  
152 - //if(kd)  
153 - // gen_kdtree();  
154 - }  
155 292
156 - /// Assignment operation  
157 - centerline& operator=(const centerline &rhs){  
158 - if(this == &rhs) return *this; //test for and handle self-assignment  
159 - copy(rhs);  
160 - return *this;  
161 - } 293 + // two centerlines
  294 + else {
  295 + // find stitch position based on nearest neighbors
  296 + size_t num1 = c1.size();
  297 + T* c = (T*)malloc(sizeof(T) * num1 * 3); // c1 as reference point
  298 + for (size_t p = 0; p < num1; p++) // centerline to array
  299 + for (size_t d = 0; d < 3; d++) // because right now my kdtree code is a relatively close code, it has its own structure
  300 + c[p * 3 + d] = c1[p][d]; // I will merge it into stimlib totally in the near future
162 301
  302 + stim::kdtree<T, 3> kdt; // kdtree object
  303 + kdt.create(c, num1, 5); // create tree
163 304
164 - /// Return the point on the fiber closest to q  
165 - /// @param q is the query point used to locate the nearest point on the fiber centerline  
166 -// stim::vec<T> nearest(stim::vec<T> q){  
167 -//  
168 -// stim::vec<double> temp( (double) q[0], (double) q[1], (double) q[2]);  
169 -//  
170 -// unsigned int idx = ann(temp); //determine the index of the nearest neighbor  
171 -//  
172 -// return stim::vec<T>((T) c[idx][0], (T) c[idx][1], (T) c[idx][2]); //return the nearest centerline point  
173 -// }  
174 -  
175 - /// Return the point index on the fiber closest to q  
176 - /// @param q is the query point used to locate the nearest point on the fiber centerline  
177 -// unsigned int nearest_idx(stim::vec<T> q){  
178 -//  
179 -// stim::vec<double> temp((double) q[0], (double) q[1], (double) q[2]);  
180 -//  
181 -// unsigned int idx = ann(temp); //determine the index of the nearest neighbor  
182 -//  
183 -// return idx; //return the nearest centerline point index  
184 -// }  
185 -  
186 - /// Returns the fiber centerline as an array of stim::vec points  
187 - std::vector< stim::vec<T> > get_centerline(){  
188 -  
189 - //create an array of stim vectors  
190 - std::vector< stim::vec3<T> > pts(N);  
191 -  
192 - //cast each point to a stim::vec, keeping only the position information  
193 - for(unsigned int i = 0; i < N; i++)  
194 - pts[i] = stim::vec3<T>((T) c[i][0], (T) c[i][1], (T) c[i][2]);  
195 -  
196 - //return the centerline array  
197 - return pts; 305 + size_t num2 = c2.size();
  306 + T* query = (T*)malloc(sizeof(T) * num2 * 3); // c2 as query point
  307 + for (size_t p = 0; p < num2; p++) {
  308 + for (size_t d = 0; d < 3; d++) {
  309 + query[p * 3 + d] = c2[p][d];
  310 + }
  311 + }
  312 + std::vector<size_t> index(num2);
  313 + std::vector<T> dist(num2);
  314 +
  315 + kdt.search(query, num2, &index[0], &dist[0]); // find the nearest neighbors in c1 for c2
  316 +
  317 + // clear up
  318 + free(query);
  319 + free(c);
  320 +
  321 + // find the average vertex distance of one centerline
  322 + T sigma1 = 0;
  323 + T sigma2 = 0;
  324 + for (size_t p = 0; p < num1 - 1; p++)
  325 + sigma1 += (c1[p] - c1[p + 1]).len();
  326 + for (size_t p = 0; p < num2 - 1; p++)
  327 + sigma2 += (c2[p] - c2[p + 1]).len();
  328 + sigma1 /= (num1 - 1);
  329 + sigma2 /= (num2 - 1);
  330 + float threshold = 4 * (sigma1 + sigma2) / 2; // better way to do this?
  331 +
  332 + T min_d = *std::min_element(dist.begin(), dist.end()); // find the minimum distance between c1 and c2
  333 +
  334 + if (min_d > threshold) { // if the minimum distance is too large
  335 + result.push_back(c1);
  336 + result.push_back(c2);
  337 +
  338 +#ifdef DEBUG
  339 + std::cout << "The distance between these two centerlines is too large" << std::endl;
  340 +#endif
  341 + }
  342 + else {
  343 + // auto smallest = std::min_element(dist.begin(), dist.end());
  344 + unsigned int smallest = std::min_element(dist.begin(), dist.end());
  345 + // auto i = std::distance(dist.begin(), smallest); // find the index of min-distance in distance list
  346 + unsigned int i = std::distance(dist.begin(), smallest); // find the index of min-distance in distance list
  347 +
  348 + // stitch position in c1 and c2
  349 + int id1 = index[i];
  350 + int id2 = i;
  351 +
  352 + // actually there are two cases
  353 + // first one inacceptable
  354 + // second one acceptable
  355 + if (id1 != 0 && id1 != num1 - 1 && id2 != 0 && id2 != num2 - 1) { // only stitch one end vertex to another centerline
  356 + result.push_back(c1);
  357 + result.push_back(c2);
  358 + }
  359 + else {
  360 + if (id1 == 0 || id1 == num1 - 1) { // if the stitch vertex is the first or last vertex of c1
  361 + // for c2, consider two cases(one degenerate case)
  362 + if (id2 == 0 || id2 == num2 - 1) { // case 1, if stitch position is also on the end of c2
  363 + // we have to decide which centerline get a new vertex, based on direction
  364 + // for c1, computer the direction change angle
  365 + stim::vec3<T> v1, v2;
  366 + float alpha1, alpha2; // direction change angle
  367 + if (id1 == 0)
  368 + v1 = (c1[1] - c1[0]).norm();
  369 + else
  370 + v1 = (c1[num1 - 2] - c1[num1 - 1]).norm();
  371 + v2 = (c2[id2] - c1[id1]).norm();
  372 + alpha1 = v1.dot(v2);
  373 + if (id2 == 0)
  374 + v1 = (c2[1] - c2[0]).norm();
  375 + else
  376 + v1 = (c2[num2 - 2] - c2[num2 - 1]).norm();
  377 + v2 = (c1[id1] - c2[id2]).norm();
  378 + alpha2 = v1.dot(v2);
  379 + if (abs(alpha1) > abs(alpha2)) { // add the vertex to c1 in order to get smooth connection
  380 + // push back c1
  381 + if (id1 == 0) { // keep geometry information
  382 + new_vertex = c2[id2];
  383 + new_centerline.push_back(new_vertex);
  384 + for (size_t p = 0; p < num1; p++) { // stitch vertex on c2 -> geometry start vertex on c1 -> geometry end vertex on c1
  385 + new_vertex = c1[p];
  386 + new_centerline.push_back(new_vertex);
  387 + }
  388 + }
  389 + else {
  390 + for (size_t p = 0; p < num1; p++) { // stitch vertex on c2 -> geometry end vertex on c1 -> geometry start vertex on c1
  391 + new_vertex = c1[p];
  392 + new_centerline.push_back(new_vertex);
  393 + }
  394 + new_vertex = c2[id2];
  395 + new_centerline.push_back(new_vertex);
  396 + }
  397 + result.push_back(new_centerline);
  398 + new_centerline.clear();
  399 +
  400 + // push back c2
  401 + for (size_t p = 0; p < num2; p++) {
  402 + new_vertex = c2[p];
  403 + new_centerline.push_back(new_vertex);
  404 + }
  405 + result.push_back(new_centerline);
  406 + }
  407 + else { // add the vertex to c2 in order to get smooth connection
  408 + // push back c1
  409 + for (size_t p = 0; p < num1; p++) {
  410 + new_vertex = c1[p];
  411 + new_centerline.push_back(new_vertex);
  412 + }
  413 + result.push_back(new_centerline);
  414 + new_centerline.clear();
  415 +
  416 + // push back c2
  417 + if (id2 == 0) { // keep geometry information
  418 + new_vertex = c1[id1];
  419 + new_centerline.push_back(new_vertex);
  420 + for (size_t p = 0; p < num2; p++) { // stitch vertex on c2 -> geometry start vertex on c1 -> geometry end vertex on c1
  421 + new_vertex = c2[p];
  422 + new_centerline.push_back(new_vertex);
  423 + }
  424 + }
  425 + else {
  426 + for (size_t p = 0; p < num2; p++) { // stitch vertex on c2 -> geometry end vertex on c1 -> geometry start vertex on c1
  427 + new_vertex = c2[p];
  428 + new_centerline.push_back(new_vertex);
  429 + }
  430 + new_vertex = c1[id1];
  431 + new_centerline.push_back(new_vertex);
  432 + }
  433 + result.push_back(new_centerline);
  434 + }
  435 + }
  436 + else { // case 2, the stitch position is on c2
  437 + // push back c1
  438 + if (id1 == 0) { // keep geometry information
  439 + new_vertex = c2[id2];
  440 + new_centerline.push_back(new_vertex);
  441 + for (size_t p = 0; p < num1; p++) { // stitch vertex on c2 -> geometry start vertex on c1 -> geometry end vertex on c1
  442 + new_vertex = c1[p];
  443 + new_centerline.push_back(new_vertex);
  444 + }
  445 + }
  446 + else {
  447 + for (size_t p = 0; p < num1; p++) { // geometry end vertex on c1 -> geometry start vertex on c1 -> stitch vertex on c2
  448 + new_vertex = c1[p];
  449 + new_centerline.push_back(new_vertex);
  450 + }
  451 + new_vertex = c2[id2];
  452 + new_centerline.push_back(new_vertex);
  453 + }
  454 + result.push_back(new_centerline);
  455 + new_centerline.clear();
  456 +
  457 + // push back c2
  458 + for (size_t p = 0; p < id2 + 1; p++) { // first part
  459 + new_vertex = c2[p];
  460 + new_centerline.push_back(new_vertex);
  461 + }
  462 + result.push_back(new_centerline);
  463 + new_centerline.clear();
  464 +
  465 + for (size_t p = id2; p < num2; p++) { // second part
  466 + new_vertex = c2[p];
  467 + new_centerline.push_back(new_vertex);
  468 + }
  469 + result.push_back(new_centerline);
  470 + }
  471 + }
  472 + else { // if the stitch vertex is the first or last vertex of c2
  473 + // push back c2
  474 + if (id2 == 0) { // keep geometry information
  475 + new_vertex = c1[id1];
  476 + new_centerline.push_back(new_vertex);
  477 + for (size_t p = 0; p < num2; p++) { // stitch vertex on c1 -> geometry start vertex on c2 -> geometry end vertex on c2
  478 + new_vertex = c2[p];
  479 + new_centerline.push_back(new_vertex);
  480 + }
  481 + }
  482 + else {
  483 + for (size_t p = 0; p < num2; p++) { // geometry end vertex on c2 -> geometry start vertex on c2 -> stitch vertex on c1
  484 + new_vertex = c2[p];
  485 + new_centerline.push_back(new_vertex);
  486 + }
  487 + new_vertex = c1[id1];
  488 + new_centerline.push_back(new_vertex);
  489 + result.push_back(new_centerline);
  490 + new_centerline.clear();
  491 +
  492 + // push back c1
  493 + for (size_t p = 0; p < id1 + 1; p++) { // first part
  494 + new_vertex = c1[p];
  495 + new_centerline.push_back(new_vertex);
  496 + }
  497 + result.push_back(new_centerline);
  498 + new_centerline.clear();
  499 +
  500 + for (size_t p = id1; p < num1; p++) { // second part
  501 + new_vertex = c1[p];
  502 + new_centerline.push_back(new_vertex);
  503 + }
  504 + result.push_back(new_centerline);
  505 + }
  506 + }
  507 + }
  508 + }
  509 + }
  510 + return result;
198 } 511 }
199 512
200 /// Split the fiber at the specified index. If the index is an end point, only one fiber is returned 513 /// Split the fiber at the specified index. If the index is an end point, only one fiber is returned
201 std::vector< stim::centerline<T> > split(unsigned int idx){ 514 std::vector< stim::centerline<T> > split(unsigned int idx){
202 515
203 - std::vector< stim::centerline<T> > fl; //create an array to store up to two fibers 516 + std::vector< stim::centerline<T> > fl; //create an array to store up to two fibers
  517 + size_t N = size();
204 518
205 //if the index is an end point, only the existing fiber is returned 519 //if the index is an end point, only the existing fiber is returned
206 if(idx == 0 || idx == N-1){ 520 if(idx == 0 || idx == N-1){
@@ -216,123 +530,84 @@ public: @@ -216,123 +530,84 @@ public:
216 530
217 fl.resize(2); //set the array size to 2 531 fl.resize(2); //set the array size to 2
218 532
219 - fl[0].init(N1); //set the size of each fiber  
220 - fl[1].init(N2); 533 + fl[0] = stim::centerline<T>(N1); //set the size of each fiber
  534 + fl[1] = stim::centerline<T>(N2);
221 535
222 //copy both halves of the fiber 536 //copy both halves of the fiber
223 - unsigned int i, d; 537 + unsigned int i;
224 538
225 //first half 539 //first half
226 - for(i = 0; i < N1; i++){ //for each centerline point  
227 - for(d = 0; d < 3; d++)  
228 - fl[0].c[i][d] = c[i][d]; //copy each coordinate  
229 - } 540 + for(i = 0; i < N1; i++) //for each centerline point
  541 + fl[0][i] = std::vector< stim::vec3<T> >::at(i);
  542 + fl[0].init(); //initialize the length vector
230 543
231 //second half 544 //second half
232 - for(i = 0; i < N2; i++){  
233 - for(d = 0; d < 3; d++)  
234 - fl[1].c[i][d] = c[idx + i][d];  
235 -  
236 - } 545 + for(i = 0; i < N2; i++)
  546 + fl[1][i] = std::vector< stim::vec3<T> >::at(idx+i);
  547 + fl[1].init(); //initialize the length vector
237 } 548 }
238 549
239 return fl; //return the array 550 return fl; //return the array
240 551
241 } 552 }
242 553
243 - /// Calculates the set of fibers resulting from a connection between the current fiber and a fiber f  
244 -  
245 - /// @param f is the fiber that will be connected to the current fiber  
246 -/* std::vector< stim::centerline<T> > connect( stim::centerline<T> &f, double dist){  
247 -  
248 - double min_dist;  
249 - unsigned int idx0, idx1;  
250 -  
251 - //go through each point in the query fiber, looking for the indices for the closest points  
252 - for(unsigned int i = 0; i < f.n_pts(); i++){  
253 - //Run through all points and find the index with the closest point, then partition the fiber and return two fibers.  
254 -  
255 - }  
256 -  
257 -  
258 -  
259 - }  
260 -*/  
261 /// Outputs the fiber as a string 554 /// Outputs the fiber as a string
262 std::string str(){ 555 std::string str(){
263 std::stringstream ss; 556 std::stringstream ss;
264 -  
265 - //create an iterator for the point list  
266 - //typename std::list< point<T> >::iterator i;  
267 - for(unsigned int i = 0; i < N; i++){  
268 - ss<<" [ ";  
269 - for(unsigned int d = 0; d < 3; d++){  
270 - ss<<c[i][d]<<" ";  
271 - }  
272 - } 557 + size_t N = std::vector< stim::vec3<T> >::size();
  558 + ss << "---------[" << N << "]---------" << std::endl;
  559 + for (size_t i = 0; i < N; i++)
  560 + ss << std::vector< stim::vec3<T> >::at(i) << std::endl;
  561 + ss << "--------------------" << std::endl;
273 562
274 return ss.str(); 563 return ss.str();
275 } 564 }
276 - /// Returns the number of centerline points in the fiber  
277 - unsigned int size(){  
278 - return N;  
279 - }  
280 -  
281 -  
282 - /// Bracket operator returns the element at index i  
283 -  
284 - /// @param i is the index of the element to be returned as a stim::vec  
285 - stim::vec<T> operator[](unsigned i){  
286 - return get_vec(i);  
287 - }  
288 565
289 /// Back method returns the last point in the fiber 566 /// Back method returns the last point in the fiber
290 - stim::vec<T> back(){  
291 - return get_vec(N-1); 567 + stim::vec3<T> back(){
  568 + return std::vector< stim::vec3<T> >::back();
292 } 569 }
  570 +
293 ////resample a fiber in the network 571 ////resample a fiber in the network
294 stim::centerline<T> resample(T spacing) 572 stim::centerline<T> resample(T spacing)
295 - {  
296 - std::cout<<"fiber::resample()"<<std::endl;  
297 -  
298 - std::vector<T> v(3); //v-direction vector of the segment  
299 - stim::vec<T> p(3); //- intermediate point to be added  
300 - stim::vec<T> p1(3); // p1 - starting point of an segment on the fiber,  
301 - stim::vec<T> p2(3); // p2 - ending point,  
302 - double sum=0; //distance summation  
303 - std::vector<stim::vec<T> > fiberPositions = centerline();  
304 - std::vector<stim::vec<T> > newPointList; // initialize list of new resampled points on the fiber 573 + {
  574 + //std::cout<<"fiber::resample()"<<std::endl;
  575 +
  576 + stim::vec3<T> v; //v-direction vector of the segment
  577 + stim::vec3<T> p; //- intermediate point to be added
  578 + stim::vec3<T> p1; // p1 - starting point of an segment on the fiber,
  579 + stim::vec3<T> p2; // p2 - ending point,
  580 + //double sum=0; //distance summation
  581 +
  582 + size_t N = size();
  583 +
  584 + centerline<T> new_c; // initialize list of new resampled points on the fiber
305 // for each point on the centerline (skip if it is the last point on centerline) 585 // for each point on the centerline (skip if it is the last point on centerline)
306 - //unsigned int N = fiberPositions.size(); // number of points on the fiber  
307 for(unsigned int f=0; f< N-1; f++) 586 for(unsigned int f=0; f< N-1; f++)
308 - { 587 + {
  588 + p1 = at(f);
  589 + p2 = at(f+1);
  590 + v = p2 - p1;
309 591
310 - p1 = fiberPositions[f]; p2 = fiberPositions[f + 1]; v = p2 - p1;  
311 - for(unsigned int d = 0; d < 3; d++){  
312 - sum +=v[d] * v[d];} //length of segment-distance between starting and ending point  
313 -  
314 - T lengthSegment = sqrt(sum); //find Length of the segment as distance between the starting and ending points of the segment  
315 -  
316 - if(lengthSegment >= spacing) // if length of the segment is greater than standard deviation resample  
317 - {  
318 - // repeat resampling until accumulated stepsize is equsl to length of the segment  
319 - for(T step=0.0; step<lengthSegment; step+=spacing)  
320 - {  
321 - // calculate the resampled point by travelling step size in the direction of normalized gradient vector  
322 - for(unsigned int i=0; i<3;i++)  
323 - {  
324 - p[i] = p1[i] + v[i]*(step/lengthSegment);  
325 - } //for each dimension  
326 - // add this resampled points to the new fiber list  
327 - newPointList.push_back(p);  
328 - } 592 + T lengthSegment = v.len(); //find Length of the segment as distance between the starting and ending points of the segment
  593 +
  594 + if(lengthSegment >= spacing){ // if length of the segment is greater than standard deviation resample
  595 +
  596 + // repeat resampling until accumulated stepsize is equsl to length of the segment
  597 + for(T step=0.0; step<lengthSegment; step+=spacing){
  598 + // calculate the resampled point by travelling step size in the direction of normalized gradient vector
  599 + p = p1 + v * (step / lengthSegment);
  600 +
  601 + // add this resampled points to the new fiber list
  602 + new_c.push_back(p);
329 } 603 }
330 - else // length of the segment is now less than standard deviation, push the ending point of the segment and proceed to the next point in the fiber  
331 - newPointList.push_back(fiberPositions[f+1]);  
332 } 604 }
333 - newPointList.push_back(fiberPositions[N-1]); //add the last point on the fiber to the new fiber list  
334 - centerline newFiber(newPointList);  
335 - return newFiber; 605 + else // length of the segment is now less than standard deviation, push the ending point of the segment and proceed to the next point in the fiber
  606 + new_c.push_back(at(f));
  607 + }
  608 + new_c.push_back(at(N-1)); //add the last point on the fiber to the new fiber list
  609 + //centerline newFiber(newPointList);
  610 + return new_c;
336 } 611 }
337 612
338 }; 613 };
stim/biomodels/centerline_dep.h 0 → 100644
  1 +/*
  2 +Copyright <2017> <David Mayerich>
  3 +
  4 +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  5 +
  6 +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  7 +
  8 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  9 +*/
  10 +
  11 +#ifndef STIM_CENTERLINE_H
  12 +#define STIM_CENTERLINE_H
  13 +
  14 +#include <vector>
  15 +#include <stim/math/vec3.h>
  16 +
  17 +namespace stim{
  18 +
  19 +/** This class stores information about a single fiber represented as a set of geometric points
  20 + * between two branch or end points. This class is used as a fundamental component of the stim::network
  21 + * class to describe an interconnected (often biological) network.
  22 + */
  23 +template<typename T>
  24 +class centerline{
  25 +
  26 +protected:
  27 + unsigned int N; //number of points in the fiber
  28 + double **c; //centerline (array of double pointers)
  29 +
  30 + /// Initialize an empty fiber
  31 + void init(){
  32 + N=0;
  33 + c=NULL;
  34 + }
  35 +
  36 + /// Initialize a fiber with N centerline points (all located at [0, 0, 0] with radius 0)
  37 + void init(unsigned int n){
  38 +
  39 + N = n; //set the number of points
  40 + c = (double**) malloc(sizeof(double*) * N); //allocate the array pointer
  41 +
  42 + for(unsigned int i = 0; i < N; i++) //allocate space for each point
  43 + c[i] = (double*) malloc(sizeof(double) * 3);
  44 + }
  45 +
  46 + /// Copies an existing fiber to the current fiber
  47 +
  48 + /// @param cpy stores the new copy of the fiber
  49 + void copy( const stim::centerline<T>& cpy, bool kd = 0){
  50 +
  51 + ///allocate space for the new fiber
  52 + init(cpy.N);
  53 +
  54 + ///copy the points
  55 + for(unsigned int i = 0; i < N; i++){
  56 + for(unsigned int d = 0; d < 3; d++) //for each dimension
  57 + c[i][d] = cpy.c[i][d]; //copy the coordinate
  58 + }
  59 + }
  60 +
  61 + /// find distance between two points
  62 + double dist(double* p0, double* p1){
  63 +
  64 + double sum = 0; // initialize variables
  65 + float v;
  66 + for(unsigned int d = 0; d < 3; d++)
  67 + {
  68 + v = p1[d] - p0[d];
  69 + sum +=v * v;
  70 + }
  71 + return sqrt(sum);
  72 + }
  73 +
  74 + /// Returns a stim::vec representing the point at index i
  75 +
  76 + /// @param i is an index of the desired centerline point
  77 + stim::vec<T> get_vec(unsigned i){
  78 + stim::vec3<T> r;
  79 + r.resize(3);
  80 + r[0] = c[i][0];
  81 + r[1] = c[i][1];
  82 + r[2] = c[i][2];
  83 +
  84 + return r;
  85 + }
  86 +
  87 +
  88 +public:
  89 +
  90 + centerline(){
  91 + init();
  92 + }
  93 +
  94 + /// Copy constructor
  95 + centerline(const stim::centerline<T> &obj){
  96 + copy(obj);
  97 + }
  98 +
  99 + //initialize a centerline with n points
  100 + centerline(int n){
  101 + init(n);
  102 + }
  103 +
  104 + /// Constructor takes a list of stim::vec points, the radius at each point is set to zero
  105 + centerline(std::vector< stim::vec<T> > p, bool kd = 0){
  106 + init(p.size()); //initialize the fiber
  107 +
  108 + //for each point, set the centerline position and radius
  109 + for(unsigned int i = 0; i < N; i++){
  110 +
  111 + //set the centerline position
  112 + for(unsigned int d = 0; d < 3; d++)
  113 + c[i][d] = (double) p[i][d];
  114 + }
  115 + }
  116 +
  117 + /// constructor takes a list of points
  118 + centerline(std::vector< stim::vec3< T > > pos, bool kd = 0){
  119 + init(pos.size()); //initialize the fiber
  120 +
  121 + //for each point, set the centerline position and radius
  122 + for(unsigned int i = 0; i < N; i++){
  123 + //set the centerline position
  124 + for(unsigned int d = 0; d < 3; d++)
  125 + c[i][d] = (double) pos[i][d];
  126 + }
  127 + }
  128 +
  129 + /// Assignment operation
  130 + centerline& operator=(const centerline &rhs){
  131 + if(this == &rhs) return *this; //test for and handle self-assignment
  132 + copy(rhs);
  133 + return *this;
  134 + }
  135 +
  136 +
  137 + /// Returns the fiber centerline as an array of stim::vec points
  138 + std::vector< stim::vec<T> > get_centerline(){
  139 +
  140 + //create an array of stim vectors
  141 + std::vector< stim::vec3<T> > pts(N);
  142 +
  143 + //cast each point to a stim::vec, keeping only the position information
  144 + for(unsigned int i = 0; i < N; i++)
  145 + pts[i] = stim::vec3<T>((T) c[i][0], (T) c[i][1], (T) c[i][2]);
  146 +
  147 + //return the centerline array
  148 + return pts;
  149 + }
  150 +
  151 + /// Split the fiber at the specified index. If the index is an end point, only one fiber is returned
  152 + std::vector< stim::centerline<T> > split(unsigned int idx){
  153 +
  154 + std::vector< stim::centerline<T> > fl; //create an array to store up to two fibers
  155 +
  156 + //if the index is an end point, only the existing fiber is returned
  157 + if(idx == 0 || idx == N-1){
  158 + fl.resize(1); //set the size of the fiber to 1
  159 + fl[0] = *this; //copy the current fiber
  160 + }
  161 +
  162 + //if the index is not an end point
  163 + else{
  164 +
  165 + unsigned int N1 = idx + 1; //calculate the size of both fibers
  166 + unsigned int N2 = N - idx;
  167 +
  168 + fl.resize(2); //set the array size to 2
  169 +
  170 + fl[0].init(N1); //set the size of each fiber
  171 + fl[1].init(N2);
  172 +
  173 + //copy both halves of the fiber
  174 + unsigned int i, d;
  175 +
  176 + //first half
  177 + for(i = 0; i < N1; i++){ //for each centerline point
  178 + for(d = 0; d < 3; d++)
  179 + fl[0].c[i][d] = c[i][d]; //copy each coordinate
  180 + }
  181 +
  182 + //second half
  183 + for(i = 0; i < N2; i++){
  184 + for(d = 0; d < 3; d++)
  185 + fl[1].c[i][d] = c[idx + i][d];
  186 +
  187 + }
  188 + }
  189 +
  190 + return fl; //return the array
  191 +
  192 + }
  193 +
  194 + /// Outputs the fiber as a string
  195 + std::string str(){
  196 + std::stringstream ss;
  197 +
  198 + //create an iterator for the point list
  199 + //typename std::list< point<T> >::iterator i;
  200 + for(unsigned int i = 0; i < N; i++){
  201 + ss<<" [ ";
  202 + for(unsigned int d = 0; d < 3; d++){
  203 + ss<<c[i][d]<<" ";
  204 + }
  205 + }
  206 +
  207 + return ss.str();
  208 + }
  209 + /// Returns the number of centerline points in the fiber
  210 + unsigned int size(){
  211 + return N;
  212 + }
  213 +
  214 +
  215 + /// Bracket operator returns the element at index i
  216 +
  217 + /// @param i is the index of the element to be returned as a stim::vec
  218 + stim::vec<T> operator[](unsigned i){
  219 + return get_vec(i);
  220 + }
  221 +
  222 + /// Back method returns the last point in the fiber
  223 + stim::vec<T> back(){
  224 + return get_vec(N-1);
  225 + }
  226 + ////resample a fiber in the network
  227 + stim::centerline<T> resample(T spacing)
  228 + {
  229 + std::cout<<"fiber::resample()"<<std::endl;
  230 +
  231 + std::vector<T> v(3); //v-direction vector of the segment
  232 + stim::vec<T> p(3); //- intermediate point to be added
  233 + stim::vec<T> p1(3); // p1 - starting point of an segment on the fiber,
  234 + stim::vec<T> p2(3); // p2 - ending point,
  235 + double sum=0; //distance summation
  236 + std::vector<stim::vec<T> > fiberPositions = centerline();
  237 + std::vector<stim::vec<T> > newPointList; // initialize list of new resampled points on the fiber
  238 + // for each point on the centerline (skip if it is the last point on centerline)
  239 + for(unsigned int f=0; f< N-1; f++)
  240 + {
  241 +
  242 + p1 = fiberPositions[f]; p2 = fiberPositions[f + 1]; v = p2 - p1;
  243 + for(unsigned int d = 0; d < 3; d++){
  244 + sum +=v[d] * v[d];} //length of segment-distance between starting and ending point
  245 +
  246 + T lengthSegment = sqrt(sum); //find Length of the segment as distance between the starting and ending points of the segment
  247 +
  248 + if(lengthSegment >= spacing) // if length of the segment is greater than standard deviation resample
  249 + {
  250 + // repeat resampling until accumulated stepsize is equsl to length of the segment
  251 + for(T step=0.0; step<lengthSegment; step+=spacing)
  252 + {
  253 + // calculate the resampled point by travelling step size in the direction of normalized gradient vector
  254 + for(unsigned int i=0; i<3;i++)
  255 + {
  256 + p[i] = p1[i] + v[i]*(step/lengthSegment);
  257 + } //for each dimension
  258 + // add this resampled points to the new fiber list
  259 + newPointList.push_back(p);
  260 + }
  261 + }
  262 + else // length of the segment is now less than standard deviation, push the ending point of the segment and proceed to the next point in the fiber
  263 + newPointList.push_back(fiberPositions[f+1]);
  264 + }
  265 + newPointList.push_back(fiberPositions[N-1]); //add the last point on the fiber to the new fiber list
  266 + centerline newFiber(newPointList);
  267 + return newFiber;
  268 + }
  269 +
  270 +};
  271 +
  272 +
  273 +
  274 +} //end namespace stim
  275 +
  276 +
  277 +
  278 +#endif
stim/biomodels/flow.h
1 -#pragma once  
2 -#include <fstream> // Required for ofstream, etc.  
3 -#include <iomanip> // Required for setw  
4 -#include <iostream> // Required for cout, cin, etc.  
5 -#include <tuple> // Required for returning multiple values from a function  
6 -  
7 -using namespace std;  
8 -  
9 -  
10 -class flow  
11 -{  
12 -public:  
13 - void backupToTxt(unsigned int nL, double **D, char filename[]);  
14 - tuple<int, int> copySrcDesRadLen(char filename[]);  
15 - void copyToArray(int *src, int *dest, double *radii, double *len);  
16 - int getDangleNodes(int datarow, int numNodes, int *row, int *column, int *dangleNodes);  
17 - void inversion(double **a, int n, double **b);  
18 -  
19 -protected:  
20 - float determinant(double **a, int n);  
21 - int minor(double **src, double **dest, int row, int col, int order);  
22 -};  
23 -  
24 -/* Function to find the dangle nodes in a network */  
25 -// Created by Cherub P. Harder (8/10/2015), U of Houston  
26 -// Modified by Cherub P. Harder on 8/12/2015  
27 -int flow::getDangleNodes(int datarow, int numNodes, int *column1, int *column2, int *dangleNodes)  
28 -{  
29 - int count = datarow, diff1 = 0, diff2 = 0, numPress = 0, st = 0;  
30 -  
31 - // Find matching nodes within column2  
32 - for( int i = 0; i < count; i++ )  
33 - {  
34 - for( int y = i+1; y < datarow; y++ )  
35 - {  
36 - if( column2[i] == column2[y] ) // Is there a match?  
37 - {  
38 - st = column2[i]; // Save the matching node  
39 -// cout << endl << column2[i] << " = " << column2[y] << endl; // Display the matching nodes  
40 - memmove(column2+i, column2+i+1, (datarow-(i+1)) * sizeof(column2[0])); // Move up the rows  
41 - // taking the places of the rows before them starting  
42 - // with where the matching node is located  
43 - column2[datarow-1] = st; // Place the matching node at the very end of the array--  
44 - // this is for comparison purpose so that the other match-  
45 - // ing node will be moved as well and then omitted later.  
46 - diff1++; // Count the matching node  
47 -  
48 - // Display the updated array (with the matching node moved to the bottommost row)  
49 -/* cout << "Updated array:" << endl;  
50 - for( int k = 0; k < datarow; k++ )  
51 - cout << column2[k] << endl; 1 +/*
  2 +Copyright <2017> <David Mayerich>
  3 +
  4 +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  5 +
  6 +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  7 +
  8 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
52 */ 9 */
53 - // Decrement the counters  
54 - // NOTE: The counters need to be decremented because the rows had been moved up, so the same  
55 - // locations need to be read again because they contain different values now after the move.  
56 - i--; // Decrement i to read the node that took over the place  
57 - // of the matching node. Otherwise, it will be skipped.  
58 - y--; // Decrement y to read the following node for comparison  
59 - count--; // The maximum count need to be decremented so that the  
60 - // matching nodes that had been moved will not be read again.  
61 - // However, the maximum count (datarow) for finding a match  
62 - // will not be decremented because the remaining matching  
63 - // node that has not been moved yet needs to be moved and  
64 - // the only way to do that is to match it with its duplicate.  
65 - }  
66 - }  
67 - }  
68 -  
69 - // Store the nodes that have no duplicates  
70 - // NOTE: This will only save the nodes that have not been moved to the bottom.  
71 -// cout << "\ndangleNodes array:" << endl;  
72 - for( int j = 0; j < datarow-diff1; j++ )  
73 - {  
74 - dangleNodes[numPress] = column2[j];  
75 -// cout << dangleNodes[j] << endl; // DELETE!!!  
76 - numPress++; // Count the non-duplicated node  
77 - }  
78 -  
79 - // Find if the non-duplicated nodes have a match from column1  
80 - count = datarow-diff1; // Reinitialize the counter  
81 -  
82 - for( int i = 0; i < count; i++ )  
83 - {  
84 - for( int j = 0; j < datarow; j++ )  
85 - {  
86 - if( dangleNodes[i] == column1[j] ) // Is there a match?  
87 - {  
88 - st = column1[j]; // Save the matching node  
89 -// cout << endl << dangleNodes[i] << " = " << column1[j] << endl; // Display the matching nodes  
90 - memmove(dangleNodes+i, dangleNodes+i+1, (datarow-diff1-(i+1)) * sizeof(dangleNodes[0]));  
91 - dangleNodes[count-1] = st; // Move the matching node to the bottom of the array  
92 - diff2++; // Count the matching node  
93 -  
94 - // Display the updated array  
95 -/* cout << "Updated dangleNodes array:" << endl;  
96 - for( int k = 0; k < count-1; k++ )  
97 - {  
98 - cout << dangleNodes[k] << endl; 10 +#ifndef STIM_FLOW_H
  11 +#define STIM_FLOW_H
  12 +
  13 +#include <vector>
  14 +#include <algorithm>
  15 +
  16 +//STIM include
  17 +#include <stim/math/vec3.h>
  18 +#include <stim/parser/arguments.h>
  19 +#include <stim/biomodels/network.h>
  20 +
  21 +#ifdef __CUDACC__
  22 +#include <cublas_v2.h>
  23 +#include <stim/cuda/cudatools/error.h>
  24 +#endif
  25 +
  26 +namespace stim {
  27 + template <typename A, typename B, typename C>
  28 + struct triple {
  29 + A first;
  30 + B second;
  31 + C third;
  32 + };
  33 +
  34 + template <typename T>
  35 + struct bridge {
  36 + std::vector<unsigned> v; // vertices' indices
  37 + std::vector<typename stim::vec3<T> > V; // vertices' coordinates
  38 + T l; // length
  39 + T r; // radii
  40 + T deltaP; // pressure drop
  41 + T Q; // volume flow rate
  42 + };
  43 +
  44 + template <typename T>
  45 + class flow {
  46 +
  47 + private:
  48 +
  49 + // calculate the cofactor of elemen[row][col]
  50 + void get_minor(T** src, T** dest, int row, int col, int order) {
  51 +
  52 + // index of element to be copied
  53 + int rowCount = 0;
  54 + int colCount = 0;
  55 +
  56 + for (int i = 0; i < order; i++) {
  57 + if (i != row) {
  58 + colCount = 0;
  59 + for (int j = 0; j < order; j++) {
  60 + // when j is not the element
  61 + if (j != col) {
  62 + dest[rowCount][colCount] = src[i][j];
  63 + colCount++;
  64 + }
  65 + }
  66 + rowCount++;
99 } 67 }
100 -*/  
101 - // Decrement the counters  
102 - i--;  
103 - j--;  
104 - count--;  
105 - numPress--; // Decrement to the exact number of dangle nodes  
106 } 68 }
107 } 69 }
108 - }  
109 70
110 - return numPress; // Number of dangle nodes  
111 -} 71 + // calculate the det()
  72 + T determinant(T** mat, int order) {
112 73
  74 + // degenate case when n = 1
  75 + if (order == 1)
  76 + return mat[0][0];
113 77
114 -// Function to make a backup copy of the contents of a matrix to a .txt file  
115 -// Created by Cherub P. Harder (8/10/2015), U of Houston  
116 -void flow::backupToTxt(unsigned int nL, double **D, char filename[])  
117 -{  
118 - ofstream output_file(filename);  
119 -  
120 - for( unsigned int i = 0; i < nL; i++ )  
121 - {  
122 - for( int j = 0; j < 4; j++ )  
123 - {  
124 - if( j < 3 )  
125 - output_file << D[i][j] << "\t";  
126 -  
127 - else  
128 - output_file << D[i][j];  
129 - } 78 + T det = 0.0; // determinant value
130 79
131 - output_file << "\n";  
132 - } 80 + // allocate the cofactor matrix
  81 + T** minor = (T**)malloc((order - 1) * sizeof(T*));
  82 + for (int i = 0; i < order - 1; i++)
  83 + minor[i] = (T*)malloc((order - 1) * sizeof(T));
133 84
134 - output_file.close( );  
135 -}  
136 85
  86 + for (int i = 0; i < order; i++) {
137 87
138 -// Function to make separate copies of the source nodes, destination nodes, radii, and lengths  
139 -// Created by Cherub P. Harder (8/10/2015), U of Houston  
140 -tuple<int, int> flow::copySrcDesRadLen(char filename[])  
141 -{  
142 - int cnt = 0, numElements = 0, numNodes = 0;  
143 - float number = 0.0;  
144 - ofstream srcData("srcCol.txt"); // A .txt file to store the source nodes  
145 - ofstream desData("destCol.txt"); // A .txt file to store the destination nodes  
146 - ofstream radiiData("radii.txt"); // A .txt file to store the radii  
147 - ofstream lenData("lengths.txt"); // A .txt file to store the lengths  
148 - FILE *fp = fopen(filename, "r"); // Create a variable of type FILE* and open the file using  
149 - // the fopen function and assign the file to the variable  
150 - // Check if the file exists  
151 - if(fp == NULL) // Alternative: if(!fp)  
152 - {  
153 - printf("Error! File does not exist.\n");  
154 - getchar( ); // Pause  
155 - exit(-1); // NOTE: Must include stdlib.h.  
156 - }  
157 -  
158 - // Store data to their respective .txt files  
159 - while(fscanf(fp, "%f", &number) == 1)  
160 - {  
161 - cnt++; // Increment counter  
162 -  
163 - // Store to srcCol.txt  
164 - if(cnt == 1)  
165 - srcData << number << endl;  
166 -  
167 - // Store to destCol.txt  
168 - if(cnt == 2)  
169 - desData << number << endl;  
170 -  
171 - // Save the current number of nodes  
172 - if(cnt < 3)  
173 - {  
174 - if(number > numNodes)  
175 - numNodes = (int)number;  
176 - } 88 + // get minor of element(0, i)
  89 + get_minor(mat, minor, 0, i, order);
177 90
178 - // Store to radii.txt  
179 - if(cnt == 3)  
180 - radiiData << number << endl; 91 + // recursion
  92 + det += (i % 2 == 1 ? -1.0 : 1.0) * mat[0][i] * determinant(minor, order - 1);
  93 + }
181 94
182 - // Store to lengths.txt  
183 - if(cnt == 4)  
184 - {  
185 - lenData << number << endl; 95 + // release memory
  96 + for (int i = 0; i < order - 1; i++)
  97 + free(minor[i]);
  98 + free(minor);
186 99
187 - numElements++; // Count the elements  
188 - cnt = 0; // Reset counter 100 + return det;
189 } 101 }
190 - }  
191 -  
192 - srcData.close( );  
193 - desData.close( );  
194 - radiiData.close( );  
195 - lenData.close( );  
196 -  
197 - return make_tuple(numNodes, numElements); // Return two values  
198 -}  
199 -  
200 -  
201 -// Function to copy data for .txt files to their respective arrays  
202 -// Created by Cherub P. Harder (8/11/2015), U of Houston  
203 -void flow::copyToArray(int *src, int *dest, double *radii, double *len)  
204 -{  
205 - int v = 0;  
206 - double tmp = 0, R = 0, L = 0;  
207 -  
208 - // Store source node values to the array src  
209 - ifstream readSrc("srcCol.txt");  
210 -  
211 - while( readSrc >> tmp )  
212 - {  
213 - src[v] = (int)tmp;  
214 - v++;  
215 - }  
216 -  
217 - readSrc.close( );  
218 102
219 - // Store destination node values to the array dest  
220 - v = 0; // Reset counter  
221 - ifstream readDest("destCol.txt"); 103 + public:
  104 + T** C; // Conductance
  105 + std::vector<typename stim::triple<unsigned, unsigned, float> > Q; // volume flow rate
  106 + std::vector<T> QQ; // Q' vector
  107 + std::vector<T> P; // initial pressure
  108 + std::vector<T> pressure; // final pressure
222 109
223 - while( readDest >> tmp )  
224 - {  
225 - dest[v] = (int)tmp;  
226 - v++;  
227 - } 110 + //std::vector<typename stim::triple<unsigned, unsigned, T> > V; // velocity
  111 + //std::vector<typename stim::triple<unsigned, unsigned, T> > Q; // volume flow rate
  112 + //std::vector<typename stim::triple<unsigned, unsigned, T> > deltaP; // pressure drop
228 113
229 - readDest.close( ); 114 + flow() {} // default constructor
230 115
231 - // Store radius values to the array radii  
232 - v = 0; // Reset counter  
233 - ifstream readRad("radii.txt");  
234 -  
235 - while( readRad >> tmp )  
236 - {  
237 - radii[v] = tmp;  
238 - v++;  
239 - } 116 + void init(unsigned n_e, unsigned n_v) {
  117 +
  118 + C = new T*[n_v]();
  119 + for (unsigned i = 0; i < n_v; i++) {
  120 + C[i] = new T[n_v]();
  121 + }
240 122
241 - readRad.close( ); 123 + QQ.resize(n_v);
  124 + P.resize(n_v);
  125 + pressure.resize(n_v);
242 126
243 - // Store length values to the array len  
244 - v = 0; // Reset counter  
245 - ifstream readLen("lengths.txt"); 127 + Q.resize(n_e);
  128 + }
246 129
247 - while( readLen >> tmp )  
248 - {  
249 - len[v] = tmp;  
250 - v++;  
251 - } 130 + void reset(unsigned n_v) {
  131 +
  132 + for (unsigned i = 0; i < n_v; i++) {
  133 + for (unsigned j = 0; j < n_v; j++) {
  134 + C[i][j] = 0;
  135 + }
  136 + }
  137 + }
252 138
253 - readLen.close( );  
254 -} 139 + void clear(unsigned n_v) {
  140 +
  141 + for (unsigned i = 0; i < n_v; i++)
  142 + delete[] C[i];
  143 + delete[] C;
  144 + }
255 145
  146 + /// Calculate the inverse of A and store the result in C
  147 + void inversion(T** A, int order, T* C) {
  148 +
  149 +#ifdef __CUDACC__
  150 +
  151 + // convert from double pointer to single pointer, make it flat
  152 + T* Aflat = (T*)malloc(order * order * sizeof(T));
  153 + for (unsigned i = 0; i < order; i++)
  154 + for (unsigned j = 0; j < order; j++)
  155 + Aflat[i * order + j] = A[i][j];
  156 +
  157 + // create device pointer
  158 + T* d_Aflat; // flat original matrix
  159 + T* d_Cflat; // flat inverse matrix
  160 + T** d_A; // put the flat original matrix into another array of pointer
  161 + T** d_C;
  162 + int *d_P;
  163 + int *d_INFO;
  164 +
  165 + // allocate memory on device
  166 + HANDLE_ERROR(cudaMalloc((void**)&d_Aflat, order * order * sizeof(T)));
  167 + HANDLE_ERROR(cudaMalloc((void**)&d_Cflat, order * order * sizeof(T)));
  168 + HANDLE_ERROR(cudaMalloc((void**)&d_A, sizeof(T*)));
  169 + HANDLE_ERROR(cudaMalloc((void**)&d_C, sizeof(T*)));
  170 + HANDLE_ERROR(cudaMalloc((void**)&d_P, order * 1 * sizeof(int)));
  171 + HANDLE_ERROR(cudaMalloc((void**)&d_INFO, 1 * sizeof(int)));
  172 +
  173 + // copy matrix from host to device
  174 + HANDLE_ERROR(cudaMemcpy(d_Aflat, Aflat, order * order * sizeof(T), cudaMemcpyHostToDevice));
  175 +
  176 + // copy matrix from device to device
  177 + HANDLE_ERROR(cudaMemcpy(d_A, &d_Aflat, sizeof(T*), cudaMemcpyHostToDevice));
  178 + HANDLE_ERROR(cudaMemcpy(d_C, &d_Cflat, sizeof(T*), cudaMemcpyHostToDevice));
256 179
257 -// Function to find the inverse of a square matrix  
258 -void flow::inversion(double **a, int n, double **b)  
259 -{  
260 - // Get 1 over the determinant of A  
261 - double det = (double)(1.0/determinant(a, n));  
262 -// cerr << "\n1/det(C) = " << det << endl; // DELETE!!!  
263 -  
264 - // Memory allocation  
265 - double *tmp = new double[(n-1) * (n-1)];  
266 - double **m = new double * [n-1];  
267 - for( int i = 0; i < n-1; i++ )  
268 - m[i] = tmp + ( i * (n-1) );  
269 -  
270 - for( int j = 0; j < n; j++)  
271 - {  
272 - for( int i = 0; i < n; i++ )  
273 - {  
274 - // Get the cofactor (matrix) of a(j,i)  
275 - minor(a, m, j, i, n);  
276 - b[i][j] = det * determinant( m, n-1 );  
277 - if( (i+j)%2 == 1 )  
278 - b[i][j] = -b[i][j];  
279 - }  
280 - }  
281 -  
282 - // Release memory  
283 - // Delete [] minor[0];  
284 - delete [] tmp;  
285 - delete [] m;  
286 -} 180 + // calculate the inverse of matrix based on cuBLAS
  181 + cublasHandle_t handle;
  182 + CUBLAS_HANDLE_ERROR(cublasCreate_v2(&handle)); // create cuBLAS handle object
287 183
  184 + CUBLAS_HANDLE_ERROR(cublasSgetrfBatched(handle, order, d_A, order, d_P, d_INFO, 1));
288 185
289 -// Function to find the determinant of a matrix using recursion  
290 -// Contribution by Edward Popko  
291 -// Modified by Cherub P. Harder (7/15/2015), U of Houston  
292 -// Arguments: a(double **) - pointer to a pointer of an arbitrary square matrix  
293 -// n(int) - dimension of the square matrix  
294 -float flow::determinant(double **a, int n)  
295 -{  
296 - int i, j, j1, j2; // General loop and matrix subscripts  
297 - double det = 0; // Initialize determinant  
298 - double **m = NULL; // Pointer to pointer to implement 2D square array  
299 -  
300 - // Display contents of matrix C (DELETE!!!)  
301 -/* std::cout << "\nThe updated matrix C:\n";  
302 - for( int j = 0; j < n; ++j )  
303 - {  
304 - std::cerr << "\t";  
305 -  
306 - for( int k = 0; k < n; ++k )  
307 - std::cerr << left << setw(15) << a[j][k];  
308 -  
309 - std::cerr << endl;  
310 - }  
311 -  
312 - getchar(); // DELETE!!!*/  
313 -  
314 - if(n < 1) { } // Error condition - should never get here  
315 -  
316 - else if (n == 1) // Should never get here  
317 - {  
318 - det = a[0][0];  
319 - }  
320 -  
321 - else if(n == 2) // Basic 2x2 sub-matrix determinate definition  
322 - { // When n == 2, this ends the recursion series  
323 - det = a[0][0] * a[1][1] - a[1][0] * a[0][1];  
324 - }  
325 - // Recursion continues, solve next sub-matrix  
326 - else // Solve the next minor by building a sub-matrix  
327 - {  
328 - det = 0; // Initialize determinant of sub-matrix  
329 -  
330 - for (j1 = 0; j1 < n; j1++) // For each column in sub-matrix get space for the  
331 - { // pointer list  
332 - m = (double **) malloc((n-1) * sizeof(double *));  
333 -  
334 - for (i = 0; i < n-1; i++)  
335 - m[i] = (double *) malloc((n-1)* sizeof(double));  
336 - // i[0][1][2][3] first malloc  
337 - // m -> + + + + space for 4 pointers  
338 - // | | | | j second malloc  
339 - // | | | +-> _ _ _ [0] pointers to  
340 - // | | +----> _ _ _ [1] and memory for  
341 - // | +-------> _ a _ [2] 4 doubles  
342 - // +----------> _ _ _ [3]  
343 - //  
344 - // a[1][2]  
345 - // Build sub-matrix with minor elements excluded  
346 -  
347 - for (i = 1; i < n; i++) 186 + int INFO = 0;
  187 + HANDLE_ERROR(cudaMemcpy(&INFO, d_INFO, sizeof(int), cudaMemcpyDeviceToHost));
  188 + if (INFO == order)
348 { 189 {
349 - j2 = 0 ; // Start at first sum-matrix column position  
350 - // Loop to copy source matrix less one column  
351 - for (j = 0; j < n; j++)  
352 - {  
353 - if (j == j1) continue; // Do not copy the minor column element  
354 -  
355 - m[i-1][j2] = a[i][j]; // Copy source element into new sub-matrix  
356 - // i-1 because new sub-matrix is one row  
357 - // (and column) smaller with excluded minors  
358 - j2++; // Move to next sub-matrix column position 190 + std::cout << "Factorization Failed : Matrix is singular." << std::endl;
  191 + cudaDeviceReset();
  192 + exit(1);
  193 + }
  194 +
  195 + CUBLAS_HANDLE_ERROR(cublasSgetriBatched(handle, order, (const T **)d_A, order, d_P, d_C, order, d_INFO, 1));
  196 +
  197 + CUBLAS_HANDLE_ERROR(cublasDestroy_v2(handle));
  198 +
  199 + // copy inverse matrix from device to device
  200 + HANDLE_ERROR(cudaMemcpy(&d_Cflat, d_C, sizeof(T*), cudaMemcpyDeviceToHost));
  201 +
  202 + // copy inverse matrix from device to host
  203 + HANDLE_ERROR(cudaMemcpy(C, d_Cflat, order * order * sizeof(T), cudaMemcpyDeviceToHost));
  204 +
  205 + // clear up
  206 + free(Aflat);
  207 + HANDLE_ERROR(cudaFree(d_Aflat));
  208 + HANDLE_ERROR(cudaFree(d_Cflat));
  209 + HANDLE_ERROR(cudaFree(d_A));
  210 + HANDLE_ERROR(cudaFree(d_C));
  211 + HANDLE_ERROR(cudaFree(d_P));
  212 + HANDLE_ERROR(cudaFree(d_INFO));
  213 +
  214 +#else
  215 + // get the determinant of a
  216 + double det = 1.0 / determinant(A, order);
  217 +
  218 + // memory allocation
  219 + T* tmp = (T*)malloc((order - 1)*(order - 1) * sizeof(T));
  220 + T** minor = (T**)malloc((order - 1) * sizeof(T*));
  221 + for (int i = 0; i < order - 1; i++)
  222 + minor[i] = tmp + (i * (order - 1));
  223 +
  224 + for (int j = 0; j < order; j++) {
  225 + for (int i = 0; i < order; i++) {
  226 + // get the co-factor (matrix) of A(j,i)
  227 + get_minor(A, minor, j, i, order);
  228 + C[i][j] = det * determinant(minor, order - 1);
  229 + if ((i + j) % 2 == 1)
  230 + C[i][j] = -C[i][j];
359 } 231 }
360 } 232 }
361 -  
362 - det += (double)pow(-1.0, 1.0 + j1 + 1.0) * a[0][j1] * determinant(m, n-1);  
363 - // Sum x raised to y power  
364 - // recursively get determinant of next  
365 - // sub-matrix which is now one  
366 - // row & column smaller  
367 -  
368 - for (i = 0; i < n-1; i++) free(m[i]); // Free the storage allocated to  
369 - // this minor's set of pointers  
370 - free(m); // Free the storage for the original  
371 - // pointer to pointer 233 +
  234 + // release memory
  235 + free(tmp);
  236 + free(minor);
  237 +#endif
372 } 238 }
373 - }  
374 -  
375 - return(det); 239 + };
376 } 240 }
377 241
378 -  
379 -// Function to calculate the cofactor of element (row, col)  
380 -int flow::minor(double **src, double **dest, int row, int col, int order)  
381 -{  
382 - // Indicate which col and row is being copied to dest  
383 - int colCount=0,rowCount=0;  
384 -  
385 - for(int i = 0; i < order; i++)  
386 - {  
387 - if(i != row)  
388 - {  
389 - colCount = 0;  
390 - for(int j = 0; j < order; j++)  
391 - {  
392 - // When j is not the element  
393 - if( j != col )  
394 - {  
395 - dest[rowCount][colCount] = src[i][j];  
396 - colCount++;  
397 - }  
398 - }  
399 -  
400 - rowCount++;  
401 - }  
402 - }  
403 -  
404 - return 1;  
405 -}  
406 \ No newline at end of file 242 \ No newline at end of file
  243 +#endif
  244 +
  245 +
  246 +
  247 +//// calculate the flow rate of 3D model(circle cross section)
  248 +//void calculate_flow_rate(unsigned e, T r) {
  249 +// stim::triple<unsigned, unsigned, T> tmp_Q;
  250 +// tmp_Q.first = V[e].first; // copy the vertices information
  251 +// tmp_Q.second = V[e].second;
  252 +// tmp_Q.third = V[e].third * stim::PI * pow(r, 2); // UNITS: uL/s
  253 +// Q.push_back(tmp_Q); // push back the volume flow rate information for every edge
  254 +//}
  255 +
  256 +//// calculate the flow rate of 2D model(rectangular cross section)
  257 +//void calculate_flow_rate(unsigned e, T r, T h) {
  258 +// stim::triple<unsigned, unsigned, T> tmp_Q;
  259 +// tmp_Q.first = V[e].first; // copy the vertices information
  260 +// tmp_Q.second = V[e].second;
  261 +// tmp_Q.third = V[e].third * h * r; // UNITS: uL/s = mm^3/s
  262 +// Q.push_back(tmp_Q); // push back the volume flow rate information for every edge
  263 +//}
  264 +
  265 +//// calculate the pressure drop of 3D model(circle cross section)
  266 +//void calculate_deltaP(unsigned e, T u, T l, T r) {
  267 +// stim::triple<unsigned, unsigned, T> tmp_deltaP;
  268 +// tmp_deltaP.first = V[e].first; // copy the vertices information
  269 +// tmp_deltaP.second = V[e].second;
  270 +// tmp_deltaP.third = (8 * u * l * Q[e].third) / (stim::PI * pow(r, 4)); // UNITS: g/mm/s^2 = Pa
  271 +// deltaP.push_back(tmp_deltaP); // push back the volume flow rate information for every edge
  272 +//}
  273 +
  274 +//// calculate the pressure drop of 2D model(rectangular cross section)
  275 +//void calculate_deltaP(unsigned e, T u, T l, T r, T h) {
  276 +// stim::triple<unsigned, unsigned, T> tmp_deltaP;
  277 +// tmp_deltaP.first = V[e].first; // copy the vertices information
  278 +// tmp_deltaP.second = V[e].second;
  279 +// tmp_deltaP.third = (12 * u * l * Q[e].third) / (h * pow(r, 3)); // UNITS: g/mm/s^2 = Pa
  280 +// deltaP.push_back(tmp_deltaP); // push back the volume flow rate information for every edge
  281 +//}
  282 +
  283 +//// better way to do this???
  284 +//// find the maximum and minimum pressure positions
  285 +//void find_max_min_pressure(size_t n_e, size_t n_v, unsigned& max, unsigned& min) {
  286 +// std::vector<T> P(n_v, FLT_MAX);
  287 +// // set one to reference
  288 +// P[Q[0].first] = 0.0;
  289 +// unsigned first = 0;
  290 +// unsigned second = 0;
  291 +// // calculate all the relative pressure in brute force manner
  292 +// for (unsigned e = 0; e < n_e; e++) {
  293 +// // assuming the obj file stores in a straight order, in other words, like swc file
  294 +// first = Q[e].first;
  295 +// second = Q[e].second;
  296 +// if (P[first] != FLT_MAX) // if pressure at start vertex is known
  297 +// P[second] = P[first] - deltaP[e].third;
  298 +// else if (P[second] != FLT_MAX) // if pressure at end vertex is known
  299 +// P[first] = P[second] + deltaP[e].third;
  300 +// }
  301 +
  302 +// // find the maximum and minimum pressure position
  303 +// auto m1 = std::max_element(P.begin(), P.end()); // temporarily max number
  304 +// auto m2 = std::min_element(P.begin(), P.end()); // temporarily min number
  305 +
  306 +// max = std::distance(P.begin(), m1);
  307 +// min = std::distance(P.begin(), m2);
  308 +
  309 +// T tmp_m = *m2;
  310 +// // Now set the lowest pressure port to reference pressure(0.0 Pa)
  311 +// for (unsigned i = 0; i < n_v; i++)
  312 +// P[i] -= tmp_m;
  313 +
  314 +// for (unsigned i = 0; i < n_v; i++)
  315 +// pressure.push_back(P[i]);
  316 +//}
407 \ No newline at end of file 317 \ No newline at end of file
stim/biomodels/flow_dep.h 0 → 100644
  1 +/*
  2 +Copyright <2017> <David Mayerich>
  3 +
  4 +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  5 +
  6 +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  7 +
  8 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  9 +*/
  10 +
  11 +#pragma once
  12 +#include <fstream> // Required for ofstream, etc.
  13 +#include <iomanip> // Required for setw
  14 +#include <iostream> // Required for cout, cin, etc.
  15 +#include <tuple> // Required for returning multiple values from a function
  16 +
  17 +using namespace std;
  18 +
  19 +
  20 +class flow
  21 +{
  22 +public:
  23 + void backupToTxt(unsigned int nL, double **D, char filename[]);
  24 + tuple<int, int> copySrcDesRadLen(char filename[]);
  25 + void copyToArray(int *src, int *dest, double *radii, double *len);
  26 + int getDangleNodes(int datarow, int numNodes, int *row, int *column, int *dangleNodes);
  27 + void inversion(double **a, int n, double **b);
  28 +
  29 +protected:
  30 + float determinant(double **a, int n);
  31 + int minor(double **src, double **dest, int row, int col, int order);
  32 +};
  33 +
  34 +/* Function to find the dangle nodes in a network */
  35 +// Created by Cherub P. Harder (8/10/2015), U of Houston
  36 +// Modified by Cherub P. Harder on 8/12/2015
  37 +int flow::getDangleNodes(int datarow, int numNodes, int *column1, int *column2, int *dangleNodes)
  38 +{
  39 + int count = datarow, diff1 = 0, diff2 = 0, numPress = 0, st = 0;
  40 +
  41 + // Find matching nodes within column2
  42 + for( int i = 0; i < count; i++ )
  43 + {
  44 + for( int y = i+1; y < datarow; y++ )
  45 + {
  46 + if( column2[i] == column2[y] ) // Is there a match?
  47 + {
  48 + st = column2[i]; // Save the matching node
  49 +// cout << endl << column2[i] << " = " << column2[y] << endl; // Display the matching nodes
  50 + memmove(column2+i, column2+i+1, (datarow-(i+1)) * sizeof(column2[0])); // Move up the rows
  51 + // taking the places of the rows before them starting
  52 + // with where the matching node is located
  53 + column2[datarow-1] = st; // Place the matching node at the very end of the array--
  54 + // this is for comparison purpose so that the other match-
  55 + // ing node will be moved as well and then omitted later.
  56 + diff1++; // Count the matching node
  57 +
  58 + // Display the updated array (with the matching node moved to the bottommost row)
  59 +/* cout << "Updated array:" << endl;
  60 + for( int k = 0; k < datarow; k++ )
  61 + cout << column2[k] << endl;
  62 +*/
  63 + // Decrement the counters
  64 + // NOTE: The counters need to be decremented because the rows had been moved up, so the same
  65 + // locations need to be read again because they contain different values now after the move.
  66 + i--; // Decrement i to read the node that took over the place
  67 + // of the matching node. Otherwise, it will be skipped.
  68 + y--; // Decrement y to read the following node for comparison
  69 + count--; // The maximum count need to be decremented so that the
  70 + // matching nodes that had been moved will not be read again.
  71 + // However, the maximum count (datarow) for finding a match
  72 + // will not be decremented because the remaining matching
  73 + // node that has not been moved yet needs to be moved and
  74 + // the only way to do that is to match it with its duplicate.
  75 + }
  76 + }
  77 + }
  78 +
  79 + // Store the nodes that have no duplicates
  80 + // NOTE: This will only save the nodes that have not been moved to the bottom.
  81 +// cout << "\ndangleNodes array:" << endl;
  82 + for( int j = 0; j < datarow-diff1; j++ )
  83 + {
  84 + dangleNodes[numPress] = column2[j];
  85 +// cout << dangleNodes[j] << endl; // DELETE!!!
  86 + numPress++; // Count the non-duplicated node
  87 + }
  88 +
  89 + // Find if the non-duplicated nodes have a match from column1
  90 + count = datarow-diff1; // Reinitialize the counter
  91 +
  92 + for( int i = 0; i < count; i++ )
  93 + {
  94 + for( int j = 0; j < datarow; j++ )
  95 + {
  96 + if( dangleNodes[i] == column1[j] ) // Is there a match?
  97 + {
  98 + st = column1[j]; // Save the matching node
  99 +// cout << endl << dangleNodes[i] << " = " << column1[j] << endl; // Display the matching nodes
  100 + memmove(dangleNodes+i, dangleNodes+i+1, (datarow-diff1-(i+1)) * sizeof(dangleNodes[0]));
  101 + dangleNodes[count-1] = st; // Move the matching node to the bottom of the array
  102 + diff2++; // Count the matching node
  103 +
  104 + // Display the updated array
  105 +/* cout << "Updated dangleNodes array:" << endl;
  106 + for( int k = 0; k < count-1; k++ )
  107 + {
  108 + cout << dangleNodes[k] << endl;
  109 + }
  110 +*/
  111 + // Decrement the counters
  112 + i--;
  113 + j--;
  114 + count--;
  115 + numPress--; // Decrement to the exact number of dangle nodes
  116 + }
  117 + }
  118 + }
  119 +
  120 + return numPress; // Number of dangle nodes
  121 +}
  122 +
  123 +
  124 +// Function to make a backup copy of the contents of a matrix to a .txt file
  125 +// Created by Cherub P. Harder (8/10/2015), U of Houston
  126 +void flow::backupToTxt(unsigned int nL, double **D, char filename[])
  127 +{
  128 + ofstream output_file(filename);
  129 +
  130 + for( unsigned int i = 0; i < nL; i++ )
  131 + {
  132 + for( int j = 0; j < 4; j++ )
  133 + {
  134 + if( j < 3 )
  135 + output_file << D[i][j] << "\t";
  136 +
  137 + else
  138 + output_file << D[i][j];
  139 + }
  140 +
  141 + output_file << "\n";
  142 + }
  143 +
  144 + output_file.close( );
  145 +}
  146 +
  147 +
  148 +// Function to make separate copies of the source nodes, destination nodes, radii, and lengths
  149 +// Created by Cherub P. Harder (8/10/2015), U of Houston
  150 +tuple<int, int> flow::copySrcDesRadLen(char filename[])
  151 +{
  152 + int cnt = 0, numElements = 0, numNodes = 0;
  153 + float number = 0.0;
  154 + ofstream srcData("srcCol.txt"); // A .txt file to store the source nodes
  155 + ofstream desData("destCol.txt"); // A .txt file to store the destination nodes
  156 + ofstream radiiData("radii.txt"); // A .txt file to store the radii
  157 + ofstream lenData("lengths.txt"); // A .txt file to store the lengths
  158 + FILE *fp = fopen(filename, "r"); // Create a variable of type FILE* and open the file using
  159 + // the fopen function and assign the file to the variable
  160 + // Check if the file exists
  161 + if(fp == NULL) // Alternative: if(!fp)
  162 + {
  163 + printf("Error! File does not exist.\n");
  164 + getchar( ); // Pause
  165 + exit(-1); // NOTE: Must include stdlib.h.
  166 + }
  167 +
  168 + // Store data to their respective .txt files
  169 + while(fscanf(fp, "%f", &number) == 1)
  170 + {
  171 + cnt++; // Increment counter
  172 +
  173 + // Store to srcCol.txt
  174 + if(cnt == 1)
  175 + srcData << number << endl;
  176 +
  177 + // Store to destCol.txt
  178 + if(cnt == 2)
  179 + desData << number << endl;
  180 +
  181 + // Save the current number of nodes
  182 + if(cnt < 3)
  183 + {
  184 + if(number > numNodes)
  185 + numNodes = (int)number;
  186 + }
  187 +
  188 + // Store to radii.txt
  189 + if(cnt == 3)
  190 + radiiData << number << endl;
  191 +
  192 + // Store to lengths.txt
  193 + if(cnt == 4)
  194 + {
  195 + lenData << number << endl;
  196 +
  197 + numElements++; // Count the elements
  198 + cnt = 0; // Reset counter
  199 + }
  200 + }
  201 +
  202 + srcData.close( );
  203 + desData.close( );
  204 + radiiData.close( );
  205 + lenData.close( );
  206 +
  207 + return make_tuple(numNodes, numElements); // Return two values
  208 +}
  209 +
  210 +
  211 +// Function to copy data for .txt files to their respective arrays
  212 +// Created by Cherub P. Harder (8/11/2015), U of Houston
  213 +void flow::copyToArray(int *src, int *dest, double *radii, double *len)
  214 +{
  215 + int v = 0;
  216 + double tmp = 0, R = 0, L = 0;
  217 +
  218 + // Store source node values to the array src
  219 + ifstream readSrc("srcCol.txt");
  220 +
  221 + while( readSrc >> tmp )
  222 + {
  223 + src[v] = (int)tmp;
  224 + v++;
  225 + }
  226 +
  227 + readSrc.close( );
  228 +
  229 + // Store destination node values to the array dest
  230 + v = 0; // Reset counter
  231 + ifstream readDest("destCol.txt");
  232 +
  233 + while( readDest >> tmp )
  234 + {
  235 + dest[v] = (int)tmp;
  236 + v++;
  237 + }
  238 +
  239 + readDest.close( );
  240 +
  241 + // Store radius values to the array radii
  242 + v = 0; // Reset counter
  243 + ifstream readRad("radii.txt");
  244 +
  245 + while( readRad >> tmp )
  246 + {
  247 + radii[v] = tmp;
  248 + v++;
  249 + }
  250 +
  251 + readRad.close( );
  252 +
  253 + // Store length values to the array len
  254 + v = 0; // Reset counter
  255 + ifstream readLen("lengths.txt");
  256 +
  257 + while( readLen >> tmp )
  258 + {
  259 + len[v] = tmp;
  260 + v++;
  261 + }
  262 +
  263 + readLen.close( );
  264 +}
  265 +
  266 +
  267 +// Function to find the inverse of a square matrix
  268 +void flow::inversion(double **a, int n, double **b)
  269 +{
  270 + // Get 1 over the determinant of A
  271 + double det = (double)(1.0/determinant(a, n));
  272 +// cerr << "\n1/det(C) = " << det << endl; // DELETE!!!
  273 +
  274 + // Memory allocation
  275 + double *tmp = new double[(n-1) * (n-1)];
  276 + double **m = new double * [n-1];
  277 + for( int i = 0; i < n-1; i++ )
  278 + m[i] = tmp + ( i * (n-1) );
  279 +
  280 + for( int j = 0; j < n; j++)
  281 + {
  282 + for( int i = 0; i < n; i++ )
  283 + {
  284 + // Get the cofactor (matrix) of a(j,i)
  285 + minor(a, m, j, i, n);
  286 + b[i][j] = det * determinant( m, n-1 );
  287 + if( (i+j)%2 == 1 )
  288 + b[i][j] = -b[i][j];
  289 + }
  290 + }
  291 +
  292 + // Release memory
  293 + // Delete [] minor[0];
  294 + delete [] tmp;
  295 + delete [] m;
  296 +}
  297 +
  298 +
  299 +// Function to find the determinant of a matrix using recursion
  300 +// Contribution by Edward Popko
  301 +// Modified by Cherub P. Harder (7/15/2015), U of Houston
  302 +// Arguments: a(double **) - pointer to a pointer of an arbitrary square matrix
  303 +// n(int) - dimension of the square matrix
  304 +float flow::determinant(double **a, int n)
  305 +{
  306 + int i, j, j1, j2; // General loop and matrix subscripts
  307 + double det = 0; // Initialize determinant
  308 + double **m = NULL; // Pointer to pointer to implement 2D square array
  309 +
  310 + // Display contents of matrix C (DELETE!!!)
  311 +/* std::cout << "\nThe updated matrix C:\n";
  312 + for( int j = 0; j < n; ++j )
  313 + {
  314 + std::cerr << "\t";
  315 +
  316 + for( int k = 0; k < n; ++k )
  317 + std::cerr << left << setw(15) << a[j][k];
  318 +
  319 + std::cerr << endl;
  320 + }
  321 +
  322 + getchar(); // DELETE!!!*/
  323 +
  324 + if(n < 1) { } // Error condition - should never get here
  325 +
  326 + else if (n == 1) // Should never get here
  327 + {
  328 + det = a[0][0];
  329 + }
  330 +
  331 + else if(n == 2) // Basic 2x2 sub-matrix determinate definition
  332 + { // When n == 2, this ends the recursion series
  333 + det = a[0][0] * a[1][1] - a[1][0] * a[0][1];
  334 + }
  335 + // Recursion continues, solve next sub-matrix
  336 + else // Solve the next minor by building a sub-matrix
  337 + {
  338 + det = 0; // Initialize determinant of sub-matrix
  339 +
  340 + for (j1 = 0; j1 < n; j1++) // For each column in sub-matrix get space for the
  341 + { // pointer list
  342 + m = (double **) malloc((n-1) * sizeof(double *));
  343 +
  344 + for (i = 0; i < n-1; i++)
  345 + m[i] = (double *) malloc((n-1)* sizeof(double));
  346 + // i[0][1][2][3] first malloc
  347 + // m -> + + + + space for 4 pointers
  348 + // | | | | j second malloc
  349 + // | | | +-> _ _ _ [0] pointers to
  350 + // | | +----> _ _ _ [1] and memory for
  351 + // | +-------> _ a _ [2] 4 doubles
  352 + // +----------> _ _ _ [3]
  353 + //
  354 + // a[1][2]
  355 + // Build sub-matrix with minor elements excluded
  356 +
  357 + for (i = 1; i < n; i++)
  358 + {
  359 + j2 = 0 ; // Start at first sum-matrix column position
  360 + // Loop to copy source matrix less one column
  361 + for (j = 0; j < n; j++)
  362 + {
  363 + if (j == j1) continue; // Do not copy the minor column element
  364 +
  365 + m[i-1][j2] = a[i][j]; // Copy source element into new sub-matrix
  366 + // i-1 because new sub-matrix is one row
  367 + // (and column) smaller with excluded minors
  368 + j2++; // Move to next sub-matrix column position
  369 + }
  370 + }
  371 +
  372 + det += (double)pow(-1.0, 1.0 + j1 + 1.0) * a[0][j1] * determinant(m, n-1);
  373 + // Sum x raised to y power
  374 + // recursively get determinant of next
  375 + // sub-matrix which is now one
  376 + // row & column smaller
  377 +
  378 + for (i = 0; i < n-1; i++) free(m[i]); // Free the storage allocated to
  379 + // this minor's set of pointers
  380 + free(m); // Free the storage for the original
  381 + // pointer to pointer
  382 + }
  383 + }
  384 +
  385 + return(det);
  386 +}
  387 +
  388 +
  389 +// Function to calculate the cofactor of element (row, col)
  390 +int flow::minor(double **src, double **dest, int row, int col, int order)
  391 +{
  392 + // Indicate which col and row is being copied to dest
  393 + int colCount=0,rowCount=0;
  394 +
  395 + for(int i = 0; i < order; i++)
  396 + {
  397 + if(i != row)
  398 + {
  399 + colCount = 0;
  400 + for(int j = 0; j < order; j++)
  401 + {
  402 + // When j is not the element
  403 + if( j != col )
  404 + {
  405 + dest[rowCount][colCount] = src[i][j];
  406 + colCount++;
  407 + }
  408 + }
  409 +
  410 + rowCount++;
  411 + }
  412 + }
  413 +
  414 + return 1;
  415 +}
0 \ No newline at end of file 416 \ No newline at end of file
stim/biomodels/network.h
  1 +/*
  2 +Copyright <2017> <David Mayerich>
  3 +
  4 +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  5 +
  6 +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  7 +
  8 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  9 +*/
1 #ifndef STIM_NETWORK_H 10 #ifndef STIM_NETWORK_H
2 #define STIM_NETWORK_H 11 #define STIM_NETWORK_H
3 12
@@ -10,19 +19,51 @@ @@ -10,19 +19,51 @@
10 #include <math.h> 19 #include <math.h>
11 #include <stim/math/vec3.h> 20 #include <stim/math/vec3.h>
12 #include <stim/visualization/obj.h> 21 #include <stim/visualization/obj.h>
  22 +#include <stim/visualization/swc.h>
13 #include <stim/visualization/cylinder.h> 23 #include <stim/visualization/cylinder.h>
14 -#include <ANN/ANN.h>  
15 -#include <boost/tuple/tuple.hpp> 24 +#include <stim/cuda/cudatools/timer.h>
  25 +#include <stim/cuda/cudatools/callable.h>
  26 +#include <stim/structures/kdtree.cuh>
  27 +//********************help function********************
  28 +// gaussian_function
  29 +CUDA_CALLABLE float gaussianFunction(float x, float std = 25) { return exp(-x / (2 * std*std)); } // std default sigma value is 25
  30 +
  31 +// compute metric in parallel
  32 +#ifdef __CUDACC__
  33 +template <typename T>
  34 +__global__ void find_metric_parallel(T* M, size_t n, T* D, float sigma){
  35 + size_t x = blockDim.x * blockIdx.x + threadIdx.x;
  36 + if(x >= n) return;
  37 + M[x] = 1.0f - gaussianFunction(D[x], sigma);
  38 +}
  39 +
  40 +//find the corresponding edge index from array index
  41 +__global__ void find_edge_index_parallel(size_t* I, size_t n, unsigned* R, size_t* E, size_t ne){
  42 + size_t x = blockDim.x * blockIdx.x + threadIdx.x;
  43 + if(x >= n) return;
  44 + unsigned i = 0;
  45 + size_t N = 0;
  46 + for(unsigned e = 0; e < ne; e++){
  47 + N += E[e];
  48 + if(I[x] < N){
  49 + R[x] = i;
  50 + break;
  51 + }
  52 + i++;
  53 + }
  54 +}
  55 +#endif
16 56
  57 +//hard-coded factor
  58 +int threshold_fac;
17 59
18 namespace stim{ 60 namespace stim{
19 /** This is the a class that interfaces with gl_spider in order to store the currently 61 /** This is the a class that interfaces with gl_spider in order to store the currently
20 * segmented network. The following data is stored and can be extracted: 62 * segmented network. The following data is stored and can be extracted:
21 * 1)Network geometry and centerline. 63 * 1)Network geometry and centerline.
22 - * 2)Network connectivity (a graph of nodes and edges), reconstructed using ANN library. 64 + * 2)Network connectivity (a graph of nodes and edges), reconstructed using kdtree.
23 */ 65 */
24 66
25 -  
26 template<typename T> 67 template<typename T>
27 class network{ 68 class network{
28 69
@@ -31,18 +72,30 @@ class network{ @@ -31,18 +72,30 @@ class network{
31 class edge : public cylinder<T> 72 class edge : public cylinder<T>
32 { 73 {
33 public: 74 public:
34 - unsigned v[2]; //unique id's designating the starting and ending 75 +
  76 + unsigned int v[2]; //unique id's designating the starting and ending
35 // default constructor 77 // default constructor
36 - edge() : cylinder<T>()  
37 - {  
38 - v[1] = -1; v[0] = -1; 78 + edge() : cylinder<T>() {
  79 + v[1] = (unsigned)(-1); v[0] = (unsigned)(-1);
39 } 80 }
40 /// Constructor - creates an edge from a list of points by calling the stim::fiber constructor 81 /// Constructor - creates an edge from a list of points by calling the stim::fiber constructor
  82 +/*
  83 + ///@param v0, the starting index.
  84 + ///@param v1, the ending index.
  85 + ///@param sz, the number of point in the fiber.
  86 + edge(unsigned int v0, unsigned int v1, unsigned int sz) : cylinder<T>(
  87 + {
41 88
  89 + }
  90 +*/
  91 + edge(std::vector<stim::vec3<T> > p, std::vector<T> s)
  92 + : cylinder<T>(p,s)
  93 + {
  94 + }
42 ///@param p is an array of positions in space 95 ///@param p is an array of positions in space
43 - edge(std::vector< stim::vec3<T> > p) : cylinder<T>(p){} 96 + edge(stim::centerline<T> p) : cylinder<T>(p){}
44 97
45 - /// Copy constructor creates an edge from a fiber 98 + /// Copy constructor creates an edge from a cylinder
46 edge(stim::cylinder<T> f) : cylinder<T>(f) {} 99 edge(stim::cylinder<T> f) : cylinder<T>(f) {}
47 100
48 /// Resamples an edge by calling the fiber resampling function 101 /// Resamples an edge by calling the fiber resampling function
@@ -57,10 +110,71 @@ class network{ @@ -57,10 +110,71 @@ class network{
57 /// Output the edge information as a string 110 /// Output the edge information as a string
58 std::string str(){ 111 std::string str(){
59 std::stringstream ss; 112 std::stringstream ss;
60 - ss<<"("<<cylinder<T>::size()<<")\tl = "<<this.length()<<"\t"<<v[0]<<"----"<<v[1]; 113 + ss<<"("<<cylinder<T>::size()<<")\tl = "<<this->length()<<"\t"<<v[0]<<"----"<<v[1];
61 return ss.str(); 114 return ss.str();
62 } 115 }
63 116
  117 + std::vector<edge> split(unsigned int idx){
  118 +
  119 + std::vector< stim::cylinder<T> > C;
  120 + C.resize(2);
  121 + C = (*this).cylinder<T>::split(idx);
  122 + std::vector<edge> E(C.size());
  123 +
  124 + for(unsigned e = 0; e < E.size(); e++){
  125 + E[e] = C[e];
  126 + }
  127 + return E;
  128 + }
  129 +
  130 + /// operator for writing the edge information into a binary .nwt file.
  131 + friend std::ofstream& operator<<(std::ofstream& out, const edge& e)
  132 + {
  133 + out.write(reinterpret_cast<const char*>(&e.v[0]), sizeof(unsigned int)); ///write the starting point.
  134 + out.write(reinterpret_cast<const char*>(&e.v[1]), sizeof(unsigned int)); ///write the ending point.
  135 + unsigned int sz = e.size(); ///write the number of point in the edge.
  136 + out.write(reinterpret_cast<const char*>(&sz), sizeof(unsigned int));
  137 + for(int i = 0; i < sz; i++) ///write each point
  138 + {
  139 + stim::vec3<T> point = e[i];
  140 + out.write(reinterpret_cast<const char*>(&point[0]), 3*sizeof(T));
  141 + // for(int j = 0; j < nmags(); j++) //future code for multiple mags
  142 + // {
  143 + out.write(reinterpret_cast<const char*>(&e.R[i]), sizeof(T)); ///write the radius
  144 + //std::cout << point.str() << " " << e.R[i] << std::endl;
  145 + // }
  146 + }
  147 + return out; //return stream
  148 + }
  149 +
  150 + /// operator for reading an edge from a binary .nwt file.
  151 + friend std::ifstream& operator>>(std::ifstream& in, edge& e)
  152 + {
  153 + unsigned int v0, v1, sz;
  154 + in.read(reinterpret_cast<char*>(&v0), sizeof(unsigned int)); //read the staring point.
  155 + in.read(reinterpret_cast<char*>(&v1), sizeof(unsigned int)); //read the ending point
  156 + in.read(reinterpret_cast<char*>(&sz), sizeof(unsigned int)); //read the number of points in the edge
  157 +// stim::centerline<T> temp = stim::centerline<T>(sz); //allocate the new edge
  158 +// e = edge(temp);
  159 + std::vector<stim::vec3<T> > p(sz);
  160 + std::vector<T> r(sz);
  161 + for(int i = 0; i < sz; i++) //set the points and radii to the newly read values
  162 + {
  163 + stim::vec3<T> point;
  164 + in.read(reinterpret_cast<char*>(&point[0]), 3*sizeof(T));
  165 + p[i] = point;
  166 + T mag;
  167 + // for(int j = 0; j < nmags(); j++) ///future code for mags
  168 + // {
  169 + in.read(reinterpret_cast<char*>(&mag), sizeof(T));
  170 + r[i] = mag;
  171 + //std::cout << point.str() << " " << mag << std::endl;
  172 + // }
  173 + }
  174 + e = edge(p,r);
  175 + e.v[0] = v0; e.v[1] = v1;
  176 + return in;
  177 + }
64 }; 178 };
65 179
66 ///Node class that stores the physical position of the node as well as the edges it is connected to (edges that connect to it), As well as any additional data necessary. 180 ///Node class that stores the physical position of the node as well as the edges it is connected to (edges that connect to it), As well as any additional data necessary.
@@ -70,12 +184,16 @@ class network{ @@ -70,12 +184,16 @@ class network{
70 //std::vector<unsigned int> edges; //indices of edges connected to this node. 184 //std::vector<unsigned int> edges; //indices of edges connected to this node.
71 std::vector<unsigned int> e[2]; //indices of edges going out (e[0]) and coming in (e[1]) 185 std::vector<unsigned int> e[2]; //indices of edges going out (e[0]) and coming in (e[1])
72 //stim::vec3<T> p; //position of this node in physical space. 186 //stim::vec3<T> p; //position of this node in physical space.
73 - 187 + //default constructor
  188 + vertex() : stim::vec3<T>()
  189 + {
  190 + }
74 //constructor takes a stim::vec 191 //constructor takes a stim::vec
75 vertex(stim::vec3<T> p) : stim::vec3<T>(p){} 192 vertex(stim::vec3<T> p) : stim::vec3<T>(p){}
76 193
77 /// Output the vertex information as a string 194 /// Output the vertex information as a string
78 - std::string str(){ 195 + std::string
  196 + str(){
79 std::stringstream ss; 197 std::stringstream ss;
80 ss<<"\t(x, y, z) = "<<stim::vec3<T>::str(); 198 ss<<"\t(x, y, z) = "<<stim::vec3<T>::str();
81 199
@@ -92,14 +210,46 @@ class network{ @@ -92,14 +210,46 @@ class network{
92 210
93 return ss.str(); 211 return ss.str();
94 } 212 }
  213 + ///operator for writing the vector into the stream;
  214 + friend std::ofstream& operator<<(std::ofstream& out, const vertex& v)
  215 + {
  216 + unsigned int s0, s1;
  217 + s0 = v.e[0].size();
  218 + s1 = v.e[1].size();
  219 + out.write(reinterpret_cast<const char*>(&v.ptr[0]), 3*sizeof(T)); ///write physical vertex location
  220 + out.write(reinterpret_cast<const char*>(&s0), sizeof(unsigned int)); ///write the number of "outgoing edges"
  221 + out.write(reinterpret_cast<const char*>(&s1), sizeof(unsigned int)); ///write the number of "incoming edges"
  222 + if (s0 != 0)
  223 + out.write(reinterpret_cast<const char*>(&v.e[0][0]), sizeof(unsigned int)*v.e[0].size()); ///write the "outgoing edges"
  224 + if (s1 != 0)
  225 + out.write(reinterpret_cast<const char*>(&v.e[1][0]), sizeof(unsigned int)*v.e[1].size()); ///write the "incoming edges"
  226 + return out;
  227 + }
95 228
  229 + ///operator for reading the vector out of the stream;
  230 + friend std::ifstream& operator>>(std::ifstream& in, vertex& v)
  231 + {
  232 + in.read(reinterpret_cast<char*>(&v[0]), 3*sizeof(T)); ///read the physical position
  233 + unsigned int s[2];
  234 + in.read(reinterpret_cast<char*>(&s[0]), 2*sizeof(unsigned int)); ///read the sizes of incoming and outgoing edge arrays
  235 +
  236 + std::vector<unsigned int> one(s[0]);
  237 + std::vector<unsigned int> two(s[1]);
  238 + v.e[0] = one;
  239 + v.e[1] = two;
  240 + if (one.size() != 0)
  241 + in.read(reinterpret_cast<char*>(&v.e[0][0]), s[0] * sizeof(unsigned int)); ///read the arrays of "outgoing edges"
  242 + if (two.size() != 0)
  243 + in.read(reinterpret_cast<char*>(&v.e[1][0]), s[1] * sizeof(unsigned int)); ///read the arrays of "incoming edges"
  244 + return in;
  245 + }
96 246
97 }; 247 };
98 248
99 protected: 249 protected:
100 250
101 std::vector<edge> E; //list of edges 251 std::vector<edge> E; //list of edges
102 - std::vector<vertex> V; //list of vertices. 252 + std::vector<vertex> V; //list of vertices.
103 253
104 public: 254 public:
105 255
@@ -125,7 +275,66 @@ public: @@ -125,7 +275,66 @@ public:
125 return V.size(); 275 return V.size();
126 } 276 }
127 277
128 - std::vector<vertex> operator*(T s){ 278 + ///Returns the radius at specific point in the edge
  279 + T get_r(unsigned e, unsigned i) {
  280 + return E[e].r(i);
  281 + }
  282 +
  283 + ///Returns the average radius of specific edge
  284 + T get_average_r(unsigned e) {
  285 + T result = 0.0;
  286 + unsigned n = E[e].size();
  287 + for (unsigned p = 0; p < n; p++)
  288 + result += E[e].r(p);
  289 +
  290 + return (T)result / n;
  291 + }
  292 +
  293 + ///Returns the length of current edge
  294 + T get_l(unsigned e) {
  295 + return E[e].length();
  296 + }
  297 +
  298 + ///Returns the start vertex of current edge
  299 + size_t get_start_vertex(unsigned e) {
  300 + return E[e].v[0];
  301 + }
  302 +
  303 + ///Returns the end vertex of current edge
  304 + size_t get_end_vertex(unsigned e) {
  305 + return E[e].v[1];
  306 + }
  307 +
  308 + ///Returns one vertex
  309 + stim::vec3<T> get_vertex(unsigned i) {
  310 + return V[i];
  311 + }
  312 +
  313 + ///Returns the boundary vertices' indices
  314 + std::vector<unsigned> get_boundary_vertex() {
  315 + std::vector<unsigned> result;
  316 +
  317 + for (unsigned v = 0; v < V.size(); v++) {
  318 + if (V[v].e[0].size() + V[v].e[1].size() == 1) { // boundary vertex
  319 + result.push_back(v);
  320 + }
  321 + }
  322 +
  323 + return result;
  324 + }
  325 +
  326 + ///Set radius
  327 + void set_r(unsigned e, std::vector<T> radius) {
  328 + E[e].cylinder<T>::copy_r(radius);
  329 + }
  330 +
  331 + void set_r(unsigned e, T radius) {
  332 + for (size_t i = 0; i < E[e].size(); i++)
  333 + E[e].cylinder<T>::set_r(i, radius);
  334 + }
  335 + //scale the network by some constant value
  336 + // I don't think these work??????
  337 + /*std::vector<vertex> operator*(T s){
129 for (unsigned i=0; i< vertices; i ++ ){ 338 for (unsigned i=0; i< vertices; i ++ ){
130 V[i] = V[i] * s; 339 V[i] = V[i] * s;
131 } 340 }
@@ -139,10 +348,9 @@ public: @@ -139,10 +348,9 @@ public:
139 } 348 }
140 } 349 }
141 return V; 350 return V;
142 - } 351 + }*/
143 352
144 // Returns an average of branching index in the network 353 // Returns an average of branching index in the network
145 -  
146 double BranchingIndex(){ 354 double BranchingIndex(){
147 double B=0; 355 double B=0;
148 for(unsigned v=0; v < V.size(); v ++){ 356 for(unsigned v=0; v < V.size(); v ++){
@@ -154,7 +362,6 @@ public: @@ -154,7 +362,6 @@ public:
154 } 362 }
155 363
156 // Returns number of branch points in thenetwork 364 // Returns number of branch points in thenetwork
157 -  
158 unsigned int BranchP(){ 365 unsigned int BranchP(){
159 unsigned int B=0; 366 unsigned int B=0;
160 unsigned int c; 367 unsigned int c;
@@ -168,7 +375,6 @@ public: @@ -168,7 +375,6 @@ public:
168 } 375 }
169 376
170 // Returns number of end points (tips) in thenetwork 377 // Returns number of end points (tips) in thenetwork
171 -  
172 unsigned int EndP(){ 378 unsigned int EndP(){
173 unsigned int B=0; 379 unsigned int B=0;
174 unsigned int c; 380 unsigned int c;
@@ -202,7 +408,7 @@ public: @@ -202,7 +408,7 @@ public:
202 // return s; 408 // return s;
203 //} 409 //}
204 410
205 - 411 + //Calculate Metrics---------------------------------------------------
206 // Returns an average of fiber/edge lengths in the network 412 // Returns an average of fiber/edge lengths in the network
207 double Lengths(){ 413 double Lengths(){
208 stim::vec<T> L; 414 stim::vec<T> L;
@@ -270,8 +476,10 @@ public: @@ -270,8 +476,10 @@ public:
270 double avg = sumFractDim / E.size(); 476 double avg = sumFractDim / E.size();
271 return avg; 477 return avg;
272 } 478 }
273 - stim::cylinder<T> get_cylinder(unsigned f){  
274 - return E[f]; //return the specified edge (casting it to a fiber) 479 +
  480 + //returns a cylinder represented a given fiber (based on edge index)
  481 + stim::cylinder<T> get_cylinder(unsigned e){
  482 + return E[e]; //return the specified edge (casting it to a fiber)
275 } 483 }
276 484
277 //load a network from an OBJ file 485 //load a network from an OBJ file
@@ -290,9 +498,10 @@ public: @@ -290,9 +498,10 @@ public:
290 std::vector< stim::vec<T> > c; //allocate an array of points for the vessel centerline 498 std::vector< stim::vec<T> > c; //allocate an array of points for the vessel centerline
291 O.getLine(l, c); //get the fiber centerline 499 O.getLine(l, c); //get the fiber centerline
292 500
293 - std::vector< stim::vec3<T> > c3(c.size()); 501 + stim::centerline<T> c3(c.size());
294 for(size_t j = 0; j < c.size(); j++) 502 for(size_t j = 0; j < c.size(); j++)
295 c3[j] = c[j]; 503 c3[j] = c[j];
  504 + c3.update();
296 505
297 // edge new_edge = c3; ///This is dangerous. 506 // edge new_edge = c3; ///This is dangerous.
298 edge new_edge(c3); 507 edge new_edge(c3);
@@ -313,10 +522,24 @@ public: @@ -313,10 +522,24 @@ public:
313 it = find(id2vert.begin(), id2vert.end(), i[0]); //look for the first node 522 it = find(id2vert.begin(), id2vert.end(), i[0]); //look for the first node
314 if(it == id2vert.end()){ //if i[0] hasn't already been used 523 if(it == id2vert.end()){ //if i[0] hasn't already been used
315 vertex new_vertex = new_edge[0]; //create a new vertex, assign it a position 524 vertex new_vertex = new_edge[0]; //create a new vertex, assign it a position
316 - new_vertex.e[0].push_back(E.size()); //add the current edge as outgoing  
317 - new_edge.v[0] = V.size(); //add the new edge to the edge  
318 - V.push_back(new_vertex); //add the new vertex to the vertex list  
319 - id2vert.push_back(i[0]); //add the ID to the ID->vertex conversion list 525 + bool flag = false;
  526 + unsigned j = 0;
  527 + for (; j < V.size(); j++) { // check whether current vertex is already exist
  528 + if (new_vertex == V[j]) {
  529 + flag = true;
  530 + break;
  531 + }
  532 + }
  533 + if (!flag) { // unique one
  534 + new_vertex.e[0].push_back(E.size()); //add the current edge as outgoing
  535 + new_edge.v[0] = V.size(); //add the new edge to the edge
  536 + V.push_back(new_vertex); //add the new vertex to the vertex list
  537 + id2vert.push_back(i[0]); //add the ID to the ID->vertex conversion list
  538 + }
  539 + else {
  540 + V[j].e[0].push_back(E.size());
  541 + new_edge.v[0] = j;
  542 + }
320 } 543 }
321 else{ //if the vertex already exists 544 else{ //if the vertex already exists
322 it_idx = std::distance(id2vert.begin(), it); 545 it_idx = std::distance(id2vert.begin(), it);
@@ -327,10 +550,24 @@ public: @@ -327,10 +550,24 @@ public:
327 it = find(id2vert.begin(), id2vert.end(), i[1]); //look for the second ID 550 it = find(id2vert.begin(), id2vert.end(), i[1]); //look for the second ID
328 if(it == id2vert.end()){ //if i[1] hasn't already been used 551 if(it == id2vert.end()){ //if i[1] hasn't already been used
329 vertex new_vertex = new_edge[I-1]; //create a new vertex, assign it a position 552 vertex new_vertex = new_edge[I-1]; //create a new vertex, assign it a position
330 - new_vertex.e[1].push_back(E.size()); //add the current edge as incoming  
331 - new_edge.v[1] = V.size(); //add the new vertex to the edge  
332 - V.push_back(new_vertex); //add the new vertex to the vertex list  
333 - id2vert.push_back(i[1]); //add the ID to the ID->vertex conversion list 553 + bool flag = false;
  554 + unsigned j = 0;
  555 + for (; j < V.size(); j++) { // check whether current vertex is already exist
  556 + if (new_vertex == V[j]) {
  557 + flag = true;
  558 + break;
  559 + }
  560 + }
  561 + if (!flag) {
  562 + new_vertex.e[1].push_back(E.size()); //add the current edge as incoming
  563 + new_edge.v[1] = V.size(); //add the new vertex to the edge
  564 + V.push_back(new_vertex); //add the new vertex to the vertex list
  565 + id2vert.push_back(i[1]); //add the ID to the ID->vertex conversion list
  566 + }
  567 + else {
  568 + V[j].e[1].push_back(E.size());
  569 + new_edge.v[1] = j;
  570 + }
334 } 571 }
335 else{ //if the vertex already exists 572 else{ //if the vertex already exists
336 it_idx = std::distance(id2vert.begin(), it); 573 it_idx = std::distance(id2vert.begin(), it);
@@ -341,6 +578,217 @@ public: @@ -341,6 +578,217 @@ public:
341 E.push_back(new_edge); //push the edge to the list 578 E.push_back(new_edge); //push the edge to the list
342 579
343 } 580 }
  581 +
  582 + // copy the radii information from OBJ
  583 + /*if (O.numVT()) {
  584 + unsigned k = 0;
  585 + for (unsigned i = 0; i < E.size(); i++) {
  586 + for (unsigned j = 0; j < E[i].size(); j++) {
  587 + E[i].cylinder<T>::set_r(j, O.getVT(k)[0] / 2);
  588 + k++;
  589 + }
  590 + }
  591 + }*/
  592 + // OBJ class assumes that in L the two values are equal
  593 + if (O.numVT()) {
  594 + std::vector< unsigned > id; //create an array to store the centerline point IDs
  595 + for (unsigned i = 0; i < O.numL(); i++) {
  596 + id.clear();
  597 + O.getLinei(i + 1, id); //get the list of point IDs for the line
  598 + for (unsigned j = 0; j < id.size(); j++)
  599 + E[i].cylinder<T>::set_r(j, O.getVT(id[j] - 1)[0] / 2);
  600 + }
  601 + }
  602 + }
  603 +
  604 + ///loads a .nwt file. Reads the header and loads the data into the network according to the header.
  605 + void
  606 + loadNwt(std::string filename)
  607 + {
  608 + int dims[2]; ///number of vertex, number of edges
  609 + readHeader(filename, &dims[0]); //read header
  610 + std::ifstream file;
  611 + file.open(filename.c_str(), std::ios::in | std::ios::binary); ///skip header information.
  612 + file.seekg(14+58+4+4, file.beg);
  613 + vertex v;
  614 + for(int i = 0; i < dims[0]; i++) ///for every vertex, read vertex, add to network.
  615 + {
  616 + file >> v;
  617 + V.push_back(v);
  618 +// std::cout << i << " " << v.str() << std::endl;
  619 + }
  620 +
  621 + std::cout << std::endl;
  622 + for(int i = 0; i < dims[1]; i++) ///for every edge, read edge, add to network.
  623 + {
  624 + edge e;
  625 + file >> e;
  626 + E.push_back(e);
  627 + //std::cout << i << " " << E[i].str() << std::endl; // not necessary?
  628 + }
  629 + file.close();
  630 + }
  631 +
  632 + ///saves a .nwt file. Writes the header in raw text format, then saves the network as a binary file.
  633 + void
  634 + saveNwt(std::string filename)
  635 + {
  636 + writeHeader(filename);
  637 + std::ofstream file;
  638 + file.open(filename.c_str(), std::ios::out | std::ios::binary | std::ios::app); ///since we have written the header we are not appending.
  639 + for(int i = 0; i < V.size(); i++) ///look through the Vertices and write each one.
  640 + {
  641 +// std::cout << i << " " << V[i].str() << std::endl;
  642 + file << V[i];
  643 + }
  644 + for(int i = 0; i < E.size(); i++) ///loop through the Edges and write each one.
  645 + {
  646 + //std::cout << i << " " << E[i].str() << std::endl; // not necesarry?
  647 + file << E[i];
  648 + }
  649 + file.close();
  650 + }
  651 +
  652 +
  653 + ///Writes the header information to a .nwt file.
  654 + void
  655 + writeHeader(std::string filename)
  656 + {
  657 + std::string magicString = "nwtFileFormat "; ///identifier for the file.
  658 + std::string desc = "fileid(14B), desc(58B), #vertices(4B), #edges(4B): bindata";
  659 + int hNumVertices = V.size(); ///int byte header storing the number of vertices in the file
  660 + int hNumEdges = E.size(); ///int byte header storing the number of edges.
  661 + std::ofstream file;
  662 + file.open(filename.c_str(), std::ios::out | std::ios::binary);
  663 + std::cout << hNumVertices << " " << hNumEdges << std::endl;
  664 + file.write(reinterpret_cast<const char*>(&magicString.c_str()[0]), 14); //write the file id
  665 + file.write(reinterpret_cast<const char*>(&desc.c_str()[0]), 58); //write the description
  666 + file.write(reinterpret_cast<const char*>(&hNumVertices), sizeof(int)); //write #vert.
  667 + file.write(reinterpret_cast<const char*>(&hNumEdges), sizeof(int)); //write #edges
  668 +// file << magicString.c_str() << desc.c_str() << hNumVertices << hNumEdges;
  669 + file.close();
  670 +
  671 + }
  672 +
  673 + ///Reads the header information from a .nwt file.
  674 + void
  675 + readHeader(std::string filename, int *dims)
  676 + {
  677 + char magicString[14]; ///id
  678 + char desc[58]; ///description
  679 + int hNumVertices; ///#vert
  680 + int hNumEdges; ///#edges
  681 + std::ifstream file; ////create stream
  682 + file.open(filename.c_str(), std::ios::in | std::ios::binary);
  683 + file.read(reinterpret_cast<char*>(&magicString[0]), 14); ///read the file id.
  684 + file.read(reinterpret_cast<char*>(&desc[0]), 58); ///read the description
  685 + file.read(reinterpret_cast<char*>(&hNumVertices), sizeof(int)); ///read the number of vertices
  686 + file.read(reinterpret_cast<char*>(&hNumEdges), sizeof(int)); ///read the number of edges
  687 +// std::cout << magicString << desc << hNumVertices << " " << hNumEdges << std::endl;
  688 + file.close(); ///close the file.
  689 + dims[0] = hNumVertices; ///fill the returned reference.
  690 + dims[1] = hNumEdges;
  691 + }
  692 +
  693 + //load a network from an SWC file
  694 + void load_swc(std::string filename) {
  695 + stim::swc<T> S; // create swc variable
  696 + S.load(filename); // load the node information
  697 + S.create_tree(); // link those node according to their linking relationships as a tree
  698 + S.resample();
  699 +
  700 + //NT.push_back(S.node[0].type); // set the neuronal_type value to the first vertex in the network
  701 + std::vector<unsigned> id2vert; // this list stores the SWC vertex ID associated with each network vertex
  702 + unsigned i[2]; // temporary, IDs associated with the first and last points
  703 +
  704 + for (unsigned int l = 0; l < S.numE(); l++) { // for every edge
  705 + //NT.push_back(S.node[l].type);
  706 +
  707 + std::vector< stim::vec3<T> > c;
  708 + S.get_points(l, c);
  709 +
  710 + stim::centerline<T> c3(c.size()); // new fiber
  711 +
  712 + for (unsigned j = 0; j < c.size(); j++)
  713 + c3[j] = c[j]; // copy the points
  714 +
  715 + c3.update(); // update the L information
  716 +
  717 + stim::cylinder<T> C3(c3); // create a new cylinder in order to copy the origin radius information
  718 + // upadate the R information
  719 + std::vector<T> radius;
  720 + S.get_radius(l, radius);
  721 +
  722 + C3.copy_r(radius);
  723 +
  724 + edge new_edge(C3); // new edge
  725 +
  726 + //create an edge from the given centerline
  727 + unsigned int I = new_edge.size(); //calculate the number of points on the centerline
  728 +
  729 + //get the first and last vertex IDs for the line
  730 + i[0] = S.E[l].front();
  731 + i[1] = S.E[l].back();
  732 +
  733 + std::vector<unsigned>::iterator it; //create an iterator for searching the id2vert array
  734 + unsigned it_idx; //create an integer for the id2vert entry index
  735 +
  736 + //find out if the nodes for this fiber have already been created
  737 + it = find(id2vert.begin(), id2vert.end(), i[0]); //look for the first node
  738 + if (it == id2vert.end()) { //if i[0] hasn't already been used
  739 + vertex new_vertex = new_edge[0]; //create a new vertex, assign it a position
  740 + new_vertex.e[0].push_back(E.size()); //add the current edge as outgoing
  741 + new_edge.v[0] = V.size(); //add the new edge to the edge
  742 + V.push_back(new_vertex); //add the new vertex to the vertex list
  743 + id2vert.push_back(i[0]); //add the ID to the ID->vertex conversion list
  744 + }
  745 + else { //if the vertex already exists
  746 + it_idx = std::distance(id2vert.begin(), it);
  747 + V[it_idx].e[0].push_back(E.size()); //add the current edge as outgoing
  748 + new_edge.v[0] = it_idx;
  749 + }
  750 +
  751 + it = find(id2vert.begin(), id2vert.end(), i[1]); //look for the second ID
  752 + if (it == id2vert.end()) { //if i[1] hasn't already been used
  753 + vertex new_vertex = new_edge[I - 1]; //create a new vertex, assign it a position
  754 + new_vertex.e[1].push_back(E.size()); //add the current edge as incoming
  755 + new_edge.v[1] = V.size(); //add the new vertex to the edge
  756 + V.push_back(new_vertex); //add the new vertex to the vertex list
  757 + id2vert.push_back(i[1]); //add the ID to the ID->vertex conversion list
  758 + }
  759 + else { //if the vertex already exists
  760 + it_idx = std::distance(id2vert.begin(), it);
  761 + V[it_idx].e[1].push_back(E.size()); //add the current edge as incoming
  762 + new_edge.v[1] = it_idx;
  763 + }
  764 +
  765 + E.push_back(new_edge); //push the edge to the list
  766 + }
  767 + }
  768 +
  769 + /// Get adjacency matrix of the network
  770 + std::vector< typename std::vector<int> > get_adj_mat() {
  771 +
  772 + unsigned n = V.size(); // get the number of vertices in the networks
  773 +
  774 + std::vector< typename std::vector<int> > result(n, std::vector<int>(n, 0)); // initialize every entry in the matrix to be 0
  775 + result.resize(n); // resize rows
  776 + for (unsigned i = 0; i < n; i++)
  777 + result[i].resize(n); // resize columns
  778 +
  779 + for (unsigned i = 0; i < n; i++) { // for every vertex
  780 + unsigned num_out = V[i].e[0].size(); // number of outgoing edges of current vertex
  781 + if (num_out != 0) {
  782 + for (unsigned j = 0; j < num_out; j++) {
  783 + int edge_idx = V[i].e[0][j]; // get the jth out-going edge index of current vertex
  784 + int vertex_idx = E[edge_idx].v[1]; // get the ending vertex of specific out-going edge
  785 + result[i][vertex_idx] = 1; // can simply set to 1 if it is simple-graph
  786 + result[vertex_idx][i] = 1; // symmetric
  787 + }
  788 + }
  789 + }
  790 +
  791 + return result;
344 } 792 }
345 793
346 /// Output the network as a string 794 /// Output the network as a string
@@ -365,7 +813,7 @@ public: @@ -365,7 +813,7 @@ public:
365 stim::network<T> resample(T spacing){ 813 stim::network<T> resample(T spacing){
366 stim::network<T> n; //create a new network that will be an exact copy, with resampled fibers 814 stim::network<T> n; //create a new network that will be an exact copy, with resampled fibers
367 n.V = V; //copy all vertices 815 n.V = V; //copy all vertices
368 - 816 + //n.NT = NT; //copy all the neuronal type information
369 n.E.resize(edges()); //allocate space for the edge list 817 n.E.resize(edges()); //allocate space for the edge list
370 818
371 //copy all fibers, resampling them in the process 819 //copy all fibers, resampling them in the process
@@ -376,8 +824,6 @@ public: @@ -376,8 +824,6 @@ public:
376 return n; //return the resampled network 824 return n; //return the resampled network
377 } 825 }
378 826
379 -  
380 -  
381 /// Calculate the total number of points on all edges. 827 /// Calculate the total number of points on all edges.
382 unsigned total_points(){ 828 unsigned total_points(){
383 unsigned n = 0; 829 unsigned n = 0;
@@ -386,16 +832,52 @@ public: @@ -386,16 +832,52 @@ public:
386 return n; 832 return n;
387 } 833 }
388 834
389 - // gaussian function  
390 - float gaussianFunction(float x, float std=25){ return exp(-x/(2*std*std));} // by default std = 25 835 + //Copy the point cloud representing the centerline for the network into an array
  836 + void centerline_cloud(T* dst) {
  837 + size_t p; //stores the current edge point
  838 + size_t P; //stores the number of points in an edge
  839 + size_t i = 0; //index into the output array of points
  840 + for (size_t e = 0; e < E.size(); e++) { //for each edge in the network
  841 + P = E[e].size(); //get the number of points in this edge
  842 + for (p = 0; p < P; p++) {
  843 + dst[i * 3 + 0] = E[e][p][0];
  844 + dst[i * 3 + 1] = E[e][p][1];
  845 + dst[i * 3 + 2] = E[e][p][2];
  846 + i++;
  847 + }
  848 + }
  849 + }
391 850
392 - // stim 3d vector to annpoint of 3 dimensions  
393 - void stim2ann(ANNpoint &a, stim::vec3<T> b){ 851 + // convert vec3 to array
  852 + void stim2array(float *a, stim::vec3<T> b){
394 a[0] = b[0]; 853 a[0] = b[0];
395 a[1] = b[1]; 854 a[1] = b[1];
396 a[2] = b[2]; 855 a[2] = b[2];
397 } 856 }
398 857
  858 + // convert vec3 to array in bunch
  859 + void edge2array(T* a, edge b){
  860 + size_t n = b.size();
  861 + for(size_t i = 0; i < n; i++){
  862 + a[i * 3 + 0] = b[i][0];
  863 + a[i * 3 + 1] = b[i][1];
  864 + a[i * 3 + 2] = b[i][2];
  865 + }
  866 + }
  867 +
  868 + // get list of metric
  869 + std::vector<T> metric() {
  870 + std::vector<T> result;
  871 + T m;
  872 + for (size_t e = 0; e < E.size(); e++) {
  873 + for (size_t p = 0; p < E[e].size(); p++) {
  874 + m = E[e].r(p);
  875 + result.push_back(m);
  876 + }
  877 + }
  878 + return result;
  879 + }
  880 +
399 /// Calculate the average magnitude across the entire network. 881 /// Calculate the average magnitude across the entire network.
400 /// @param m is the magnitude value to use. The default is 0 (usually radius). 882 /// @param m is the magnitude value to use. The default is 0 (usually radius).
401 T average(unsigned m = 0){ 883 T average(unsigned m = 0){
@@ -403,7 +885,7 @@ public: @@ -403,7 +885,7 @@ public:
403 T M, L; //allocate space for the total magnitude and length 885 T M, L; //allocate space for the total magnitude and length
404 M = L = 0; //initialize both the initial magnitude and length to zero 886 M = L = 0; //initialize both the initial magnitude and length to zero
405 for(unsigned e = 0; e < E.size(); e++){ //for each edge in the network 887 for(unsigned e = 0; e < E.size(); e++){ //for each edge in the network
406 - M += E[e].integrate(m); //get the integrated magnitude 888 + M += E[e].integrate(); //get the integrated magnitude
407 L += E[e].length(); //get the edge length 889 L += E[e].length(); //get the edge length
408 } 890 }
409 891
@@ -411,67 +893,391 @@ public: @@ -411,67 +893,391 @@ public:
411 } 893 }
412 894
413 /// This function compares two networks and returns the percentage of the current network that is missing from A. 895 /// This function compares two networks and returns the percentage of the current network that is missing from A.
414 -  
415 /// @param A is the network to compare to - the field is generated for A 896 /// @param A is the network to compare to - the field is generated for A
416 /// @param sigma is the user-defined tolerance value - smaller values provide a stricter comparison 897 /// @param sigma is the user-defined tolerance value - smaller values provide a stricter comparison
417 - stim::network<T> compare(stim::network<T> A, float sigma){ 898 + stim::network<T> compare(stim::network<T> A, float sigma, int device = -1){
418 899
419 - stim::network<T> R; //generate a network storing the result of the comparison  
420 - R = (*this); //initialize the result with the current network 900 + stim::network<T> R; //generate a network storing the result of the comparison
  901 + R = (*this); //initialize the result with the current network
421 902
422 - //generate a KD-tree for network A  
423 - float metric = 0.0; // initialize metric to be returned after comparing the networks  
424 - ANNkd_tree* kdt; // initialize a pointer to a kd tree  
425 - double **c; // centerline (array of double pointers) - points on kdtree must be double  
426 - unsigned int n_data = A.total_points(); // set the number of points  
427 - c = (double**) malloc(sizeof(double*) * n_data); // allocate the array pointer  
428 - for(unsigned int i = 0; i < n_data; i++) // allocate space for each point of 3 dimensions  
429 - c[i] = (double*) malloc(sizeof(double) * 3); 903 + T *c; // centerline (array of double pointers) - points on kdtree must be double
  904 + size_t n_data = A.total_points(); // set the number of points
  905 + c = (T*) malloc(sizeof(T) * n_data * 3); // allocate an array to store all points in the data set
430 906
431 unsigned t = 0; 907 unsigned t = 0;
432 - for(unsigned e = 0; e < A.E.size(); e++){ //for each edge in the network  
433 - for(unsigned p = 0; p < A.E[e].size(); p++){ //for each point in the edge 908 + for(unsigned e = 0; e < A.E.size(); e++){ //for each edge in the network
  909 + for(unsigned p = 0; p < A.E[e].size(); p++){ //for each point in the edge
434 for(unsigned d = 0; d < 3; d++){ //for each coordinate 910 for(unsigned d = 0; d < 3; d++){ //for each coordinate
435 911
436 - c[t][d] = A.E[e][p][d]; 912 + c[t * 3 + d] = A.E[e][p][d]; //copy the point into the array c
437 } 913 }
438 t++; 914 t++;
439 } 915 }
440 } 916 }
441 917
442 - //compare each point in the current network to the field produced by A  
443 - ANNpointArray pts = (ANNpointArray)c; // create an array of data points of type double  
444 - kdt = new ANNkd_tree(pts, n_data, 3); // build a KD tree using the annpointarray  
445 - double eps = 0; // error bound  
446 - ANNdistArray dists = new ANNdist[1]; // near neighbor distances  
447 - ANNidxArray nnIdx = new ANNidx[1]; // near neighbor indices // allocate near neigh indices 918 + //generate a KD-tree for network A
  919 + size_t MaxTreeLevels = 3; // max tree level
  920 +
  921 +#ifdef __CUDACC__
  922 + cudaSetDevice(device);
  923 + stim::kdtree<T, 3> kdt; // initialize a pointer to a kd tree
  924 +
  925 + kdt.create(c, n_data, MaxTreeLevels); // build a KD tree
448 926
449 - stim::vec3<T> p0, p1;  
450 - float m1;  
451 - float M = 0; //stores the total metric value  
452 - float L = 0; //stores the total network length  
453 - ANNpoint queryPt = annAllocPt(3);  
454 for(unsigned e = 0; e < R.E.size(); e++){ //for each edge in A 927 for(unsigned e = 0; e < R.E.size(); e++){ //for each edge in A
455 - R.E[e].add_mag(0); //add a new magnitude for the metric 928 + //R.E[e].add_mag(0); //add a new magnitude for the metric
  929 + //size_t errormag_id = R.E[e].nmags() - 1; //get the id for the new magnitude
  930 +
  931 + size_t n = R.E[e].size(); // the number of points in current edge
  932 + T* queryPt = new T[3 * n];
  933 + T* m1 = new T[n];
  934 + T* dists = new T[n];
  935 + size_t* nnIdx = new size_t[n];
  936 +
  937 + T* d_dists;
  938 + T* d_m1;
  939 + cudaMalloc((void**)&d_dists, n * sizeof(T));
  940 + cudaMalloc((void**)&d_m1, n * sizeof(T));
  941 +
  942 + edge2array(queryPt, R.E[e]);
  943 + kdt.search(queryPt, n, nnIdx, dists);
  944 +
  945 + cudaMemcpy(d_dists, dists, n * sizeof(T), cudaMemcpyHostToDevice); // copy dists from host to device
456 946
457 - for(unsigned p = 0; p < R.E[e].size(); p++){ //for each point in the edge 947 + // configuration parameters
  948 + size_t threads = (1024>n)?n:1024;
  949 + size_t blocks = n/threads + (n%threads)?1:0;
458 950
459 - p1 = R.E[e][p]; //get the next point in the edge  
460 - stim2ann(queryPt, p1);  
461 - kdt->annkSearch( queryPt, 1, nnIdx, dists, eps); //find the distance between A and the current network  
462 - m1 = 1.0f - gaussianFunction((float)dists[0], sigma); //calculate the metric value based on the distance  
463 - R.E[e].set_mag(m1, p, 1); //set the error for the second point in the segment 951 + find_metric_parallel<<<blocks, threads>>>(d_m1, n, d_dists, sigma); //calculate the metric value based on the distance
464 952
  953 + cudaMemcpy(m1, d_m1, n * sizeof(T), cudaMemcpyDeviceToHost);
  954 +
  955 + for(unsigned p = 0; p < n; p++){
  956 + R.E[e].set_r(p, m1[p]);
465 } 957 }
  958 +
  959 + //d_set_mag<<<blocks, threads>>>(R.E[e].M, errormag_id, n, m1);
466 } 960 }
467 961
  962 +#else
  963 + stim::kdtree<T, 3> kdt;
  964 + kdt.create(c, n_data, MaxTreeLevels);
  965 +
  966 + for(unsigned e = 0; e < R.E.size(); e++){ //for each edge in A
  967 +
  968 + size_t n = R.E[e].size(); // the number of points in current edge
  969 + T* query = new T[3 * n];
  970 + T* m1 = new T[n];
  971 + T* dists = new T[n];
  972 + size_t* nnIdx = new size_t[n];
  973 +
  974 + edge2array(query, R.E[e]);
  975 +
  976 + kdt.cpu_search(query, n, nnIdx, dists); //find the distance between A and the current network
  977 +
  978 + for(unsigned p = 0; p < R.E[e].size(); p++){
  979 + m1[p] = 1.0f - gaussianFunction((T)dists[p], sigma); //calculate the metric value based on the distance
  980 + R.E[e].set_r(p, m1[p]); //set the error for the second point in the segment
  981 + }
  982 + }
  983 +#endif
468 return R; //return the resulting network 984 return R; //return the resulting network
469 } 985 }
470 986
471 - /// Returns the number of magnitude values stored in each edge. This should be uniform across the network.  
472 - unsigned nmags(){  
473 - return E[0].nmags(); 987 + /// This function compares two networks and split the current one according to the nearest neighbor of each point in each edge
  988 + /// @param A is the network to split
  989 + /// @param B is the corresponding mapping network
  990 + /// @param sigma is the user-defined tolerance value - smaller values provide a stricter comparison
  991 + /// @param device is the device that user want to use
  992 + void split(stim::network<T> A, stim::network<T> B, float sigma, int device, float threshold){
  993 +
  994 + T *c;
  995 + size_t n_data = B.total_points();
  996 + c = (T*) malloc(sizeof(T) * n_data * 3);
  997 +
  998 + size_t NB = B.E.size(); // the number of edges in B
  999 + unsigned t = 0;
  1000 + for(unsigned e = 0; e < NB; e++){ // for every edge in B
  1001 + for(unsigned p = 0; p < B.E[e].size(); p++){ // for every points in B.E[e]
  1002 + for(unsigned d = 0; d < 3; d++){
  1003 +
  1004 + c[t * 3 + d] = B.E[e][p][d]; // convert to array
  1005 + }
  1006 + t++;
  1007 + }
  1008 + }
  1009 + size_t MaxTreeLevels = 3; // max tree level
  1010 +
  1011 +#ifdef __CUDACC__
  1012 + cudaSetDevice(device);
  1013 + stim::kdtree<T, 3> kdt; // initialize a pointer to a kd tree
  1014 +
  1015 + //compare each point in the current network to the field produced by A
  1016 + kdt.create(c, n_data, MaxTreeLevels); // build a KD tree
  1017 +
  1018 + std::vector<std::vector<unsigned> > relation; // the relationship between GT and T corresponding to NN
  1019 + relation.resize(A.E.size());
  1020 +
  1021 + for(unsigned e = 0; e < A.E.size(); e++){ //for each edge in A
  1022 + //A.E[e].add_mag(0); //add a new magnitude for the metric
  1023 + //size_t errormag_id = A.E[e].nmags() - 1;
  1024 +
  1025 + size_t n = A.E[e].size(); // the number of edges in A
  1026 +
  1027 + T* queryPt = new T[3 * n]; // set of all the points in current edge
  1028 + T* m1 = new T[n]; // array of metrics for every point in current edge
  1029 + T* dists = new T[n]; // store the distances for every point in current edge
  1030 + size_t* nnIdx = new size_t[n]; // store the indices for every point in current edge
  1031 +
  1032 + // define pointers in device
  1033 + T* d_dists;
  1034 + T* d_m1;
  1035 + size_t* d_nnIdx;
  1036 +
  1037 + // allocate memory for defined pointers
  1038 + cudaMalloc((void**)&d_dists, n * sizeof(T));
  1039 + cudaMalloc((void**)&d_m1, n * sizeof(T));
  1040 + cudaMalloc((void**)&d_nnIdx, n * sizeof(size_t));
  1041 +
  1042 + edge2array(queryPt, A.E[e]); // convert edge to array
  1043 + kdt.search(queryPt, n, nnIdx, dists); // search the tree to find the NN for every point in current edge
  1044 +
  1045 + cudaMemcpy(d_dists, dists, n * sizeof(T), cudaMemcpyHostToDevice); // copy dists from host to device
  1046 + cudaMemcpy(d_nnIdx, nnIdx, n * sizeof(size_t), cudaMemcpyHostToDevice); // copy Idx from host to device
  1047 +
  1048 + // configuration parameters
  1049 + size_t threads = (1024>n)?n:1024; // test to see whether the number of point in current edge is more than 1024
  1050 + size_t blocks = n/threads + (n%threads)?1:0;
  1051 +
  1052 + find_metric_parallel<<<blocks, threads>>>(d_m1, n, d_dists, sigma); // calculate the metrics in parallel
  1053 +
  1054 + cudaMemcpy(m1, d_m1, n * sizeof(T), cudaMemcpyDeviceToHost);
  1055 +
  1056 + for(unsigned p = 0; p < n; p++){
  1057 + A.E[e].set_r(p, m1[p]); // set the error(radius) value to every point in current edge
  1058 + }
  1059 +
  1060 + relation[e].resize(n); // resize every edge relation size
  1061 +
  1062 + unsigned* d_relation;
  1063 + cudaMalloc((void**)&d_relation, n * sizeof(unsigned)); // allocate memory
  1064 +
  1065 + std::vector<size_t> edge_point_num(NB); // %th element is the number of points that %th edge has
  1066 + for(unsigned ee = 0; ee < NB; ee++)
  1067 + edge_point_num[ee] = B.E[ee].size();
  1068 +
  1069 + size_t* d_edge_point_num;
  1070 + cudaMalloc((void**)&d_edge_point_num, NB * sizeof(size_t));
  1071 + cudaMemcpy(d_edge_point_num, &edge_point_num[0], NB * sizeof(size_t), cudaMemcpyHostToDevice);
  1072 +
  1073 + find_edge_index_parallel<<<blocks, threads>>>(d_nnIdx, n, d_relation, d_edge_point_num, NB); // find the edge corresponding to the array index in parallel
  1074 +
  1075 + cudaMemcpy(&relation[e][0], d_relation, n * sizeof(unsigned), cudaMemcpyDeviceToHost); //copy relationship from device to host
  1076 + }
  1077 +#else
  1078 + stim::kdtree<T, 3> kdt;
  1079 + kdt.create(c, n_data, MaxTreeLevels);
  1080 +
  1081 + std::vector<std::vector<unsigned>> relation; // the mapping relationship between two networks
  1082 + relation.resize(A.E.size());
  1083 + for(unsigned i = 0; i < A.E.size(); i++)
  1084 + relation[i].resize(A.E[i].size());
  1085 +
  1086 + std::vector<size_t> edge_point_num(NB); //%th element is the number of points that %th edge has
  1087 + for(unsigned ee = 0; ee < NB; ee++)
  1088 + edge_point_num[ee] = B.E[ee].size();
  1089 +
  1090 + for(unsigned e = 0; e < A.E.size(); e++){ //for each edge in A
  1091 +
  1092 + size_t n = A.E[e].size(); //the number of edges in A
  1093 +
  1094 + T* queryPt = new T[3 * n];
  1095 + T* m1 = new T[n];
  1096 + T* dists = new T[n]; //store the distances
  1097 + size_t* nnIdx = new size_t[n]; //store the indices
  1098 +
  1099 + edge2array(queryPt, A.E[e]);
  1100 + kdt.search(queryPt, n, nnIdx, dists);
  1101 +
  1102 + for(unsigned p = 0; p < A.E[e].size(); p++){
  1103 + m1[p] = 1.0f - gaussianFunction((T)dists[p], sigma); //calculate the metric value based on the distance
  1104 + A.E[e].set_r(p, m1[p]); //set the error for the second point in the segment
  1105 +
  1106 + unsigned id = 0; //mapping edge's idx
  1107 + size_t num = 0; //total number of points before #th edge
  1108 + for(unsigned i = 0; i < NB; i++){
  1109 + num += B.E[i].size();
  1110 + if(nnIdx[p] < num){ //find the edge it belongs to
  1111 + relation[e][p] = id;
  1112 + break;
  1113 + }
  1114 + id++; //current edge won't be the one, move to next edge
  1115 + }
  1116 + }
  1117 + }
  1118 +#endif
  1119 + E = A.E;
  1120 + V = A.V;
  1121 +
  1122 + unsigned int id = 0; // split value
  1123 + for(unsigned e = 0; e < E.size(); e++){ // for every edge
  1124 + for(unsigned p = 0; p < E[e].size() - 1; p++){ // for every point in each edge
  1125 + int t = (int)(E[e].length() / sigma * 2);
  1126 + if (t <= 20)
  1127 + threshold_fac = E[e].size();
  1128 + else
  1129 + threshold_fac = (E[e].length() / sigma * 2)/10;
  1130 + if(relation[e][p] != relation[e][p + 1]){ // find the nearest edge changing point
  1131 + id = p + 1; // if there is no change in NN
  1132 + if(id < threshold_fac || (E[e].size() - id) < threshold_fac)
  1133 + id = E[e].size() - 1; // extreme situation is not acceptable
  1134 + else
  1135 + break;
  1136 + }
  1137 + if(p == E[e].size() - 2) // if there is no splitting index, set the id to the last point index of current edge
  1138 + id = E[e].size() - 1;
  1139 + }
  1140 + //unsigned errormag_id = E[e].nmags() - 1;
  1141 + T G = 0; // test to see whether it has its nearest neighbor
  1142 + for(unsigned i = 0; i < E[e].size(); i++)
  1143 + G += E[e].r(i); // won't split special edges
  1144 + if(G / E[e].size() > threshold) // should based on the color map
  1145 + id = E[e].size() - 1; // set split idx to outgoing direction vertex
  1146 +
  1147 + std::vector<edge> tmpe;
  1148 + tmpe.resize(2);
  1149 + tmpe = E[e].split(id);
  1150 + vertex tmpv = stim::vec3<T>(-1, -1, 0); // store the split point as vertex
  1151 + if(tmpe.size() == 2){
  1152 + relation.resize(relation.size() + 1);
  1153 + for(unsigned d = id; d < E[e].size(); d++)
  1154 + relation[relation.size() - 1].push_back(relation[e][d]);
  1155 + tmpe[0].v[0] = E[e].v[0]; // begining vertex of first half edge -> original begining vertex
  1156 + tmpe[1].v[1] = E[e].v[1]; // ending vertex of second half edge -> original ending vertex
  1157 + tmpv = E[e][id];
  1158 + V.push_back(tmpv);
  1159 + tmpe[0].v[1] = (unsigned)V.size() - 1; // ending vertex of first half edge -> new vertex
  1160 + tmpe[1].v[0] = (unsigned)V.size() - 1; // begining vertex of second half edge -> new vertex
  1161 + edge tmp(E[e]);
  1162 + E[e] = tmpe[0]; // replace original edge by first half edge
  1163 + E.push_back(tmpe[1]); // push second half edge to the last
  1164 + V[V.size() - 1].e[1].push_back(e); // push first half edge to the incoming of new vertex
  1165 + V[V.size() - 1].e[0].push_back((unsigned)E.size() - 1); // push second half edge to the outgoing of new vertex
  1166 + for(unsigned i = 0; i < V[tmp.v[1]].e[1].size(); i++) // find the incoming edge of original ending vertex
  1167 + if(V[tmp.v[1]].e[1][i] == e)
  1168 + V[tmp.v[1]].e[1][i] = (unsigned)E.size() - 1; // set to new edge
  1169 + }
  1170 + }
474 } 1171 }
  1172 +
  1173 + /// This function compares two splitted networks and yields a mapping relationship between them according to NN
  1174 + /// @param B is the network that the current network is going to map to
  1175 + /// @param C is the mapping relationship: C[e1] = _e1 means e1 edge in current network is mapping the _e1 edge in B
  1176 + /// @param device is the device that user want to use
  1177 + void mapping(stim::network<T> B, std::vector<unsigned> &C, int device, float threshold){
  1178 + stim::network<T> A; //generate a network storing the result of the comparison
  1179 + A = (*this);
  1180 +
  1181 + size_t n = A.E.size(); // the number of edges in A
  1182 + size_t NB = B.E.size(); // the number of edges in B
  1183 +
  1184 + C.resize(A.E.size());
  1185 +
  1186 + T *c; // centerline (array of double pointers) - points on kdtree must be double
  1187 + size_t n_data = B.total_points(); // set the number of points
  1188 + c = (T*) malloc(sizeof(T) * n_data * 3);
  1189 +
  1190 + unsigned t = 0;
  1191 + for(unsigned e = 0; e < NB; e++){ // for each edge in the network
  1192 + for(unsigned p = 0; p < B.E[e].size(); p++){ // for each point in the edge
  1193 + for(unsigned d = 0; d < 3; d++){ // for each coordinate
  1194 +
  1195 + c[t * 3 + d] = B.E[e][p][d];
  1196 + }
  1197 + t++;
  1198 + }
  1199 + }
  1200 +
  1201 + //generate a KD-tree for network A
  1202 + //float metric = 0.0; // initialize metric to be returned after comparing the network
  1203 + size_t MaxTreeLevels = 3; // max tree level
  1204 +
  1205 +#ifdef __CUDACC__
  1206 + cudaSetDevice(device);
  1207 + stim::kdtree<T, 3> kdt; // initialize a pointer to a kd tree
  1208 +
  1209 + kdt.create(c, n_data, MaxTreeLevels); // build a KD tree
  1210 +
  1211 + for(unsigned e = 0; e < n; e++){ //for each edge in A
  1212 + //size_t errormag_id = A.E[e].nmags() - 1; //get the id for the new magnitude
  1213 +
  1214 + //pre-judge to get rid of impossibly mapping edges
  1215 + T M = 0;
  1216 + for(unsigned p = 0; p < A.E[e].size(); p++)
  1217 + M += A.E[e].r(p);
  1218 + M = M / A.E[e].size();
  1219 + if(M > threshold)
  1220 + C[e] = (unsigned)-1; //set the nearest edge of impossibly mapping edges to maximum of unsigned
  1221 + else{
  1222 + T* queryPt = new T[3];
  1223 + T* dists = new T[1];
  1224 + size_t* nnIdx = new size_t[1];
  1225 +
  1226 + stim2array(queryPt, A.E[e][A.E[e].size()/2]);
  1227 + kdt.search(queryPt, 1, nnIdx, dists);
  1228 +
  1229 + unsigned id = 0; //mapping edge's idx
  1230 + size_t num = 0; //total number of points before #th edge
  1231 + for(unsigned i = 0; i < NB; i++){
  1232 + num += B.E[i].size();
  1233 + if(nnIdx[0] < num){
  1234 + C[e] = id;
  1235 + break;
  1236 + }
  1237 + id++;
  1238 + }
  1239 + }
  1240 + }
  1241 +#else
  1242 + stim::kdtree<T, 3> kdt;
  1243 + kdt.create(c, n_data, MaxTreeLevels);
  1244 + T *dists = new T[1]; // near neighbor distances
  1245 + size_t *nnIdx = new size_t[1]; // near neighbor indices // allocate near neigh indices
  1246 +
  1247 + stim::vec3<T> p0, p1;
  1248 + T* queryPt = new T[3];
  1249 +
  1250 + for(unsigned int e = 0; e < R.E.size(); e++){ // for each edge in A
  1251 + T M; // the sum of metrics of current edge
  1252 + for(unsigned p = 0; p < R.E[e].size(); p++)
  1253 + M += A.E[e].r(p);
  1254 + M = M / A.E[e].size();
  1255 + if(M > threshold)
  1256 + C[e] = (unsigned)-1;
  1257 + else{ // if it should have corresponding edge in B, then...
  1258 + p1 = R.E[e][R.E[e].size()/2];
  1259 + stim2array(queryPt, p1);
  1260 + kdt.cpu_search(queryPt, 1, nnIdx, dists); // search the tree
  1261 +
  1262 + unsigned id = 0; //mapping edge's idx
  1263 + size_t num = 0; //total number of points before #th edge
  1264 + for(unsigned i = 0; i < NB; i++){
  1265 + num += B.E[i].size();
  1266 + if(nnIdx[0] < num){
  1267 + C[e] = id;
  1268 + break;
  1269 + }
  1270 + id++;
  1271 + }
  1272 + }
  1273 + }
  1274 +#endif
  1275 + }
  1276 +
  1277 + /// Returns the number of magnitude values stored in each edge. This should be uniform across the network.
  1278 + //unsigned nmags(){
  1279 + // return E[0].nmags();
  1280 + //}
475 // split a string in text by the character sep 1281 // split a string in text by the character sep
476 stim::vec<T> split(std::string &text, char sep) 1282 stim::vec<T> split(std::string &text, char sep)
477 { 1283 {
@@ -488,7 +1294,7 @@ public: @@ -488,7 +1294,7 @@ public:
488 void load_txt(std::string filename) 1294 void load_txt(std::string filename)
489 { 1295 {
490 std::vector <std::string> file_contents; 1296 std::vector <std::string> file_contents;
491 - std::ifstream file(filename); 1297 + std::ifstream file(filename.c_str());
492 std::string line; 1298 std::string line;
493 std::vector<unsigned> id2vert; //this list stores the vertex ID associated with each network vertex 1299 std::vector<unsigned> id2vert; //this list stores the vertex ID associated with each network vertex
494 //for each line in the text file, store them as strings in file_contents 1300 //for each line in the text file, store them as strings in file_contents
@@ -539,7 +1345,7 @@ public: @@ -539,7 +1345,7 @@ public:
539 for(unsigned int d = 0; d < 3; d++){ 1345 for(unsigned int d = 0; d < 3; d++){
540 ss<<p[i][d]; 1346 ss<<p[i][d];
541 } 1347 }
542 - ss < "\n"; 1348 + ss << "\n";
543 } 1349 }
544 return ss.str(); 1350 return ss.str();
545 } 1351 }
@@ -553,8 +1359,8 @@ public: @@ -553,8 +1359,8 @@ public:
553 void 1359 void
554 to_txt(std::string filename) 1360 to_txt(std::string filename)
555 { 1361 {
556 - std::ofstream ofs(filename, std::ofstream::out | std::ofstream::app);  
557 - int num; 1362 + std::ofstream ofs(filename.c_str(), std::ofstream::out | std::ofstream::app);
  1363 + //int num;
558 ofs << (E.size()).str() << "\n"; 1364 ofs << (E.size()).str() << "\n";
559 for(unsigned int i = 0; i < E.size(); i++) 1365 for(unsigned int i = 0; i < E.size(); i++)
560 { 1366 {
@@ -567,7 +1373,8 @@ public: @@ -567,7 +1373,8 @@ public:
567 { 1373 {
568 std::string str; 1374 std::string str;
569 str = V[i].str(); 1375 str = V[i].str();
570 - removeCharsFromString(str, "[],"); 1376 + char temp[4] = "[],";
  1377 + removeCharsFromString(str, temp);
571 ofs << str << "\n"; 1378 ofs << str << "\n";
572 } 1379 }
573 ofs.close(); 1380 ofs.close();
stim/biomodels/network_dep.h
  1 +/*
  2 +Copyright <2017> <David Mayerich>
  3 +
  4 +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  5 +
  6 +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  7 +
  8 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  9 +*/
1 #ifndef STIM_NETWORK_H 10 #ifndef STIM_NETWORK_H
2 #define STIM_NETWORK_H 11 #define STIM_NETWORK_H
3 12
4 #include <stim/math/vector.h> 13 #include <stim/math/vector.h>
5 #include <stim/visualization/obj.h> 14 #include <stim/visualization/obj.h>
6 #include <list> 15 #include <list>
7 -#include <ANN/ANN.h> 16 +//#include <ANN/ANN.h>
8 17
9 namespace stim{ 18 namespace stim{
10 19
stim/biomodels/nwt_format.pptx 0 → 100644
No preview for this file type
stim/biomodels/test.nwt 0 → 100644
No preview for this file type
stim/cuda/arraymath/array_cart2polar.cuh
@@ -4,14 +4,14 @@ @@ -4,14 +4,14 @@
4 namespace stim{ 4 namespace stim{
5 namespace cuda{ 5 namespace cuda{
6 template<typename T> 6 template<typename T>
7 - __global__ void cuda_cart2polar(T* a, int x, int y, float rotation){ 7 + __global__ void cuda_cart2polar(T* a, size_t x, size_t y, float rotation){
8 8
9 9
10 // calculate the 2D coordinates for this current thread. 10 // calculate the 2D coordinates for this current thread.
11 - int xi = blockIdx.x * blockDim.x + threadIdx.x;  
12 - int yi = blockIdx.y * blockDim.y + threadIdx.y; 11 + size_t xi = blockIdx.x * blockDim.x + threadIdx.x;
  12 + size_t yi = blockIdx.y * blockDim.y + threadIdx.y;
13 // convert 2D coordinates to 1D 13 // convert 2D coordinates to 1D
14 - int i = yi * x + xi; 14 + size_t i = yi * x + xi;
15 15
16 16
17 if(xi >= x|| yi >= y) return; 17 if(xi >= x|| yi >= y) return;
@@ -27,11 +27,11 @@ namespace stim{ @@ -27,11 +27,11 @@ namespace stim{
27 27
28 28
29 template<typename T> 29 template<typename T>
30 - void gpu_cart2polar(T* gpuGrad, unsigned int x, unsigned int y, float rotation = 0){ 30 + void gpu_cart2polar(T* gpuGrad, size_t x, size_t y, float rotation = 0){
31 31
32 unsigned int max_threads = stim::maxThreadsPerBlock(); 32 unsigned int max_threads = stim::maxThreadsPerBlock();
33 dim3 threads(max_threads, 1); 33 dim3 threads(max_threads, 1);
34 - dim3 blocks(x/threads.x + (x %threads.x == 0 ? 0:1) , y); 34 + dim3 blocks(((unsigned int)x/threads.x) + ((unsigned int)x %threads.x == 0 ? 0:1) , (unsigned int)y);
35 35
36 //call the kernel to do the multiplication 36 //call the kernel to do the multiplication
37 cuda_cart2polar <<< blocks, threads >>>(gpuGrad, x, y, rotation); 37 cuda_cart2polar <<< blocks, threads >>>(gpuGrad, x, y, rotation);
@@ -40,11 +40,11 @@ namespace stim{ @@ -40,11 +40,11 @@ namespace stim{
40 40
41 41
42 template<typename T> 42 template<typename T>
43 - void cpu_cart2polar(T* a, unsigned int x, unsigned int y){ 43 + void cpu_cart2polar(T* a, size_t x, size_t y){
44 44
45 //calculate the number of bytes in the array 45 //calculate the number of bytes in the array
46 - unsigned int N = x *y;  
47 - unsigned int bytes = N * sizeof(T) * 2; 46 + size_t N = x *y;
  47 + size_t bytes = N * sizeof(T) * 2;
48 48
49 //allocate memory on the GPU for the array 49 //allocate memory on the GPU for the array
50 T* gpuA; 50 T* gpuA;
stim/cuda/branch_detection.cuh
@@ -11,7 +11,7 @@ typedef unsigned int uint; @@ -11,7 +11,7 @@ typedef unsigned int uint;
11 11
12 12
13 std::vector< stim::vec<float> > 13 std::vector< stim::vec<float> >
14 -find_branch(GLint texbufferID, GLenum texType, unsigned int x, unsigned int y) 14 +find_branch(GLint texbufferID, GLenum texType, unsigned int x, unsigned int y, int iter = 0)
15 { 15 {
16 float sigma = 2.0; 16 float sigma = 2.0;
17 unsigned int conn = 7; 17 unsigned int conn = 7;
@@ -22,7 +22,7 @@ find_branch(GLint texbufferID, GLenum texType, unsigned int x, unsigned int y) @@ -22,7 +22,7 @@ find_branch(GLint texbufferID, GLenum texType, unsigned int x, unsigned int y)
22 stringstream name; 22 stringstream name;
23 23
24 24
25 - cpuCenters = stim::cuda::get_centers(texbufferID, texType, x, y, sizek, sigma, conn, threshold); 25 + cpuCenters = stim::cuda::get_centers(texbufferID, texType, x, y, sizek, sigma, conn, threshold, iter);
26 cudaDeviceSynchronize(); 26 cudaDeviceSynchronize();
27 27
28 28
stim/cuda/cudatools/callable.h
@@ -7,4 +7,10 @@ @@ -7,4 +7,10 @@
7 #define CUDA_CALLABLE 7 #define CUDA_CALLABLE
8 #endif 8 #endif
9 9
  10 +#ifdef __CUDACC__
  11 +#define CUDA_UNCALLABLE __host__ inline
  12 +#else
  13 +#define CUDA_UNCALLABLE
  14 +#endif
  15 +
10 #endif 16 #endif
stim/cuda/cudatools/devices.h
1 -#ifndef RTS_CUDA_DEVICES  
2 -#define RTS_CUDA_DEVICES 1 +#ifndef STIM_CUDA_DEVICES
  2 +#define STIM_CUDA_DEVICES
3 3
4 #include <cuda.h> 4 #include <cuda.h>
5 5
6 namespace stim{ 6 namespace stim{
7 -extern "C"  
8 -int maxThreadsPerBlock()  
9 -{  
10 - int device;  
11 - cudaGetDevice(&device); //get the id of the current device  
12 - cudaDeviceProp props; //device property structure  
13 - cudaGetDeviceProperties(&props, device);  
14 - return props.maxThreadsPerBlock;  
15 -}  
16 -  
17 -extern "C"  
18 -size_t sharedMemPerBlock()  
19 -{  
20 - int device;  
21 - cudaGetDevice(&device); //get the id of the current device  
22 - cudaDeviceProp props; //device property structure  
23 - cudaGetDeviceProperties(&props, device);  
24 - return props.sharedMemPerBlock;  
25 -}  
26 -  
27 -extern "C"  
28 -size_t constMem()  
29 -{  
30 - int device;  
31 - cudaGetDevice(&device); //get the id of the current device  
32 - cudaDeviceProp props; //device property structure  
33 - cudaGetDeviceProperties(&props, device);  
34 - return props.totalConstMem;  
35 -} 7 + extern "C"
  8 + int maxThreadsPerBlock(){
  9 + int device;
  10 + cudaGetDevice(&device); //get the id of the current device
  11 + cudaDeviceProp props; //device property structure
  12 + cudaGetDeviceProperties(&props, device);
  13 + return props.maxThreadsPerBlock;
  14 + }
  15 +
  16 + extern "C"
  17 + size_t sharedMemPerBlock(){
  18 + int device;
  19 + cudaGetDevice(&device); //get the id of the current device
  20 + cudaDeviceProp props; //device property structure
  21 + cudaGetDeviceProperties(&props, device);
  22 + return props.sharedMemPerBlock;
  23 + }
  24 +
  25 + extern "C"
  26 + size_t constMem(){
  27 + int device;
  28 + cudaGetDevice(&device); //get the id of the current device
  29 + cudaDeviceProp props; //device property structure
  30 + cudaGetDeviceProperties(&props, device);
  31 + return props.totalConstMem;
  32 + }
  33 +
  34 + //tests that a given device ID is valid and provides at least the specified compute capability
  35 + bool testDevice(int d, int major, int minor){
  36 + int nd;
  37 + cudaGetDeviceCount(&nd); //get the number of CUDA devices
  38 + if(d <= nd && d >= 0) { //if the given ID has an associated device
  39 + cudaDeviceProp props;
  40 + cudaGetDeviceProperties(&props, d); //get the device properties structure
  41 + if(props.major > major){
  42 + return true;
  43 + }
  44 + else if(props.major == major && props.minor >= minor){
  45 + return true;
  46 + }
  47 + }
  48 + return false;
  49 + }
  50 +
  51 + //tests each device ID in a list and returns the number of devices that fit the desired
  52 + // compute capability
  53 + int testDevices(int* dlist, unsigned n_devices, int major, int minor){
  54 + int valid = 0;
  55 + for(int d = 0; d < n_devices; d++){
  56 + if(testDevice(dlist[d], major, minor))
  57 + valid++;
  58 + }
  59 + return valid;
  60 + }
  61 +
  62 + void printDevice(int device){
  63 + int nd;
  64 + cudaGetDeviceCount(&nd); //get the number of CUDA devices
  65 + printf("CUDA Device Diagnosis: [%i]\n", device);
  66 + if(device < 0){
  67 + printf("Device %i is an invalid device ID\n", device);
  68 + }
  69 + else if(device >= nd){
  70 + printf("Device %i is unavailable - only %i devices are detected", device, nd);
  71 + }
  72 + else{
  73 + cudaDeviceProp props;
  74 + cudaGetDeviceProperties(&props, device); //get the device properties structure
  75 + printf("compute capability: %i.%i\n", props.major, props.minor);
  76 + }
  77 + }
36 } //end namespace rts 78 } //end namespace rts
37 79
38 #endif 80 #endif
stim/cuda/cudatools/error.h
  1 +#ifndef STIM_CUDA_ERROR_H
  2 +#define STIM_CUDA_ERROR_H
  3 +
1 #include <stdio.h> 4 #include <stdio.h>
2 #include <iostream> 5 #include <iostream>
3 -using namespace std;  
4 #include "cuda_runtime.h" 6 #include "cuda_runtime.h"
5 #include "device_launch_parameters.h" 7 #include "device_launch_parameters.h"
6 #include "cufft.h" 8 #include "cufft.h"
7 -  
8 -#ifndef CUDA_HANDLE_ERROR_H  
9 -#define CUDA_HANDLE_ERROR_H 9 +#include "cublas_v2.h"
10 10
11 //handle error macro 11 //handle error macro
12 -static void HandleError( cudaError_t err, const char *file, int line ) { 12 +static void cuHandleError( cudaError_t err, const char *file, int line ) {
13 if (err != cudaSuccess) { 13 if (err != cudaSuccess) {
14 printf("%s in %s at line %d\n", cudaGetErrorString( err ), file, line ); 14 printf("%s in %s at line %d\n", cudaGetErrorString( err ), file, line );
  15 +
15 } 16 }
16 } 17 }
17 -#define HANDLE_ERROR( err ) (HandleError( err, __FILE__, __LINE__ ))  
18 -  
19 -static void CufftError( cufftResult err ) 18 +#define HANDLE_ERROR( err ) (cuHandleError( err, __FILE__, __LINE__ ))
  19 +static void cufftHandleError( cufftResult err, const char*file, int line )
20 { 20 {
21 if (err != CUFFT_SUCCESS) 21 if (err != CUFFT_SUCCESS)
22 { 22 {
23 if(err == CUFFT_INVALID_PLAN) 23 if(err == CUFFT_INVALID_PLAN)
24 - cout<<"The plan parameter is not a valid handle."<<endl; 24 + std::cout<<"The plan parameter is not a valid handle."<<std::endl;
25 else if(err == CUFFT_ALLOC_FAILED) 25 else if(err == CUFFT_ALLOC_FAILED)
26 - cout<<"Allocation failed."<<endl; 26 + std::cout<<"Allocation failed."<<std::endl;
27 else if(err == CUFFT_INVALID_VALUE) 27 else if(err == CUFFT_INVALID_VALUE)
28 - cout<<"At least one of the parameters idata, odata, and direction is not valid."<<endl; 28 + std::cout<<"At least one of the parameters idata, odata, and direction is not valid."<<std::endl;
29 else if(err == CUFFT_INTERNAL_ERROR) 29 else if(err == CUFFT_INTERNAL_ERROR)
30 - cout<<"An internal driver error was detected."<<endl; 30 + std::cout<<"An internal driver error was detected."<<std::endl;
31 else if(err == CUFFT_EXEC_FAILED) 31 else if(err == CUFFT_EXEC_FAILED)
32 - cout<<"CUFFT failed to execute the transform on the GPU."<<endl; 32 + std::cout<<"CUFFT failed to execute the transform on the GPU."<<std::endl;
33 else if(err == CUFFT_SETUP_FAILED) 33 else if(err == CUFFT_SETUP_FAILED)
34 - cout<<"The CUFFT library failed to initialize."<<endl; 34 + std::cout<<"The CUFFT library failed to initialize."<<std::endl;
35 else 35 else
36 - cout<<"Unknown error: "<<err<<endl; 36 + std::cout<<"Unknown error: "<<err<<std::endl;
37 37
38 } 38 }
39 } 39 }
  40 +#define CUFFT_HANDLE_ERROR( err ) (cufftHandleError( err, __FILE__, __LINE__ ))
40 41
  42 +static void cublasHandleError( cublasStatus_t err, const char*file, int line ){
  43 + if(err != CUBLAS_STATUS_SUCCESS){
  44 + if(err == CUBLAS_STATUS_NOT_INITIALIZED)
  45 + std::cout<<"CUBLAS_STATUS_NOT_INITIALIZED" <<" in file "<<file<<" line "<<std::endl;
  46 + else if(err == CUBLAS_STATUS_ALLOC_FAILED)
  47 + std::cout<<"CUBLAS_STATUS_ALLOC_FAILED" <<" in file "<<file<<" line "<<std::endl;
  48 + else if(err == CUBLAS_STATUS_INVALID_VALUE)
  49 + std::cout<<"CUBLAS_STATUS_INVALID_VALUE" <<" in file "<<file<<" line "<<std::endl;
  50 + else if(err == CUBLAS_STATUS_ARCH_MISMATCH)
  51 + std::cout<<"CUBLAS_STATUS_ARCH_MISMATCH" <<" in file "<<file<<" line "<<std::endl;
  52 + else if(err == CUBLAS_STATUS_MAPPING_ERROR)
  53 + std::cout<<"CUBLAS_STATUS_MAPPING_ERROR" <<" in file "<<file<<" line "<<std::endl;
  54 + else if(err == CUBLAS_STATUS_EXECUTION_FAILED)
  55 + std::cout<<"CUBLAS_STATUS_EXECUTION_FAILED" <<" in file "<<file<<" line "<<std::endl;
  56 + else if(err == CUBLAS_STATUS_INTERNAL_ERROR)
  57 + std::cout<<"CUBLAS_STATUS_INTERNAL_ERROR" <<" in file "<<file<<" line "<<std::endl;
  58 + else
  59 + std::cout<<"Unknown error"<<" in file "<<file<<" line "<<std::endl;
  60 + }
  61 +}
  62 +#define CUBLAS_HANDLE_ERROR( err ) (cublasHandleError( err, __FILE__, __LINE__ ))
41 63
42 64
43 #endif 65 #endif
stim/cuda/filter.cuh
@@ -26,6 +26,11 @@ namespace stim @@ -26,6 +26,11 @@ namespace stim
26 float* LoG; 26 float* LoG;
27 float* res; 27 float* res;
28 float* centers; 28 float* centers;
  29 +
  30 +//#ifdef DEBUG
  31 + float* print;
  32 +//#endif
  33 +
29 stim::cuda::cuda_texture tx; 34 stim::cuda::cuda_texture tx;
30 35
31 36
@@ -44,6 +49,13 @@ namespace stim @@ -44,6 +49,13 @@ namespace stim
44 HANDLE_ERROR( 49 HANDLE_ERROR(
45 cudaMalloc( (void**) &centers, DIM_Y*DIM_X*sizeof(float)) 50 cudaMalloc( (void**) &centers, DIM_Y*DIM_X*sizeof(float))
46 ); 51 );
  52 +
  53 +//#ifdef DEBUG
  54 + HANDLE_ERROR(
  55 + cudaMalloc( (void**) &print, DIM_Y*DIM_X*sizeof(float))
  56 + );
  57 +//#endif
  58 +
47 // checkCUDAerrors("Memory Allocation, Result"); 59 // checkCUDAerrors("Memory Allocation, Result");
48 } 60 }
49 61
@@ -58,6 +70,11 @@ namespace stim @@ -58,6 +70,11 @@ namespace stim
58 HANDLE_ERROR( 70 HANDLE_ERROR(
59 cudaFree(centers) 71 cudaFree(centers)
60 ); 72 );
  73 +//#ifdef DEBUG
  74 + HANDLE_ERROR(
  75 + cudaFree(print)
  76 + );
  77 +//#endif
61 free(LoG); 78 free(LoG);
62 } 79 }
63 80
@@ -89,7 +106,7 @@ namespace stim @@ -89,7 +106,7 @@ namespace stim
89 //Shared memory would be better. 106 //Shared memory would be better.
90 __global__ 107 __global__
91 void 108 void
92 - applyFilter(cudaTextureObject_t texIn, unsigned int DIM_X, unsigned int DIM_Y, int kr, int kl, float *res, float* gpuLoG){ 109 + applyFilter(cudaTextureObject_t texIn, unsigned int DIM_X, unsigned int DIM_Y, int kr, int kl, float *res, float* gpuLoG, float* p){
93 //R = floor(size/2) 110 //R = floor(size/2)
94 //THIS IS A NAIVE WAY TO DO IT, and there is a better way) 111 //THIS IS A NAIVE WAY TO DO IT, and there is a better way)
95 112
@@ -101,16 +118,15 @@ namespace stim @@ -101,16 +118,15 @@ namespace stim
101 // float val = 0; 118 // float val = 0;
102 float tu = (x-kr+xi)/(float)DIM_X; 119 float tu = (x-kr+xi)/(float)DIM_X;
103 float tv = (y-kr+yi)/(float)DIM_Y; 120 float tv = (y-kr+yi)/(float)DIM_Y;
  121 + int idx = y*DIM_X+x;
104 shared[xi][yi] = gpuLoG[yi*kl+xi]*(255.0-(float)tex2D<unsigned char>(texIn, tu, tv)); 122 shared[xi][yi] = gpuLoG[yi*kl+xi]*(255.0-(float)tex2D<unsigned char>(texIn, tu, tv));
105 __syncthreads(); 123 __syncthreads();
106 -  
107 124
108 //x = max(0,x); 125 //x = max(0,x);
109 //x = min(x, width-1); 126 //x = min(x, width-1);
110 //y = max(y, 0); 127 //y = max(y, 0);
111 //y = min(y, height - 1); 128 //y = min(y, height - 1);
112 129
113 - int idx = y*DIM_X+x;  
114 // int k_idx; 130 // int k_idx;
115 for(unsigned int step = blockDim.x/2; step >= 1; step >>= 1) 131 for(unsigned int step = blockDim.x/2; step >= 1; step >>= 1)
116 { 132 {
@@ -135,11 +151,12 @@ namespace stim @@ -135,11 +151,12 @@ namespace stim
135 __syncthreads(); 151 __syncthreads();
136 if(xi == 0 && yi == 0) 152 if(xi == 0 && yi == 0)
137 res[idx] = shared[0][0]; 153 res[idx] = shared[0][0];
  154 +
138 } 155 }
139 156
140 extern "C" 157 extern "C"
141 float * 158 float *
142 - get_centers(GLint texbufferID, GLenum texType, int DIM_X, int DIM_Y, int sizeK, float sigma, float conn, float threshold) 159 + get_centers(GLint texbufferID, GLenum texType, int DIM_X, int DIM_Y, int sizeK, float sigma, float conn, float threshold, int iter = 0)
143 { 160 {
144 tx.SetTextureCoordinates(1); 161 tx.SetTextureCoordinates(1);
145 tx.SetAddressMode(1, 3); 162 tx.SetAddressMode(1, 3);
@@ -153,7 +170,14 @@ namespace stim @@ -153,7 +170,14 @@ namespace stim
153 dim3 numBlocks(DIM_X, DIM_Y); 170 dim3 numBlocks(DIM_X, DIM_Y);
154 dim3 threadsPerBlock(sizeK, sizeK); 171 dim3 threadsPerBlock(sizeK, sizeK);
155 172
156 - applyFilter <<< numBlocks, threadsPerBlock >>> (tx.getTexture(), DIM_X, DIM_Y, floor(sizeK/2), sizeK, res, gpuLoG); 173 + applyFilter <<< numBlocks, threadsPerBlock >>> (tx.getTexture(), DIM_X, DIM_Y, floor(sizeK/2), sizeK, res, gpuLoG, print);
  174 +
  175 + #ifdef DEBUG
  176 + stringstream name;
  177 + name.str("");
  178 + name << "Fiber Cylinder " << iter << ".bmp";
  179 + stim::gpu2image<float>(res, name.str(), DIM_X, DIM_Y, 0, 255);
  180 + #endif
157 181
158 182
159 stim::cuda::gpu_local_max<float>(centers, res, threshold, conn, DIM_X, DIM_Y); 183 stim::cuda::gpu_local_max<float>(centers, res, threshold, conn, DIM_X, DIM_Y);
stim/cuda/kmeans.cuh 0 → 100644
  1 +//This software is dervied from Professor Wei-keng Liao's parallel k-means
  2 +//clustering code obtained on November 21, 2010 from
  3 +// http://users.eecs.northwestern.edu/~wkliao/Kmeans/index.html
  4 +//(http://users.eecs.northwestern.edu/~wkliao/Kmeans/simple_kmeans.tar.gz).
  5 +//
  6 +//With his permission, Serban Giuroiu is publishing his CUDA implementation based on his code
  7 +//under the open-source MIT license. See the LICENSE file for more details.
  8 +
  9 +// The original code can be found on Github ( https://github.com/serban/kmeans )
  10 +// Here I have just made a few changes to get it to work
  11 +
  12 +
  13 +
  14 +
  15 +#define malloc2D(name, xDim, yDim, type) do { \
  16 + name = (type **)malloc(xDim * sizeof(type *)); \
  17 + assert(name != NULL); \
  18 + name[0] = (type *)malloc(xDim * yDim * sizeof(type)); \
  19 + assert(name[0] != NULL); \
  20 + for (size_t i = 1; i < xDim; i++) \
  21 + name[i] = name[i-1] + yDim; \
  22 +} while (0)
  23 +
  24 +
  25 +
  26 +static void handleError(cudaError_t error, const char* file, int line){
  27 +
  28 + if(error != cudaSuccess){
  29 + cout << cudaGetErrorString(error) << " in " << file << " at line " << line << endl;
  30 + exit(1);
  31 + }
  32 +}
  33 +
  34 +#define handle_error(error) handleError(error, __FILE__ , __LINE__)
  35 +
  36 +
  37 +
  38 +static inline int nextPowerOfTwo(int n) {
  39 + n--;
  40 +
  41 + n = n >> 1 | n;
  42 + n = n >> 2 | n;
  43 + n = n >> 4 | n;
  44 + n = n >> 8 | n;
  45 + n = n >> 16 | n;
  46 + // n = n >> 32 | n; // For 64-bit ints
  47 +
  48 + return ++n;
  49 +}
  50 +
  51 +/*----< euclid_dist_2() >----------------------------------------------------*/
  52 +/* square of Euclid distance between two multi-dimensional points */
  53 +__host__ __device__ inline static
  54 +float euclid_dist_2(int numCoords,
  55 + int numObjs,
  56 + int numClusters,
  57 + float *objects, // [numCoords][numObjs]
  58 + float *clusters, // [numCoords][numClusters]
  59 + int objectId,
  60 + int clusterId)
  61 +{
  62 + int i;
  63 + float ans=0.0;
  64 +
  65 + for (i = 0; i < numCoords; i++) {
  66 + ans += (objects[numObjs * i + objectId] - clusters[numClusters * i + clusterId]) *
  67 + (objects[numObjs * i + objectId] - clusters[numClusters * i + clusterId]);
  68 + }
  69 +
  70 + return(ans);
  71 +}
  72 +
  73 +/*----< find_nearest_cluster() >---------------------------------------------*/
  74 +__global__ static
  75 +void find_nearest_cluster(int numCoords,
  76 + int numObjs,
  77 + int numClusters,
  78 + float *objects, // [numCoords][numObjs]
  79 + float *deviceClusters, // [numCoords][numClusters]
  80 + int *membership, // [numObjs]
  81 + int *intermediates)
  82 +{
  83 + extern __shared__ char sharedMemory[];
  84 +
  85 + // The type chosen for membershipChanged must be large enough to support
  86 + // reductions! There are blockDim.x elements, one for each thread in the
  87 + // block. See numThreadsPerClusterBlock in cuda_kmeans().
  88 + unsigned char *membershipChanged = (unsigned char *)sharedMemory;
  89 +#ifdef BLOCK_SHARED_MEM_OPTIMIZATION
  90 + float *clusters = (float *)(sharedMemory + blockDim.x);
  91 +#else
  92 + float *clusters = deviceClusters;
  93 +#endif
  94 +
  95 + membershipChanged[threadIdx.x] = 0;
  96 +
  97 +#ifdef BLOCK_SHARED_MEM_OPTIMIZATION
  98 + // BEWARE: We can overrun our shared memory here if there are too many
  99 + // clusters or too many coordinates! For reference, a Tesla C1060 has 16
  100 + // KiB of shared memory per block, and a GeForce GTX 480 has 48 KiB of
  101 + // shared memory per block.
  102 + for (int i = threadIdx.x; i < numClusters; i += blockDim.x) {
  103 + for (int j = 0; j < numCoords; j++) {
  104 + clusters[numClusters * j + i] = deviceClusters[numClusters * j + i];
  105 + }
  106 + }
  107 + __syncthreads();
  108 +#endif
  109 +
  110 + int objectId = blockDim.x * blockIdx.x + threadIdx.x;
  111 +
  112 + if (objectId < numObjs) {
  113 + int index, i;
  114 + float dist, min_dist;
  115 +
  116 + /* find the cluster id that has min distance to object */
  117 + index = 0;
  118 + min_dist = euclid_dist_2(numCoords, numObjs, numClusters,
  119 + objects, clusters, objectId, 0);
  120 +
  121 + for (i=1; i<numClusters; i++) {
  122 + dist = euclid_dist_2(numCoords, numObjs, numClusters,
  123 + objects, clusters, objectId, i);
  124 + /* no need square root */
  125 + if (dist < min_dist) { /* find the min and its array index */
  126 + min_dist = dist;
  127 + index = i;
  128 + }
  129 + }
  130 +
  131 + if (membership[objectId] != index) {
  132 + membershipChanged[threadIdx.x] = 1;
  133 + }
  134 +
  135 + /* assign the membership to object objectId */
  136 + membership[objectId] = index;
  137 +
  138 + __syncthreads(); // For membershipChanged[]
  139 +
  140 + // blockDim.x *must* be a power of two!
  141 + for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {
  142 + if (threadIdx.x < s) {
  143 + membershipChanged[threadIdx.x] +=
  144 + membershipChanged[threadIdx.x + s];
  145 + }
  146 + __syncthreads();
  147 + }
  148 +
  149 + if (threadIdx.x == 0) {
  150 + intermediates[blockIdx.x] = membershipChanged[0];
  151 + }
  152 + }
  153 +}
  154 +
  155 +__global__ static
  156 +void compute_delta(int *deviceIntermediates,
  157 + int numIntermediates, // The actual number of intermediates
  158 + int numIntermediates2) // The next power of two
  159 +{
  160 + // The number of elements in this array should be equal to
  161 + // numIntermediates2, the number of threads launched. It *must* be a power
  162 + // of two!
  163 + extern __shared__ unsigned int intermediates[];
  164 +
  165 + // Copy global intermediate values into shared memory.
  166 + intermediates[threadIdx.x] =
  167 + (threadIdx.x < numIntermediates) ? deviceIntermediates[threadIdx.x] : 0;
  168 +
  169 + __syncthreads();
  170 +
  171 + // numIntermediates2 *must* be a power of two!
  172 + for (unsigned int s = numIntermediates2 / 2; s > 0; s >>= 1) {
  173 + if (threadIdx.x < s) {
  174 + intermediates[threadIdx.x] += intermediates[threadIdx.x + s];
  175 + }
  176 + __syncthreads();
  177 + }
  178 +
  179 + if (threadIdx.x == 0) {
  180 + deviceIntermediates[0] = intermediates[0];
  181 + }
  182 +}
  183 +
  184 +/*----< cuda_kmeans() >-------------------------------------------------------*/
  185 +//
  186 +// ----------------------------------------
  187 +// DATA LAYOUT
  188 +//
  189 +// objects [numObjs][numCoords]
  190 +// clusters [numClusters][numCoords]
  191 +// dimObjects [numCoords][numObjs]
  192 +// dimClusters [numCoords][numClusters]
  193 +// newClusters [numCoords][numClusters]
  194 +// deviceObjects [numCoords][numObjs]
  195 +// deviceClusters [numCoords][numClusters]
  196 +// ----------------------------------------
  197 +//
  198 +/* return an array of cluster centers of size [numClusters][numCoords] */
  199 +float** cuda_kmeans(float **objects, /* in: [numObjs][numCoords] */
  200 + unsigned int numCoords, /* no. features */
  201 + unsigned int numObjs, /* no. objects */
  202 + unsigned int numClusters, /* no. clusters */
  203 + float threshold, /* % objects change membership */
  204 + int *membership, /* out: [numObjs] */
  205 + int loops)
  206 +{
  207 + int i, j, index, loop=0;
  208 + int *newClusterSize; /* [numClusters]: no. objects assigned in each
  209 + new cluster */
  210 + float delta; /* % of objects change their clusters */
  211 + float **dimObjects;
  212 + float **clusters; /* out: [numClusters][numCoords] */
  213 + float **dimClusters;
  214 + float **newClusters; /* [numCoords][numClusters] */
  215 +
  216 + float *deviceObjects;
  217 + float *deviceClusters;
  218 + int *deviceMembership;
  219 + int *deviceIntermediates;
  220 +
  221 + // Copy objects given in [numObjs][numCoords] layout to new
  222 + // [numCoords][numObjs] layout
  223 + malloc2D(dimObjects, numCoords, numObjs, float);
  224 + for (i = 0; i < numCoords; i++) {
  225 + for (j = 0; j < numObjs; j++) {
  226 + dimObjects[i][j] = objects[j][i];
  227 + }
  228 + }
  229 +
  230 + /* pick first numClusters elements of objects[] as initial cluster centers*/
  231 + malloc2D(dimClusters, numCoords, numClusters, float);
  232 + for (i = 0; i < numCoords; i++) {
  233 + for (j = 0; j < numClusters; j++) {
  234 + dimClusters[i][j] = dimObjects[i][j];
  235 + }
  236 + }
  237 +
  238 + /* initialize membership[] */
  239 + for (i=0; i<numObjs; i++) membership[i] = -1;
  240 +
  241 + /* need to initialize newClusterSize and newClusters[0] to all 0 */
  242 + newClusterSize = (int*) calloc(numClusters, sizeof(int));
  243 + assert(newClusterSize != NULL);
  244 +
  245 + malloc2D(newClusters, numCoords, numClusters, float);
  246 + memset(newClusters[0], 0, numCoords * numClusters * sizeof(float));
  247 +
  248 + // To support reduction, numThreadsPerClusterBlock *must* be a power of
  249 + // two, and it *must* be no larger than the number of bits that will
  250 + // fit into an unsigned char, the type used to keep track of membership
  251 + // changes in the kernel.
  252 + cudaDeviceProp props;
  253 + handle_error(cudaGetDeviceProperties(&props, 0));
  254 + const unsigned int numThreadsPerClusterBlock = props.maxThreadsPerBlock;
  255 + const unsigned int numClusterBlocks =
  256 + ceil(numObjs / (double)numThreadsPerClusterBlock);
  257 +
  258 +#ifdef BLOCK_SHARED_MEM_OPTIMIZATION
  259 + const unsigned int clusterBlockSharedDataSize =
  260 + numThreadsPerClusterBlock * sizeof(unsigned char) +
  261 + numClusters * numCoords * sizeof(float);
  262 +
  263 + cudaDeviceProp deviceProp;
  264 + int deviceNum;
  265 + cudaGetDevice(&deviceNum);
  266 + cudaGetDeviceProperties(&deviceProp, deviceNum);
  267 +
  268 + if (clusterBlockSharedDataSize > deviceProp.sharedMemPerBlock) {
  269 + std::cout << "ERROR: insufficient shared memory. Please don't use the definition 'BLOCK_SHARED_MEM_OPTIMIZATION'" << endl;
  270 + exit(1);
  271 + }
  272 +#else
  273 + const unsigned int clusterBlockSharedDataSize =
  274 + numThreadsPerClusterBlock * sizeof(unsigned char);
  275 +#endif
  276 +
  277 + const unsigned int numReductionThreads =
  278 + nextPowerOfTwo(numClusterBlocks);
  279 + const unsigned int reductionBlockSharedDataSize =
  280 + numReductionThreads * sizeof(unsigned int);
  281 +
  282 + handle_error(cudaMalloc((void**)&deviceObjects, numObjs*numCoords*sizeof(float)));
  283 + handle_error(cudaMalloc((void**)&deviceClusters, numClusters*numCoords*sizeof(float)));
  284 + handle_error(cudaMalloc((void**)&deviceMembership, numObjs*sizeof(int)));
  285 + handle_error(cudaMalloc((void**)&deviceIntermediates, numReductionThreads*sizeof(unsigned int)));
  286 +
  287 + handle_error(cudaMemcpy(deviceObjects, dimObjects[0],
  288 + numObjs*numCoords*sizeof(float), cudaMemcpyHostToDevice));
  289 + handle_error(cudaMemcpy(deviceMembership, membership,
  290 + numObjs*sizeof(int), cudaMemcpyHostToDevice));
  291 +
  292 + do {
  293 + handle_error(cudaMemcpy(deviceClusters, dimClusters[0],
  294 + numClusters*numCoords*sizeof(float), cudaMemcpyHostToDevice));
  295 +
  296 + find_nearest_cluster
  297 + <<< numClusterBlocks, numThreadsPerClusterBlock, clusterBlockSharedDataSize >>>
  298 + (numCoords, numObjs, numClusters,
  299 + deviceObjects, deviceClusters, deviceMembership, deviceIntermediates);
  300 +
  301 + cudaDeviceSynchronize();
  302 +
  303 + compute_delta <<< 1, numReductionThreads, reductionBlockSharedDataSize >>>
  304 + (deviceIntermediates, numClusterBlocks, numReductionThreads);
  305 +
  306 + cudaDeviceSynchronize();
  307 +
  308 + int d;
  309 + handle_error(cudaMemcpy(&d, deviceIntermediates,
  310 + sizeof(int), cudaMemcpyDeviceToHost));
  311 + delta = (float)d;
  312 +
  313 + handle_error(cudaMemcpy(membership, deviceMembership,
  314 + numObjs*sizeof(int), cudaMemcpyDeviceToHost));
  315 +
  316 + for (i=0; i<numObjs; i++) {
  317 + /* find the array index of nestest cluster center */
  318 + index = membership[i];
  319 +
  320 + /* update new cluster centers : sum of objects located within */
  321 + newClusterSize[index]++;
  322 + for (j=0; j<numCoords; j++)
  323 + newClusters[j][index] += objects[i][j];
  324 + }
  325 +
  326 + // TODO: Flip the nesting order
  327 + // TODO: Change layout of newClusters to [numClusters][numCoords]
  328 + /* average the sum and replace old cluster centers with newClusters */
  329 + for (i=0; i<numClusters; i++) {
  330 + for (j=0; j<numCoords; j++) {
  331 + if (newClusterSize[i] > 0)
  332 + dimClusters[j][i] = newClusters[j][i] / newClusterSize[i];
  333 + newClusters[j][i] = 0.0; /* set back to 0 */
  334 + }
  335 + newClusterSize[i] = 0; /* set back to 0 */
  336 + }
  337 +
  338 + delta /= numObjs;
  339 + } while (delta > threshold && loop++ < loops);
  340 +
  341 +
  342 +
  343 + /* allocate a 2D space for returning variable clusters[] (coordinates
  344 + of cluster centers) */
  345 + malloc2D(clusters, numClusters, numCoords, float);
  346 + for (i = 0; i < numClusters; i++) {
  347 + for (j = 0; j < numCoords; j++) {
  348 + clusters[i][j] = dimClusters[j][i];
  349 + }
  350 + }
  351 +
  352 + handle_error(cudaFree(deviceObjects));
  353 + handle_error(cudaFree(deviceClusters));
  354 + handle_error(cudaFree(deviceMembership));
  355 + handle_error(cudaFree(deviceIntermediates));
  356 +
  357 + free(dimObjects[0]);
  358 + free(dimObjects);
  359 + free(dimClusters[0]);
  360 + free(dimClusters);
  361 + free(newClusters[0]);
  362 + free(newClusters);
  363 + free(newClusterSize);
  364 +
  365 + return clusters;
  366 +}
stim/cuda/templates/conv2sep.cuh
@@ -8,8 +8,6 @@ @@ -8,8 +8,6 @@
8 #include <stim/cuda/sharedmem.cuh> 8 #include <stim/cuda/sharedmem.cuh>
9 #include <stim/cuda/cudatools/error.h> 9 #include <stim/cuda/cudatools/error.h>
10 10
11 -#define pi 3.14159  
12 -  
13 namespace stim{ 11 namespace stim{
14 namespace cuda{ 12 namespace cuda{
15 13
stim/cuda/templates/gradient.cuh
@@ -9,57 +9,50 @@ namespace stim{ @@ -9,57 +9,50 @@ namespace stim{
9 namespace cuda{ 9 namespace cuda{
10 10
11 template<typename T> 11 template<typename T>
12 - __global__ void gradient_2d(T* out, T* in, int x, int y){ 12 + __global__ void gradient_2d(T* out, T* in, size_t x, size_t y){
13 13
14 14
15 // calculate the 2D coordinates for this current thread. 15 // calculate the 2D coordinates for this current thread.
16 - int xi = blockIdx.x * blockDim.x + threadIdx.x;  
17 - int yi = blockIdx.y * blockDim.y + threadIdx.y; 16 + size_t xi = blockIdx.x * blockDim.x + threadIdx.x;
  17 + size_t yi = blockIdx.y * blockDim.y + threadIdx.y;
18 // convert 2D coordinates to 1D 18 // convert 2D coordinates to 1D
19 - int i = yi * x + xi; 19 + size_t i = yi * x + xi;
20 20
21 //return if the pixel is outside of the image 21 //return if the pixel is outside of the image
22 if(xi >= x || yi >= y) return; 22 if(xi >= x || yi >= y) return;
23 23
24 //calculate indices for the forward difference 24 //calculate indices for the forward difference
25 - int i_xp = yi * x + (xi + 1);  
26 - int i_yp = (yi + 1) * x + xi; 25 + size_t i_xp = yi * x + (xi + 1);
  26 + size_t i_yp = (yi + 1) * x + xi;
27 27
28 //calculate indices for the backward difference 28 //calculate indices for the backward difference
29 - int i_xn = yi * x + (xi - 1);  
30 - int i_yn = (yi - 1) * x + xi; 29 + size_t i_xn = yi * x + (xi - 1);
  30 + size_t i_yn = (yi - 1) * x + xi;
31 31
32 //use forward differences if a coordinate is zero 32 //use forward differences if a coordinate is zero
33 if(xi == 0) 33 if(xi == 0)
34 out[i * 2 + 0] = in[i_xp] - in[i]; 34 out[i * 2 + 0] = in[i_xp] - in[i];
35 - if(yi == 0)  
36 - out[i * 2 + 1] = in[i_yp] - in[i];  
37 -  
38 - //use backward differences if the coordinate is at the maximum edge  
39 - if(xi == x-1) 35 + else if (xi == x - 1)
40 out[i * 2 + 0] = in[i] - in[i_xn]; 36 out[i * 2 + 0] = in[i] - in[i_xn];
41 - if(yi == y-1)  
42 - out[i * 2 + 1] = in[i] - in[i_yn];  
43 -  
44 - //otherwise use central differences  
45 - if(xi > 0 && xi < x-1) 37 + else
46 out[i * 2 + 0] = (in[i_xp] - in[i_xn]) / 2; 38 out[i * 2 + 0] = (in[i_xp] - in[i_xn]) / 2;
47 39
48 - if(yi > 0 && yi < y-1) 40 + if(yi == 0)
  41 + out[i * 2 + 1] = in[i_yp] - in[i];
  42 + else if(yi == y-1)
  43 + out[i * 2 + 1] = in[i] - in[i_yn];
  44 + else
49 out[i * 2 + 1] = (in[i_yp] - in[i_yn]) / 2; 45 out[i * 2 + 1] = (in[i_yp] - in[i_yn]) / 2;
50 46
51 } 47 }
52 48
53 template<typename T> 49 template<typename T>
54 - void gpu_gradient_2d(T* gpuGrad, T* gpuI, unsigned int x, unsigned int y){  
55 -  
56 - //get the number of pixels in the image  
57 - unsigned int pixels = x * y; 50 + void gpu_gradient_2d(T* gpuGrad, T* gpuI, size_t x, size_t y){
58 51
59 //get the maximum number of threads per block for the CUDA device 52 //get the maximum number of threads per block for the CUDA device
60 unsigned int max_threads = stim::maxThreadsPerBlock(); 53 unsigned int max_threads = stim::maxThreadsPerBlock();
61 dim3 threads(max_threads, 1); 54 dim3 threads(max_threads, 1);
62 - dim3 blocks(x/threads.x + 1 , y); 55 + dim3 blocks((unsigned int)(x/threads.x) + 1 , (unsigned int)y);
63 56
64 57
65 //call the GPU kernel to determine the gradient 58 //call the GPU kernel to determine the gradient
stim/cuda/testKernel.cuh
@@ -53,11 +53,31 @@ @@ -53,11 +53,31 @@
53 53
54 float valIn = tex2D<unsigned char>(texIn, x, y); 54 float valIn = tex2D<unsigned char>(texIn, x, y);
55 float templa = templ(x, 32)*255.0; 55 float templa = templ(x, 32)*255.0;
56 - print[idx] = abs(valIn-templa); ///temporary 56 + //print[idx] = abs(valIn-templa); ///temporary
  57 + print[idx] = abs(valIn);
57 //print[idx] = abs(templa); ///temporary 58 //print[idx] = abs(templa); ///temporary
58 59
59 } 60 }
60 61
  62 + ///Find the difference of the given set of samples and the template
  63 + ///using cuda acceleration.
  64 + ///@param stim::cuda::cuda_texture t --stim texture that holds all the references
  65 + /// to the data.
  66 + ///@param float* result --a pointer to the memory that stores the result.
  67 + __global__
  68 + //void get_diff (float *result)
  69 + void get_diff2 (cudaTextureObject_t texIn, float *print, int dx)
  70 + {
  71 + int x = threadIdx.x + blockIdx.x * blockDim.x;
  72 + int y = threadIdx.y + blockIdx.y * blockDim.y;
  73 + int idx = y*dx+x;
  74 + // int idx = y*16+x;
  75 +
  76 + float valIn = tex2D<unsigned char>(texIn, x, y);
  77 + print[idx] = abs(valIn); ///temporary
  78 +
  79 + }
  80 +
61 void test(cudaTextureObject_t tObj, int x, int y, std::string nam) 81 void test(cudaTextureObject_t tObj, int x, int y, std::string nam)
62 { 82 {
63 83
@@ -86,3 +106,31 @@ @@ -86,3 +106,31 @@
86 cleanUP(); 106 cleanUP();
87 } 107 }
88 108
  109 +
  110 + void test(cudaTextureObject_t tObj, int x, int y, std::string nam, int iter)
  111 + {
  112 +
  113 + //Bind the Texture in GL and allow access to cuda.
  114 +
  115 + //initialize the return arrays.
  116 +
  117 + initArray(x,y);
  118 + dim3 numBlocks(1, y);
  119 + dim3 threadsPerBlock(x, 1);
  120 + int max_threads = stim::maxThreadsPerBlock();
  121 + //dim3 threads(max_threads, 1);
  122 + //dim3 blocks(x / threads.x + 1, y);
  123 + //dim3 numBlocks(2, 2);
  124 + //dim3 threadsPerBlock(8, 108);
  125 +
  126 +
  127 +// get_diff <<< blocks, threads >>> (tx.getTexture(), print);
  128 + get_diff2 <<< numBlocks, threadsPerBlock >>> (tObj, print, x);
  129 +
  130 + cudaDeviceSynchronize();
  131 + stringstream name; //for debugging
  132 + name << nam.c_str();
  133 + stim::gpu2image<float>(print, name.str(),x,y,0,255);
  134 +
  135 + cleanUP();
  136 + }
stim/envi/agilent_binary.h
@@ -4,13 +4,17 @@ @@ -4,13 +4,17 @@
4 4
5 #include <string> 5 #include <string>
6 #include <fstream> 6 #include <fstream>
  7 +#include <complex>
  8 +#include <cstring>
  9 +#include <chrono>
7 10
8 //CUDA 11 //CUDA
9 -#ifdef CUDA_FOUND  
10 - #include <cuda_runtime.h>  
11 - #include "cufft.h"  
12 - #include <stim/cuda/cudatools/error.h>  
13 -#endif 12 +//#ifdef CUDA_FOUND
  13 +#include <cuda_runtime.h>
  14 +#include "cufft.h"
  15 +#include <stim/cuda/cudatools/error.h>
  16 +#include <stim/envi/envi_header.h>
  17 +//#endif
14 18
15 namespace stim{ 19 namespace stim{
16 20
@@ -33,15 +37,25 @@ public: @@ -33,15 +37,25 @@ public:
33 return size() * sizeof(T); 37 return size() * sizeof(T);
34 } 38 }
35 void alloc(){ 39 void alloc(){
  40 + if (ptr != NULL) free(ptr);
  41 + ptr = NULL;
36 ptr = (T*) malloc(bytes()); 42 ptr = (T*) malloc(bytes());
37 } 43 }
38 - void alloc(size_t x, size_t y, size_t z){ 44 + void alloc(size_t x, size_t y, size_t z){
39 R[0] = x; 45 R[0] = x;
40 R[1] = y; 46 R[1] = y;
41 R[2] = z; 47 R[2] = z;
42 alloc(); 48 alloc();
43 } 49 }
44 50
  51 + char* data() {
  52 + return (char*)ptr;
  53 + }
  54 +
  55 + size_t dim(size_t i){
  56 + return R[i];
  57 + }
  58 +
45 /// Create a deep copy of an agileng_binary object 59 /// Create a deep copy of an agileng_binary object
46 void deep_copy(agilent_binary<T>* dst, const agilent_binary<T>* src){ 60 void deep_copy(agilent_binary<T>* dst, const agilent_binary<T>* src){
47 dst->alloc(src->R[0], src->R[1], src->R[2]); //allocate memory 61 dst->alloc(src->R[0], src->R[1], src->R[2]); //allocate memory
@@ -51,23 +65,28 @@ public: @@ -51,23 +65,28 @@ public:
51 65
52 /// Default constructor, sets the resolution to zero and the data pointer to NULL 66 /// Default constructor, sets the resolution to zero and the data pointer to NULL
53 agilent_binary(){ 67 agilent_binary(){
54 - memset(R, 0, sizeof(size_t) * 3); //set the resolution to zero  
55 ptr = NULL; 68 ptr = NULL;
  69 + memset(R, 0, sizeof(size_t) * 3); //set the resolution to zero
  70 + memset(Z, 0, sizeof(double) * 2);
56 } 71 }
57 72
58 /// Constructor with resolution 73 /// Constructor with resolution
59 agilent_binary(size_t x, size_t y, size_t z){ 74 agilent_binary(size_t x, size_t y, size_t z){
  75 + ptr = NULL;
60 alloc(x, y, z); 76 alloc(x, y, z);
  77 + memset(Z, 0, sizeof(double) * 2);
61 } 78 }
62 79
63 /// Constructor with filename 80 /// Constructor with filename
64 agilent_binary(std::string filename){ 81 agilent_binary(std::string filename){
65 ptr = NULL; 82 ptr = NULL;
  83 + memset(Z, 0, sizeof(double) * 2);
66 load(filename); 84 load(filename);
67 } 85 }
68 86
69 /// Copy constructor 87 /// Copy constructor
70 agilent_binary(const agilent_binary<T> &obj){ 88 agilent_binary(const agilent_binary<T> &obj){
  89 + ptr = NULL;
71 deep_copy(this, &obj); 90 deep_copy(this, &obj);
72 } 91 }
73 92
@@ -78,32 +97,42 @@ public: @@ -78,32 +97,42 @@ public:
78 return *this; //return the result 97 return *this; //return the result
79 } 98 }
80 99
  100 + operator bool() {
  101 + if (R[0] == 0 || R[1] == 0 || R[2] == 0) return false;
  102 + else return true;
  103 + }
  104 +
81 ~agilent_binary(){ 105 ~agilent_binary(){
82 - free(ptr); 106 + if(ptr != NULL)
  107 + free(ptr);
83 } 108 }
84 109
85 void load(std::string filename){ 110 void load(std::string filename){
86 - if(ptr != NULL) free(ptr); //if memory has been allocated, free it 111 + if(ptr != NULL) free(ptr); //if memory has been allocated, free it
  112 + ptr = NULL;
87 113
88 - fname = filename; //save the filename 114 + fname = filename; //save the filename
89 115
90 short x, y, z; 116 short x, y, z;
91 117
92 std::ifstream infile(fname, std::ios::binary); //open the input file 118 std::ifstream infile(fname, std::ios::binary); //open the input file
93 - infile.seekg(9, std::ios::beg); //seek past 9 bytes from the beginning of the file 119 + if (infile) {
  120 + infile.seekg(9, std::ios::beg); //seek past 9 bytes from the beginning of the file
94 121
95 - infile.read((char*)(&z), 2); //read two bytes of data (the number of samples is stored as a 16-bit integer) 122 + infile.read((char*)(&z), 2); //read two bytes of data (the number of samples is stored as a 16-bit integer)
96 123
97 - infile.seekg(13, std::ios::cur); //skip another 13 bytes  
98 - infile.read((char*)(&x), 2); //read the X and Y dimensions  
99 - infile.read((char*)(&y), 2); 124 + infile.seekg(13, std::ios::cur); //skip another 13 bytes
  125 + infile.read((char*)(&x), 2); //read the X and Y dimensions
  126 + infile.read((char*)(&y), 2);
100 127
101 - infile.seekg(header, std::ios::beg); //seek to the start of the data 128 + infile.seekg(header, std::ios::beg); //seek to the start of the data
102 129
103 - alloc(x, y, z);  
104 - ptr = (T*) malloc(bytes()); //allocate space for the data  
105 - infile.read((char*)ptr, bytes()); //read the data  
106 - infile.close(); 130 + alloc(x, y, z); //allocate the data
  131 + infile.read((char*)ptr, bytes()); //read the data
  132 + infile.close(); //close the file
  133 + Z[0] = 1;
  134 + Z[1] = (double)R[2];
  135 + }
107 } 136 }
108 137
109 void save(std::string filename){ 138 void save(std::string filename){
@@ -167,9 +196,8 @@ public: @@ -167,9 +196,8 @@ public:
167 196
168 //pads to the nearest power-of-two 197 //pads to the nearest power-of-two
169 void zeropad(){ 198 void zeropad(){
170 - size_t newZ = pow(2, ceil(log(R[2])/log(2))); //find the nearest power-of-two 199 + size_t newZ = (size_t)pow(2, ceil(log(R[2])/log(2))); //find the nearest power-of-two
171 size_t n = newZ - R[2]; //calculate the number of bands to add 200 size_t n = newZ - R[2]; //calculate the number of bands to add
172 - std::cout<<"band padding: "<<n<<std::endl;  
173 zeropad(n); //add the padding 201 zeropad(n); //add the padding
174 } 202 }
175 203
@@ -184,9 +212,20 @@ public: @@ -184,9 +212,20 @@ public:
184 ptr[i] = -log10(ptr[i] / background->ptr[i]); 212 ptr[i] = -log10(ptr[i] / background->ptr[i]);
185 } 213 }
186 214
187 -#ifdef CUDA_FOUND 215 + //crops the image down to a set number of samples
  216 + void crop(size_t n) {
  217 + if (n < R[2]) { //if the requested size is smaller than the image
  218 + R[2] = n; //update the number of bands
  219 + T* old_ptr = ptr; //store the old pointer
  220 + alloc(); //allocate space for the new image
  221 + memcpy(ptr, old_ptr, bytes()); //copy the old data to the new image
  222 + free(old_ptr); //free the old data
  223 + }
  224 + }
  225 +
  226 +//#ifdef CUDA_FOUND
188 /// Perform an FFT and return a binary file with bands in the specified range 227 /// Perform an FFT and return a binary file with bands in the specified range
189 - agilent_binary<T> fft(double band_min, double band_max, double ELWN = 15798, int UDR = 2){ 228 + agilent_binary<T> fft(double band_min, double band_max, double ELWN = 15798, int UDR = 2, int device = 0){
190 auto total_start = std::chrono::high_resolution_clock::now(); 229 auto total_start = std::chrono::high_resolution_clock::now();
191 230
192 auto start = std::chrono::high_resolution_clock::now(); 231 auto start = std::chrono::high_resolution_clock::now();
@@ -201,6 +240,29 @@ public: @@ -201,6 +240,29 @@ public:
201 // std::cout << "Transpose data: " << diff.count() << " s\n"; 240 // std::cout << "Transpose data: " << diff.count() << " s\n";
202 241
203 start = std::chrono::high_resolution_clock::now(); 242 start = std::chrono::high_resolution_clock::now();
  243 + if (device >= 0) { //if a CUDA device is specified
  244 + int dev_count;
  245 + HANDLE_ERROR(cudaGetDeviceCount(&dev_count)); //get the number of CUDA devices
  246 + //std::cout << "Number of CUDA devices: " << dev_count << std::endl; //output the number of CUDA devices
  247 + cudaDeviceProp prop;
  248 + //std::cout << "CUDA devices----" << std::endl;
  249 + for (int d = 0; d < dev_count; d++) { //for each CUDA device
  250 + cudaGetDeviceProperties(&prop, d); //get the property of the first device
  251 + //float cc = prop.major + prop.minor / 10.0f; //calculate the compute capability
  252 + //std::cout << d << ": [" << prop.major << "." << prop.minor << "] " << prop.name << std::endl; //display the device information
  253 + //if(cc > best_device_cc){
  254 + // best_device_cc = cc; //if this is better than the previous device, use it
  255 + // best_device_id = d;
  256 + //}
  257 + }
  258 + if (dev_count > 0 && dev_count > device) { //if the first device is not an emulator
  259 + cudaGetDeviceProperties(&prop, device); //get the property of the requested CUDA device
  260 + if (prop.major != 9999) {
  261 + //std::cout << "Using device " << device << std::endl;
  262 + HANDLE_ERROR(cudaSetDevice(device));
  263 + }
  264 + }
  265 + }
204 cufftHandle plan; //allocate space for a cufft plan 266 cufftHandle plan; //allocate space for a cufft plan
205 cufftReal* gpu_data; //create a pointer to the data 267 cufftReal* gpu_data; //create a pointer to the data
206 size_t batch = R[0] * R[1]; //calculate the batch size (X * Y) 268 size_t batch = R[0] * R[1]; //calculate the batch size (X * Y)
@@ -237,18 +299,19 @@ public: @@ -237,18 +299,19 @@ public:
237 HANDLE_ERROR(cudaMemcpy(cpu_fft, gpu_fft, R[0] * R[1] * (R[2]/2+1) * sizeof(cufftComplex), cudaMemcpyDeviceToHost)); //copy data from the host to the device 299 HANDLE_ERROR(cudaMemcpy(cpu_fft, gpu_fft, R[0] * R[1] * (R[2]/2+1) * sizeof(cufftComplex), cudaMemcpyDeviceToHost)); //copy data from the host to the device
238 300
239 //double int_delta = 0.00012656; //interferogram sample spacing in centimeters 301 //double int_delta = 0.00012656; //interferogram sample spacing in centimeters
240 - double int_delta = (1.0 / ELWN) * ((double)UDR / 2.0); //calculate the interferogram spacing  
241 - double int_length = int_delta * R[2]; //interferogram length in centimeters  
242 - double fft_delta = 1/int_length; //spectrum spacing (in inverse centimeters, wavenumber)  
243 - double fft_max = fft_delta * R[2]/2; //get the maximum wavenumber value supported by the specified number of interferogram samples 302 + double int_delta = (1.0 / ELWN) * ((double)UDR / 2.0); //calculate the interferogram spacing
  303 + double int_length = int_delta * R[2]; //interferogram length in centimeters
  304 + double fft_delta = 1/int_length; //spectrum spacing (in inverse centimeters, wavenumber)
  305 + double fft_max = fft_delta * R[2]/2; //get the maximum wavenumber value supported by the specified number of interferogram samples
244 306
245 - if(band_max > fft_max) band_max = fft_max; //the user gave a band outside of the FFT range, reset the band to the maximum available 307 + if(band_max > fft_max) band_max = fft_max; //the user gave a band outside of the FFT range, reset the band to the maximum available
  308 + if (band_min < 0) band_min = 0;
246 309
247 size_t start_i = (size_t)std::ceil(band_min / fft_delta); //calculate the first band to store 310 size_t start_i = (size_t)std::ceil(band_min / fft_delta); //calculate the first band to store
248 size_t size_i = (size_t)std::floor(band_max / fft_delta) - start_i; //calculate the number of bands to store 311 size_t size_i = (size_t)std::floor(band_max / fft_delta) - start_i; //calculate the number of bands to store
249 - size_t end_i = start_i + size_i; //last band number 312 + size_t end_i = start_i + size_i; //last band number
250 agilent_binary<T> result(R[0], R[1], size_i); 313 agilent_binary<T> result(R[0], R[1], size_i);
251 - result.Z[0] = start_i * fft_delta; //set the range for the FFT result 314 + result.Z[0] = start_i * fft_delta; //set the range for the FFT result
252 result.Z[1] = end_i * fft_delta; 315 result.Z[1] = end_i * fft_delta;
253 316
254 for(size_t b = start_i; b < end_i; b++){ 317 for(size_t b = start_i; b < end_i; b++){
@@ -271,7 +334,22 @@ public: @@ -271,7 +334,22 @@ public:
271 334
272 return result; 335 return result;
273 } 336 }
274 -#endif 337 +
  338 + //saves the binary as an ENVI file with a BIP interleave format
  339 + int bip(T* bip_ptr){
  340 + //std::ofstream out(outfile.c_str(), std::ios::binary); //create a binary file stream for output
  341 + size_t XY = R[0] * R[1];
  342 + size_t B = R[2];
  343 + size_t b;
  344 +
  345 + for(size_t xy = 0; xy < XY; xy++){
  346 + for(b = 0; b < B; b++){
  347 + bip_ptr[xy * B + b] = ptr[b * XY + xy];
  348 + }
  349 + }
  350 + return 0;
  351 + }
  352 +//#endif
275 353
276 }; 354 };
277 355
@@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
4 #include "../envi/envi_header.h" 4 #include "../envi/envi_header.h"
5 #include "../envi/hsi.h" 5 #include "../envi/hsi.h"
6 #include "../math/fd_coefficients.h" 6 #include "../math/fd_coefficients.h"
  7 +#include <stim/cuda/cudatools/error.h>
7 #include <cstring> 8 #include <cstring>
8 #include <utility> 9 #include <utility>
9 #include <deque> 10 #include <deque>
@@ -54,11 +55,12 @@ public: @@ -54,11 +55,12 @@ public:
54 unsigned long long Y, 55 unsigned long long Y,
55 unsigned long long B, 56 unsigned long long B,
56 unsigned long long header_offset, 57 unsigned long long header_offset,
57 - std::vector<double> wavelengths){ 58 + std::vector<double> wavelengths,
  59 + stim::iotype io = stim::io_in){
58 60
59 w = wavelengths; 61 w = wavelengths;
60 62
61 - return open(filename, vec<unsigned long long>(X, B, Y), header_offset); 63 + return open(filename, vec<unsigned long long>(X, B, Y), header_offset, io);
62 64
63 } 65 }
64 66
@@ -118,7 +120,7 @@ public: @@ -118,7 +120,7 @@ public:
118 page++; 120 page++;
119 //if wavelength is larger than the last wavelength in header file 121 //if wavelength is larger than the last wavelength in header file
120 if (page == Z()) { 122 if (page == Z()) {
121 - band_index(p, Z()-1); 123 + band_index(p, Z()-1, PROGRESS);
122 return true; 124 return true;
123 } 125 }
124 } 126 }
@@ -224,10 +226,44 @@ public: @@ -224,10 +226,44 @@ public:
224 } 226 }
225 227
226 //given a Y ,return a XZ slice 228 //given a Y ,return a XZ slice
227 - bool read_plane_y(T * p, unsigned long long y){ 229 + bool read_plane_xz(T * p, size_t y){
228 return binary<T>::read_plane_2(p, y); 230 return binary<T>::read_plane_2(p, y);
229 } 231 }
230 232
  233 + //given a Y, return ZX slice (transposed such that the spectrum is the leading dimension)
  234 + int read_plane_zx(T* p, size_t y){
  235 + T* temp = (T*) malloc(X() * Z() * sizeof(T)); //allocate space to store the temporary xz plane
  236 + binary<T>::read_plane_2(temp, y); //load the plane from disk
  237 + size_t z, x;
  238 + for(z = 0; z < Z(); z++){
  239 + for(x = 0; x <= z; x++){
  240 + p[x * Z() + z] = temp[z * X() + x]; //copy to the destination frame
  241 + }
  242 + }
  243 + }
  244 +
  245 + //load a frame y into a pre-allocated double-precision array
  246 + int read_plane_xzd(double* f, size_t y){
  247 + size_t XB = X() * Z();
  248 + T* temp = (T*) malloc(XB * sizeof(T)); //create a temporary location to store the plane at current precision
  249 + if(!read_plane_y(temp, y)) return 1; //read the plane in its native format, if it fails return a 1
  250 + for(size_t i = 0; i < XB; i++) f[i] = temp[i]; //convert the plane to a double
  251 + return 0;
  252 + }
  253 +
  254 + //given a Y, return ZX slice (transposed such that the spectrum is the leading dimension)
  255 + int read_plane_zxd(double* p, size_t y){
  256 + T* temp = (T*) malloc(X() * Z() * sizeof(T)); //allocate space to store the temporary xz plane
  257 + binary<T>::read_plane_2(temp, y); //load the plane from disk
  258 + size_t z, x;
  259 + for(z = 0; z < Z(); z++){
  260 + for(x = 0; x < X(); x++){
  261 + p[x * Z() + z] = (double)temp[z * X() + x]; //copy to the destination frame
  262 + }
  263 + }
  264 + return 0;
  265 + }
  266 +
231 267
232 /// Perform baseline correction given a list of baseline points and stores the result in a new BSQ file. 268 /// Perform baseline correction given a list of baseline points and stores the result in a new BSQ file.
233 269
@@ -268,7 +304,7 @@ public: @@ -268,7 +304,7 @@ public:
268 for (unsigned long long k =0; k < Y(); k++) 304 for (unsigned long long k =0; k < Y(); k++)
269 { 305 {
270 //get the current y slice 306 //get the current y slice
271 - read_plane_y(c, k); 307 + read_plane_xz(c, k);
272 308
273 //initialize lownum, highnum, low, high 309 //initialize lownum, highnum, low, high
274 ai = w[0]; 310 ai = w[0];
@@ -369,7 +405,7 @@ public: @@ -369,7 +405,7 @@ public:
369 405
370 for(unsigned long long j = 0; j < Y(); j++) 406 for(unsigned long long j = 0; j < Y(); j++)
371 { 407 {
372 - read_plane_y(c, j); 408 + read_plane_xz(c, j);
373 for(unsigned long long i = 0; i < B; i++) 409 for(unsigned long long i = 0; i < B; i++)
374 { 410 {
375 for(unsigned long long m = 0; m < X(); m++) 411 for(unsigned long long m = 0; m < X(); m++)
@@ -426,6 +462,11 @@ public: @@ -426,6 +462,11 @@ public:
426 }*/ 462 }*/
427 } 463 }
428 464
  465 + bool select(std::string outfile, std::vector<double> bandlist, unsigned char* mask = NULL, bool PROGRESS = NULL) {
  466 + std::cout << "ERROR: select() not implemented for BIL" << std::endl;
  467 + exit(1);
  468 + }
  469 +
429 /// Convert the current BIL file to a BSQ file with the specified file name. 470 /// Convert the current BIL file to a BSQ file with the specified file name.
430 471
431 /// @param outname is the name of the output BSQ file to be saved to disk. 472 /// @param outname is the name of the output BSQ file to be saved to disk.
@@ -469,7 +510,7 @@ public: @@ -469,7 +510,7 @@ public:
469 510
470 for ( unsigned long long i = 0; i < Y(); i++) 511 for ( unsigned long long i = 0; i < Y(); i++)
471 { 512 {
472 - read_plane_y(p, i); 513 + read_plane_xz(p, i);
473 for ( unsigned long long k = 0; k < Z(); k++) 514 for ( unsigned long long k = 0; k < Z(); k++)
474 { 515 {
475 unsigned long long ks = k * X(); 516 unsigned long long ks = k * X();
@@ -863,7 +904,7 @@ public: @@ -863,7 +904,7 @@ public:
863 904
864 for (unsigned long long i = 0; i < Y(); i++) //for each value in Y() (BIP should be X) 905 for (unsigned long long i = 0; i < Y(); i++) //for each value in Y() (BIP should be X)
865 { 906 {
866 - read_plane_y(temp, i); //retrieve an ZX slice, stored in temp 907 + read_plane_xz(temp, i); //retrieve an ZX slice, stored in temp
867 for ( unsigned long long j = 0; j < Z(); j++) //for each Z() (Y) 908 for ( unsigned long long j = 0; j < Z(); j++) //for each Z() (Y)
868 { 909 {
869 for (unsigned long long k = 0; k < X(); k++) //for each band 910 for (unsigned long long k = 0; k < X(); k++) //for each band
@@ -933,7 +974,7 @@ public: @@ -933,7 +974,7 @@ public:
933 //for each slice along the y axis 974 //for each slice along the y axis
934 for (unsigned long long y = 0; y < Y(); y++) //Select a page by choosing Y coordinate, Y() 975 for (unsigned long long y = 0; y < Y(); y++) //Select a page by choosing Y coordinate, Y()
935 { 976 {
936 - read_plane_y(slice, y); //retrieve an ZX page, store in "slice" 977 + read_plane_xz(slice, y); //retrieve an ZX page, store in "slice"
937 978
938 //for each sample along X 979 //for each sample along X
939 for (unsigned long long x = 0; x < X(); x++) //Select a pixel by choosing X coordinate in the page, X() 980 for (unsigned long long x = 0; x < X(); x++) //Select a pixel by choosing X coordinate in the page, X()
@@ -1004,7 +1045,7 @@ public: @@ -1004,7 +1045,7 @@ public:
1004 1045
1005 double x; //create a register to store the pixel value 1046 double x; //create a register to store the pixel value
1006 for (unsigned long long k = 0; k < Y(); k++){ 1047 for (unsigned long long k = 0; k < Y(); k++){
1007 - read_plane_y(temp, k); 1048 + read_plane_xz(temp, k);
1008 unsigned long long kx = k * X(); 1049 unsigned long long kx = k * X();
1009 for (unsigned long long i = 0; i < X(); i++){ 1050 for (unsigned long long i = 0; i < X(); i++){
1010 if (mask == NULL || mask[kx + i] != 0){ 1051 if (mask == NULL || mask[kx + i] != 0){
@@ -1025,13 +1066,103 @@ public: @@ -1025,13 +1066,103 @@ public:
1025 return true; 1066 return true;
1026 } 1067 }
1027 1068
  1069 + int co_matrix_cublas(double* co, double* avg, unsigned char *mask, bool PROGRESS = false){
  1070 + cublasStatus_t stat;
  1071 + cublasHandle_t handle;
  1072 +
  1073 + progress = 0; //initialize the progress to zero (0)
  1074 + size_t XY = X() * Y(); //calculate the number of elements in a band image
  1075 + size_t XB = X() * Z();
  1076 + size_t B = Z(); //calculate the number of spectral elements
  1077 +
  1078 + double* F = (double*)malloc(sizeof(double) * B * X()); //allocate space for the frame that will be pulled from the file
  1079 + double* F_dev;
  1080 + HANDLE_ERROR(cudaMalloc(&F_dev, X() * B * sizeof(double))); //allocate space for the frame on the GPU
  1081 + double* s_dev; //declare a device pointer that will store the spectrum on the GPU
  1082 + double* A_dev; //declare a device pointer that will store the covariance matrix on the GPU
  1083 + double* avg_dev; //declare a device pointer that will store the average spectrum
  1084 + HANDLE_ERROR(cudaMalloc(&s_dev, B * sizeof(double))); //allocate space on the CUDA device for a spectrum
  1085 + HANDLE_ERROR(cudaMalloc(&A_dev, B * B * sizeof(double))); //allocate space on the CUDA device for the covariance matrix
  1086 + HANDLE_ERROR(cudaMemset(A_dev, 0, B * B * sizeof(double))); //initialize the covariance matrix to zero (0)
  1087 + HANDLE_ERROR(cudaMalloc(&avg_dev, XB * sizeof(double))); //allocate space on the CUDA device for the average spectrum
  1088 + for(size_t x = 0; x < X(); x++) //make multiple copies of the average spectrum in order to build a matrix
  1089 + HANDLE_ERROR(cudaMemcpy(&avg_dev[x * B], avg, B * sizeof(double), cudaMemcpyHostToDevice));
  1090 + //stat = cublasSetVector((int)B, sizeof(double), avg, 1, avg_dev, 1); //copy the average spectrum to the CUDA device
  1091 +
  1092 + double ger_alpha = 1.0/(double)XY; //scale the outer product by the inverse of the number of samples (mean outer product)
  1093 + double axpy_alpha = -1; //multiplication factor for the average spectrum (in order to perform a subtraction)
  1094 +
  1095 + CUBLAS_HANDLE_ERROR(stat = cublasCreate(&handle)); //create a cuBLAS instance
  1096 + if (stat != CUBLAS_STATUS_SUCCESS) return 1; //test the cuBLAS instance to make sure it is valid
  1097 +
  1098 + else std::cout<<"Using cuBLAS to calculate the mean covariance matrix..."<<std::endl;
  1099 + double beta = 1.0;
  1100 + size_t x, y;
  1101 + for(y = 0; y < Y(); y++){ //for each line
  1102 + read_plane_zxd(F, y); //read a frame from the file
  1103 + HANDLE_ERROR(cudaMemcpy(F_dev, F, XB * sizeof(double), cudaMemcpyHostToDevice)); //copy the frame to the GPU
  1104 + CUBLAS_HANDLE_ERROR(cublasDgeam(handle, CUBLAS_OP_N, CUBLAS_OP_N, (int)B, (int)X(), &axpy_alpha, avg_dev, (int)B, &beta, F_dev, (int)B, F_dev, (int)B));//subtract the mean spectrum
  1105 +
  1106 + for(x = 0; x < X(); x++)
  1107 + CUBLAS_HANDLE_ERROR(cublasDsyr(handle, CUBLAS_FILL_MODE_UPPER, (int)B, &ger_alpha, &F_dev[x*B], 1, A_dev, (int)B)); //perform an outer product
  1108 + if(PROGRESS) progress = (double)(y + 1) / Y() * 100;
  1109 + }
  1110 +
  1111 + cublasGetMatrix((int)B, (int)B, sizeof(double), A_dev, (int)B, co, (int)B); //copy the result from the GPU to the CPU
  1112 +
  1113 + cudaFree(A_dev); //clean up allocated device memory
  1114 + cudaFree(s_dev);
  1115 + cudaFree(avg_dev);
  1116 +
  1117 + for(unsigned long long i = 0; i < B; i++){ //copy the upper triangular portion to the lower triangular portion
  1118 + for(unsigned long long j = i+1; j < B; j++){
  1119 + co[B * i + j] = co[B * j + i];
  1120 + }
  1121 + }
  1122 +
  1123 + return 0;
  1124 +
  1125 +
  1126 +
  1127 + }
  1128 +
  1129 +
1028 /// Calculate the covariance matrix for all masked pixels in the image. 1130 /// Calculate the covariance matrix for all masked pixels in the image.
1029 1131
1030 /// @param co is a pointer to pre-allocated memory of size [B * B] that stores the resulting covariance matrix 1132 /// @param co is a pointer to pre-allocated memory of size [B * B] that stores the resulting covariance matrix
1031 /// @param avg is a pointer to memory of size B that stores the average spectrum 1133 /// @param avg is a pointer to memory of size B that stores the average spectrum
1032 /// @param mask is a pointer to memory of size [X * Y] that stores the mask value at each pixel location 1134 /// @param mask is a pointer to memory of size [X * Y] that stores the mask value at each pixel location
1033 - bool co_matrix(double* co, double* avg, unsigned char *mask, bool PROGRESS = false){ 1135 + bool co_matrix(double* co, double* avg, unsigned char *mask, int cuda_device = 0, bool PROGRESS = false){
1034 progress = 0; 1136 progress = 0;
  1137 +
  1138 + if (cuda_device >= 0) { //if a CUDA device is specified
  1139 + int dev_count;
  1140 + HANDLE_ERROR(cudaGetDeviceCount(&dev_count)); //get the number of CUDA devices
  1141 + std::cout << "Number of CUDA devices: " << dev_count << std::endl; //output the number of CUDA devices
  1142 + cudaDeviceProp prop;
  1143 + //int best_device_id = 0; //stores the best CUDA device
  1144 + //float best_device_cc = 0.0f; //stores the compute capability of the best device
  1145 + std::cout << "CUDA devices----" << std::endl;
  1146 + for (int d = 0; d < dev_count; d++) { //for each CUDA device
  1147 + cudaGetDeviceProperties(&prop, d); //get the property of the first device
  1148 + //float cc = prop.major + prop.minor / 10.0f; //calculate the compute capability
  1149 + std::cout << d << ": [" << prop.major << "." << prop.minor << "] " << prop.name << std::endl; //display the device information
  1150 + //if(cc > best_device_cc){
  1151 + // best_device_cc = cc; //if this is better than the previous device, use it
  1152 + // best_device_id = d;
  1153 + //}
  1154 + }
  1155 + if (dev_count > 0 && dev_count > cuda_device) { //if the first device is not an emulator
  1156 + cudaGetDeviceProperties(&prop, cuda_device); //get the property of the requested CUDA device
  1157 + if (prop.major != 9999) {
  1158 + std::cout << "Using device " << cuda_device << std::endl;
  1159 + HANDLE_ERROR(cudaSetDevice(cuda_device));
  1160 + int status = co_matrix_cublas(co, avg, mask, PROGRESS); //use cuBLAS to calculate the covariance matrix
  1161 + if (status == 0) return true; //if the cuBLAS function returned correctly, we're done
  1162 + }
  1163 + }
  1164 + }
  1165 +
1035 //memory allocation 1166 //memory allocation
1036 unsigned long long xy = X() * Y(); 1167 unsigned long long xy = X() * Y();
1037 unsigned long long B = Z(); 1168 unsigned long long B = Z();
@@ -1089,9 +1220,9 @@ public: @@ -1089,9 +1220,9 @@ public:
1089 bool PROGRESS = false){ 1220 bool PROGRESS = false){
1090 1221
1091 //calculate the new image parameters 1222 //calculate the new image parameters
1092 - unsigned long long samples = x1 - x0;  
1093 - unsigned long long lines = y1 - y0;  
1094 - unsigned long long bands = b1 - b0; 1223 + unsigned long long samples = x1 - x0 + 1;
  1224 + unsigned long long lines = y1 - y0 + 1;
  1225 + unsigned long long bands = b1 - b0 + 1;
1095 1226
1096 //calculate the size of a line 1227 //calculate the size of a line
1097 unsigned long long L = samples * sizeof(T); 1228 unsigned long long L = samples * sizeof(T);
@@ -1107,19 +1238,19 @@ public: @@ -1107,19 +1238,19 @@ public:
1107 unsigned long long jumpb = (X() - samples) * sizeof(T); 1238 unsigned long long jumpb = (X() - samples) * sizeof(T);
1108 1239
1109 //distance needed to jump from the previous line of the last band to the next line of the first band 1240 //distance needed to jump from the previous line of the last band to the next line of the first band
1110 - unsigned long long longjump = ((Z() - b1) * X() + b0 * X()) * sizeof(T); 1241 + unsigned long long longjump = ((Z() - bands) * X()) * sizeof(T);
1111 1242
1112 //set the start position for the cropped region 1243 //set the start position for the cropped region
1113 file.seekg((y0 * X() * Z() + b0 * X() + x0) * sizeof(T), std::ios::beg); 1244 file.seekg((y0 * X() * Z() + b0 * X() + x0) * sizeof(T), std::ios::beg);
1114 1245
1115 for (unsigned long long x = 0; x < lines; x++) 1246 for (unsigned long long x = 0; x < lines; x++)
1116 { 1247 {
1117 - for (unsigned long long z = b0; z < b1; z++) 1248 + for (unsigned long long z = b0; z <= b1; z++)
1118 { 1249 {
1119 file.read((char *)(temp + z * samples), sizeof(T) * samples); 1250 file.read((char *)(temp + z * samples), sizeof(T) * samples);
1120 file.seekg(jumpb, std::ios::cur); //go to the next band 1251 file.seekg(jumpb, std::ios::cur); //go to the next band
1121 1252
1122 - if(PROGRESS) progress = (double)(x * Z() + z+1) / (lines * Z()) * 100; 1253 + if(PROGRESS) progress = (double)(x * (b1 - b0 + 1) + z + 1) / (lines * (b1 - b0 + 1)) * 100;
1123 } 1254 }
1124 1255
1125 //write slice data into target file 1256 //write slice data into target file
@@ -1211,6 +1342,27 @@ public: @@ -1211,6 +1342,27 @@ public:
1211 } 1342 }
1212 } 1343 }
1213 1344
  1345 + ///Append two files together along the band dimension
  1346 + void append(std::string outfile, bil<T>* C, bool PROGRESS = false) {
  1347 + std::ofstream out(outfile.c_str(), std::ios::binary); //open the output file for writing
  1348 + file.seekg(0, std::ios::beg); //move to the beginning of both files
  1349 + C->file.seekg(0, std::ios::beg);
  1350 + size_t a_bytes = X() * Z() * sizeof(T); //calculate the number of bytes in a single plane of this file
  1351 + size_t b_bytes = C->X() * C->Z() * sizeof(T); //calculate the number of bytes in a single plane of the appending file
  1352 + T* a = (T*)malloc(a_bytes); //allocate space for a plane of the current file
  1353 + T* b = (T*)malloc(b_bytes); //allocate space for a plane of the appended file
  1354 + if (PROGRESS) progress = 0;
  1355 + for (size_t y = 0; y < Y(); y++) {
  1356 + read_plane_xz(a, y); //read a plane from the current file
  1357 + out.write((char*)a, a_bytes); //write the plane to disk
  1358 + C->read_plane_xz(b, y); //read a plane from the appending file
  1359 + out.write((char*)b, b_bytes);
  1360 + if (PROGRESS) progress = (double)(y + 1) / (double)(Y()) * 100;
  1361 + }
  1362 +
  1363 + out.close();
  1364 + }
  1365 +
1214 /// Convolve the given band range with a kernel specified by a vector of coefficients. 1366 /// Convolve the given band range with a kernel specified by a vector of coefficients.
1215 1367
1216 /// @param outfile is an already open stream to the output file 1368 /// @param outfile is an already open stream to the output file
@@ -1328,7 +1480,7 @@ public: @@ -1328,7 +1480,7 @@ public:
1328 c = (T*)malloc( L ); //allocate space for the slice 1480 c = (T*)malloc( L ); //allocate space for the slice
1329 1481
1330 for(unsigned long long j = 0; j < Y(); j++){ //for each line 1482 for(unsigned long long j = 0; j < Y(); j++){ //for each line
1331 - read_plane_y(c, j); //load the line into memory 1483 + read_plane_xz(c, j); //load the line into memory
1332 for(unsigned long long i = 0; i < B; i++){ //for each band 1484 for(unsigned long long i = 0; i < B; i++){ //for each band
1333 for(unsigned long long m = 0; m < X(); m++){ //for each sample 1485 for(unsigned long long m = 0; m < X(); m++){ //for each sample
1334 if( mask == NULL && mask[m + j * X()] ) //if the pixel is masked 1486 if( mask == NULL && mask[m + j * X()] ) //if the pixel is masked
@@ -1358,7 +1510,7 @@ public: @@ -1358,7 +1510,7 @@ public:
1358 c = (T*)malloc( L ); //allocate space for the slice 1510 c = (T*)malloc( L ); //allocate space for the slice
1359 1511
1360 for(unsigned long long j = 0; j < Y(); j++){ //for each line 1512 for(unsigned long long j = 0; j < Y(); j++){ //for each line
1361 - read_plane_y(c, j); //load the line into memory 1513 + read_plane_xz(c, j); //load the line into memory
1362 for(unsigned long long i = 0; i < B; i++){ //for each band 1514 for(unsigned long long i = 0; i < B; i++){ //for each band
1363 for(unsigned long long m = 0; m < X(); m++){ //for each sample 1515 for(unsigned long long m = 0; m < X(); m++){ //for each sample
1364 if( mask == NULL && mask[m + j * X()] ) //if the pixel is masked 1516 if( mask == NULL && mask[m + j * X()] ) //if the pixel is masked
stim/envi/binary.h
@@ -3,8 +3,8 @@ @@ -3,8 +3,8 @@
3 #ifndef RTS_BINARY_H 3 #ifndef RTS_BINARY_H
4 #define RTS_BINARY_H 4 #define RTS_BINARY_H
5 5
6 -#include "../envi/envi_header.h"  
7 -#include "../math/vector.h" 6 +#include <stim/envi/envi_header.h>
  7 +#include <stim/math/vector.h>
8 #include <fstream> 8 #include <fstream>
9 #include <sys/stat.h> 9 #include <sys/stat.h>
10 #include <cstring> 10 #include <cstring>
@@ -134,73 +134,6 @@ public: @@ -134,73 +134,6 @@ public:
134 } 134 }
135 } 135 }
136 } 136 }
137 -  
138 - /*// this function updates the optimizer, given the number of bytes processed in an interval and time spent processing  
139 - size_t update(size_t bytes_processed, size_t ms_spent){  
140 - interval_B += bytes_processed; //increment the number of bytes processed  
141 - interval_ms += ms_spent; //increment the number of milliseconds spent processing  
142 -  
143 - //if we have sufficient information to evaluate the optimization function at this point  
144 - if(interval_ms >= window_ms){ //if sufficient time has passed to get a reliable Bps measurement  
145 - size_t new_Bps = interval_B / interval_ms; //calculate the current Bps  
146 -  
147 - if(sample_step){ //if this is a sample step, collect the information for Bps = f(n0)  
148 - Bps = new_Bps; //set the Bps to the evaluated value  
149 - n[1] = n[0] - dn; //reduce the batch size by one delta to take a second sample  
150 - if(n[1] == 0){ //if the resulting batch size is zero  
151 - n[1] = 2*dn; //we're at the left edge: set the new sample point to 2*dn  
152 - }  
153 -  
154 - interval_B = interval_ms = 0; //start a new interval at the new sample point  
155 - sample_step = false; //next step will calculate the new batch size via optimization  
156 - return n[1]; //return the new batch size  
157 - }  
158 - else{ //if we have sufficient information to evaluate the derivative and optimize  
159 - double f = (double)new_Bps; //we have evaluated the function at this location  
160 - double fprime;  
161 - if(n[1] < n[0] ){ //if the new point is less than the previous point (usually the case)  
162 - fprime = (double)(Bps - new_Bps) / (double)dn; //calculate the forward difference  
163 - }  
164 - else{ //if the new point is larger (only happens at the minimum limit)  
165 - fprime = (double)(new_Bps - Bps) / (double)dn; //calculate the backward difference  
166 - }  
167 - size_t bestn = n[1] - (size_t)(f / fprime); //calculate the best value for B using Newton's method  
168 - n[0] = round_limit( (size_t)bestn ); //set the new dependent point  
169 - sample_step = true; //the next step will be a sample step  
170 - }  
171 -  
172 - }  
173 - if(sample_step) return n[0];  
174 - return n[1]; //insufficient information, keep the same batch size  
175 - }*/  
176 -  
177 - /*size_t update(size_t bytes_processed, size_t ms_spent){  
178 - interval_B += bytes_processed; //increment the number of bytes processed  
179 - interval_ms += ms_spent; //increment the number of milliseconds spent processing  
180 -  
181 - //if( Bps[0] == 0 ){ //if the left boundary hasn't been processed  
182 -  
183 -  
184 - //if we have sufficient information to evaluate the optimization function at this point  
185 - if(interval_ms >= window_ms){  
186 - size_t new_Bps = interval_B / interval_ms; //calculate the current Bps  
187 -  
188 - if(Bps[0] == 0) //if the left interval Bps hasn't been calculated  
189 - Bps[0] = interval_B / interval_ms; //that is the interval being processed  
190 - else  
191 - Bps[1] = interval_B / interval_ms; //otherwise the right interval is being processed  
192 -  
193 - if(Bps[0] != 0 && Bps[1] != 0){ //if both intervals have been processed  
194 -  
195 -  
196 - }  
197 - }*/  
198 -  
199 - /*size_t update(size_t bytes_processed, size_t ms_spent, size_t& data_rate, bool VERBOSE){  
200 - size_t time = update(bytes_processed, ms_spent, VERBOSE);  
201 - data_rate = Bps[0];  
202 - return time;  
203 - }*/  
204 }; 137 };
205 138
206 /** This class manages the streaming of large multidimensional binary files. 139 /** This class manages the streaming of large multidimensional binary files.
@@ -210,6 +143,8 @@ public: @@ -210,6 +143,8 @@ public:
210 * @param T is the data type used to store data to disk (generally float or double) 143 * @param T is the data type used to store data to disk (generally float or double)
211 * @param D is the dimension of the data (default 3) 144 * @param D is the dimension of the data (default 3)
212 */ 145 */
  146 +
  147 +enum iotype {io_in, io_out};
213 template< typename T, unsigned int D = 3 > 148 template< typename T, unsigned int D = 3 >
214 class binary{ 149 class binary{
215 150
@@ -272,19 +207,21 @@ protected: @@ -272,19 +207,21 @@ protected:
272 /// Private helper file that opens a specified binary file. 207 /// Private helper file that opens a specified binary file.
273 208
274 /// @param filename is the name of the binary file to stream 209 /// @param filename is the name of the binary file to stream
275 - bool open_file(std::string filename){ 210 + bool open_file(std::string filename, stim::iotype io = io_in){
276 //open the file as binary for reading and writing 211 //open the file as binary for reading and writing
277 - file.open(filename.c_str(), std::ios::in | std::ios::out | std::ios::binary);  
278 -  
279 - //if the file isn't open, the user may only have read access  
280 - if(!file.is_open()){  
281 - std::cout<<"class STIM::BINARY - failed to open file, trying for read only"<<std::endl; 212 + if(io == io_in)
282 file.open(filename.c_str(), std::ios::in | std::ios::binary); 213 file.open(filename.c_str(), std::ios::in | std::ios::binary);
283 - if(!file.is_open()){  
284 - std::cout<<" still unable to load the file"<<std::endl; 214 + else if (io == io_out) {
  215 + file.open(filename.c_str(), std::ios::out | std::ios::binary);
  216 + if (!file.is_open()) {
  217 + std::cout << "stim::binary ERROR - unable to open file for writing: " << filename << std::endl;
285 return false; 218 return false;
286 } 219 }
287 } 220 }
  221 + else {
  222 + std::cout << "stim::binary ERROR - unrecognized IO format" << std::endl;
  223 + return false;
  224 + }
288 225
289 //if the file is successful 226 //if the file is successful
290 if(file){ 227 if(file){
@@ -342,20 +279,24 @@ public: @@ -342,20 +279,24 @@ public:
342 /// @param filename is the name of the binary file 279 /// @param filename is the name of the binary file
343 /// @param r is a STIM vector specifying the size of the binary file along each dimension 280 /// @param r is a STIM vector specifying the size of the binary file along each dimension
344 /// @param h is the length (in bytes) of any header file (default zero) 281 /// @param h is the length (in bytes) of any header file (default zero)
345 - bool open(std::string filename, vec<unsigned long long> r, unsigned long long h = 0){ 282 + bool open(std::string filename, vec<unsigned long long> r, unsigned long long h = 0, stim::iotype io = stim::io_in){
346 283
347 for(unsigned long long i = 0; i < D; i++) //set the dimensions of the binary file object 284 for(unsigned long long i = 0; i < D; i++) //set the dimensions of the binary file object
348 R[i] = r[i]; 285 R[i] = r[i];
349 286
350 header = h; //save the header size 287 header = h; //save the header size
351 288
352 - if(!open_file(filename)) return false; //open the binary file 289 + if(!open_file(filename), io) return false; //open the binary file
353 290
354 //reset(); 291 //reset();
355 292
356 return test_file_size(); 293 return test_file_size();
357 } 294 }
358 295
  296 + bool is_open() {
  297 + return file.is_open();
  298 + }
  299 +
359 /// Creates a new binary file for streaming 300 /// Creates a new binary file for streaming
360 301
361 /// @param filename is the name of the binary file to be created 302 /// @param filename is the name of the binary file to be created
@@ -605,7 +546,7 @@ public: @@ -605,7 +546,7 @@ public:
605 size_t size_bytes = sx * sy * sz * sizeof(T); //size of the block to read in bytes 546 size_t size_bytes = sx * sy * sz * sizeof(T); //size of the block to read in bytes
606 547
607 size_t start = z * R[0] * R[1] + y * R[0] + x; //calculate the start postion 548 size_t start = z * R[0] * R[1] + y * R[0] + x; //calculate the start postion
608 - size_t start_bytes = start * sizeof(T); //start position in bytes 549 + //size_t start_bytes = start * sizeof(T); //start position in bytes
609 file.seekg(start * sizeof(T), std::ios::beg); //seek to the start position 550 file.seekg(start * sizeof(T), std::ios::beg); //seek to the start position
610 551
611 552
@@ -5,13 +5,17 @@ @@ -5,13 +5,17 @@
5 #include "../envi/bil.h" 5 #include "../envi/bil.h"
6 #include "../envi/hsi.h" 6 #include "../envi/hsi.h"
7 #include <cstring> 7 #include <cstring>
  8 +#include <complex>
8 #include <utility> 9 #include <utility>
  10 +#include <algorithm>
9 11
10 //CUDA 12 //CUDA
11 -#ifdef CUDA_FOUND  
12 - #include <cuda_runtime.h>  
13 - #include "cublas_v2.h"  
14 -#endif 13 +//#ifdef CUDA_FOUND
  14 +#include <stim/cuda/cudatools/error.h>
  15 +#include <cuda_runtime.h>
  16 +#include "cublas_v2.h"
  17 +#include "cufft.h"
  18 +//#endif
15 19
16 namespace stim{ 20 namespace stim{
17 21
@@ -62,14 +66,15 @@ public: @@ -62,14 +66,15 @@ public:
62 unsigned long long Y, 66 unsigned long long Y,
63 unsigned long long B, 67 unsigned long long B,
64 unsigned long long header_offset, 68 unsigned long long header_offset,
65 - std::vector<double> wavelengths){ 69 + std::vector<double> wavelengths,
  70 + stim::iotype io = stim::io_in){
66 71
67 //copy the wavelengths to the BSQ file structure 72 //copy the wavelengths to the BSQ file structure
68 w = wavelengths; 73 w = wavelengths;
69 //copy the offset to the structure 74 //copy the offset to the structure
70 offset = header_offset; 75 offset = header_offset;
71 76
72 - return open(filename, vec<unsigned long long>(B, X, Y), header_offset); 77 + return open(filename, vec<unsigned long long>(B, X, Y), header_offset, io);
73 78
74 } 79 }
75 80
@@ -257,7 +262,7 @@ public: @@ -257,7 +262,7 @@ public:
257 } 262 }
258 263
259 //given a Y ,return a ZX slice 264 //given a Y ,return a ZX slice
260 - bool read_plane_y(T * p, unsigned long long y){ 265 + bool read_plane_y(T * p, size_t y){
261 return binary<T>::read_plane_2(p, y); 266 return binary<T>::read_plane_2(p, y);
262 } 267 }
263 268
@@ -388,6 +393,50 @@ public: @@ -388,6 +393,50 @@ public:
388 } 393 }
389 } 394 }
390 395
  396 + /// This function loads a specified set of bands and saves them into a new output file
  397 + bool select(std::string outfile, std::vector<double> bandlist, unsigned char* mask = NULL, bool PROGRESS = false) {
  398 + std::ofstream target(outfile.c_str(), std::ios::binary); //open the target binary file
  399 + if (!target) {
  400 + std::cout << "ERROR opening output file: " << outfile << std::endl;
  401 + return false;
  402 + }
  403 + file.seekg(0, std::ios::beg); //move the pointer to the current file to the beginning
  404 +
  405 + size_t B = Z(); //number of spectral components
  406 + size_t XY = X() * Y(); //calculate the number of pixels
  407 + size_t Bout = bandlist.size();
  408 + size_t in_bytes = B * sizeof(T); //number of bytes in a spectrum
  409 + size_t out_bytes = Bout * sizeof(T); //number of bytes in an output spectrum
  410 +
  411 + T* in = (T*)malloc(in_bytes); //allocate space for the input spectrum
  412 + T* out = (T*)malloc(out_bytes); //allocate space for the output spectrum
  413 +
  414 + double wc; //register to store the desired wavelength
  415 + double w0, w1; //registers to store the wavelengths surrounding the given band
  416 + size_t b0, b1; //indices of the bands surrounding the specified wavelength
  417 + for (size_t xy = 0; xy < XY; xy++) { //for each pixel
  418 + //memset(out, 0, out_bytes); //set the spectrum to zero
  419 + if (mask == NULL || mask[xy]) { //if the pixel is masked
  420 + file.read((char*)in, in_bytes); //read an input spectrum
  421 + for (size_t b = 0; b < Bout; b++) { //for each band
  422 + wc = bandlist[b]; //set the desired wavelength
  423 + hsi<T>::band_bounds(wc, b0, b1); //get the surrounding bands
  424 + w0 = w[b0]; //get the wavelength for the lower band
  425 + w1 = w[b1]; //get the wavelength for the higher band
  426 + out[b] = hsi<T>::lerp(wc, in[b0], w0, in[b1], w1); //interpolate the spectral values to get the desired output value
  427 + }
  428 + }
  429 + else
  430 + file.seekg(Bout, std::ios::cur); //otherwise skip a spectrum
  431 + target.write((char*)out, out_bytes); //output the normalized spectrum
  432 + if (PROGRESS) progress = (double)(xy + 1) / (double)XY * 100; //update the progress
  433 + }
  434 +
  435 + free(in);
  436 + free(out);
  437 + return true;
  438 + }
  439 +
391 440
392 /// Convert the current BIP file to a BIL file with the specified file name. 441 /// Convert the current BIP file to a BIL file with the specified file name.
393 442
@@ -954,7 +1003,7 @@ public: @@ -954,7 +1003,7 @@ public:
954 1003
955 /// @param p is a pointer to pre-allocated memory of size [B * sizeof(T)] that stores the mean spectrum 1004 /// @param p is a pointer to pre-allocated memory of size [B * sizeof(T)] that stores the mean spectrum
956 /// @param mask is a pointer to memory of size [X * Y] that stores the mask value at each pixel location 1005 /// @param mask is a pointer to memory of size [X * Y] that stores the mask value at each pixel location
957 - bool mean_spectrum(double* m, double* std, unsigned char* mask = NULL, bool PROGRESS = false){ 1006 + bool mean_spectrum(double* m, double* std = NULL, unsigned char* mask = NULL, bool PROGRESS = false){
958 unsigned long long XY = X() * Y(); //calculate the total number of pixels in the HSI 1007 unsigned long long XY = X() * Y(); //calculate the total number of pixels in the HSI
959 T* temp = (T*)malloc(sizeof(T) * Z()); //allocate space for the current spectrum to be read 1008 T* temp = (T*)malloc(sizeof(T) * Z()); //allocate space for the current spectrum to be read
960 memset(m, 0, Z() * sizeof(double)); //set the mean spectrum to zero 1009 memset(m, 0, Z() * sizeof(double)); //set the mean spectrum to zero
@@ -976,22 +1025,23 @@ public: @@ -976,22 +1025,23 @@ public:
976 } 1025 }
977 1026
978 //calculate the standard deviation 1027 //calculate the standard deviation
979 - for(size_t i = 0; i < Z(); i++)  
980 - std[i] = sqrt(e_x2[i] - m[i] * m[i]); 1028 + if (std != NULL) {
  1029 + for (size_t i = 0; i < Z(); i++)
  1030 + std[i] = sqrt(e_x2[i] - m[i] * m[i]);
  1031 + }
981 1032
982 free(temp); 1033 free(temp);
983 return true; 1034 return true;
984 } 1035 }
985 -#ifdef CUDA_FOUND 1036 +//#ifdef CUDA_FOUND
986 /// Calculate the covariance matrix for masked pixels using cuBLAS 1037 /// Calculate the covariance matrix for masked pixels using cuBLAS
987 /// Note that cuBLAS only supports integer-sized arrays, so there may be issues with large spectra 1038 /// Note that cuBLAS only supports integer-sized arrays, so there may be issues with large spectra
988 - bool co_matrix_cublas(double* co, double* avg, unsigned char *mask, bool PROGRESS = false){ 1039 + int co_matrix_cublas(double* co, double* avg, unsigned char *mask, bool PROGRESS = false){
989 1040
990 cudaError_t cudaStat; 1041 cudaError_t cudaStat;
991 cublasStatus_t stat; 1042 cublasStatus_t stat;
992 cublasHandle_t handle; 1043 cublasHandle_t handle;
993 1044
994 - progress = 0; //initialize the progress to zero (0)  
995 unsigned long long XY = X() * Y(); //calculate the number of elements in a band image 1045 unsigned long long XY = X() * Y(); //calculate the number of elements in a band image
996 unsigned long long B = Z(); //calculate the number of spectral elements 1046 unsigned long long B = Z(); //calculate the number of spectral elements
997 1047
@@ -1009,10 +1059,9 @@ public: @@ -1009,10 +1059,9 @@ public:
1009 double axpy_alpha = -1; //multiplication factor for the average spectrum (in order to perform a subtraction) 1059 double axpy_alpha = -1; //multiplication factor for the average spectrum (in order to perform a subtraction)
1010 1060
1011 stat = cublasCreate(&handle); //create a cuBLAS instance 1061 stat = cublasCreate(&handle); //create a cuBLAS instance
1012 - if (stat != CUBLAS_STATUS_SUCCESS) { //test the cuBLAS instance to make sure it is valid  
1013 - printf ("CUBLAS initialization failed\n");  
1014 - return EXIT_FAILURE;  
1015 - } 1062 + if (stat != CUBLAS_STATUS_SUCCESS) return 1; //test the cuBLAS instance to make sure it is valid
  1063 +
  1064 + //else std::cout<<"Using cuBLAS to calculate the mean covariance matrix..."<<std::endl;
1016 for (unsigned long long xy = 0; xy < XY; xy++){ //for each pixel 1065 for (unsigned long long xy = 0; xy < XY; xy++){ //for each pixel
1017 if (mask == NULL || mask[xy] != 0){ 1066 if (mask == NULL || mask[xy] != 0){
1018 pixeld(s, xy); //retreive the spectrum at the current xy pixel location 1067 pixeld(s, xy); //retreive the spectrum at the current xy pixel location
@@ -1036,26 +1085,33 @@ public: @@ -1036,26 +1085,33 @@ public:
1036 } 1085 }
1037 } 1086 }
1038 1087
1039 - return true; 1088 + return 0;
1040 } 1089 }
1041 -#endif 1090 +//#endif
1042 1091
1043 /// Calculate the covariance matrix for all masked pixels in the image with 64-bit floating point precision. 1092 /// Calculate the covariance matrix for all masked pixels in the image with 64-bit floating point precision.
1044 1093
1045 /// @param co is a pointer to pre-allocated memory of size [B * B] that stores the resulting covariance matrix 1094 /// @param co is a pointer to pre-allocated memory of size [B * B] that stores the resulting covariance matrix
1046 /// @param avg is a pointer to memory of size B that stores the average spectrum 1095 /// @param avg is a pointer to memory of size B that stores the average spectrum
1047 /// @param mask is a pointer to memory of size [X * Y] that stores the mask value at each pixel location 1096 /// @param mask is a pointer to memory of size [X * Y] that stores the mask value at each pixel location
1048 - bool co_matrix(double* co, double* avg, unsigned char *mask, bool PROGRESS = false){  
1049 -  
1050 -#ifdef CUDA_FOUND  
1051 - int dev_count;  
1052 - cudaGetDeviceCount(&dev_count); //get the number of CUDA devices  
1053 - cudaDeviceProp prop;  
1054 - cudaGetDeviceProperties(&prop, 0); //get the property of the first device  
1055 - if(dev_count > 0 && prop.major != 9999) //if the first device is not an emulator  
1056 - return co_matrix_cublas(co, avg, mask, PROGRESS); //use cuBLAS to calculate the covariance matrix  
1057 -#endif 1097 + bool co_matrix(double* co, double* avg, unsigned char *mask, int cuda_device = 0, bool PROGRESS = false){
1058 progress = 0; 1098 progress = 0;
  1099 +
  1100 + if(cuda_device >= 0){ //if a CUDA device is specified
  1101 + int dev_count;
  1102 + HANDLE_ERROR(cudaGetDeviceCount(&dev_count)); //get the number of CUDA devices
  1103 + if(dev_count > 0 && dev_count > cuda_device){ //if the first device is not an emulator
  1104 + cudaDeviceProp prop;
  1105 + cudaGetDeviceProperties(&prop, cuda_device); //get the property of the requested CUDA device
  1106 + if (prop.major != 9999) {
  1107 + std::cout << "Using CUDA device [" << cuda_device << "] to calculate the mean covariance matrix..."<<std::endl;
  1108 + HANDLE_ERROR(cudaSetDevice(cuda_device));
  1109 + int status = co_matrix_cublas(co, avg, mask, PROGRESS); //use cuBLAS to calculate the covariance matrix
  1110 + if (status == 0) return true; //if the cuBLAS function returned correctly, we're done
  1111 + }
  1112 + } //otherwise continue using the CPU
  1113 + std::cout<<"WARNING: cuBLAS failed, using CPU"<<std::endl;
  1114 + }
1059 //memory allocation 1115 //memory allocation
1060 unsigned long long XY = X() * Y(); 1116 unsigned long long XY = X() * Y();
1061 unsigned long long B = Z(); 1117 unsigned long long B = Z();
@@ -1097,10 +1153,7 @@ public: @@ -1097,10 +1153,7 @@ public:
1097 } 1153 }
1098 1154
1099 1155
1100 -#ifdef CUDA_FOUND  
1101 - /// Calculate the covariance matrix of Noise for masked pixels using cuBLAS  
1102 - /// Note that cuBLAS only supports integer-sized arrays, so there may be issues with large spectra  
1103 - bool coNoise_matrix_cublas(double* coN, double* avg, unsigned char *mask, bool PROGRESS = false){ 1156 + int coNoise_matrix_cublas(double* coN, double* avg, unsigned char *mask, bool PROGRESS = false) {
1104 1157
1105 cudaError_t cudaStat; 1158 cudaError_t cudaStat;
1106 cublasStatus_t stat; 1159 cublasStatus_t stat;
@@ -1113,9 +1166,10 @@ public: @@ -1113,9 +1166,10 @@ public:
1113 double* s = (double*)malloc(sizeof(double) * B); //allocate space for the spectrum that will be pulled from the file 1166 double* s = (double*)malloc(sizeof(double) * B); //allocate space for the spectrum that will be pulled from the file
1114 double* s_dev; //declare a device pointer that will store the spectrum on the GPU 1167 double* s_dev; //declare a device pointer that will store the spectrum on the GPU
1115 1168
1116 - double* s2_dev; // device pointer on the GPU  
1117 - cudaStat = cudaMalloc(&s2_dev, B * sizeof(double)); // allocate space on the CUDA device  
1118 - cudaStat = cudaMemset(s2_dev, 0, B * sizeof(double)); // initialize s2_dev to zero (0) 1169 + double* s2 = (double*)malloc(sizeof(double) * B); //allocate space for the spectrum of second pixel that will be pulled from the file
  1170 + double* s2_dev; // device pointer on the GPU
  1171 + cudaStat = cudaMalloc(&s2_dev, B * sizeof(double)); // allocate space on the CUDA device
  1172 + cudaStat = cudaMemset(s2_dev, 0, B * sizeof(double)); // initialize s2_dev to zero (0)
1119 1173
1120 double* A_dev; //declare a device pointer that will store the covariance matrix on the GPU 1174 double* A_dev; //declare a device pointer that will store the covariance matrix on the GPU
1121 double* avg_dev; //declare a device pointer that will store the average spectrum 1175 double* avg_dev; //declare a device pointer that will store the average spectrum
@@ -1125,28 +1179,32 @@ public: @@ -1125,28 +1179,32 @@ public:
1125 cudaStat = cudaMalloc(&avg_dev, B * sizeof(double)); //allocate space on the CUDA device for the average spectrum 1179 cudaStat = cudaMalloc(&avg_dev, B * sizeof(double)); //allocate space on the CUDA device for the average spectrum
1126 stat = cublasSetVector((int)B, sizeof(double), avg, 1, avg_dev, 1); //copy the average spectrum to the CUDA device 1180 stat = cublasSetVector((int)B, sizeof(double), avg, 1, avg_dev, 1); //copy the average spectrum to the CUDA device
1127 1181
1128 - double ger_alpha = 1.0/(double)XY; //scale the outer product by the inverse of the number of samples (mean outer product) 1182 + double ger_alpha = 1.0 / (double)XY; //scale the outer product by the inverse of the number of samples (mean outer product)
1129 double axpy_alpha = -1; //multiplication factor for the average spectrum (in order to perform a subtraction) 1183 double axpy_alpha = -1; //multiplication factor for the average spectrum (in order to perform a subtraction)
1130 1184
1131 - stat = cublasCreate(&handle); //create a cuBLAS instance  
1132 - if (stat != CUBLAS_STATUS_SUCCESS) { //test the cuBLAS instance to make sure it is valid  
1133 - printf ("CUBLAS initialization failed\n");  
1134 - return EXIT_FAILURE;  
1135 - }  
1136 - for (unsigned long long xy = 0; xy < XY; xy++){ //for each pixel  
1137 - if (mask == NULL || mask[xy] != 0){  
1138 - pixeld(s, xy); //retreive the spectrum at the current xy pixel location 1185 + CUBLAS_HANDLE_ERROR(cublasCreate(&handle)); //create a cuBLAS instance
  1186 + if (stat != CUBLAS_STATUS_SUCCESS) return 1; //test the cuBLAS instance to make sure it is valid
1139 1187
1140 - stat = cublasSetVector((int)B, sizeof(double), s, 1, s_dev, 1); //copy the spectrum from the host to the device 1188 + for (unsigned long long xy = 0; xy < XY; xy++) { //for each pixel
  1189 + if (mask == NULL || mask[xy] != 0) {
  1190 + pixeld(s, xy); //retreive the spectrum at the current xy pixel location
  1191 + if (xy < XY - X()) {
  1192 + pixeld(s2, xy + X()); //retreive the spectrum at the current xy+X pixel location, which is adjacent (bellow) to the pixel at xy location (in y direction)
  1193 + }
  1194 + else {
  1195 + pixeld(s2, xy - X()); //for the last row we consider the the adjacent pixel which is located above pixel xy
  1196 + }
  1197 + stat = cublasSetVector((int)B, sizeof(double), s, 1, s_dev, 1); //copy the spectrum of first pixel from the host to the device
1141 stat = cublasDaxpy(handle, (int)B, &axpy_alpha, avg_dev, 1, s_dev, 1); //subtract the average spectrum 1198 stat = cublasDaxpy(handle, (int)B, &axpy_alpha, avg_dev, 1, s_dev, 1); //subtract the average spectrum
1142 1199
1143 - cudaMemcpy(s2_dev, s_dev + 1 , (B-1) * sizeof(double), cudaMemcpyDeviceToDevice); //copy B-1 elements from shifted source data (s_dev) to device pointer (s2_dev )  
1144 - stat = cublasDaxpy(handle, (int)B, &axpy_alpha, s2_dev, 1, s_dev, 1); //Minimum/Maximum Autocorrelation Factors (MAF) method : subtranct each pixel from adjacent pixel (z direction is choosed to do so , which is almost the same as x or y direction or even average of them ) 1200 + stat = cublasSetVector((int)B, sizeof(double), s2, 1, s2_dev, 1); //copy the spectrum of second pixel from the host to the device
  1201 + stat = cublasDaxpy(handle, (int)B, &axpy_alpha, avg_dev, 1, s2_dev, 1); //subtract the average spectrum
1145 1202
  1203 + stat = cublasDaxpy(handle, (int)B, &axpy_alpha, s2_dev, 1, s_dev, 1); //Minimum/Maximum Autocorrelation Factors (MAF) method : subtranct each pixel from adjacent pixel (in y direction)
1146 1204
1147 stat = cublasDsyr(handle, CUBLAS_FILL_MODE_UPPER, (int)B, &ger_alpha, s_dev, 1, A_dev, (int)B); //calculate the covariance matrix (symmetric outer product) 1205 stat = cublasDsyr(handle, CUBLAS_FILL_MODE_UPPER, (int)B, &ger_alpha, s_dev, 1, A_dev, (int)B); //calculate the covariance matrix (symmetric outer product)
1148 } 1206 }
1149 - if(PROGRESS) progress = (double)(xy+1) / XY * 100; //record the current progress 1207 + if (PROGRESS) progress = (double)(xy + 1) / XY * 100; //record the current progress
1150 1208
1151 } 1209 }
1152 1210
@@ -1157,80 +1215,96 @@ public: @@ -1157,80 +1215,96 @@ public:
1157 cudaFree(s2_dev); 1215 cudaFree(s2_dev);
1158 cudaFree(avg_dev); 1216 cudaFree(avg_dev);
1159 1217
1160 - for(unsigned long long i = 0; i < B; i++){ //copy the upper triangular portion to the lower triangular portion  
1161 - for(unsigned long long j = i+1; j < B; j++){ 1218 + for (unsigned long long i = 0; i < B; i++) { //copy the upper triangular portion to the lower triangular portion
  1219 + for (unsigned long long j = i + 1; j < B; j++) {
1162 coN[B * i + j] = coN[B * j + i]; 1220 coN[B * i + j] = coN[B * j + i];
1163 } 1221 }
1164 } 1222 }
1165 1223
1166 - return true; 1224 + return 0;
1167 } 1225 }
1168 -#endif 1226 + //#endif
1169 1227
1170 /// Calculate the covariance of noise matrix for all masked pixels in the image with 64-bit floating point precision. 1228 /// Calculate the covariance of noise matrix for all masked pixels in the image with 64-bit floating point precision.
1171 1229
1172 /// @param coN is a pointer to pre-allocated memory of size [B * B] that stores the resulting covariance matrix 1230 /// @param coN is a pointer to pre-allocated memory of size [B * B] that stores the resulting covariance matrix
1173 /// @param avg is a pointer to memory of size B that stores the average spectrum 1231 /// @param avg is a pointer to memory of size B that stores the average spectrum
1174 /// @param mask is a pointer to memory of size [X * Y] that stores the mask value at each pixel location 1232 /// @param mask is a pointer to memory of size [X * Y] that stores the mask value at each pixel location
1175 - bool coNoise_matrix(double* coN, double* avg, unsigned char *mask, bool PROGRESS = false){  
1176 -  
1177 -#ifdef CUDA_FOUND  
1178 - int dev_count;  
1179 - cudaGetDeviceCount(&dev_count); //get the number of CUDA devices  
1180 - cudaDeviceProp prop;  
1181 - cudaGetDeviceProperties(&prop, 0); //get the property of the first device  
1182 - if(dev_count > 0 && prop.major != 9999) //if the first device is not an emulator  
1183 - return coNoise_matrix_cublas(coN, avg, mask, PROGRESS); //use cuBLAS to calculate the covariance matrix  
1184 -#endif  
1185 -  
1186 - 1233 + bool coNoise_matrix(double* coN, double* avg, unsigned char *mask, int cuda_device = 0, bool PROGRESS = false) {
  1234 +
  1235 + if (cuda_device >= 0) { //if a CUDA device is specified
  1236 + int dev_count;
  1237 + HANDLE_ERROR(cudaGetDeviceCount(&dev_count)); //get the number of CUDA devices
  1238 + if (dev_count > 0 && dev_count > cuda_device) { //if the first device is not an emulator
  1239 + cudaDeviceProp prop;
  1240 + cudaGetDeviceProperties(&prop, cuda_device); //get the property of the requested CUDA device
  1241 + if (prop.major != 9999) {
  1242 + std::cout << "Using CUDA device [" << cuda_device << "] to calculate the noise covariance matrix..." << std::endl;
  1243 + HANDLE_ERROR(cudaSetDevice(cuda_device));
  1244 + int status = coNoise_matrix_cublas(coN, avg, mask, PROGRESS); //use cuBLAS to calculate the covariance matrix
  1245 + if (status == 0) return true; //if the cuBLAS function returned correctly, we're done
  1246 + }
  1247 + } //otherwise continue using the CPU
  1248 + std::cout << "WARNING: cuBLAS failed, using CPU" << std::endl;
  1249 + }
1187 1250
1188 progress = 0; 1251 progress = 0;
1189 //memory allocation 1252 //memory allocation
1190 unsigned long long XY = X() * Y(); 1253 unsigned long long XY = X() * Y();
1191 unsigned long long B = Z(); 1254 unsigned long long B = Z();
1192 T* temp = (T*)malloc(sizeof(T) * B); 1255 T* temp = (T*)malloc(sizeof(T) * B);
  1256 + T* temp2 = (T*)malloc(sizeof(T) * B);
1193 1257
1194 unsigned long long count = nnz(mask); //count the number of masked pixels 1258 unsigned long long count = nnz(mask); //count the number of masked pixels
1195 1259
1196 - //initialize covariance matrix of noise 1260 + //initialize covariance matrix of noise
1197 memset(coN, 0, B * B * sizeof(double)); 1261 memset(coN, 0, B * B * sizeof(double));
1198 1262
1199 //calculate covariance matrix 1263 //calculate covariance matrix
1200 - double* coN_half = (double*) malloc(B * B * sizeof(double)); //allocate space for a higher-precision intermediate matrix  
1201 - double* temp_precise = (double*) malloc(B * sizeof(double)); 1264 + double* coN_half = (double*)malloc(B * B * sizeof(double)); //allocate space for a higher-precision intermediate matrix
  1265 + double* temp_precise = (double*)malloc(B * sizeof(double));
  1266 + double* temp_precise2 = (double*)malloc(B * sizeof(double));
1202 memset(coN_half, 0, B * B * sizeof(double)); //initialize the high-precision matrix with zeros 1267 memset(coN_half, 0, B * B * sizeof(double)); //initialize the high-precision matrix with zeros
1203 unsigned long long idx; //stores i*B to speed indexing 1268 unsigned long long idx; //stores i*B to speed indexing
1204 - for (unsigned long long xy = 0; xy < XY; xy++){  
1205 - if (mask == NULL || mask[xy] != 0){  
1206 - pixel(temp, xy); //retreive the spectrum at the current xy pixel location  
1207 - for(unsigned long long b = 0; b < B; b++) //subtract the mean from this spectrum and increase the precision 1269 + for (unsigned long long xy = 0; xy < XY; xy++) {
  1270 + if (mask == NULL || mask[xy] != 0) {
  1271 + pixel(temp, xy); //retreive the spectrum at the current xy pixel location
  1272 + if (xy < XY - X()) {
  1273 + pixel(temp2, xy + X()); //retreive the spectrum at the current xy+X pixel location, which is adjacent (bellow) to the pixel at xy location (in y direction)
  1274 + }
  1275 + else {
  1276 + pixel(temp2, xy - X()); //for the last row we consider the the adjacent pixel which is located above pixel xy
  1277 + }
  1278 + for (unsigned long long b = 0; b < B; b++) { //subtract the mean from this spectrum and increase the precision
1208 temp_precise[b] = (double)temp[b] - (double)avg[b]; 1279 temp_precise[b] = (double)temp[b] - (double)avg[b];
  1280 + temp_precise2[b] = (double)temp2[b] - (double)avg[b];
  1281 + }
1209 1282
1210 - for(unsigned long long b2 = 0; b2 < B-1; b2++) //Minimum/Maximum Autocorrelation Factors (MAF) method : subtranct each pixel from adjacent pixel (z direction is choosed to do so , which is almost the same as x or y direction or even average of them )  
1211 - temp_precise[b2] -= temp_precise[b2+1]; 1283 + for (unsigned long long b2 = 0; b2 < B; b2++) //Minimum/Maximum Autocorrelation Factors (MAF) method : subtranct each pixel from adjacent pixel (in y direction)
  1284 + temp_precise[b2] -= temp_precise2[b2];
1212 1285
1213 idx = 0; 1286 idx = 0;
1214 - for (unsigned long long b0 = 0; b0 < B; b0++){ //for each band 1287 + for (unsigned long long b0 = 0; b0 < B; b0++) { //for each band
1215 for (unsigned long long b1 = b0; b1 < B; b1++) 1288 for (unsigned long long b1 = b0; b1 < B; b1++)
1216 coN_half[idx++] += temp_precise[b0] * temp_precise[b1]; 1289 coN_half[idx++] += temp_precise[b0] * temp_precise[b1];
1217 } 1290 }
1218 } 1291 }
1219 - if(PROGRESS) progress = (double)(xy+1) / XY * 100; 1292 + if (PROGRESS) progress = (double)(xy + 1) / XY * 100;
1220 } 1293 }
1221 idx = 0; 1294 idx = 0;
1222 - for (unsigned long long i = 0; i < B; i++){ //copy the precision matrix to both halves of the output matrix  
1223 - for (unsigned long long j = i; j < B; j++){  
1224 - coN[j * B + i] = coN[i * B + j] = coN_half[idx++] / (double) count; 1295 + for (unsigned long long i = 0; i < B; i++) { //copy the precision matrix to both halves of the output matrix
  1296 + for (unsigned long long j = i; j < B; j++) {
  1297 + coN[j * B + i] = coN[i * B + j] = coN_half[idx++] / (double)count;
1225 } 1298 }
1226 } 1299 }
1227 1300
1228 free(temp); 1301 free(temp);
  1302 + free(temp2);
1229 free(temp_precise); 1303 free(temp_precise);
  1304 + free(temp_precise2);
1230 return true; 1305 return true;
1231 } 1306 }
1232 1307
1233 - #ifdef CUDA_FOUND  
1234 /// Project the spectra onto a set of basis functions 1308 /// Project the spectra onto a set of basis functions
1235 /// @param outfile is the name of the new binary output file that will be created 1309 /// @param outfile is the name of the new binary output file that will be created
1236 /// @param center is a spectrum about which the data set will be rotated (ex. when performing mean centering) 1310 /// @param center is a spectrum about which the data set will be rotated (ex. when performing mean centering)
@@ -1239,8 +1313,8 @@ public: @@ -1239,8 +1313,8 @@ public:
1239 /// @param mask is a character mask used to limit processing to valid pixels 1313 /// @param mask is a character mask used to limit processing to valid pixels
1240 bool project_cublas(std::string outfile, double* center, double* basis, unsigned long long M, unsigned char* mask = NULL, bool PROGRESS = false){ 1314 bool project_cublas(std::string outfile, double* center, double* basis, unsigned long long M, unsigned char* mask = NULL, bool PROGRESS = false){
1241 1315
1242 - cudaError_t cudaStat;  
1243 - cublasStatus_t stat; 1316 + //cudaError_t cudaStat;
  1317 + //cublasStatus_t stat;
1244 cublasHandle_t handle; 1318 cublasHandle_t handle;
1245 1319
1246 std::ofstream target(outfile.c_str(), std::ios::binary); //open the target binary file 1320 std::ofstream target(outfile.c_str(), std::ios::binary); //open the target binary file
@@ -1251,12 +1325,12 @@ public: @@ -1251,12 +1325,12 @@ public:
1251 1325
1252 double* s = (double*)malloc(sizeof(double) * B); //allocate space for the spectrum that will be pulled from the file 1326 double* s = (double*)malloc(sizeof(double) * B); //allocate space for the spectrum that will be pulled from the file
1253 double* s_dev; //declare a device pointer that will store the spectrum on the GPU 1327 double* s_dev; //declare a device pointer that will store the spectrum on the GPU
1254 - cudaStat = cudaMalloc(&s_dev, B * sizeof(double)); //allocate space on the CUDA device for the spectrum 1328 + HANDLE_ERROR(cudaMalloc(&s_dev, B * sizeof(double))); //allocate space on the CUDA device for the spectrum
1255 1329
1256 1330
1257 double* basis_dev; // device pointer on the GPU 1331 double* basis_dev; // device pointer on the GPU
1258 - cudaStat = cudaMalloc(&basis_dev, M * B * sizeof(double)); // allocate space on the CUDA device  
1259 - cudaStat = cudaMemset(basis_dev, 0, M * B * sizeof(double)); // initialize basis_dev to zero (0) 1332 + HANDLE_ERROR(cudaMalloc(&basis_dev, M * B * sizeof(double))); // allocate space on the CUDA device
  1333 + HANDLE_ERROR(cudaMemset(basis_dev, 0, M * B * sizeof(double))); // initialize basis_dev to zero (0)
1260 1334
1261 1335
1262 /// transposing basis matrix (because cuBLAS is column-major) 1336 /// transposing basis matrix (because cuBLAS is column-major)
@@ -1265,28 +1339,24 @@ public: @@ -1265,28 +1339,24 @@ public:
1265 for (int i = 0; i<M; i++) 1339 for (int i = 0; i<M; i++)
1266 for (int j = 0; j<B; j++) 1340 for (int j = 0; j<B; j++)
1267 basis_Transposed[i+j*M] = basis[i*B+j]; 1341 basis_Transposed[i+j*M] = basis[i*B+j];
  1342 + //copy the basis_Transposed matrix to the CUDA device (both matrices are stored in column-major format)
  1343 + CUBLAS_HANDLE_ERROR(cublasSetMatrix((int)M, (int)B, sizeof(double),basis_Transposed, (int)M, basis_dev, (int)M));
1268 1344
1269 - stat = cublasSetMatrix((int)M, (int)B, sizeof(double),basis_Transposed, (int)M, basis_dev, (int)M); //copy the basis_Transposed matrix to the CUDA device (both matrices are stored in column-major format)  
1270 -  
1271 - double* center_dev; //declare a device pointer that will store the center (average)  
1272 - cudaStat = cudaMalloc(&center_dev, B * sizeof(double)); //allocate space on the CUDA device for the center (average)  
1273 - stat = cublasSetVector((int)B, sizeof(double), center, 1, center_dev, 1); //copy the center vector (average) to the CUDA device (from host to device) 1345 + double* center_dev; //declare a device pointer that will store the center (average)
  1346 + HANDLE_ERROR(cudaMalloc(&center_dev, B * sizeof(double))); //allocate space on the CUDA device for the center (average)
  1347 + CUBLAS_HANDLE_ERROR(cublasSetVector((int)B, sizeof(double), center, 1, center_dev, 1)); //copy the center vector (average) to the CUDA device (from host to device)
1274 1348
1275 1349
1276 - double* A = (double*)malloc(sizeof(double) * M); //allocate space for the projected pixel on the host  
1277 - double* A_dev; //declare a device pointer that will store the projected pixel on the GPU  
1278 - cudaStat = cudaMalloc(&A_dev,M * sizeof(double)); //allocate space on the CUDA device for the projected pixel  
1279 - cudaStat = cudaMemset(A_dev, 0,M * sizeof(double)); //initialize the projected pixel to zero (0) 1350 + double* A = (double*)malloc(sizeof(double) * M); //allocate space for the projected pixel on the host
  1351 + double* A_dev; //declare a device pointer that will store the projected pixel on the GPU
  1352 + HANDLE_ERROR(cudaMalloc(&A_dev,M * sizeof(double))); //allocate space on the CUDA device for the projected pixel
  1353 + HANDLE_ERROR(cudaMemset(A_dev, 0,M * sizeof(double))); //initialize the projected pixel to zero (0)
1280 1354
1281 double axpy_alpha = -1; //multiplication factor for the center (in order to perform a subtraction) 1355 double axpy_alpha = -1; //multiplication factor for the center (in order to perform a subtraction)
1282 double axpy_alpha2 = 1; //multiplication factor for the matrix-vector multiplication 1356 double axpy_alpha2 = 1; //multiplication factor for the matrix-vector multiplication
1283 double axpy_beta = 0; //multiplication factor for the matrix-vector multiplication (there is no second scalor) 1357 double axpy_beta = 0; //multiplication factor for the matrix-vector multiplication (there is no second scalor)
1284 1358
1285 - stat = cublasCreate(&handle); //create a cuBLAS instance  
1286 - if (stat != CUBLAS_STATUS_SUCCESS) { //test the cuBLAS instance to make sure it is valid  
1287 - printf ("CUBLAS initialization failed\n");  
1288 - return EXIT_FAILURE;  
1289 - } 1359 + CUBLAS_HANDLE_ERROR(cublasCreate(&handle)); //create a cuBLAS instance
1290 1360
1291 T* temp = (T*)malloc(sizeof(T) * M); //allocate space for the projected pixel to be written on the disc 1361 T* temp = (T*)malloc(sizeof(T) * M); //allocate space for the projected pixel to be written on the disc
1292 size_t i; 1362 size_t i;
@@ -1294,14 +1364,11 @@ public: @@ -1294,14 +1364,11 @@ public:
1294 if (mask == NULL || mask[xy] != 0){ 1364 if (mask == NULL || mask[xy] != 0){
1295 pixeld(s, xy); //retreive the spectrum at the current xy pixel location 1365 pixeld(s, xy); //retreive the spectrum at the current xy pixel location
1296 1366
1297 - stat = cublasSetVector((int)B, sizeof(double), s, 1, s_dev, 1); //copy the spectrum from the host to the device  
1298 - stat = cublasDaxpy(handle, (int)B, &axpy_alpha, center_dev, 1, s_dev, 1); //subtract the center (average)  
1299 - stat = cublasDgemv(handle,CUBLAS_OP_N,(int)M,(int)B,&axpy_alpha2,basis_dev,(int)M,s_dev,1,&axpy_beta,A_dev,1); //performs the matrix-vector multiplication  
1300 - stat = cublasGetVector((int)M, sizeof(double), A_dev, 1, A, 1); //copy the projected pixel to the host (from GPU to CPU)  
1301 -  
1302 - //stat = cublasGetVector((int)B, sizeof(double), s_dev, 1, A, 1); //copy the projected pixel to the host (from GPU to CPU)  
1303 -  
1304 - //std::copy<double*, T*>(A, A + M, temp); 1367 + CUBLAS_HANDLE_ERROR(cublasSetVector((int)B, sizeof(double), s, 1, s_dev, 1)); //copy the spectrum from the host to the device
  1368 + CUBLAS_HANDLE_ERROR(cublasDaxpy(handle, (int)B, &axpy_alpha, center_dev, 1, s_dev, 1)); //subtract the center (average)
  1369 + CUBLAS_HANDLE_ERROR(cublasDgemv(handle,CUBLAS_OP_N,(int)M,(int)B,&axpy_alpha2,basis_dev,(int)M,s_dev,1,&axpy_beta,A_dev,1)); //performs the matrix-vector multiplication
  1370 + CUBLAS_HANDLE_ERROR(cublasGetVector((int)M, sizeof(double), A_dev, 1, A, 1)); //copy the projected pixel to the host (from GPU to CPU)
  1371 +
1305 for(i = 0; i < M; i++) temp[i] = (T)A[i]; //casting projected pixel from double to whatever T is 1372 for(i = 0; i < M; i++) temp[i] = (T)A[i]; //casting projected pixel from double to whatever T is
1306 } 1373 }
1307 else 1374 else
@@ -1313,10 +1380,11 @@ public: @@ -1313,10 +1380,11 @@ public:
1313 } 1380 }
1314 1381
1315 //clean up allocated device memory 1382 //clean up allocated device memory
1316 - cudaFree(A_dev);  
1317 - cudaFree(s_dev);  
1318 - cudaFree(basis_dev);  
1319 - cudaFree(center_dev); 1383 + HANDLE_ERROR(cudaFree(A_dev));
  1384 + HANDLE_ERROR(cudaFree(s_dev));
  1385 + HANDLE_ERROR(cudaFree(basis_dev));
  1386 + HANDLE_ERROR(cudaFree(center_dev));
  1387 + CUBLAS_HANDLE_ERROR(cublasDestroy(handle));
1320 free(A); 1388 free(A);
1321 free(s); 1389 free(s);
1322 free(temp); 1390 free(temp);
@@ -1324,7 +1392,6 @@ public: @@ -1324,7 +1392,6 @@ public:
1324 1392
1325 return true; 1393 return true;
1326 } 1394 }
1327 -#endif  
1328 1395
1329 /// Project the spectra onto a set of basis functions 1396 /// Project the spectra onto a set of basis functions
1330 /// @param outfile is the name of the new binary output file that will be created 1397 /// @param outfile is the name of the new binary output file that will be created
@@ -1332,16 +1399,22 @@ public: @@ -1332,16 +1399,22 @@ public:
1332 /// @param basis a set of basis vectors that the data set will be projected onto (after centering) 1399 /// @param basis a set of basis vectors that the data set will be projected onto (after centering)
1333 /// @param M is the number of basis vectors 1400 /// @param M is the number of basis vectors
1334 /// @param mask is a character mask used to limit processing to valid pixels 1401 /// @param mask is a character mask used to limit processing to valid pixels
1335 - bool project(std::string outfile, double* center, double* basis, unsigned long long M, unsigned char* mask = NULL, bool PROGRESS = false){  
1336 -  
1337 -#ifdef CUDA_FOUND  
1338 - int dev_count;  
1339 - cudaGetDeviceCount(&dev_count); //get the number of CUDA devices  
1340 - cudaDeviceProp prop;  
1341 - cudaGetDeviceProperties(&prop, 0); //get the property of the first device  
1342 - if(dev_count > 0 && prop.major != 9999) //if the first device is not an emulator  
1343 - return project_cublas(outfile,center,basis,M,mask,PROGRESS); //use cuBLAS to calculate the covariance matrix  
1344 -#endif 1402 + bool project(std::string outfile, double* center, double* basis, unsigned long long M, unsigned char* mask = NULL, int cuda_device = 0, bool PROGRESS = false){
  1403 + if (cuda_device >= 0) { //if a CUDA device is specified
  1404 + int dev_count;
  1405 + HANDLE_ERROR(cudaGetDeviceCount(&dev_count)); //get the number of CUDA devices
  1406 + if (dev_count > 0 && dev_count > cuda_device) { //if the first device is not an emulator
  1407 + cudaDeviceProp prop;
  1408 + cudaGetDeviceProperties(&prop, cuda_device); //get the property of the requested CUDA device
  1409 + if (prop.major != 9999) {
  1410 + std::cout << "Using CUDA device [" << cuda_device << "] to perform a basis projection..." << std::endl;
  1411 + HANDLE_ERROR(cudaSetDevice(cuda_device));
  1412 + return project_cublas(outfile, center, basis, M, mask, PROGRESS);
  1413 + }
  1414 + } //otherwise continue using the CPU
  1415 + std::cout << "WARNING: cuBLAS failed, using CPU" << std::endl;
  1416 + }
  1417 +
1345 std::ofstream target(outfile.c_str(), std::ios::binary); //open the target binary file 1418 std::ofstream target(outfile.c_str(), std::ios::binary); //open the target binary file
1346 //std::string headername = outfile + ".hdr"; //the header file name 1419 //std::string headername = outfile + ".hdr"; //the header file name
1347 1420
@@ -1426,9 +1499,9 @@ public: @@ -1426,9 +1499,9 @@ public:
1426 bool PROGRESS = false){ 1499 bool PROGRESS = false){
1427 1500
1428 //calculate the new number of samples, lines, and bands 1501 //calculate the new number of samples, lines, and bands
1429 - unsigned long long samples = x1 - x0;  
1430 - unsigned long long lines = y1 - y0;  
1431 - unsigned long long bands = b1 - b0; 1502 + unsigned long long samples = x1 - x0 + 1;
  1503 + unsigned long long lines = y1 - y0 + 1;
  1504 + unsigned long long bands = b1 - b0 + 1;
1432 1505
1433 //calculate the length of one cropped spectrum 1506 //calculate the length of one cropped spectrum
1434 unsigned long long L = bands * sizeof(T); 1507 unsigned long long L = bands * sizeof(T);
@@ -1436,41 +1509,46 @@ public: @@ -1436,41 +1509,46 @@ public:
1436 //unsigned long long L = Z() * sizeof(T); 1509 //unsigned long long L = Z() * sizeof(T);
1437 1510
1438 //allocate space for the spectrum 1511 //allocate space for the spectrum
1439 - T* temp = (T*)malloc(L); 1512 + char* temp = (char*)malloc(L);
1440 1513
1441 //open an output file for binary writing 1514 //open an output file for binary writing
1442 std::ofstream out(outfile.c_str(), std::ios::binary); 1515 std::ofstream out(outfile.c_str(), std::ios::binary);
1443 1516
1444 //seek to the first pixel in the cropped image 1517 //seek to the first pixel in the cropped image
1445 - file.seekg( (y0 * X() * Z() + x0 * Z() + b0) * sizeof(T), std::ios::beg); 1518 + size_t startx = x0 * Z();
  1519 + size_t starty = y0 * X() * Z();
  1520 + size_t startb = b0;
  1521 + file.seekg( (starty + startx + startb) * sizeof(T), std::ios::beg);
1446 1522
1447 //distance between sample spectra in the same line 1523 //distance between sample spectra in the same line
1448 - unsigned long long jump_sample = ( (Z() - b1) + b0 ) * sizeof(T); 1524 + size_t dist_between_samples = Z() - bands;
  1525 + size_t jump_sample = dist_between_samples * sizeof(T);
1449 1526
1450 //distance between sample spectra in adjacent lines 1527 //distance between sample spectra in adjacent lines
1451 - unsigned long long jump_line = (X() - x1) * Z() * sizeof(T); 1528 + //unsigned long long jump_line = ( X() - x1 + x0 ) * Z() * sizeof(T);
  1529 + size_t dist_between_lines = X() - samples;
  1530 + size_t jump_line = dist_between_lines * Z() * sizeof(T);
1452 1531
1453 1532
1454 //unsigned long long sp = y0 * X() + x0; //start pixel 1533 //unsigned long long sp = y0 * X() + x0; //start pixel
1455 1534
1456 //for each pixel in the image 1535 //for each pixel in the image
1457 - for (unsigned y = 0; y < lines; y++)  
1458 - {  
1459 - for (unsigned x = 0; x < samples; x++)  
1460 - { 1536 + for (unsigned y = 0; y < lines; y++) {
  1537 + for (unsigned x = 0; x < samples; x++) {
1461 //read the cropped spectral region 1538 //read the cropped spectral region
1462 - file.read( (char*) temp, L ); 1539 + file.read(temp, L );
1463 //pixel(temp, sp + x + y * X()); 1540 //pixel(temp, sp + x + y * X());
1464 - out.write(reinterpret_cast<const char*>(temp), L); //write slice data into target file 1541 + out.write(temp, L); //write slice data into target file
1465 1542
1466 file.seekg(jump_sample, std::ios::cur); 1543 file.seekg(jump_sample, std::ios::cur);
1467 1544
1468 - if(PROGRESS) progress = (double)((y+1) * samples + x + 1) / (lines * samples) * 100; 1545 + if(PROGRESS) progress = (double)(y * samples + x + 1) / (lines * samples) * 100;
1469 } 1546 }
1470 1547
1471 file.seekg(jump_line, std::ios::cur); 1548 file.seekg(jump_line, std::ios::cur);
1472 } 1549 }
1473 free(temp); 1550 free(temp);
  1551 + out.close();
1474 1552
1475 return true; 1553 return true;
1476 } 1554 }
@@ -1546,6 +1624,26 @@ public: @@ -1546,6 +1624,26 @@ public:
1546 } 1624 }
1547 } 1625 }
1548 1626
  1627 + ///Append two files together along the band dimension
  1628 + void append(std::string outfile, bip<T>* C, bool PROGRESS = false) {
  1629 + std::ofstream out(outfile.c_str(), std::ios::binary); //open the output file for writing
  1630 + file.seekg(0, std::ios::beg); //move to the beginning of both files
  1631 + C->file.seekg(0, std::ios::beg);
  1632 + size_t a_bytes = Z() * sizeof(T); //calculate the number of bytes in a single plane of this file
  1633 + size_t b_bytes = C->Z() * sizeof(T); //calculate the number of bytes in a single plane of the appending file
  1634 + T* a = (T*)malloc(a_bytes); //allocate space for a plane of the current file
  1635 + T* b = (T*)malloc(b_bytes); //allocate space for a plane of the appended file
  1636 + if (PROGRESS) progress = 0;
  1637 + for (size_t xy = 0; xy < X()*Y(); xy++) {
  1638 + spectrum(a, xy); //read a plane from the current file
  1639 + out.write((char*)a, a_bytes); //write the plane to disk
  1640 + C->spectrum(b, xy); //read a plane from the appending file
  1641 + out.write((char*)b, b_bytes);
  1642 + if (PROGRESS) progress = (double)(xy + 1) / (double)(X() * Y()) * 100;
  1643 + }
  1644 +
  1645 + out.close();
  1646 + }
1549 /// Convolve the given band range with a kernel specified by a vector of coefficients. 1647 /// Convolve the given band range with a kernel specified by a vector of coefficients.
1550 1648
1551 /// @param outfile is an already open stream to the output file 1649 /// @param outfile is an already open stream to the output file
@@ -1687,7 +1785,117 @@ public: @@ -1687,7 +1785,117 @@ public:
1687 return true; 1785 return true;
1688 } 1786 }
1689 1787
  1788 + int fft(std::string outname, size_t bandmin, size_t bandmax, size_t samples = 0, T* ratio = NULL, size_t rx = 0, size_t ry = 0, bool PROGRESS = false, int device = 0){
  1789 + if(device == -1){
  1790 + std::cout<<"ERROR: GPU required for FFT (uses cuFFT)."<<std::endl;
  1791 + exit(1);
  1792 + }
  1793 + if(samples == 0) samples = Z(); //if samples are specified, use all of them
  1794 + if(samples > Z()){
  1795 + std::cout<<"ERROR: stim::envi doesn't support FFT padding just yet."<<std::endl;
  1796 + exit(1);
  1797 + }
  1798 + int nd; //stores the number of CUDA devices
  1799 + HANDLE_ERROR(cudaGetDeviceCount(&nd)); //get the number of CUDA devices
  1800 + if(device >= nd){ //test for the existence of the requested device
  1801 + std::cout<<"ERROR: requested CUDA device for stim::envi::fft() doesn't exist"<<std::endl;
  1802 + exit(1);
  1803 + }
  1804 + HANDLE_ERROR(cudaSetDevice(device)); //set the CUDA device
  1805 + cudaDeviceProp prop;
  1806 + HANDLE_ERROR(cudaGetDeviceProperties(&prop, device)); //get the CUDA device properties
  1807 +
  1808 + size_t B = Z();
  1809 + size_t S = samples;
  1810 + size_t fft_size = S * sizeof(T); //number of bytes for each FFT
  1811 + size_t cuda_bytes = prop.totalGlobalMem; //get the number of bytes of global memory available
  1812 + size_t cuda_use = (size_t)floor(cuda_bytes * 0.2); //only use 80%
  1813 + size_t nS = cuda_use / fft_size; //calculate the number of spectra that can be loaded onto the GPU as a single batch
  1814 + size_t batch_bytes = nS * fft_size; //calculate the size of a batch (in bytes)
  1815 + size_t fft_bytes = nS * (S/2 + 1) * sizeof(cufftComplex);
  1816 + T* batch = (T*) malloc(batch_bytes); //allocate space in host memory to store a batch
  1817 + memset(batch, 0, batch_bytes);
  1818 + std::complex<T>* batch_fft = (std::complex<T>*) malloc(fft_bytes);
  1819 + T* gpu_batch; //device pointer to the batch
  1820 + HANDLE_ERROR(cudaMalloc(&gpu_batch, batch_bytes)); //allocate space on the device for the FFT batch
  1821 + cufftComplex* gpu_batch_fft; //allocate space for the FFT result
  1822 + HANDLE_ERROR(cudaMalloc(&gpu_batch_fft, fft_bytes));
  1823 + int N[1]; //create an array with the interferogram size (required for cuFFT input)
  1824 + N[0] = (int)S; //set the only array value to the interferogram size
  1825 +
  1826 + //if a background is provided for a ratio
  1827 + std::complex<T>* ratio_fft = NULL; //create a pointer for the FFT of the ratio image (if it exists)
  1828 + if(ratio){
  1829 + size_t bkg_bytes = rx * ry * S * sizeof(T); //calculate the total number of bytes in the background image
  1830 + T* bkg_copy = (T*) malloc(bkg_bytes); //allocate space to copy the background
  1831 + if(S == Z()) memcpy(bkg_copy, ratio, bkg_bytes); //if the number of samples used in processing equals the number of available samples
  1832 + else{
  1833 + for(size_t xyi = 0; xyi < rx*ry; xyi++)
  1834 + memcpy(&bkg_copy[xyi * S], &ratio[xyi * B], S * sizeof(T));
  1835 + }
  1836 + T* gpu_ratio;
  1837 + HANDLE_ERROR(cudaMalloc(&gpu_ratio, bkg_bytes));
  1838 + HANDLE_ERROR(cudaMemcpy(gpu_ratio, bkg_copy, bkg_bytes, cudaMemcpyHostToDevice));
  1839 + cufftHandle bkg_plan;
  1840 + CUFFT_HANDLE_ERROR(cufftPlanMany(&bkg_plan, 1, N, NULL, 1, N[0], NULL, 1, N[0], CUFFT_R2C, (int)(rx * ry)));
  1841 + size_t bkg_fft_bytes = rx * ry * (S / 2 + 1) * sizeof(cufftComplex);
  1842 + T* gpu_ratio_fft;
  1843 + HANDLE_ERROR(cudaMalloc(&gpu_ratio_fft, bkg_fft_bytes));
  1844 + CUFFT_HANDLE_ERROR(cufftExecR2C(bkg_plan, (cufftReal*)gpu_ratio, (cufftComplex*)gpu_ratio_fft));
  1845 + ratio_fft = (std::complex<T>*) malloc(bkg_fft_bytes);
  1846 + HANDLE_ERROR(cudaMemcpy(ratio_fft, gpu_ratio_fft, bkg_fft_bytes, cudaMemcpyDeviceToHost));
  1847 + HANDLE_ERROR(cudaFree(gpu_ratio));
  1848 + HANDLE_ERROR(cudaFree(gpu_ratio_fft));
  1849 + CUFFT_HANDLE_ERROR(cufftDestroy(bkg_plan));
  1850 + }
1690 1851
  1852 + cufftHandle plan; //create a CUFFT plan
  1853 + CUFFT_HANDLE_ERROR(cufftPlanMany(&plan, 1, N, NULL, 1, N[0], NULL, 1, N[0], CUFFT_R2C, (int)nS));
  1854 +
  1855 + std::ofstream outfile(outname, std::ios::binary); //open a file for writing
  1856 +
  1857 + size_t XY = X() * Y(); //calculate the number of spectra
  1858 + size_t xy = 0;
  1859 + size_t bs; //stores the number of spectra in the current batch
  1860 + size_t s, b;
  1861 + size_t S_fft = S/2 + 1;
  1862 + size_t bandkeep = bandmax - bandmin + 1;
  1863 + size_t x, y;
  1864 + size_t ratio_i;
  1865 + T* temp_spec = (T*) malloc(Z() * sizeof(T)); //allocate space to hold a single pixel
  1866 + while(xy < XY){ //while there are unprocessed spectra
  1867 + bs = (std::min)(XY - xy, nS); //calculate the number of spectra to include in the batch
  1868 + for(s = 0; s < bs; s++){ //for each spectrum in the batch
  1869 + pixel(temp_spec, xy + s); //read a pixel from disk
  1870 + memcpy(&batch[s * S], temp_spec, S * sizeof(T));
  1871 + //pixel(&batch[s * S], xy + s); //read the next spectrum
  1872 + }
  1873 + HANDLE_ERROR(cudaMemcpy(gpu_batch, batch, batch_bytes, cudaMemcpyHostToDevice));
  1874 + CUFFT_HANDLE_ERROR(cufftExecR2C(plan, (cufftReal*)gpu_batch, gpu_batch_fft)); //execute the (implicitly forward) transform
  1875 + HANDLE_ERROR(cudaMemcpy(batch_fft, gpu_batch_fft, fft_bytes, cudaMemcpyDeviceToHost)); //copy the data back to the GPU
  1876 + for(s = 0; s < bs; s++){ //for each spectrum in the batch
  1877 + y = (xy + s)/X();
  1878 + x = xy + s - y * X();
  1879 + if(ratio_fft) ratio_i = (y % ry) * rx + (x % rx); //if a background is used, calculate the coordinates into it
  1880 + for(b = 0; b < S/2 + 1; b++){ //for each sample
  1881 + if(ratio_fft)
  1882 + batch[s * S + b] = -log(abs(batch_fft[s * S_fft + b]) / abs(ratio_fft[ratio_i * S_fft + b]));
  1883 + else
  1884 + batch[s * S + b] = abs(batch_fft[s * S_fft + b]); //calculate the magnitude of the spectrum
  1885 + }
  1886 + outfile.write((char*)&batch[s * S + bandmin], bandkeep * sizeof(T)); //save the resulting spectrum
  1887 + }
  1888 + xy += bs; //increment xy by the number of spectra processed
  1889 + if(PROGRESS) progress = (double)xy / (double)XY * 100;
  1890 + }
  1891 + outfile.close();
  1892 + free(ratio_fft);
  1893 + free(batch_fft);
  1894 + free(batch);
  1895 + HANDLE_ERROR(cudaFree(gpu_batch));
  1896 + HANDLE_ERROR(cudaFree(gpu_batch_fft));
  1897 + return 0;
  1898 + }
1691 1899
1692 /// Close the file. 1900 /// Close the file.
1693 bool close(){ 1901 bool close(){
1 #ifndef STIM_BSQ_H 1 #ifndef STIM_BSQ_H
2 #define STIM_BSQ_H 2 #define STIM_BSQ_H
3 3
4 -#include "../envi/envi_header.h"  
5 -#include "../envi/hsi.h"  
6 -#include "../envi/bil.h" 4 +#include <stim/envi/envi_header.h>
  5 +#include <stim/envi/hsi.h>
  6 +#include <stim/envi/bil.h>
7 #include <cstring> 7 #include <cstring>
8 #include <utility> 8 #include <utility>
9 #include <vector> 9 #include <vector>
@@ -64,14 +64,15 @@ public: @@ -64,14 +64,15 @@ public:
64 unsigned long long Y, 64 unsigned long long Y,
65 unsigned long long B, 65 unsigned long long B,
66 unsigned long long header_offset, 66 unsigned long long header_offset,
67 - std::vector<double> wavelengths){ 67 + std::vector<double> wavelengths,
  68 + stim::iotype io = stim::io_in){
68 69
69 //copy the wavelengths to the BSQ file structure 70 //copy the wavelengths to the BSQ file structure
70 w = wavelengths; 71 w = wavelengths;
71 //copy the wavelengths to the structure 72 //copy the wavelengths to the structure
72 offset = header_offset; 73 offset = header_offset;
73 74
74 - return open(filename, vec<unsigned long long>(X, Y, B), header_offset); 75 + return open(filename, vec<unsigned long long>(X, Y, B), header_offset, io);
75 } 76 }
76 77
77 /// Retrieve a single band (based on index) and stores it in pre-allocated memory. 78 /// Retrieve a single band (based on index) and stores it in pre-allocated memory.
@@ -104,6 +105,7 @@ public: @@ -104,6 +105,7 @@ public:
104 //if wavelength is smaller than the first one in header file 105 //if wavelength is smaller than the first one in header file
105 if ( w[page] > wavelength ){ 106 if ( w[page] > wavelength ){
106 band_index(p, page); 107 band_index(p, page);
  108 + if(PROGRESS) progress = 100;
107 return true; 109 return true;
108 } 110 }
109 111
@@ -114,6 +116,7 @@ public: @@ -114,6 +116,7 @@ public:
114 // (the wavelength is out of bounds) 116 // (the wavelength is out of bounds)
115 if (page == Z()) { 117 if (page == Z()) {
116 band_index(p, Z()-1); //return the last band 118 band_index(p, Z()-1); //return the last band
  119 + if(PROGRESS) progress = 100;
117 return true; 120 return true;
118 } 121 }
119 } 122 }
@@ -377,6 +380,30 @@ public: @@ -377,6 +380,30 @@ public:
377 380
378 } 381 }
379 382
  383 + bool select(std::string outfile, std::vector<double> bandlist, unsigned char* mask = NULL, bool PROGRESS = false) {
  384 + std::ofstream out(outfile.c_str(), std::ios::binary); //open the output file
  385 + if (!out) {
  386 + std::cout << "ERROR opening output file: " << outfile << std::endl;
  387 + return false;
  388 + }
  389 + file.seekg(0, std::ios::beg); //move the pointer to the current file to the beginning
  390 +
  391 + size_t B = Z(); //calculate the number of bands
  392 + size_t XY = X() * Y(); //calculate the number of pixels in a band
  393 + size_t in_bytes = XY * sizeof(T); //calculate the size of a band in bytes
  394 +
  395 + T* in = (T*)malloc(in_bytes); //allocate space for the band image
  396 + size_t nb = bandlist.size(); //get the number of bands in the output image
  397 + for (size_t b = 0; b < nb; b++) {
  398 + band(in, bandlist[b]); //get the band associated with the given wavelength
  399 + out.write((char*)in, in_bytes); //write the band to the output file
  400 + if (PROGRESS) progress = (double)(b + 1) / (double)bandlist.size() * 100;
  401 + }
  402 + out.close();
  403 + free(in);
  404 + return true;
  405 + }
  406 +
380 size_t readlines(T* dest, size_t start, size_t n){ 407 size_t readlines(T* dest, size_t start, size_t n){
381 return hsi<T>::read(dest, 0, start, 0, X(), n, Z()); 408 return hsi<T>::read(dest, 0, start, 0, X(), n, Z());
382 } 409 }
@@ -648,13 +675,24 @@ public: @@ -648,13 +675,24 @@ public:
648 675
649 //to make sure the left and the right bound are in the bandwidth 676 //to make sure the left and the right bound are in the bandwidth
650 if (lb < w[0] || rb < w[0] || lb > w[n-1] || rb >w[n-1]){ 677 if (lb < w[0] || rb < w[0] || lb > w[n-1] || rb >w[n-1]){
651 - std::cout<<"ERROR: left bound or right bound out of bandwidth"<<std::endl;  
652 - exit(1); 678 + if (lb < w[0]) {
  679 + std::cout << "bsq::area ERROR - left bound " << lb << " is below the minimum available wavelength " << w[0] << std::endl;
  680 + }
  681 + if (rb < w[0]) {
  682 + std::cout << "bsq::area ERROR - right bound " << rb << " is below the minimum available wavelength " << w[0] << std::endl;
  683 + }
  684 + if (lb > w[n - 1]) {
  685 + std::cout << "bsq::area ERROR - left bound " << lb << " is above the maximum available wavelength " << w[n - 1] << std::endl;
  686 + }
  687 + if (rb > w[n - 1]) {
  688 + std::cout << "bsq::area ERROR - right bound " << rb << " is above the maximum available wavelength " << w[0] << std::endl;
  689 + }
  690 + return false;
653 } 691 }
654 //to make sure right bound is bigger than left bound 692 //to make sure right bound is bigger than left bound
655 else if(lb > rb){ 693 else if(lb > rb){
656 - std::cout<<"ERROR: right bound should be bigger than left bound"<<std::endl;  
657 - exit(1); 694 + std::cout << "bsq::area ERROR - right bound " << rb << " should be larger than left bound " << lb << std::endl;
  695 + return false;
658 } 696 }
659 697
660 //find the indices of the left and right baseline points 698 //find the indices of the left and right baseline points
@@ -994,7 +1032,7 @@ public: @@ -994,7 +1032,7 @@ public:
994 matrix[i*Z() + b] = band_image[xy]; //copy it to the appropriate point in the values[] array 1032 matrix[i*Z() + b] = band_image[xy]; //copy it to the appropriate point in the values[] array
995 i++; 1033 i++;
996 } 1034 }
997 - if(PROGRESS) progress = (double)(xy+1) / (double)XY * 100; 1035 + if(PROGRESS) progress = (double)(b * XY + xy+1) / (double)(XY * Z()) * 100;
998 } 1036 }
999 } 1037 }
1000 1038
@@ -1196,9 +1234,9 @@ public: @@ -1196,9 +1234,9 @@ public:
1196 bool PROGRESS = false){ 1234 bool PROGRESS = false){
1197 1235
1198 //calculate the new number of samples, lines, and bands 1236 //calculate the new number of samples, lines, and bands
1199 - unsigned long long samples = x1 - x0;  
1200 - unsigned long long lines = y1 - y0;  
1201 - unsigned long long bands = b1 - b0; 1237 + unsigned long long samples = x1 - x0 + 1;
  1238 + unsigned long long lines = y1 - y0 + 1;
  1239 + unsigned long long bands = b1 - b0 + 1;
1202 1240
1203 //calculate the size of a single band 1241 //calculate the size of a single band
1204 unsigned long long L = samples * lines * sizeof(T); 1242 unsigned long long L = samples * lines * sizeof(T);
@@ -1219,7 +1257,7 @@ public: @@ -1219,7 +1257,7 @@ public:
1219 file.seekg( (b0 * X() * Y() + y0 * X() + x0) * sizeof(T), std::ios::beg); 1257 file.seekg( (b0 * X() * Y() + y0 * X() + x0) * sizeof(T), std::ios::beg);
1220 1258
1221 //for each band 1259 //for each band
1222 - for (unsigned long long z = b0; z < b1; z++) 1260 + for (unsigned long long z = b0; z <= b1; z++)
1223 { 1261 {
1224 //std::cout<<z<<std::endl; 1262 //std::cout<<z<<std::endl;
1225 for (unsigned long long y = 0; y < lines; y++) 1263 for (unsigned long long y = 0; y < lines; y++)
@@ -1227,7 +1265,7 @@ public: @@ -1227,7 +1265,7 @@ public:
1227 file.read((char *)(temp + y * samples), sizeof(T) * samples); 1265 file.read((char *)(temp + y * samples), sizeof(T) * samples);
1228 file.seekg(jumpl, std::ios::cur); //go to the next band 1266 file.seekg(jumpl, std::ios::cur); //go to the next band
1229 1267
1230 - if(PROGRESS) progress = (double)((z - b0 + 1) * lines + y + 1) / ((b1 - b0) * lines) * 100; 1268 + if(PROGRESS) progress = (double)((z - b0) * lines + y + 1) / ((b1 - b0 + 1) * lines) * 100;
1231 } 1269 }
1232 out.write(reinterpret_cast<const char*>(temp), L); //write slice data into target file 1270 out.write(reinterpret_cast<const char*>(temp), L); //write slice data into target file
1233 file.seekg(jumpb, std::ios::cur); 1271 file.seekg(jumpb, std::ios::cur);
@@ -1237,6 +1275,52 @@ public: @@ -1237,6 +1275,52 @@ public:
1237 return true; 1275 return true;
1238 } 1276 }
1239 1277
  1278 + ///Crop out several subimages and assemble a new image from these concatenated subimages
  1279 +
  1280 + /// @param outfile is the file name for the output image
  1281 + /// @param sx is the width of each subimage
  1282 + /// @param sy is the height of each subimage
  1283 + /// @mask is the mask used to define subimage positions extracted from the input file
  1284 + void subimages(std::string outfile, size_t sx, size_t sy, unsigned char* mask, bool PROGRESS = false){
  1285 +
  1286 + size_t N = nnz(mask); //get the number of subimages
  1287 + T* dst = (T*) malloc(N * sx * sy * sizeof(T)); //allocate space for a single band of the output image
  1288 + memset(dst, 0, N*sx*sy*sizeof(T)); //initialize the band image to zero
  1289 +
  1290 + std::ofstream out(outfile, std::ios::binary); //open a file for writing
  1291 +
  1292 + T* src = (T*) malloc(X() * Y() * sizeof(T));
  1293 +
  1294 + for(size_t b = 0; b < Z(); b++){ //for each band
  1295 + band_index(src, b); //load the band image
  1296 + size_t i = 0; //create an image index and initialize it to zero
  1297 + size_t n = 0;
  1298 + while(n < N){ //for each subimage
  1299 + if(mask[i]){ //if the pixel is masked, copy the surrounding pixels into the destination band
  1300 + size_t yi = i / X(); //determine the y position of the current pixel
  1301 + size_t xi = i - yi * X(); //determine the x position of the current pixel
  1302 + if( xi > sx/2 && xi < X() - sx/2 && //if the subimage is completely within the bounds of the original image
  1303 + yi > sy/2 && yi < Y() - sy/2){
  1304 + size_t cx = xi - sx/2; //calculate the corner position for the subimage
  1305 + size_t cy = yi - sy/2;
  1306 + for(size_t syi = 0; syi < sy; syi++){ //for each line in the subimage
  1307 + size_t src_i = (cy + syi) * X() + cx;
  1308 + //size_t dst_i = syi * (N * sx) + n * sx;
  1309 + size_t dst_i = (n * sy + syi) * sx;
  1310 + memcpy(&dst[dst_i], &src[src_i], sx * sizeof(T)); //copy one line from the subimage to the destination image
  1311 + }
  1312 + n++;
  1313 + }
  1314 + }
  1315 + i++;
  1316 + if(PROGRESS) progress = (double)( (n+1) * (b+1) ) / (N * Z()) * 100;
  1317 + }//end while n
  1318 + out.write((const char*)dst, N * sx * sy * sizeof(T)); //write the band to memory
  1319 + }
  1320 + free(dst); //free memory
  1321 + free(src);
  1322 + }
  1323 +
1240 /// Remove a list of bands from the ENVI file 1324 /// Remove a list of bands from the ENVI file
1241 1325
1242 /// @param outfile is the file name for the output hyperspectral image (with trimmed bands) 1326 /// @param outfile is the file name for the output hyperspectral image (with trimmed bands)
@@ -1311,6 +1395,20 @@ public: @@ -1311,6 +1395,20 @@ public:
1311 out.close(); 1395 out.close();
1312 } 1396 }
1313 1397
  1398 + /// Append an image to this one along the band dimension
  1399 + void append(std::string outfile, bsq<T>* C, bool PROGRESS = false) {
  1400 + std::ofstream out(outfile.c_str(), std::ios::binary); //open the output file for writing
  1401 + file.seekg(0, std::ios::beg); //move to the beginning of both files
  1402 + C->file.seekg(0, std::ios::beg);
  1403 +
  1404 + if (PROGRESS) progress = 0;
  1405 + out << file.rdbuf(); //copy the data from this ENVI file
  1406 + if (PROGRESS) progress = (double)(Z() + 1) / (double)(Z() + C->Z()) * 100;
  1407 + out << C->file.rdbuf(); //copy the data from the appending file
  1408 + if (PROGRESS) progress = 100;
  1409 + out.close();
  1410 + }
  1411 +
1314 /// Convolve the given band range with a kernel specified by a vector of coefficients. 1412 /// Convolve the given band range with a kernel specified by a vector of coefficients.
1315 1413
1316 /// @param outfile is an already open stream to the output file 1414 /// @param outfile is an already open stream to the output file
1 #ifndef STIM_ENVI_H 1 #ifndef STIM_ENVI_H
2 #define STIM_ENVI_H 2 #define STIM_ENVI_H
3 3
4 -#include "../envi/envi_header.h"  
5 -#include "../envi/bsq.h"  
6 -#include "../envi/bip.h"  
7 -#include "../envi/bil.h"  
8 -#include "../math/fd_coefficients.h" 4 +#include <stim/envi/envi_header.h>
  5 +#include <stim/envi/bsq.h>
  6 +#include <stim/envi/bip.h>
  7 +#include <stim/envi/bil.h>
  8 +#include <stim/math/fd_coefficients.h>
  9 +#include <stim/parser/filename.h>
  10 +#include <stim/util/filesize.h>
9 #include <iostream> 11 #include <iostream>
10 #include <fstream> 12 #include <fstream>
11 //#include "../image/image.h" 13 //#include "../image/image.h"
@@ -69,14 +71,67 @@ public: @@ -69,14 +71,67 @@ public:
69 file = NULL; //set the file pointer to NULL 71 file = NULL; //set the file pointer to NULL
70 } 72 }
71 73
72 - envi(std::string filename, std::string headername){ 74 + envi(std::string filename, std::string headername) : envi(){
73 header.load(headername); 75 header.load(headername);
74 76
75 fname = filename; //save the filename 77 fname = filename; //save the filename
76 78
77 allocate(); 79 allocate();
78 } 80 }
  81 + //used to test if the current ENVI file is valid
  82 + operator bool(){
  83 + if (header.interleave == envi_header::BSQ) { //if the infile is bsq file
  84 + if (header.data_type == envi_header::float32)
  85 + return ((bsq<float>*)file)->is_open();
  86 + else if (header.data_type == envi_header::float64)
  87 + return ((bsq<double>*)file)->is_open();
  88 + else
  89 + std::cout << "ERROR: unidentified data type" << std::endl;
  90 + }
  91 +
  92 + else if (header.interleave == envi_header::BIL) { //if the infile is bil file
  93 + if (header.data_type == envi_header::float32)
  94 + return ((bil<float>*)file)->is_open();
  95 + else if (header.data_type == envi_header::float64)
  96 + return ((bil<double>*)file)->is_open();
  97 + else
  98 + std::cout << "ERROR: unidentified data type" << std::endl;
  99 + }
  100 +
  101 + else if (header.interleave == envi_header::BIP) { //if the infile is bip file
  102 + if (header.data_type == envi_header::float32)
  103 + return ((bip<float>*)file)->is_open();
  104 + else if (header.data_type == envi_header::float64)
  105 + return ((bip<double>*)file)->is_open();
  106 + else
  107 + std::cout << "ERROR: unidentified data type" << std::endl;
  108 + }
  109 + else {
  110 + std::cout << "ERROR: unidentified file type" << std::endl;
  111 + exit(1);
  112 + }
  113 + return false;
  114 + }
  115 +
  116 + //test to determine if the specified file is an ENVI file
  117 + static bool is_envi(std::string fname, std::string hname = ""){
  118 + stim::filename data_file(fname);
  119 + stim::filename header_file;
  120 + if(hname == ""){ //if the header isn't provided
  121 + header_file = data_file; //assume that it's the same name as the data file, with a .hdr extension
  122 + header_file = header_file.extension("hdr");
  123 + }
  124 + else header_file = hname; //otherwise load the passed header
79 125
  126 + stim::envi_header H;
  127 + if(H.load(header_file) == false) //load the header file, if it doesn't load return false
  128 + return false;
  129 + size_t targetBytes = H.data_bytes(); //get the number of bytes that SHOULD be in the data file
  130 + size_t bytes = stim::file_size(fname);
  131 + if(bytes != targetBytes) return false; //if the data doesn't match the header, return false
  132 + return true; //otherwise everything looks fine
  133 +
  134 + }
80 135
81 136
82 void* malloc_spectrum(){ 137 void* malloc_spectrum(){
@@ -308,7 +363,7 @@ public: @@ -308,7 +363,7 @@ public:
308 } 363 }
309 364
310 /// Open a previously opened ENVI file 365 /// Open a previously opened ENVI file
311 - bool open(){ 366 + bool open(stim::iotype io = stim::io_in){
312 367
313 //load the file 368 //load the file
314 if(header.interleave == envi_header::BSQ) { //if the infile is bsq file 369 if(header.interleave == envi_header::BSQ) { //if the infile is bsq file
@@ -396,7 +451,7 @@ public: @@ -396,7 +451,7 @@ public:
396 451
397 /// @param filename is the name of the ENVI binary file 452 /// @param filename is the name of the ENVI binary file
398 /// @param header is an ENVI header structure 453 /// @param header is an ENVI header structure
399 - bool open(std::string filename, stim::envi_header h){ 454 + bool open(std::string filename, stim::envi_header h, stim::iotype io = stim::io_in){
400 455
401 456
402 header = h; //store the header 457 header = h; //store the header
@@ -404,7 +459,7 @@ public: @@ -404,7 +459,7 @@ public:
404 459
405 allocate(); 460 allocate();
406 461
407 - return open(); //open the ENVI file; 462 + return open(io); //open the ENVI file;
408 463
409 464
410 } 465 }
@@ -413,18 +468,21 @@ public: @@ -413,18 +468,21 @@ public:
413 468
414 /// @param filename is the name of the ENVI binary file 469 /// @param filename is the name of the ENVI binary file
415 /// @param headername is the name of the ENVI header file 470 /// @param headername is the name of the ENVI header file
416 - bool open(std::string filename, std::string headername){ 471 + bool open(std::string filename, std::string headername, stim::iotype io = stim::io_in){
417 472
418 //allocate memory 473 //allocate memory
419 //allocate(); 474 //allocate();
420 475
421 stim::envi_header h; 476 stim::envi_header h;
422 - h.load(headername); 477 + if (!h.load(headername)) {
  478 + std::cout << "Error loading header file: " << headername << std::endl;
  479 + return false;
  480 + }
423 481
424 //load the header 482 //load the header
425 //header.load(headername); 483 //header.load(headername);
426 484
427 - return open(filename, h); 485 + return open(filename, h, io);
428 } 486 }
429 487
430 /// Normalize a hyperspectral ENVI file given a band number and threshold. 488 /// Normalize a hyperspectral ENVI file given a band number and threshold.
@@ -504,6 +562,41 @@ public: @@ -504,6 +562,41 @@ public:
504 } 562 }
505 } 563 }
506 564
  565 + bool select(std::string outfile, std::vector<double> bandlist, unsigned char* MASK = NULL, bool PROGRESS = false) {
  566 + stim::envi_header new_header = header; //copy all of the data from the current header file to the new one
  567 + new_header.bands = bandlist.size(); //the number of bands in the new file is equal to the number of bands provided by the user
  568 + new_header.wavelength = bandlist; //the wavelength values in the output file are the same as those specified by the user
  569 + new_header.band_names.empty(); //no band names will be provided in the output file
  570 + new_header.save(outfile + ".hdr"); //save the output header file
  571 +
  572 + if (header.interleave == envi_header::BSQ) { //if the infile is bip file
  573 + if (header.data_type == envi_header::float32)
  574 + return ((bsq<float>*)file)->select(outfile, bandlist, MASK, PROGRESS);
  575 + else if (header.data_type == envi_header::float64)
  576 + return ((bsq<double>*)file)->select(outfile, bandlist, MASK, PROGRESS);
  577 + else
  578 + std::cout << "ERROR: unidentified data type" << std::endl;
  579 + }
  580 + else if (header.interleave == envi_header::BIL) { //if the infile is bip file
  581 + if (header.data_type == envi_header::float32)
  582 + return ((bil<float>*)file)->select(outfile, bandlist, MASK, PROGRESS);
  583 + else if (header.data_type == envi_header::float64)
  584 + return ((bil<double>*)file)->select(outfile, bandlist, MASK, PROGRESS);
  585 + else
  586 + std::cout << "ERROR: unidentified data type" << std::endl;
  587 + }
  588 + else if (header.interleave == envi_header::BIP) { //if the infile is bip file
  589 + if (header.data_type == envi_header::float32)
  590 + return ((bip<float>*)file)->select(outfile, bandlist, MASK, PROGRESS);
  591 + else if (header.data_type == envi_header::float64)
  592 + return ((bip<double>*)file)->select(outfile, bandlist, MASK, PROGRESS);
  593 + else
  594 + std::cout << "ERROR: unidentified data type" << std::endl;
  595 + }
  596 +
  597 + return false;
  598 + }
  599 +
507 /// Performs piecewise linear baseline correction of a hyperspectral file/ 600 /// Performs piecewise linear baseline correction of a hyperspectral file/
508 601
509 /// @param outfile is the file name for the baseline corrected output 602 /// @param outfile is the file name for the baseline corrected output
@@ -549,7 +642,9 @@ public: @@ -549,7 +642,9 @@ public:
549 } 642 }
550 } 643 }
551 644
552 - void project(std::string outfile, double* center, double* basis, unsigned long long M, unsigned char* mask, bool PROGRESS = false){ 645 + /// Project an array of coefficients onto a basis matrix
  646 +
  647 + void project(std::string outfile, double* center, double* basis, size_t M, std::vector<double> bands, unsigned char* mask, int cuda_device = 0, bool PROGRESS = false) {
553 if(header.interleave == envi_header::BSQ){ //if the infile is bsq file 648 if(header.interleave == envi_header::BSQ){ //if the infile is bsq file
554 std::cout<<"ERROR: BSQ projection not supported"<<std::endl; 649 std::cout<<"ERROR: BSQ projection not supported"<<std::endl;
555 exit(1); 650 exit(1);
@@ -562,9 +657,9 @@ public: @@ -562,9 +657,9 @@ public:
562 657
563 else if(header.interleave == envi_header::BIP){ //if the infile is bip file 658 else if(header.interleave == envi_header::BIP){ //if the infile is bip file
564 if(header.data_type ==envi_header::float32) 659 if(header.data_type ==envi_header::float32)
565 - ((bip<float>*)file)->project(outfile, center, basis, M, mask, PROGRESS); 660 + ((bip<float>*)file)->project(outfile, center, basis, M, mask, cuda_device, PROGRESS);
566 else if(header.data_type == envi_header::float64) 661 else if(header.data_type == envi_header::float64)
567 - ((bip<double>*)file)->project(outfile, center, basis, M, mask, PROGRESS); 662 + ((bip<double>*)file)->project(outfile, center, basis, M, mask, cuda_device, PROGRESS);
568 else{ 663 else{
569 std::cout<<"ERROR: unidentified data type"<<std::endl; 664 std::cout<<"ERROR: unidentified data type"<<std::endl;
570 exit(1); 665 exit(1);
@@ -573,7 +668,7 @@ public: @@ -573,7 +668,7 @@ public:
573 668
574 stim::envi_header out_hdr = header; 669 stim::envi_header out_hdr = header;
575 out_hdr.bands = M; //set the number of bands in the output header 670 out_hdr.bands = M; //set the number of bands in the output header
576 - out_hdr.wavelength.clear(); 671 + out_hdr.wavelength = bands;
577 out_hdr.band_names.clear(); 672 out_hdr.band_names.clear();
578 out_hdr.save(outfile + ".hdr"); //save the output header 673 out_hdr.save(outfile + ".hdr"); //save the output header
579 } 674 }
@@ -636,7 +731,7 @@ public: @@ -636,7 +731,7 @@ public:
636 ((bsq<double>*)file)->bil(outfile, PROGRESS, OPTIMIZATION); 731 ((bsq<double>*)file)->bil(outfile, PROGRESS, OPTIMIZATION);
637 else if(interleave == envi_header::BIP){ //ERROR 732 else if(interleave == envi_header::BIP){ //ERROR
638 //std::cout<<"ERROR: conversion from BSQ to BIP isn't practical; use BSQ->BIL->BIP instead"<<std::endl; 733 //std::cout<<"ERROR: conversion from BSQ to BIP isn't practical; use BSQ->BIL->BIP instead"<<std::endl;
639 - ((bsq<float>*)file)->bip(outfile, PROGRESS, OPTIMIZATION); 734 + ((bsq<double>*)file)->bip(outfile, PROGRESS, OPTIMIZATION);
640 //exit(1); 735 //exit(1);
641 } 736 }
642 } 737 }
@@ -989,7 +1084,7 @@ public: @@ -989,7 +1084,7 @@ public:
989 else if(header.data_type == envi_header::float64) 1084 else if(header.data_type == envi_header::float64)
990 return ((bsq<double>*)file)->ph_to_ph((double*)result, lb1, rb1, pos1, lb2, rb2, pos2, mask); 1085 return ((bsq<double>*)file)->ph_to_ph((double*)result, lb1, rb1, pos1, lb2, rb2, pos2, mask);
991 else 1086 else
992 - std::cout<<"ERROR: unidentified data type"<<std::endl; 1087 + std::cout<<"envi::ph_to_ph ERROR - unidentified data type"<<std::endl;
993 } 1088 }
994 1089
995 else if(header.interleave == envi_header::BIL){ //if the infile is bil file 1090 else if(header.interleave == envi_header::BIL){ //if the infile is bil file
@@ -998,7 +1093,7 @@ public: @@ -998,7 +1093,7 @@ public:
998 else if(header.data_type == envi_header::float64) 1093 else if(header.data_type == envi_header::float64)
999 return ((bil<double>*)file)->ph_to_ph((double*)result, lb1, rb1, pos1, lb2, rb2, pos2, mask); 1094 return ((bil<double>*)file)->ph_to_ph((double*)result, lb1, rb1, pos1, lb2, rb2, pos2, mask);
1000 else 1095 else
1001 - std::cout<<"ERROR: unidentified data type"<<std::endl; 1096 + std::cout<<"envi::ph_to_ph ERROR - unidentified data type"<<std::endl;
1002 } 1097 }
1003 1098
1004 else if(header.interleave == envi_header::BIP){ //if the infile is bip file 1099 else if(header.interleave == envi_header::BIP){ //if the infile is bip file
@@ -1007,11 +1102,11 @@ public: @@ -1007,11 +1102,11 @@ public:
1007 else if(header.data_type == envi_header::float64) 1102 else if(header.data_type == envi_header::float64)
1008 return ((bip<double>*)file)->ph_to_ph((double*)result, lb1, rb1, pos1, lb2, rb2, pos2, mask); 1103 return ((bip<double>*)file)->ph_to_ph((double*)result, lb1, rb1, pos1, lb2, rb2, pos2, mask);
1009 else 1104 else
1010 - std::cout<<"ERROR: unidentified data type"<<std::endl; 1105 + std::cout<<"envi::ph_to_ph ERROR - unidentified data type"<<std::endl;
1011 } 1106 }
1012 1107
1013 else{ 1108 else{
1014 - std::cout<<"ERROR: unidentified file type"<<std::endl; 1109 + std::cout<<"envi::ph_to_ph ERROR - unidentified file type"<<std::endl;
1015 exit(1); 1110 exit(1);
1016 } 1111 }
1017 return false; 1112 return false;
@@ -1273,6 +1368,39 @@ public: @@ -1273,6 +1368,39 @@ public:
1273 return false; 1368 return false;
1274 } 1369 }
1275 1370
  1371 + void band_bounds(double wavelength, size_t& low, size_t& high) {
  1372 + if (header.interleave == envi_header::BSQ) { //if the infile is bsq file
  1373 + if (header.data_type == envi_header::float32)
  1374 + ((bsq<float>*)file)->band_bounds(wavelength, low, high);
  1375 + else if (header.data_type == envi_header::float64)
  1376 + ((bsq<double>*)file)->band_bounds(wavelength, low, high);
  1377 + else {
  1378 + std::cout << "ERROR: unidentified data type" << std::endl;
  1379 + exit(1);
  1380 + }
  1381 + }
  1382 + else if (header.interleave == envi_header::BIL) {
  1383 + if (header.data_type == envi_header::float32)
  1384 + ((bil<float>*)file)->band_bounds(wavelength, low, high);
  1385 + else if (header.data_type == envi_header::float64)
  1386 + ((bil<double>*)file)->band_bounds(wavelength, low, high);
  1387 + else {
  1388 + std::cout << "ERROR: unidentified data type" << std::endl;
  1389 + exit(1);
  1390 + }
  1391 + }
  1392 + else if (header.interleave == envi_header::BIP) {
  1393 + if (header.data_type == envi_header::float32)
  1394 + ((bip<float>*)file)->band_bounds(wavelength, low, high);
  1395 + else if (header.data_type == envi_header::float64)
  1396 + ((bip<double>*)file)->band_bounds(wavelength, low, high);
  1397 + else {
  1398 + std::cout << "ERROR: unidentified data type" << std::endl;
  1399 + exit(1);
  1400 + }
  1401 + }
  1402 + }
  1403 +
1276 // Retrieve a spectrum at the specified 1D location 1404 // Retrieve a spectrum at the specified 1D location
1277 1405
1278 /// @param ptr is a pointer to pre-allocated memory of size B*sizeof(T) 1406 /// @param ptr is a pointer to pre-allocated memory of size B*sizeof(T)
@@ -1325,6 +1453,7 @@ public: @@ -1325,6 +1453,7 @@ public:
1325 exit(1); 1453 exit(1);
1326 } 1454 }
1327 } 1455 }
  1456 + free(temp);
1328 } 1457 }
1329 1458
1330 /// Retrieve a spectrum from the specified (x, y) location 1459 /// Retrieve a spectrum from the specified (x, y) location
@@ -1441,16 +1570,16 @@ public: @@ -1441,16 +1570,16 @@ public:
1441 /// @param co is a pointer to pre-allocated memory of size [B * B] that stores the resulting covariance matrix 1570 /// @param co is a pointer to pre-allocated memory of size [B * B] that stores the resulting covariance matrix
1442 /// @param avg is a pointer to memory of size B that stores the average spectrum 1571 /// @param avg is a pointer to memory of size B that stores the average spectrum
1443 /// @param mask is a pointer to memory of size [X * Y] that stores the mask value at each pixel location 1572 /// @param mask is a pointer to memory of size [X * Y] that stores the mask value at each pixel location
1444 - bool co_matrix(double* co, double* avg, unsigned char* mask, bool PROGRESS = false){ 1573 + bool co_matrix(double* co, double* avg, unsigned char* mask, int cuda_device, bool PROGRESS = false){
1445 if (header.interleave == envi_header::BSQ){ 1574 if (header.interleave == envi_header::BSQ){
1446 std::cout<<"ERROR: calculating the covariance matrix for a BSQ file is impractical; convert to BIL or BIP first"<<std::endl; 1575 std::cout<<"ERROR: calculating the covariance matrix for a BSQ file is impractical; convert to BIL or BIP first"<<std::endl;
1447 exit(1); 1576 exit(1);
1448 } 1577 }
1449 else if (header.interleave == envi_header::BIL){ 1578 else if (header.interleave == envi_header::BIL){
1450 if (header.data_type == envi_header::float32) 1579 if (header.data_type == envi_header::float32)
1451 - return ((bil<float>*)file)->co_matrix(co, avg, mask, PROGRESS); 1580 + return ((bil<float>*)file)->co_matrix(co, avg, mask, cuda_device, PROGRESS);
1452 else if (header.data_type == envi_header::float64) 1581 else if (header.data_type == envi_header::float64)
1453 - return ((bil<double>*)file)->co_matrix(co, avg, mask, PROGRESS); 1582 + return ((bil<double>*)file)->co_matrix(co, avg, mask, cuda_device, PROGRESS);
1454 else{ 1583 else{
1455 std::cout << "ERROR: unidentified data type" << std::endl; 1584 std::cout << "ERROR: unidentified data type" << std::endl;
1456 exit(1); 1585 exit(1);
@@ -1458,9 +1587,9 @@ public: @@ -1458,9 +1587,9 @@ public:
1458 } 1587 }
1459 else if (header.interleave == envi_header::BIP){ 1588 else if (header.interleave == envi_header::BIP){
1460 if (header.data_type == envi_header::float32) 1589 if (header.data_type == envi_header::float32)
1461 - return ((bip<float>*)file)->co_matrix(co, avg, mask, PROGRESS); 1590 + return ((bip<float>*)file)->co_matrix(co, avg, mask, cuda_device, PROGRESS);
1462 else if (header.data_type == envi_header::float64) 1591 else if (header.data_type == envi_header::float64)
1463 - return ((bip<double>*)file)->co_matrix(co, avg, mask, PROGRESS); 1592 + return ((bip<double>*)file)->co_matrix(co, avg, mask, cuda_device, PROGRESS);
1464 else{ 1593 else{
1465 std::cout << "ERROR: unidentified data type" << std::endl; 1594 std::cout << "ERROR: unidentified data type" << std::endl;
1466 exit(1); 1595 exit(1);
@@ -1474,7 +1603,7 @@ public: @@ -1474,7 +1603,7 @@ public:
1474 /// @param co is a pointer to pre-allocated memory of size [B * B] that stores the resulting covariance matrix 1603 /// @param co is a pointer to pre-allocated memory of size [B * B] that stores the resulting covariance matrix
1475 /// @param avg is a pointer to memory of size B that stores the average spectrum 1604 /// @param avg is a pointer to memory of size B that stores the average spectrum
1476 /// @param mask is a pointer to memory of size [X * Y] that stores the mask value at each pixel location 1605 /// @param mask is a pointer to memory of size [X * Y] that stores the mask value at each pixel location
1477 - bool coNoise_matrix(double* coN, double* avg, unsigned char* mask, bool PROGRESS = false){ 1606 + bool coNoise_matrix(double* coN, double* avg, unsigned char* mask, int cuda_device = 0, bool PROGRESS = false){
1478 if (header.interleave == envi_header::BSQ){ 1607 if (header.interleave == envi_header::BSQ){
1479 std::cout<<"ERROR: calculating the covariance matrix of noise for a BSQ file is impractical; convert to BIP first"<<std::endl; 1608 std::cout<<"ERROR: calculating the covariance matrix of noise for a BSQ file is impractical; convert to BIP first"<<std::endl;
1480 exit(1); 1609 exit(1);
@@ -1488,9 +1617,9 @@ public: @@ -1488,9 +1617,9 @@ public:
1488 1617
1489 else if (header.interleave == envi_header::BIP){ 1618 else if (header.interleave == envi_header::BIP){
1490 if (header.data_type == envi_header::float32) 1619 if (header.data_type == envi_header::float32)
1491 - return ((bip<float>*)file)->coNoise_matrix(coN, avg, mask, PROGRESS); 1620 + return ((bip<float>*)file)->coNoise_matrix(coN, avg, mask, cuda_device, PROGRESS);
1492 else if (header.data_type == envi_header::float64) 1621 else if (header.data_type == envi_header::float64)
1493 - return ((bip<double>*)file)->coNoise_matrix(coN, avg, mask, PROGRESS); 1622 + return ((bip<double>*)file)->coNoise_matrix(coN, avg, mask, cuda_device, PROGRESS);
1494 else{ 1623 else{
1495 std::cout << "ERROR: unidentified data type" << std::endl; 1624 std::cout << "ERROR: unidentified data type" << std::endl;
1496 exit(1); 1625 exit(1);
@@ -1517,11 +1646,11 @@ public: @@ -1517,11 +1646,11 @@ public:
1517 1646
1518 //save the header for the cropped file 1647 //save the header for the cropped file
1519 stim::envi_header new_header = header; 1648 stim::envi_header new_header = header;
1520 - new_header.samples = x1 - x0;  
1521 - new_header.lines = y1 - y0;  
1522 - new_header.bands = b1 - b0; 1649 + new_header.samples = x1 - x0 + 1;
  1650 + new_header.lines = y1 - y0 + 1;
  1651 + new_header.bands = b1 - b0 + 1;
1523 std::vector<double>::const_iterator first = new_header.wavelength.begin() + b0; 1652 std::vector<double>::const_iterator first = new_header.wavelength.begin() + b0;
1524 - std::vector<double>::const_iterator last = new_header.wavelength.begin() + b1; 1653 + std::vector<double>::const_iterator last = new_header.wavelength.begin() + b1 + 1;
1525 new_header.wavelength = std::vector<double>(first, last); 1654 new_header.wavelength = std::vector<double>(first, last);
1526 new_header.save(outfile + ".hdr"); 1655 new_header.save(outfile + ".hdr");
1527 1656
@@ -1558,6 +1687,41 @@ public: @@ -1558,6 +1687,41 @@ public:
1558 return false; 1687 return false;
1559 } 1688 }
1560 1689
  1690 + void subimages(std::string outfile, size_t nx, size_t ny, unsigned char* mask, bool PROGRESS = false){
  1691 +
  1692 + size_t nnz = 0; //initialize the number of subimages to zero
  1693 + for(size_t i = 0; i < header.lines * header.samples; i++) //for each pixel in the mask
  1694 + if(mask[i]) nnz++; //if the pixel is valid, add a subimage
  1695 +
  1696 +
  1697 + //save the header for the cropped file
  1698 + stim::envi_header new_header = header;
  1699 + new_header.samples = nx; //calculate the width of the output image (concatenated subimages)
  1700 + new_header.lines = nnz * ny; //calculate the height of the output image (height of subimages)
  1701 +
  1702 +
  1703 + if (header.interleave == envi_header::BSQ){
  1704 + if (header.data_type == envi_header::float32)
  1705 + ((bsq<float>*)file)->subimages(outfile, nx, ny, mask, PROGRESS);
  1706 + else if (header.data_type == envi_header::float64)
  1707 + ((bsq<double>*)file)->subimages(outfile, nx, ny, mask, PROGRESS);
  1708 + else{
  1709 + std::cout << "ERROR: unidentified data type" << std::endl;
  1710 + exit(1);
  1711 + }
  1712 + }
  1713 + else if (header.interleave == envi_header::BIL){
  1714 + std::cout << "ERROR: unidentified data type" << std::endl;
  1715 + exit(1);
  1716 + }
  1717 + else if (header.interleave == envi_header::BIP){
  1718 + std::cout << "ERROR: unidentified data type" << std::endl;
  1719 + exit(1);
  1720 + }
  1721 +
  1722 + new_header.save(outfile + ".hdr"); //save the header for the output file
  1723 + }
  1724 +
1561 /// Remove a list of bands from the ENVI file 1725 /// Remove a list of bands from the ENVI file
1562 1726
1563 /// @param outfile is the file name for the output hyperspectral image (with trimmed bands) 1727 /// @param outfile is the file name for the output hyperspectral image (with trimmed bands)
@@ -1665,6 +1829,67 @@ public: @@ -1665,6 +1829,67 @@ public:
1665 } 1829 }
1666 } 1830 }
1667 1831
  1832 + void append(std::string outfile, envi C, bool PROGRESS = false) {
  1833 + if (C.header.samples != header.samples || //verify that the images are the same size
  1834 + C.header.lines != header.lines) {
  1835 + std::cout << "ERROR - appended images must be the same size: input = [" << header.samples << " x " << header.lines << "], output = [" << C.header.samples << " x " << C.header.lines << "]" << std::endl;
  1836 + exit(1);
  1837 + }
  1838 + if (C.header.interleave != header.interleave) {
  1839 + std::cout << "ERROR - appended images must have the same interleave format" << std::endl;
  1840 + exit(1);
  1841 + }
  1842 +
  1843 + stim::envi_header new_header = header; //create a header for the output image
  1844 + new_header.bands = header.bands + C.header.bands; //calculate the number of bands in the new image
  1845 +
  1846 + if (header.wavelength.size() != 0 && C.header.wavelength.size() != 0) { //if both files contain wavelength information
  1847 + for (size_t b = 0; b < C.header.wavelength.size(); b++)
  1848 + new_header.wavelength.push_back(C.header.wavelength[b]); //append the wavelength labels to the new header array
  1849 + }
  1850 + else new_header.wavelength.clear();
  1851 +
  1852 + if (header.band_names.size() != 0 && C.header.band_names.size() != 0) { //if both files contain band name information
  1853 + for (size_t b = 0; b < C.header.band_names.size(); b++)
  1854 + new_header.band_names.push_back(C.header.band_names[b]); //append the wavelength labels to the new header array
  1855 + }
  1856 + else new_header.wavelength.clear();
  1857 +
  1858 + new_header.save(outfile + ".hdr"); //save the output
  1859 +
  1860 + if (header.interleave == envi_header::BSQ) {
  1861 + if (header.data_type == envi_header::float32)
  1862 + ((bsq<float>*)file)->append(outfile, (bsq<float>*)C.file, PROGRESS);
  1863 + else if (header.data_type == envi_header::float64)
  1864 + ((bsq<double>*)file)->append(outfile, (bsq<double>*)C.file, PROGRESS);
  1865 + else {
  1866 + std::cout << "ERROR: unidentified data type" << std::endl;
  1867 + exit(1);
  1868 + }
  1869 + }
  1870 + else if (header.interleave == envi_header::BIL) {
  1871 + if (header.data_type == envi_header::float32)
  1872 + ((bil<float>*)file)->append(outfile, (bil<float>*)C.file, PROGRESS);
  1873 + else if (header.data_type == envi_header::float64)
  1874 + ((bil<double>*)file)->append(outfile, (bil<double>*)C.file, PROGRESS);
  1875 + else {
  1876 + std::cout << "ERROR: unidentified data type" << std::endl;
  1877 + exit(1);
  1878 + }
  1879 + }
  1880 + else if (header.interleave == envi_header::BIP) {
  1881 + if (header.data_type == envi_header::float32)
  1882 + ((bip<float>*)file)->append(outfile, (bip<float>*)C.file, PROGRESS);
  1883 + else if (header.data_type == envi_header::float64)
  1884 + ((bip<double>*)file)->append(outfile, (bip<double>*)C.file, PROGRESS);
  1885 + else {
  1886 + std::cout << "ERROR: unidentified data type" << std::endl;
  1887 + exit(1);
  1888 + }
  1889 + }
  1890 +
  1891 + }
  1892 +
1668 /// Convolve the given band range with a kernel specified by a vector of coefficients. 1893 /// Convolve the given band range with a kernel specified by a vector of coefficients.
1669 1894
1670 /// @param outfile is the combined file to be output 1895 /// @param outfile is the combined file to be output
@@ -1835,6 +2060,44 @@ public: @@ -1835,6 +2060,44 @@ public:
1835 } 2060 }
1836 exit(1); 2061 exit(1);
1837 } 2062 }
  2063 +
  2064 +
  2065 +
  2066 +
  2067 + void fft(std::string outfile, double band_min, double band_max, size_t samples = 0, void* ratio = NULL, size_t rx = 0, size_t ry = 0, bool PROGRESS = false, int cuda_device = 0){
  2068 + if(samples == 0) samples = header.bands;
  2069 + //double B = (double)header.bands;
  2070 + double delta = header.wavelength[1] - header.wavelength[0]; //calculate spacing in the current domain
  2071 + double span = samples * delta; //calculate the span in the current domain
  2072 + double fft_delta = 1.0 / span; //calculate the span in the FFT domain
  2073 + double fft_max = fft_delta * samples/2; //calculate the maximum range of the FFT
  2074 +
  2075 + if(band_max > fft_max) band_max = fft_max; //the user gave a band outside of the FFT range, reset the band to the maximum available
  2076 + size_t start_i = (size_t)std::ceil(band_min / fft_delta); //calculate the first band to store
  2077 + size_t size_i = (size_t)std::floor(band_max / fft_delta) - start_i + 1; //calculate the number of bands to store
  2078 + size_t end_i = start_i + size_i - 1; //last band number
  2079 +
  2080 + envi_header new_header = header;
  2081 + new_header.bands = size_i;
  2082 + new_header.set_wavelengths(start_i * fft_delta, fft_delta);
  2083 + new_header.wavelength_units = "inv_" + header.wavelength_units;
  2084 + new_header.save(outfile + ".hdr");
  2085 +
  2086 + if (header.interleave == envi_header::BIP){
  2087 + if (header.data_type == envi_header::float32)
  2088 + ((bip<float>*)file)->fft(outfile, start_i, end_i, samples, (float*)ratio, rx, ry, PROGRESS, cuda_device);
  2089 + else if (header.data_type == envi_header::float64)
  2090 + ((bip<double>*)file)->fft(outfile, start_i, end_i, samples, (double*)ratio, rx, ry, PROGRESS, cuda_device);
  2091 + else{
  2092 + std::cout << "ERROR: unidentified data type" << std::endl;
  2093 + exit(1);
  2094 + }
  2095 + }
  2096 + else{
  2097 + std::cout<<"ERROR: only BIP files supported for FFT"<<std::endl;
  2098 + exit(1);
  2099 + }
  2100 + }
1838 }; //end ENVI 2101 }; //end ENVI
1839 2102
1840 } //end namespace rts 2103 } //end namespace rts
stim/envi/envi_header.h
@@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
8 #include <vector> 8 #include <vector>
9 #include <algorithm> 9 #include <algorithm>
10 #include <stdlib.h> 10 #include <stdlib.h>
  11 +#include <cmath>
11 12
12 //information from an ENVI header file 13 //information from an ENVI header file
13 //A good resource can be found here: http://www.exelisvis.com/docs/enviheaderfiles.html 14 //A good resource can be found here: http://www.exelisvis.com/docs/enviheaderfiles.html
@@ -77,6 +78,14 @@ struct envi_header @@ -77,6 +78,14 @@ struct envi_header
77 load(name); 78 load(name);
78 } 79 }
79 80
  81 + //sets the wavelength vector given a starting value and uniform step size
  82 + void set_wavelengths(double start, double step){
  83 + size_t B = bands; //get the number of bands
  84 + wavelength.resize(B);
  85 + for(size_t b = 0; b < B; b++)
  86 + wavelength[b] = start + b * step;
  87 + }
  88 +
80 std::string trim(std::string line){ 89 std::string trim(std::string line){
81 90
82 if(line.length() == 0) 91 if(line.length() == 0)
@@ -416,8 +425,13 @@ struct envi_header @@ -416,8 +425,13 @@ struct envi_header
416 default: 425 default:
417 return 0; 426 return 0;
418 } 427 }
  428 + }
419 429
  430 + //return the number of bytes that SHOULD be in the data file
  431 + size_t data_bytes(){
  432 + return samples * lines * bands * valsize() + header_offset;
420 } 433 }
  434 +
421 435
422 /// Convert an interleave type to a string 436 /// Convert an interleave type to a string
423 static std::string interleave_str(interleaveType t){ 437 static std::string interleave_str(interleaveType t){
1 #ifndef STIM_HSI_H 1 #ifndef STIM_HSI_H
2 #define STIM_HSI_H 2 #define STIM_HSI_H
3 3
4 -#include "../envi/envi_header.h"  
5 -#include "../envi/binary.h" 4 +#include <stim/envi/envi_header.h>
  5 +#include <stim/envi/binary.h>
6 #include <cstring> 6 #include <cstring>
7 #include <utility> 7 #include <utility>
8 8
@@ -55,43 +55,19 @@ protected: @@ -55,43 +55,19 @@ protected:
55 return n; //return the number of masked pixels 55 return n; //return the number of masked pixels
56 } 56 }
57 57
  58 + //perform linear interpolation between two bands
58 T lerp(double w, T low_v, double low_w, T high_v, double high_w){ 59 T lerp(double w, T low_v, double low_w, T high_v, double high_w){
59 if(low_w == high_w) return low_v; //if the interval is of zero length, just return one of the bounds 60 if(low_w == high_w) return low_v; //if the interval is of zero length, just return one of the bounds
60 double alpha = (w - low_w) / (high_w - low_w); //calculate the interpolation factor 61 double alpha = (w - low_w) / (high_w - low_w); //calculate the interpolation factor
61 return (T)((1.0 - alpha) * low_v + alpha * high_v); //interpolate 62 return (T)((1.0 - alpha) * low_v + alpha * high_v); //interpolate
62 } 63 }
63 64
64 - /// Gets the two band indices surrounding a given wavelength  
65 - void band_bounds(double wavelength, unsigned long long& low, unsigned long long& high){  
66 - unsigned long long B = Z();  
67 - for(high = 0; high < B; high++){  
68 - if(w[high] > wavelength) break;  
69 - }  
70 - low = 0;  
71 - if(high > 0)  
72 - low = high-1;  
73 - }  
74 -  
75 - /// Get the list of band numbers that bound a list of wavelengths  
76 - void band_bounds(std::vector<double> wavelengths,  
77 - std::vector<unsigned long long>& low_bands,  
78 - std::vector<unsigned long long>& high_bands){  
79 -  
80 - unsigned long long W = w.size(); //get the number of wavelengths in the list  
81 - low_bands.resize(W); //pre-allocate space for the band lists  
82 - high_bands.resize(W);  
83 -  
84 - for(unsigned long long wl = 0; wl < W; wl++){ //for each wavelength  
85 - band_bounds(wavelengths[wl], low_bands[wl], high_bands[wl]); //find the low and high bands  
86 - }  
87 - }  
88 -  
89 /// Returns the interpolated in the given spectrum based on the given wavelength 65 /// Returns the interpolated in the given spectrum based on the given wavelength
90 66
91 /// @param s is the spectrum in main memory of length Z() 67 /// @param s is the spectrum in main memory of length Z()
92 /// @param wavelength is the wavelength value to interpolate out 68 /// @param wavelength is the wavelength value to interpolate out
93 T interp_spectrum(T* s, double wavelength){ 69 T interp_spectrum(T* s, double wavelength){
94 - unsigned long long low, high; //indices for the bands surrounding wavelength 70 + size_t low, high; //indices for the bands surrounding wavelength
95 band_bounds(wavelength, low, high); //get the surrounding band indices 71 band_bounds(wavelength, low, high); //get the surrounding band indices
96 72
97 if(high == w.size()) return s[w.size()-1]; //if the high band is above the wavelength range, return the highest wavelength value 73 if(high == w.size()) return s[w.size()-1]; //if the high band is above the wavelength range, return the highest wavelength value
@@ -138,11 +114,36 @@ protected: @@ -138,11 +114,36 @@ protected:
138 } 114 }
139 115
140 public: 116 public:
  117 +
  118 + /// Gets the two band indices surrounding a given wavelength
  119 + void band_bounds(double wavelength, size_t& low, size_t& high) {
  120 + size_t B = Z();
  121 + for (high = 0; high < B; high++) {
  122 + if (w[high] > wavelength) break;
  123 + }
  124 + low = 0;
  125 + if (high > 0)
  126 + low = high - 1;
  127 + }
  128 +
  129 + /// Get the list of band numbers that bound a list of wavelengths
  130 + void band_bounds(std::vector<double> wavelengths,
  131 + std::vector<unsigned long long>& low_bands,
  132 + std::vector<unsigned long long>& high_bands) {
  133 +
  134 + unsigned long long W = w.size(); //get the number of wavelengths in the list
  135 + low_bands.resize(W); //pre-allocate space for the band lists
  136 + high_bands.resize(W);
  137 +
  138 + for (unsigned long long wl = 0; wl < W; wl++) { //for each wavelength
  139 + band_bounds(wavelengths[wl], low_bands[wl], high_bands[wl]); //find the low and high bands
  140 + }
  141 + }
141 /// Get a mask that has all pixels with inf or NaN values masked out (false) 142 /// Get a mask that has all pixels with inf or NaN values masked out (false)
142 void mask_finite(unsigned char* out_mask, unsigned char* mask, bool PROGRESS = false){ 143 void mask_finite(unsigned char* out_mask, unsigned char* mask, bool PROGRESS = false){
143 size_t XY = X() * Y(); 144 size_t XY = X() * Y();
144 if(mask == NULL) //if no mask is provided 145 if(mask == NULL) //if no mask is provided
145 - memset(mask, 255, XY * sizeof(unsigned char)); //initialize the mask to 255 146 + memset(out_mask, 255, XY * sizeof(unsigned char)); //initialize the mask to 255
146 else //if a mask is provided 147 else //if a mask is provided
147 memcpy(out_mask, mask, XY * sizeof(unsigned char)); //initialize the current mask to that one 148 memcpy(out_mask, mask, XY * sizeof(unsigned char)); //initialize the current mask to that one
148 T* page = (T*)malloc(R[0] * R[1] * sizeof(T)); //allocate space for a page of data 149 T* page = (T*)malloc(R[0] * R[1] * sizeof(T)); //allocate space for a page of data
@@ -224,4 +225,4 @@ public: @@ -224,4 +225,4 @@ public:
224 225
225 } //end namespace STIM 226 } //end namespace STIM
226 227
227 -#endif  
228 \ No newline at end of file 228 \ No newline at end of file
  229 +#endif
stim/gl/gl_spider.h
1 -#ifndef STIM_GL_SPIDER_H 1 + #ifndef STIM_GL_SPIDER_H
2 #define STIM_GL_SPIDER_H 2 #define STIM_GL_SPIDER_H
3 3
4 //#include <GL/glew.h> 4 //#include <GL/glew.h>
@@ -27,7 +27,6 @@ @@ -27,7 +27,6 @@
27 #include <stim/cuda/branch_detection.cuh> 27 #include <stim/cuda/branch_detection.cuh>
28 #include "../../../volume-spider/glnetwork.h" 28 #include "../../../volume-spider/glnetwork.h"
29 #include <stim/visualization/cylinder.h> 29 #include <stim/visualization/cylinder.h>
30 -#include <stim/cuda/testKernel.cuh>  
31 #include <iostream> 30 #include <iostream>
32 #include <fstream> 31 #include <fstream>
33 #ifdef TIMING 32 #ifdef TIMING
@@ -40,6 +39,9 @@ @@ -40,6 +39,9 @@
40 #include <ctime> 39 #include <ctime>
41 #endif 40 #endif
42 41
  42 +#ifdef DEBUG
  43 + #include <stim/cuda/testKernel.cuh>
  44 +#endif
43 45
44 namespace stim 46 namespace stim
45 { 47 {
@@ -138,11 +140,13 @@ class gl_spider // : public virtual gl_texture&lt;T&gt; @@ -138,11 +140,13 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
138 stim::cuda::cuda_texture t_pos; //cuda_texture object used as an interface between OpenGL and cuda for position vectors. 140 stim::cuda::cuda_texture t_pos; //cuda_texture object used as an interface between OpenGL and cuda for position vectors.
139 stim::cuda::cuda_texture t_mag; //cuda_texture object used as an interface between OpenGL and cuda for size vectors. 141 stim::cuda::cuda_texture t_mag; //cuda_texture object used as an interface between OpenGL and cuda for size vectors.
140 stim::cuda::cuda_texture t_len; //cuda_texture object used as an interface between OpenGL and cuda for size vectors. 142 stim::cuda::cuda_texture t_len; //cuda_texture object used as an interface between OpenGL and cuda for size vectors.
141 - 143 +
  144 + int last_fiber; //variable that tracks the last fiber hit during tracing. -1 if no fiber was hit.
  145 +
142 146
143 #ifdef DEBUG 147 #ifdef DEBUG
144 - stringstream name;  
145 int iter; 148 int iter;
  149 + stringstream name;
146 int iter_pos; 150 int iter_pos;
147 int iter_dir; 151 int iter_dir;
148 int iter_siz; 152 int iter_siz;
@@ -292,6 +296,7 @@ class gl_spider // : public virtual gl_texture&lt;T&gt; @@ -292,6 +296,7 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
292 DrawLongCylinder(n, l_template, l_square); ///Draw the cylinder. 296 DrawLongCylinder(n, l_template, l_square); ///Draw the cylinder.
293 stim::cylinder<float> cyl(cL, cM); 297 stim::cylinder<float> cyl(cL, cM);
294 std::vector< stim::vec<float> > result = find_branch(cylinder_texID, GL_TEXTURE_2D, n*l_square, (cL.size()-1)*l_template); ///find all the centers in cuda 298 std::vector< stim::vec<float> > result = find_branch(cylinder_texID, GL_TEXTURE_2D, n*l_square, (cL.size()-1)*l_template); ///find all the centers in cuda
  299 +
295 stim::vec3<float> size(S[0]*R[0], S[1]*R[1], S[2]*R[2]); ///the borders of the texture. 300 stim::vec3<float> size(S[0]*R[0], S[1]*R[1], S[2]*R[2]); ///the borders of the texture.
296 float pval; //pvalue associated with the points on the cylinder. 301 float pval; //pvalue associated with the points on the cylinder.
297 if(!result.empty()) ///if we have any points 302 if(!result.empty()) ///if we have any points
@@ -315,7 +320,8 @@ class gl_spider // : public virtual gl_texture&lt;T&gt; @@ -315,7 +320,8 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
315 } 320 }
316 stim::vec3<float> v = cyl.surf(pval, result[i][0]); ///find the coordinates of the point at pval on the surface in tissue space. 321 stim::vec3<float> v = cyl.surf(pval, result[i][0]); ///find the coordinates of the point at pval on the surface in tissue space.
317 stim::vec3<float> di = cyl.p(pval); ///find the coord of v in tissue space projected on the centerline. 322 stim::vec3<float> di = cyl.p(pval); ///find the coord of v in tissue space projected on the centerline.
318 - float rad = cyl.r(pval)/2; ///find the radius at the pvalue's location 323 + float rad = cyl.r(pval); ///find the radius at the pvalue's location
  324 + // float rad = cyl.r(pval)/2; ///find the radius at the pvalue's location
319 if( 325 if(
320 !(v[0] > size[0] || v[1] > size[1] 326 !(v[0] > size[0] || v[1] > size[1]
321 || v[2] > size[2] || v[0] < 0 327 || v[2] > size[2] || v[0] < 0
@@ -372,7 +378,7 @@ class gl_spider // : public virtual gl_texture&lt;T&gt; @@ -372,7 +378,7 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
372 ///Stored in a display list. 378 ///Stored in a display list.
373 ///uses the default d vector <0,0,1> 379 ///uses the default d vector <0,0,1>
374 void 380 void
375 - genDirectionVectors(float solidAngle = stim::PI/2) 381 + genDirectionVectors(float solidAngle = 3*stim::PI/4)
376 { 382 {
377 383
378 //Set up the vectors necessary for Rectangle creation. 384 //Set up the vectors necessary for Rectangle creation.
@@ -954,7 +960,7 @@ class gl_spider // : public virtual gl_texture&lt;T&gt; @@ -954,7 +960,7 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
954 iter_dir = 0; 960 iter_dir = 0;
955 iter_siz = 0; 961 iter_siz = 0;
956 #endif 962 #endif
957 - stepsize = 3.0; 963 + stepsize = 6.0;
958 n_pixels = 16.0; 964 n_pixels = 16.0;
959 965
960 srand(100); 966 srand(100);
@@ -1316,20 +1322,20 @@ class gl_spider // : public virtual gl_texture&lt;T&gt; @@ -1316,20 +1322,20 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1316 void 1322 void
1317 saveNetwork(std::string name) 1323 saveNetwork(std::string name)
1318 { 1324 {
1319 -/* stim::glObj<float> sk; 1325 + stim::glObj<float> sk1;
1320 for(int i = 0; i < nt.sizeE(); i++) 1326 for(int i = 0; i < nt.sizeE(); i++)
1321 { 1327 {
1322 - std::vector<stim::vec< float > > cm = nt.getEdgeCenterLineMag(i); 1328 + std::vector<float> cm = nt.getEdgeCenterLineMag(i);
1323 std::vector<stim::vec3< float > > ce = nt.getEdgeCenterLine(i); 1329 std::vector<stim::vec3< float > > ce = nt.getEdgeCenterLine(i);
1324 - sk.Begin(stim::OBJ_LINE); 1330 + sk1.Begin(stim::OBJ_LINE);
1325 for(int j = 0; j < ce.size(); j++) 1331 for(int j = 0; j < ce.size(); j++)
1326 { 1332 {
1327 - sk.TexCoord(cm[j][0]);  
1328 - sk.Vertex(ce[j][0], ce[j][1], ce[j][2]); 1333 + sk1.TexCoord(cm[j]);
  1334 + sk1.Vertex(ce[j][0], ce[j][1], ce[j][2]);
1329 } 1335 }
1330 - sk.End(); 1336 + sk1.End();
1331 } 1337 }
1332 -*/ sk.save(name); 1338 + sk1.save(name);
1333 } 1339 }
1334 1340
1335 ///Depreciated, but might be reused later() 1341 ///Depreciated, but might be reused later()
@@ -1377,20 +1383,31 @@ class gl_spider // : public virtual gl_texture&lt;T&gt; @@ -1377,20 +1383,31 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1377 Step() 1383 Step()
1378 { 1384 {
1379 #ifdef DEBUG 1385 #ifdef DEBUG
1380 - std::cerr << "Took a step" << std::endl; 1386 + std::cerr << "Took a step";
1381 #endif 1387 #endif
1382 Bind(direction_texID, direction_buffID, numSamples, n_pixels); 1388 Bind(direction_texID, direction_buffID, numSamples, n_pixels);
1383 CHECK_OPENGL_ERROR 1389 CHECK_OPENGL_ERROR
1384 findOptimalDirection(); 1390 findOptimalDirection();
1385 Unbind(); 1391 Unbind();
  1392 + #ifdef DEBUG
  1393 + std::cerr << " " << current_cost;
  1394 + #endif
1386 Bind(position_texID, position_buffID, numSamplesPos, n_pixels); 1395 Bind(position_texID, position_buffID, numSamplesPos, n_pixels);
1387 findOptimalPosition(); 1396 findOptimalPosition();
1388 Unbind(); 1397 Unbind();
  1398 + #ifdef DEBUG
  1399 + std::cerr << " " << current_cost;
  1400 + #endif
1389 Bind(radius_texID, radius_buffID, numSamplesMag, n_pixels); 1401 Bind(radius_texID, radius_buffID, numSamplesMag, n_pixels);
1390 findOptimalRadius(); 1402 findOptimalRadius();
1391 Unbind(); 1403 Unbind();
  1404 + #ifdef DEBUG
  1405 + std::cerr << " " << current_cost;
  1406 + #endif
1392 CHECK_OPENGL_ERROR 1407 CHECK_OPENGL_ERROR
1393 - 1408 + #ifdef DEBUG
  1409 + std::cerr << std::endl;
  1410 + #endif
1394 return current_cost; 1411 return current_cost;
1395 } 1412 }
1396 1413
@@ -1517,9 +1534,6 @@ class gl_spider // : public virtual gl_texture&lt;T&gt; @@ -1517,9 +1534,6 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1517 while(!Empty()) 1534 while(!Empty())
1518 { 1535 {
1519 //clear the currently traced line and start a new one. 1536 //clear the currently traced line and start a new one.
1520 - cL.clear();  
1521 - cM.clear();  
1522 - cD.clear();  
1523 curSeed = seeds.top(); 1537 curSeed = seeds.top();
1524 curSeedVec = seedsvecs.top(); 1538 curSeedVec = seedsvecs.top();
1525 curSeedMag = seedsmags.top(); 1539 curSeedMag = seedsmags.top();
@@ -1539,9 +1553,9 @@ class gl_spider // : public virtual gl_texture&lt;T&gt; @@ -1539,9 +1553,9 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1539 // findOptimalDirection(); 1553 // findOptimalDirection();
1540 // Unbind(); 1554 // Unbind();
1541 //THIS IS EXPERIMENTAL 1555 //THIS IS EXPERIMENTAL
1542 - Bind(radius_texID, radius_buffID, numSamplesMag, n_pixels);  
1543 - findOptimalRadius();  
1544 - Unbind(); 1556 + // Bind(radius_texID, radius_buffID, numSamplesMag, n_pixels);
  1557 + // findOptimalRadius();
  1558 + // Unbind();
1545 //THIS IS EXPERIMENTAL 1559 //THIS IS EXPERIMENTAL
1546 1560
1547 // cL.push_back(curSeed); 1561 // cL.push_back(curSeed);
@@ -1593,17 +1607,17 @@ class gl_spider // : public virtual gl_texture&lt;T&gt; @@ -1593,17 +1607,17 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1593 ds[0], ds[1], ds[2], 1607 ds[0], ds[1], ds[2],
1594 ups[0], ups[1], ups[2]); 1608 ups[0], ups[1], ups[2]);
1595 ///Set the look at distance 1609 ///Set the look at distance
1596 - sk.Render(); ///Render the network  
1597 -// nt.Render(); 1610 +// sk.Render(); ///Render the network
  1611 + nt.Render();
1598 1612
1599 CHECK_OPENGL_ERROR 1613 CHECK_OPENGL_ERROR
1600 1614
1601 1615
1602 - glLoadName((int) sk.numL()); ///Load all the names  
1603 -// glLoadName(nt.sizeE()); 1616 +// glLoadName((int) sk.numL()); ///Load all the names
  1617 + glLoadName(nt.sizeE());
1604 1618
1605 - sk.RenderLine(cL); ///Render the current line.  
1606 -// nt.RenderLine(cL); 1619 +// sk.RenderLine(cL); ///Render the current line.
  1620 + nt.RenderLine(cL);
1607 1621
1608 // glPopName(); 1622 // glPopName();
1609 glFlush(); ///Flush the buffer 1623 glFlush(); ///Flush the buffer
@@ -1654,55 +1668,51 @@ class gl_spider // : public virtual gl_texture&lt;T&gt; @@ -1654,55 +1668,51 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1654 cM.clear(); 1668 cM.clear();
1655 } 1669 }
1656 1670
1657 -/* 1671 +
1658 void 1672 void
1659 - addToNetwork(pair<stim::fiber<float>, int> in, stim::vec3<float> spos,  
1660 - stim::vec<float> smag, stim::vec3<float> sdir) 1673 + addToNetwork(std::vector<stim::vec3<float> > L, std::vector<float > M, stim::vec3<float> spos, stim::vec3<float> sdir, float smag)
1661 { 1674 {
1662 - #ifdef TIMING  
1663 - double s = std::clock();  
1664 - #endif  
1665 -  
1666 - std::vector<stim::vec3<float> > ce = in.first.centerline();  
1667 - std::vector<stim::vec<float> > cm = in.first.centerlinemag();  
1668 //if the fiber is longer than 2 steps (the number it takes to diverge) 1675 //if the fiber is longer than 2 steps (the number it takes to diverge)
1669 - if(ce.size() > 3) 1676 + if(L.size() > 3)
1670 { 1677 {
1671 //if we did not hit a fiber 1678 //if we did not hit a fiber
1672 - if(in.second == -1) 1679 + if(last_fiber == -1)
1673 { 1680 {
1674 - spos[0] = spos[0]-sdir[0]*smag[0]/2.;  
1675 - spos[1] = spos[1]-sdir[1]*smag[0]/2.;  
1676 - spos[2] = spos[2]-sdir[2]*smag[0]/2.;  
1677 - int h = selectObject(spos, -sdir, smag[0]); 1681 + spos[0] = spos[0]-sdir[0]*smag;
  1682 + spos[1] = spos[1]-sdir[1]*smag;
  1683 + spos[2] = spos[2]-sdir[2]*smag;
  1684 + int h = selectObject(spos, -sdir, smag);
1678 //did we start with a fiber? 1685 //did we start with a fiber?
1679 if(h != -1 && h < nt.sizeE()) 1686 if(h != -1 && h < nt.sizeE())
1680 - nt.addEdge(ce, cm, h, -1); 1687 + nt.addEdge(L, M, h, -1);
1681 else 1688 else
1682 - nt.addEdge(ce, cm, -1, -1); 1689 + nt.addEdge(L, M, -1, -1);
1683 } 1690 }
1684 //if we hit a fiber? 1691 //if we hit a fiber?
1685 - else if(in.second != -1) 1692 + else if(last_fiber != -1)
1686 { 1693 {
1687 - nt.addEdge(ce,cm,-1, in.second);  
1688 - spos[0] = spos[0]-sdir[0]*smag[0]/2.;  
1689 - spos[1] = spos[1]-sdir[1]*smag[0]/2.;  
1690 - spos[2] = spos[2]-sdir[2]*smag[0]/2.;  
1691 - int h = selectObject(spos, -sdir, smag[0]); 1694 + nt.addEdge(L, M, -1, last_fiber);
  1695 + spos[0] = spos[0]-sdir[0]*smag;
  1696 + spos[1] = spos[1]-sdir[1]*smag;
  1697 + spos[2] = spos[2]-sdir[2]*smag;
  1698 + int h = selectObject(spos, -sdir, smag);
1692 //did start with a fiber? 1699 //did start with a fiber?
1693 if(h != -1 && h < nt.sizeE()){ 1700 if(h != -1 && h < nt.sizeE()){
1694 // std::cout << "got here double" << smag.str() << std::endl; 1701 // std::cout << "got here double" << smag.str() << std::endl;
1695 - nt.addEdge(ce,cm, h, in.second);  
1696 - } else { nt.addEdge(ce,cm, -1, -1);} 1702 + nt.addEdge(L, M, h, last_fiber);
  1703 + }
  1704 + else
  1705 + {
  1706 + nt.addEdge(L, M, -1, -1);
  1707 + }
1697 } 1708 }
1698 } 1709 }
1699 1710
1700 - #ifdef TIMING  
1701 - double nt = (std::clock() - s) / (double) CLOCKS_PER_SEC;  
1702 - network_time += nt * 1000.0; 1711 + #ifdef DEBUG
  1712 + iter++;
1703 #endif 1713 #endif
1704 } 1714 }
1705 -*/ 1715 +/*
1706 void 1716 void
1707 addToNetwork(std::vector<stim::vec3<float> > L, std::vector<float > M) 1717 addToNetwork(std::vector<stim::vec3<float> > L, std::vector<float > M)
1708 { 1718 {
@@ -1722,7 +1732,7 @@ class gl_spider // : public virtual gl_texture&lt;T&gt; @@ -1722,7 +1732,7 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1722 #endif 1732 #endif
1723 } 1733 }
1724 } 1734 }
1725 - 1735 +*/
1726 1736
1727 void 1737 void
1728 printSizes() 1738 printSizes()
@@ -1735,22 +1745,31 @@ class gl_spider // : public virtual gl_texture&lt;T&gt; @@ -1735,22 +1745,31 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1735 traceLine(stim::vec3<float> pos, float mag, int min_cost) 1745 traceLine(stim::vec3<float> pos, float mag, int min_cost)
1736 { 1746 {
1737 //starting (seed) position and magnitude. 1747 //starting (seed) position and magnitude.
  1748 + last_fiber = -1;
  1749 + cL.clear();
  1750 + cM.clear();
  1751 + cD.clear();
  1752 +
1738 stim::vec3<float> spos = getPosition(); 1753 stim::vec3<float> spos = getPosition();
  1754 + stim::vec3<float> sdir = getDirection();
1739 float smag = getMagnitude(); 1755 float smag = getMagnitude();
1740 - stim::vec3<float> sdir = getDirection();  
1741 1756
1742 -// Bind();  
1743 -// sk.Begin(stim::OBJ_LINE); 1757 + setPosition(pos);
  1758 + setMagnitude(mag);
1744 1759
  1760 + cL.push_back(p);
  1761 + cD.push_back(d);
  1762 + cM.push_back(m);
  1763 +// stim::vec3<float> spos = getPosition();
  1764 +// float smag = getMagnitude();
  1765 +// stim::vec3<float> sdir = getDirection();
1745 1766
1746 - sk.createFromSelf(GL_SELECT);  
1747 -// nt.createFromSelf(GL_SELECT); 1767 +// Bind();
  1768 +// sk.Begin(stim::OBJ_LINE);
1748 1769
1749 - cL.push_back(pos);  
1750 - cM.push_back(mag);  
1751 1770
1752 -// setPosition(pos);  
1753 -// setMagnitude(mag); 1771 + //sk.createFromSelf(GL_SELECT);
  1772 + nt.createFromSelf(GL_SELECT);
1754 int h; 1773 int h;
1755 bool started = false; 1774 bool started = false;
1756 bool running = true; 1775 bool running = true;
@@ -1761,7 +1780,7 @@ class gl_spider // : public virtual gl_texture&lt;T&gt; @@ -1761,7 +1780,7 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1761 if (cost > min_cost){ 1780 if (cost > min_cost){
1762 running = false; 1781 running = false;
1763 branchDetection2(); 1782 branchDetection2();
1764 - addToNetwork(cL, cM); 1783 + addToNetwork(cL, cM, spos, sdir, smag);
1765 #ifdef DEBUG 1784 #ifdef DEBUG
1766 std::cerr << "the cost of " << cost << " > " << min_cost << std::endl; 1785 std::cerr << "the cost of " << cost << " > " << min_cost << std::endl;
1767 #endif 1786 #endif
@@ -1769,13 +1788,14 @@ class gl_spider // : public virtual gl_texture&lt;T&gt; @@ -1769,13 +1788,14 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1769 } else { 1788 } else {
1770 //Have we found the edge of the map? 1789 //Have we found the edge of the map?
1771 pos = getPosition(); 1790 pos = getPosition();
1772 - if(pos[0] > size[0] || pos[1] > size[1]  
1773 - || pos[2] > size[2] || pos[0] < 0  
1774 - || pos[1] < 0 || pos[2] < 0) 1791 + if(p[0] > size[0] || p[1] > size[1]
  1792 + || p[2] > size[2] || p[0] < 0
  1793 + || p[1] < 0 || p[2] < 0)
1775 { 1794 {
1776 running = false; 1795 running = false;
1777 branchDetection2(); 1796 branchDetection2();
1778 - addToNetwork(cL, cM); 1797 + // addToNetwork(cL, cM);
  1798 + addToNetwork(cL, cM, spos, sdir, smag);
1779 #ifdef DEBUG 1799 #ifdef DEBUG
1780 std::cerr << "I hit and edge" << std::endl; 1800 std::cerr << "I hit and edge" << std::endl;
1781 #endif 1801 #endif
@@ -1790,10 +1810,11 @@ class gl_spider // : public virtual gl_texture&lt;T&gt; @@ -1790,10 +1810,11 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1790 } 1810 }
1791 //Has the template size gotten unreasonable? 1811 //Has the template size gotten unreasonable?
1792 mag = getMagnitude(); 1812 mag = getMagnitude();
1793 - if(mag > 75 || mag < 1){ 1813 + if(m > 75 || m < 1){
1794 running = false; 1814 running = false;
1795 branchDetection2(); 1815 branchDetection2();
1796 - addToNetwork(cL, cM); 1816 + // addToNetwork(cL, cM);
  1817 + addToNetwork(cL, cM, spos, sdir, smag);
1797 #ifdef DEBUG 1818 #ifdef DEBUG
1798 std::cerr << "The templates are too big" << std::endl; 1819 std::cerr << "The templates are too big" << std::endl;
1799 #endif 1820 #endif
@@ -1807,13 +1828,16 @@ class gl_spider // : public virtual gl_texture&lt;T&gt; @@ -1807,13 +1828,16 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1807 #ifdef DEBUG 1828 #ifdef DEBUG
1808 std::cerr << "I hit the fiber " << h << std::endl; 1829 std::cerr << "I hit the fiber " << h << std::endl;
1809 #endif 1830 #endif
  1831 + last_fiber = h;
1810 running = false; 1832 running = false;
1811 branchDetection2(); 1833 branchDetection2();
1812 - addToNetwork(cL, cM); 1834 + // addToNetwork(cL, cM);
  1835 + addToNetwork(cL, cM, spos, sdir, smag);
1813 break; 1836 break;
1814 } 1837 }
1815 else { 1838 else {
1816 - cL.push_back(stim::vec3<float>(p[0], p[1],p[2])); 1839 + cL.push_back(p);
  1840 + cD.push_back(d);
1817 cM.push_back(m); 1841 cM.push_back(m);
1818 // Unbind(); 1842 // Unbind();
1819 CHECK_OPENGL_ERROR 1843 CHECK_OPENGL_ERROR
stim/gl/gl_texture.h
@@ -30,9 +30,9 @@ class gl_texture : public virtual image_stack&lt;T, F&gt; @@ -30,9 +30,9 @@ class gl_texture : public virtual image_stack&lt;T, F&gt;
30 GLenum cpu_type; 30 GLenum cpu_type;
31 GLenum gpu_type; 31 GLenum gpu_type;
32 GLenum format; //format for the texture (GL_RGBA, GL_LUMINANCE, etc.) 32 GLenum format; //format for the texture (GL_RGBA, GL_LUMINANCE, etc.)
33 - using image_stack<T>::R; 33 + using image_stack<T,F>::R;
34 //using image_stack<T>::S; 34 //using image_stack<T>::S;
35 - using image_stack<T>::ptr; 35 + using image_stack<T,F>::ptr;
36 36
37 /// Sets the internal texture_type, based on the data dimensions 37 /// Sets the internal texture_type, based on the data dimensions
38 void setTextureType(){ 38 void setTextureType(){
@@ -247,7 +247,7 @@ class gl_texture : public virtual image_stack&lt;T, F&gt; @@ -247,7 +247,7 @@ class gl_texture : public virtual image_stack&lt;T, F&gt;
247 } 247 }
248 248
249 ///returns the dimentions of the data in the x, y, z directions. 249 ///returns the dimentions of the data in the x, y, z directions.
250 - vec<int> getSize(){ 250 + stim::vec<int> getSize(){
251 stim::vec<int> size(R[1], R[2], R[3]); 251 stim::vec<int> size(R[1], R[2], R[3]);
252 return size; 252 return size;
253 } 253 }
@@ -282,7 +282,7 @@ class gl_texture : public virtual image_stack&lt;T, F&gt; @@ -282,7 +282,7 @@ class gl_texture : public virtual image_stack&lt;T, F&gt;
282 ///@param file_mask specifies the file(s) to be loaded 282 ///@param file_mask specifies the file(s) to be loaded
283 /// Sets the path and calls the loader on that path. 283 /// Sets the path and calls the loader on that path.
284 void load_images(std::string file_mask){ 284 void load_images(std::string file_mask){
285 - image_stack<T>::load_images(file_mask); //load images 285 + image_stack<T, F>::load_images(file_mask); //load images
286 guess_parameters(); 286 guess_parameters();
287 } 287 }
288 288
@@ -292,13 +292,18 @@ class gl_texture : public virtual image_stack&lt;T, F&gt; @@ -292,13 +292,18 @@ class gl_texture : public virtual image_stack&lt;T, F&gt;
292 return texID; 292 return texID;
293 } 293 }
294 294
295 - 295 +
296 T* getData(){ 296 T* getData(){
297 return ptr; 297 return ptr;
298 } 298 }
299 -  
300 - 299 +
  300 + void setData(T* rts)
  301 + {
  302 +
  303 + }
  304 +
301 }; 305 };
  306 +
302 } 307 }
303 308
304 309
stim/grids/grid3.h 0 → 100644
  1 +#ifndef STIM_GRID3_H
  2 +#define STIM_GRID3_H
  3 +
  4 +namespace stim{
  5 +
  6 +template<typename T, typename F = float>
  7 +class grid : public stim::grid<T, 3, F>{
  8 +
  9 +public:
  10 +
  11 + /// Convert grid coordinates (integers) into world coordinates (F) based on the pixel spacing
  12 + void grid2volume(size_t xi, size_t yi, size_t zi, F& x, F& y, F&z){
  13 +
  14 + }
  15 +
  16 + /// Use linear interpolation to get a value from the grid at (x, y, z) in VOLUME space (based on voxel size)
  17 + T lerp(F x, F y, F z){
  18 +
  19 + }
  20 + /// Create a resampled grid with isotropic voxel sizes
  21 + grid3<T, F> resample_iso(){
  22 +
  23 + //find the smallest spacing
  24 + //create a new grid of the appropriate size
  25 + //use linear interpolation to resample the old grid into the new grid
  26 + }
  27 +
  28 +
  29 +};
  30 +} //end namespace stim
  31 +
  32 +#endif
0 \ No newline at end of file 33 \ No newline at end of file
stim/grids/image_stack.h
@@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
5 #include <stim/parser/filename.h> 5 #include <stim/parser/filename.h>
6 #include <stim/grids/grid.h> 6 #include <stim/grids/grid.h>
7 #include <stim/image/image.h> 7 #include <stim/image/image.h>
  8 +#include <stim/math/vec3.h>
8 9
9 namespace stim{ 10 namespace stim{
10 11
@@ -17,13 +18,13 @@ class image_stack : public virtual stim::grid&lt;T, 4, F&gt;{ @@ -17,13 +18,13 @@ class image_stack : public virtual stim::grid&lt;T, 4, F&gt;{
17 18
18 protected: 19 protected:
19 //using stim::grid<T, 4>::S; 20 //using stim::grid<T, 4>::S;
20 - using stim::grid<T, 4>::R;  
21 - using stim::grid<T, 4>::ptr;  
22 - using stim::grid<T, 4>::read; 21 + using stim::grid<T, 4, F>::R;
  22 + using stim::grid<T, 4, F>::ptr;
  23 + using stim::grid<T, 4, F>::S;
23 24
24 public: 25 public:
25 //default constructor 26 //default constructor
26 - image_stack() : grid<T, 4>() { 27 + image_stack() : grid<T, 4, F>() {
27 28
28 } 29 }
29 30
@@ -37,6 +38,20 @@ public: @@ -37,6 +38,20 @@ public:
37 return R[1] * R[2] * R[3]; //return the number of spatial samples 38 return R[1] * R[2] * R[3]; //return the number of spatial samples
38 } 39 }
39 40
  41 + // get the number of pixels in each dimension
  42 + size_t nc() {
  43 + return R[0];
  44 + }
  45 + size_t nx() {
  46 + return R[1];
  47 + }
  48 + size_t ny() {
  49 + return R[2];
  50 + }
  51 + size_t nz() {
  52 + return R[3];
  53 + }
  54 +
40 /// Returns the number of color channels 55 /// Returns the number of color channels
41 size_t channels(){ 56 size_t channels(){
42 return R[0]; 57 return R[0];
@@ -113,7 +128,8 @@ public: @@ -113,7 +128,8 @@ public:
113 /// @param i is the page to be saved 128 /// @param i is the page to be saved
114 void save_image(std::string file_name, unsigned int i){ 129 void save_image(std::string file_name, unsigned int i){
115 stim::image<T> I; //create an image 130 stim::image<T> I; //create an image
116 - I.set_interleaved_rgb(&ptr[ i * R[0] * R[1] * R[2] ], R[1], R[2], R[0]); //retrieve the interlaced data from the image - store it in the grid 131 + I.set_interleaved(&ptr[ i * R[0] * R[1] * R[2] ], R[1], R[2], R[0]); //retrieve the interlaced data from the image - store it in the grid
  132 +// I.set_interleaved_rgb(&ptr[ i * R[0] * R[1] * R[2] ], R[1], R[2], R[0]); //retrieve the interlaced data from the image - store it in the grid
117 I.save(file_name); 133 I.save(file_name);
118 } 134 }
119 135
@@ -138,13 +154,14 @@ public: @@ -138,13 +154,14 @@ public:
138 /// @param depth, number of pixels in depth. 154 /// @param depth, number of pixels in depth.
139 void init(int channels, int width, int height, int depth) 155 void init(int channels, int width, int height, int depth)
140 { 156 {
141 - R.resize(4); 157 + //R.resize(4);
142 R[0] = channels; 158 R[0] = channels;
143 R[1] = width; 159 R[1] = width;
144 R[2] = height; 160 R[2] = height;
145 R[3] = depth; 161 R[3] = depth;
146 162
147 ptr = (T*)malloc(sizeof(T) * samples()); 163 ptr = (T*)malloc(sizeof(T) * samples());
  164 + memset(ptr, 0, sizeof(T) * samples());
148 } 165 }
149 166
150 ///Saves the entire stack to a set of images 167 ///Saves the entire stack to a set of images
@@ -152,12 +169,13 @@ public: @@ -152,12 +169,13 @@ public:
152 void save_images(std::string file_mask){ 169 void save_images(std::string file_mask){
153 170
154 stim::filename file_path(file_mask); 171 stim::filename file_path(file_mask);
  172 + //stim::filename abs_file_path = file_pat
155 173
156 //create a list of file names 174 //create a list of file names
157 std::vector<std::string> file_list = stim::wildcards::increment(file_path.str(), 0, R[3]-1, 1); 175 std::vector<std::string> file_list = stim::wildcards::increment(file_path.str(), 0, R[3]-1, 1);
158 -  
159 - for(int i=0; i<R[3]; i++) 176 + for (int i = 0; i < R[3]; i++) {
160 save_image(file_list[i], i); 177 save_image(file_list[i], i);
  178 + }
161 } 179 }
162 180
163 /// Returns the pixel at the specified point 181 /// Returns the pixel at the specified point
@@ -165,9 +183,35 @@ public: @@ -165,9 +183,35 @@ public:
165 return ptr[z * R[0] * R[1] * R[2] + y * R[0] * R[1] + x * R[0] + c]; 183 return ptr[z * R[0] * R[1] * R[2] + y * R[0] * R[1] + x * R[0] + c];
166 } 184 }
167 185
  186 + /// Returns the world-space position at an index point (i, j, k)
  187 + vec3<F> p(size_t i, size_t j, size_t k){
  188 + vec3<F> result;
  189 + result[0] = (F)i * S[1];
  190 + result[1] = (F)j * S[2];
  191 + result[2] = (F)k * S[3];
  192 + return result;
  193 + }
  194 +
  195 + // set the pixel at the specified point
  196 + void set(size_t i, size_t j, size_t k, T value, size_t c = 0){
  197 + ptr[k * R[0] * R[1] * R[2] + j * R[0] * R[1] + i * R[0] + c] = value;
  198 + }
  199 + void set(T* Ptr, size_t k) {
  200 +
  201 + for (unsigned i = 0; i < R[0] * R[1] * R[2]; i++)
  202 + ptr[i + k * R[0] * R[1] * R[2]] = Ptr[i];
  203 + }
  204 + void copy(T* Ptr) {
  205 + ptr = Ptr;
  206 + }
  207 +
  208 +
  209 + /* This was causing compiler errors. I don't think this function call exists anywhere:
  210 +
168 void read(std::string file, unsigned int X, unsigned int Y, unsigned int Z, unsigned int C = 1, unsigned int header = 0){ 211 void read(std::string file, unsigned int X, unsigned int Y, unsigned int Z, unsigned int C = 1, unsigned int header = 0){
169 read(file, stim::vec<unsigned long>(C, X, Y, Z), header); 212 read(file, stim::vec<unsigned long>(C, X, Y, Z), header);
170 } 213 }
  214 + */
171 215
172 T* data(){ 216 T* data(){
173 return ptr; 217 return ptr;
stim/iVote/ivote2.cuh 0 → 100644
  1 +#ifndef STIM_IVOTE2_CUH
  2 +#define STIM_IVOTE2_CUH
  3 +
  4 +#include <iostream>
  5 +#include <fstream>
  6 +#include <stim/cuda/cudatools/error.h>
  7 +#include <stim/cuda/templates/gradient.cuh>
  8 +#include <stim/cuda/arraymath.cuh>
  9 +#include <stim/iVote/ivote2/iter_vote2.cuh>
  10 +#include <stim/iVote/ivote2/local_max.cuh>
  11 +#include <stim/math/constants.h>
  12 +#include <stim/math/vector.h>
  13 +#include <stim/visualization/colormap.h>
  14 +
  15 +
  16 +namespace stim {
  17 + // this function precomputes the atan2 values
  18 + template<typename T>
  19 + void atan_2(T* cpuTable, unsigned int rmax) {
  20 + int xsize = 2 * rmax + 1; //initialize the width and height of the window which atan2 are computed in.
  21 + int ysize = 2 * rmax + 1;
  22 + int yi = rmax; // assign the center coordinates of the atan2 window to yi and xi
  23 + int xi = rmax;
  24 + for (int xt = 0; xt < xsize; xt++) { //for each element in the atan2 table
  25 + for (int yt = 0; yt < ysize; yt++) {
  26 + int id = yt * xsize + xt; //convert the current 2D coordinates to 1D
  27 + int xd = xi - xt; // calculate the distance between the pixel and the center of the atan2 window
  28 + int yd = yi - yt;
  29 + T atan_2d = atan2((T)yd, (T)xd); // calculate the angle between the pixel and the center of the atan2 window and store the result.
  30 + cpuTable[id] = atan_2d;
  31 + }
  32 + }
  33 + }
  34 +
  35 + //this kernel invert the 2D image
  36 + template<typename T>
  37 + __global__ void cuda_invert(T* gpuI, size_t x, size_t y) {
  38 + // calculate the 2D coordinates for this current thread.
  39 + size_t xi = blockIdx.x * blockDim.x + threadIdx.x;
  40 + size_t yi = blockIdx.y * blockDim.y + threadIdx.y;
  41 +
  42 + if (xi >= x || yi >= y) return;
  43 + size_t i = yi * x + xi; // convert 2D coordinates to 1D
  44 + gpuI[i] = 255 - gpuI[i]; //invert the pixel intensity
  45 + }
  46 +
  47 +
  48 +
  49 + //this function calculate the threshold using OTSU method
  50 + template<typename T>
  51 + T th_otsu(T* pts, size_t pixels, unsigned int th_num = 20) {
  52 + T Imax = pts[0]; //initialize the maximum value to the first one
  53 + T Imin = pts[0]; //initialize the maximum value to the first on
  54 +
  55 + for (size_t n = 0; n < pixels; n++) { //for every value
  56 + if (pts[n] > Imax) { //if the value is higher than the current max
  57 + Imax = pts[n];
  58 + }
  59 + }
  60 + for (size_t n = 0; n< pixels; n++) { //for every value
  61 + if (pts[n] < Imin) { //if the value is higher than the current max
  62 + Imin = pts[n];
  63 + }
  64 + }
  65 +
  66 + T th_step = ((Imax - Imin) / th_num);
  67 + std::vector<T> var_b;
  68 + for (unsigned int t0 = 0; t0 < th_num; t0++) {
  69 + T th = t0 * th_step + Imin;
  70 + unsigned int n_b(0), n_o(0); //these variables save the number of elements that are below and over the threshold
  71 + T m_b(0), m_o(0); //these variables save the mean value for each cluster
  72 + for (unsigned int idx = 0; idx < pixels; idx++) {
  73 + if (pts[idx] <= th) {
  74 + m_b += pts[idx];
  75 + n_b += 1;
  76 + }
  77 + else {
  78 + m_o += pts[idx];
  79 + n_o += 1;
  80 + }
  81 + }
  82 +
  83 + m_b = m_b / n_b; //calculate the mean value for the below threshold cluster
  84 + m_o = m_o / n_o; //calculate the mean value for the over threshold cluster
  85 +
  86 + var_b.push_back(n_b * n_o * pow((m_b - m_o), 2));
  87 + }
  88 +
  89 + std::vector<float>::iterator max_var = std::max_element(var_b.begin(), var_b.end()); //finding maximum elements in the vector
  90 + size_t th_idx = std::distance(var_b.begin(), max_var);
  91 + T threshold = Imin + (T)(th_idx * th_step);
  92 + return threshold;
  93 + }
  94 +
  95 + //this function performs the 2D iterative voting algorithm on the image stored in the gpu
  96 + template<typename T>
  97 + void gpu_ivote2(T* gpuI, unsigned int rmax, size_t x, size_t y, bool invert = false, T t = 0, int iter = 8, T phi = 15.0f * (float)stim::PI / 180, int conn = 8, bool debug = false) {
  98 +
  99 + size_t pixels = x * y; //compute the size of input image
  100 + //
  101 + if (invert) { //if inversion is required call the kernel to invert the image
  102 + unsigned int max_threads = stim::maxThreadsPerBlock();
  103 + dim3 threads((unsigned int)sqrt(max_threads), (unsigned int)sqrt(max_threads));
  104 + dim3 blocks((unsigned int)x / threads.x + 1, (unsigned int)y / threads.y + 1);
  105 + cuda_invert << <blocks, threads >> > (gpuI, x, y);
  106 + }
  107 + //
  108 + size_t table_bytes = (size_t)(pow(2 * rmax + 1, 2) * sizeof(T)); // create the atan2 table
  109 + T* cpuTable = (T*)malloc(table_bytes); //assign memory on the cpu for atan2 table
  110 + atan_2<T>(cpuTable, rmax); //call the function to precompute the atan2 table
  111 + T* gpuTable; HANDLE_ERROR(cudaMalloc(&gpuTable, table_bytes));
  112 + HANDLE_ERROR(cudaMemcpy(gpuTable, cpuTable, table_bytes, cudaMemcpyHostToDevice)); //copy atan2 table to the gpu
  113 +
  114 + size_t bytes = pixels* sizeof(T); //calculate the bytes of the input
  115 + float dphi = phi / iter; //change in phi for each iteration
  116 +
  117 + float* gpuGrad; HANDLE_ERROR(cudaMalloc(&gpuGrad, bytes * 2)); //allocate space to store the 2D gradient
  118 + float* gpuVote; HANDLE_ERROR(cudaMalloc(&gpuVote, bytes)); //allocate space to store the vote image
  119 +
  120 + stim::cuda::gpu_gradient_2d<float>(gpuGrad, gpuI, x, y); //calculate the 2D gradient
  121 + stim::cuda::gpu_cart2polar<float>(gpuGrad, x, y); //convert cartesian coordinate of gradient to the polar
  122 +
  123 + for (int i = 0; i < iter; i++) { //for each iteration
  124 + cudaMemset(gpuVote, 0, bytes); //reset the vote image to 0
  125 + stim::cuda::gpu_vote<float>(gpuVote, gpuGrad, gpuTable, phi, rmax, x, y, debug); //perform voting
  126 + stim::cuda::gpu_update_dir<float>(gpuVote, gpuGrad, gpuTable, phi, rmax, x, y, debug); //update the voter directions
  127 + phi = phi - dphi; //decrement phi
  128 + }
  129 + stim::cuda::gpu_local_max<float>(gpuI, gpuVote, conn, x, y); //calculate the local maxima
  130 +
  131 + if (t > 0) {
  132 + T* pts = (T*)malloc(bytes); //allocate memory on the cpu to store the output of iterative voting
  133 + HANDLE_ERROR(cudaMemcpy(pts, gpuI, bytes, cudaMemcpyDeviceToHost)); //copy the output from gpu to the cpu memory
  134 +
  135 + T threshold;
  136 + threshold = t;
  137 +
  138 + size_t ind;
  139 + for (size_t ix = 0; ix < x; ix++) {
  140 + for (size_t iy = 0; iy < y; iy++) {
  141 + ind = iy * x + ix;
  142 + if (pts[ind] > threshold) {
  143 + pts[ind] = 1;
  144 + }
  145 + else pts[ind] = 0;
  146 + }
  147 + }
  148 + HANDLE_ERROR(cudaMemcpy(gpuI, pts, bytes, cudaMemcpyHostToDevice)); //copy the points to the gpu
  149 + }
  150 +
  151 + }
  152 +
  153 +
  154 + template<typename T>
  155 + void cpu_ivote2(T* cpuI, unsigned int rmax, size_t x, size_t y, float &gpu_time, bool invert = false, T t = 0, int iter = 8, T phi = 15.0f * (float)stim::PI / 180, int conn = 8, bool debug = false) {
  156 + size_t bytes = x*y * sizeof(T);
  157 + T* gpuI; //allocate space on the gpu to save the input image
  158 +
  159 + gpuTimer_start();
  160 + HANDLE_ERROR(cudaMalloc(&gpuI, bytes));
  161 + HANDLE_ERROR(cudaMemcpy(gpuI, cpuI, bytes, cudaMemcpyHostToDevice)); //copy the image to the gpu
  162 + stim::gpu_ivote2<T>(gpuI, rmax, x, y, invert, t, iter, phi, conn, debug); //call the gpu version of the ivote
  163 + HANDLE_ERROR(cudaMemcpy(cpuI, gpuI, bytes, cudaMemcpyDeviceToHost)); //copy the output to the cpu
  164 +
  165 + gpu_time = gpuTimer_end();
  166 + }
  167 +}
  168 +#endif
0 \ No newline at end of file 169 \ No newline at end of file
stim/iVote/ivote2/iter_vote2.cuh 0 → 100644
  1 +#ifndef STIM_CUDA_ITER_VOTE2_H
  2 +#define STIM_CUDA_ITER_VOTE2_H
  3 +
  4 +//extern bool DEBUG;
  5 +
  6 +#include "update_dir_bb.cuh"
  7 +#include "vote_atomic_bb.cuh"
  8 +
  9 +namespace stim{
  10 + namespace cuda{
  11 +
  12 + }
  13 +}
  14 +
  15 +
  16 +
  17 +#endif
0 \ No newline at end of file 18 \ No newline at end of file
stim/cuda/ivote/local_max.cuh renamed to stim/iVote/ivote2/local_max.cuh
@@ -10,7 +10,7 @@ namespace stim{ @@ -10,7 +10,7 @@ namespace stim{
10 10
11 // this kernel calculates the local maximum for finding the cell centers 11 // this kernel calculates the local maximum for finding the cell centers
12 template<typename T> 12 template<typename T>
13 - __global__ void cuda_local_max(T* gpuCenters, T* gpuVote, T final_t, int conn, int x, int y){ 13 + __global__ void cuda_local_max(T* gpuCenters, T* gpuVote, int conn, int x, int y){
14 14
15 // calculate the 2D coordinates for this current thread. 15 // calculate the 2D coordinates for this current thread.
16 int xi = blockIdx.x * blockDim.x + threadIdx.x; 16 int xi = blockIdx.x * blockDim.x + threadIdx.x;
@@ -20,30 +20,21 @@ namespace stim{ @@ -20,30 +20,21 @@ namespace stim{
20 return; 20 return;
21 21
22 // convert 2D coordinates to 1D 22 // convert 2D coordinates to 1D
23 - int i = yi * x + xi; 23 + int i = yi * x + xi;
24 24
25 gpuCenters[i] = 0; //initialize the value at this location to zero 25 gpuCenters[i] = 0; //initialize the value at this location to zero
26 26
27 T val = gpuVote[i]; 27 T val = gpuVote[i];
28 28
29 - //compare to the threshold  
30 - if(val < final_t) return;  
31 -  
32 - //define an array to store indices with same vote value  
33 - /*int * IdxEq;  
34 - IdxEq = new int [2*conn];  
35 - int n = 0;*/  
36 -  
37 - for(int xl = xi - conn; xl < xi + conn; xl++){  
38 - for(int yl = yi - conn; yl < yi + conn; yl++){ 29 + for(unsigned int xl = xi - conn; xl < xi + conn; xl++){
  30 + for(unsigned int yl = yi - conn; yl < yi + conn; yl++){
39 if(xl >= 0 && xl < x && yl >= 0 && yl < y){ 31 if(xl >= 0 && xl < x && yl >= 0 && yl < y){
40 - int il = yl * x + xl; 32 + unsigned int il = yl * x + xl;
41 if(gpuVote[il] > val){ 33 if(gpuVote[il] > val){
42 return; 34 return;
43 } 35 }
44 if (gpuVote[il] == val){ 36 if (gpuVote[il] == val){
45 - /*IdxEq[n] = il;  
46 - n = n+1;*/ 37 +
47 if( il > i){ 38 if( il > i){
48 return; 39 return;
49 } 40 }
@@ -51,29 +42,25 @@ namespace stim{ @@ -51,29 +42,25 @@ namespace stim{
51 } 42 }
52 } 43 }
53 } 44 }
54 - /*if (n!=0){  
55 - if(IdxEq[n/2] !=i){  
56 - return;  
57 - }  
58 - } */  
59 - gpuCenters[i] = 1; 45 +
  46 + gpuCenters[i] = gpuVote[i];
60 } 47 }
61 48
62 template<typename T> 49 template<typename T>
63 - void gpu_local_max(T* gpuCenters, T* gpuVote, T final_t, unsigned int conn, unsigned int x, unsigned int y){ 50 + void gpu_local_max(T* gpuCenters, T* gpuVote, unsigned int conn, unsigned int x, unsigned int y){
64 51
65 unsigned int max_threads = stim::maxThreadsPerBlock(); 52 unsigned int max_threads = stim::maxThreadsPerBlock();
66 /*dim3 threads(max_threads, 1); 53 /*dim3 threads(max_threads, 1);
67 dim3 blocks(x/threads.x + (x %threads.x == 0 ? 0:1) , y);*/ 54 dim3 blocks(x/threads.x + (x %threads.x == 0 ? 0:1) , y);*/
68 - dim3 threads( sqrt(max_threads), sqrt(max_threads) );  
69 - dim3 blocks(x/threads.x + 1, y/threads.y + 1); 55 + dim3 threads((unsigned int)sqrt(max_threads), (unsigned int)sqrt(max_threads) );
  56 + dim3 blocks((unsigned int)x/threads.x + 1, (unsigned int)y/threads.y + 1);
70 57
71 //call the kernel to find the local maximum. 58 //call the kernel to find the local maximum.
72 - cuda_local_max <<< blocks, threads >>>(gpuCenters, gpuVote, final_t, conn, x, y); 59 + cuda_local_max <<< blocks, threads >>>(gpuCenters, gpuVote, conn, x, y);
73 } 60 }
74 61
75 template<typename T> 62 template<typename T>
76 - void cpu_local_max(T* cpuCenters, T* cpuVote, T final_t, unsigned int conn, unsigned int x, unsigned int y){ 63 + void cpu_local_max(T* cpuCenters, T* cpuVote, unsigned int conn, unsigned int x, unsigned int y){
77 64
78 //calculate the number of bytes in the array 65 //calculate the number of bytes in the array
79 unsigned int bytes = x * y * sizeof(T); 66 unsigned int bytes = x * y * sizeof(T);
@@ -90,7 +77,7 @@ namespace stim{ @@ -90,7 +77,7 @@ namespace stim{
90 HANDLE_ERROR(cudaMemcpy(gpuVote, cpuVote, bytes, cudaMemcpyHostToDevice)); 77 HANDLE_ERROR(cudaMemcpy(gpuVote, cpuVote, bytes, cudaMemcpyHostToDevice));
91 78
92 //call the GPU version of the local max function 79 //call the GPU version of the local max function
93 - gpu_local_max<T>(gpuCenters, gpuVote, final_t, conn, x, y); 80 + gpu_local_max<T>(gpuCenters, gpuVote, conn, x, y);
94 81
95 //copy the cell centers data to the CPU 82 //copy the cell centers data to the CPU
96 cudaMemcpy(cpuCenters, gpuCenters, bytes, cudaMemcpyDeviceToHost) ; 83 cudaMemcpy(cpuCenters, gpuCenters, bytes, cudaMemcpyDeviceToHost) ;
stim/cuda/ivote/update_dir.cuh renamed to stim/iVote/ivote2/update_dir.cuh
stim/cuda/ivote/update_dir_bb.cuh renamed to stim/iVote/ivote2/update_dir_bb.cuh
@@ -40,8 +40,9 @@ namespace stim{ @@ -40,8 +40,9 @@ namespace stim{
40 bb.insert(xi + ceil(rmax * cos(theta + phi)), yi + ceil(rmax * sin(theta + phi))); //insert the final corner into the bounding box 40 bb.insert(xi + ceil(rmax * cos(theta + phi)), yi + ceil(rmax * sin(theta + phi))); //insert the final corner into the bounding box
41 41
42 int x_table = 2*rmax +1; 42 int x_table = 2*rmax +1;
43 - int lut_i;  
44 T rmax_sq = rmax * rmax; 43 T rmax_sq = rmax * rmax;
  44 +
  45 + int lut_i;
45 T dx_sq, dy_sq; 46 T dx_sq, dy_sq;
46 47
47 bb.trim_low(0, 0); //make sure the bounding box doesn't go outside the image 48 bb.trim_low(0, 0); //make sure the bounding box doesn't go outside the image
@@ -49,11 +50,12 @@ namespace stim{ @@ -49,11 +50,12 @@ namespace stim{
49 50
50 int by, bx; 51 int by, bx;
51 int dx, dy; //coordinate relative to (xi, yi) 52 int dx, dy; //coordinate relative to (xi, yi)
  53 +
52 T v; 54 T v;
53 T max_v = 0; //initialize the maximum vote value to zero 55 T max_v = 0; //initialize the maximum vote value to zero
54 T alpha; 56 T alpha;
55 - int max_dx = bb.low[0];  
56 - int max_dy = bb.low[1]; 57 + int max_dx = bb.low[0] - xi;
  58 + int max_dy = bb.low[1] - yi;
57 for(by = bb.low[1]; by <= bb.high[1]; by++){ //for each element in the bounding box 59 for(by = bb.low[1]; by <= bb.high[1]; by++){ //for each element in the bounding box
58 dy = by - yi; //calculate the y coordinate of the current point relative to yi 60 dy = by - yi; //calculate the y coordinate of the current point relative to yi
59 dy_sq = dy * dy; 61 dy_sq = dy * dy;
@@ -79,26 +81,26 @@ namespace stim{ @@ -79,26 +81,26 @@ namespace stim{
79 81
80 // this kernel updates the gradient direction by the calculated voting direction. 82 // this kernel updates the gradient direction by the calculated voting direction.
81 template<typename T> 83 template<typename T>
82 - __global__ void cuda_update_grad(T* gpuGrad, T* gpuDir, int x, int y){ 84 + __global__ void cuda_update_grad(T* gpuGrad, T* gpuDir, size_t x, size_t y){
83 85
84 // calculate the 2D coordinates for this current thread. 86 // calculate the 2D coordinates for this current thread.
85 - int xi = blockIdx.x * blockDim.x + threadIdx.x;  
86 - int yi = blockIdx.y * blockDim.y + threadIdx.y; 87 + size_t xi = blockIdx.x * blockDim.x + threadIdx.x;
  88 + size_t yi = blockIdx.y * blockDim.y + threadIdx.y;
87 89
88 if(xi >= x || yi >= y) return; 90 if(xi >= x || yi >= y) return;
89 91
90 // convert 2D coordinates to 1D 92 // convert 2D coordinates to 1D
91 - int i = yi * x + xi; 93 + size_t i = yi * x + xi;
92 94
93 //update the gradient image with the vote direction 95 //update the gradient image with the vote direction
94 gpuGrad[2*i] = gpuDir[i]; 96 gpuGrad[2*i] = gpuDir[i];
95 } 97 }
96 98
97 template<typename T> 99 template<typename T>
98 - void gpu_update_dir(T* gpuVote, T* gpuGrad, T* gpuTable, T phi, unsigned int rmax, unsigned int x, unsigned int y){ 100 + void gpu_update_dir(T* gpuVote, T* gpuGrad, T* gpuTable, T phi, unsigned int rmax, size_t x, size_t y, bool DEBUG = false){
99 101
100 //calculate the number of bytes in the array 102 //calculate the number of bytes in the array
101 - unsigned int bytes = x * y * sizeof(T); 103 + size_t bytes = x * y * sizeof(T);
102 104
103 // allocate space on the GPU for the updated vote direction 105 // allocate space on the GPU for the updated vote direction
104 T* gpuDir; 106 T* gpuDir;
@@ -106,14 +108,14 @@ namespace stim{ @@ -106,14 +108,14 @@ namespace stim{
106 108
107 unsigned int max_threads = stim::maxThreadsPerBlock(); 109 unsigned int max_threads = stim::maxThreadsPerBlock();
108 110
109 - dim3 threads( sqrt(max_threads), sqrt(max_threads) );  
110 - dim3 blocks(x/threads.x + 1, y/threads.y + 1); 111 + dim3 threads( (unsigned int)sqrt(max_threads), (unsigned int)sqrt(max_threads) );
  112 + dim3 blocks((unsigned int)x/threads.x + 1, (unsigned int)y/threads.y + 1);
111 113
112 size_t table_bytes = sizeof(T) * (rmax * 2 + 1) * (rmax * 2 + 1); 114 size_t table_bytes = sizeof(T) * (rmax * 2 + 1) * (rmax * 2 + 1);
113 //size_t curtain = 2 * rmax; 115 //size_t curtain = 2 * rmax;
114 //size_t template_bytes = sizeof(T) * (threads.x + curtain) * (threads.y + curtain); 116 //size_t template_bytes = sizeof(T) * (threads.x + curtain) * (threads.y + curtain);
115 size_t shared_mem_req = table_bytes;// + template_bytes; 117 size_t shared_mem_req = table_bytes;// + template_bytes;
116 - std::cout<<"Shared Memory required: "<<shared_mem_req<<std::endl; 118 + if (DEBUG) std::cout << "Shared Memory required: " << shared_mem_req << std::endl;
117 119
118 size_t shared_mem = stim::sharedMemPerBlock(); 120 size_t shared_mem = stim::sharedMemPerBlock();
119 if(shared_mem_req > shared_mem){ 121 if(shared_mem_req > shared_mem){
@@ -122,16 +124,10 @@ namespace stim{ @@ -122,16 +124,10 @@ namespace stim{
122 } 124 }
123 125
124 //call the kernel to calculate the new voting direction 126 //call the kernel to calculate the new voting direction
125 - cuda_update_dir <<< blocks, threads, shared_mem_req>>>(gpuDir, gpuVote, gpuGrad, gpuTable, phi, rmax, x , y);  
126 - //stim::gpu2image<T>(gpuDir, "dir_david.bmp", x, y, -pi, pi, stim::cmBrewer);  
127 -  
128 - //exit(0);  
129 -  
130 - //threads = dim3( sqrt(max_threads), sqrt(max_threads) );  
131 - //blocks = dim3(x/threads.x + 1, y/threads.y + 1); 127 + cuda_update_dir <<< blocks, threads, shared_mem_req>>>(gpuDir, gpuVote, gpuGrad, gpuTable, phi, rmax, (int)x , (int)y);
132 128
133 //call the kernel to update the gradient direction 129 //call the kernel to update the gradient direction
134 - cuda_update_grad <<< blocks, threads >>>(gpuGrad, gpuDir, x , y); 130 + cuda_update_grad <<< blocks, threads >>>(gpuGrad, gpuDir, (int)x , (int)y);
135 //free allocated memory 131 //free allocated memory
136 HANDLE_ERROR( cudaFree(gpuDir) ); 132 HANDLE_ERROR( cudaFree(gpuDir) );
137 133
stim/cuda/ivote/update_dir_shared.cuh renamed to stim/iVote/ivote2/update_dir_shared.cuh
stim/cuda/ivote/update_dir_threshold_global.cuh renamed to stim/iVote/ivote2/update_dir_threshold_global.cuh
stim/cuda/ivote/vote.cuh renamed to stim/iVote/ivote2/vote.cuh
stim/cuda/ivote/vote_atomic.cuh renamed to stim/iVote/ivote2/vote_atomic.cuh
stim/cuda/ivote/vote_atomic_bb.cuh renamed to stim/iVote/ivote2/vote_atomic_bb.cuh
@@ -9,12 +9,14 @@ @@ -9,12 +9,14 @@
9 #include <stim/visualization/colormap.h> 9 #include <stim/visualization/colormap.h>
10 #include <math.h> 10 #include <math.h>
11 11
  12 +
  13 +
12 namespace stim{ 14 namespace stim{
13 namespace cuda{ 15 namespace cuda{
14 16
15 // this kernel calculates the vote value by adding up the gradient magnitudes of every voter that this pixel is located in their voting area 17 // this kernel calculates the vote value by adding up the gradient magnitudes of every voter that this pixel is located in their voting area
16 template<typename T> 18 template<typename T>
17 - __global__ void cuda_vote(T* gpuVote, T* gpuGrad, T* gpuTable, T phi, int rmax, int x, int y){ 19 + __global__ void cuda_vote(T* gpuVote, T* gpuGrad, T* gpuTable, T phi, int rmax, size_t x, size_t y, bool gradmag = true){
18 20
19 extern __shared__ T S[]; 21 extern __shared__ T S[];
20 T* shared_atan = S; 22 T* shared_atan = S;
@@ -22,12 +24,12 @@ namespace stim{ @@ -22,12 +24,12 @@ namespace stim{
22 stim::cuda::threadedMemcpy((char*)shared_atan, (char*)gpuTable, sizeof(T) * n_table, threadIdx.x, blockDim.x); 24 stim::cuda::threadedMemcpy((char*)shared_atan, (char*)gpuTable, sizeof(T) * n_table, threadIdx.x, blockDim.x);
23 25
24 // calculate the 2D coordinates for this current thread. 26 // calculate the 2D coordinates for this current thread.
25 - int xi = blockIdx.x * blockDim.x + threadIdx.x;  
26 - int yi = blockIdx.y * blockDim.y + threadIdx.y; 27 + size_t xi = blockIdx.x * blockDim.x + threadIdx.x;
  28 + size_t yi = blockIdx.y * blockDim.y + threadIdx.y;
27 29
28 if(xi >= x || yi >= y) return; 30 if(xi >= x || yi >= y) return;
29 // convert 2D coordinates to 1D 31 // convert 2D coordinates to 1D
30 - int i = yi * x + xi; 32 + size_t i = yi * x + xi;
31 33
32 // calculate the voting direction based on the grtadient direction 34 // calculate the voting direction based on the grtadient direction
33 float theta = gpuGrad[2*i]; 35 float theta = gpuGrad[2*i];
@@ -50,7 +52,7 @@ namespace stim{ @@ -50,7 +52,7 @@ namespace stim{
50 bb.trim_low(0, 0); //make sure the bounding box doesn't go outside the image 52 bb.trim_low(0, 0); //make sure the bounding box doesn't go outside the image
51 bb.trim_high(x-1, y-1); 53 bb.trim_high(x-1, y-1);
52 54
53 - int by, bx; 55 + size_t by, bx;
54 int dx, dy; 56 int dx, dy;
55 57
56 unsigned int ind_g; //initialize the maximum vote value to zero 58 unsigned int ind_g; //initialize the maximum vote value to zero
@@ -66,7 +68,8 @@ namespace stim{ @@ -66,7 +68,8 @@ namespace stim{
66 alpha = shared_atan[lut_i]; 68 alpha = shared_atan[lut_i];
67 if(dx_sq + dy_sq < rmax_sq && abs(alpha - theta) < phi){ 69 if(dx_sq + dy_sq < rmax_sq && abs(alpha - theta) < phi){
68 ind_g = (by)*x + (bx); 70 ind_g = (by)*x + (bx);
69 - atomicAdd(&gpuVote[ind_g], mag); 71 + if(gradmag) atomicAdd(&gpuVote[ind_g], mag); //add the gradient magnitude (if the gradmag flag is enabled)
  72 + else atomicAdd(&gpuVote[ind_g], 1.0f); //otherwise just add 1
70 73
71 } 74 }
72 } 75 }
@@ -75,24 +78,30 @@ namespace stim{ @@ -75,24 +78,30 @@ namespace stim{
75 } 78 }
76 79
77 80
  81 + /// Iterative voting for an image
  82 + /// @param gpuVote is the resulting vote image
  83 + /// @param gpuGrad is the gradient of the input image
  84 + /// @param gpuTable is the pre-computed atan2() table
  85 + /// @param phi is the angle of the vote region
  86 + /// @param rmax is the estimated radius of the blob (defines the "width" of the vote region)
  87 + /// @param x and y are the spatial dimensions of the gradient image
  88 + /// @param gradmag defines whether or not the gradient magnitude is taken into account during the vote
78 template<typename T> 89 template<typename T>
79 - void gpu_vote(T* gpuVote, T* gpuGrad, T* gpuTable, T phi, unsigned int rmax, unsigned int x, unsigned int y){  
80 -  
81 - 90 + void gpu_vote(T* gpuVote, T* gpuGrad, T* gpuTable, T phi, unsigned int rmax, size_t x, size_t y, bool DEBUG = false, bool gradmag = true){
82 unsigned int max_threads = stim::maxThreadsPerBlock(); 91 unsigned int max_threads = stim::maxThreadsPerBlock();
83 - dim3 threads( sqrt(max_threads), sqrt(max_threads) );  
84 - dim3 blocks(x/threads.x + 1, y/threads.y + 1); 92 + dim3 threads( (unsigned int)sqrt(max_threads), (unsigned int)sqrt(max_threads) );
  93 + dim3 blocks((unsigned int)x/threads.x + 1, (unsigned int)y/threads.y + 1);
85 size_t table_bytes = sizeof(T) * (rmax * 2 + 1) * (rmax * 2 + 1); 94 size_t table_bytes = sizeof(T) * (rmax * 2 + 1) * (rmax * 2 + 1);
86 size_t shared_mem_req = table_bytes;// + template_bytes; 95 size_t shared_mem_req = table_bytes;// + template_bytes;
87 - std::cout<<"Shared Memory required: "<<shared_mem_req<<std::endl; 96 + if (DEBUG) std::cout<<"Shared Memory required: "<<shared_mem_req<<std::endl;
88 size_t shared_mem = stim::sharedMemPerBlock(); 97 size_t shared_mem = stim::sharedMemPerBlock();
89 if(shared_mem_req > shared_mem){ 98 if(shared_mem_req > shared_mem){
90 - std::cout<<"Error: insufficient shared memory for this implementation of cuda_update_dir()."<<std::endl; 99 + std::cout<<"Error: insufficient shared memory for this implementation of cuda_vote()."<<std::endl;
91 exit(1); 100 exit(1);
92 } 101 }
93 102
94 //call the kernel to do the voting 103 //call the kernel to do the voting
95 - cuda_vote <<< blocks, threads, shared_mem_req>>>(gpuVote, gpuGrad, gpuTable, phi, rmax, x , y); 104 + cuda_vote <<< blocks, threads, shared_mem_req>>>(gpuVote, gpuGrad, gpuTable, phi, rmax, x , y, gradmag);
96 105
97 } 106 }
98 107
stim/cuda/ivote/vote_atomic_shared.cuh renamed to stim/iVote/ivote2/vote_atomic_shared.cuh
stim/cuda/ivote/vote_shared.cuh renamed to stim/iVote/ivote2/vote_shared.cuh
stim/cuda/ivote/vote_shared_32-32.cuh renamed to stim/iVote/ivote2/vote_shared_32-32.cuh
stim/cuda/ivote/vote_threshold_global.cuh renamed to stim/iVote/ivote2/vote_threshold_global.cuh
stim/image/bmp.h 0 → 100644
  1 +#ifndef STIM_BMP_H
  2 +#define STIM_BMP_H
  3 +
  4 +#include <fstream>
  5 +#include <iostream>
  6 +
  7 +namespace stim {
  8 +#pragma pack(1)
  9 + typedef unsigned int DWORD;
  10 + typedef unsigned short WORD;
  11 + typedef signed int LONG;
  12 + typedef struct tagBITMAPFILEHEADER {
  13 + WORD bfType;
  14 + DWORD bfSize;
  15 + WORD bfReserved1;
  16 + WORD bfReserved2;
  17 + DWORD bfOffBits;
  18 + } BITMAPFILEHEADER, *PBITMAPFILEHEADER;
  19 +
  20 + const unsigned int DIB_BITMAPCOREHEADER = 12;
  21 + const unsigned int DIB_OS21XBITMAPHEADER = 16;
  22 + const unsigned int DIB_BITMAPINFOHEADER = 40;
  23 + const unsigned int DIB_BITMAPV2INFOHEADER = 52;
  24 + const unsigned int DIB_BITMAPV3INFOHEADER = 56;
  25 + const unsigned int DIB_OS22XBITMAPHEADER = 64;
  26 + const unsigned int DIB_BITMAPV4HEADER = 108;
  27 + const unsigned int DIB_BITMAPV5HEADER = 124;
  28 +
  29 + typedef struct tagBITMAPCOREHEADER {
  30 + DWORD bcSize;
  31 + WORD bcWidth;
  32 + WORD bcHeight;
  33 + WORD bcPlanes;
  34 + WORD bcBitCount;
  35 + } BITMAPCOREHEADER, *PBITMAPCOREHEADER;
  36 +
  37 + typedef struct tagBITMAPINFOHEADER {
  38 + DWORD biSize; //40 bytes
  39 + LONG biWidth;
  40 + LONG biHeight;
  41 + WORD biPlanes;
  42 + WORD biBitCount;
  43 + DWORD biCompression;
  44 + DWORD biSizeImage;
  45 + LONG biXPelsPerMeter;
  46 + LONG biYPelsPerMeter;
  47 + DWORD biClrUsed;
  48 + DWORD biClrImportant;
  49 + } BITMAPINFOHEADER, *PBITMAPINFOHEADER;
  50 +
  51 + // From FileFormat.info
  52 + typedef struct {
  53 + DWORD Size; /* Size of this header in bytes */
  54 + LONG Width; /* Image width in pixels */
  55 + LONG Height; /* Image height in pixels */
  56 + WORD Planes; /* Number of color planes */
  57 + WORD BitsPerPixel; /* Number of bits per pixel */
  58 + DWORD Compression; /* Compression methods used */
  59 + DWORD SizeOfBitmap; /* Size of bitmap in bytes */
  60 + LONG HorzResolution; /* Horizontal resolution in pixels per meter */
  61 + LONG VertResolution; /* Vertical resolution in pixels per meter */
  62 + DWORD ColorsUsed; /* Number of colors in the image */
  63 + DWORD ColorsImportant; /* Minimum number of important colors */
  64 + /* Fields added for Windows 4.x follow this line */
  65 +
  66 + DWORD RedMask; /* Mask identifying bits of red component */
  67 + DWORD GreenMask; /* Mask identifying bits of green component */
  68 + DWORD BlueMask; /* Mask identifying bits of blue component */
  69 + DWORD AlphaMask; /* Mask identifying bits of alpha component */
  70 + DWORD CSType; /* Color space type */
  71 + LONG RedX; /* X coordinate of red endpoint */
  72 + LONG RedY; /* Y coordinate of red endpoint */
  73 + LONG RedZ; /* Z coordinate of red endpoint */
  74 + LONG GreenX; /* X coordinate of green endpoint */
  75 + LONG GreenY; /* Y coordinate of green endpoint */
  76 + LONG GreenZ; /* Z coordinate of green endpoint */
  77 + LONG BlueX; /* X coordinate of blue endpoint */
  78 + LONG BlueY; /* Y coordinate of blue endpoint */
  79 + LONG BlueZ; /* Z coordinate of blue endpoint */
  80 + DWORD GammaRed; /* Gamma red coordinate scale value */
  81 + DWORD GammaGreen; /* Gamma green coordinate scale value */
  82 + DWORD GammaBlue; /* Gamma blue coordinate scale value */
  83 + } WIN4XBITMAPHEADER;
  84 +
  85 + typedef struct {
  86 + DWORD bV5Size;
  87 + LONG bV5Width;
  88 + LONG bV5Height;
  89 + WORD bV5Planes;
  90 + WORD bV5BitCount;
  91 + DWORD bV5Compression;
  92 + DWORD bV5SizeImage;
  93 + LONG bV5XPelsPerMeter;
  94 + LONG bV5YPelsPerMeter;
  95 + DWORD bV5ClrUsed;
  96 + DWORD bV5ClrImportant;
  97 + DWORD bV5RedMask;
  98 + DWORD bV5GreenMask;
  99 + DWORD bV5BlueMask;
  100 + DWORD bV5AlphaMask;
  101 + DWORD bV5CSType;
  102 + LONG RedX; /* X coordinate of red endpoint */
  103 + LONG RedY; /* Y coordinate of red endpoint */
  104 + LONG RedZ; /* Z coordinate of red endpoint */
  105 + LONG GreenX; /* X coordinate of green endpoint */
  106 + LONG GreenY; /* Y coordinate of green endpoint */
  107 + LONG GreenZ; /* Z coordinate of green endpoint */
  108 + LONG BlueX; /* X coordinate of blue endpoint */
  109 + LONG BlueY; /* Y coordinate of blue endpoint */
  110 + LONG BlueZ; /* Z coordinate of blue endpoint */
  111 + DWORD bV5GammaRed;
  112 + DWORD bV5GammaGreen;
  113 + DWORD bV5GammaBlue;
  114 + DWORD bV5Intent;
  115 + DWORD bV5ProfileData;
  116 + DWORD bV5ProfileSize;
  117 + DWORD bV5Reserved;
  118 + } BITMAPV5HEADER, *PBITMAPV5HEADER;
  119 +
  120 +
  121 + //compression methods
  122 + const unsigned int STIM_BI_RGB = 0;
  123 + const unsigned int STIM_BI_BITFIELDS = 3;
  124 +
  125 + class bmp {
  126 + std::ifstream file;
  127 + public:
  128 + unsigned int dib_header_size;
  129 + size_t bit_pos; // start position (relative to the beginning of the file) of the bitmap bits
  130 + size_t total_size; //total size of the bitmap file (in bytes)
  131 + size_t width;
  132 + size_t height;
  133 + int channels;
  134 + int bits_per_pixel;
  135 + unsigned int compression;
  136 +
  137 + size_t bytes() {
  138 + return width * height * bits_per_pixel / 8;
  139 + }
  140 + void read_bmpFileHeader() {
  141 + BITMAPFILEHEADER file_header;
  142 + file.read((char*)&file_header, sizeof(BITMAPFILEHEADER));
  143 + bit_pos = file_header.bfOffBits;
  144 + total_size = file_header.bfSize;
  145 + }
  146 + void read_bmpCoreHeader() {
  147 + tagBITMAPCOREHEADER header;
  148 + file.read((char*)&header, sizeof(tagBITMAPCOREHEADER));
  149 + width = header.bcWidth;
  150 + height = header.bcHeight;
  151 + bits_per_pixel = header.bcBitCount;
  152 + compression = 0;
  153 + }
  154 + void read_bmpInfoHeader() {
  155 + tagBITMAPINFOHEADER info_header;
  156 + file.read((char*)&info_header, sizeof(tagBITMAPINFOHEADER));
  157 + width = info_header.biWidth;
  158 + height = info_header.biHeight;
  159 + bits_per_pixel = info_header.biBitCount;
  160 + compression = info_header.biCompression;
  161 + }
  162 + void read_bmpV4Header() {
  163 + WIN4XBITMAPHEADER header;
  164 + file.read((char*)&header, sizeof(WIN4XBITMAPHEADER));
  165 + width = header.Width;
  166 + height = header.Height;
  167 + bits_per_pixel = header.BitsPerPixel;
  168 + compression = header.Compression;
  169 + }
  170 + void read_bmpV5Header() {
  171 + BITMAPV5HEADER header;
  172 + file.read((char*)&header, sizeof(BITMAPV5HEADER));
  173 + width = header.bV5Width;
  174 + height = header.bV5Height;
  175 + bits_per_pixel = header.bV5BitCount;
  176 + compression = header.bV5Compression;
  177 + }
  178 + void read_dib() { //read the bitmap DIB information header
  179 + std::streamoff header_pos = file.tellg();
  180 + file.read((char*)&dib_header_size, sizeof(unsigned int));
  181 + file.seekg(header_pos);
  182 + switch (dib_header_size) {
  183 + case DIB_BITMAPCOREHEADER: read_bmpCoreHeader(); break;
  184 + case DIB_BITMAPINFOHEADER: read_bmpInfoHeader(); break;
  185 + case DIB_BITMAPV4HEADER: read_bmpV4Header(); break;
  186 + case DIB_BITMAPV5HEADER: read_bmpV5Header(); break;
  187 + default:
  188 + std::cout << "stim::bmp ERROR: this bitmap header format isn't supported" << std::endl;
  189 + exit(1);
  190 + }
  191 + }
  192 +
  193 + bool open(std::string filename) { //open the bitmap file and read the header data
  194 + file.open(filename, std::ifstream::binary);
  195 + if (!file) {
  196 + std::cout << "stim::bmp ERROR: error opening file: " << filename.c_str() << std::endl;
  197 + return false;
  198 + }
  199 + read_bmpFileHeader(); //read the file header
  200 + read_dib();
  201 + if (compression != STIM_BI_RGB) { //check for compression
  202 + std::cout << "stim::bmp ERROR: this file is compressed, and compression is not supported" << std::endl;
  203 + return false;
  204 + }
  205 + return true;
  206 + }
  207 + void close() {
  208 + file.close();
  209 + }
  210 +
  211 + /// Copy the bitmap data into a pre-allocated array
  212 + bool read(char* dst){
  213 + file.seekg(bit_pos); //seek to the beginning of the data array
  214 + size_t row_bytes = width * bits_per_pixel / 8; //number of bytes in each row
  215 + size_t padding = row_bytes % 4; //calculate the padding on disk for each row (rows must be multiples of 4)
  216 +
  217 + if(file){
  218 + for (size_t h = 0; h < height; h++) { //for each row in the image
  219 + file.read(dst + (height - h - 1) * row_bytes, row_bytes); //read the row of image data
  220 + file.seekg(padding, std::ios::cur); //seek to the end of the row on disk
  221 + if (file.eof()) std::cout << "stim::bmp ERROR: array size incorrect, end of file reached while reading bitmap." << std::endl;
  222 + else if (file.fail()) std::cout << "stim::bmp ERROR: reading bitmap array failed." << std::endl;
  223 + else if (file.bad()) std::cout << "stim::bmp ERROR: stream integrity failed while reading bitmap array" << std::endl;
  224 + }
  225 + return true;
  226 + }
  227 + else{
  228 + std::cout<<"stim::bmp ERROR: could not read array from file."<<std::endl;
  229 + return false;
  230 + }
  231 + }
  232 + };
  233 +
  234 + bool save_bmp(std::string filename, char* bits, size_t width, size_t height) {
  235 + size_t bits_per_pixel = 24;
  236 + size_t row_bytes = width * bits_per_pixel / 8; //number of bytes in each row
  237 + size_t padding = row_bytes % 4; //calculate the padding on disk for each row (rows must be multiples of 4)
  238 +
  239 + tagBITMAPFILEHEADER file_header;
  240 + memset(&file_header, 0, sizeof(tagBITMAPFILEHEADER)); //initialize the file header structure to zero
  241 + file_header.bfOffBits = sizeof(tagBITMAPFILEHEADER) + sizeof(tagBITMAPCOREHEADER); //the offset includes both the file and DIB header
  242 + file_header.bfSize = (stim::DWORD)(file_header.bfOffBits + (row_bytes + padding) * height); //calculate the size of the bitmap file
  243 + file_header.bfType = (stim::DWORD)0x4D42;
  244 +
  245 + tagBITMAPCOREHEADER info_header;
  246 + memset(&info_header, 0, sizeof(tagBITMAPCOREHEADER)); //initialize the info header to zero
  247 + info_header.bcBitCount = (stim::DWORD)bits_per_pixel;
  248 + info_header.bcHeight = (stim::WORD)height;
  249 + info_header.bcWidth = (stim::WORD)width;
  250 + info_header.bcSize = sizeof(tagBITMAPCOREHEADER);
  251 + info_header.bcPlanes = 1;
  252 +
  253 + std::ofstream outfile(filename, std::ios::binary); //open the output file for binary writing
  254 + outfile.write((char*)&file_header, sizeof(tagBITMAPFILEHEADER)); //write the file header
  255 + outfile.write((char*)&info_header, sizeof(tagBITMAPCOREHEADER)); //write the information header
  256 +
  257 + char* pad = (char*)malloc(padding); //create a buffer that will be written as padding
  258 + memset(pad, 0, padding);
  259 + for (size_t h = 0; h < height; h++) {
  260 + outfile.write((char*)(bits + (height - h - 1) * row_bytes), row_bytes); //write the bitmap data
  261 + outfile.write(pad, padding);
  262 + }
  263 + free(pad);
  264 + return true;
  265 + }
  266 +}
  267 +
  268 +#endif
0 \ No newline at end of file 269 \ No newline at end of file
stim/image/image.h
1 #ifndef STIM_IMAGE_H 1 #ifndef STIM_IMAGE_H
2 #define STIM_IMAGE_H 2 #define STIM_IMAGE_H
3 3
  4 +#ifdef _WIN32
  5 +#undef max
  6 +#endif
  7 +
4 #ifdef USING_OPENCV 8 #ifdef USING_OPENCV
5 - #include <opencv2/core/core.hpp>  
6 - #include <opencv2/highgui/highgui.hpp> 9 + //#include <opencv2/core/core.hpp>
  10 + //#include <opencv2/highgui/highgui.hpp>
  11 + #include <opencv2/opencv.hpp>
  12 +#else
  13 + #include <stim/image/bmp.h>
7 #endif 14 #endif
8 #include <vector> 15 #include <vector>
9 #include <iostream> 16 #include <iostream>
10 -#include <limits> 17 +#include <limits> //use limits and remove the MIN and MAX macros
11 #include <typeinfo> 18 #include <typeinfo>
12 #include <fstream> 19 #include <fstream>
  20 +#include <cstring>
  21 +
  22 +
  23 +#include <stim/parser/filename.h>
13 24
14 namespace stim{ 25 namespace stim{
15 /// This static class provides the STIM interface for loading, saving, and storing 2D images. 26 /// This static class provides the STIM interface for loading, saving, and storing 2D images.
@@ -45,6 +56,10 @@ class image{ @@ -45,6 +56,10 @@ class image{
45 void allocate(){ 56 void allocate(){
46 unalloc(); 57 unalloc();
47 img = (T*) malloc( sizeof(T) * R[0] * R[1] * R[2] ); //allocate memory 58 img = (T*) malloc( sizeof(T) * R[0] * R[1] * R[2] ); //allocate memory
  59 + if (img == NULL) {
  60 + std::cout << "stim::image ERROR - failed to allocate memory for image" << std::endl;
  61 + exit(1);
  62 + }
48 } 63 }
49 64
50 void allocate(size_t x, size_t y, size_t c){ //allocate memory based on the resolution 65 void allocate(size_t x, size_t y, size_t c){ //allocate memory based on the resolution
@@ -52,8 +67,6 @@ class image{ @@ -52,8 +67,6 @@ class image{
52 allocate(); //allocate memory 67 allocate(); //allocate memory
53 } 68 }
54 69
55 - size_t bytes(){ return size() * sizeof(T); }  
56 -  
57 inline size_t idx(size_t x, size_t y, size_t c = 0) const { 70 inline size_t idx(size_t x, size_t y, size_t c = 0) const {
58 return y * R[0] * R[1] + x * R[0] + c; 71 return y * R[0] * R[1] + x * R[0] + c;
59 } 72 }
@@ -74,23 +87,16 @@ class image{ @@ -74,23 +87,16 @@ class image{
74 #endif 87 #endif
75 /// Returns the value for "white" based on the dynamic range (assumes white is 1.0 for floating point images) 88 /// Returns the value for "white" based on the dynamic range (assumes white is 1.0 for floating point images)
76 T white(){ 89 T white(){
77 -  
78 - if(typeid(T) == typeid(unsigned char)) return UCHAR_MAX;  
79 - if(typeid(T) == typeid(unsigned short)) return SHRT_MAX;  
80 - if(typeid(T) == typeid(unsigned)) return UINT_MAX;  
81 - if(typeid(T) == typeid(unsigned long)) return ULONG_MAX;  
82 - if(typeid(T) == typeid(unsigned long long)) return ULLONG_MAX;  
83 - if(typeid(T) == typeid(float)) return 1.0f;  
84 - if(typeid(T) == typeid(double)) return 1.0;  
85 -  
86 - std::cout<<"ERROR in stim::image::white - no white value known for this data type"<<std::endl;  
87 - exit(1);  
88 - 90 + if (typeid(T) == typeid(double) || typeid(T) == typeid(float))
  91 + return (T)1.0;
  92 + else
  93 + return std::numeric_limits<T>::max();
89 } 94 }
90 95
91 -  
92 public: 96 public:
93 97
  98 + size_t bytes() { return size() * sizeof(T); }
  99 +
94 /// Default constructor - creates an empty image object 100 /// Default constructor - creates an empty image object
95 image(){ init(); } //initialize all variables to zero, don't allocate any memory 101 image(){ init(); } //initialize all variables to zero, don't allocate any memory
96 102
@@ -125,19 +131,49 @@ public: @@ -125,19 +131,49 @@ public:
125 free(img); 131 free(img);
126 } 132 }
127 133
128 - ///Resize an image 134 + ///Resize an image - this function looks like it hasn't been implemented
129 void resize(size_t x, size_t y, size_t c = 1) { 135 void resize(size_t x, size_t y, size_t c = 1) {
130 allocate(x, y, c); 136 allocate(x, y, c);
131 } 137 }
132 138
133 stim::image<T>& operator=(const stim::image<T>& I){ 139 stim::image<T>& operator=(const stim::image<T>& I){
134 - init();  
135 if(&I == this) //handle self-assignment 140 if(&I == this) //handle self-assignment
136 return *this; 141 return *this;
  142 + init();
137 allocate(I.X(), I.Y(), I.C()); 143 allocate(I.X(), I.Y(), I.C());
138 memcpy(img, I.img, bytes()); 144 memcpy(img, I.img, bytes());
139 return *this; 145 return *this;
140 } 146 }
  147 +#ifndef USING_OPENCV
  148 + void load_bmp(std::string filename) {
  149 + stim::bmp bitmap;
  150 + bitmap.open(filename); //load the bitmap and read the headers
  151 + resize(bitmap.width, bitmap.height, 3); //resize the current image to match the bitmap
  152 + if (!bitmap.read((char*)img)) { //read the bits from file
  153 + std::cout << "stim::image ERROR: problem loading bitmap image." << std::endl;
  154 + exit(1);
  155 + }
  156 + bitmap.close(); //close the bitmap file
  157 + }
  158 +#endif
  159 +
  160 + //determines if a filename represents a valid file format that can be loaded/saved
  161 + static bool test_filename(std::string f) {
  162 + stim::filename fname = f;
  163 + std::string ext = fname.extension();
  164 +#ifdef USING_OPENCV
  165 + if (ext == "bmp" ||
  166 + ext == "jpg" ||
  167 + ext == "png" ||
  168 + ext == "pbm" ||
  169 + ext == "tif" )
  170 + return true;
  171 +#else
  172 + if (ext == "pbm" || ext == "bmp")
  173 + return true;
  174 +#endif
  175 + return false;
  176 + }
141 177
142 //save a Netpbm file 178 //save a Netpbm file
143 void load_netpbm(std::string filename) { 179 void load_netpbm(std::string filename) {
@@ -146,10 +182,6 @@ public: @@ -146,10 +182,6 @@ public:
146 std::cout << "Error opening input file in image::load_netpbm()" << std::endl; 182 std::cout << "Error opening input file in image::load_netpbm()" << std::endl;
147 exit(1); 183 exit(1);
148 } 184 }
149 - if (sizeof(T) != 1) {  
150 - std::cout << "Error in image::load_netpbm() - data type must be 8-bit integer." << std::endl;  
151 - exit(1);  
152 - }  
153 185
154 size_t nc; //allocate space for the number of channels 186 size_t nc; //allocate space for the number of channels
155 char format[2]; //allocate space to hold the image format tag 187 char format[2]; //allocate space to hold the image format tag
@@ -196,9 +228,12 @@ public: @@ -196,9 +228,12 @@ public:
196 } 228 }
197 size_t h = atoi(sh.c_str()); //convert the string into an integer 229 size_t h = atoi(sh.c_str()); //convert the string into an integer
198 230
199 - allocate(w, h, nc); //allocate space for the image  
200 - infile.read((char*)img, size()); //copy the binary data from the file to the image  
201 - infile.close(); 231 + allocate(w, h, nc); //allocate space for the image
  232 + unsigned char* buffer = (unsigned char*)malloc(w * h * nc); //create a buffer to store the read data
  233 + infile.read((char*)buffer, size()); //copy the binary data from the file to the image
  234 + infile.close(); //close the file
  235 + for (size_t n = 0; n < size(); n++) img[n] = (T)buffer[n]; //copy the buffer data into the image
  236 + free(buffer); //free the buffer array
202 } 237 }
203 238
204 239
@@ -218,6 +253,14 @@ public: @@ -218,6 +253,14 @@ public:
218 } 253 }
219 } 254 }
220 #endif 255 #endif
  256 + //Copy N data points from source to dest, casting while doing so
  257 + template<typename S, typename D>
  258 + void type_copy(S* source, D* dest, size_t N) {
  259 + if (typeid(S) == typeid(D)) //if both types are the same
  260 + memcpy(dest, source, N * sizeof(S)); //just use a memcpy
  261 + for (size_t n = 0; n < N; n++) //otherwise, iterate through each element
  262 + dest[n] = (D)source[n]; //copy and cast
  263 + }
221 /// Load an image from a file 264 /// Load an image from a file
222 void load(std::string filename){ 265 void load(std::string filename){
223 #ifdef USING_OPENCV 266 #ifdef USING_OPENCV
@@ -226,17 +269,24 @@ public: @@ -226,17 +269,24 @@ public:
226 std::cout<<"ERROR stim::image::load() - unable to find image "<<filename<<std::endl; 269 std::cout<<"ERROR stim::image::load() - unable to find image "<<filename<<std::endl;
227 exit(1); 270 exit(1);
228 } 271 }
  272 + int cv_type = cvImage.type();
229 int cols = cvImage.cols; 273 int cols = cvImage.cols;
230 int rows = cvImage.rows; 274 int rows = cvImage.rows;
231 int channels = cvImage.channels(); 275 int channels = cvImage.channels();
232 allocate(cols, rows, channels); //allocate space for the image 276 allocate(cols, rows, channels); //allocate space for the image
233 - unsigned char* cv_ptr = (unsigned char*)cvImage.data;  
234 - if(C() == 1) //if this is a single-color image, just copy the data  
235 - memcpy(img, cv_ptr, bytes()); 277 + size_t img_bytes = bytes();
  278 + unsigned char* cv_ptr = (unsigned char*)cvImage.data;
  279 + if (C() == 1) //if this is a single-color image, just copy the data
  280 + type_copy<unsigned char, T>(cv_ptr, img, size());
  281 + //memcpy(img, cv_ptr, bytes());
236 if(C() == 3) //if this is a 3-color image, OpenCV uses BGR interleaving 282 if(C() == 3) //if this is a 3-color image, OpenCV uses BGR interleaving
237 from_opencv(cv_ptr, X(), Y()); 283 from_opencv(cv_ptr, X(), Y());
238 #else 284 #else
239 - load_netpbm(filename); 285 + stim::filename file(filename);
  286 + if (file.extension() == "ppm")
  287 + load_netpbm(filename);
  288 + else if (file.extension() == "bmp")
  289 + load_bmp(filename);
240 #endif 290 #endif
241 } 291 }
242 292
@@ -267,9 +317,20 @@ public: @@ -267,9 +317,20 @@ public:
267 outfile.write((const char*)img, size()); //write the binary data 317 outfile.write((const char*)img, size()); //write the binary data
268 outfile.close(); 318 outfile.close();
269 } 319 }
  320 +#ifndef USING_OPENCV
  321 + void save_bmp(std::string filename) {
  322 + stim::save_bmp(filename, (char*)img, width(), height());
  323 + }
  324 +#endif
270 325
271 //save a file 326 //save a file
272 void save(std::string filename){ 327 void save(std::string filename){
  328 + stim::filename file(filename);
  329 + if (file.extension() == "raw" || file.extension() == "") {
  330 + std::ofstream outfile(filename.c_str(), std::ios::binary);
  331 + outfile.write((char*)img, sizeof(T) * R[0] * R[1] * R[2]);
  332 + outfile.close();
  333 + }
273 #ifdef USING_OPENCV 334 #ifdef USING_OPENCV
274 //OpenCV uses an interleaved format, so convert first and then output 335 //OpenCV uses an interleaved format, so convert first and then output
275 T* buffer = (T*) malloc(bytes()); 336 T* buffer = (T*) malloc(bytes());
@@ -282,10 +343,34 @@ public: @@ -282,10 +343,34 @@ public:
282 cv::imwrite(filename, cvImage); 343 cv::imwrite(filename, cvImage);
283 free(buffer); 344 free(buffer);
284 #else 345 #else
285 - save_netpbm(filename); 346 + if (file.extension() == "ppm")
  347 + save_netpbm(filename);
  348 + else if (file.extension() == "bmp")
  349 + save_bmp(filename);
  350 + else {
  351 + std::cout << "stim::image ERROR: File type not supported without OpenCV. Make sure to link OpenCV and define USING_OPENCV" << std::endl;
  352 + exit(1);
  353 + }
286 #endif 354 #endif
287 } 355 }
288 356
  357 + /// Returns an image cast to the specified format
  358 + template<typename U>
  359 + image<U> convert() {
  360 +
  361 + image<U> new_image(R[1], R[2], R[0]); //create a new image with the destination data type
  362 +
  363 + size_t ni = R[0] * R[1] * R[2]; //calculate the number of data points in the image
  364 +
  365 + double inmax = (std::numeric_limits<T>::max)(); //get the maximum value for the input image
  366 + double outmax = (std::numeric_limits<U>::max)(); //get the maximum value for the output image
  367 + for (size_t i = 0; i < ni; i++) { //for each pixel in the image
  368 + if (img[i] > outmax) new_image(i) = outmax; //if the source pixel is greater than the maximum destination pixel, set the output to maximum
  369 + else new_image(i) = img[i]; //otherwise, copy the source value and cast it to the destination value
  370 + }
  371 + return new_image;
  372 + }
  373 +
289 void set_interleaved(T* buffer, size_t width, size_t height, size_t channels){ 374 void set_interleaved(T* buffer, size_t width, size_t height, size_t channels){
290 allocate(width, height, channels); 375 allocate(width, height, channels);
291 memcpy(img, buffer, bytes()); 376 memcpy(img, buffer, bytes());
@@ -379,18 +464,92 @@ public: @@ -379,18 +464,92 @@ public:
379 return img[idx(x, y, c)]; 464 return img[idx(x, y, c)];
380 } 465 }
381 466
  467 + /// This function returns a pixel reference based on a 1D index into the image
  468 + T& operator()(size_t i) {
  469 + return img[i];
  470 + }
  471 +
382 /// Set all elements in the image to a given scalar value 472 /// Set all elements in the image to a given scalar value
383 473
384 /// @param v is the value used to set all values in the image 474 /// @param v is the value used to set all values in the image
385 - image<T> operator=(T v){  
386 - 475 + void set_all(T v) { //set all elements of the image to a given value v
387 size_t N = size(); 476 size_t N = size();
388 -  
389 - for(size_t n = 0; n < N; n++)  
390 - img[n] = v;  
391 - 477 + for (size_t n = 0; n < N; n++) img[n] = v;
  478 + }
  479 + image<T> operator=(T v){
  480 + set_all(v);
392 return *this; 481 return *this;
  482 + }
393 483
  484 + /// invert the image, given a specified maximum value (ex. maxval = 255, I' = 255 - I)
  485 + /*image<T> invert(T maxval) {
  486 + image<T> result(width(), height(), channels()); //create a new image
  487 + size_t N = size(); //get the number of elements in the image
  488 + for (size_t n = 0; n < N; n++)
  489 + result.data()[n] = maxval - img[n]; //perform the inversion and save the result to the new image
  490 + return result;
  491 + }*/
  492 +
  493 + /// Stretch the contrast of the image such that the minimum and maximum intensity match the given values
  494 + image<T> stretch(T low, T high) {
  495 + T maxval = maxv();
  496 + T minval = minv();
  497 + image<T> result = *this; //create a new image for output
  498 + if (maxval == minval) { //if the minimum and maximum values are the same, return an image composed of low
  499 + result = low;
  500 + return result;
  501 + }
  502 +
  503 + size_t N = size(); //get the number of values in the image
  504 + T range = maxval - minval; //calculate the current range of the image
  505 + T desired_range = high - low; //calculate the desired range of the image
  506 + for (size_t n = 0; n < N; n++) { //for each element in the image
  507 + result.data()[n] = desired_range * (img[n] - minval) / range + low;
  508 + }
  509 + return result;
  510 + }
  511 +
  512 + /// Add a border of width w with the given value around the image
  513 + /// @param w specifies the total size of the border
  514 + /// @param T is the pixel value (all channels will be the same)
  515 + image<T> border(size_t w, T value = 0) {
  516 + image<T> result(width() + w * 2, height() + w * 2, channels()); //create an output image
  517 + result = value; //assign the border value to all pixels in the new image
  518 + for (size_t y = 0; y < height(); y++) { //for each pixel in the original image
  519 + for (size_t x = 0; x < width(); x++) {
  520 + size_t n = (y + w) * (width() + w * 2) + x + w; //calculate the index of the corresponding pixel in the result image
  521 + size_t n0 = idx(x,y); //calculate the index for this pixel in the original image
  522 + result.data()[n] = img[n0]; // copy the original image to the result image afer the border area
  523 + }
  524 + }
  525 + return result;
  526 + }
  527 +
  528 + /// Adds curcular padding for the specified number of pixels - in this case replicating the boundary pixels
  529 + image<T> pad_replicate(size_t p) {
  530 + image<T> result(width() + p * 2, height() + p * 2, channels()); //create an output image
  531 + result = 0;
  532 + //result = value; //assign the border value to all pixels in the new image
  533 + for (size_t y = 0; y < height(); y++) { //for each pixel in the original image
  534 + for (size_t x = 0; x < width(); x++) {
  535 + size_t n = (y + p) * (width() + p * 2) + x + p; //calculate the index of the corresponding pixel in the result image
  536 + size_t n0 = idx(x, y); //calculate the index for this pixel in the original image
  537 + result.data()[n] = img[n0]; // copy the original image to the result image afer the border area
  538 + }
  539 + }
  540 + size_t l = p;
  541 + size_t r = p + width() - 1;
  542 + size_t t = p;
  543 + size_t b = p + height() - 1;
  544 + for (size_t y = 0; y < p; y++) for (size_t x = l; x <= r; x++) result(x, y) = result(x, t); //pad the top
  545 + for (size_t y = b + 1; y < result.height(); y++) for (size_t x = l; x <= r; x++) result(x, y) = result(x, b); //pad the bottom
  546 + for (size_t y = t; y <= b; y++) for (size_t x = 0; x < l; x++) result(x, y) = result(l, y); //pad the left
  547 + for (size_t y = t; y <= b; y++) for (size_t x = r+1; x < result.width(); x++) result(x, y) = result(r, y); //pad the right
  548 + for (size_t y = 0; y < t; y++) for (size_t x = 0; x < l; x++) result(x, y) = result(l, t); //pad the top left
  549 + for (size_t y = 0; y < t; y++) for (size_t x = r+1; x < result.width(); x++) result(x, y) = result(r, t); //pad the top right
  550 + for (size_t y = b+1; y < result.height(); y++) for (size_t x = 0; x < l; x++) result(x, y) = result(l, b); //pad the bottom left
  551 + for (size_t y = b+1; y < result.height(); y++) for (size_t x = r + 1; x < result.width(); x++) result(x, y) = result(r, b); //pad the bottom right
  552 + return result;
394 } 553 }
395 554
396 /// Copy the given data to the specified channel 555 /// Copy the given data to the specified channel
@@ -474,7 +633,6 @@ public: @@ -474,7 +633,6 @@ public:
474 max_val = img[n]; 633 max_val = img[n];
475 } 634 }
476 } 635 }
477 -  
478 return max_val; 636 return max_val;
479 } 637 }
480 638
@@ -502,13 +660,48 @@ public: @@ -502,13 +660,48 @@ public:
502 return r; //return the inverted image 660 return r; //return the inverted image
503 } 661 }
504 662
  663 + image<T> crop(size_t x0, size_t y0, size_t w, size_t h){
  664 + image<T> result(w, h, C()); //create the output cropped image
  665 +
  666 + size_t srci;
  667 + size_t dsti;
  668 + size_t line_bytes = w * C(); //calculate the number of bytes in a line
  669 + for (size_t yi = 0; yi < h; yi++) { //for each row in the cropped image
  670 + srci = (y0 + yi) * X() * C() + x0 * C(); //calculate the source index
  671 + dsti = yi * w * C(); //calculate the destination index
  672 + memcpy(&result.img[dsti], &img[srci], line_bytes); //copy the data
  673 + }
  674 + return result;
  675 + }
  676 +
  677 + //crop regions given by an array of 1D index values
  678 + std::vector< image<T> > crop_idx(size_t w, size_t h, std::vector<size_t> idx) {
  679 + std::vector< image<T> > result(idx.size()); //create an array of image files to return
  680 + for (size_t i = 0; i < idx.size(); i++) { //for each specified index point
  681 + size_t y = idx[i] / X(); //calculate the y coordinate from the 1D index (center of ROI)
  682 + size_t x = idx[i] - y * X(); //calculate the x coordinate (center of ROI)
  683 + y -= w / 2; //update x and y values to reflect the lower corner of the ROI
  684 + x -= h / 2;
  685 + result[i] = crop(x, y, w, h); //get the cropped image and store it in the result array
  686 + }
  687 + return result;
  688 + }
  689 +
  690 + //operator functions
  691 + image<T> operator+(image<T> rhs) {
  692 + size_t N = size(); //calculate the total number of values in the image
  693 + image<T> r(X(), Y(), C()); //allocate space for the resulting image
  694 + for (size_t n = 0; n < N; n++)
  695 + r.img[n] = img[n] + rhs.img[n]; //perform the inversion
  696 + return r; //return the inverted image
  697 + }
  698 +
505 image<T> srgb2lab(){ 699 image<T> srgb2lab(){
506 std::cout<<"ERROR stim::image::srgb2lab - function has been broken, re-implement."<<std::endl; 700 std::cout<<"ERROR stim::image::srgb2lab - function has been broken, re-implement."<<std::endl;
507 exit(1); 701 exit(1);
508 } 702 }
509 703
510 image<T> convolve2(image<T> mask){ 704 image<T> convolve2(image<T> mask){
511 -  
512 std::cout<<"ERROR stim::image::convolve2 - function has been broken, and shouldn't really be in here."<<std::endl; 705 std::cout<<"ERROR stim::image::convolve2 - function has been broken, and shouldn't really be in here."<<std::endl;
513 exit(1); 706 exit(1);
514 } 707 }
stim/math/bessel - Copy.h 0 → 100644
  1 +#ifndef RTS_BESSEL_H
  2 +#define RTS_BESSEL_H
  3 +
  4 +#define _USE_MATH_DEFINES
  5 +#include <math.h>
  6 +#include "../math/complex.h"
  7 +#define eps 1e-15
  8 +#define el 0.5772156649015329
  9 +
  10 +
  11 +namespace stim{
  12 +
  13 +static complex<double> cii(0.0,1.0);
  14 +static complex<double> cone(1.0,0.0);
  15 +static complex<double> czero(0.0,0.0);
  16 +
  17 +template< typename P >
  18 +P gamma(P x)
  19 +{
  20 + int i,k,m;
  21 + P ga,gr,r,z;
  22 +
  23 + static P g[] = {
  24 + 1.0,
  25 + 0.5772156649015329,
  26 + -0.6558780715202538,
  27 + -0.420026350340952e-1,
  28 + 0.1665386113822915,
  29 + -0.421977345555443e-1,
  30 + -0.9621971527877e-2,
  31 + 0.7218943246663e-2,
  32 + -0.11651675918591e-2,
  33 + -0.2152416741149e-3,
  34 + 0.1280502823882e-3,
  35 + -0.201348547807e-4,
  36 + -0.12504934821e-5,
  37 + 0.1133027232e-5,
  38 + -0.2056338417e-6,
  39 + 0.6116095e-8,
  40 + 0.50020075e-8,
  41 + -0.11812746e-8,
  42 + 0.1043427e-9,
  43 + 0.77823e-11,
  44 + -0.36968e-11,
  45 + 0.51e-12,
  46 + -0.206e-13,
  47 + -0.54e-14,
  48 + 0.14e-14};
  49 +
  50 + if (x > 171.0) return 1e308; // This value is an overflow flag.
  51 + if (x == (int)x) {
  52 + if (x > 0.0) {
  53 + ga = 1.0; // use factorial
  54 + for (i=2;i<x;i++) {
  55 + ga *= i;
  56 + }
  57 + }
  58 + else
  59 + ga = 1e308;
  60 + }
  61 + else {
  62 + if (fabs(x) > 1.0) {
  63 + z = fabs(x);
  64 + m = (int)z;
  65 + r = 1.0;
  66 + for (k=1;k<=m;k++) {
  67 + r *= (z-k);
  68 + }
  69 + z -= m;
  70 + }
  71 + else
  72 + z = x;
  73 + gr = g[24];
  74 + for (k=23;k>=0;k--) {
  75 + gr = gr*z+g[k];
  76 + }
  77 + ga = 1.0/(gr*z);
  78 + if (fabs(x) > 1.0) {
  79 + ga *= r;
  80 + if (x < 0.0) {
  81 + ga = -M_PI/(x*ga*sin(M_PI*x));
  82 + }
  83 + }
  84 + }
  85 + return ga;
  86 +}
  87 +
  88 +template<typename P>
  89 +int bessjy01a(P x,P &j0,P &j1,P &y0,P &y1,
  90 + P &j0p,P &j1p,P &y0p,P &y1p)
  91 +{
  92 + P x2,r,ec,w0,w1,r0,r1,cs0,cs1;
  93 + P cu,p0,q0,p1,q1,t1,t2;
  94 + int k,kz;
  95 + static P a[] = {
  96 + -7.03125e-2,
  97 + 0.112152099609375,
  98 + -0.5725014209747314,
  99 + 6.074042001273483,
  100 + -1.100171402692467e2,
  101 + 3.038090510922384e3,
  102 + -1.188384262567832e5,
  103 + 6.252951493434797e6,
  104 + -4.259392165047669e8,
  105 + 3.646840080706556e10,
  106 + -3.833534661393944e12,
  107 + 4.854014686852901e14,
  108 + -7.286857349377656e16,
  109 + 1.279721941975975e19};
  110 + static P b[] = {
  111 + 7.32421875e-2,
  112 + -0.2271080017089844,
  113 + 1.727727502584457,
  114 + -2.438052969955606e1,
  115 + 5.513358961220206e2,
  116 + -1.825775547429318e4,
  117 + 8.328593040162893e5,
  118 + -5.006958953198893e7,
  119 + 3.836255180230433e9,
  120 + -3.649010818849833e11,
  121 + 4.218971570284096e13,
  122 + -5.827244631566907e15,
  123 + 9.476288099260110e17,
  124 + -1.792162323051699e20};
  125 + static P a1[] = {
  126 + 0.1171875,
  127 + -0.1441955566406250,
  128 + 0.6765925884246826,
  129 + -6.883914268109947,
  130 + 1.215978918765359e2,
  131 + -3.302272294480852e3,
  132 + 1.276412726461746e5,
  133 + -6.656367718817688e6,
  134 + 4.502786003050393e8,
  135 + -3.833857520742790e10,
  136 + 4.011838599133198e12,
  137 + -5.060568503314727e14,
  138 + 7.572616461117958e16,
  139 + -1.326257285320556e19};
  140 + static P b1[] = {
  141 + -0.1025390625,
  142 + 0.2775764465332031,
  143 + -1.993531733751297,
  144 + 2.724882731126854e1,
  145 + -6.038440767050702e2,
  146 + 1.971837591223663e4,
  147 + -8.902978767070678e5,
  148 + 5.310411010968522e7,
  149 + -4.043620325107754e9,
  150 + 3.827011346598605e11,
  151 + -4.406481417852278e13,
  152 + 6.065091351222699e15,
  153 + -9.833883876590679e17,
  154 + 1.855045211579828e20};
  155 +
  156 + if (x < 0.0) return 1;
  157 + if (x == 0.0) {
  158 + j0 = 1.0;
  159 + j1 = 0.0;
  160 + y0 = -1e308;
  161 + y1 = -1e308;
  162 + j0p = 0.0;
  163 + j1p = 0.5;
  164 + y0p = 1e308;
  165 + y1p = 1e308;
  166 + return 0;
  167 + }
  168 + x2 = x*x;
  169 + if (x <= 12.0) {
  170 + j0 = 1.0;
  171 + r = 1.0;
  172 + for (k=1;k<=30;k++) {
  173 + r *= -0.25*x2/(k*k);
  174 + j0 += r;
  175 + if (fabs(r) < fabs(j0)*1e-15) break;
  176 + }
  177 + j1 = 1.0;
  178 + r = 1.0;
  179 + for (k=1;k<=30;k++) {
  180 + r *= -0.25*x2/(k*(k+1));
  181 + j1 += r;
  182 + if (fabs(r) < fabs(j1)*1e-15) break;
  183 + }
  184 + j1 *= 0.5*x;
  185 + ec = log(0.5*x)+el;
  186 + cs0 = 0.0;
  187 + w0 = 0.0;
  188 + r0 = 1.0;
  189 + for (k=1;k<=30;k++) {
  190 + w0 += 1.0/k;
  191 + r0 *= -0.25*x2/(k*k);
  192 + r = r0 * w0;
  193 + cs0 += r;
  194 + if (fabs(r) < fabs(cs0)*1e-15) break;
  195 + }
  196 + y0 = M_2_PI*(ec*j0-cs0);
  197 + cs1 = 1.0;
  198 + w1 = 0.0;
  199 + r1 = 1.0;
  200 + for (k=1;k<=30;k++) {
  201 + w1 += 1.0/k;
  202 + r1 *= -0.25*x2/(k*(k+1));
  203 + r = r1*(2.0*w1+1.0/(k+1));
  204 + cs1 += r;
  205 + if (fabs(r) < fabs(cs1)*1e-15) break;
  206 + }
  207 + y1 = M_2_PI * (ec*j1-1.0/x-0.25*x*cs1);
  208 + }
  209 + else {
  210 + if (x >= 50.0) kz = 8; // Can be changed to 10
  211 + else if (x >= 35.0) kz = 10; // " " 12
  212 + else kz = 12; // " " 14
  213 + t1 = x-M_PI_4;
  214 + p0 = 1.0;
  215 + q0 = -0.125/x;
  216 + for (k=0;k<kz;k++) {
  217 + p0 += a[k]*pow(x,-2*k-2);
  218 + q0 += b[k]*pow(x,-2*k-3);
  219 + }
  220 + cu = sqrt(M_2_PI/x);
  221 + j0 = cu*(p0*cos(t1)-q0*sin(t1));
  222 + y0 = cu*(p0*sin(t1)+q0*cos(t1));
  223 + t2 = x-0.75*M_PI;
  224 + p1 = 1.0;
  225 + q1 = 0.375/x;
  226 + for (k=0;k<kz;k++) {
  227 + p1 += a1[k]*pow(x,-2*k-2);
  228 + q1 += b1[k]*pow(x,-2*k-3);
  229 + }
  230 + j1 = cu*(p1*cos(t2)-q1*sin(t2));
  231 + y1 = cu*(p1*sin(t2)+q1*cos(t2));
  232 + }
  233 + j0p = -j1;
  234 + j1p = j0-j1/x;
  235 + y0p = -y1;
  236 + y1p = y0-y1/x;
  237 + return 0;
  238 +}
  239 +//
  240 +// INPUT:
  241 +// double x -- argument of Bessel function
  242 +//
  243 +// OUTPUT:
  244 +// double j0 -- Bessel function of 1st kind, 0th order
  245 +// double j1 -- Bessel function of 1st kind, 1st order
  246 +// double y0 -- Bessel function of 2nd kind, 0th order
  247 +// double y1 -- Bessel function of 2nd kind, 1st order
  248 +// double j0p -- derivative of Bessel function of 1st kind, 0th order
  249 +// double j1p -- derivative of Bessel function of 1st kind, 1st order
  250 +// double y0p -- derivative of Bessel function of 2nd kind, 0th order
  251 +// double y1p -- derivative of Bessel function of 2nd kind, 1st order
  252 +//
  253 +// RETURN:
  254 +// int error code: 0 = OK, 1 = error
  255 +//
  256 +// This algorithm computes the functions using polynomial approximations.
  257 +//
  258 +template<typename P>
  259 +int bessjy01b(P x,P &j0,P &j1,P &y0,P &y1,
  260 + P &j0p,P &j1p,P &y0p,P &y1p)
  261 +{
  262 + P t,t2,dtmp,a0,p0,q0,p1,q1,ta0,ta1;
  263 + if (x < 0.0) return 1;
  264 + if (x == 0.0) {
  265 + j0 = 1.0;
  266 + j1 = 0.0;
  267 + y0 = -1e308;
  268 + y1 = -1e308;
  269 + j0p = 0.0;
  270 + j1p = 0.5;
  271 + y0p = 1e308;
  272 + y1p = 1e308;
  273 + return 0;
  274 + }
  275 + if(x <= 4.0) {
  276 + t = x/4.0;
  277 + t2 = t*t;
  278 + j0 = ((((((-0.5014415e-3*t2+0.76771853e-2)*t2-0.0709253492)*t2+
  279 + 0.4443584263)*t2-1.7777560599)*t2+3.9999973021)*t2
  280 + -3.9999998721)*t2+1.0;
  281 + j1 = t*(((((((-0.1289769e-3*t2+0.22069155e-2)*t2-0.0236616773)*t2+
  282 + 0.1777582922)*t2-0.8888839649)*t2+2.6666660544)*t2-
  283 + 3.999999971)*t2+1.9999999998);
  284 + dtmp = (((((((-0.567433e-4*t2+0.859977e-3)*t2-0.94855882e-2)*t2+
  285 + 0.0772975809)*t2-0.4261737419)*t2+1.4216421221)*t2-
  286 + 2.3498519931)*t2+1.0766115157)*t2+0.3674669052;
  287 + y0 = M_2_PI*log(0.5*x)*j0+dtmp;
  288 + dtmp = (((((((0.6535773e-3*t2-0.0108175626)*t2+0.107657607)*t2-
  289 + 0.7268945577)*t2+3.1261399273)*t2-7.3980241381)*t2+
  290 + 6.8529236342)*t2+0.3932562018)*t2-0.6366197726;
  291 + y1 = M_2_PI*log(0.5*x)*j1+dtmp/x;
  292 + }
  293 + else {
  294 + t = 4.0/x;
  295 + t2 = t*t;
  296 + a0 = sqrt(M_2_PI/x);
  297 + p0 = ((((-0.9285e-5*t2+0.43506e-4)*t2-0.122226e-3)*t2+
  298 + 0.434725e-3)*t2-0.4394275e-2)*t2+0.999999997;
  299 + q0 = t*(((((0.8099e-5*t2-0.35614e-4)*t2+0.85844e-4)*t2-
  300 + 0.218024e-3)*t2+0.1144106e-2)*t2-0.031249995);
  301 + ta0 = x-M_PI_4;
  302 + j0 = a0*(p0*cos(ta0)-q0*sin(ta0));
  303 + y0 = a0*(p0*sin(ta0)+q0*cos(ta0));
  304 + p1 = ((((0.10632e-4*t2-0.50363e-4)*t2+0.145575e-3)*t2
  305 + -0.559487e-3)*t2+0.7323931e-2)*t2+1.000000004;
  306 + q1 = t*(((((-0.9173e-5*t2+0.40658e-4)*t2-0.99941e-4)*t2
  307 + +0.266891e-3)*t2-0.1601836e-2)*t2+0.093749994);
  308 + ta1 = x-0.75*M_PI;
  309 + j1 = a0*(p1*cos(ta1)-q1*sin(ta1));
  310 + y1 = a0*(p1*sin(ta1)+q1*cos(ta1));
  311 + }
  312 + j0p = -j1;
  313 + j1p = j0-j1/x;
  314 + y0p = -y1;
  315 + y1p = y0-y1/x;
  316 + return 0;
  317 +}
  318 +template<typename P>
  319 +int msta1(P x,int mp)
  320 +{
  321 + P a0,f0,f1,f;
  322 + int i,n0,n1,nn;
  323 +
  324 + a0 = fabs(x);
  325 + n0 = (int)(1.1*a0)+1;
  326 + f0 = 0.5*log10(6.28*n0)-n0*log10(1.36*a0/n0)-mp;
  327 + n1 = n0+5;
  328 + f1 = 0.5*log10(6.28*n1)-n1*log10(1.36*a0/n1)-mp;
  329 + for (i=0;i<20;i++) {
  330 + nn = (int)(n1-(n1-n0)/(1.0-f0/f1));
  331 + f = 0.5*log10(6.28*nn)-nn*log10(1.36*a0/nn)-mp;
  332 + if (std::abs(nn-n1) < 1) break;
  333 + n0 = n1;
  334 + f0 = f1;
  335 + n1 = nn;
  336 + f1 = f;
  337 + }
  338 + return nn;
  339 +}
  340 +template<typename P>
  341 +int msta2(P x,int n,int mp)
  342 +{
  343 + P a0,ejn,hmp,f0,f1,f,obj;
  344 + int i,n0,n1,nn;
  345 +
  346 + a0 = fabs(x);
  347 + hmp = 0.5*mp;
  348 + ejn = 0.5*log10(6.28*n)-n*log10(1.36*a0/n);
  349 + if (ejn <= hmp) {
  350 + obj = mp;
  351 + n0 = (int)(1.1*a0);
  352 + if (n0 < 1) n0 = 1;
  353 + }
  354 + else {
  355 + obj = hmp+ejn;
  356 + n0 = n;
  357 + }
  358 + f0 = 0.5*log10(6.28*n0)-n0*log10(1.36*a0/n0)-obj;
  359 + n1 = n0+5;
  360 + f1 = 0.5*log10(6.28*n1)-n1*log10(1.36*a0/n1)-obj;
  361 + for (i=0;i<20;i++) {
  362 + nn = (int)(n1-(n1-n0)/(1.0-f0/f1));
  363 + f = 0.5*log10(6.28*nn)-nn*log10(1.36*a0/nn)-obj;
  364 + if (std::abs(nn-n1) < 1) break;
  365 + n0 = n1;
  366 + f0 = f1;
  367 + n1 = nn;
  368 + f1 = f;
  369 + }
  370 + return nn+10;
  371 +}
  372 +//
  373 +// INPUT:
  374 +// double x -- argument of Bessel function of 1st and 2nd kind.
  375 +// int n -- order
  376 +//
  377 +// OUPUT:
  378 +//
  379 +// int nm -- highest order actually computed (nm <= n)
  380 +// double jn[] -- Bessel function of 1st kind, orders from 0 to nm
  381 +// double yn[] -- Bessel function of 2nd kind, orders from 0 to nm
  382 +// double j'n[]-- derivative of Bessel function of 1st kind,
  383 +// orders from 0 to nm
  384 +// double y'n[]-- derivative of Bessel function of 2nd kind,
  385 +// orders from 0 to nm
  386 +//
  387 +// Computes Bessel functions of all order up to 'n' using recurrence
  388 +// relations. If 'nm' < 'n' only 'nm' orders are returned.
  389 +//
  390 +template<typename P>
  391 +int bessjyna(int n,P x,int &nm,P *jn,P *yn,
  392 + P *jnp,P *ynp)
  393 +{
  394 + P bj0,bj1,f,f0,f1,f2,cs;
  395 + int i,k,m,ecode;
  396 +
  397 + nm = n;
  398 + if ((x < 0.0) || (n < 0)) return 1;
  399 + if (x < 1e-15) {
  400 + for (i=0;i<=n;i++) {
  401 + jn[i] = 0.0;
  402 + yn[i] = -1e308;
  403 + jnp[i] = 0.0;
  404 + ynp[i] = 1e308;
  405 + }
  406 + jn[0] = 1.0;
  407 + jnp[1] = 0.5;
  408 + return 0;
  409 + }
  410 + ecode = bessjy01a(x,jn[0],jn[1],yn[0],yn[1],jnp[0],jnp[1],ynp[0],ynp[1]);
  411 + if (n < 2) return 0;
  412 + bj0 = jn[0];
  413 + bj1 = jn[1];
  414 + if (n < (int)0.9*x) {
  415 + for (k=2;k<=n;k++) {
  416 + jn[k] = 2.0*(k-1.0)*bj1/x-bj0;
  417 + bj0 = bj1;
  418 + bj1 = jn[k];
  419 + }
  420 + }
  421 + else {
  422 + m = msta1(x,200);
  423 + if (m < n) nm = m;
  424 + else m = msta2(x,n,15);
  425 + f2 = 0.0;
  426 + f1 = 1.0e-100;
  427 + for (k=m;k>=0;k--) {
  428 + f = 2.0*(k+1.0)/x*f1-f2;
  429 + if (k <= nm) jn[k] = f;
  430 + f2 = f1;
  431 + f1 = f;
  432 + }
  433 + if (fabs(bj0) > fabs(bj1)) cs = bj0/f;
  434 + else cs = bj1/f2;
  435 + for (k=0;k<=nm;k++) {
  436 + jn[k] *= cs;
  437 + }
  438 + }
  439 + for (k=2;k<=nm;k++) {
  440 + jnp[k] = jn[k-1]-k*jn[k]/x;
  441 + }
  442 + f0 = yn[0];
  443 + f1 = yn[1];
  444 + for (k=2;k<=nm;k++) {
  445 + f = 2.0*(k-1.0)*f1/x-f0;
  446 + yn[k] = f;
  447 + f0 = f1;
  448 + f1 = f;
  449 + }
  450 + for (k=2;k<=nm;k++) {
  451 + ynp[k] = yn[k-1]-k*yn[k]/x;
  452 + }
  453 + return 0;
  454 +}
  455 +//
  456 +// Same input and output conventions as above. Different recurrence
  457 +// relations used for 'x' < 300.
  458 +//
  459 +template<typename P>
  460 +int bessjynb(int n,P x,int &nm,P *jn,P *yn,
  461 + P *jnp,P *ynp)
  462 +{
  463 + P t1,t2,f,f1,f2,bj0,bj1,bjk,by0,by1,cu,s0,su,sv;
  464 + P ec,bs,byk,p0,p1,q0,q1;
  465 + static P a[] = {
  466 + -0.7031250000000000e-1,
  467 + 0.1121520996093750,
  468 + -0.5725014209747314,
  469 + 6.074042001273483};
  470 + static P b[] = {
  471 + 0.7324218750000000e-1,
  472 + -0.2271080017089844,
  473 + 1.727727502584457,
  474 + -2.438052969955606e1};
  475 + static P a1[] = {
  476 + 0.1171875,
  477 + -0.1441955566406250,
  478 + 0.6765925884246826,
  479 + -6.883914268109947};
  480 + static P b1[] = {
  481 + -0.1025390625,
  482 + 0.2775764465332031,
  483 + -1.993531733751297,
  484 + 2.724882731126854e1};
  485 +
  486 + int i,k,m;
  487 + nm = n;
  488 + if ((x < 0.0) || (n < 0)) return 1;
  489 + if (x < 1e-15) {
  490 + for (i=0;i<=n;i++) {
  491 + jn[i] = 0.0;
  492 + yn[i] = -1e308;
  493 + jnp[i] = 0.0;
  494 + ynp[i] = 1e308;
  495 + }
  496 + jn[0] = 1.0;
  497 + jnp[1] = 0.5;
  498 + return 0;
  499 + }
  500 + if (x <= 300.0 || n > (int)(0.9*x)) {
  501 + if (n == 0) nm = 1;
  502 + m = msta1(x,200);
  503 + if (m < nm) nm = m;
  504 + else m = msta2(x,nm,15);
  505 + bs = 0.0;
  506 + su = 0.0;
  507 + sv = 0.0;
  508 + f2 = 0.0;
  509 + f1 = 1.0e-100;
  510 + for (k = m;k>=0;k--) {
  511 + f = 2.0*(k+1.0)/x*f1 - f2;
  512 + if (k <= nm) jn[k] = f;
  513 + if ((k == 2*(int)(k/2)) && (k != 0)) {
  514 + bs += 2.0*f;
  515 +// su += pow(-1,k>>1)*f/(double)k;
  516 + su += (-1)*((k & 2)-1)*f/(P)k;
  517 + }
  518 + else if (k > 1) {
  519 +// sv += pow(-1,k>>1)*k*f/(k*k-1.0);
  520 + sv += (-1)*((k & 2)-1)*(P)k*f/(k*k-1.0);
  521 + }
  522 + f2 = f1;
  523 + f1 = f;
  524 + }
  525 + s0 = bs+f;
  526 + for (k=0;k<=nm;k++) {
  527 + jn[k] /= s0;
  528 + }
  529 + ec = log(0.5*x) +0.5772156649015329;
  530 + by0 = M_2_PI*(ec*jn[0]-4.0*su/s0);
  531 + yn[0] = by0;
  532 + by1 = M_2_PI*((ec-1.0)*jn[1]-jn[0]/x-4.0*sv/s0);
  533 + yn[1] = by1;
  534 + }
  535 + else {
  536 + t1 = x-M_PI_4;
  537 + p0 = 1.0;
  538 + q0 = -0.125/x;
  539 + for (k=0;k<4;k++) {
  540 + p0 += a[k]*pow(x,-2*k-2);
  541 + q0 += b[k]*pow(x,-2*k-3);
  542 + }
  543 + cu = sqrt(M_2_PI/x);
  544 + bj0 = cu*(p0*cos(t1)-q0*sin(t1));
  545 + by0 = cu*(p0*sin(t1)+q0*cos(t1));
  546 + jn[0] = bj0;
  547 + yn[0] = by0;
  548 + t2 = x-0.75*M_PI;
  549 + p1 = 1.0;
  550 + q1 = 0.375/x;
  551 + for (k=0;k<4;k++) {
  552 + p1 += a1[k]*pow(x,-2*k-2);
  553 + q1 += b1[k]*pow(x,-2*k-3);
  554 + }
  555 + bj1 = cu*(p1*cos(t2)-q1*sin(t2));
  556 + by1 = cu*(p1*sin(t2)+q1*cos(t2));
  557 + jn[1] = bj1;
  558 + yn[1] = by1;
  559 + for (k=2;k<=nm;k++) {
  560 + bjk = 2.0*(k-1.0)*bj1/x-bj0;
  561 + jn[k] = bjk;
  562 + bj0 = bj1;
  563 + bj1 = bjk;
  564 + }
  565 + }
  566 + jnp[0] = -jn[1];
  567 + for (k=1;k<=nm;k++) {
  568 + jnp[k] = jn[k-1]-k*jn[k]/x;
  569 + }
  570 + for (k=2;k<=nm;k++) {
  571 + byk = 2.0*(k-1.0)*by1/x-by0;
  572 + yn[k] = byk;
  573 + by0 = by1;
  574 + by1 = byk;
  575 + }
  576 + ynp[0] = -yn[1];
  577 + for (k=1;k<=nm;k++) {
  578 + ynp[k] = yn[k-1]-k*yn[k]/x;
  579 + }
  580 + return 0;
  581 +
  582 +}
  583 +
  584 +// The following routine computes Bessel Jv(x) and Yv(x) for
  585 +// arbitrary positive order (v). For negative order, use:
  586 +//
  587 +// J-v(x) = Jv(x)cos(v pi) - Yv(x)sin(v pi)
  588 +// Y-v(x) = Jv(x)sin(v pi) + Yv(x)cos(v pi)
  589 +//
  590 +template<typename P>
  591 +int bessjyv(P v,P x,P &vm,P *jv,P *yv,
  592 + P *djv,P *dyv)
  593 +{
  594 + P v0,vl,vg,vv,a,a0,r,x2,bjv0,bjv1,bjvl,f,f0,f1,f2;
  595 + P r0,r1,ck,cs,cs0,cs1,sk,qx,px,byv0,byv1,rp,xk,rq;
  596 + P b,ec,w0,w1,bju0,bju1,pv0,pv1,byvk;
  597 + int j,k,l,m,n,kz;
  598 +
  599 + x2 = x*x;
  600 + n = (int)v;
  601 + v0 = v-n;
  602 + if ((x < 0.0) || (v < 0.0)) return 1;
  603 + if (x < 1e-15) {
  604 + for (k=0;k<=n;k++) {
  605 + jv[k] = 0.0;
  606 + yv[k] = -1e308;
  607 + djv[k] = 0.0;
  608 + dyv[k] = 1e308;
  609 + if (v0 == 0.0) {
  610 + jv[0] = 1.0;
  611 + djv[1] = 0.5;
  612 + }
  613 + else djv[0] = 1e308;
  614 + }
  615 + vm = v;
  616 + return 0;
  617 + }
  618 + if (x <= 12.0) {
  619 + for (l=0;l<2;l++) {
  620 + vl = v0 + l;
  621 + bjvl = 1.0;
  622 + r = 1.0;
  623 + for (k=1;k<=40;k++) {
  624 + r *= -0.25*x2/(k*(k+vl));
  625 + bjvl += r;
  626 + if (fabs(r) < fabs(bjvl)*1e-15) break;
  627 + }
  628 + vg = 1.0 + vl;
  629 + a = pow(0.5*x,vl)/gamma(vg);
  630 + if (l == 0) bjv0 = bjvl*a;
  631 + else bjv1 = bjvl*a;
  632 + }
  633 + }
  634 + else {
  635 + if (x >= 50.0) kz = 8;
  636 + else if (x >= 35.0) kz = 10;
  637 + else kz = 11;
  638 + for (j=0;j<2;j++) {
  639 + vv = 4.0*(j+v0)*(j+v0);
  640 + px = 1.0;
  641 + rp = 1.0;
  642 + for (k=1;k<=kz;k++) {
  643 + rp *= (-0.78125e-2)*(vv-pow(4.0*k-3.0,2.0))*
  644 + (vv-pow(4.0*k-1.0,2.0))/(k*(2.0*k-1.0)*x2);
  645 + px += rp;
  646 + }
  647 + qx = 1.0;
  648 + rq = 1.0;
  649 + for (k=1;k<=kz;k++) {
  650 + rq *= (-0.78125e-2)*(vv-pow(4.0*k-1.0,2.0))*
  651 + (vv-pow(4.0*k+1.0,2.0))/(k*(2.0*k+1.0)*x2);
  652 + qx += rq;
  653 + }
  654 + qx *= 0.125*(vv-1.0)/x;
  655 + xk = x-(0.5*(j+v0)+0.25)*M_PI;
  656 + a0 = sqrt(M_2_PI/x);
  657 + ck = cos(xk);
  658 + sk = sin(xk);
  659 +
  660 + if (j == 0) {
  661 + bjv0 = a0*(px*ck-qx*sk);
  662 + byv0 = a0*(px*sk+qx*ck);
  663 + }
  664 + else if (j == 1) {
  665 + bjv1 = a0*(px*ck-qx*sk);
  666 + byv1 = a0*(px*sk+qx*ck);
  667 + }
  668 + }
  669 + }
  670 + jv[0] = bjv0;
  671 + jv[1] = bjv1;
  672 + djv[0] = v0*jv[0]/x-jv[1];
  673 + djv[1] = -(1.0+v0)*jv[1]/x+jv[0];
  674 + if ((n >= 2) && (n <= (int)(0.9*x))) {
  675 + f0 = bjv0;
  676 + f1 = bjv1;
  677 + for (k=2;k<=n;k++) {
  678 + f = 2.0*(k+v0-1.0)*f1/x-f0;
  679 + jv[k] = f;
  680 + f0 = f1;
  681 + f1 = f;
  682 + }
  683 + }
  684 + else if (n >= 2) {
  685 + m = msta1(x,200);
  686 + if (m < n) n = m;
  687 + else m = msta2(x,n,15);
  688 + f2 = 0.0;
  689 + f1 = 1.0e-100;
  690 + for (k=m;k>=0;k--) {
  691 + f = 2.0*(v0+k+1.0)*f1/x-f2;
  692 + if (k <= n) jv[k] = f;
  693 + f2 = f1;
  694 + f1 = f;
  695 + }
  696 + if (fabs(bjv0) > fabs(bjv1)) cs = bjv0/f;
  697 + else cs = bjv1/f2;
  698 + for (k=0;k<=n;k++) {
  699 + jv[k] *= cs;
  700 + }
  701 + }
  702 + for (k=2;k<=n;k++) {
  703 + djv[k] = -(k+v0)*jv[k]/x+jv[k-1];
  704 + }
  705 + if (x <= 12.0) {
  706 + if (v0 != 0.0) {
  707 + for (l=0;l<2;l++) {
  708 + vl = v0 +l;
  709 + bjvl = 1.0;
  710 + r = 1.0;
  711 + for (k=1;k<=40;k++) {
  712 + r *= -0.25*x2/(k*(k-vl));
  713 + bjvl += r;
  714 + if (fabs(r) < fabs(bjvl)*1e-15) break;
  715 + }
  716 + vg = 1.0-vl;
  717 + b = pow(2.0/x,vl)/gamma(vg);
  718 + if (l == 0) bju0 = bjvl*b;
  719 + else bju1 = bjvl*b;
  720 + }
  721 + pv0 = M_PI*v0;
  722 + pv1 = M_PI*(1.0+v0);
  723 + byv0 = (bjv0*cos(pv0)-bju0)/sin(pv0);
  724 + byv1 = (bjv1*cos(pv1)-bju1)/sin(pv1);
  725 + }
  726 + else {
  727 + ec = log(0.5*x)+el;
  728 + cs0 = 0.0;
  729 + w0 = 0.0;
  730 + r0 = 1.0;
  731 + for (k=1;k<=30;k++) {
  732 + w0 += 1.0/k;
  733 + r0 *= -0.25*x2/(k*k);
  734 + cs0 += r0*w0;
  735 + }
  736 + byv0 = M_2_PI*(ec*bjv0-cs0);
  737 + cs1 = 1.0;
  738 + w1 = 0.0;
  739 + r1 = 1.0;
  740 + for (k=1;k<=30;k++) {
  741 + w1 += 1.0/k;
  742 + r1 *= -0.25*x2/(k*(k+1));
  743 + cs1 += r1*(2.0*w1+1.0/(k+1.0));
  744 + }
  745 + byv1 = M_2_PI*(ec*bjv1-1.0/x-0.25*x*cs1);
  746 + }
  747 + }
  748 + yv[0] = byv0;
  749 + yv[1] = byv1;
  750 + for (k=2;k<=n;k++) {
  751 + byvk = 2.0*(v0+k-1.0)*byv1/x-byv0;
  752 + yv[k] = byvk;
  753 + byv0 = byv1;
  754 + byv1 = byvk;
  755 + }
  756 + dyv[0] = v0*yv[0]/x-yv[1];
  757 + for (k=1;k<=n;k++) {
  758 + dyv[k] = -(k+v0)*yv[k]/x+yv[k-1];
  759 + }
  760 + vm = n + v0;
  761 + return 0;
  762 +}
  763 +
  764 +template<typename P>
  765 +int bessjyv_sph(int v, P z, P &vm, P* cjv,
  766 + P* cyv, P* cjvp, P* cyvp)
  767 +{
  768 + //first, compute the bessel functions of fractional order
  769 + bessjyv<P>(v + 0.5, z, vm, cjv, cyv, cjvp, cyvp);
  770 +
  771 + //iterate through each and scale
  772 + for(int n = 0; n<=v; n++)
  773 + {
  774 +
  775 + cjv[n] = cjv[n] * sqrt(stim::PI/(z * 2.0));
  776 + cyv[n] = cyv[n] * sqrt(stim::PI/(z * 2.0));
  777 +
  778 + cjvp[n] = -1.0 / (z * 2.0) * cjv[n] + cjvp[n] * sqrt(stim::PI / (z * 2.0));
  779 + cyvp[n] = -1.0 / (z * 2.0) * cyv[n] + cyvp[n] * sqrt(stim::PI / (z * 2.0));
  780 + }
  781 +
  782 + return 0;
  783 +
  784 +}
  785 +
  786 +template<typename P>
  787 +int cbessjy01(complex<P> z,complex<P> &cj0,complex<P> &cj1,
  788 + complex<P> &cy0,complex<P> &cy1,complex<P> &cj0p,
  789 + complex<P> &cj1p,complex<P> &cy0p,complex<P> &cy1p)
  790 +{
  791 + complex<P> z1,z2,cr,cp,cs,cp0,cq0,cp1,cq1,ct1,ct2,cu;
  792 + P a0,w0,w1;
  793 + int k,kz;
  794 +
  795 + static P a[] = {
  796 + -7.03125e-2,
  797 + 0.112152099609375,
  798 + -0.5725014209747314,
  799 + 6.074042001273483,
  800 + -1.100171402692467e2,
  801 + 3.038090510922384e3,
  802 + -1.188384262567832e5,
  803 + 6.252951493434797e6,
  804 + -4.259392165047669e8,
  805 + 3.646840080706556e10,
  806 + -3.833534661393944e12,
  807 + 4.854014686852901e14,
  808 + -7.286857349377656e16,
  809 + 1.279721941975975e19};
  810 + static P b[] = {
  811 + 7.32421875e-2,
  812 + -0.2271080017089844,
  813 + 1.727727502584457,
  814 + -2.438052969955606e1,
  815 + 5.513358961220206e2,
  816 + -1.825775547429318e4,
  817 + 8.328593040162893e5,
  818 + -5.006958953198893e7,
  819 + 3.836255180230433e9,
  820 + -3.649010818849833e11,
  821 + 4.218971570284096e13,
  822 + -5.827244631566907e15,
  823 + 9.476288099260110e17,
  824 + -1.792162323051699e20};
  825 + static P a1[] = {
  826 + 0.1171875,
  827 + -0.1441955566406250,
  828 + 0.6765925884246826,
  829 + -6.883914268109947,
  830 + 1.215978918765359e2,
  831 + -3.302272294480852e3,
  832 + 1.276412726461746e5,
  833 + -6.656367718817688e6,
  834 + 4.502786003050393e8,
  835 + -3.833857520742790e10,
  836 + 4.011838599133198e12,
  837 + -5.060568503314727e14,
  838 + 7.572616461117958e16,
  839 + -1.326257285320556e19};
  840 + static P b1[] = {
  841 + -0.1025390625,
  842 + 0.2775764465332031,
  843 + -1.993531733751297,
  844 + 2.724882731126854e1,
  845 + -6.038440767050702e2,
  846 + 1.971837591223663e4,
  847 + -8.902978767070678e5,
  848 + 5.310411010968522e7,
  849 + -4.043620325107754e9,
  850 + 3.827011346598605e11,
  851 + -4.406481417852278e13,
  852 + 6.065091351222699e15,
  853 + -9.833883876590679e17,
  854 + 1.855045211579828e20};
  855 +
  856 + a0 = abs(z);
  857 + z2 = z*z;
  858 + z1 = z;
  859 + if (a0 == 0.0) {
  860 + cj0 = cone;
  861 + cj1 = czero;
  862 + cy0 = complex<P>(-1e308,0);
  863 + cy1 = complex<P>(-1e308,0);
  864 + cj0p = czero;
  865 + cj1p = complex<P>(0.5,0.0);
  866 + cy0p = complex<P>(1e308,0);
  867 + cy1p = complex<P>(1e308,0);
  868 + return 0;
  869 + }
  870 + if (real(z) < 0.0) z1 = -z;
  871 + if (a0 <= 12.0) {
  872 + cj0 = cone;
  873 + cr = cone;
  874 + for (k=1;k<=40;k++) {
  875 + cr *= -0.25*z2/(P)(k*k);
  876 + cj0 += cr;
  877 + if (abs(cr) < abs(cj0)*eps) break;
  878 + }
  879 + cj1 = cone;
  880 + cr = cone;
  881 + for (k=1;k<=40;k++) {
  882 + cr *= -0.25*z2/(k*(k+1.0));
  883 + cj1 += cr;
  884 + if (abs(cr) < abs(cj1)*eps) break;
  885 + }
  886 + cj1 *= 0.5*z1;
  887 + w0 = 0.0;
  888 + cr = cone;
  889 + cs = czero;
  890 + for (k=1;k<=40;k++) {
  891 + w0 += 1.0/k;
  892 + cr *= -0.25*z2/(P)(k*k);
  893 + cp = cr*w0;
  894 + cs += cp;
  895 + if (abs(cp) < abs(cs)*eps) break;
  896 + }
  897 + cy0 = M_2_PI*((log(0.5*z1)+el)*cj0-cs);
  898 + w1 = 0.0;
  899 + cr = cone;
  900 + cs = cone;
  901 + for (k=1;k<=40;k++) {
  902 + w1 += 1.0/k;
  903 + cr *= -0.25*z2/(k*(k+1.0));
  904 + cp = cr*(2.0*w1+1.0/(k+1.0));
  905 + cs += cp;
  906 + if (abs(cp) < abs(cs)*eps) break;
  907 + }
  908 + cy1 = M_2_PI*((log(0.5*z1)+el)*cj1-1.0/z1-0.25*z1*cs);
  909 + }
  910 + else {
  911 + if (a0 >= 50.0) kz = 8; // can be changed to 10
  912 + else if (a0 >= 35.0) kz = 10; // " " " 12
  913 + else kz = 12; // " " " 14
  914 + ct1 = z1 - M_PI_4;
  915 + cp0 = cone;
  916 + for (k=0;k<kz;k++) {
  917 + cp0 += a[k]*pow(z1,-2.0*k-2.0);
  918 + }
  919 + cq0 = -0.125/z1;
  920 + for (k=0;k<kz;k++) {
  921 + cq0 += b[k]*pow(z1,-2.0*k-3.0);
  922 + }
  923 + cu = sqrt(M_2_PI/z1);
  924 + cj0 = cu*(cp0*cos(ct1)-cq0*sin(ct1));
  925 + cy0 = cu*(cp0*sin(ct1)+cq0*cos(ct1));
  926 + ct2 = z1 - 0.75*M_PI;
  927 + cp1 = cone;
  928 + for (k=0;k<kz;k++) {
  929 + cp1 += a1[k]*pow(z1,-2.0*k-2.0);
  930 + }
  931 + cq1 = 0.375/z1;
  932 + for (k=0;k<kz;k++) {
  933 + cq1 += b1[k]*pow(z1,-2.0*k-3.0);
  934 + }
  935 + cj1 = cu*(cp1*cos(ct2)-cq1*sin(ct2));
  936 + cy1 = cu*(cp1*sin(ct2)+cq1*cos(ct2));
  937 + }
  938 + if (real(z) < 0.0) {
  939 + if (imag(z) < 0.0) {
  940 + cy0 -= 2.0*cii*cj0;
  941 + cy1 = -(cy1-2.0*cii*cj1);
  942 + }
  943 + else if (imag(z) > 0.0) {
  944 + cy0 += 2.0*cii*cj0;
  945 + cy1 = -(cy1+2.0*cii*cj1);
  946 + }
  947 + cj1 = -cj1;
  948 + }
  949 + cj0p = -cj1;
  950 + cj1p = cj0-cj1/z;
  951 + cy0p = -cy1;
  952 + cy1p = cy0-cy1/z;
  953 + return 0;
  954 +}
  955 +
  956 +template<typename P>
  957 +int cbessjyna(int n,complex<P> z,int &nm,complex<P> *cj,
  958 + complex<P> *cy,complex<P> *cjp,complex<P> *cyp)
  959 +{
  960 + complex<P> cbj0,cbj1,cby0,cby1,cj0,cjk,cj1,cf,cf1,cf2;
  961 + complex<P> cs,cg0,cg1,cyk,cyl1,cyl2,cylk,cp11,cp12,cp21,cp22;
  962 + complex<P> ch0,ch1,ch2;
  963 + P a0,yak,ya1,ya0,wa;
  964 + int m,k,lb,lb0;
  965 +
  966 + if (n < 0) return 1;
  967 + a0 = abs(z);
  968 + nm = n;
  969 + if (a0 < 1.0e-100) {
  970 + for (k=0;k<=n;k++) {
  971 + cj[k] = czero;
  972 + cy[k] = complex<P> (-1e308,0);
  973 + cjp[k] = czero;
  974 + cyp[k] = complex<P>(1e308,0);
  975 + }
  976 + cj[0] = cone;
  977 + cjp[1] = complex<P>(0.5,0.0);
  978 + return 0;
  979 + }
  980 + cbessjy01(z,cj[0],cj[1],cy[0],cy[1],cjp[0],cjp[1],cyp[0],cyp[1]);
  981 + cbj0 = cj[0];
  982 + cbj1 = cj[1];
  983 + cby0 = cy[0];
  984 + cby1 = cy[1];
  985 + if (n <= 1) return 0;
  986 + if (n < (int)0.25*a0) {
  987 + cj0 = cbj0;
  988 + cj1 = cbj1;
  989 + for (k=2;k<=n;k++) {
  990 + cjk = 2.0*(k-1.0)*cj1/z-cj0;
  991 + cj[k] = cjk;
  992 + cj0 = cj1;
  993 + cj1 = cjk;
  994 + }
  995 + }
  996 + else {
  997 + m = msta1(a0,200);
  998 + if (m < n) nm = m;
  999 + else m = msta2(a0,n,15);
  1000 + cf2 = czero;
  1001 + cf1 = complex<P> (1.0e-100,0.0);
  1002 + for (k=m;k>=0;k--) {
  1003 + cf = 2.0*(k+1.0)*cf1/z-cf2;
  1004 + if (k <=nm) cj[k] = cf;
  1005 + cf2 = cf1;
  1006 + cf1 = cf;
  1007 + }
  1008 + if (abs(cbj0) > abs(cbj1)) cs = cbj0/cf;
  1009 + else cs = cbj1/cf2;
  1010 + for (k=0;k<=nm;k++) {
  1011 + cj[k] *= cs;
  1012 + }
  1013 + }
  1014 + for (k=2;k<=nm;k++) {
  1015 + cjp[k] = cj[k-1]-(P)k*cj[k]/z;
  1016 + }
  1017 + ya0 = abs(cby0);
  1018 + lb = 0;
  1019 + cg0 = cby0;
  1020 + cg1 = cby1;
  1021 + for (k=2;k<=nm;k++) {
  1022 + cyk = 2.0*(k-1.0)*cg1/z-cg0;
  1023 + yak = abs(cyk);
  1024 + ya1 = abs(cg0);
  1025 + if ((yak < ya0) && (yak < ya1)) lb = k;
  1026 + cy[k] = cyk;
  1027 + cg0 = cg1;
  1028 + cg1 = cyk;
  1029 + }
  1030 + lb0 = 0;
  1031 + if ((lb > 4) && (imag(z) != 0.0)) {
  1032 + while (lb != lb0) {
  1033 + ch2 = cone;
  1034 + ch1 = czero;
  1035 + lb0 = lb;
  1036 + for (k=lb;k>=1;k--) {
  1037 + ch0 = 2.0*k*ch1/z-ch2;
  1038 + ch2 = ch1;
  1039 + ch1 = ch0;
  1040 + }
  1041 + cp12 = ch0;
  1042 + cp22 = ch2;
  1043 + ch2 = czero;
  1044 + ch1 = cone;
  1045 + for (k=lb;k>=1;k--) {
  1046 + ch0 = 2.0*k*ch1/z-ch2;
  1047 + ch2 = ch1;
  1048 + ch1 = ch0;
  1049 + }
  1050 + cp11 = ch0;
  1051 + cp21 = ch2;
  1052 + if (lb == nm)
  1053 + cj[lb+1] = 2.0*lb*cj[lb]/z-cj[lb-1];
  1054 + if (abs(cj[0]) > abs(cj[1])) {
  1055 + cy[lb+1] = (cj[lb+1]*cby0-2.0*cp11/(M_PI*z))/cj[0];
  1056 + cy[lb] = (cj[lb]*cby0+2.0*cp12/(M_PI*z))/cj[0];
  1057 + }
  1058 + else {
  1059 + cy[lb+1] = (cj[lb+1]*cby1-2.0*cp21/(M_PI*z))/cj[1];
  1060 + cy[lb] = (cj[lb]*cby1+2.0*cp22/(M_PI*z))/cj[1];
  1061 + }
  1062 + cyl2 = cy[lb+1];
  1063 + cyl1 = cy[lb];
  1064 + for (k=lb-1;k>=0;k--) {
  1065 + cylk = 2.0*(k+1.0)*cyl1/z-cyl2;
  1066 + cy[k] = cylk;
  1067 + cyl2 = cyl1;
  1068 + cyl1 = cylk;
  1069 + }
  1070 + cyl1 = cy[lb];
  1071 + cyl2 = cy[lb+1];
  1072 + for (k=lb+1;k<n;k++) {
  1073 + cylk = 2.0*k*cyl2/z-cyl1;
  1074 + cy[k+1] = cylk;
  1075 + cyl1 = cyl2;
  1076 + cyl2 = cylk;
  1077 + }
  1078 + for (k=2;k<=nm;k++) {
  1079 + wa = abs(cy[k]);
  1080 + if (wa < abs(cy[k-1])) lb = k;
  1081 + }
  1082 + }
  1083 + }
  1084 + for (k=2;k<=nm;k++) {
  1085 + cyp[k] = cy[k-1]-(P)k*cy[k]/z;
  1086 + }
  1087 + return 0;
  1088 +}
  1089 +
  1090 +template<typename P>
  1091 +int cbessjynb(int n,complex<P> z,int &nm,complex<P> *cj,
  1092 + complex<P> *cy,complex<P> *cjp,complex<P> *cyp)
  1093 +{
  1094 + complex<P> cf,cf0,cf1,cf2,cbs,csu,csv,cs0,ce;
  1095 + complex<P> ct1,cp0,cq0,cp1,cq1,cu,cbj0,cby0,cbj1,cby1;
  1096 + complex<P> cyy,cbjk,ct2;
  1097 + P a0,y0;
  1098 + int k,m;
  1099 + static P a[] = {
  1100 + -0.7031250000000000e-1,
  1101 + 0.1121520996093750,
  1102 + -0.5725014209747314,
  1103 + 6.074042001273483};
  1104 + static P b[] = {
  1105 + 0.7324218750000000e-1,
  1106 + -0.2271080017089844,
  1107 + 1.727727502584457,
  1108 + -2.438052969955606e1};
  1109 + static P a1[] = {
  1110 + 0.1171875,
  1111 + -0.1441955566406250,
  1112 + 0.6765925884246826,
  1113 + -6.883914268109947};
  1114 + static P b1[] = {
  1115 + -0.1025390625,
  1116 + 0.2775764465332031,
  1117 + -1.993531733751297,
  1118 + 2.724882731126854e1};
  1119 +
  1120 + y0 = abs(imag(z));
  1121 + a0 = abs(z);
  1122 + nm = n;
  1123 + if (a0 < 1.0e-100) {
  1124 + for (k=0;k<=n;k++) {
  1125 + cj[k] = czero;
  1126 + cy[k] = complex<P> (-1e308,0);
  1127 + cjp[k] = czero;
  1128 + cyp[k] = complex<P>(1e308,0);
  1129 + }
  1130 + cj[0] = cone;
  1131 + cjp[1] = complex<P>(0.5,0.0);
  1132 + return 0;
  1133 + }
  1134 + if ((a0 <= 300.0) || (n > (int)(0.25*a0))) {
  1135 + if (n == 0) nm = 1;
  1136 + m = msta1(a0,200);
  1137 + if (m < nm) nm = m;
  1138 + else m = msta2(a0,nm,15);
  1139 + cbs = czero;
  1140 + csu = czero;
  1141 + csv = czero;
  1142 + cf2 = czero;
  1143 + cf1 = complex<P> (1.0e-100,0.0);
  1144 + for (k=m;k>=0;k--) {
  1145 + cf = 2.0*(k+1.0)*cf1/z-cf2;
  1146 + if (k <= nm) cj[k] = cf;
  1147 + if (((k & 1) == 0) && (k != 0)) {
  1148 + if (y0 <= 1.0) {
  1149 + cbs += 2.0*cf;
  1150 + }
  1151 + else {
  1152 + cbs += (-1)*((k & 2)-1)*2.0*cf;
  1153 + }
  1154 + csu += (P)((-1)*((k & 2)-1))*cf/(P)k;
  1155 + }
  1156 + else if (k > 1) {
  1157 + csv += (P)((-1)*((k & 2)-1)*k)*cf/(P)(k*k-1.0);
  1158 + }
  1159 + cf2 = cf1;
  1160 + cf1 = cf;
  1161 + }
  1162 + if (y0 <= 1.0) cs0 = cbs+cf;
  1163 + else cs0 = (cbs+cf)/cos(z);
  1164 + for (k=0;k<=nm;k++) {
  1165 + cj[k] /= cs0;
  1166 + }
  1167 + ce = log(0.5*z)+el;
  1168 + cy[0] = M_2_PI*(ce*cj[0]-4.0*csu/cs0);
  1169 + cy[1] = M_2_PI*(-cj[0]/z+(ce-1.0)*cj[1]-4.0*csv/cs0);
  1170 + }
  1171 + else {
  1172 + ct1 = z-M_PI_4;
  1173 + cp0 = cone;
  1174 + for (k=0;k<4;k++) {
  1175 + cp0 += a[k]*pow(z,-2.0*k-2.0);
  1176 + }
  1177 + cq0 = -0.125/z;
  1178 + for (k=0;k<4;k++) {
  1179 + cq0 += b[k] *pow(z,-2.0*k-3.0);
  1180 + }
  1181 + cu = sqrt(M_2_PI/z);
  1182 + cbj0 = cu*(cp0*cos(ct1)-cq0*sin(ct1));
  1183 + cby0 = cu*(cp0*sin(ct1)+cq0*cos(ct1));
  1184 + cj[0] = cbj0;
  1185 + cy[0] = cby0;
  1186 + ct2 = z-0.75*M_PI;
  1187 + cp1 = cone;
  1188 + for (k=0;k<4;k++) {
  1189 + cp1 += a1[k]*pow(z,-2.0*k-2.0);
  1190 + }
  1191 + cq1 = 0.375/z;
  1192 + for (k=0;k<4;k++) {
  1193 + cq1 += b1[k]*pow(z,-2.0*k-3.0);
  1194 + }
  1195 + cbj1 = cu*(cp1*cos(ct2)-cq1*sin(ct2));
  1196 + cby1 = cu*(cp1*sin(ct2)+cq1*cos(ct2));
  1197 + cj[1] = cbj1;
  1198 + cy[1] = cby1;
  1199 + for (k=2;k<=n;k++) {
  1200 + cbjk = 2.0*(k-1.0)*cbj1/z-cbj0;
  1201 + cj[k] = cbjk;
  1202 + cbj0 = cbj1;
  1203 + cbj1 = cbjk;
  1204 + }
  1205 + }
  1206 + cjp[0] = -cj[1];
  1207 + for (k=1;k<=nm;k++) {
  1208 + cjp[k] = cj[k-1]-(P)k*cj[k]/z;
  1209 + }
  1210 + if (abs(cj[0]) > 1.0)
  1211 + cy[1] = (cj[1]*cy[0]-2.0/(M_PI*z))/cj[0];
  1212 + for (k=2;k<=nm;k++) {
  1213 + if (abs(cj[k-1]) >= abs(cj[k-2]))
  1214 + cyy = (cj[k]*cy[k-1]-2.0/(M_PI*z))/cj[k-1];
  1215 + else
  1216 + cyy = (cj[k]*cy[k-2]-4.0*(k-1.0)/(M_PI*z*z))/cj[k-2];
  1217 + cy[k] = cyy;
  1218 + }
  1219 + cyp[0] = -cy[1];
  1220 + for (k=1;k<=nm;k++) {
  1221 + cyp[k] = cy[k-1]-(P)k*cy[k]/z;
  1222 + }
  1223 +
  1224 + return 0;
  1225 +}
  1226 +
  1227 +template<typename P>
  1228 +int cbessjyva(P v,complex<P> z,P &vm,complex<P>*cjv,
  1229 + complex<P>*cyv,complex<P>*cjvp,complex<P>*cyvp)
  1230 +{
  1231 + complex<P> z1,z2,zk,cjvl,cr,ca,cjv0,cjv1,cpz,crp;
  1232 + complex<P> cqz,crq,ca0,cck,csk,cyv0,cyv1,cju0,cju1,cb;
  1233 + complex<P> cs,cs0,cr0,cs1,cr1,cec,cf,cf0,cf1,cf2;
  1234 + complex<P> cfac0,cfac1,cg0,cg1,cyk,cp11,cp12,cp21,cp22;
  1235 + complex<P> ch0,ch1,ch2,cyl1,cyl2,cylk;
  1236 +
  1237 + P a0,v0,pv0,pv1,vl,ga,gb,vg,vv,w0,w1,ya0,yak,ya1,wa;
  1238 + int j,n,k,kz,l,lb,lb0,m;
  1239 +
  1240 + a0 = abs(z);
  1241 + z1 = z;
  1242 + z2 = z*z;
  1243 + n = (int)v;
  1244 +
  1245 +
  1246 + v0 = v-n;
  1247 +
  1248 + pv0 = M_PI*v0;
  1249 + pv1 = M_PI*(1.0+v0);
  1250 + if (a0 < 1.0e-100) {
  1251 + for (k=0;k<=n;k++) {
  1252 + cjv[k] = czero;
  1253 + cyv[k] = complex<P> (-1e308,0);
  1254 + cjvp[k] = czero;
  1255 + cyvp[k] = complex<P> (1e308,0);
  1256 +
  1257 + }
  1258 + if (v0 == 0.0) {
  1259 + cjv[0] = cone;
  1260 + cjvp[1] = complex<P> (0.5,0.0);
  1261 + }
  1262 + else {
  1263 + cjvp[0] = complex<P> (1e308,0);
  1264 + }
  1265 + vm = v;
  1266 + return 0;
  1267 + }
  1268 + if (real(z1) < 0.0) z1 = -z;
  1269 + if (a0 <= 12.0) {
  1270 + for (l=0;l<2;l++) {
  1271 + vl = v0+l;
  1272 + cjvl = cone;
  1273 + cr = cone;
  1274 + for (k=1;k<=40;k++) {
  1275 + cr *= -0.25*z2/(k*(k+vl));
  1276 + cjvl += cr;
  1277 + if (abs(cr) < abs(cjvl)*eps) break;
  1278 + }
  1279 + vg = 1.0 + vl;
  1280 + ga = gamma(vg);
  1281 + ca = pow(0.5*z1,vl)/ga;
  1282 + if (l == 0) cjv0 = cjvl*ca;
  1283 + else cjv1 = cjvl*ca;
  1284 + }
  1285 + }
  1286 + else {
  1287 + if (a0 >= 50.0) kz = 8;
  1288 + else if (a0 >= 35.0) kz = 10;
  1289 + else kz = 11;
  1290 + for (j=0;j<2;j++) {
  1291 + vv = 4.0*(j+v0)*(j+v0);
  1292 + cpz = cone;
  1293 + crp = cone;
  1294 + for (k=1;k<=kz;k++) {
  1295 + crp = -0.78125e-2*crp*(vv-pow(4.0*k-3.0,2.0))*
  1296 + (vv-pow(4.0*k-1.0,2.0))/(k*(2.0*k-1.0)*z2);
  1297 + cpz += crp;
  1298 + }
  1299 + cqz = cone;
  1300 + crq = cone;
  1301 + for (k=1;k<=kz;k++) {
  1302 + crq = -0.78125e-2*crq*(vv-pow(4.0*k-1.0,2.0))*
  1303 + (vv-pow(4.0*k+1.0,2.0))/(k*(2.0*k+1.0)*z2);
  1304 + cqz += crq;
  1305 + }
  1306 + cqz *= 0.125*(vv-1.0)/z1;
  1307 + zk = z1-(0.5*(j+v0)+0.25)*M_PI;
  1308 + ca0 = sqrt(M_2_PI/z1);
  1309 + cck = cos(zk);
  1310 + csk = sin(zk);
  1311 + if (j == 0) {
  1312 + cjv0 = ca0*(cpz*cck-cqz*csk);
  1313 + cyv0 = ca0*(cpz*csk+cqz+cck);
  1314 + }
  1315 + else {
  1316 + cjv1 = ca0*(cpz*cck-cqz*csk);
  1317 + cyv1 = ca0*(cpz*csk+cqz*cck);
  1318 + }
  1319 + }
  1320 + }
  1321 + if (a0 <= 12.0) {
  1322 + if (v0 != 0.0) {
  1323 + for (l=0;l<2;l++) {
  1324 + vl = v0+l;
  1325 + cjvl = cone;
  1326 + cr = cone;
  1327 + for (k=1;k<=40;k++) {
  1328 + cr *= -0.25*z2/(k*(k-vl));
  1329 + cjvl += cr;
  1330 + if (abs(cr) < abs(cjvl)*eps) break;
  1331 + }
  1332 + vg = 1.0-vl;
  1333 + gb = gamma(vg);
  1334 + cb = pow(2.0/z1,vl)/gb;
  1335 + if (l == 0) cju0 = cjvl*cb;
  1336 + else cju1 = cjvl*cb;
  1337 + }
  1338 + cyv0 = (cjv0*cos(pv0)-cju0)/sin(pv0);
  1339 + cyv1 = (cjv1*cos(pv1)-cju1)/sin(pv1);
  1340 + }
  1341 + else {
  1342 + cec = log(0.5*z1)+el;
  1343 + cs0 = czero;
  1344 + w0 = 0.0;
  1345 + cr0 = cone;
  1346 + for (k=1;k<=30;k++) {
  1347 + w0 += 1.0/k;
  1348 + cr0 *= -0.25*z2/(P)(k*k);
  1349 + cs0 += cr0*w0;
  1350 + }
  1351 + cyv0 = M_2_PI*(cec*cjv0-cs0);
  1352 + cs1 = cone;
  1353 + w1 = 0.0;
  1354 + cr1 = cone;
  1355 + for (k=1;k<=30;k++) {
  1356 + w1 += 1.0/k;
  1357 + cr1 *= -0.25*z2/(k*(k+1.0));
  1358 + cs1 += cr1*(2.0*w1+1.0/(k+1.0));
  1359 + }
  1360 + cyv1 = M_2_PI*(cec*cjv1-1.0/z1-0.25*z1*cs1);
  1361 + }
  1362 + }
  1363 + if (real(z) < 0.0) {
  1364 + cfac0 = exp(pv0*cii);
  1365 + cfac1 = exp(pv1*cii);
  1366 + if (imag(z) < 0.0) {
  1367 + cyv0 = cfac0*cyv0-(P)2.0*(complex<P>)cii*cos(pv0)*cjv0;
  1368 + cyv1 = cfac1*cyv1-(P)2.0*(complex<P>)cii*cos(pv1)*cjv1;
  1369 + cjv0 /= cfac0;
  1370 + cjv1 /= cfac1;
  1371 + }
  1372 + else if (imag(z) > 0.0) {
  1373 + cyv0 = cyv0/cfac0+(P)2.0*(complex<P>)cii*cos(pv0)*cjv0;
  1374 + cyv1 = cyv1/cfac1+(P)2.0*(complex<P>)cii*cos(pv1)*cjv1;
  1375 + cjv0 *= cfac0;
  1376 + cjv1 *= cfac1;
  1377 + }
  1378 + }
  1379 + cjv[0] = cjv0;
  1380 + cjv[1] = cjv1;
  1381 + if ((n >= 2) && (n <= (int)(0.25*a0))) {
  1382 + cf0 = cjv0;
  1383 + cf1 = cjv1;
  1384 + for (k=2;k<= n;k++) {
  1385 + cf = 2.0*(k+v0-1.0)*cf1/z-cf0;
  1386 + cjv[k] = cf;
  1387 + cf0 = cf1;
  1388 + cf1 = cf;
  1389 + }
  1390 + }
  1391 + else if (n >= 2) {
  1392 + m = msta1(a0,200);
  1393 + if (m < n) n = m;
  1394 + else m = msta2(a0,n,15);
  1395 + cf2 = czero;
  1396 + cf1 = complex<P>(1.0e-100,0.0);
  1397 + for (k=m;k>=0;k--) {
  1398 + cf = 2.0*(v0+k+1.0)*cf1/z-cf2;
  1399 + if (k <= n) cjv[k] = cf;
  1400 + cf2 = cf1;
  1401 + cf1 = cf;
  1402 + }
  1403 + if (abs(cjv0) > abs(cjv1)) cs = cjv0/cf;
  1404 + else cs = cjv1/cf2;
  1405 + for (k=0;k<=n;k++) {
  1406 + cjv[k] *= cs;
  1407 + }
  1408 + }
  1409 + cjvp[0] = v0*cjv[0]/z-cjv[1];
  1410 + for (k=1;k<=n;k++) {
  1411 + cjvp[k] = -(k+v0)*cjv[k]/z+cjv[k-1];
  1412 + }
  1413 + cyv[0] = cyv0;
  1414 + cyv[1] = cyv1;
  1415 + ya0 = abs(cyv0);
  1416 + lb = 0;
  1417 + cg0 = cyv0;
  1418 + cg1 = cyv1;
  1419 + for (k=2;k<=n;k++) {
  1420 + cyk = 2.0*(v0+k-1.0)*cg1/z-cg0;
  1421 + yak = abs(cyk);
  1422 + ya1 = abs(cg0);
  1423 + if ((yak < ya0) && (yak< ya1)) lb = k;
  1424 + cyv[k] = cyk;
  1425 + cg0 = cg1;
  1426 + cg1 = cyk;
  1427 + }
  1428 + lb0 = 0;
  1429 + if ((lb > 4) && (imag(z) != 0.0)) {
  1430 + while(lb != lb0) {
  1431 + ch2 = cone;
  1432 + ch1 = czero;
  1433 + lb0 = lb;
  1434 + for (k=lb;k>=1;k--) {
  1435 + ch0 = 2.0*(k+v0)*ch1/z-ch2;
  1436 + ch2 = ch1;
  1437 + ch1 = ch0;
  1438 + }
  1439 + cp12 = ch0;
  1440 + cp22 = ch2;
  1441 + ch2 = czero;
  1442 + ch1 = cone;
  1443 + for (k=lb;k>=1;k--) {
  1444 + ch0 = 2.0*(k+v0)*ch1/z-ch2;
  1445 + ch2 = ch1;
  1446 + ch1 = ch0;
  1447 + }
  1448 + cp11 = ch0;
  1449 + cp21 = ch2;
  1450 + if (lb == n)
  1451 + cjv[lb+1] = 2.0*(lb+v0)*cjv[lb]/z-cjv[lb-1];
  1452 + if (abs(cjv[0]) > abs(cjv[1])) {
  1453 + cyv[lb+1] = (cjv[lb+1]*cyv0-2.0*cp11/(M_PI*z))/cjv[0];
  1454 + cyv[lb] = (cjv[lb]*cyv0+2.0*cp12/(M_PI*z))/cjv[0];
  1455 + }
  1456 + else {
  1457 + cyv[lb+1] = (cjv[lb+1]*cyv1-2.0*cp21/(M_PI*z))/cjv[1];
  1458 + cyv[lb] = (cjv[lb]*cyv1+2.0*cp22/(M_PI*z))/cjv[1];
  1459 + }
  1460 + cyl2 = cyv[lb+1];
  1461 + cyl1 = cyv[lb];
  1462 + for (k=lb-1;k>=0;k--) {
  1463 + cylk = 2.0*(k+v0+1.0)*cyl1/z-cyl2;
  1464 + cyv[k] = cylk;
  1465 + cyl2 = cyl1;
  1466 + cyl1 = cylk;
  1467 + }
  1468 + cyl1 = cyv[lb];
  1469 + cyl2 = cyv[lb+1];
  1470 + for (k=lb+1;k<n;k++) {
  1471 + cylk = 2.0*(k+v0)*cyl2/z-cyl1;
  1472 + cyv[k+1] = cylk;
  1473 + cyl1 = cyl2;
  1474 + cyl2 = cylk;
  1475 + }
  1476 + for (k=2;k<=n;k++) {
  1477 + wa = abs(cyv[k]);
  1478 + if (wa < abs(cyv[k-1])) lb = k;
  1479 + }
  1480 + }
  1481 + }
  1482 + cyvp[0] = v0*cyv[0]/z-cyv[1];
  1483 + for (k=1;k<=n;k++) {
  1484 + cyvp[k] = cyv[k-1]-(k+v0)*cyv[k]/z;
  1485 + }
  1486 + vm = n+v0;
  1487 + return 0;
  1488 +}
  1489 +
  1490 +template<typename P>
  1491 +int cbessjyva_sph(int v,complex<P> z,P &vm,complex<P>*cjv,
  1492 + complex<P>*cyv,complex<P>*cjvp,complex<P>*cyvp)
  1493 +{
  1494 + //first, compute the bessel functions of fractional order
  1495 + cbessjyva<P>(v + 0.5, z, vm, cjv, cyv, cjvp, cyvp);
  1496 +
  1497 + //iterate through each and scale
  1498 + for(int n = 0; n<=v; n++)
  1499 + {
  1500 +
  1501 + cjv[n] = cjv[n] * sqrt(stim::PI/(z * 2.0));
  1502 + cyv[n] = cyv[n] * sqrt(stim::PI/(z * 2.0));
  1503 +
  1504 + cjvp[n] = -1.0 / (z * 2.0) * cjv[n] + cjvp[n] * sqrt(stim::PI / (z * 2.0));
  1505 + cyvp[n] = -1.0 / (z * 2.0) * cyv[n] + cyvp[n] * sqrt(stim::PI / (z * 2.0));
  1506 + }
  1507 +
  1508 + return 0;
  1509 +
  1510 +}
  1511 +
  1512 +} //end namespace rts
  1513 +
  1514 +
  1515 +#endif
stim/math/bessel.h
@@ -17,10 +17,10 @@ static complex&lt;double&gt; czero(0.0,0.0); @@ -17,10 +17,10 @@ static complex&lt;double&gt; czero(0.0,0.0);
17 template< typename P > 17 template< typename P >
18 P gamma(P x) 18 P gamma(P x)
19 { 19 {
20 - const P EPS = numeric_limits<P>::epsilon();  
21 - const P FPMIN_MAG = numeric_limits<P>::min();  
22 - const P FPMIN = numeric_limits<P>::lowest();  
23 - const P FPMAX = numeric_limits<P>::max(); 20 + const P EPS = std::numeric_limits<P>::epsilon();
  21 + const P FPMIN_MAG = std::numeric_limits<P>::min();
  22 + const P FPMIN = std::numeric_limits<P>::lowest();
  23 + const P FPMAX = std::numeric_limits<P>::max();
24 24
25 int i,k,m; 25 int i,k,m;
26 P ga,gr,r,z; 26 P ga,gr,r,z;
@@ -94,10 +94,10 @@ template&lt;typename P&gt; @@ -94,10 +94,10 @@ template&lt;typename P&gt;
94 int bessjy01a(P x,P &j0,P &j1,P &y0,P &y1, 94 int bessjy01a(P x,P &j0,P &j1,P &y0,P &y1,
95 P &j0p,P &j1p,P &y0p,P &y1p) 95 P &j0p,P &j1p,P &y0p,P &y1p)
96 { 96 {
97 - const P EPS = numeric_limits<P>::epsilon();  
98 - const P FPMIN_MAG = numeric_limits<P>::min();  
99 - const P FPMIN = numeric_limits<P>::lowest();  
100 - const P FPMAX = numeric_limits<P>::max(); 97 + const P EPS = std::numeric_limits<P>::epsilon();
  98 + const P FPMIN_MAG = std::numeric_limits<P>::min();
  99 + const P FPMIN = std::numeric_limits<P>::lowest();
  100 + const P FPMAX = std::numeric_limits<P>::max();
101 101
102 P x2,r,ec,w0,w1,r0,r1,cs0,cs1; 102 P x2,r,ec,w0,w1,r0,r1,cs0,cs1;
103 P cu,p0,q0,p1,q1,t1,t2; 103 P cu,p0,q0,p1,q1,t1,t2;
@@ -606,10 +606,10 @@ int bessjyv(P v,P x,P &amp;vm,P *jv,P *yv, @@ -606,10 +606,10 @@ int bessjyv(P v,P x,P &amp;vm,P *jv,P *yv,
606 P b,ec,w0,w1,bju0,bju1,pv0,pv1,byvk; 606 P b,ec,w0,w1,bju0,bju1,pv0,pv1,byvk;
607 int j,k,l,m,n,kz; 607 int j,k,l,m,n,kz;
608 608
609 - const P EPS = numeric_limits<P>::epsilon();  
610 - const P FPMIN_MAG = numeric_limits<P>::min();  
611 - const P FPMIN = numeric_limits<P>::lowest();  
612 - const P FPMAX = numeric_limits<P>::max(); 609 + const P EPS = std::numeric_limits<P>::epsilon();
  610 + const P FPMIN_MAG = std::numeric_limits<P>::min();
  611 + const P FPMIN = std::numeric_limits<P>::lowest();
  612 + const P FPMAX = std::numeric_limits<P>::max();
613 613
614 x2 = x*x; 614 x2 = x*x;
615 n = (int)v; 615 n = (int)v;
@@ -1508,6 +1508,8 @@ int cbessjyva(P v,complex&lt;P&gt; z,P &amp;vm,complex&lt;P&gt;*cjv, @@ -1508,6 +1508,8 @@ int cbessjyva(P v,complex&lt;P&gt; z,P &amp;vm,complex&lt;P&gt;*cjv,
1508 return 0; 1508 return 0;
1509 } 1509 }
1510 1510
  1511 +///Calculate the spherical bessel functions and their derivatives up to order v
  1512 +/// When allocating arrays to store the resulting values, arrays must be of size [v+2]
1511 template<typename P> 1513 template<typename P>
1512 int cbessjyva_sph(int v,complex<P> z,P &vm,complex<P>*cjv, 1514 int cbessjyva_sph(int v,complex<P> z,P &vm,complex<P>*cjv,
1513 complex<P>*cyv,complex<P>*cjvp,complex<P>*cyvp) 1515 complex<P>*cyv,complex<P>*cjvp,complex<P>*cyvp)
stim/math/circle.h
@@ -18,13 +18,14 @@ class circle : plane&lt;T&gt; @@ -18,13 +18,14 @@ class circle : plane&lt;T&gt;
18 18
19 private: 19 private:
20 20
21 - stim::vec3<T> Y; 21 + //stim::vec3<T> Y;
  22 + T R; //radius of the circle
22 23
23 - CUDA_CALLABLE void 24 + /*CUDA_CALLABLE void
24 init() 25 init()
25 { 26 {
26 Y = U.cross(N).norm(); 27 Y = U.cross(N).norm();
27 - } 28 + }*/
28 29
29 public: 30 public:
30 using stim::plane<T>::n; 31 using stim::plane<T>::n;
@@ -34,6 +35,8 @@ public: @@ -34,6 +35,8 @@ public:
34 using stim::plane<T>::rotate; 35 using stim::plane<T>::rotate;
35 using stim::plane<T>::setU; 36 using stim::plane<T>::setU;
36 37
  38 + using stim::plane<T>::init;
  39 +
37 ///base constructor 40 ///base constructor
38 ///@param th value of the angle of the starting point from 0 to 360. 41 ///@param th value of the angle of the starting point from 0 to 360.
39 CUDA_CALLABLE 42 CUDA_CALLABLE
@@ -42,26 +45,28 @@ public: @@ -42,26 +45,28 @@ public:
42 init(); 45 init();
43 } 46 }
44 47
45 - ///create a rectangle given a size and position in Z space. 48 + ///create a circle given a size and position in Z space.
46 ///@param size: size of the rectangle in ND space. 49 ///@param size: size of the rectangle in ND space.
47 ///@param z_pos z coordinate of the rectangle. 50 ///@param z_pos z coordinate of the rectangle.
48 CUDA_CALLABLE 51 CUDA_CALLABLE
49 - circle(T size, T z_pos = (T)0) : plane<T>() 52 + circle(T radius, T z_pos = (T)0) : plane<T>(z_pos)
50 { 53 {
51 - center(stim::vec3<T>(0,0,z_pos));  
52 - scale(size);  
53 - init(); 54 + //center(stim::vec3<T>(0, 0, z_pos));
  55 + //scale(size);
  56 + //init();
  57 + R = radius;
54 } 58 }
55 59
56 ///create a rectangle from a center point, normal 60 ///create a rectangle from a center point, normal
57 ///@param c: x,y,z location of the center. 61 ///@param c: x,y,z location of the center.
58 ///@param n: x,y,z direction of the normal. 62 ///@param n: x,y,z direction of the normal.
59 CUDA_CALLABLE 63 CUDA_CALLABLE
60 - circle(vec3<T> c, vec3<T> n = vec3<T>(0,0,1)) : plane<T>() 64 + circle(vec3<T> c, vec3<T> n = vec3<T>(0,0,1)) : plane<T>(n, c)
61 { 65 {
62 - center(c);  
63 - normal(n);  
64 - init(); 66 + //center(c);
  67 + //normal(n);
  68 + //init();
  69 + R = (T)1;
65 } 70 }
66 71
67 /*///create a rectangle from a center point, normal, and size 72 /*///create a rectangle from a center point, normal, and size
@@ -84,14 +89,18 @@ public: @@ -84,14 +89,18 @@ public:
84 ///@param n: x,y,z direction of the normal. 89 ///@param n: x,y,z direction of the normal.
85 ///@param u: x,y,z direction for the zero vector (from where the rotation starts) 90 ///@param u: x,y,z direction for the zero vector (from where the rotation starts)
86 CUDA_CALLABLE 91 CUDA_CALLABLE
87 - circle(vec3<T> c, T s, vec3<T> n = vec3<T>(0,0,1), vec3<T> u = vec3<T>(1, 0, 0)) : plane<T>() 92 + circle(vec3<T> c, T r, vec3<T> n = vec3<T>(0,0,1), vec3<T> u = vec3<T>(1, 0, 0)) : plane<T>()
88 { 93 {
89 - init(); 94 + P = c;
  95 + N = n;
90 setU(u); 96 setU(u);
  97 + R = r;
  98 + //init();
  99 + //setU(u);
91 // U = u; 100 // U = u;
92 - center(c);  
93 - normal(n);  
94 - scale(s); 101 + //center(c);
  102 + //normal(n);
  103 + //scale(s);
95 } 104 }
96 105
97 ///scales the circle by a certain factor 106 ///scales the circle by a certain factor
@@ -99,86 +108,111 @@ public: @@ -99,86 +108,111 @@ public:
99 CUDA_CALLABLE 108 CUDA_CALLABLE
100 void scale(T factor) 109 void scale(T factor)
101 { 110 {
102 - U *= factor;  
103 - Y *= factor; 111 + //U *= factor;
  112 + //Y *= factor;
  113 + R *= factor;
  114 + }
  115 +
  116 + ///set the radius of circle to a certain value
  117 + ///@param value: the new radius of the circle
  118 + CUDA_CALLABLE
  119 + void set_R(T value)
  120 + {
  121 + R = value;
104 } 122 }
105 123
106 ///sets the normal for the cirlce 124 ///sets the normal for the cirlce
107 ///@param n: x,y,z direction of the normal. 125 ///@param n: x,y,z direction of the normal.
108 CUDA_CALLABLE void 126 CUDA_CALLABLE void
109 - normal(vec3<T> n)  
110 - {  
111 - rotate(n, Y); 127 + normal(vec3<T> n){
  128 + rotate(n);
112 } 129 }
113 130
114 ///sets the center of the circle. 131 ///sets the center of the circle.
115 ///@param n: x,y,z location of the center. 132 ///@param n: x,y,z location of the center.
116 CUDA_CALLABLE void 133 CUDA_CALLABLE void
117 center(vec3<T> p){ 134 center(vec3<T> p){
118 - this->P = p; 135 + P = p;
119 } 136 }
120 137
121 ///boolean comparison 138 ///boolean comparison
122 bool 139 bool
123 operator==(const circle<T> & rhs) 140 operator==(const circle<T> & rhs)
124 { 141 {
125 - if(P == rhs.P && U == rhs.U && Y == rhs.Y) 142 + if(P == rhs.P && U == rhs.U)
126 return true; 143 return true;
127 else 144 else
128 return false; 145 return false;
129 } 146 }
130 147
  148 + //returns the point in world space corresponding to the polar coordinate (r, theta)
  149 + CUDA_CALLABLE stim::vec3<T>
  150 + p(T r, T theta) {
  151 + T u = r * cos(theta); //calculate the coordinates in the planar space defined by the circle
  152 + T v = r * sin(theta);
  153 +
  154 + vec3<T> V = U.cross(N); //calculate the orthogonal vector V
  155 + return P + U * u + V * v; //calculate the cartesian coordinate of the specified point
  156 + }
  157 +
  158 + //returns the point in world space corresponding to the value theta at radius R
  159 + CUDA_CALLABLE stim::vec3<T>
  160 + p(T theta) {
  161 + return p(R, theta);
  162 + }
  163 +
  164 + //get the world space value given the polar coordinates (r, theta)
  165 +
131 ///get the world space value given the planar coordinates a, b in [0, 1] 166 ///get the world space value given the planar coordinates a, b in [0, 1]
132 - CUDA_CALLABLE stim::vec3<T> p(T a, T b) 167 + /*CUDA_CALLABLE stim::vec3<T> p(T a, T b)
133 { 168 {
134 stim::vec3<T> result; 169 stim::vec3<T> result;
135 170
136 vec3<T> A = this->P - this->U * (T)0.5 - Y * (T)0.5; 171 vec3<T> A = this->P - this->U * (T)0.5 - Y * (T)0.5;
137 result = A + this->U * a + Y * b; 172 result = A + this->U * a + Y * b;
138 return result; 173 return result;
139 - } 174 + }*/
140 175
141 ///parenthesis operator returns the world space given rectangular coordinates a and b in [0 1] 176 ///parenthesis operator returns the world space given rectangular coordinates a and b in [0 1]
142 - CUDA_CALLABLE stim::vec3<T> operator()(T a, T b) 177 + CUDA_CALLABLE stim::vec3<T> operator()(T r, T theta)
143 { 178 {
144 - return p(a,b); 179 + return p(r, theta);
  180 + }
  181 +
  182 + //parenthesis operator returns the world space coordinate at the edge of the circle given theta
  183 + CUDA_CALLABLE stim::vec3<T> operator()(T theta) {
  184 + return p(theta);
145 } 185 }
146 186
147 ///returns a vector with the points on the initialized circle. 187 ///returns a vector with the points on the initialized circle.
148 ///connecting the points results in a circle. 188 ///connecting the points results in a circle.
149 ///@param n: integer for the number of points representing the circle. 189 ///@param n: integer for the number of points representing the circle.
150 - std::vector<stim::vec3<T> >  
151 - getPoints(int n)  
152 - {  
153 - std::vector<stim::vec3<T> > result;  
154 - stim::vec3<T> point;  
155 - T x,y;  
156 - float step = 360.0/(float) n;  
157 - for(float j = 0; j <= 360.0; j += step)  
158 - {  
159 - y = 0.5*cos(j*stim::TAU/360.0)+0.5;  
160 - x = 0.5*sin(j*stim::TAU/360.0)+0.5;  
161 - result.push_back(p(x,y));  
162 - } 190 + std::vector< stim::vec3<T> > points(unsigned n) {
  191 + std::vector< stim::vec3<T> > result(n); //initialize a vector of n points
  192 +
  193 + float dt = stim::TAU / n;
  194 + for (unsigned i = 0; i < n; i++)
  195 + result[i] = p(i * dt); //calculate a point on the edge of the circle
163 return result; 196 return result;
164 } 197 }
165 -  
166 - ///returns a vector with the points on the initialized circle.  
167 - ///connecting the points results in a circle.  
168 - ///@param n: integer for the number of points representing the circle.  
169 - CUDA_CALLABLE stim::vec3<T>  
170 - p(T theta)  
171 - {  
172 - T x,y;  
173 - y = 0.5*cos(theta*STIM_TAU/360.0)+0.5;  
174 - x = 0.5*sin(theta*STIM_TAU/360.0)+0.5;  
175 - return p(x,y);  
176 - }  
177 198
  199 + ///returns a vector with the points on the initialized circle
  200 + ///connecting the points results in a circle
  201 + ///@param n: integer for the number of points representing the circle
  202 + ///the only difference between points and glpoints is that the first point appears twice in the returning lists
  203 + std::vector< stim::vec3<T> > glpoints(unsigned n) {
  204 + std::vector< stim::vec3<T> > result(n + 1);
  205 + float dt = stim::TAU / n;
  206 + for (unsigned i = 0; i < n; i++)
  207 + result[i] = p(i * dt);
  208 + result[n] = p(0); //close the circle!
  209 + return result;
  210 + }
  211 +
178 std::string str() const 212 std::string str() const
179 { 213 {
180 std::stringstream ss; 214 std::stringstream ss;
181 - ss << "(P=" << P.str() << ", N=" << N.str() << ", U=" << U.str() << ", Y=" << Y.str(); 215 + ss << "r = "<<R<<" (P=" << P.str() << ", N=" << N.str() << ", U=" << U.str() << ")";
182 return ss.str(); 216 return ss.str();
183 } 217 }
184 218
stim/math/circle_dep.h 0 → 100644
  1 +#ifndef STIM_CIRCLE_H
  2 +#define STIM_CIRCLE_H
  3 +
  4 +#include <stim/cuda/cudatools/callable.h>
  5 +#include <stim/math/plane.h>
  6 +#include <stim/math/vector.h>
  7 +#include <stim/math/triangle.h>
  8 +#include <stim/math/constants.h>
  9 +#include <assert.h>
  10 +#include <algorithm>
  11 +#include <iostream>
  12 +
  13 +namespace stim{
  14 +
  15 +template <typename T>
  16 +class circle : plane<T>
  17 +{
  18 +
  19 +private:
  20 +
  21 + stim::vec3<T> Y;
  22 +
  23 + CUDA_CALLABLE void
  24 + init()
  25 + {
  26 + Y = U.cross(N).norm();
  27 + }
  28 +
  29 +public:
  30 + using stim::plane<T>::n;
  31 + using stim::plane<T>::P;
  32 + using stim::plane<T>::N;
  33 + using stim::plane<T>::U;
  34 + using stim::plane<T>::rotate;
  35 + using stim::plane<T>::setU;
  36 +
  37 + ///base constructor
  38 + ///@param th value of the angle of the starting point from 0 to 360.
  39 + CUDA_CALLABLE
  40 + circle() : plane<T>()
  41 + {
  42 + init();
  43 + }
  44 +
  45 + ///create a rectangle given a size and position in Z space.
  46 + ///@param size: size of the rectangle in ND space.
  47 + ///@param z_pos z coordinate of the rectangle.
  48 + CUDA_CALLABLE
  49 + circle(T size, T z_pos = (T)0) : plane<T>()
  50 + {
  51 + center(stim::vec3<T>(0,0,z_pos));
  52 + scale(size);
  53 + init();
  54 + }
  55 +
  56 + ///create a rectangle from a center point, normal
  57 + ///@param c: x,y,z location of the center.
  58 + ///@param n: x,y,z direction of the normal.
  59 + CUDA_CALLABLE
  60 + circle(vec3<T> c, vec3<T> n = vec3<T>(0,0,1)) : plane<T>()
  61 + {
  62 + center(c);
  63 + normal(n);
  64 + init();
  65 + }
  66 +
  67 + /*///create a rectangle from a center point, normal, and size
  68 + ///@param c: x,y,z location of the center.
  69 + ///@param s: size of the rectangle.
  70 + ///@param n: x,y,z direction of the normal.
  71 + CUDA_CALLABLE
  72 + circle(vec3<T> c, T s, vec3<T> n = vec3<T>(0,0,1)) : plane<T>()
  73 + {
  74 + init();
  75 + center(c);
  76 + rotate(n, U, Y);
  77 + scale(s);
  78 + }
  79 + */
  80 +
  81 + ///create a rectangle from a center point, normal, and size
  82 + ///@param c: x,y,z location of the center.
  83 + ///@param s: size of the rectangle.
  84 + ///@param n: x,y,z direction of the normal.
  85 + ///@param u: x,y,z direction for the zero vector (from where the rotation starts)
  86 + CUDA_CALLABLE
  87 + circle(vec3<T> c, T s, vec3<T> n = vec3<T>(0,0,1), vec3<T> u = vec3<T>(1, 0, 0)) : plane<T>()
  88 + {
  89 + init();
  90 + setU(u);
  91 +// U = u;
  92 + center(c);
  93 + normal(n);
  94 + scale(s);
  95 + }
  96 +
  97 + ///scales the circle by a certain factor
  98 + ///@param factor: the factor by which the dimensions of the shape are scaled.
  99 + CUDA_CALLABLE
  100 + void scale(T factor)
  101 + {
  102 + U *= factor;
  103 + Y *= factor;
  104 + }
  105 +
  106 + ///sets the normal for the cirlce
  107 + ///@param n: x,y,z direction of the normal.
  108 + CUDA_CALLABLE void
  109 + normal(vec3<T> n)
  110 + {
  111 + rotate(n, Y);
  112 + }
  113 +
  114 + ///sets the center of the circle.
  115 + ///@param n: x,y,z location of the center.
  116 + CUDA_CALLABLE void
  117 + center(vec3<T> p){
  118 + this->P = p;
  119 + }
  120 +
  121 + ///boolean comparison
  122 + bool
  123 + operator==(const circle<T> & rhs)
  124 + {
  125 + if(P == rhs.P && U == rhs.U && Y == rhs.Y)
  126 + return true;
  127 + else
  128 + return false;
  129 + }
  130 +
  131 + ///get the world space value given the planar coordinates a, b in [0, 1]
  132 + CUDA_CALLABLE stim::vec3<T> p(T a, T b)
  133 + {
  134 + stim::vec3<T> result;
  135 +
  136 + vec3<T> A = this->P - this->U * (T)0.5 - Y * (T)0.5;
  137 + result = A + this->U * a + Y * b;
  138 + return result;
  139 + }
  140 +
  141 + ///parenthesis operator returns the world space given rectangular coordinates a and b in [0 1]
  142 + CUDA_CALLABLE stim::vec3<T> operator()(T a, T b)
  143 + {
  144 + return p(a,b);
  145 + }
  146 +
  147 + ///returns a vector with the points on the initialized circle.
  148 + ///connecting the points results in a circle.
  149 + ///@param n: integer for the number of points representing the circle.
  150 + std::vector<stim::vec3<T> >
  151 + getPoints(int n)
  152 + {
  153 + std::vector<stim::vec3<T> > result;
  154 + stim::vec3<T> point;
  155 + T x,y;
  156 + float step = 360.0/(float) n;
  157 + for(float j = 0; j <= 360.0; j += step)
  158 + {
  159 + y = 0.5*cos(j*stim::TAU/360.0)+0.5;
  160 + x = 0.5*sin(j*stim::TAU/360.0)+0.5;
  161 + result.push_back(p(x,y));
  162 + }
  163 + return result;
  164 + }
  165 +
  166 + ///returns a vector with the points on the initialized circle.
  167 + ///connecting the points results in a circle.
  168 + ///@param n: integer for the number of points representing the circle.
  169 + CUDA_CALLABLE stim::vec3<T>
  170 + p(T theta)
  171 + {
  172 + T x,y;
  173 + y = 0.5*cos(theta*STIM_TAU/360.0)+0.5;
  174 + x = 0.5*sin(theta*STIM_TAU/360.0)+0.5;
  175 + return p(x,y);
  176 + }
  177 +
  178 + std::string str() const
  179 + {
  180 + std::stringstream ss;
  181 + ss << "(P=" << P.str() << ", N=" << N.str() << ", U=" << U.str() << ", Y=" << Y.str();
  182 + return ss.str();
  183 + }
  184 +
  185 +};
  186 +}
  187 +#endif
stim/math/constants.h
@@ -4,7 +4,6 @@ @@ -4,7 +4,6 @@
4 #define STIM_PI 3.1415926535897932384626433832795028841971693993751058209749445923078164062862 4 #define STIM_PI 3.1415926535897932384626433832795028841971693993751058209749445923078164062862
5 #define STIM_TAU 2 * STIM_PI 5 #define STIM_TAU 2 * STIM_PI
6 6
7 -#include "stim/cuda/cudatools/callable.h"  
8 namespace stim{ 7 namespace stim{
9 const double PI = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862; 8 const double PI = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862;
10 const double TAU = 2 * stim::PI; 9 const double TAU = 2 * stim::PI;
stim/math/filters/conv2.cuh 0 → 100644
  1 +#ifndef STIM_CUDA_CONV2_H
  2 +#define STIM_CUDA_CONV2_H
  3 +//#define __CUDACC__
  4 +
  5 +#include <stim/cuda/cudatools.h>
  6 +#include <stim/cuda/sharedmem.cuh>
  7 +
  8 +namespace stim {
  9 + //Kernel function that performs the 2D convolution.
  10 + template<typename T, typename K>
  11 + __global__ void kernel_conv2(T* out, T* in, K* kernel, size_t sx, size_t sy, size_t kx, size_t ky) {
  12 + extern __shared__ T s[]; //declare a shared memory array
  13 + size_t xi = blockIdx.x * blockDim.x + threadIdx.x; //threads correspond to indices into the output image
  14 + size_t yi = blockIdx.y * blockDim.y + threadIdx.y;
  15 + size_t tid = threadIdx.y * blockDim.x + threadIdx.x;
  16 + size_t nt = blockDim.x * blockDim.y;
  17 +
  18 + size_t cx = blockIdx.x * blockDim.x; //find the upper left corner of the input region
  19 + size_t cy = blockIdx.y * blockDim.y;
  20 +
  21 + size_t X = sx - kx + 1; //calculate the size of the output image
  22 + size_t Y = sy - ky + 1;
  23 +
  24 + if (cx >= X || cy >= Y) return; //return if the entire block is outside the image
  25 + size_t smx = min(blockDim.x + kx - 1, sx - cx); //size of the shared copy of the input image
  26 + size_t smy = min(blockDim.y + ky - 1, sy - cy); // min function is used to deal with boundary blocks
  27 + stim::cuda::threadedMemcpy2D<T>(s, smx, smy, in, cx, cy, sx, sy, tid, nt); //copy the input region to shared memory
  28 + __syncthreads();
  29 +
  30 + if (xi >= X || yi >= Y) return; //returns if the thread is outside of the output image
  31 +
  32 + //loop through the kernel
  33 + size_t kxi, kyi;
  34 + K v = 0;
  35 + for (kyi = 0; kyi < ky; kyi++) {
  36 + for (kxi = 0; kxi < kx; kxi++) {
  37 + v += s[(threadIdx.y + kyi) * smx + threadIdx.x + kxi] * kernel[kyi * kx + kxi];
  38 + //v += in[(yi + kyi) * sx + xi + kxi] * kernel[kyi * kx + kxi];
  39 + }
  40 + }
  41 + out[yi * X + xi] = (T)v; //write the result to global memory
  42 +
  43 + }
  44 +
  45 + //Performs a convolution of a 2D image using the GPU. All pointers are assumed to be to memory on the current device.
  46 + //@param out is a pointer to the output image, which is of size (sx - kx + 1) x (sy - ky + 1)
  47 + //@param in is a pointer to the input image
  48 + //@param sx is the size of the input image along X
  49 + //@param sy is the size of the input image along Y
  50 + //@param kx is the size of the kernel along X
  51 + //@param ky is the size of the kernel along Y
  52 + template<typename T, typename K>
  53 + void gpu_conv2(T* out, T* in, K* kernel, size_t sx, size_t sy, size_t kx, size_t ky) {
  54 + cudaDeviceProp p;
  55 + HANDLE_ERROR(cudaGetDeviceProperties(&p, 0));
  56 + size_t tmax = p.maxThreadsPerBlock;
  57 + dim3 nt(sqrt(tmax), sqrt(tmax)); //calculate the block dimensions
  58 + size_t X = sx - kx + 1; //calculate the size of the output image
  59 + size_t Y = sy - ky + 1;
  60 + dim3 nb(X / nt.x + 1, Y / nt.y + 1); //calculate the grid dimensions
  61 + size_t sm = (nt.x + kx - 1) * (nt.y + ky - 1) * sizeof(T); //shared memory bytes required to store block data
  62 + if (sm > p.sharedMemPerBlock) {
  63 + std::cout << "Error in stim::gpu_conv2() - insufficient shared memory for this kernel." << std::endl;
  64 + exit(1);
  65 + }
  66 + kernel_conv2 <<<nb, nt, sm>>> (out, in, kernel, sx, sy, kx, ky); //launch the kernel
  67 + }
  68 +//#endif
  69 + //Performs a convolution of a 2D image. Only valid pixels based on the kernel are returned.
  70 + // As a result, the output image will be smaller than the input image by (kx-1, ky-1)
  71 + //@param out is a pointer to the output image
  72 + //@param in is a pointer to the input image
  73 + //@param sx is the size of the input image along X
  74 + //@param sy is the size of the input image along Y
  75 + //@param kx is the size of the kernel along X
  76 + //@param ky is the size of the kernel along Y
  77 + template<typename T, typename K>
  78 + void cpu_conv2(T* out, T* in, K* kernel, size_t sx, size_t sy, size_t kx, size_t ky) {
  79 + size_t X = sx - kx + 1; //x size of the output image
  80 + size_t Y = sy - ky + 1; //y size of the output image
  81 +
  82 + //allocate memory and copy everything to the GPU
  83 + T* gpu_in;
  84 + HANDLE_ERROR(cudaMalloc(&gpu_in, sx * sy * sizeof(T)));
  85 + HANDLE_ERROR(cudaMemcpy(gpu_in, in, sx * sy * sizeof(T), cudaMemcpyHostToDevice));
  86 + K* gpu_kernel;
  87 + HANDLE_ERROR(cudaMalloc(&gpu_kernel, kx * ky * sizeof(K)));
  88 + HANDLE_ERROR(cudaMemcpy(gpu_kernel, kernel, kx * ky * sizeof(K), cudaMemcpyHostToDevice));
  89 + T* gpu_out;
  90 + HANDLE_ERROR(cudaMalloc(&gpu_out, X * Y * sizeof(T)));
  91 + gpu_conv2(gpu_out, gpu_in, gpu_kernel, sx, sy, kx, ky); //execute the GPU kernel
  92 + HANDLE_ERROR(cudaMemcpy(out, gpu_out, X * Y * sizeof(T), cudaMemcpyDeviceToHost)); //copy the result to the host
  93 + HANDLE_ERROR(cudaFree(gpu_in));
  94 + HANDLE_ERROR(cudaFree(gpu_kernel));
  95 + HANDLE_ERROR(cudaFree(gpu_out));
  96 +/* CPU CODE
  97 + K v; //register stores the integral of the current pixel value
  98 + size_t yi, xi, kyi, kxi, yi_kyi_sx;
  99 + for (yi = 0; yi < Y; yi++) { //for each pixel in the output image
  100 + for (xi = 0; xi < X; xi++) {
  101 + v = 0;
  102 + for (kyi = 0; kyi < ky; kyi++) { //for each pixel in the kernel
  103 + yi_kyi_sx = (yi + kyi) * sx;
  104 + for (kxi = 0; kxi < kx; kxi++) {
  105 + v += in[yi_kyi_sx + xi + kxi] * kernel[kyi * kx + kxi];
  106 + }
  107 + }
  108 + out[yi * X + xi] = v; //save the result to the output array
  109 + }
  110 + }
  111 +
  112 + */
  113 + }
  114 +
  115 +
  116 +}
  117 +
  118 +
  119 +#endif
0 \ No newline at end of file 120 \ No newline at end of file
stim/math/filters/gauss2.h renamed to stim/math/filters/gauss2.cuh
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 #define STIM_CUDA_GAUSS2_H 2 #define STIM_CUDA_GAUSS2_H
3 3
4 #include <stim/image/image.h> 4 #include <stim/image/image.h>
5 -#include <stim/math/filters/sepconv2.h> 5 +#include <stim/math/filters/sepconv2.cuh>
6 #include <stim/math/constants.h> 6 #include <stim/math/constants.h>
7 7
8 namespace stim { 8 namespace stim {
@@ -18,8 +18,8 @@ namespace stim { @@ -18,8 +18,8 @@ namespace stim {
18 ///@param nstds specifies the number of standard deviations of the Gaussian that will be kept in the kernel 18 ///@param nstds specifies the number of standard deviations of the Gaussian that will be kept in the kernel
19 template<typename T, typename K> 19 template<typename T, typename K>
20 stim::image<T> cpu_gauss2(const stim::image<T>& in, K stdx, K stdy, size_t nstds = 3) { 20 stim::image<T> cpu_gauss2(const stim::image<T>& in, K stdx, K stdy, size_t nstds = 3) {
21 - size_t kx = stdx * nstds * 2; //calculate the kernel sizes  
22 - size_t ky = stdy * nstds * 2; 21 + size_t kx = (size_t)(stdx * nstds * 2) + 1; //calculate the kernel sizes
  22 + size_t ky = (size_t)(stdy * nstds * 2) + 1;
23 size_t X = in.width() - kx + 1; //calculate the size of the output image 23 size_t X = in.width() - kx + 1; //calculate the size of the output image
24 size_t Y = in.height() - ky + 1; 24 size_t Y = in.height() - ky + 1;
25 stim::image<T> r(X, Y, in.channels()); //create an output image 25 stim::image<T> r(X, Y, in.channels()); //create an output image
@@ -33,10 +33,10 @@ namespace stim { @@ -33,10 +33,10 @@ namespace stim {
33 for (size_t yi = 0; yi < ky; yi++) 33 for (size_t yi = 0; yi < ky; yi++)
34 gy[yi] = gauss1d((K)yi, muy, stdy); 34 gy[yi] = gauss1d((K)yi, muy, stdy);
35 35
36 - std::vector< stim::image<T> > IN = in.split(); //split the input image into channels 36 + std::vector< stim::image<T> > clist = in.split(); //split the input image into channels
37 std::vector< stim::image<T> > R = r.split(); //split the output image into channels 37 std::vector< stim::image<T> > R = r.split(); //split the output image into channels
38 for (size_t c = 0; c < in.channels(); c++) //for each channel 38 for (size_t c = 0; c < in.channels(); c++) //for each channel
39 - cpu_sepconv2(R[c].data(), IN[c].data(), gx, gy, IN[c].width(), IN[c].height(), kx, ky); 39 + cpu_sepconv2(R[c].data(), clist[c].data(), gx, gy, clist[c].width(), clist[c].height(), kx, ky);
40 40
41 r.merge(R); //merge the blurred channels into the final image 41 r.merge(R); //merge the blurred channels into the final image
42 return r; 42 return r;
stim/math/filters/gauss3.cuh 0 → 100644
  1 +#ifndef STIM_CUDA_GAUSS3_H
  2 +#define STIM_CUDA_GAUSS3_H
  3 +#include <stim/math/filters/sepconv3.cuh>
  4 +#include <stim/math/filters/gauss2.cuh>
  5 +#include <stim/math/constants.h>
  6 +
  7 +namespace stim
  8 +{
  9 + ///Perform a 3D gaussian convolution on an input image.
  10 + ///@param in is a pointer to the input data.
  11 + ///@param dimx is the size of in* in the x direction.
  12 + ///@param dimx is the size of in* in the y direction.
  13 + ///@param dimx is the size of in* in the z direction.
  14 + ///@param stdx is the standard deviation (in pixels) along the x axis.
  15 + ///@param stdy is the standard deviation (in pixels) along the y axis.
  16 + ///@param nstds specifies the number of standard deviations of the Gaussian that will be kept in the kernel.
  17 + template<typename T, typename K>
  18 + void cpu_gauss3(T* in, K dimx, K dimy, K dimz, K stdx, K stdy, K stdz, size_t nstds = 3)
  19 + {
  20 + //Set up the sizes of the gaussian Kernels.
  21 + size_t kx = stdx * nstds * 2;
  22 + size_t ky = stdy * nstds * 2;
  23 + size_t kz = stdz * nstds * 2;
  24 +
  25 + //Set up the sizes of the new output, which will be kx, ky, kz, smaller than the input.
  26 + size_t X = dimx - kx +1;
  27 + size_t Y = dimy - ky +1;
  28 + size_t Z = dimz - kz +1;
  29 + T* out = (T*) malloc(X*Y*Z* sizeof(T));
  30 +
  31 + ///Set up the memory that will store the gaussians
  32 + K* gaussx = (K*)malloc(kx *sizeof(K));
  33 + K* gaussy = (K*)malloc(ky *sizeof(K));
  34 + K* gaussz = (K*)malloc(kz *sizeof(K));
  35 +
  36 + ///Set up the midpoints of the gaussians.
  37 + K midgaussx = (K) kx/ (K)2;
  38 + K midgaussy = (K) ky/ (K)2;
  39 + K midgaussz = (K) kz/ (K)2;
  40 +
  41 + ///Evaluate the kernels in each cardinal direction.
  42 + for(size_t i = 0; i < kx; i++)
  43 + gaussx[i] = gauss1d((K) i, midgaussx, stdx);
  44 +
  45 + for(size_t i = 0; i < kx; i++)
  46 + gaussy[i] = gauss1d((K) i, midgaussy, stdy);
  47 +
  48 + for(size_t i = 0; i < kx; i++)
  49 + gaussz[i] = gauss1d((K) i, midgaussz, stdz);
  50 +
  51 + cpu_sepconv3(out, in, gaussx, gaussy, gaussz, dimx, dimy, dimz, kx, ky, kz);
  52 +
  53 + }
  54 +}
  55 +#endif
stim/math/filters/gradient.cuh 0 → 100644
  1 +#ifndef STIM_CUDA_GRADIENT_H
  2 +#define STIM_CUDA_GRADIENT_H
  3 +
  4 +#include <iostream>
  5 +#include <cuda.h>
  6 +#include <stim/cuda/cudatools.h>
  7 +
  8 +namespace stim{
  9 + namespace cuda{
  10 +
  11 + template<typename T>
  12 + __global__ void gradient_2d(T* out, T* in, int x, int y){
  13 +
  14 +
  15 + // calculate the 2D coordinates for this current thread.
  16 + int xi = blockIdx.x * blockDim.x + threadIdx.x;
  17 + int yi = blockIdx.y * blockDim.y + threadIdx.y;
  18 + // convert 2D coordinates to 1D
  19 + int i = yi * x + xi;
  20 +
  21 + //return if the pixel is outside of the image
  22 + if(xi >= x || yi >= y) return;
  23 +
  24 + //calculate indices for the forward difference
  25 + int i_xp = yi * x + (xi + 1);
  26 + int i_yp = (yi + 1) * x + xi;
  27 +
  28 + //calculate indices for the backward difference
  29 + int i_xn = yi * x + (xi - 1);
  30 + int i_yn = (yi - 1) * x + xi;
  31 +
  32 + //use forward differences if a coordinate is zero
  33 + if(xi == 0)
  34 + out[i * 2 + 0] = in[i_xp] - in[i];
  35 + if(yi == 0)
  36 + out[i * 2 + 1] = in[i_yp] - in[i];
  37 +
  38 + //use backward differences if the coordinate is at the maximum edge
  39 + if(xi == x-1)
  40 + out[i * 2 + 0] = in[i] - in[i_xn];
  41 + if(yi == y-1)
  42 + out[i * 2 + 1] = in[i] - in[i_yn];
  43 +
  44 + //otherwise use central differences
  45 + if(xi > 0 && xi < x-1)
  46 + out[i * 2 + 0] = (in[i_xp] - in[i_xn]) / 2;
  47 +
  48 + if(yi > 0 && yi < y-1)
  49 + out[i * 2 + 1] = (in[i_yp] - in[i_yn]) / 2;
  50 +
  51 + }
  52 +
  53 + template<typename T>
  54 + void gpu_gradient_2d(T* gpuGrad, T* gpuI, unsigned int x, unsigned int y){
  55 +
  56 + //get the maximum number of threads per block for the CUDA device
  57 + unsigned int max_threads = stim::maxThreadsPerBlock();
  58 + dim3 threads(max_threads, 1);
  59 + dim3 blocks(x/threads.x + 1 , y);
  60 +
  61 +
  62 + //call the GPU kernel to determine the gradient
  63 + gradient_2d<T> <<< blocks, threads >>>(gpuGrad, gpuI, x, y);
  64 +
  65 + }
  66 +
  67 + template<typename T>
  68 + void cpu_gradient_2d(T* out, T* in, unsigned int x, unsigned int y){
  69 +
  70 + //get the number of pixels in the image
  71 + unsigned int pixels = x * y;
  72 + unsigned int bytes = pixels * sizeof(T);
  73 +
  74 + //allocate space on the GPU for the input image
  75 + T* gpuIn;
  76 + HANDLE_ERROR(cudaMalloc(&gpuIn, bytes));
  77 +
  78 + //copy the image data to the GPU
  79 + HANDLE_ERROR(cudaMemcpy(gpuIn, in, bytes, cudaMemcpyHostToDevice));
  80 +
  81 + //allocate space on the GPU for the output gradient image
  82 + T* gpuOut;
  83 + cudaMalloc(&gpuOut, bytes * 2); //the output image will have two channels (x, y)
  84 +
  85 + //call the GPU version of this function
  86 + gpu_gradient_2d(gpuOut, gpuIn, x, y);
  87 +
  88 + //copy the results to the CPU
  89 + cudaMemcpy(out, gpuOut, bytes * 2, cudaMemcpyDeviceToHost);
  90 +
  91 + //free allocated memory
  92 + cudaFree(gpuOut);
  93 + cudaFree(gpuIn);
  94 + }
  95 +
  96 + }
  97 +}
  98 +
  99 +
  100 +#endif
0 \ No newline at end of file 101 \ No newline at end of file
stim/math/filters/resample2.cuh 0 → 100644
  1 +#ifndef STIM_CUDA_RESAMPLE2_H
  2 +#define STIM_CUDA_RESAMPLE2_H
  3 +
  4 +#include <stim/cuda/cudatools.h>
  5 +#include <stim/cuda/sharedmem.cuh>
  6 +
  7 +
  8 +///Downsamples a 2D image by a factor f using a box filter. Any pixels outside of the valid region
  9 +/// (for example if X%f != 0) are chopped.
  10 +template<typename T, typename K>
  11 +void cpu_resample2(T* out, T* in, size_t f, size_t sx, size_t sy) {
  12 +
  13 +}
  14 +
  15 +
  16 +#endif
0 \ No newline at end of file 17 \ No newline at end of file
stim/math/filters/sepconv2.h renamed to stim/math/filters/sepconv2.cuh
1 #ifndef STIM_CUDA_SEPCONV2_H 1 #ifndef STIM_CUDA_SEPCONV2_H
2 #define STIM_CUDA_SEPCONV2_H 2 #define STIM_CUDA_SEPCONV2_H
3 -#include <stim/math/filters/conv2.h> 3 +#include <stim/math/filters/conv2.cuh>
4 #ifdef __CUDACC__ 4 #ifdef __CUDACC__
5 #include <stim/cuda/cudatools.h> 5 #include <stim/cuda/cudatools.h>
6 #include <stim/cuda/sharedmem.cuh> 6 #include <stim/cuda/sharedmem.cuh>
@@ -20,12 +20,12 @@ namespace stim { @@ -20,12 +20,12 @@ namespace stim {
20 cudaDeviceProp p; 20 cudaDeviceProp p;
21 HANDLE_ERROR(cudaGetDeviceProperties(&p, 0)); 21 HANDLE_ERROR(cudaGetDeviceProperties(&p, 0));
22 size_t tmax = p.maxThreadsPerBlock; 22 size_t tmax = p.maxThreadsPerBlock;
23 - dim3 nt(sqrt(tmax), sqrt(tmax)); //calculate the block dimensions 23 + dim3 nt((unsigned int)sqrt(tmax), (unsigned int)sqrt(tmax)); //calculate the block dimensions
24 size_t X = sx - kx + 1; //calculate the x size of the output image 24 size_t X = sx - kx + 1; //calculate the x size of the output image
25 T* temp; //declare a temporary variable to store the intermediate image 25 T* temp; //declare a temporary variable to store the intermediate image
26 HANDLE_ERROR(cudaMalloc(&temp, X * sy * sizeof(T))); //allocate memory for the intermediate image 26 HANDLE_ERROR(cudaMalloc(&temp, X * sy * sizeof(T))); //allocate memory for the intermediate image
27 27
28 - dim3 nb(X / nt.x + 1, sy / nt.y + 1); //calculate the grid dimensions 28 + dim3 nb((unsigned int)(X / nt.x) + 1, (unsigned int)(sy / nt.y) + 1); //calculate the grid dimensions
29 size_t sm = (nt.x + kx - 1) * nt.y * sizeof(T); //shared memory bytes required to store block data 29 size_t sm = (nt.x + kx - 1) * nt.y * sizeof(T); //shared memory bytes required to store block data
30 if (sm > p.sharedMemPerBlock) { 30 if (sm > p.sharedMemPerBlock) {
31 std::cout << "Error in stim::gpu_conv2() - insufficient shared memory for this kernel." << std::endl; 31 std::cout << "Error in stim::gpu_conv2() - insufficient shared memory for this kernel." << std::endl;
@@ -34,7 +34,7 @@ namespace stim { @@ -34,7 +34,7 @@ namespace stim {
34 kernel_conv2 <<<nb, nt, sm>>> (temp, in, k0, sx, sy, kx, 1); //launch the kernel to compute the intermediate image 34 kernel_conv2 <<<nb, nt, sm>>> (temp, in, k0, sx, sy, kx, 1); //launch the kernel to compute the intermediate image
35 35
36 size_t Y = sy - ky + 1; //calculate the y size of the output image 36 size_t Y = sy - ky + 1; //calculate the y size of the output image
37 - nb.y = Y / nt.y + 1; //update the grid dimensions to reflect the Y-axis size of the output image 37 + nb.y = (unsigned int)(Y / nt.y) + 1; //update the grid dimensions to reflect the Y-axis size of the output image
38 sm = nt.x * (nt.y + ky - 1) * sizeof(T); //calculate the amount of shared memory needed for the second pass 38 sm = nt.x * (nt.y + ky - 1) * sizeof(T); //calculate the amount of shared memory needed for the second pass
39 if (sm > p.sharedMemPerBlock) { 39 if (sm > p.sharedMemPerBlock) {
40 std::cout << "Error in stim::gpu_conv2() - insufficient shared memory for this kernel." << std::endl; 40 std::cout << "Error in stim::gpu_conv2() - insufficient shared memory for this kernel." << std::endl;
@@ -86,4 +86,4 @@ namespace stim { @@ -86,4 +86,4 @@ namespace stim {
86 } 86 }
87 } 87 }
88 88
89 -#endif  
90 \ No newline at end of file 89 \ No newline at end of file
  90 +#endif
stim/math/filters/sepconv3.cuh 0 → 100644
  1 +#ifndef STIM_CUDA_SEPCONV3_H
  2 +#define STIM_CUDA_SEPCONV3_H
  3 +
  4 +#include <stim/math/filters/conv2.cuh>
  5 +#include <stim/math/filters/sepconv2.cuh>
  6 +#ifdef __CUDACC__
  7 + #include <stim/cuda/cudatools.h>
  8 + #include <stim/cuda/sharedmem.cuh>
  9 +#endif
  10 +
  11 +namespace stim
  12 +{
  13 +#ifdef __CUDACC__
  14 + template<typename T, typename K>
  15 + void gpu_sepconv3(T* out, T* in, K* k0, K* k1, K* k2, size_t dimx, size_t dimy, size_t dimz, size_t kx, size_t ky, size_t kz)
  16 +{
  17 +
  18 + size_t X = dimx - kx + 1;
  19 + size_t Y = dimy - ky + 1;
  20 + size_t Z = dimz - kz + 1;
  21 +
  22 + T* temp_out;
  23 + int idx_IN;
  24 + int idx_OUT;
  25 + HANDLE_ERROR(cudaMalloc(&temp_out, X*Y*dimz*sizeof(T)));
  26 +
  27 + for(int i = 0; i < dimz; i++)
  28 + {
  29 + idx_IN = (dimx*dimy)*i-i;
  30 + idx_OUT = (X*Y)*i-i;
  31 + gpu_sepconv2(&temp_out[idx_OUT], &in[idx_IN], k0, k1, dimx, dimy, kx, ky);
  32 + }
  33 +
  34 + cudaDeviceProp p;
  35 + HANDLE_ERROR(cudaGetDeviceProperties(&p, 0));
  36 + size_t tmax = p.maxThreadsPerBlock;
  37 +
  38 + dim3 numThreads(sqrt(tmax), sqrt(tmax));
  39 + dim3 numBlocks(X*Y/numThreads.x +1, dimz/numThreads.y + 1);
  40 + size_t sharedMem = (numThreads.x + kz - 1) * numThreads.y * sizeof(T);
  41 + if(sharedMem > p.sharedMemPerBlock)
  42 + {
  43 + std::cout << "Error in stim::gpu_sepconv3() - insufficient shared memory for this kernel." << std::endl;
  44 + exit(1);
  45 + }
  46 + kernel_conv2 <<< numBlocks, numThreads, sharedMem >>> (out, temp_out, k2, X*Y, dimz, 1, kz);
  47 + HANDLE_ERROR(cudaFree(temp_out));
  48 +
  49 +
  50 +}
  51 +#endif
  52 +
  53 + //Performs a separable convolution of a 3D image. Only valid pixels based on the kernel ar e returned.
  54 + // As a result, the output image will be smaller than the input image by (kx-1, ky-1 , kz-1)
  55 + //@param out is a pointer to the output image
  56 + //@param in is a pointer to the input image
  57 + //@param kx is the x-axis convolution filter
  58 + //@param ky is the y-axis convolution filter
  59 + //@param kz is the z-axis convolution filter
  60 + //@param dimx is the size of the input image along X
  61 + //@param dimy is the size of the input image along Y
  62 + //@param dimz is the size of the input image along Z
  63 + //@param kx is the size of the kernel along X
  64 + //@param ky is the size of the kernel along Y
  65 + //@param kz is the size of the kernel along Z
  66 +
  67 + template <typename T, typename K>
  68 + void cpu_sepconv3(T* out, T* in, K* k0, K* k1, K* k2, size_t dimx, size_t dimy, size_t dimz, size_t kx, size_t ky, size_t kz)
  69 + {
  70 + //Set up the sizes of the new output, which will be kx, ky, kz, smaller than the i nput.
  71 + size_t X = dimx - kx + 1;
  72 + size_t Y = dimy - ky + 1;
  73 + size_t Z = dimz - kz + 1;
  74 +
  75 +#ifdef __CUDACC__
  76 + ///Set up all of the memory on the GPU
  77 + T* gpu_in;
  78 + HANDLE_ERROR(cudaMalloc(&gpu_in, dimx*dimy*dimz*sizeof(T)));
  79 + HANDLE_ERROR(cudaMemcpy(gpu_in, in, dimx*dimy*dimz*sizeof(T),cudaMemcpyHostToDevice));
  80 + K* gpu_kx;
  81 + HANDLE_ERROR(cudaMalloc(&gpu_kx, kx*sizeof(K)));
  82 + HANDLE_ERROR(cudaMemcpy(gpu_kx, k0, kx*sizeof(K),cudaMemcpyHostToDevice));
  83 + K* gpu_ky;
  84 + HANDLE_ERROR(cudaMalloc(&gpu_ky, ky*sizeof(K)));
  85 + HANDLE_ERROR(cudaMemcpy(gpu_ky, k1, ky*sizeof(K),cudaMemcpyHostToDevice));
  86 + K* gpu_kz;
  87 + HANDLE_ERROR(cudaMalloc(&gpu_kz, kz*sizeof(K)));
  88 + HANDLE_ERROR(cudaMemcpy(gpu_kz, k2, kz*sizeof(K),cudaMemcpyHostToDevice));
  89 + T* gpu_out;
  90 + HANDLE_ERROR(cudaMalloc(&gpu_out, X * Y * Z*sizeof(T)));
  91 +
  92 + ///run the kernel
  93 + gpu_sepconv3(gpu_out, gpu_in, gpu_kx, gpu_ky, gpu_kz, dimx, dimy, dimz, kx, ky, kz);
  94 +
  95 + ///Copy the output
  96 + HANDLE_ERROR(cudaMemcpy(out, gpu_out, X*Y*Z*sizeof(T), cudaMemcpyDeviceToHost));
  97 +
  98 + ///Free all the memory used.
  99 + HANDLE_ERROR(cudaFree(gpu_in));
  100 + HANDLE_ERROR(cudaFree(gpu_kx));
  101 + HANDLE_ERROR(cudaFree(gpu_ky));
  102 + HANDLE_ERROR(cudaFree(gpu_kz));
  103 + HANDLE_ERROR(cudaFree(gpu_out));
  104 +#else
  105 + T* temp = (T*) malloc(X * dimy * sizeof(T));
  106 + T* temp3 = (T*) malloc(X * Y * dimz * sizeof(T));
  107 + for(int i = 0; i < dimz; i++)
  108 + {
  109 + idx_IN = (dimx*dimy)*i-i;
  110 + idx_OUT = (X*Y)*i-i;
  111 + cpu_conv2(temp, &in[idx_IN], k0, dimx, dimy, kx, 1)
  112 + cpu_conv2(&temp3[idx_OUT], temp, k1, X, dimy, 1, ky);
  113 + }
  114 + cpu_conv2(out, temp, k2, X*Y, dimz, 1, kz);
  115 + free(temp);
  116 + free(temp3);
  117 +
  118 +#endif
  119 + }
  120 +}
  121 +
  122 +
  123 +#endif
stim/math/matrix.h
1 -#ifndef RTS_MATRIX_H  
2 -#define RTS_MATRIX_H 1 +#ifndef STIM_MATRIX_H
  2 +#define STIM_MATRIX_H
3 3
4 //#include "rts/vector.h" 4 //#include "rts/vector.h"
5 #include <string.h> 5 #include <string.h>
6 #include <iostream> 6 #include <iostream>
  7 +#include <fstream>
7 #include <stim/math/vector.h> 8 #include <stim/math/vector.h>
8 #include <stim/math/vec3.h> 9 #include <stim/math/vec3.h>
9 -#include <stim/cuda/cudatools/callable.h> 10 +//#include <stim/cuda/cudatools/callable.h>
10 11
11 namespace stim{ 12 namespace stim{
12 13
13 -template <class T, int N>  
14 -struct matrix  
15 -{ 14 + enum mat4Format {
  15 + mat4_float64,
  16 + mat4_float32,
  17 + mat4_int32,
  18 + mat4_int16,
  19 + mat4_uint16,
  20 + mat4_uint8,
  21 + mat4_float //floating point type, determined automatically
  22 + };
  23 +
  24 + static size_t mat4Format_size(mat4Format f){
  25 + switch(f){
  26 + case mat4_float64: return 8;
  27 + case mat4_float32:
  28 + case mat4_int32: return 4;
  29 + case mat4_int16:
  30 + case mat4_uint16: return 2;
  31 + case mat4_uint8: return 1;
  32 + default: return 0;
  33 + }
  34 + }
  35 +
  36 + //class encapsulates a mat4 file, and can be used to write multiple matrices to a single mat4 file
  37 + class mat4file {
  38 + std::ofstream matfile;
  39 +
  40 + public:
  41 + /// Constructor opens a mat4 file for writing
  42 + mat4file(std::string filename) {
  43 + matfile.open(filename, std::ios::binary);
  44 + }
  45 +
  46 + bool is_open() {
  47 + return matfile.is_open();
  48 + }
  49 +
  50 + void close() {
  51 + matfile.close();
  52 + }
  53 +
  54 + bool writemat(char* data, std::string varname, size_t sx, size_t sy, mat4Format format) {
  55 + //save the matrix file here (use the mat4 function above)
  56 + //data format: https://maxwell.ict.griffith.edu.au/spl/matlab-page/matfile_format.pdf (page 32)
  57 +
  58 + int MOPT = 0; //initialize the MOPT type value to zero
  59 + int m = 0; //little endian
  60 + int o = 0; //reserved, always 0
  61 + int p = format;
  62 + int t = 0;
  63 + MOPT = m * 1000 + o * 100 + p * 10 + t; //calculate the type value
  64 + int mrows = (int)sx;
  65 + int ncols = (int)sy;
  66 + int imagf = 0; //assume real (for now)
  67 + varname.push_back('\0'); //add a null to the string
  68 + int namlen = (int)varname.size(); //calculate the name size
  69 +
  70 + size_t bytes = sx * sy * mat4Format_size(format);
  71 + matfile.write((char*)&MOPT, 4);
  72 + matfile.write((char*)&mrows, 4);
  73 + matfile.write((char*)&ncols, 4);
  74 + matfile.write((char*)&imagf, 4);
  75 + matfile.write((char*)&namlen, 4);
  76 + matfile.write((char*)&varname[0], namlen);
  77 + matfile.write((char*)data, bytes); //write the matrix data
  78 + return is_open();
  79 + }
  80 + };
  81 +
  82 + static void save_mat4(char* data, std::string filename, std::string varname, size_t sx, size_t sy, mat4Format format){
  83 + mat4file outfile(filename); //create a mat4 file object
  84 + if (outfile.is_open()) { //if the file is open
  85 + outfile.writemat(data, varname, sx, sy, format); //write the matrix
  86 + outfile.close(); //close the file
  87 + }
  88 + }
  89 +
  90 +template <class T>
  91 +class matrix {
16 //the matrix will be stored in column-major order (compatible with OpenGL) 92 //the matrix will be stored in column-major order (compatible with OpenGL)
17 - T M[N*N]; 93 + T* M; //pointer to the matrix data
  94 + size_t R; //number of rows
  95 + size_t C; //number of colums
18 96
19 - CUDA_CALLABLE matrix()  
20 - {  
21 - for(int r=0; r<N; r++)  
22 - for(int c=0; c<N; c++)  
23 - if(r == c)  
24 - (*this)(r, c) = 1;  
25 - else  
26 - (*this)(r, c) = 0; 97 + size_t bytes() {
  98 + return R * C * sizeof(T); //return the number of bytes of matrix data
27 } 99 }
  100 + /*void init(size_t rows, size_t cols){
  101 + R = rows;
  102 + C = cols;
  103 + if (R == 0 || C == 0) M = NULL;
  104 + else
  105 + M = (T*)malloc(R * C * sizeof(T)); //allocate space for the matrix
  106 + }*/
28 107
29 - CUDA_CALLABLE matrix(T rhs[N*N])  
30 - {  
31 - memcpy(M,rhs, sizeof(T)*N*N); 108 + T get(const size_t row, const size_t col) const {
  109 + if (row >= R || col >= C) {
  110 + std::cout << "ERROR: row or column out of range." << std::endl;
  111 + exit(1);
  112 + }
  113 + return M[col * R + row];
32 } 114 }
33 115
34 - CUDA_CALLABLE matrix<T,N> set(T rhs[N*N])  
35 - {  
36 - memcpy(M, rhs, sizeof(T)*N*N);  
37 - return *this; 116 + T& at(size_t row, size_t col){
  117 + if (row >= R || col >= C) {
  118 + std::cout << "ERROR: row or column out of range." << std::endl;
  119 + exit(1);
  120 + }
  121 + return M[col * R + row];
  122 + }
  123 +
  124 +public:
  125 + matrix() {
  126 + R = 0;
  127 + C = 0;
  128 + M = NULL;
  129 + }
  130 +
  131 + matrix(size_t rows, size_t cols) {
  132 + R = rows;
  133 + C = cols;
  134 + M = NULL;
  135 + if (R * C > 0)
  136 + M = (T*) malloc(R * C * sizeof(T));
  137 + }
  138 +
  139 + matrix(size_t rows, size_t cols, const T* data) {
  140 + R = rows;
  141 + C = cols;
  142 + M = NULL;
  143 + if (R * C > 0)
  144 + M = (T*)malloc(R * C * sizeof(T));
  145 + memcpy(M, data, R * C * sizeof(T));
  146 + }
  147 +
  148 + matrix(const matrix<T>& cpy){
  149 + M = NULL;
  150 + if (cpy.R * cpy.C > 0)
  151 + M = (T*)malloc(cpy.R * cpy.C * sizeof(T));
  152 + memcpy(M, cpy.M, cpy.R * cpy.C * sizeof(T));
  153 +
  154 + R = cpy.R;
  155 + C = cpy.C;
  156 + }
  157 +
  158 + ~matrix() {
  159 + if(M) free(M);
  160 + M = NULL;
  161 + R = C = 0;
  162 + }
  163 +
  164 + size_t rows() const {
  165 + return R;
38 } 166 }
39 167
40 - CUDA_CALLABLE T& operator()(int row, int col)  
41 - {  
42 - return M[col * N + row]; 168 + size_t cols() const {
  169 + return C;
43 } 170 }
44 171
45 - CUDA_CALLABLE matrix<T, N> operator=(T rhs)  
46 - {  
47 - int Nsq = N*N;  
48 - for(int i=0; i<Nsq; i++)  
49 - M[i] = rhs; 172 + T& operator()(size_t row, size_t col) {
  173 + return at(row, col);
  174 + }
  175 +
  176 + matrix<T>& operator=(const T rhs) {
  177 + //init(R, C);
  178 + size_t N = R * C;
  179 + for(size_t n=0; n<N; n++)
  180 + M[n] = rhs;
50 181
51 return *this; 182 return *this;
52 } 183 }
53 184
54 - template<typename Y>  
55 - vec<Y> operator*(vec<Y> rhs){  
56 - unsigned int M = rhs.size(); 185 + matrix<T>& operator=(const matrix<T>& rhs){
  186 + if (this != &rhs) { //if the matrix isn't self-assigned
  187 + T* new_matrix = new T[rhs.R * rhs.C]; //allocate new resources
  188 + memcpy(new_matrix, rhs.M, rhs.R * rhs.C * sizeof(T)); //copy the matrix
  189 +
  190 + delete[] M; //delete the previous array
  191 + M = new_matrix;
  192 + R = rhs.R;
  193 + C = rhs.C;
  194 + }
  195 + return *this;
  196 + }
  197 +
  198 + //element-wise operations
  199 + matrix<T> operator+(const T rhs) const {
  200 + matrix<T> result(R, C); //create a result matrix
  201 + size_t N = R * C;
  202 +
  203 + for(int i=0; i<N; i++)
  204 + result.M[i] = M[i] + rhs; //calculate the operation and assign to result
  205 +
  206 + return result;
  207 + }
  208 +
  209 + matrix<T> operator+(const matrix<T> rhs) const {
  210 + if (R != rhs.R || C != rhs.C) {
  211 + std::cout << "ERROR: addition is only defined for matrices that are the same size." << std::endl;
  212 + exit(1);
  213 + }
  214 + matrix<T> result(R, C); //create a result matrix
  215 + size_t N = R * C;
  216 +
  217 + for (int i = 0; i < N; i++)
  218 + result.M[i] = M[i] + rhs.M[i]; //calculate the operation and assign to result
  219 +
  220 + return result;
  221 + }
  222 +
  223 + matrix<T> operator-(const T rhs) const {
  224 + return operator+(-rhs); //add the negative of rhs
  225 + }
  226 +
  227 + matrix<T> operator-(const matrix<T> rhs) const {
  228 + return operator+(-rhs);
  229 + }
  230 +
  231 + matrix<T> operator-() const {
  232 + matrix<T> result(R, C); //create a result matrix
  233 + size_t N = R * C;
  234 +
  235 + for (int i = 0; i < N; i++)
  236 + result.M[i] = -M[i]; //calculate the operation and assign to result
  237 +
  238 + return result;
  239 + }
  240 +
  241 + matrix<T> operator*(const T rhs) const {
  242 + matrix<T> result(R, C); //create a result matrix
  243 + size_t N = R * C;
  244 +
  245 + for(int i=0; i<N; i++)
  246 + result.M[i] = M[i] * rhs; //calculate the operation and assign to result
  247 +
  248 + return result;
  249 + }
  250 +
  251 + matrix<T> operator/(const T rhs) const {
  252 + matrix<T> result(R, C); //create a result matrix
  253 + size_t N = R * C;
  254 +
  255 + for(int i=0; i<N; i++)
  256 + result.M[i] = M[i] / rhs; //calculate the operation and assign to result
  257 +
  258 + return result;
  259 + }
  260 +
  261 + //matrix multiplication
  262 + matrix<T> operator*(const matrix<T> rhs) const {
  263 + if(C != rhs.R){
  264 + std::cout<<"ERROR: matrix multiplication is undefined for matrices of size ";
  265 + std::cout<<"[ "<<R<<" x "<<C<<" ] and [ "<<rhs.R<<" x "<<rhs.C<<"]"<<std::endl;
  266 + exit(1);
  267 + }
57 268
58 - vec<Y> result;  
59 - result.resize(M); 269 + matrix<T> result(R, rhs.C); //create the output matrix
  270 + T inner; //stores the running inner product
  271 + size_t c, r, i;
  272 + for(c = 0; c < rhs.C; c++){
  273 + for(r = 0; r < R; r++){
  274 + inner = (T)0;
  275 + for(i = 0; i < C; i++){
  276 + inner += get(r, i) * rhs.get(i, c);
  277 + }
  278 + result.M[c * R + r] = inner;
  279 + }
  280 + }
  281 + return result;
  282 + }
60 283
61 - for(int r=0; r<M; r++)  
62 - for(int c=0; c<M; c++)  
63 - result[r] += (*this)(r, c) * rhs[c]; 284 + //returns a pointer to the raw matrix data (in column major format)
  285 + T* data(){
  286 + return M;
  287 + }
64 288
  289 + //return a transposed matrix
  290 + matrix<T> transpose() const {
  291 + matrix<T> result(C, R);
  292 + size_t c, r;
  293 + for(c = 0; c < C; c++){
  294 + for(r = 0; r < R; r++){
  295 + result.M[r * C + c] = M[c * R + r];
  296 + }
  297 + }
65 return result; 298 return result;
66 } 299 }
67 300
68 - template<typename Y>  
69 - CUDA_CALLABLE vec3<Y> operator*(vec3<Y> rhs){  
70 - vec3<Y> result = 0;  
71 - for(int r=0; r<3; r++)  
72 - for(int c=0; c<3; c++)  
73 - result[r] += (*this)(r, c) * rhs[c]; 301 + // Reshapes the matrix in place
  302 + void reshape(size_t rows, size_t cols) {
  303 + R = rows;
  304 + C = cols;
  305 + }
  306 +
  307 + ///Calculate and return the determinant of the matrix
  308 + T det() const {
  309 + if (R != C) {
  310 + std::cout << "ERROR: a determinant can only be calculated for a square matrix." << std::endl;
  311 + exit(1);
  312 + }
  313 + if (R == 1) return M[0]; //if the matrix only contains one value, return it
74 314
  315 + int r, c, ri, cia, cib;
  316 + T a = 0;
  317 + T b = 0;
  318 + for (c = 0; c < (int)C; c++) {
  319 + for (r = 0; r < R; r++) {
  320 + ri = r;
  321 + cia = (r + c) % (int)C;
  322 + cib = ((int)C - 1 - r) % (int)C;
  323 + a += get(ri, cia);
  324 + b += get(ri, cib);
  325 + }
  326 + }
  327 + return a - b;
  328 + }
  329 +
  330 + /// Sum all elements in the matrix
  331 + T sum() const {
  332 + size_t N = R * C; //calculate the number of elements in the matrix
  333 + T s = (T)0; //allocate a register to store the sum
  334 + for (size_t n = 0; n < N; n++) s += M[n]; //perform the summation
  335 + return s;
  336 + }
  337 +
  338 + /// Sort rows of the matrix by the specified indices
  339 + matrix<T> sort_rows(size_t* idx) const {
  340 + matrix<T> result(C, R); //create the output matrix
  341 + size_t r, c;
  342 + for (c = 0; c < C; c++) { //for each column
  343 + for (r = 0; r < R; r++) { //for each row element
  344 + result.M[c * R + r] = M[c * R + idx[r]]; //copy each element of the row into its new position
  345 + }
  346 + }
  347 + return result;
  348 + }
  349 +
  350 + /// Sort columns of the matrix by the specified indices
  351 + matrix<T> sort_cols(size_t* idx, size_t data_type = mat4_float) const {
  352 + matrix<T> result(C, R);
  353 + size_t c;
  354 + for (c = 0; c < C; c++) { //for each column
  355 + memcpy(&result.M[c * R], &M[idx[c] * R], sizeof(T) * R); //copy the entire column from this matrix to the appropriate location
  356 + }
75 return result; 357 return result;
76 } 358 }
77 359
78 - std::string toStr()  
79 - { 360 + /// Return the column specified by index i
  361 + matrix<T> col(size_t i) {
  362 + matrix<T> c(R, 1); //create a single column matrix
  363 + memcpy(c.data(), &data()[R*i], C * sizeof(T)); //copy the column
  364 + return c;
  365 + }
  366 +
  367 + /// Return the row specified by index i
  368 + matrix<T> row(size_t i) {
  369 + matrix<T> r(1, C); //create a single row matrix
  370 + for (size_t c = 0; c < C; c++)
  371 + r(0, c) = at(i, c);
  372 + return r;
  373 + }
  374 +
  375 + std::string toStr() const {
80 std::stringstream ss; 376 std::stringstream ss;
81 377
82 - for(int r = 0; r < N; r++)  
83 - { 378 + for(int r = 0; r < R; r++) {
84 ss << "| "; 379 ss << "| ";
85 - for(int c=0; c<N; c++)  
86 - {  
87 - ss << (*this)(r, c) << " "; 380 + for(int c=0; c<C; c++) {
  381 + ss << M[c * R + r] << " ";
88 } 382 }
89 ss << "|" << std::endl; 383 ss << "|" << std::endl;
90 } 384 }
91 -  
92 return ss.str(); 385 return ss.str();
93 } 386 }
  387 +
  388 + void csv(std::ostream& out) const {
  389 + //std::stringstream csvss;
  390 + for (size_t i = 0; i < R; i++) {
  391 + out << std::fixed << M[i];
  392 + for (size_t j = 1; j < C; j++)
  393 + out << ", " << std::fixed << M[j * R + i];
  394 + out << std::endl;
  395 + }
  396 + //return csvss.str();
  397 + }
  398 +
  399 + std::string csv() const {
  400 + std::stringstream csvss;
  401 + int digits = std::numeric_limits<double>::max_digits10;
  402 + csvss.precision(digits);
  403 + csv(csvss);
  404 + return csvss.str();
  405 + }
  406 +
  407 +
  408 +
  409 + //save the data as a CSV file
  410 + void csv(std::string filename) const {
  411 + std::ofstream basisfile(filename.c_str());
  412 + basisfile << csv();
  413 + basisfile.close();
  414 + }
  415 +
  416 + static matrix<T> I(size_t N) {
  417 + matrix<T> result(N, N); //create the identity matrix
  418 + memset(result.M, 0, N * N * sizeof(T)); //set the entire matrix to zero
  419 + for (size_t n = 0; n < N; n++) {
  420 + result(n, n) = (T)1; //set the diagonal component to 1
  421 + }
  422 + return result;
  423 + }
  424 +
  425 + //loads a matrix from a stream in CSV format
  426 + void csv(std::istream& in) {
  427 + size_t c, r;
  428 + T v;
  429 + for (r = 0; r < R; r++) {
  430 + for (c = 0; c < C; c++) {
  431 + in >> v;
  432 + if (in.peek() == ',') in.seekg(1, std::ios::cur);
  433 + at(r, c) = v;;
  434 + }
  435 + }
  436 + }
  437 +
  438 + void raw(std::string filename) {
  439 + std::ofstream out(filename, std::ios::binary);
  440 + if (out) {
  441 + out.write((char*)data(), rows() * cols() * sizeof(T));
  442 + out.close();
  443 + }
  444 + }
  445 +
  446 + void mat4(stim::mat4file& file, std::string name = std::string("unknown"), mat4Format format = mat4_float) {
  447 + //make sure the matrix name is valid (only numbers and letters, with a letter at the beginning
  448 + for (size_t c = 0; c < name.size(); c++) {
  449 + if (name[c] < 48 || //if the character isn't a number or letter, replace it with '_'
  450 + (name[c] > 57 && name[c] < 65) ||
  451 + (name[c] > 90 && name[c] < 97) ||
  452 + (name[c] > 122)) {
  453 + name[c] = '_';
  454 + }
  455 + }
  456 + if (name[0] < 65 ||
  457 + (name[0] > 91 && name[0] < 97) ||
  458 + name[0] > 122) {
  459 + name = std::string("m") + name;
  460 + }
  461 + if (format == mat4_float) {
  462 + if (sizeof(T) == 4) format = mat4_float32;
  463 + else if (sizeof(T) == 8) format = mat4_float64;
  464 + else {
  465 + std::cout << "stim::matrix ERROR - incorrect format specified" << std::endl;
  466 + exit(1);
  467 + }
  468 + }
  469 + //the name is now valid
  470 +
  471 + //if the size of the array is more than 100,000,000 elements, the matrix isn't supported
  472 + if (rows() * cols() > 100000000) { //break the matrix up into multiple parts
  473 + //mat4file out(filename); //create a mat4 object to write the matrix
  474 + if (file.is_open()) {
  475 + if (rows() < 100000000) { //if the size of the row is less than 100,000,000, split the matrix up by columns
  476 + size_t ncols = 100000000 / rows(); //calculate the number of columns that can fit in one matrix
  477 + size_t nmat = (size_t)std::ceil((double)cols() / (double)ncols); //calculate the number of matrices required
  478 + for (size_t m = 0; m < nmat; m++) { //for each matrix
  479 + std::stringstream ss;
  480 + ss << name << "_part_" << m + 1;
  481 + if (m == nmat - 1)
  482 + file.writemat((char*)(data() + m * ncols * rows()), ss.str(), rows(), cols() - m * ncols, format);
  483 + else
  484 + file.writemat((char*)(data() + m * ncols * rows()), ss.str(), rows(), ncols, format);
  485 + }
  486 + }
  487 + }
  488 + }
  489 + //call the mat4 subroutine
  490 + else
  491 + //stim::save_mat4((char*)M, filename, name, rows(), cols(), format);
  492 + file.writemat((char*)data(), name, rows(), cols(), format);
  493 + }
  494 +
  495 + // saves the matrix as a Level-4 MATLAB file
  496 + void mat4(std::string filename, std::string name = std::string("unknown"), mat4Format format = mat4_float) {
  497 + stim::mat4file matfile(filename);
  498 +
  499 + if (matfile.is_open()) {
  500 + mat4(matfile, name, format);
  501 + matfile.close();
  502 + }
  503 + }
94 }; 504 };
95 505
96 } //end namespace rts 506 } //end namespace rts
97 507
98 -template <typename T, int N>  
99 -std::ostream& operator<<(std::ostream& os, stim::matrix<T, N> M)  
100 -{  
101 - os<<M.toStr();  
102 - return os;  
103 -}  
104 -  
105 -//#if __GNUC__ > 3 && __GNUC_MINOR__ > 7  
106 -//template<class T, int N> using rtsMatrix = rts::matrix<T, N>;  
107 -//#endif  
108 508
109 #endif 509 #endif
stim/math/matrix_sq.h 0 → 100644
  1 +#ifndef STIM_MATRIX_SQ_H
  2 +#define STIM_MATRIX_SQ_H
  3 +
  4 +//#include "rts/vector.h"
  5 +#include <string.h>
  6 +#include <iostream>
  7 +#include <stim/math/vector.h>
  8 +#include <stim/math/vec3.h>
  9 +#include <stim/cuda/cudatools/callable.h>
  10 +
  11 +namespace stim{
  12 +
  13 +template <class T, int N>
  14 +struct matrix_sq
  15 +{
  16 + //the matrix will be stored in column-major order (compatible with OpenGL)
  17 + T M[N*N];
  18 +
  19 + CUDA_CALLABLE matrix_sq()
  20 + {
  21 + for(int r=0; r<N; r++)
  22 + for(int c=0; c<N; c++)
  23 + if(r == c)
  24 + (*this)(r, c) = 1;
  25 + else
  26 + (*this)(r, c) = 0;
  27 + }
  28 +
  29 + CUDA_CALLABLE matrix_sq(T rhs[N*N])
  30 + {
  31 + memcpy(M,rhs, sizeof(T)*N*N);
  32 + }
  33 +
  34 + CUDA_CALLABLE matrix_sq<T,N> set(T rhs[N*N])
  35 + {
  36 + memcpy(M, rhs, sizeof(T)*N*N);
  37 + return *this;
  38 + }
  39 +
  40 + //create a symmetric matrix given the rhs values, given in column-major order
  41 + CUDA_CALLABLE void setsym(T rhs[(N*N+N)/2]){
  42 + const size_t L = (N*N+N)/2; //store the number of values
  43 +
  44 + size_t r, c;
  45 + r = c = 0;
  46 + for(size_t i = 0; i < L; i++){ //for each value
  47 + if(r == c) M[c * N + r] = rhs[i];
  48 + else M[c*N + r] = M[r * N + c] = rhs[i];
  49 + r++;
  50 + if(r == N) r = ++c;
  51 + }
  52 + }
  53 +
  54 + CUDA_CALLABLE T& operator()(int row, int col)
  55 + {
  56 + return M[col * N + row];
  57 + }
  58 +
  59 + CUDA_CALLABLE matrix_sq<T, N> operator=(T rhs)
  60 + {
  61 + int Nsq = N*N;
  62 + for(int i=0; i<Nsq; i++)
  63 + M[i] = rhs;
  64 +
  65 + return *this;
  66 + }
  67 +
  68 + // M - rhs*I
  69 + CUDA_CALLABLE matrix_sq<T, N> operator-(T rhs)
  70 + {
  71 + for(int i=0; i<N; i++)
  72 + for(int j=0 ; j<N; j++)
  73 + if(i == j)
  74 + M[i*N+j] -= rhs;
  75 + return *this;
  76 + }
  77 +
  78 + template<typename Y>
  79 + vec<Y> operator*(vec<Y> rhs){
  80 + unsigned int M = rhs.size();
  81 +
  82 + vec<Y> result;
  83 + result.resize(M);
  84 +
  85 + for(int r=0; r<M; r++)
  86 + for(int c=0; c<M; c++)
  87 + result[r] += (*this)(r, c) * rhs[c];
  88 +
  89 + return result;
  90 + }
  91 +
  92 + template<typename Y>
  93 + CUDA_CALLABLE vec3<Y> operator*(vec3<Y> rhs){
  94 + vec3<Y> result = 0;
  95 + for(int r=0; r<3; r++)
  96 + for(int c=0; c<3; c++)
  97 + result[r] += (*this)(r, c) * rhs[c];
  98 +
  99 + return result;
  100 + }
  101 +
  102 + std::string toStr()
  103 + {
  104 + std::stringstream ss;
  105 +
  106 + for(int r = 0; r < N; r++)
  107 + {
  108 + ss << "| ";
  109 + for(int c=0; c<N; c++)
  110 + {
  111 + ss << (*this)(r, c) << " ";
  112 + }
  113 + ss << "|" << std::endl;
  114 + }
  115 +
  116 + return ss.str();
  117 + }
  118 +
  119 + static matrix_sq<T, N> identity() {
  120 + matrix_sq<T, N> I;
  121 + I = 0;
  122 + for (size_t i = 0; i < N; i++)
  123 + I.M[i * N + i] = 1;
  124 + return I;
  125 + }
  126 +};
  127 +
  128 +} //end namespace rts
  129 +
  130 +template <typename T, int N>
  131 +std::ostream& operator<<(std::ostream& os, stim::matrix_sq<T, N> M)
  132 +{
  133 + os<<M.toStr();
  134 + return os;
  135 +}
  136 +
  137 +//#if __GNUC__ > 3 && __GNUC_MINOR__ > 7
  138 +//template<class T, int N> using rtsMatrix = rts::matrix<T, N>;
  139 +//#endif
  140 +
  141 +#endif
stim/math/matrix_sym.h 0 → 100644
  1 +#ifndef STIM_MATRIX_SYM_H
  2 +#define STIM_MATRIX_SYM_H
  3 +
  4 +#include <stim/cuda/cudatools/callable.h>
  5 +#include <stim/math/matrix.h>
  6 +
  7 +/* This class represents a rank 2, 3-dimensional tensor viable
  8 +for representing tensor fields such as structure and diffusion tensors
  9 +*/
  10 +namespace stim{
  11 +
  12 +template <typename T, int D>
  13 +class matrix_sym{
  14 +
  15 +protected:
  16 + //values are stored in column-major order as a lower-triangular matrix
  17 + T M[D*(D + 1)/2];
  18 +
  19 + static size_t idx(size_t r, size_t c) {
  20 + //if the index is in the upper-triangular portion, swap the indices
  21 + if(r < c){
  22 + size_t t = r;
  23 + r = c;
  24 + c = t;
  25 + }
  26 +
  27 + size_t ci = (c + 1) * (D + (D - c))/2 - 1; //index to the end of column c
  28 + size_t i = ci - (D - r - 1);
  29 + return i;
  30 + }
  31 +
  32 + //calculate the row and column given an index
  33 + //static void indices(size_t& r, size_t& c, size_t idx) {
  34 + // size_t col = 0;
  35 + // for ( ; col < D; col++)
  36 + // if(idx <= ((D - col + D) * (col + 1)/2 - 1))
  37 + // break;
  38 +
  39 + // c = col;
  40 + // size_t ci = (D - (col - 1) + D) * col / 2 - 1; //index to the end of last column col -1
  41 + // r = idx - ci + c - 1;
  42 + //}
  43 + static void indices(size_t& r, size_t& c, size_t idx) {
  44 + size_t cf = -1/2 * sqrt(4 * D * D + 4 * D - (7 + 8 * idx)) + D - 1/2;
  45 + c = ceil(cf);
  46 + r = idx - D * c + c * (c + 1) / 2;
  47 + }
  48 +
  49 +public:
  50 + //return the symmetric matrix associated with this tensor
  51 + stim::matrix<T> mat() {
  52 + stim::matrix<T> r;
  53 + r.setsym(M);
  54 + return r;
  55 + }
  56 +
  57 + CUDA_CALLABLE T& operator()(int r, int c) {
  58 + return M[idx(r, c)];
  59 + }
  60 +
  61 + CUDA_CALLABLE matrix_sym<T, D> operator=(T rhs) {
  62 + int Nsq = D*(D+1)/2;
  63 + for(int i=0; i<Nsq; i++)
  64 + M[i] = rhs;
  65 +
  66 + return *this;
  67 + }
  68 +
  69 + CUDA_CALLABLE matrix_sym<T, D> operator=(matrix_sym<T, D> rhs) {
  70 + size_t N = D * (D + 1) / 2;
  71 + for (size_t i = 0; i < N; i++) M[i] = rhs.M[i];
  72 + return *this;
  73 + }
  74 +
  75 + CUDA_CALLABLE T trace() {
  76 + T tr = 0;
  77 + for (size_t i = 0; i < D; i++) //for each diagonal value
  78 + tr += M[idx(i, i)]; //add the value on the diagonal
  79 + return tr;
  80 + }
  81 + // overload matrix multiply scalar
  82 + CUDA_CALLABLE void operator_product(matrix_sym<T, D> &B, T rhs) {
  83 + int Nsq = D*(D+1)/2;
  84 + for(int i=0; i<Nsq; i++)
  85 + B.M[i] *= rhs;
  86 + }
  87 +
  88 + //return the tensor as a string
  89 + std::string str() {
  90 + std::stringstream ss;
  91 + for(int r = 0; r < D; r++){
  92 + ss << "| ";
  93 + for(int c=0; c<D; c++)
  94 + {
  95 + ss << (*this)(r, c) << " ";
  96 + }
  97 + ss << "|" << std::endl;
  98 + }
  99 +
  100 + return ss.str();
  101 + }
  102 +
  103 + //returns an identity matrix
  104 + static matrix_sym<T, D> identity() {
  105 + matrix_sym<T, D> I;
  106 + I = 0;
  107 + for (size_t i = 0; i < D; i++)
  108 + I.M[matrix_sym<T, D>::idx(i, i)] = 1;
  109 + return I;
  110 + }
  111 +};
  112 +
  113 +
  114 +
  115 +} //end namespace stim
  116 +
  117 +
  118 +#endif
@@ -188,9 +188,9 @@ class plane @@ -188,9 +188,9 @@ class plane
188 { 188 {
189 quaternion<T> q; 189 quaternion<T> q;
190 q.CreateRotation(N, n); 190 q.CreateRotation(N, n);
191 -  
192 - N = q.toMatrix3() * N;  
193 - U = q.toMatrix3() * U; 191 + matrix_sq<T, 3> M = q.toMatrix3();
  192 + N = M * N;
  193 + U = M * U;
194 194
195 } 195 }
196 196
stim/math/quaternion.h
1 #ifndef RTS_QUATERNION_H 1 #ifndef RTS_QUATERNION_H
2 #define RTS_QUATERNION_H 2 #define RTS_QUATERNION_H
3 3
4 -#include <stim/math/matrix.h> 4 +#include <stim/math/matrix_sq.h>
5 #include <stim/cuda/cudatools/callable.h> 5 #include <stim/cuda/cudatools/callable.h>
6 6
7 namespace stim{ 7 namespace stim{
@@ -46,7 +46,9 @@ public: @@ -46,7 +46,9 @@ public:
46 from = from.norm(); 46 from = from.norm();
47 to = to.norm(); 47 to = to.norm();
48 vec3<T> r = from.cross(to); //compute the rotation vector 48 vec3<T> r = from.cross(to); //compute the rotation vector
49 - T theta = asin(r.len()); //compute the angle of the rotation about r 49 + T l = r.len();
  50 + if (l > 1) l = 1; //we have seen degenerate cases where |r| > 1 (probably due to loss of precision in the cross product)
  51 + T theta = asin(l); //compute the angle of the rotation about r
50 //deal with a zero vector (both k and kn point in the same direction) 52 //deal with a zero vector (both k and kn point in the same direction)
51 if(theta == (T)0){ 53 if(theta == (T)0){
52 return; 54 return;
@@ -81,9 +83,9 @@ public: @@ -81,9 +83,9 @@ public:
81 return result; 83 return result;
82 } 84 }
83 85
84 - CUDA_CALLABLE matrix<T, 3> toMatrix3(){ 86 + CUDA_CALLABLE matrix_sq<T, 3> toMatrix3(){
85 87
86 - matrix<T, 3> result; 88 + matrix_sq<T, 3> result;
87 89
88 90
89 T wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2; 91 T wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2;
@@ -114,9 +116,9 @@ public: @@ -114,9 +116,9 @@ public:
114 return result; 116 return result;
115 } 117 }
116 118
117 - CUDA_CALLABLE matrix<T, 4> toMatrix4(){ 119 + CUDA_CALLABLE matrix_sq<T, 4> toMatrix4(){
118 120
119 - matrix<T, 4> result; 121 + matrix_sq<T, 4> result;
120 T wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2; 122 T wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2;
121 123
122 // calculate coefficients 124 // calculate coefficients
stim/math/random.h 0 → 100644
  1 +#ifndef STIM_RANDOM
  2 +#define STIM_RANDOM
  3 +
  4 +#include <stdio.h>
  5 +#include <stdlib.h>
  6 +#include <time.h>
  7 +#include <stim/math/vec3.h>
  8 +#include <stim/math/constants.h>
  9 +
  10 +namespace stim{
  11 +
  12 +template<class T>
  13 +class Random{
  14 +protected:
  15 + void init() {
  16 + srand(time(NULL));
  17 + }
  18 +
  19 + void init(unsigned int seed){
  20 + srand(seed);
  21 + }
  22 +
  23 +public:
  24 + /// Default Constructor
  25 + Random(){
  26 + init();
  27 + }
  28 +
  29 + /// Constructor from a seed.
  30 + /// A positive seed sets, 0 or negative yeilds the
  31 + Random(unsigned int seed){
  32 + init(seed);
  33 + }
  34 +
  35 + ///Returns a random number uniformly sampled between 0 and 1
  36 + static
  37 + T uniformRandom()
  38 + {
  39 + return ( (T)(rand()))/( (T)(RAND_MAX)); ///generates a random number between 0 and 1 using the uniform distribution.
  40 + }
  41 +
  42 + ///Returns a random number from a normal distribution between 0 to 1.
  43 + static
  44 + T normalRandom()
  45 + {
  46 + T u1 = uniformRandom();
  47 + T u2 = uniformRandom();
  48 + return cos(2.0*atan(1.0)*u2)*sqrt(-1.0*log(u1)); ///generate a random number using the normal distribution between 0 and 1.
  49 + }
  50 + ///Return a random vec3 each value between 0 and 1 from a uniform distribution.
  51 + static
  52 + stim::vec3<T> uniformRandVector()
  53 + {
  54 + stim::vec3<T> r(uniformRandom(), uniformRandom(), 1.0); ///generate a random vector using the uniform distribution between 0 and 1.
  55 + return r;
  56 + }
  57 + ///Return a random vec3, each value between 0 and 1 from a normal distribution.
  58 + static
  59 + stim::vec3<T> normalRandVector()
  60 + {
  61 + stim::vec3<float> r(normalRandom(), normalRandom(), 1.0); ///generate a random vector using the normal distribution between 0 and 1.
  62 + return r;
  63 + }
  64 +
  65 + ///place num_samples of samples on the surface of a sphere of radius r.
  66 + ///returns an std::vector of vec3's in cartisian coordinates.
  67 + static std::vector<stim::vec3 <T> >
  68 + sample_sphere(unsigned int num_samples, T radius = 1, T solidAngle = stim::TAU)
  69 + {
  70 + std::cout << "did this" << std::endl;
  71 + T PHI[2], Z[2], range; ///Range of angles in cylinderical coordinates
  72 + PHI[0] = solidAngle/2; ///project the solid angle into spherical coords
  73 + PHI[1] = asin(0); ///
  74 + Z[0] = cos(PHI[0]); ///project the z into spherical coordinates
  75 + Z[1] = cos(PHI[1]); ///
  76 + range = Z[0] - Z[1]; ///the range of all possible z values.
  77 +
  78 + T z, theta, phi; /// temporary individual
  79 +
  80 + std::vector<stim::vec3<T> > samples;
  81 +
  82 + //srand(100); ///set random seed
  83 +
  84 + for(int i = 0; i < num_samples; i++) ///for each sample
  85 + {
  86 + z = uniformRandom()*range + Z[1]; ///find a random z based on the solid angle
  87 + theta = uniformRandom() * stim::TAU; ///find theta
  88 + phi = acos(z); ///project into spherical coord phi
  89 + stim::vec3<T> sph(radius, theta, phi); ///assume spherical
  90 + stim::vec3<T> cart = sph.sph2cart(); ///conver to cartesisn
  91 + samples.push_back(cart); ///push into list
  92 + }
  93 +
  94 +///THIS IS DEBUGGING CODE, UNCOMMENT TO CHECK WHETHER THE SURFACE IS WELL SAMPLED!
  95 +/*
  96 + std::stringstream name;
  97 + for(int i = 0; i < num_samples; i++)
  98 + name << samples[i].str() << std::endl;
  99 +
  100 +
  101 + std::ofstream outFile;
  102 + outFile.open("Sampled Surface.txt");
  103 + outFile << name.str().c_str();
  104 +*/
  105 +
  106 + return samples; ///return full list.
  107 + }
  108 +};
  109 +
  110 +}
  111 +
  112 +#endif
stim/math/spharmonics.h
1 #ifndef STIM_SPH_HARMONICS 1 #ifndef STIM_SPH_HARMONICS
2 #define STIM_SPH_HARMONICS 2 #define STIM_SPH_HARMONICS
3 3
4 -#include <stim/math/vector.h> 4 +#include <complex>
5 #include <boost/math/special_functions/spherical_harmonic.hpp> 5 #include <boost/math/special_functions/spherical_harmonic.hpp>
  6 +#include <stim/math/constants.h>
  7 +#include <stim/math/random.h>
6 #include <vector> 8 #include <vector>
7 9
8 -#define PI 3.14159  
9 #define WIRE_SCALE 1.001 10 #define WIRE_SCALE 1.001
10 -namespace stim{ 11 +namespace stim {
11 12
12 -template<class T>  
13 -class spharmonics{ 13 + template<class T>
  14 + class spharmonics {
14 15
15 -protected: 16 + public:
  17 + std::vector<T> C; //list of SH coefficients
16 18
17 - std::vector<T> C; //list of SH coefficients 19 + protected:
  20 + unsigned int mcN; //number of Monte-Carlo samples
  21 + unsigned int coeff_1d(unsigned int l, int m) { //convert (l,m) to i (1D coefficient index)
  22 + return pow(l + 1, 2) - (l - m) - 1;
  23 + }
  24 + void coeff_2d(size_t c, unsigned int& l, int& m) { //convert a 1D coefficient index into (l, m)
  25 + l = (unsigned int)ceil(sqrt((double)c + 1)) - 1; //the major index is equal to sqrt(c) - 1
  26 + m = (int)(c - (size_t)(l * l)) - (int)l; //the minor index is calculated by finding the difference
  27 + }
  28 +
  29 + public:
  30 + spharmonics() {
  31 + mcN = 0;
  32 + }
  33 + spharmonics(size_t c) : spharmonics() {
  34 + resize(c);
  35 + }
  36 +
  37 + void push(T c) {
  38 + C.push_back(c);
  39 + }
  40 +
  41 + void resize(unsigned int n) {
  42 + C.resize(n);
  43 + }
  44 +
  45 + void setc(unsigned int l, int m, T value) {
  46 + unsigned int c = coeff_1d(l, m);
  47 + C[c] = value;
  48 + }
  49 +
  50 + T getc(unsigned int l, int m) {
  51 + unsigned int c = coeff_1d(l, m);
  52 + return C[c];
  53 + }
  54 +
  55 + void setc(unsigned int c, T value) {
  56 + C[c] = value;
  57 + }
18 58
19 - unsigned int mcN; //number of Monte-Carlo samples 59 + unsigned int getSize() const {
  60 + return C.size();
  61 + }
20 62
21 - //calculate the value of the SH basis function (l, m) at (theta, phi) 63 + std::vector<T> getC() const {
  64 + return C;
  65 + }
  66 + //calculate the value of the SH basis function (l, m) at (theta, phi)
22 //here, theta = [0, PI], phi = [0, 2*PI] 67 //here, theta = [0, PI], phi = [0, 2*PI]
23 - double SH(int l, int m, double theta, double phi){  
24 - return boost::math::spherical_harmonic_r(l, m, phi, theta);  
25 - } 68 + T SH(unsigned int l, int m, T theta, T phi) {
  69 + //std::complex<T> result = boost::math::spherical_harmonic(l, m, phi, theta);
  70 + //return result.imag() + result.real();
  71 +
  72 + //this calculation is based on calculating the real spherical harmonics:
  73 + // https://en.wikipedia.org/wiki/Spherical_harmonics#Addition_theorem
  74 + if (m < 0) {
  75 + return sqrt(2.0) * pow(-1, m) * boost::math::spherical_harmonic(l, abs(m), phi, theta).imag();
  76 + }
  77 + else if (m == 0) {
  78 + return boost::math::spherical_harmonic(l, m, phi, theta).real();
  79 + }
  80 + else {
  81 + return sqrt(2.0) * pow(-1, m) * boost::math::spherical_harmonic(l, m, phi, theta).real();
  82 + }
  83 + }
26 84
27 - unsigned int coeff_1d(unsigned int l, int m){  
28 - return pow(l + 1, 2) - (l - m) - 1;  
29 - } 85 + /// Calculate the spherical harmonic result given a 1D coefficient index
  86 + T SH(size_t c, T theta, T phi) {
  87 + unsigned int l;
  88 + int m;
  89 + coeff_2d(c, l, m);
  90 + return SH(l, m, theta, phi);
  91 + }
30 92
31 -  
32 93
33 94
34 -public: 95 + /// Initialize Monte-Carlo sampling of a function using N spherical harmonics coefficients
35 96
36 - void push(double c){  
37 - C.push_back(c);  
38 - } 97 + /// @param N is the number of spherical harmonics coefficients used to represent the user function
  98 + void mcBegin(unsigned int coefficients) {
  99 + C.resize(coefficients, 0);
  100 + mcN = 0;
  101 + }
39 102
40 - void resize(unsigned int n){  
41 - C.resize(n);  
42 - } 103 + void mcBegin(unsigned int l, int m) {
  104 + unsigned int c = pow(l + 1, 2) - (l - m);
  105 + mcBegin(c);
  106 + }
43 107
44 - void setc(unsigned int l, int m, T value){  
45 - unsigned int c = coeff_1d(l, m);  
46 - C[c] = value;  
47 - } 108 + void mcSample(T theta, T phi, T val) {
48 109
49 - void setc(unsigned int c, T value){  
50 - C[c] = value;  
51 - } 110 + int l, m;
  111 + T sh;
52 112
53 - /// Initialize Monte-Carlo sampling of a function using N spherical harmonics coefficients 113 + l = m = 0;
  114 + for (unsigned int i = 0; i < C.size(); i++) {
54 115
55 - /// @param N is the number of spherical harmonics coefficients used to represent the user function  
56 - void mcBegin(unsigned int coefficients){  
57 - C.resize(coefficients, 0);  
58 - mcN = 0;  
59 - } 116 + sh = SH(l, m, theta, phi);
  117 + C[i] += sh * val;
60 118
61 - void mcBegin(unsigned int l, int m){  
62 - unsigned int c = pow(l + 1, 2) - (l - m);  
63 - mcBegin(c);  
64 - } 119 + m++; //increment m
65 120
66 - void mcSample(double theta, double phi, double val){ 121 + //if we're in a new tier, increment l and set m = -l
  122 + if (m > l) {
  123 + l++;
  124 + m = -l;
  125 + }
  126 + } //end for all coefficients
67 127
68 - int l, m;  
69 - double sh; 128 + //increment the number of samples
  129 + mcN++;
70 130
71 - l = m = 0;  
72 - for(unsigned int i = 0; i < C.size(); i++){ 131 + } //end mcSample()
73 132
74 - sh = SH(l, m, theta, phi);  
75 - C[i] += sh * val; 133 + void mcEnd() {
76 134
77 - m++; //increment m 135 + //divide all coefficients by the number of samples
  136 + for (unsigned int i = 0; i < C.size(); i++)
  137 + C[i] /= mcN;
  138 + }
78 139
79 - //if we're in a new tier, increment l and set m = -l  
80 - if(m > l){  
81 - l++;  
82 - m = -l; 140 + /// Generates a PDF describing the probability distribution of points on a spherical surface
  141 + /// @param sph_pts is a list of points in spherical coordinates (theta, phi) where theta = [0, 2pi] and phi = [0, pi]
  142 + /// @param l is the maximum degree of the spherical harmonic function
  143 + /// @param m is the maximum order
  144 + void pdf(std::vector<stim::vec3<T> > sph_pts, unsigned int l, int m) {
  145 + mcBegin(l, m); //begin spherical harmonic sampling
  146 + unsigned int nP = sph_pts.size();
  147 + for (unsigned int p = 0; p < nP; p++) {
  148 + mcSample(sph_pts[p][1], sph_pts[p][2], 1.0);
83 } 149 }
84 - } //end for all coefficients 150 + mcEnd();
  151 + }
85 152
86 - //increment the number of samples  
87 - mcN++; 153 + void pdf(std::vector<stim::vec3<T> > sph_pts, size_t c) {
  154 + unsigned int l;
  155 + int m;
  156 + coeff_2d(c, l, m);
  157 + pdf(sph_pts, l, m);
  158 + }
88 159
89 - } //end mcSample() 160 + /// Project a set of samples onto a spherical harmonic basis
  161 + void project(std::vector<stim::vec3<T> > sph_pts, unsigned int l, int m) {
  162 + mcBegin(l, m); //begin spherical harmonic sampling
  163 + unsigned int nP = sph_pts.size();
  164 + for (unsigned int p = 0; p < nP; p++) {
  165 + mcSample(sph_pts[p][1], sph_pts[p][2], sph_pts[p][0]);
  166 + }
  167 + mcEnd();
  168 + }
  169 + void project(std::vector<stim::vec3<T> > sph_pts, size_t c) {
  170 + unsigned int l;
  171 + int m;
  172 + coeff_2d(c, l, m);
  173 + project(sph_pts, l, m);
  174 + }
90 175
91 - void mcEnd(){ 176 + /// Generates a PDF describing the density distribution of points on a sphere
  177 + /// @param sph_pts is a list of points in cartesian coordinates
  178 + /// @param l is the maximum degree of the spherical harmonic function
  179 + /// @param m is the maximum order
  180 + /// @param c is the centroid of the points in sph_pts. DEFAULT 0,0,0
  181 + /// @param n is the number of points of the surface of the sphere used to create the PDF. DEFAULT 1000
  182 + /// @param norm, a boolean that sets where the output vectors will be normalized between 0 and 1.
  183 + /// @param
  184 + /*void pdf(std::vector<stim::vec3<T> > sph_pts, unsigned int l, int m, stim::vec3<T> c = stim::vec3<T>(0, 0, 0), unsigned int n = 1000, bool norm = true, std::vector<T> w = std::vector<T>())
  185 + {
  186 + std::vector<double> weights; ///the weight at each point on the surface of the sphere.
  187 + // weights.resize(n);
  188 + unsigned int nP = sph_pts.size();
  189 + std::vector<stim::vec3<T> > sphere = stim::Random<T>::sample_sphere(n, 1.0, stim::TAU);
  190 + if (w.size() < nP)
  191 + w = std::vector<T>(nP, 1.0);
  192 +
  193 + for (int i = 0; i < n; i++)
  194 + {
  195 + T val = 0;
  196 + for (int j = 0; j < nP; j++)
  197 + {
  198 + stim::vec3<T> temp = sph_pts[j] - c;
  199 + if (temp.dot(sphere[i]) > 0)
  200 + val += pow(temp.dot(sphere[i]), 4)*w[j];
  201 + }
  202 + weights.push_back(val);
  203 + }
92 204
93 - //divide all coefficients by the number of samples  
94 - for(unsigned int i = 0; i < C.size(); i++)  
95 - C[i] /= mcN;  
96 - } 205 + mcBegin(l, m); //begin spherical harmonic sampling
97 206
98 - /// Generates a PDF describing the probability distribution of points on a spherical surface 207 + if (norm)
  208 + {
  209 + T min = *std::min_element(weights.begin(), weights.end());
  210 + T max = *std::max_element(weights.begin(), weights.end());
  211 + for (unsigned int i = 0; i < n; i++)
  212 + {
  213 + stim::vec3<T> sph = sphere[i].cart2sph();
  214 + mcSample(sph[1], sph[2], (weights[i] - min) / (max - min));
  215 + }
99 216
100 - /// @param sph_pts is a list of points in spherical coordinates (theta, phi) where theta = [0, 2pi] and phi = [0, pi]  
101 - /// @param l is the maximum degree of the spherical harmonic function  
102 - /// @param m is the maximum order  
103 - void pdf(std::vector<stim::vec<double> > sph_pts, unsigned int l, int m){  
104 -  
105 - mcBegin( l, m ); //begin spherical harmonic sampling 217 + }
  218 + else {
  219 + for (unsigned int i = 0; i < n; i++)
  220 + {
  221 + stim::vec3<T> sph = sphere[i].cart2sph();
  222 + mcSample(sph[1], sph[2], weights[i]);
  223 + }
  224 + }
  225 + mcEnd();
  226 + }*/
106 227
107 - unsigned int nP = sph_pts.size(); 228 + std::string str() {
108 229
109 - for(unsigned int p = 0; p < nP; p++){  
110 - mcSample(sph_pts[p][1], sph_pts[p][2], 1.0);  
111 - } 230 + std::stringstream ss;
112 231
113 - mcEnd();  
114 - } 232 + int l, m;
  233 + l = m = 0;
  234 + for (unsigned int i = 0; i < C.size(); i++) {
115 235
116 - std::string str(){ 236 + ss << C[i] << '\t';
117 237
118 - std::stringstream ss; 238 + m++; //increment m
119 239
120 - int l, m;  
121 - l = m = 0;  
122 - for(unsigned int i = 0; i < C.size(); i++){  
123 -  
124 - ss<<C[i]<<'\t'; 240 + //if we're in a new tier, increment l and set m = -l
  241 + if (m > l) {
  242 + l++;
  243 + m = -l;
125 244
126 - m++; //increment m 245 + ss << std::endl;
127 246
128 - //if we're in a new tier, increment l and set m = -l  
129 - if(m > l){  
130 - l++;  
131 - m = -l; 247 + }
  248 + }
  249 +
  250 + return ss.str();
  251 +
  252 +
  253 + }
132 254
133 - ss<<std::endl;  
134 - 255 + /// Returns the value of the function at coordinate (theta, phi)
  256 + T p(T theta, T phi) {
  257 + T fx = 0;
  258 +
  259 + int l = 0;
  260 + int m = 0;
  261 + for (unsigned int i = 0; i < C.size(); i++) {
  262 + fx += C[i] * SH(l, m, theta, phi);
  263 + m++;
  264 + if (m > l) {
  265 + l++;
  266 + m = -l;
  267 + }
135 } 268 }
  269 + return fx;
136 } 270 }
137 271
138 - return ss.str(); 272 + /// Returns the derivative of the spherical function with respect to theta
  273 + /// return value is in cartesian coordinates
  274 + vec3<T> dtheta(T theta, T phi, T d = 0.01) {
  275 + T r = p(theta, phi); //calculate the value of the spherical function at three points
  276 + T rt = p(theta + d, phi);
  277 + //double rp = p(theta, phi + d);
139 278
  279 + vec3<T> s(r, theta, phi); //get the spherical coordinate position for all three points
  280 + vec3<T> st(rt, theta + d, phi);
  281 + //vec3<double> sp(rp, theta, phi + d);
140 282
141 - } 283 + vec3<T> c = s.sph2cart();
  284 + vec3<T> ct = st.sph2cart();
  285 + //vec3<double> cp = sp.sph2cart();
142 286
143 - /// Returns the value of the function at the coordinate (theta, phi) 287 + vec3<T> dt = (ct - c)/d; //calculate the derivative
  288 + return dt;
  289 + }
  290 +
  291 + /// Returns the derivative of the spherical function with respect to phi
  292 + /// return value is in cartesian coordinates
  293 + vec3<T> dphi(T theta, T phi, T d = 0.01) {
  294 + T r = p(theta, phi); //calculate the value of the spherical function at three points
  295 + //double rt = p(theta + d, phi);
  296 + T rp = p(theta, phi + d);
  297 +
  298 + vec3<T> s(r, theta, phi); //get the spherical coordinate position for all three points
  299 + //vec3<double> st(rt, theta + d, phi);
  300 + vec3<T> sp(rp, theta, phi + d);
  301 +
  302 + vec3<T> c = s.sph2cart();
  303 + //vec3<double> ct = st.sph2cart();
  304 + vec3<T> cp = sp.sph2cart();
  305 +
  306 + vec3<T> dp = (cp - c) / d; //calculate the derivative
  307 + return dp;
  308 + }
  309 +
  310 + /// Returns the value of the function at the coordinate (theta, phi)
  311 + /// @param theta = [0, 2pi]
  312 + /// @param phi = [0, pi]
  313 + T operator()(T theta, T phi) {
  314 + return p(theta, phi);
  315 + }
  316 +
  317 + //overload arithmetic operations
  318 +
  319 + spharmonics<T> operator*(T rhs) const {
  320 +
  321 + spharmonics<T> result(C.size()); //create a new spherical harmonics object
  322 +
  323 + for (size_t c = 0; c < C.size(); c++) //for each coefficient
  324 +
  325 + result.C[c] = C[c] * rhs; //calculate the factor and store the result in the new spharmonics object
  326 +
  327 + return result;
  328 +
  329 + }
  330 +
  331 +
  332 +
  333 + spharmonics<T> operator+(spharmonics<T> rhs) {
  334 +
  335 + size_t low = std::min(C.size(), rhs.C.size()); //store the number of coefficients in the lowest object
  336 + size_t high = std::max(C.size(), rhs.C.size()); //store the number of coefficients in the result
  337 + bool rhs_lowest = false; //true if rhs has the lowest number of coefficients
  338 + if (rhs.C.size() < C.size()) rhs_lowest = true; //if rhs has a lower number of coefficients, set the flag
144 339
145 - /// @param theta = [0, 2pi]  
146 - /// @param phi = [0, pi]  
147 - double operator()(double theta, double phi){  
148 340
149 - double fx = 0;  
150 341
151 - int l = 0;  
152 - int m = 0;  
153 - for(unsigned int i = 0; i < C.size(); i++){  
154 - fx += C[i] * SH(l, m, theta, phi);  
155 - m++;  
156 - if(m > l){  
157 - l++;  
158 - m = -l; 342 + spharmonics<T> result(high); //create a new object
  343 +
  344 + size_t c;
  345 + for (c = 0; c < low; c++) //perform the first batch of additions
  346 + result.C[c] = C[c] + rhs.C[c]; //perform the addition
  347 +
  348 + for (c = low; c < high; c++) {
  349 + if (rhs_lowest)
  350 + result.C[c] = C[c];
  351 + else
  352 + result.C[c] = rhs.C[c];
  353 + }
  354 + return result;
  355 + }
  356 +
  357 +
  358 +
  359 + spharmonics<T> operator-(spharmonics<T> rhs) {
  360 + return (*this) + (rhs * (T)(-1));
  361 + }
  362 + /// Fill an NxN grid with the spherical function for theta = [0 2pi] and phi = [0 pi]
  363 + void get_func(T* data, size_t X, size_t Y) {
  364 + T dt = stim::TAU / (T)X; //calculate the step size in each direction
  365 + T dp = stim::PI / (T)(Y - 1);
  366 + for (size_t ti = 0; ti < X; ti++) {
  367 + for (size_t pi = 0; pi < Y; pi++) {
  368 + data[pi * X + ti] = (*this)((T)ti * dt, (T)pi * dp);
  369 + }
159 } 370 }
  371 + }
160 372
  373 + /// Project a spherical function onto the basis using C coefficients
  374 + /// @param data is a pointer to the function values in (theta, phi) coordinates
  375 + /// @param N is the number of samples along each axis, where theta = [0 2pi), phi = [0 pi]
  376 + void project(T* data, size_t x, size_t y, size_t nc) {
  377 + stim::cpu2image(data, "test.ppm", x, y, stim::cmBrewer);
  378 + C.resize(nc, 0); //resize the coefficient array to store the necessary coefficients
  379 + T dtheta = stim::TAU / (T)(x - 1); //calculate the grid spacing along theta
  380 + T dphi = stim::PI / (T)y; //calculate the grid spacing along phi
  381 + T theta, phi;
  382 + for (size_t c = 0; c < nc; c++) { //for each coefficient
  383 + for (size_t theta_i = 0; theta_i < x; theta_i++) { //for each coordinate in the provided array
  384 + theta = theta_i * dtheta; //calculate theta
  385 + for (size_t phi_i = 0; phi_i < y; phi_i++) {
  386 + phi = phi_i * dphi; //calculate phi
  387 + C[c] += data[phi_i * x + theta_i] * SH(c, theta, phi) * dtheta * dphi * sin(phi);
  388 + }
  389 + }
  390 + }
161 } 391 }
162 392
163 - return fx;  
164 - } 393 + /// Generate spherical harmonic coefficients based on a set of N samples
  394 + /*void fit(std::vector<stim::vec3<T> > sph_pts, unsigned int L, bool norm = true)
  395 + {
  396 + //std::vector<T> coeffs;
  397 +
  398 + //generate a matrix for fitting
  399 + int B = L*(L+2)+1; //calculate the matrix size
  400 + stim::matrix<T> mat(B, B); //allocate space for the matrix
  401 +
  402 +
  403 +
  404 + std::vector<T> sums;
  405 + //int B = l*(l+2)+1;
  406 + coeffs.resize(B);
  407 + sums.resize(B);
  408 + //stim::matrix<T> mat(B, B);
  409 + for(int i = 0; i < sph_pts.size(); i++)
  410 + {
  411 + mcBegin(l,m);
  412 + mcSample(sph_pts[i][1], sph_pts[i][2], 1.0);
  413 + for(int j = 0; j < B; j++)
  414 + {
  415 + sums[j] += C[j];
  416 + // sums[j] += C[j]*sums[j];
  417 + }
  418 + mcEnd();
  419 + }
  420 + for(int i = 0; i < B; i++)
  421 + {
  422 + for(int j = 0; j < B; j++)
  423 + {
  424 + mat(i,j) = sums[i]*sums[j];
  425 + }
  426 + }
  427 +
  428 + if(mat.det() == 0)
  429 + {
  430 + std::cerr << " matrix not solvable " << std::endl;
  431 + }
  432 + else
  433 + {
  434 + //for(int i = 0; i <
  435 + }
  436 + }*/
  437 +
  438 +
  439 +
  440 +
165 441
166 -}; //end class sph_harmonics 442 + }; //end class sph_harmonics
167 443
168 444
169 445
stim/math/tensor2.h 0 → 100644
  1 +#ifndef STIM_TENSOR2_H
  2 +#define STIM_TENSOR2_H
  3 +
  4 +#include "matrix_sym.h"
  5 +
  6 +namespace stim {
  7 +
  8 +/*This class represents a symmetric rank-2 2D tensor, useful for structure tensors
  9 +*/
  10 +template<typename T>
  11 +class tensor2 : public matrix_sym<T, 2> {
  12 +
  13 +protected:
  14 +
  15 +public:
  16 +
  17 + //calculate the eigenvectors and eigenvalues of the tensor
  18 + CUDA_CALLABLE void eig(stim::matrix<T, 2>& v, stim::matrix<T, 2>& lambda) {
  19 +
  20 + lambda = 0; //initialize the eigenvalue matrix to zero
  21 +
  22 + T t = M[0] + M[2]; //calculate the trace of the tensor
  23 + T d = M[0] * M[2] - M[1] * M[1]; //calculate the determinant of the tensor
  24 +
  25 + lambda(0, 0) = t / 2 + sqrt(t*t / 4 - d);
  26 + lambda(1, 1) = t / 2 - sqrt(t*t / 4 - d);
  27 +
  28 + if (M[1] == 0) {
  29 + v = stim::matrix<T, 2>::identity();
  30 + }
  31 + else {
  32 + v(0, 0) = lambda(0, 0) - d;
  33 + v(0, 1) = lambda(1, 1) - d;
  34 + v(1, 0) = v(1, 1) = M[1];
  35 + }
  36 + }
  37 +
  38 + CUDA_CALLABLE tensor2<T> operator=(stim::matrix_sym<T, 2> rhs){
  39 + stim::matrix_sym<T, 2>::operator=(rhs);
  40 + return *this;
  41 + }
  42 +};
  43 +
  44 +
  45 +} //end namespace stim
  46 +
  47 +
  48 +#endif
0 \ No newline at end of file 49 \ No newline at end of file
stim/math/tensor3.h 0 → 100644
  1 +#ifndef STIM_TENSOR3_H
  2 +#define STIM_TENSOR3_H
  3 +
  4 +#include "matrix_sym.h"
  5 +#include <stim/math/constants.h>
  6 +
  7 +namespace stim {
  8 +
  9 + /*This class represents a symmetric rank-2 2D tensor, useful for structure tensors
  10 + */
  11 +
  12 + //Matrix ID cheat sheet
  13 + // | 0 1 2 |
  14 + // | 1 3 4 |
  15 + // | 2 4 5 |
  16 + template<typename T>
  17 + class tensor3 : public matrix_sym<T, 3> {
  18 +
  19 + protected:
  20 +
  21 + public:
  22 +
  23 + //calculates the determinant of the tensor
  24 + CUDA_CALLABLE T det() {
  25 + return M[0] * M[3] * M[5] + 2 * (M[1] * M[4] * M[2]) - M[2] * M[3] * M[2] - M[1] * M[1] * M[5] - M[0] * M[4] * M[4];
  26 + }
  27 +
  28 + //calculate the eigenvalues for the tensor
  29 + //adapted from https://en.wikipedia.org/wiki/Eigenvalue_algorithm
  30 +
  31 + CUDA_CALLABLE stim::vec3<T> lambda() {
  32 + stim::vec3<T> lam;
  33 + T p1 = M[1] * M[1] + M[2] * M[2] + M[4] * M[4]; //calculate the sum of the squared off-diagonal values
  34 + if (p1 == 0) { //if this value is zero, the matrix is diagonal
  35 + lam[0] = M[0]; //the eigenvalues are the diagonal values
  36 + lam[1] = M[3];
  37 + lam[2] = M[5];
  38 + return lam; //return the eigenvalue vector
  39 + }
  40 +
  41 + T tr = matrix_sym<T, 3>::trace(); //calculate the trace of the matrix
  42 + T q = tr / 3;
  43 + T p2 = (M[0] - q) * (M[0] - q) + (M[3] - q) * (M[3] - q) + (M[5] - q) * (M[5] - q) + 2 * p1;
  44 + T p = sqrt(p2 / 6);
  45 + tensor3<T> Q; //allocate space for Q (q along the diagonals)
  46 + Q = (T)0; //initialize Q to zeros
  47 + Q(0, 0) = Q(1, 1) = Q(2, 2) = q; //set the diagonal values to q
  48 + tensor3<T> B = *this; // B1 = A
  49 + B.M[0] = (B.M[0] - q);
  50 + B.M[3] = (B.M[3] - q);
  51 + B.M[5] = (B.M[5] - q);
  52 + matrix_sym<T, 3>::operator_product(B, 1/p); // B = (1/p) * (A - q*I)
  53 + //B.M[0] = B.M[0] * 1/p;
  54 + //B.M[1] = B.M[1] * 1/p;
  55 + //B.M[2] = B.M[2] * 1/p;
  56 + //B.M[3] = B.M[3] * 1/p;
  57 + //B.M[4] = B.M[4] * 1/p;
  58 + //B.M[5] = B.M[5] * 1/p;
  59 + T r = B.det() / 2; //calculate det(B) / 2
  60 +
  61 + // In exact arithmetic for a symmetric matrix - 1 <= r <= 1
  62 + // but computation error can leave it slightly outside this range.
  63 + T phi;
  64 + if (r <= -1) phi = stim::PI / 3;
  65 + else if (r >= 1) phi = 0;
  66 + else phi = acos(r) / 3;
  67 +
  68 + // the eigenvalues satisfy eig3 >= eig2 >= eig1
  69 + lam[2] = q + 2 * p * cos(phi);
  70 + lam[0] = q + 2 * p * cos(phi + (2 * stim::PI / 3));
  71 + lam[1] = 3 * q - (lam[2] + lam[0]);
  72 +
  73 + return lam;
  74 + }
  75 +
  76 + CUDA_CALLABLE stim::matrix<T> eig(stim::vec3<T>& lambda = stim::vec3<T>()) {
  77 + stim::matrix<T> V;
  78 +
  79 + stim::matrix<T> M1 = matrix_sym<T, 3>::mat();
  80 + stim::matrix<T> M2 = matrix_sym<T, 3>::mat();
  81 + stim::matrix<T> M3 = matrix_sym<T, 3>::mat(); // fill a tensor with symmetric values
  82 +
  83 + M1 = M1 - lambda[0]; // M1 = A - lambda[0] * I
  84 +
  85 + M2 = M2 - lambda[1]; // M2 = A - lambda[1] * I
  86 +
  87 + M3 = M3 - lambda[2]; // M3 = A - lambda[2] * I
  88 +
  89 + T Mod = 0; // module of one column
  90 +
  91 + T tmp1[9] = {0};
  92 + for(int i = 0; i < 9; i++) {
  93 + for(int j = 0; j < 3; j++){
  94 + tmp1[i] += M2(i%3, j) * M3(j, i/3);
  95 + }
  96 + }
  97 + if(tmp1[0] * tmp1[1] * tmp1[2] != 0) { // test whether it is zero column
  98 + Mod = sqrt(pow(tmp1[0],2) + pow(tmp1[1],2) + pow(tmp1[2],2));
  99 + V(0, 0) = tmp1[0]/Mod;
  100 + V(1, 0) = tmp1[1]/Mod;
  101 + V(2, 0) = tmp1[2]/Mod;
  102 + }
  103 + else {
  104 + Mod = sqrt(pow(tmp1[3],2) + pow(tmp1[4],2) + pow(tmp1[5],2));
  105 + V(0, 0) = tmp1[3]/Mod;
  106 + V(1, 0) = tmp1[4]/Mod;
  107 + V(2, 0) = tmp1[5]/Mod;
  108 + }
  109 +
  110 + T tmp2[9] = {0};
  111 + for(int i = 0; i < 9; i++) {
  112 + for(int j = 0; j < 3; j++){
  113 + tmp2[i] += M1(i%3, j) * M3(j, i/3);
  114 + }
  115 + }
  116 + if(tmp2[0] * tmp2[1] * tmp2[2] != 0) {
  117 + Mod = sqrt(pow(tmp2[0],2) + pow(tmp2[1],2) + pow(tmp2[2],2));
  118 + V(0, 1) = tmp2[0]/Mod;
  119 + V(1, 1) = tmp2[1]/Mod;
  120 + V(2, 1) = tmp2[2]/Mod;
  121 + }
  122 + else {
  123 + Mod = sqrt(pow(tmp2[3],2) + pow(tmp2[4],2) + pow(tmp2[5],2));
  124 + V(0, 1) = tmp2[3]/Mod;
  125 + V(1, 1) = tmp2[4]/Mod;
  126 + V(2, 1) = tmp2[5]/Mod;
  127 + }
  128 +
  129 + T tmp3[9] = {0};
  130 + for(int i = 0; i < 9; i++) {
  131 + for(int j = 0; j < 3; j++){
  132 + tmp3[i] += M1(i%3, j) * M2(j, i/3);
  133 + }
  134 + }
  135 + if(tmp3[0] * tmp3[1] * tmp3[2] != 0) {
  136 + Mod = sqrt(pow(tmp3[0],2) + pow(tmp3[1],2) + pow(tmp3[2],2));
  137 + V(0, 2) = tmp3[0]/Mod;
  138 + V(1, 2) = tmp3[1]/Mod;
  139 + V(2, 2) = tmp3[2]/Mod;
  140 + }
  141 + else {
  142 + Mod = sqrt(pow(tmp3[3],2) + pow(tmp3[4],2) + pow(tmp3[5],2));
  143 + V(0, 2) = tmp3[3]/Mod;
  144 + V(1, 2) = tmp3[4]/Mod;
  145 + V(2, 2) = tmp3[5]/Mod;
  146 + }
  147 + return V; //return the eigenvector matrix
  148 + }
  149 + // return one specific eigenvector
  150 + CUDA_CALLABLE stim::vec3<T> eig(int n, stim::vec3<T>& lambda = stim::vec3<T>()) {
  151 + stim::matrix<T, 3> V = eig(lambda);
  152 + stim::vec3<T> v;
  153 + for(int i = 0; i < 3; i++)
  154 + v[i] = V(i, n);
  155 + return v;
  156 + }
  157 +
  158 +
  159 + CUDA_CALLABLE T linear(stim::vec3<T>& lambda = stim::vec3<T>()) {
  160 + T cl = (lambda[2] - lambda[1]) / (lambda[0] + lambda[1] + lambda[2]);
  161 + return cl;
  162 + }
  163 +
  164 + CUDA_CALLABLE T Planar(stim::vec3<T>& lambda = stim::vec3<T>()) {
  165 + T cp = 2 * (lambda[1] - lambda[0]) / (lambda[0] + lambda[1] + lambda[2]);
  166 + return cp;
  167 + }
  168 +
  169 + CUDA_CALLABLE T spherical(stim::vec3<T>& lambda = stim::vec3<T>()) {
  170 + T cs = 3 * lambda[0] / (lambda[0] + lambda[1] + lambda[2]);
  171 + return cs;
  172 + }
  173 +
  174 + CUDA_CALLABLE T fa(stim::vec3<T>& lambda = stim::vec3<T>()) {
  175 + T fa = sqrt(1/2) * sqrt(pow(lambda[2] - lambda[1], 2) + pow(lambda[1] - lambda[0], 2) + pow(lambda[0] - lambda[2], 2)) / sqrt(pow(lambda[2], 2) + pow(lambda[1], 2) + pow(lambda[0], 2));
  176 + }
  177 + //JACK 2: write functions to calculate anisotropy
  178 + //ex: fa(), linear(), planar(), spherical()
  179 +
  180 +
  181 + //calculate the eigenvectors and eigenvalues of the tensor
  182 + //CUDA_CALLABLE void eig(stim::matrix<T, 3>& v, stim::matrix<T, 3>& lambda){
  183 +
  184 + //}
  185 + CUDA_CALLABLE tensor3<T> operator=(T rhs) {
  186 + stim::matrix_sym<T, 3>::operator=(rhs);
  187 + return *this;
  188 + }
  189 +
  190 + CUDA_CALLABLE tensor3<T> operator=(stim::matrix_sym<T, 3> rhs) {
  191 + stim::matrix_sym<T, 3>::operator=(rhs);
  192 + return *this;
  193 + }
  194 + };
  195 +
  196 +
  197 +} //end namespace stim
  198 +
  199 +
  200 +#endif
0 \ No newline at end of file 201 \ No newline at end of file
@@ -5,6 +5,8 @@ @@ -5,6 +5,8 @@
5 #include <stim/cuda/cudatools/callable.h> 5 #include <stim/cuda/cudatools/callable.h>
6 #include <cmath> 6 #include <cmath>
7 7
  8 +#include <sstream>
  9 +
8 10
9 namespace stim{ 11 namespace stim{
10 12
@@ -68,7 +70,7 @@ public: @@ -68,7 +70,7 @@ public:
68 } 70 }
69 71
70 72
71 - /// Convert the vector from cartesian to spherical coordinates (x, y, z -> r, theta, phi where theta = [0, 2*pi]) 73 + /// Convert the vector from cartesian to spherical coordinates (x, y, z -> r, theta, phi where theta = [-PI, PI])
72 CUDA_CALLABLE vec3<T> cart2sph() const{ 74 CUDA_CALLABLE vec3<T> cart2sph() const{
73 vec3<T> sph; 75 vec3<T> sph;
74 sph.ptr[0] = len(); 76 sph.ptr[0] = len();
@@ -236,9 +238,16 @@ public: @@ -236,9 +238,16 @@ public:
236 return result; 238 return result;
237 } 239 }
238 240
239 -//#ifndef __NVCC__ 241 + CUDA_CALLABLE bool operator==(vec3<T> rhs) const{
  242 + if(rhs[0] == ptr[0] && rhs[1] == ptr[1] && rhs[2] == ptr[2])
  243 + return true;
  244 + else
  245 + return false;
  246 + }
  247 +
  248 +//#ifndef __CUDACC__
240 /// Outputs the vector as a string 249 /// Outputs the vector as a string
241 - std::string str() const{ 250 +std::string str() const{
242 std::stringstream ss; 251 std::stringstream ss;
243 252
244 const size_t N = 3; 253 const size_t N = 3;
stim/math/vector.h
@@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
5 #include <cmath> 5 #include <cmath>
6 #include <sstream> 6 #include <sstream>
7 #include <vector> 7 #include <vector>
  8 +#include <algorithm>
8 9
9 #include <stim/cuda/cudatools/callable.h> 10 #include <stim/cuda/cudatools/callable.h>
10 #include <stim/math/vec3.h> 11 #include <stim/math/vec3.h>
@@ -338,7 +339,7 @@ struct vec : public std::vector&lt;T&gt; @@ -338,7 +339,7 @@ struct vec : public std::vector&lt;T&gt;
338 /// Cast to a vec3 339 /// Cast to a vec3
339 operator stim::vec3<T>(){ 340 operator stim::vec3<T>(){
340 stim::vec3<T> r; 341 stim::vec3<T> r;
341 - size_t N = min(size(), (size_t)3); 342 + size_t N = std::min(size(), (size_t)3);
342 for(size_t i = 0; i < N; i++) 343 for(size_t i = 0; i < N; i++)
343 r[i] = at(i); 344 r[i] = at(i);
344 return r; 345 return r;