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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 11 #ifndef STIM_CELLSET_H
2 12 #define STIM_CELLSET_H
3 13  
... ... @@ -117,7 +127,7 @@ public:
117 127 }
118 128  
119 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 131 size_t idx = fields[field]; //get the field index
122 132 size_t ncells = cells.size(); //get the total number of cells
123 133 double maxval, val; //stores the current and maximum values
... ... @@ -130,7 +140,7 @@ public:
130 140 }
131 141  
132 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 144 size_t idx = fields[field]; //get the field index
135 145 size_t ncells = cells.size(); //get the total number of cells
136 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 11 #ifndef STIM_CENTERLINE_H
2 12 #define STIM_CENTERLINE_H
3 13  
4 14 #include <vector>
5 15 #include <stim/math/vec3.h>
6   -//#include <ANN/ANN.h>
  16 +#include <stim/structures/kdtree.cuh>
7 17  
8 18 namespace stim{
9 19  
... ... @@ -12,195 +22,499 @@ namespace stim{
12 22 * class to describe an interconnected (often biological) network.
13 23 */
14 24 template<typename T>
15   -class centerline{
  25 +class centerline : public std::vector< stim::vec3<T> >{
16 26  
17 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 65 /// Returns a stim::vec representing the point at index i
91 66  
92 67 /// @param i is an index of the desired centerline point
93 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 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 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 513 /// Split the fiber at the specified index. If the index is an end point, only one fiber is returned
201 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 519 //if the index is an end point, only the existing fiber is returned
206 520 if(idx == 0 || idx == N-1){
... ... @@ -216,123 +530,84 @@ public:
216 530  
217 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 536 //copy both halves of the fiber
223   - unsigned int i, d;
  537 + unsigned int i;
224 538  
225 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 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 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 554 /// Outputs the fiber as a string
262 555 std::string str(){
263 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 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 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 571 ////resample a fiber in the network
294 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 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 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 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 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 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 10 #ifndef STIM_NETWORK_H
2 11 #define STIM_NETWORK_H
3 12  
... ... @@ -10,19 +19,51 @@
10 19 #include <math.h>
11 20 #include <stim/math/vec3.h>
12 21 #include <stim/visualization/obj.h>
  22 +#include <stim/visualization/swc.h>
13 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 60 namespace stim{
19 61 /** This is the a class that interfaces with gl_spider in order to store the currently
20 62 * segmented network. The following data is stored and can be extracted:
21 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 67 template<typename T>
27 68 class network{
28 69  
... ... @@ -31,18 +72,30 @@ class network{
31 72 class edge : public cylinder<T>
32 73 {
33 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 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 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 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 99 edge(stim::cylinder<T> f) : cylinder<T>(f) {}
47 100  
48 101 /// Resamples an edge by calling the fiber resampling function
... ... @@ -57,10 +110,71 @@ class network{
57 110 /// Output the edge information as a string
58 111 std::string str(){
59 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 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 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 184 //std::vector<unsigned int> edges; //indices of edges connected to this node.
71 185 std::vector<unsigned int> e[2]; //indices of edges going out (e[0]) and coming in (e[1])
72 186 //stim::vec3<T> p; //position of this node in physical space.
73   -
  187 + //default constructor
  188 + vertex() : stim::vec3<T>()
  189 + {
  190 + }
74 191 //constructor takes a stim::vec
75 192 vertex(stim::vec3<T> p) : stim::vec3<T>(p){}
76 193  
77 194 /// Output the vertex information as a string
78   - std::string str(){
  195 + std::string
  196 + str(){
79 197 std::stringstream ss;
80 198 ss<<"\t(x, y, z) = "<<stim::vec3<T>::str();
81 199  
... ... @@ -92,14 +210,46 @@ class network{
92 210  
93 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 249 protected:
100 250  
101 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 254 public:
105 255  
... ... @@ -125,7 +275,66 @@ public:
125 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 338 for (unsigned i=0; i< vertices; i ++ ){
130 339 V[i] = V[i] * s;
131 340 }
... ... @@ -139,10 +348,9 @@ public:
139 348 }
140 349 }
141 350 return V;
142   - }
  351 + }*/
143 352  
144 353 // Returns an average of branching index in the network
145   -
146 354 double BranchingIndex(){
147 355 double B=0;
148 356 for(unsigned v=0; v < V.size(); v ++){
... ... @@ -154,7 +362,6 @@ public:
154 362 }
155 363  
156 364 // Returns number of branch points in thenetwork
157   -
158 365 unsigned int BranchP(){
159 366 unsigned int B=0;
160 367 unsigned int c;
... ... @@ -168,7 +375,6 @@ public:
168 375 }
169 376  
170 377 // Returns number of end points (tips) in thenetwork
171   -
172 378 unsigned int EndP(){
173 379 unsigned int B=0;
174 380 unsigned int c;
... ... @@ -202,7 +408,7 @@ public:
202 408 // return s;
203 409 //}
204 410  
205   -
  411 + //Calculate Metrics---------------------------------------------------
206 412 // Returns an average of fiber/edge lengths in the network
207 413 double Lengths(){
208 414 stim::vec<T> L;
... ... @@ -270,8 +476,10 @@ public:
270 476 double avg = sumFractDim / E.size();
271 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 485 //load a network from an OBJ file
... ... @@ -290,9 +498,10 @@ public:
290 498 std::vector< stim::vec<T> > c; //allocate an array of points for the vessel centerline
291 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 502 for(size_t j = 0; j < c.size(); j++)
295 503 c3[j] = c[j];
  504 + c3.update();
296 505  
297 506 // edge new_edge = c3; ///This is dangerous.
298 507 edge new_edge(c3);
... ... @@ -313,10 +522,24 @@ public:
313 522 it = find(id2vert.begin(), id2vert.end(), i[0]); //look for the first node
314 523 if(it == id2vert.end()){ //if i[0] hasn't already been used
315 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 544 else{ //if the vertex already exists
322 545 it_idx = std::distance(id2vert.begin(), it);
... ... @@ -327,10 +550,24 @@ public:
327 550 it = find(id2vert.begin(), id2vert.end(), i[1]); //look for the second ID
328 551 if(it == id2vert.end()){ //if i[1] hasn't already been used
329 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 572 else{ //if the vertex already exists
336 573 it_idx = std::distance(id2vert.begin(), it);
... ... @@ -341,6 +578,217 @@ public:
341 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 794 /// Output the network as a string
... ... @@ -365,7 +813,7 @@ public:
365 813 stim::network<T> resample(T spacing){
366 814 stim::network<T> n; //create a new network that will be an exact copy, with resampled fibers
367 815 n.V = V; //copy all vertices
368   -
  816 + //n.NT = NT; //copy all the neuronal type information
369 817 n.E.resize(edges()); //allocate space for the edge list
370 818  
371 819 //copy all fibers, resampling them in the process
... ... @@ -376,8 +824,6 @@ public:
376 824 return n; //return the resampled network
377 825 }
378 826  
379   -
380   -
381 827 /// Calculate the total number of points on all edges.
382 828 unsigned total_points(){
383 829 unsigned n = 0;
... ... @@ -386,16 +832,52 @@ public:
386 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 853 a[0] = b[0];
395 854 a[1] = b[1];
396 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 881 /// Calculate the average magnitude across the entire network.
400 882 /// @param m is the magnitude value to use. The default is 0 (usually radius).
401 883 T average(unsigned m = 0){
... ... @@ -403,7 +885,7 @@ public:
403 885 T M, L; //allocate space for the total magnitude and length
404 886 M = L = 0; //initialize both the initial magnitude and length to zero
405 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 889 L += E[e].length(); //get the edge length
408 890 }
409 891  
... ... @@ -411,67 +893,391 @@ public:
411 893 }
412 894  
413 895 /// This function compares two networks and returns the percentage of the current network that is missing from A.
414   -
415 896 /// @param A is the network to compare to - the field is generated for A
416 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 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 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 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 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 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 1281 // split a string in text by the character sep
476 1282 stim::vec<T> split(std::string &text, char sep)
477 1283 {
... ... @@ -488,7 +1294,7 @@ public:
488 1294 void load_txt(std::string filename)
489 1295 {
490 1296 std::vector <std::string> file_contents;
491   - std::ifstream file(filename);
  1297 + std::ifstream file(filename.c_str());
492 1298 std::string line;
493 1299 std::vector<unsigned> id2vert; //this list stores the vertex ID associated with each network vertex
494 1300 //for each line in the text file, store them as strings in file_contents
... ... @@ -539,7 +1345,7 @@ public:
539 1345 for(unsigned int d = 0; d < 3; d++){
540 1346 ss<<p[i][d];
541 1347 }
542   - ss < "\n";
  1348 + ss << "\n";
543 1349 }
544 1350 return ss.str();
545 1351 }
... ... @@ -553,8 +1359,8 @@ public:
553 1359 void
554 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 1364 ofs << (E.size()).str() << "\n";
559 1365 for(unsigned int i = 0; i < E.size(); i++)
560 1366 {
... ... @@ -567,7 +1373,8 @@ public:
567 1373 {
568 1374 std::string str;
569 1375 str = V[i].str();
570   - removeCharsFromString(str, "[],");
  1376 + char temp[4] = "[],";
  1377 + removeCharsFromString(str, temp);
571 1378 ofs << str << "\n";
572 1379 }
573 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 10 #ifndef STIM_NETWORK_H
2 11 #define STIM_NETWORK_H
3 12  
4 13 #include <stim/math/vector.h>
5 14 #include <stim/visualization/obj.h>
6 15 #include <list>
7   -#include <ANN/ANN.h>
  16 +//#include <ANN/ANN.h>
8 17  
9 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 4 namespace stim{
5 5 namespace cuda{
6 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 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 13 // convert 2D coordinates to 1D
14   - int i = yi * x + xi;
  14 + size_t i = yi * x + xi;
15 15  
16 16  
17 17 if(xi >= x|| yi >= y) return;
... ... @@ -27,11 +27,11 @@ namespace stim{
27 27  
28 28  
29 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 32 unsigned int max_threads = stim::maxThreadsPerBlock();
33 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 36 //call the kernel to do the multiplication
37 37 cuda_cart2polar <<< blocks, threads >>>(gpuGrad, x, y, rotation);
... ... @@ -40,11 +40,11 @@ namespace stim{
40 40  
41 41  
42 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 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 49 //allocate memory on the GPU for the array
50 50 T* gpuA;
... ...
stim/cuda/branch_detection.cuh
... ... @@ -11,7 +11,7 @@ typedef unsigned int uint;
11 11  
12 12  
13 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 16 float sigma = 2.0;
17 17 unsigned int conn = 7;
... ... @@ -22,7 +22,7 @@ find_branch(GLint texbufferID, GLenum texType, unsigned int x, unsigned int y)
22 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 26 cudaDeviceSynchronize();
27 27  
28 28  
... ...
stim/cuda/cudatools/callable.h
... ... @@ -7,4 +7,10 @@
7 7 #define CUDA_CALLABLE
8 8 #endif
9 9  
  10 +#ifdef __CUDACC__
  11 +#define CUDA_UNCALLABLE __host__ inline
  12 +#else
  13 +#define CUDA_UNCALLABLE
  14 +#endif
  15 +
10 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 4 #include <cuda.h>
5 5  
6 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 78 } //end namespace rts
37 79  
38 80 #endif
... ...
stim/cuda/cudatools/error.h
  1 +#ifndef STIM_CUDA_ERROR_H
  2 +#define STIM_CUDA_ERROR_H
  3 +
1 4 #include <stdio.h>
2 5 #include <iostream>
3   -using namespace std;
4 6 #include "cuda_runtime.h"
5 7 #include "device_launch_parameters.h"
6 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 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 13 if (err != cudaSuccess) {
14 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 21 if (err != CUFFT_SUCCESS)
22 22 {
23 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 25 else if(err == CUFFT_ALLOC_FAILED)
26   - cout<<"Allocation failed."<<endl;
  26 + std::cout<<"Allocation failed."<<std::endl;
27 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 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 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 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 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 65 #endif
... ...
stim/cuda/filter.cuh
... ... @@ -26,6 +26,11 @@ namespace stim
26 26 float* LoG;
27 27 float* res;
28 28 float* centers;
  29 +
  30 +//#ifdef DEBUG
  31 + float* print;
  32 +//#endif
  33 +
29 34 stim::cuda::cuda_texture tx;
30 35  
31 36  
... ... @@ -44,6 +49,13 @@ namespace stim
44 49 HANDLE_ERROR(
45 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 59 // checkCUDAerrors("Memory Allocation, Result");
48 60 }
49 61  
... ... @@ -58,6 +70,11 @@ namespace stim
58 70 HANDLE_ERROR(
59 71 cudaFree(centers)
60 72 );
  73 +//#ifdef DEBUG
  74 + HANDLE_ERROR(
  75 + cudaFree(print)
  76 + );
  77 +//#endif
61 78 free(LoG);
62 79 }
63 80  
... ... @@ -89,7 +106,7 @@ namespace stim
89 106 //Shared memory would be better.
90 107 __global__
91 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 110 //R = floor(size/2)
94 111 //THIS IS A NAIVE WAY TO DO IT, and there is a better way)
95 112  
... ... @@ -101,16 +118,15 @@ namespace stim
101 118 // float val = 0;
102 119 float tu = (x-kr+xi)/(float)DIM_X;
103 120 float tv = (y-kr+yi)/(float)DIM_Y;
  121 + int idx = y*DIM_X+x;
104 122 shared[xi][yi] = gpuLoG[yi*kl+xi]*(255.0-(float)tex2D<unsigned char>(texIn, tu, tv));
105 123 __syncthreads();
106   -
107 124  
108 125 //x = max(0,x);
109 126 //x = min(x, width-1);
110 127 //y = max(y, 0);
111 128 //y = min(y, height - 1);
112 129  
113   - int idx = y*DIM_X+x;
114 130 // int k_idx;
115 131 for(unsigned int step = blockDim.x/2; step >= 1; step >>= 1)
116 132 {
... ... @@ -135,11 +151,12 @@ namespace stim
135 151 __syncthreads();
136 152 if(xi == 0 && yi == 0)
137 153 res[idx] = shared[0][0];
  154 +
138 155 }
139 156  
140 157 extern "C"
141 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 161 tx.SetTextureCoordinates(1);
145 162 tx.SetAddressMode(1, 3);
... ... @@ -153,7 +170,14 @@ namespace stim
153 170 dim3 numBlocks(DIM_X, DIM_Y);
154 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 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 #include <stim/cuda/sharedmem.cuh>
9 9 #include <stim/cuda/cudatools/error.h>
10 10  
11   -#define pi 3.14159
12   -
13 11 namespace stim{
14 12 namespace cuda{
15 13  
... ...
stim/cuda/templates/gradient.cuh
... ... @@ -9,57 +9,50 @@ namespace stim{
9 9 namespace cuda{
10 10  
11 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 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 18 // convert 2D coordinates to 1D
19   - int i = yi * x + xi;
  19 + size_t i = yi * x + xi;
20 20  
21 21 //return if the pixel is outside of the image
22 22 if(xi >= x || yi >= y) return;
23 23  
24 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 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 32 //use forward differences if a coordinate is zero
33 33 if(xi == 0)
34 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 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 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 45 out[i * 2 + 1] = (in[i_yp] - in[i_yn]) / 2;
50 46  
51 47 }
52 48  
53 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 52 //get the maximum number of threads per block for the CUDA device
60 53 unsigned int max_threads = stim::maxThreadsPerBlock();
61 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 58 //call the GPU kernel to determine the gradient
... ...
stim/cuda/testKernel.cuh
... ... @@ -53,11 +53,31 @@
53 53  
54 54 float valIn = tex2D<unsigned char>(texIn, x, y);
55 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 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 81 void test(cudaTextureObject_t tObj, int x, int y, std::string nam)
62 82 {
63 83  
... ... @@ -86,3 +106,31 @@
86 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 4  
5 5 #include <string>
6 6 #include <fstream>
  7 +#include <complex>
  8 +#include <cstring>
  9 +#include <chrono>
7 10  
8 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 19 namespace stim{
16 20  
... ... @@ -33,15 +37,25 @@ public:
33 37 return size() * sizeof(T);
34 38 }
35 39 void alloc(){
  40 + if (ptr != NULL) free(ptr);
  41 + ptr = NULL;
36 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 45 R[0] = x;
40 46 R[1] = y;
41 47 R[2] = z;
42 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 59 /// Create a deep copy of an agileng_binary object
46 60 void deep_copy(agilent_binary<T>* dst, const agilent_binary<T>* src){
47 61 dst->alloc(src->R[0], src->R[1], src->R[2]); //allocate memory
... ... @@ -51,23 +65,28 @@ public:
51 65  
52 66 /// Default constructor, sets the resolution to zero and the data pointer to NULL
53 67 agilent_binary(){
54   - memset(R, 0, sizeof(size_t) * 3); //set the resolution to zero
55 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 73 /// Constructor with resolution
59 74 agilent_binary(size_t x, size_t y, size_t z){
  75 + ptr = NULL;
60 76 alloc(x, y, z);
  77 + memset(Z, 0, sizeof(double) * 2);
61 78 }
62 79  
63 80 /// Constructor with filename
64 81 agilent_binary(std::string filename){
65 82 ptr = NULL;
  83 + memset(Z, 0, sizeof(double) * 2);
66 84 load(filename);
67 85 }
68 86  
69 87 /// Copy constructor
70 88 agilent_binary(const agilent_binary<T> &obj){
  89 + ptr = NULL;
71 90 deep_copy(this, &obj);
72 91 }
73 92  
... ... @@ -78,32 +97,42 @@ public:
78 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 105 ~agilent_binary(){
82   - free(ptr);
  106 + if(ptr != NULL)
  107 + free(ptr);
83 108 }
84 109  
85 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 116 short x, y, z;
91 117  
92 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 138 void save(std::string filename){
... ... @@ -167,9 +196,8 @@ public:
167 196  
168 197 //pads to the nearest power-of-two
169 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 200 size_t n = newZ - R[2]; //calculate the number of bands to add
172   - std::cout<<"band padding: "<<n<<std::endl;
173 201 zeropad(n); //add the padding
174 202 }
175 203  
... ... @@ -184,9 +212,20 @@ public:
184 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 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 229 auto total_start = std::chrono::high_resolution_clock::now();
191 230  
192 231 auto start = std::chrono::high_resolution_clock::now();
... ... @@ -201,6 +240,29 @@ public:
201 240 // std::cout << "Transpose data: " << diff.count() << " s\n";
202 241  
203 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 266 cufftHandle plan; //allocate space for a cufft plan
205 267 cufftReal* gpu_data; //create a pointer to the data
206 268 size_t batch = R[0] * R[1]; //calculate the batch size (X * Y)
... ... @@ -237,18 +299,19 @@ public:
237 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 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 310 size_t start_i = (size_t)std::ceil(band_min / fft_delta); //calculate the first band to store
248 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 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 315 result.Z[1] = end_i * fft_delta;
253 316  
254 317 for(size_t b = start_i; b < end_i; b++){
... ... @@ -271,7 +334,22 @@ public:
271 334  
272 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  
... ...
stim/envi/bil.h
... ... @@ -4,6 +4,7 @@
4 4 #include "../envi/envi_header.h"
5 5 #include "../envi/hsi.h"
6 6 #include "../math/fd_coefficients.h"
  7 +#include <stim/cuda/cudatools/error.h>
7 8 #include <cstring>
8 9 #include <utility>
9 10 #include <deque>
... ... @@ -54,11 +55,12 @@ public:
54 55 unsigned long long Y,
55 56 unsigned long long B,
56 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 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 120 page++;
119 121 //if wavelength is larger than the last wavelength in header file
120 122 if (page == Z()) {
121   - band_index(p, Z()-1);
  123 + band_index(p, Z()-1, PROGRESS);
122 124 return true;
123 125 }
124 126 }
... ... @@ -224,10 +226,44 @@ public:
224 226 }
225 227  
226 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 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 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 304 for (unsigned long long k =0; k < Y(); k++)
269 305 {
270 306 //get the current y slice
271   - read_plane_y(c, k);
  307 + read_plane_xz(c, k);
272 308  
273 309 //initialize lownum, highnum, low, high
274 310 ai = w[0];
... ... @@ -369,7 +405,7 @@ public:
369 405  
370 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 409 for(unsigned long long i = 0; i < B; i++)
374 410 {
375 411 for(unsigned long long m = 0; m < X(); m++)
... ... @@ -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 470 /// Convert the current BIL file to a BSQ file with the specified file name.
430 471  
431 472 /// @param outname is the name of the output BSQ file to be saved to disk.
... ... @@ -469,7 +510,7 @@ public:
469 510  
470 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 514 for ( unsigned long long k = 0; k < Z(); k++)
474 515 {
475 516 unsigned long long ks = k * X();
... ... @@ -863,7 +904,7 @@ public:
863 904  
864 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 908 for ( unsigned long long j = 0; j < Z(); j++) //for each Z() (Y)
868 909 {
869 910 for (unsigned long long k = 0; k < X(); k++) //for each band
... ... @@ -933,7 +974,7 @@ public:
933 974 //for each slice along the y axis
934 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 979 //for each sample along X
939 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 1045  
1005 1046 double x; //create a register to store the pixel value
1006 1047 for (unsigned long long k = 0; k < Y(); k++){
1007   - read_plane_y(temp, k);
  1048 + read_plane_xz(temp, k);
1008 1049 unsigned long long kx = k * X();
1009 1050 for (unsigned long long i = 0; i < X(); i++){
1010 1051 if (mask == NULL || mask[kx + i] != 0){
... ... @@ -1025,13 +1066,103 @@ public:
1025 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 1130 /// Calculate the covariance matrix for all masked pixels in the image.
1029 1131  
1030 1132 /// @param co is a pointer to pre-allocated memory of size [B * B] that stores the resulting covariance matrix
1031 1133 /// @param avg is a pointer to memory of size B that stores the average spectrum
1032 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 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 1166 //memory allocation
1036 1167 unsigned long long xy = X() * Y();
1037 1168 unsigned long long B = Z();
... ... @@ -1089,9 +1220,9 @@ public:
1089 1220 bool PROGRESS = false){
1090 1221  
1091 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 1227 //calculate the size of a line
1097 1228 unsigned long long L = samples * sizeof(T);
... ... @@ -1107,19 +1238,19 @@ public:
1107 1238 unsigned long long jumpb = (X() - samples) * sizeof(T);
1108 1239  
1109 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 1243 //set the start position for the cropped region
1113 1244 file.seekg((y0 * X() * Z() + b0 * X() + x0) * sizeof(T), std::ios::beg);
1114 1245  
1115 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 1250 file.read((char *)(temp + z * samples), sizeof(T) * samples);
1120 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 1256 //write slice data into target file
... ... @@ -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 1366 /// Convolve the given band range with a kernel specified by a vector of coefficients.
1215 1367  
1216 1368 /// @param outfile is an already open stream to the output file
... ... @@ -1328,7 +1480,7 @@ public:
1328 1480 c = (T*)malloc( L ); //allocate space for the slice
1329 1481  
1330 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 1484 for(unsigned long long i = 0; i < B; i++){ //for each band
1333 1485 for(unsigned long long m = 0; m < X(); m++){ //for each sample
1334 1486 if( mask == NULL && mask[m + j * X()] ) //if the pixel is masked
... ... @@ -1358,7 +1510,7 @@ public:
1358 1510 c = (T*)malloc( L ); //allocate space for the slice
1359 1511  
1360 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 1514 for(unsigned long long i = 0; i < B; i++){ //for each band
1363 1515 for(unsigned long long m = 0; m < X(); m++){ //for each sample
1364 1516 if( mask == NULL && mask[m + j * X()] ) //if the pixel is masked
... ...
stim/envi/binary.h
... ... @@ -3,8 +3,8 @@
3 3 #ifndef RTS_BINARY_H
4 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 8 #include <fstream>
9 9 #include <sys/stat.h>
10 10 #include <cstring>
... ... @@ -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 139 /** This class manages the streaming of large multidimensional binary files.
... ... @@ -210,6 +143,8 @@ public:
210 143 * @param T is the data type used to store data to disk (generally float or double)
211 144 * @param D is the dimension of the data (default 3)
212 145 */
  146 +
  147 +enum iotype {io_in, io_out};
213 148 template< typename T, unsigned int D = 3 >
214 149 class binary{
215 150  
... ... @@ -272,19 +207,21 @@ protected:
272 207 /// Private helper file that opens a specified binary file.
273 208  
274 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 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 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 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 226 //if the file is successful
290 227 if(file){
... ... @@ -342,20 +279,24 @@ public:
342 279 /// @param filename is the name of the binary file
343 280 /// @param r is a STIM vector specifying the size of the binary file along each dimension
344 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 284 for(unsigned long long i = 0; i < D; i++) //set the dimensions of the binary file object
348 285 R[i] = r[i];
349 286  
350 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 291 //reset();
355 292  
356 293 return test_file_size();
357 294 }
358 295  
  296 + bool is_open() {
  297 + return file.is_open();
  298 + }
  299 +
359 300 /// Creates a new binary file for streaming
360 301  
361 302 /// @param filename is the name of the binary file to be created
... ... @@ -605,7 +546,7 @@ public:
605 546 size_t size_bytes = sx * sy * sz * sizeof(T); //size of the block to read in bytes
606 547  
607 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 550 file.seekg(start * sizeof(T), std::ios::beg); //seek to the start position
610 551  
611 552  
... ...
stim/envi/bip.h
... ... @@ -5,13 +5,17 @@
5 5 #include "../envi/bil.h"
6 6 #include "../envi/hsi.h"
7 7 #include <cstring>
  8 +#include <complex>
8 9 #include <utility>
  10 +#include <algorithm>
9 11  
10 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 20 namespace stim{
17 21  
... ... @@ -62,14 +66,15 @@ public:
62 66 unsigned long long Y,
63 67 unsigned long long B,
64 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 72 //copy the wavelengths to the BSQ file structure
68 73 w = wavelengths;
69 74 //copy the offset to the structure
70 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 262 }
258 263  
259 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 266 return binary<T>::read_plane_2(p, y);
262 267 }
263 268  
... ... @@ -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 441 /// Convert the current BIP file to a BIL file with the specified file name.
393 442  
... ... @@ -954,7 +1003,7 @@ public:
954 1003  
955 1004 /// @param p is a pointer to pre-allocated memory of size [B * sizeof(T)] that stores the mean spectrum
956 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 1007 unsigned long long XY = X() * Y(); //calculate the total number of pixels in the HSI
959 1008 T* temp = (T*)malloc(sizeof(T) * Z()); //allocate space for the current spectrum to be read
960 1009 memset(m, 0, Z() * sizeof(double)); //set the mean spectrum to zero
... ... @@ -976,22 +1025,23 @@ public:
976 1025 }
977 1026  
978 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 1033 free(temp);
983 1034 return true;
984 1035 }
985   -#ifdef CUDA_FOUND
  1036 +//#ifdef CUDA_FOUND
986 1037 /// Calculate the covariance matrix for masked pixels using cuBLAS
987 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 1041 cudaError_t cudaStat;
991 1042 cublasStatus_t stat;
992 1043 cublasHandle_t handle;
993 1044  
994   - progress = 0; //initialize the progress to zero (0)
995 1045 unsigned long long XY = X() * Y(); //calculate the number of elements in a band image
996 1046 unsigned long long B = Z(); //calculate the number of spectral elements
997 1047  
... ... @@ -1009,10 +1059,9 @@ public:
1009 1059 double axpy_alpha = -1; //multiplication factor for the average spectrum (in order to perform a subtraction)
1010 1060  
1011 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 1065 for (unsigned long long xy = 0; xy < XY; xy++){ //for each pixel
1017 1066 if (mask == NULL || mask[xy] != 0){
1018 1067 pixeld(s, xy); //retreive the spectrum at the current xy pixel location
... ... @@ -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 1092 /// Calculate the covariance matrix for all masked pixels in the image with 64-bit floating point precision.
1044 1093  
1045 1094 /// @param co is a pointer to pre-allocated memory of size [B * B] that stores the resulting covariance matrix
1046 1095 /// @param avg is a pointer to memory of size B that stores the average spectrum
1047 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 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 1115 //memory allocation
1060 1116 unsigned long long XY = X() * Y();
1061 1117 unsigned long long B = Z();
... ... @@ -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 1158 cudaError_t cudaStat;
1106 1159 cublasStatus_t stat;
... ... @@ -1113,9 +1166,10 @@ public:
1113 1166 double* s = (double*)malloc(sizeof(double) * B); //allocate space for the spectrum that will be pulled from the file
1114 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 1174 double* A_dev; //declare a device pointer that will store the covariance matrix on the GPU
1121 1175 double* avg_dev; //declare a device pointer that will store the average spectrum
... ... @@ -1125,28 +1179,32 @@ public:
1125 1179 cudaStat = cudaMalloc(&avg_dev, B * sizeof(double)); //allocate space on the CUDA device for the average spectrum
1126 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 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 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 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 1215 cudaFree(s2_dev);
1158 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 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 1228 /// Calculate the covariance of noise matrix for all masked pixels in the image with 64-bit floating point precision.
1171 1229  
1172 1230 /// @param coN is a pointer to pre-allocated memory of size [B * B] that stores the resulting covariance matrix
1173 1231 /// @param avg is a pointer to memory of size B that stores the average spectrum
1174 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 1251 progress = 0;
1189 1252 //memory allocation
1190 1253 unsigned long long XY = X() * Y();
1191 1254 unsigned long long B = Z();
1192 1255 T* temp = (T*)malloc(sizeof(T) * B);
  1256 + T* temp2 = (T*)malloc(sizeof(T) * B);
1193 1257  
1194 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 1261 memset(coN, 0, B * B * sizeof(double));
1198 1262  
1199 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 1267 memset(coN_half, 0, B * B * sizeof(double)); //initialize the high-precision matrix with zeros
1203 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 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 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 1288 for (unsigned long long b1 = b0; b1 < B; b1++)
1216 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 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 1301 free(temp);
  1302 + free(temp2);
1229 1303 free(temp_precise);
  1304 + free(temp_precise2);
1230 1305 return true;
1231 1306 }
1232 1307  
1233   - #ifdef CUDA_FOUND
1234 1308 /// Project the spectra onto a set of basis functions
1235 1309 /// @param outfile is the name of the new binary output file that will be created
1236 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 1313 /// @param mask is a character mask used to limit processing to valid pixels
1240 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 1318 cublasHandle_t handle;
1245 1319  
1246 1320 std::ofstream target(outfile.c_str(), std::ios::binary); //open the target binary file
... ... @@ -1251,12 +1325,12 @@ public:
1251 1325  
1252 1326 double* s = (double*)malloc(sizeof(double) * B); //allocate space for the spectrum that will be pulled from the file
1253 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 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 1336 /// transposing basis matrix (because cuBLAS is column-major)
... ... @@ -1265,28 +1339,24 @@ public:
1265 1339 for (int i = 0; i<M; i++)
1266 1340 for (int j = 0; j<B; j++)
1267 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 1355 double axpy_alpha = -1; //multiplication factor for the center (in order to perform a subtraction)
1282 1356 double axpy_alpha2 = 1; //multiplication factor for the matrix-vector multiplication
1283 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 1361 T* temp = (T*)malloc(sizeof(T) * M); //allocate space for the projected pixel to be written on the disc
1292 1362 size_t i;
... ... @@ -1294,14 +1364,11 @@ public:
1294 1364 if (mask == NULL || mask[xy] != 0){
1295 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 1372 for(i = 0; i < M; i++) temp[i] = (T)A[i]; //casting projected pixel from double to whatever T is
1306 1373 }
1307 1374 else
... ... @@ -1313,10 +1380,11 @@ public:
1313 1380 }
1314 1381  
1315 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 1388 free(A);
1321 1389 free(s);
1322 1390 free(temp);
... ... @@ -1324,7 +1392,6 @@ public:
1324 1392  
1325 1393 return true;
1326 1394 }
1327   -#endif
1328 1395  
1329 1396 /// Project the spectra onto a set of basis functions
1330 1397 /// @param outfile is the name of the new binary output file that will be created
... ... @@ -1332,16 +1399,22 @@ public:
1332 1399 /// @param basis a set of basis vectors that the data set will be projected onto (after centering)
1333 1400 /// @param M is the number of basis vectors
1334 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 1418 std::ofstream target(outfile.c_str(), std::ios::binary); //open the target binary file
1346 1419 //std::string headername = outfile + ".hdr"; //the header file name
1347 1420  
... ... @@ -1426,9 +1499,9 @@ public:
1426 1499 bool PROGRESS = false){
1427 1500  
1428 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 1506 //calculate the length of one cropped spectrum
1434 1507 unsigned long long L = bands * sizeof(T);
... ... @@ -1436,41 +1509,46 @@ public:
1436 1509 //unsigned long long L = Z() * sizeof(T);
1437 1510  
1438 1511 //allocate space for the spectrum
1439   - T* temp = (T*)malloc(L);
  1512 + char* temp = (char*)malloc(L);
1440 1513  
1441 1514 //open an output file for binary writing
1442 1515 std::ofstream out(outfile.c_str(), std::ios::binary);
1443 1516  
1444 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 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 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 1533 //unsigned long long sp = y0 * X() + x0; //start pixel
1455 1534  
1456 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 1538 //read the cropped spectral region
1462   - file.read( (char*) temp, L );
  1539 + file.read(temp, L );
1463 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 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 1548 file.seekg(jump_line, std::ios::cur);
1472 1549 }
1473 1550 free(temp);
  1551 + out.close();
1474 1552  
1475 1553 return true;
1476 1554 }
... ... @@ -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 1647 /// Convolve the given band range with a kernel specified by a vector of coefficients.
1550 1648  
1551 1649 /// @param outfile is an already open stream to the output file
... ... @@ -1687,7 +1785,117 @@ public:
1687 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 1900 /// Close the file.
1693 1901 bool close(){
... ...
stim/envi/bsq.h
1 1 #ifndef STIM_BSQ_H
2 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 7 #include <cstring>
8 8 #include <utility>
9 9 #include <vector>
... ... @@ -64,14 +64,15 @@ public:
64 64 unsigned long long Y,
65 65 unsigned long long B,
66 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 70 //copy the wavelengths to the BSQ file structure
70 71 w = wavelengths;
71 72 //copy the wavelengths to the structure
72 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 78 /// Retrieve a single band (based on index) and stores it in pre-allocated memory.
... ... @@ -104,6 +105,7 @@ public:
104 105 //if wavelength is smaller than the first one in header file
105 106 if ( w[page] > wavelength ){
106 107 band_index(p, page);
  108 + if(PROGRESS) progress = 100;
107 109 return true;
108 110 }
109 111  
... ... @@ -114,6 +116,7 @@ public:
114 116 // (the wavelength is out of bounds)
115 117 if (page == Z()) {
116 118 band_index(p, Z()-1); //return the last band
  119 + if(PROGRESS) progress = 100;
117 120 return true;
118 121 }
119 122 }
... ... @@ -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 407 size_t readlines(T* dest, size_t start, size_t n){
381 408 return hsi<T>::read(dest, 0, start, 0, X(), n, Z());
382 409 }
... ... @@ -648,13 +675,24 @@ public:
648 675  
649 676 //to make sure the left and the right bound are in the bandwidth
650 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 692 //to make sure right bound is bigger than left bound
655 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 698 //find the indices of the left and right baseline points
... ... @@ -994,7 +1032,7 @@ public:
994 1032 matrix[i*Z() + b] = band_image[xy]; //copy it to the appropriate point in the values[] array
995 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 1234 bool PROGRESS = false){
1197 1235  
1198 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 1241 //calculate the size of a single band
1204 1242 unsigned long long L = samples * lines * sizeof(T);
... ... @@ -1219,7 +1257,7 @@ public:
1219 1257 file.seekg( (b0 * X() * Y() + y0 * X() + x0) * sizeof(T), std::ios::beg);
1220 1258  
1221 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 1262 //std::cout<<z<<std::endl;
1225 1263 for (unsigned long long y = 0; y < lines; y++)
... ... @@ -1227,7 +1265,7 @@ public:
1227 1265 file.read((char *)(temp + y * samples), sizeof(T) * samples);
1228 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 1270 out.write(reinterpret_cast<const char*>(temp), L); //write slice data into target file
1233 1271 file.seekg(jumpb, std::ios::cur);
... ... @@ -1237,6 +1275,52 @@ public:
1237 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 1324 /// Remove a list of bands from the ENVI file
1241 1325  
1242 1326 /// @param outfile is the file name for the output hyperspectral image (with trimmed bands)
... ... @@ -1311,6 +1395,20 @@ public:
1311 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 1412 /// Convolve the given band range with a kernel specified by a vector of coefficients.
1315 1413  
1316 1414 /// @param outfile is an already open stream to the output file
... ...
stim/envi/envi.h
1 1 #ifndef STIM_ENVI_H
2 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 11 #include <iostream>
10 12 #include <fstream>
11 13 //#include "../image/image.h"
... ... @@ -69,14 +71,67 @@ public:
69 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 75 header.load(headername);
74 76  
75 77 fname = filename; //save the filename
76 78  
77 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 137 void* malloc_spectrum(){
... ... @@ -308,7 +363,7 @@ public:
308 363 }
309 364  
310 365 /// Open a previously opened ENVI file
311   - bool open(){
  366 + bool open(stim::iotype io = stim::io_in){
312 367  
313 368 //load the file
314 369 if(header.interleave == envi_header::BSQ) { //if the infile is bsq file
... ... @@ -396,7 +451,7 @@ public:
396 451  
397 452 /// @param filename is the name of the ENVI binary file
398 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 457 header = h; //store the header
... ... @@ -404,7 +459,7 @@ public:
404 459  
405 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 468  
414 469 /// @param filename is the name of the ENVI binary file
415 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 473 //allocate memory
419 474 //allocate();
420 475  
421 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 482 //load the header
425 483 //header.load(headername);
426 484  
427   - return open(filename, h);
  485 + return open(filename, h, io);
428 486 }
429 487  
430 488 /// Normalize a hyperspectral ENVI file given a band number and threshold.
... ... @@ -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 600 /// Performs piecewise linear baseline correction of a hyperspectral file/
508 601  
509 602 /// @param outfile is the file name for the baseline corrected output
... ... @@ -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 648 if(header.interleave == envi_header::BSQ){ //if the infile is bsq file
554 649 std::cout<<"ERROR: BSQ projection not supported"<<std::endl;
555 650 exit(1);
... ... @@ -562,9 +657,9 @@ public:
562 657  
563 658 else if(header.interleave == envi_header::BIP){ //if the infile is bip file
564 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 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 663 else{
569 664 std::cout<<"ERROR: unidentified data type"<<std::endl;
570 665 exit(1);
... ... @@ -573,7 +668,7 @@ public:
573 668  
574 669 stim::envi_header out_hdr = header;
575 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 672 out_hdr.band_names.clear();
578 673 out_hdr.save(outfile + ".hdr"); //save the output header
579 674 }
... ... @@ -636,7 +731,7 @@ public:
636 731 ((bsq<double>*)file)->bil(outfile, PROGRESS, OPTIMIZATION);
637 732 else if(interleave == envi_header::BIP){ //ERROR
638 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 735 //exit(1);
641 736 }
642 737 }
... ... @@ -989,7 +1084,7 @@ public:
989 1084 else if(header.data_type == envi_header::float64)
990 1085 return ((bsq<double>*)file)->ph_to_ph((double*)result, lb1, rb1, pos1, lb2, rb2, pos2, mask);
991 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 1090 else if(header.interleave == envi_header::BIL){ //if the infile is bil file
... ... @@ -998,7 +1093,7 @@ public:
998 1093 else if(header.data_type == envi_header::float64)
999 1094 return ((bil<double>*)file)->ph_to_ph((double*)result, lb1, rb1, pos1, lb2, rb2, pos2, mask);
1000 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 1099 else if(header.interleave == envi_header::BIP){ //if the infile is bip file
... ... @@ -1007,11 +1102,11 @@ public:
1007 1102 else if(header.data_type == envi_header::float64)
1008 1103 return ((bip<double>*)file)->ph_to_ph((double*)result, lb1, rb1, pos1, lb2, rb2, pos2, mask);
1009 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 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 1110 exit(1);
1016 1111 }
1017 1112 return false;
... ... @@ -1273,6 +1368,39 @@ public:
1273 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 1404 // Retrieve a spectrum at the specified 1D location
1277 1405  
1278 1406 /// @param ptr is a pointer to pre-allocated memory of size B*sizeof(T)
... ... @@ -1325,6 +1453,7 @@ public:
1325 1453 exit(1);
1326 1454 }
1327 1455 }
  1456 + free(temp);
1328 1457 }
1329 1458  
1330 1459 /// Retrieve a spectrum from the specified (x, y) location
... ... @@ -1441,16 +1570,16 @@ public:
1441 1570 /// @param co is a pointer to pre-allocated memory of size [B * B] that stores the resulting covariance matrix
1442 1571 /// @param avg is a pointer to memory of size B that stores the average spectrum
1443 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 1574 if (header.interleave == envi_header::BSQ){
1446 1575 std::cout<<"ERROR: calculating the covariance matrix for a BSQ file is impractical; convert to BIL or BIP first"<<std::endl;
1447 1576 exit(1);
1448 1577 }
1449 1578 else if (header.interleave == envi_header::BIL){
1450 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 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 1583 else{
1455 1584 std::cout << "ERROR: unidentified data type" << std::endl;
1456 1585 exit(1);
... ... @@ -1458,9 +1587,9 @@ public:
1458 1587 }
1459 1588 else if (header.interleave == envi_header::BIP){
1460 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 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 1593 else{
1465 1594 std::cout << "ERROR: unidentified data type" << std::endl;
1466 1595 exit(1);
... ... @@ -1474,7 +1603,7 @@ public:
1474 1603 /// @param co is a pointer to pre-allocated memory of size [B * B] that stores the resulting covariance matrix
1475 1604 /// @param avg is a pointer to memory of size B that stores the average spectrum
1476 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 1607 if (header.interleave == envi_header::BSQ){
1479 1608 std::cout<<"ERROR: calculating the covariance matrix of noise for a BSQ file is impractical; convert to BIP first"<<std::endl;
1480 1609 exit(1);
... ... @@ -1488,9 +1617,9 @@ public:
1488 1617  
1489 1618 else if (header.interleave == envi_header::BIP){
1490 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 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 1623 else{
1495 1624 std::cout << "ERROR: unidentified data type" << std::endl;
1496 1625 exit(1);
... ... @@ -1517,11 +1646,11 @@ public:
1517 1646  
1518 1647 //save the header for the cropped file
1519 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 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 1654 new_header.wavelength = std::vector<double>(first, last);
1526 1655 new_header.save(outfile + ".hdr");
1527 1656  
... ... @@ -1558,6 +1687,41 @@ public:
1558 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 1725 /// Remove a list of bands from the ENVI file
1562 1726  
1563 1727 /// @param outfile is the file name for the output hyperspectral image (with trimmed bands)
... ... @@ -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 1893 /// Convolve the given band range with a kernel specified by a vector of coefficients.
1669 1894  
1670 1895 /// @param outfile is the combined file to be output
... ... @@ -1835,6 +2060,44 @@ public:
1835 2060 }
1836 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 2101 }; //end ENVI
1839 2102  
1840 2103 } //end namespace rts
... ...
stim/envi/envi_header.h
... ... @@ -8,6 +8,7 @@
8 8 #include <vector>
9 9 #include <algorithm>
10 10 #include <stdlib.h>
  11 +#include <cmath>
11 12  
12 13 //information from an ENVI header file
13 14 //A good resource can be found here: http://www.exelisvis.com/docs/enviheaderfiles.html
... ... @@ -77,6 +78,14 @@ struct envi_header
77 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 89 std::string trim(std::string line){
81 90  
82 91 if(line.length() == 0)
... ... @@ -416,8 +425,13 @@ struct envi_header
416 425 default:
417 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 436 /// Convert an interleave type to a string
423 437 static std::string interleave_str(interleaveType t){
... ...
stim/envi/hsi.h
1 1 #ifndef STIM_HSI_H
2 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 6 #include <cstring>
7 7 #include <utility>
8 8  
... ... @@ -55,43 +55,19 @@ protected:
55 55 return n; //return the number of masked pixels
56 56 }
57 57  
  58 + //perform linear interpolation between two bands
58 59 T lerp(double w, T low_v, double low_w, T high_v, double high_w){
59 60 if(low_w == high_w) return low_v; //if the interval is of zero length, just return one of the bounds
60 61 double alpha = (w - low_w) / (high_w - low_w); //calculate the interpolation factor
61 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 65 /// Returns the interpolated in the given spectrum based on the given wavelength
90 66  
91 67 /// @param s is the spectrum in main memory of length Z()
92 68 /// @param wavelength is the wavelength value to interpolate out
93 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 71 band_bounds(wavelength, low, high); //get the surrounding band indices
96 72  
97 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 114 }
139 115  
140 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 142 /// Get a mask that has all pixels with inf or NaN values masked out (false)
142 143 void mask_finite(unsigned char* out_mask, unsigned char* mask, bool PROGRESS = false){
143 144 size_t XY = X() * Y();
144 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 147 else //if a mask is provided
147 148 memcpy(out_mask, mask, XY * sizeof(unsigned char)); //initialize the current mask to that one
148 149 T* page = (T*)malloc(R[0] * R[1] * sizeof(T)); //allocate space for a page of data
... ... @@ -224,4 +225,4 @@ public:
224 225  
225 226 } //end namespace STIM
226 227  
227   -#endif
228 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 2 #define STIM_GL_SPIDER_H
3 3  
4 4 //#include <GL/glew.h>
... ... @@ -27,7 +27,6 @@
27 27 #include <stim/cuda/branch_detection.cuh>
28 28 #include "../../../volume-spider/glnetwork.h"
29 29 #include <stim/visualization/cylinder.h>
30   -#include <stim/cuda/testKernel.cuh>
31 30 #include <iostream>
32 31 #include <fstream>
33 32 #ifdef TIMING
... ... @@ -40,6 +39,9 @@
40 39 #include <ctime>
41 40 #endif
42 41  
  42 +#ifdef DEBUG
  43 + #include <stim/cuda/testKernel.cuh>
  44 +#endif
43 45  
44 46 namespace stim
45 47 {
... ... @@ -138,11 +140,13 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
138 140 stim::cuda::cuda_texture t_pos; //cuda_texture object used as an interface between OpenGL and cuda for position vectors.
139 141 stim::cuda::cuda_texture t_mag; //cuda_texture object used as an interface between OpenGL and cuda for size vectors.
140 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 147 #ifdef DEBUG
144   - stringstream name;
145 148 int iter;
  149 + stringstream name;
146 150 int iter_pos;
147 151 int iter_dir;
148 152 int iter_siz;
... ... @@ -292,6 +296,7 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
292 296 DrawLongCylinder(n, l_template, l_square); ///Draw the cylinder.
293 297 stim::cylinder<float> cyl(cL, cM);
294 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 300 stim::vec3<float> size(S[0]*R[0], S[1]*R[1], S[2]*R[2]); ///the borders of the texture.
296 301 float pval; //pvalue associated with the points on the cylinder.
297 302 if(!result.empty()) ///if we have any points
... ... @@ -315,7 +320,8 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
315 320 }
316 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 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 325 if(
320 326 !(v[0] > size[0] || v[1] > size[1]
321 327 || v[2] > size[2] || v[0] < 0
... ... @@ -372,7 +378,7 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
372 378 ///Stored in a display list.
373 379 ///uses the default d vector <0,0,1>
374 380 void
375   - genDirectionVectors(float solidAngle = stim::PI/2)
  381 + genDirectionVectors(float solidAngle = 3*stim::PI/4)
376 382 {
377 383  
378 384 //Set up the vectors necessary for Rectangle creation.
... ... @@ -954,7 +960,7 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
954 960 iter_dir = 0;
955 961 iter_siz = 0;
956 962 #endif
957   - stepsize = 3.0;
  963 + stepsize = 6.0;
958 964 n_pixels = 16.0;
959 965  
960 966 srand(100);
... ... @@ -1316,20 +1322,20 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1316 1322 void
1317 1323 saveNetwork(std::string name)
1318 1324 {
1319   -/* stim::glObj<float> sk;
  1325 + stim::glObj<float> sk1;
1320 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 1329 std::vector<stim::vec3< float > > ce = nt.getEdgeCenterLine(i);
1324   - sk.Begin(stim::OBJ_LINE);
  1330 + sk1.Begin(stim::OBJ_LINE);
1325 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 1341 ///Depreciated, but might be reused later()
... ... @@ -1377,20 +1383,31 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1377 1383 Step()
1378 1384 {
1379 1385 #ifdef DEBUG
1380   - std::cerr << "Took a step" << std::endl;
  1386 + std::cerr << "Took a step";
1381 1387 #endif
1382 1388 Bind(direction_texID, direction_buffID, numSamples, n_pixels);
1383 1389 CHECK_OPENGL_ERROR
1384 1390 findOptimalDirection();
1385 1391 Unbind();
  1392 + #ifdef DEBUG
  1393 + std::cerr << " " << current_cost;
  1394 + #endif
1386 1395 Bind(position_texID, position_buffID, numSamplesPos, n_pixels);
1387 1396 findOptimalPosition();
1388 1397 Unbind();
  1398 + #ifdef DEBUG
  1399 + std::cerr << " " << current_cost;
  1400 + #endif
1389 1401 Bind(radius_texID, radius_buffID, numSamplesMag, n_pixels);
1390 1402 findOptimalRadius();
1391 1403 Unbind();
  1404 + #ifdef DEBUG
  1405 + std::cerr << " " << current_cost;
  1406 + #endif
1392 1407 CHECK_OPENGL_ERROR
1393   -
  1408 + #ifdef DEBUG
  1409 + std::cerr << std::endl;
  1410 + #endif
1394 1411 return current_cost;
1395 1412 }
1396 1413  
... ... @@ -1517,9 +1534,6 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1517 1534 while(!Empty())
1518 1535 {
1519 1536 //clear the currently traced line and start a new one.
1520   - cL.clear();
1521   - cM.clear();
1522   - cD.clear();
1523 1537 curSeed = seeds.top();
1524 1538 curSeedVec = seedsvecs.top();
1525 1539 curSeedMag = seedsmags.top();
... ... @@ -1539,9 +1553,9 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1539 1553 // findOptimalDirection();
1540 1554 // Unbind();
1541 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 1559 //THIS IS EXPERIMENTAL
1546 1560  
1547 1561 // cL.push_back(curSeed);
... ... @@ -1593,17 +1607,17 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1593 1607 ds[0], ds[1], ds[2],
1594 1608 ups[0], ups[1], ups[2]);
1595 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 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 1622 // glPopName();
1609 1623 glFlush(); ///Flush the buffer
... ... @@ -1654,55 +1668,51 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1654 1668 cM.clear();
1655 1669 }
1656 1670  
1657   -/*
  1671 +
1658 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 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 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 1685 //did we start with a fiber?
1679 1686 if(h != -1 && h < nt.sizeE())
1680   - nt.addEdge(ce, cm, h, -1);
  1687 + nt.addEdge(L, M, h, -1);
1681 1688 else
1682   - nt.addEdge(ce, cm, -1, -1);
  1689 + nt.addEdge(L, M, -1, -1);
1683 1690 }
1684 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 1699 //did start with a fiber?
1693 1700 if(h != -1 && h < nt.sizeE()){
1694 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 1713 #endif
1704 1714 }
1705   -*/
  1715 +/*
1706 1716 void
1707 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 1732 #endif
1723 1733 }
1724 1734 }
1725   -
  1735 +*/
1726 1736  
1727 1737 void
1728 1738 printSizes()
... ... @@ -1735,22 +1745,31 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1735 1745 traceLine(stim::vec3<float> pos, float mag, int min_cost)
1736 1746 {
1737 1747 //starting (seed) position and magnitude.
  1748 + last_fiber = -1;
  1749 + cL.clear();
  1750 + cM.clear();
  1751 + cD.clear();
  1752 +
1738 1753 stim::vec3<float> spos = getPosition();
  1754 + stim::vec3<float> sdir = getDirection();
1739 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 1773 int h;
1755 1774 bool started = false;
1756 1775 bool running = true;
... ... @@ -1761,7 +1780,7 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1761 1780 if (cost > min_cost){
1762 1781 running = false;
1763 1782 branchDetection2();
1764   - addToNetwork(cL, cM);
  1783 + addToNetwork(cL, cM, spos, sdir, smag);
1765 1784 #ifdef DEBUG
1766 1785 std::cerr << "the cost of " << cost << " > " << min_cost << std::endl;
1767 1786 #endif
... ... @@ -1769,13 +1788,14 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1769 1788 } else {
1770 1789 //Have we found the edge of the map?
1771 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 1795 running = false;
1777 1796 branchDetection2();
1778   - addToNetwork(cL, cM);
  1797 + // addToNetwork(cL, cM);
  1798 + addToNetwork(cL, cM, spos, sdir, smag);
1779 1799 #ifdef DEBUG
1780 1800 std::cerr << "I hit and edge" << std::endl;
1781 1801 #endif
... ... @@ -1790,10 +1810,11 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1790 1810 }
1791 1811 //Has the template size gotten unreasonable?
1792 1812 mag = getMagnitude();
1793   - if(mag > 75 || mag < 1){
  1813 + if(m > 75 || m < 1){
1794 1814 running = false;
1795 1815 branchDetection2();
1796   - addToNetwork(cL, cM);
  1816 + // addToNetwork(cL, cM);
  1817 + addToNetwork(cL, cM, spos, sdir, smag);
1797 1818 #ifdef DEBUG
1798 1819 std::cerr << "The templates are too big" << std::endl;
1799 1820 #endif
... ... @@ -1807,13 +1828,16 @@ class gl_spider // : public virtual gl_texture&lt;T&gt;
1807 1828 #ifdef DEBUG
1808 1829 std::cerr << "I hit the fiber " << h << std::endl;
1809 1830 #endif
  1831 + last_fiber = h;
1810 1832 running = false;
1811 1833 branchDetection2();
1812   - addToNetwork(cL, cM);
  1834 + // addToNetwork(cL, cM);
  1835 + addToNetwork(cL, cM, spos, sdir, smag);
1813 1836 break;
1814 1837 }
1815 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 1841 cM.push_back(m);
1818 1842 // Unbind();
1819 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 30 GLenum cpu_type;
31 31 GLenum gpu_type;
32 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 34 //using image_stack<T>::S;
35   - using image_stack<T>::ptr;
  35 + using image_stack<T,F>::ptr;
36 36  
37 37 /// Sets the internal texture_type, based on the data dimensions
38 38 void setTextureType(){
... ... @@ -247,7 +247,7 @@ class gl_texture : public virtual image_stack&lt;T, F&gt;
247 247 }
248 248  
249 249 ///returns the dimentions of the data in the x, y, z directions.
250   - vec<int> getSize(){
  250 + stim::vec<int> getSize(){
251 251 stim::vec<int> size(R[1], R[2], R[3]);
252 252 return size;
253 253 }
... ... @@ -282,7 +282,7 @@ class gl_texture : public virtual image_stack&lt;T, F&gt;
282 282 ///@param file_mask specifies the file(s) to be loaded
283 283 /// Sets the path and calls the loader on that path.
284 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 286 guess_parameters();
287 287 }
288 288  
... ... @@ -292,13 +292,18 @@ class gl_texture : public virtual image_stack&lt;T, F&gt;
292 292 return texID;
293 293 }
294 294  
295   -
  295 +
296 296 T* getData(){
297 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 33 \ No newline at end of file
... ...
stim/grids/image_stack.h
... ... @@ -5,6 +5,7 @@
5 5 #include <stim/parser/filename.h>
6 6 #include <stim/grids/grid.h>
7 7 #include <stim/image/image.h>
  8 +#include <stim/math/vec3.h>
8 9  
9 10 namespace stim{
10 11  
... ... @@ -17,13 +18,13 @@ class image_stack : public virtual stim::grid&lt;T, 4, F&gt;{
17 18  
18 19 protected:
19 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 25 public:
25 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 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 55 /// Returns the number of color channels
41 56 size_t channels(){
42 57 return R[0];
... ... @@ -113,7 +128,8 @@ public:
113 128 /// @param i is the page to be saved
114 129 void save_image(std::string file_name, unsigned int i){
115 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 133 I.save(file_name);
118 134 }
119 135  
... ... @@ -138,13 +154,14 @@ public:
138 154 /// @param depth, number of pixels in depth.
139 155 void init(int channels, int width, int height, int depth)
140 156 {
141   - R.resize(4);
  157 + //R.resize(4);
142 158 R[0] = channels;
143 159 R[1] = width;
144 160 R[2] = height;
145 161 R[3] = depth;
146 162  
147 163 ptr = (T*)malloc(sizeof(T) * samples());
  164 + memset(ptr, 0, sizeof(T) * samples());
148 165 }
149 166  
150 167 ///Saves the entire stack to a set of images
... ... @@ -152,12 +169,13 @@ public:
152 169 void save_images(std::string file_mask){
153 170  
154 171 stim::filename file_path(file_mask);
  172 + //stim::filename abs_file_path = file_pat
155 173  
156 174 //create a list of file names
157 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 177 save_image(file_list[i], i);
  178 + }
161 179 }
162 180  
163 181 /// Returns the pixel at the specified point
... ... @@ -165,9 +183,35 @@ public:
165 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 211 void read(std::string file, unsigned int X, unsigned int Y, unsigned int Z, unsigned int C = 1, unsigned int header = 0){
169 212 read(file, stim::vec<unsigned long>(C, X, Y, Z), header);
170 213 }
  214 + */
171 215  
172 216 T* data(){
173 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 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 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 10  
11 11 // this kernel calculates the local maximum for finding the cell centers
12 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 15 // calculate the 2D coordinates for this current thread.
16 16 int xi = blockIdx.x * blockDim.x + threadIdx.x;
... ... @@ -20,30 +20,21 @@ namespace stim{
20 20 return;
21 21  
22 22 // convert 2D coordinates to 1D
23   - int i = yi * x + xi;
  23 + int i = yi * x + xi;
24 24  
25 25 gpuCenters[i] = 0; //initialize the value at this location to zero
26 26  
27 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 31 if(xl >= 0 && xl < x && yl >= 0 && yl < y){
40   - int il = yl * x + xl;
  32 + unsigned int il = yl * x + xl;
41 33 if(gpuVote[il] > val){
42 34 return;
43 35 }
44 36 if (gpuVote[il] == val){
45   - /*IdxEq[n] = il;
46   - n = n+1;*/
  37 +
47 38 if( il > i){
48 39 return;
49 40 }
... ... @@ -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 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 52 unsigned int max_threads = stim::maxThreadsPerBlock();
66 53 /*dim3 threads(max_threads, 1);
67 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 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 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 65 //calculate the number of bytes in the array
79 66 unsigned int bytes = x * y * sizeof(T);
... ... @@ -90,7 +77,7 @@ namespace stim{
90 77 HANDLE_ERROR(cudaMemcpy(gpuVote, cpuVote, bytes, cudaMemcpyHostToDevice));
91 78  
92 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 82 //copy the cell centers data to the CPU
96 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 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 42 int x_table = 2*rmax +1;
43   - int lut_i;
44 43 T rmax_sq = rmax * rmax;
  44 +
  45 + int lut_i;
45 46 T dx_sq, dy_sq;
46 47  
47 48 bb.trim_low(0, 0); //make sure the bounding box doesn't go outside the image
... ... @@ -49,11 +50,12 @@ namespace stim{
49 50  
50 51 int by, bx;
51 52 int dx, dy; //coordinate relative to (xi, yi)
  53 +
52 54 T v;
53 55 T max_v = 0; //initialize the maximum vote value to zero
54 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 59 for(by = bb.low[1]; by <= bb.high[1]; by++){ //for each element in the bounding box
58 60 dy = by - yi; //calculate the y coordinate of the current point relative to yi
59 61 dy_sq = dy * dy;
... ... @@ -79,26 +81,26 @@ namespace stim{
79 81  
80 82 // this kernel updates the gradient direction by the calculated voting direction.
81 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 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 90 if(xi >= x || yi >= y) return;
89 91  
90 92 // convert 2D coordinates to 1D
91   - int i = yi * x + xi;
  93 + size_t i = yi * x + xi;
92 94  
93 95 //update the gradient image with the vote direction
94 96 gpuGrad[2*i] = gpuDir[i];
95 97 }
96 98  
97 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 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 105 // allocate space on the GPU for the updated vote direction
104 106 T* gpuDir;
... ... @@ -106,14 +108,14 @@ namespace stim{
106 108  
107 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 114 size_t table_bytes = sizeof(T) * (rmax * 2 + 1) * (rmax * 2 + 1);
113 115 //size_t curtain = 2 * rmax;
114 116 //size_t template_bytes = sizeof(T) * (threads.x + curtain) * (threads.y + curtain);
115 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 120 size_t shared_mem = stim::sharedMemPerBlock();
119 121 if(shared_mem_req > shared_mem){
... ... @@ -122,16 +124,10 @@ namespace stim{
122 124 }
123 125  
124 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 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 131 //free allocated memory
136 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 9 #include <stim/visualization/colormap.h>
10 10 #include <math.h>
11 11  
  12 +
  13 +
12 14 namespace stim{
13 15 namespace cuda{
14 16  
15 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 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 21 extern __shared__ T S[];
20 22 T* shared_atan = S;
... ... @@ -22,12 +24,12 @@ namespace stim{
22 24 stim::cuda::threadedMemcpy((char*)shared_atan, (char*)gpuTable, sizeof(T) * n_table, threadIdx.x, blockDim.x);
23 25  
24 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 30 if(xi >= x || yi >= y) return;
29 31 // convert 2D coordinates to 1D
30   - int i = yi * x + xi;
  32 + size_t i = yi * x + xi;
31 33  
32 34 // calculate the voting direction based on the grtadient direction
33 35 float theta = gpuGrad[2*i];
... ... @@ -50,7 +52,7 @@ namespace stim{
50 52 bb.trim_low(0, 0); //make sure the bounding box doesn't go outside the image
51 53 bb.trim_high(x-1, y-1);
52 54  
53   - int by, bx;
  55 + size_t by, bx;
54 56 int dx, dy;
55 57  
56 58 unsigned int ind_g; //initialize the maximum vote value to zero
... ... @@ -66,7 +68,8 @@ namespace stim{
66 68 alpha = shared_atan[lut_i];
67 69 if(dx_sq + dy_sq < rmax_sq && abs(alpha - theta) < phi){
68 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 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 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 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 94 size_t table_bytes = sizeof(T) * (rmax * 2 + 1) * (rmax * 2 + 1);
86 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 97 size_t shared_mem = stim::sharedMemPerBlock();
89 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 100 exit(1);
92 101 }
93 102  
94 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 269 \ No newline at end of file
... ...
stim/image/image.h
1 1 #ifndef STIM_IMAGE_H
2 2 #define STIM_IMAGE_H
3 3  
  4 +#ifdef _WIN32
  5 +#undef max
  6 +#endif
  7 +
4 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 14 #endif
8 15 #include <vector>
9 16 #include <iostream>
10   -#include <limits>
  17 +#include <limits> //use limits and remove the MIN and MAX macros
11 18 #include <typeinfo>
12 19 #include <fstream>
  20 +#include <cstring>
  21 +
  22 +
  23 +#include <stim/parser/filename.h>
13 24  
14 25 namespace stim{
15 26 /// This static class provides the STIM interface for loading, saving, and storing 2D images.
... ... @@ -45,6 +56,10 @@ class image{
45 56 void allocate(){
46 57 unalloc();
47 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 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 67 allocate(); //allocate memory
53 68 }
54 69  
55   - size_t bytes(){ return size() * sizeof(T); }
56   -
57 70 inline size_t idx(size_t x, size_t y, size_t c = 0) const {
58 71 return y * R[0] * R[1] + x * R[0] + c;
59 72 }
... ... @@ -74,23 +87,16 @@ class image{
74 87 #endif
75 88 /// Returns the value for "white" based on the dynamic range (assumes white is 1.0 for floating point images)
76 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 96 public:
93 97  
  98 + size_t bytes() { return size() * sizeof(T); }
  99 +
94 100 /// Default constructor - creates an empty image object
95 101 image(){ init(); } //initialize all variables to zero, don't allocate any memory
96 102  
... ... @@ -125,19 +131,49 @@ public:
125 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 135 void resize(size_t x, size_t y, size_t c = 1) {
130 136 allocate(x, y, c);
131 137 }
132 138  
133 139 stim::image<T>& operator=(const stim::image<T>& I){
134   - init();
135 140 if(&I == this) //handle self-assignment
136 141 return *this;
  142 + init();
137 143 allocate(I.X(), I.Y(), I.C());
138 144 memcpy(img, I.img, bytes());
139 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 178 //save a Netpbm file
143 179 void load_netpbm(std::string filename) {
... ... @@ -146,10 +182,6 @@ public:
146 182 std::cout << "Error opening input file in image::load_netpbm()" << std::endl;
147 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 186 size_t nc; //allocate space for the number of channels
155 187 char format[2]; //allocate space to hold the image format tag
... ... @@ -196,9 +228,12 @@ public:
196 228 }
197 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 253 }
219 254 }
220 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 264 /// Load an image from a file
222 265 void load(std::string filename){
223 266 #ifdef USING_OPENCV
... ... @@ -226,17 +269,24 @@ public:
226 269 std::cout<<"ERROR stim::image::load() - unable to find image "<<filename<<std::endl;
227 270 exit(1);
228 271 }
  272 + int cv_type = cvImage.type();
229 273 int cols = cvImage.cols;
230 274 int rows = cvImage.rows;
231 275 int channels = cvImage.channels();
232 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 282 if(C() == 3) //if this is a 3-color image, OpenCV uses BGR interleaving
237 283 from_opencv(cv_ptr, X(), Y());
238 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 290 #endif
241 291 }
242 292  
... ... @@ -267,9 +317,20 @@ public:
267 317 outfile.write((const char*)img, size()); //write the binary data
268 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 326 //save a file
272 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 334 #ifdef USING_OPENCV
274 335 //OpenCV uses an interleaved format, so convert first and then output
275 336 T* buffer = (T*) malloc(bytes());
... ... @@ -282,10 +343,34 @@ public:
282 343 cv::imwrite(filename, cvImage);
283 344 free(buffer);
284 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 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 374 void set_interleaved(T* buffer, size_t width, size_t height, size_t channels){
290 375 allocate(width, height, channels);
291 376 memcpy(img, buffer, bytes());
... ... @@ -379,18 +464,92 @@ public:
379 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 472 /// Set all elements in the image to a given scalar value
383 473  
384 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 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 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 555 /// Copy the given data to the specified channel
... ... @@ -474,7 +633,6 @@ public:
474 633 max_val = img[n];
475 634 }
476 635 }
477   -
478 636 return max_val;
479 637 }
480 638  
... ... @@ -502,13 +660,48 @@ public:
502 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 699 image<T> srgb2lab(){
506 700 std::cout<<"ERROR stim::image::srgb2lab - function has been broken, re-implement."<<std::endl;
507 701 exit(1);
508 702 }
509 703  
510 704 image<T> convolve2(image<T> mask){
511   -
512 705 std::cout<<"ERROR stim::image::convolve2 - function has been broken, and shouldn't really be in here."<<std::endl;
513 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 17 template< typename P >
18 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 25 int i,k,m;
26 26 P ga,gr,r,z;
... ... @@ -94,10 +94,10 @@ template&lt;typename P&gt;
94 94 int bessjy01a(P x,P &j0,P &j1,P &y0,P &y1,
95 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 102 P x2,r,ec,w0,w1,r0,r1,cs0,cs1;
103 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 606 P b,ec,w0,w1,bju0,bju1,pv0,pv1,byvk;
607 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 614 x2 = x*x;
615 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 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 1513 template<typename P>
1512 1514 int cbessjyva_sph(int v,complex<P> z,P &vm,complex<P>*cjv,
1513 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 18  
19 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 25 init()
25 26 {
26 27 Y = U.cross(N).norm();
27   - }
  28 + }*/
28 29  
29 30 public:
30 31 using stim::plane<T>::n;
... ... @@ -34,6 +35,8 @@ public:
34 35 using stim::plane<T>::rotate;
35 36 using stim::plane<T>::setU;
36 37  
  38 + using stim::plane<T>::init;
  39 +
37 40 ///base constructor
38 41 ///@param th value of the angle of the starting point from 0 to 360.
39 42 CUDA_CALLABLE
... ... @@ -42,26 +45,28 @@ public:
42 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 49 ///@param size: size of the rectangle in ND space.
47 50 ///@param z_pos z coordinate of the rectangle.
48 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 60 ///create a rectangle from a center point, normal
57 61 ///@param c: x,y,z location of the center.
58 62 ///@param n: x,y,z direction of the normal.
59 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 72 /*///create a rectangle from a center point, normal, and size
... ... @@ -84,14 +89,18 @@ public:
84 89 ///@param n: x,y,z direction of the normal.
85 90 ///@param u: x,y,z direction for the zero vector (from where the rotation starts)
86 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 96 setU(u);
  97 + R = r;
  98 + //init();
  99 + //setU(u);
91 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 106 ///scales the circle by a certain factor
... ... @@ -99,86 +108,111 @@ public:
99 108 CUDA_CALLABLE
100 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 124 ///sets the normal for the cirlce
107 125 ///@param n: x,y,z direction of the normal.
108 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 131 ///sets the center of the circle.
115 132 ///@param n: x,y,z location of the center.
116 133 CUDA_CALLABLE void
117 134 center(vec3<T> p){
118   - this->P = p;
  135 + P = p;
119 136 }
120 137  
121 138 ///boolean comparison
122 139 bool
123 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 143 return true;
127 144 else
128 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 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 169 stim::vec3<T> result;
135 170  
136 171 vec3<T> A = this->P - this->U * (T)0.5 - Y * (T)0.5;
137 172 result = A + this->U * a + Y * b;
138 173 return result;
139   - }
  174 + }*/
140 175  
141 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 187 ///returns a vector with the points on the initialized circle.
148 188 ///connecting the points results in a circle.
149 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 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 212 std::string str() const
179 213 {
180 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 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 4 #define STIM_PI 3.1415926535897932384626433832795028841971693993751058209749445923078164062862
5 5 #define STIM_TAU 2 * STIM_PI
6 6  
7   -#include "stim/cuda/cudatools/callable.h"
8 7 namespace stim{
9 8 const double PI = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862;
10 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 120 \ No newline at end of file
... ...
stim/math/filters/gauss2.h renamed to stim/math/filters/gauss2.cuh
... ... @@ -2,7 +2,7 @@
2 2 #define STIM_CUDA_GAUSS2_H
3 3  
4 4 #include <stim/image/image.h>
5   -#include <stim/math/filters/sepconv2.h>
  5 +#include <stim/math/filters/sepconv2.cuh>
6 6 #include <stim/math/constants.h>
7 7  
8 8 namespace stim {
... ... @@ -18,8 +18,8 @@ namespace stim {
18 18 ///@param nstds specifies the number of standard deviations of the Gaussian that will be kept in the kernel
19 19 template<typename T, typename K>
20 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 23 size_t X = in.width() - kx + 1; //calculate the size of the output image
24 24 size_t Y = in.height() - ky + 1;
25 25 stim::image<T> r(X, Y, in.channels()); //create an output image
... ... @@ -33,10 +33,10 @@ namespace stim {
33 33 for (size_t yi = 0; yi < ky; yi++)
34 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 37 std::vector< stim::image<T> > R = r.split(); //split the output image into channels
38 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 41 r.merge(R); //merge the blurred channels into the final image
42 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 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 17 \ No newline at end of file
... ...
stim/math/filters/sepconv2.h renamed to stim/math/filters/sepconv2.cuh
1 1 #ifndef STIM_CUDA_SEPCONV2_H
2 2 #define STIM_CUDA_SEPCONV2_H
3   -#include <stim/math/filters/conv2.h>
  3 +#include <stim/math/filters/conv2.cuh>
4 4 #ifdef __CUDACC__
5 5 #include <stim/cuda/cudatools.h>
6 6 #include <stim/cuda/sharedmem.cuh>
... ... @@ -20,12 +20,12 @@ namespace stim {
20 20 cudaDeviceProp p;
21 21 HANDLE_ERROR(cudaGetDeviceProperties(&p, 0));
22 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 24 size_t X = sx - kx + 1; //calculate the x size of the output image
25 25 T* temp; //declare a temporary variable to store the intermediate image
26 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 29 size_t sm = (nt.x + kx - 1) * nt.y * sizeof(T); //shared memory bytes required to store block data
30 30 if (sm > p.sharedMemPerBlock) {
31 31 std::cout << "Error in stim::gpu_conv2() - insufficient shared memory for this kernel." << std::endl;
... ... @@ -34,7 +34,7 @@ namespace stim {
34 34 kernel_conv2 <<<nb, nt, sm>>> (temp, in, k0, sx, sy, kx, 1); //launch the kernel to compute the intermediate image
35 35  
36 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 38 sm = nt.x * (nt.y + ky - 1) * sizeof(T); //calculate the amount of shared memory needed for the second pass
39 39 if (sm > p.sharedMemPerBlock) {
40 40 std::cout << "Error in stim::gpu_conv2() - insufficient shared memory for this kernel." << std::endl;
... ... @@ -86,4 +86,4 @@ namespace stim {
86 86 }
87 87 }
88 88  
89   -#endif
90 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 4 //#include "rts/vector.h"
5 5 #include <string.h>
6 6 #include <iostream>
  7 +#include <fstream>
7 8 #include <stim/math/vector.h>
8 9 #include <stim/math/vec3.h>
9   -#include <stim/cuda/cudatools/callable.h>
  10 +//#include <stim/cuda/cudatools/callable.h>
10 11  
11 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 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 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 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 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 376 std::stringstream ss;
81 377  
82   - for(int r = 0; r < N; r++)
83   - {
  378 + for(int r = 0; r < R; r++) {
84 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 383 ss << "|" << std::endl;
90 384 }
91   -
92 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 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 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
... ...
stim/math/plane.h
... ... @@ -188,9 +188,9 @@ class plane
188 188 {
189 189 quaternion<T> q;
190 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 1 #ifndef RTS_QUATERNION_H
2 2 #define RTS_QUATERNION_H
3 3  
4   -#include <stim/math/matrix.h>
  4 +#include <stim/math/matrix_sq.h>
5 5 #include <stim/cuda/cudatools/callable.h>
6 6  
7 7 namespace stim{
... ... @@ -46,7 +46,9 @@ public:
46 46 from = from.norm();
47 47 to = to.norm();
48 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 52 //deal with a zero vector (both k and kn point in the same direction)
51 53 if(theta == (T)0){
52 54 return;
... ... @@ -81,9 +83,9 @@ public:
81 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 91 T wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2;
... ... @@ -114,9 +116,9 @@ public:
114 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 122 T wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2;
121 123  
122 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 1 #ifndef STIM_SPH_HARMONICS
2 2 #define STIM_SPH_HARMONICS
3 3  
4   -#include <stim/math/vector.h>
  4 +#include <complex>
5 5 #include <boost/math/special_functions/spherical_harmonic.hpp>
  6 +#include <stim/math/constants.h>
  7 +#include <stim/math/random.h>
6 8 #include <vector>
7 9  
8   -#define PI 3.14159
9 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 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 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 201 \ No newline at end of file
... ...
stim/math/vec3.h
... ... @@ -5,6 +5,8 @@
5 5 #include <stim/cuda/cudatools/callable.h>
6 6 #include <cmath>
7 7  
  8 +#include <sstream>
  9 +
8 10  
9 11 namespace stim{
10 12  
... ... @@ -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 74 CUDA_CALLABLE vec3<T> cart2sph() const{
73 75 vec3<T> sph;
74 76 sph.ptr[0] = len();
... ... @@ -236,9 +238,16 @@ public:
236 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 249 /// Outputs the vector as a string
241   - std::string str() const{
  250 +std::string str() const{
242 251 std::stringstream ss;
243 252  
244 253 const size_t N = 3;
... ...
stim/math/vector.h
... ... @@ -5,6 +5,7 @@
5 5 #include <cmath>
6 6 #include <sstream>
7 7 #include <vector>
  8 +#include <algorithm>
8 9  
9 10 #include <stim/cuda/cudatools/callable.h>
10 11 #include <stim/math/vec3.h>
... ... @@ -338,7 +339,7 @@ struct vec : public std::vector&lt;T&gt;
338 339 /// Cast to a vec3
339 340 operator stim::vec3<T>(){
340 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 343 for(size_t i = 0; i < N; i++)
343 344 r[i] = at(i);
344 345 return r;
... ...