Compare View

switch
from
...
to
 
Commits (21)
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/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 +
... ...
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
  9 +import scipy.ndimage
  10 +import progressbar
  11 +import glob
  12 +
  13 +def st2(I, s=1, dtype=numpy.float32):
  14 +
  15 + #calculates the 2D structure tensor for an image using a gaussian window with standard deviation s
  16 +
  17 + #calculate the gradient
  18 + dI = numpy.gradient(I)
  19 +
  20 + #calculate the dimensions of the tensor field
  21 + field_dims = [dI[0].shape[0], dI[0].shape[1], 3]
  22 +
  23 + #allocate space for the tensor field
  24 + Tg = numpy.zeros(field_dims, dtype=dtype)
  25 +
  26 + #calculate the gradient components of the tensor
  27 + ti = 0
  28 + for i in range(2):
  29 + for j in range(i + 1):
  30 + Tg[:, :, ti] = dI[j] * dI[i]
  31 + ti = ti + 1
  32 +
  33 + #blur the tensor field
  34 + T = numpy.zeros(field_dims, dtype=dtype)
  35 +
  36 + for i in range(3):
  37 + T[:, :, i] = scipy.ndimage.filters.gaussian_filter(Tg[:, :, i], [s, s])
  38 +
  39 +
  40 + return T
  41 +
  42 +def st3(I, s=1):
  43 + #calculate the structure tensor field for the 3D input image I given the window size s in 3D
  44 + #check the format for the window size
  45 + if type(s) is not list:
  46 + s = [s] * 3
  47 + elif len(s) == 1:
  48 + s = s * 3
  49 + elif len(s) == 2:
  50 + s.insert(1, s[0])
  51 +
  52 + print("\nCalculating gradient...")
  53 + dI = numpy.gradient(I)
  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 = numpy.zeros(field_dims, dtype=numpy.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 = numpy.zeros(field_dims, dtype=numpy.float32)
  73 +
  74 + print("\nConvolving tensor field...")
  75 + bar = progressbar.ProgressBar()
  76 + bar.max_value = 6
  77 + for i in range(6):
  78 + T[:, :, :, i] = scipy.ndimage.filters.gaussian_filter(Tg[:, :, :, i], s)
  79 + bar.update(i+1)
  80 +
  81 + return T
  82 +
  83 +def st(I, s=1):
  84 + if I.ndim == 3:
  85 + return st3(I, s)
  86 + elif I.ndim == 2:
  87 + return st2(I, s)
  88 + else:
  89 + print("Image must be 2D or 3D")
  90 + return
  91 +
  92 +
  93 +
  94 +def sym2mat(T):
  95 + #Calculate the full symmetric matrix from a list of lower triangular elements.
  96 + #The lower triangular components in the input field T are assumed to be the
  97 + # final dimension of the input matrix.
  98 +
  99 + # | 1 2 4 7 |
  100 + # | 0 3 5 8 |
  101 + # | 0 0 6 9 |
  102 + # | 0 0 0 10 |
  103 +
  104 + in_s = T.shape
  105 +
  106 + #get the number of tensor elements
  107 + n = in_s[T.ndim - 1]
  108 +
  109 + #calculate the dimension of the symmetric matrix
  110 + d = int(0.5 * (numpy.sqrt(8. * n + 1.) - 1.))
  111 +
  112 + #calculate the dimensions of the output field
  113 + out_s = list(in_s)[:-1] + [d] + [d]
  114 +
  115 + #allocate space for the output field
  116 + R = numpy.zeros(out_s)
  117 +
  118 + ni = 0
  119 + for i in range(d):
  120 + for j in range(i + 1):
  121 + R[..., i, j] = T[..., ni]
  122 + if i != j:
  123 + R[..., j, i] = T[..., ni]
  124 + ni = ni + 1
  125 +
  126 + return R
  127 +
  128 +def st2vec(S, vector='largest'):
  129 + #Create a color image from a 2D or 3D structure tensor slice
  130 +
  131 + #convert the field to a full rank-2 tensor
  132 + T = sym2mat(S);
  133 + del(S)
  134 +
  135 + #calculate the eigenvectors and eigenvalues
  136 + l, v = numpy.linalg.eig(T)
  137 +
  138 + #get the dimension of the tensor field
  139 + d = T.shape[2]
  140 +
  141 + #allocate space for the vector field
  142 + V = numpy.zeros([T.shape[0], T.shape[1], 3])
  143 +
  144 + idx = l.argsort()
  145 +
  146 + for di in range(d):
  147 + if vector == 'smallest':
  148 + b = idx[:, :, 0] == di
  149 + elif vector == 'largest':
  150 + b = idx[:, :, d-1] == di
  151 + else:
  152 + b = idx[:, :, 1] == di
  153 + V[b, 0:d] = v[b, :, di]
  154 +
  155 + return V
  156 +
  157 +def loadstack(filemask):
  158 + #Load an image stack as a 3D grayscale array
  159 +
  160 + #get a list of all files matching the given mask
  161 + files = [file for file in glob.glob(filemask)]
  162 +
  163 + #calculate the size of the output stack
  164 + I = scipy.misc.imread(files[0])
  165 + X = I.shape[0]
  166 + Y = I.shape[1]
  167 + Z = len(files)
  168 +
  169 + #allocate space for the image stack
  170 + M = numpy.zeros([X, Y, Z]).astype('float32')
  171 +
  172 + #create a progress bar
  173 + bar = progressbar.ProgressBar()
  174 + bar.max_value = Z
  175 +
  176 + #for each file
  177 + for z in range(Z):
  178 + #load the file and save it to the image stack
  179 + M[:, :, z] = scipy.misc.imread(files[z], flatten="True").astype('float32')
  180 + bar.update(z+1)
  181 + return M
  182 +
  183 +def anisotropy(S):
  184 +
  185 + Sf = sym2mat(S)
  186 +
  187 + #calculate the eigenvectors and eigenvalues
  188 + l, v = numpy.linalg.eig(Sf)
  189 +
  190 + #store the sorted eigenvalues
  191 + ls = numpy.sort(l)
  192 + l0 = ls[:, :, 0]
  193 + l1 = ls[:, :, 1]
  194 + l2 = ls[:, :, 2]
  195 +
  196 + #calculate the linear anisotropy
  197 + Cl = (l2 - l1)/(l2 + l1 + l0)
  198 +
  199 + #calculate the planar anisotropy
  200 + Cp = 2 * (l1 - l0) / (l2 + l1 + l0)
  201 +
  202 + #calculate the spherical anisotropy
  203 + Cs = 3 * l0 / (l2 + l1 + l0)
  204 +
  205 + #calculate the fractional anisotropy
  206 + l_hat = (l0 + l1 + l2)/3
  207 + fa_num = (l2 - l_hat) ** 2 + (l1 - l_hat) ** 2 + (l0 - l_hat) ** 2;
  208 + fa_den = l0 ** 2 + l1 ** 2 + l2 ** 2
  209 + FA = numpy.sqrt(3./2.) * numpy.sqrt(fa_num) / numpy.sqrt(fa_den)
  210 +
  211 + return FA, Cl, Cp, Cs
  212 +
  213 +def st2amira(filename, T):
  214 + #generates a tensor field that can be imported into Amira
  215 +
  216 + # 0 dx dx ----> 0
  217 + # 1 dx dy ----> 1
  218 + # 2 dy dy ----> 3
  219 + # 3 dx dz ----> 2
  220 + # 4 dy dz ----> 4
  221 + # 5 dz dz ----> 5
  222 +
  223 + #swap the 2nd and 3rd tensor components
  224 + A = numpy.copy(T)
  225 + A[..., 3] = T[..., 2]
  226 + A[..., 2] = T[..., 3]
  227 +
  228 + #roll the tensor axis so that it is the leading component
  229 + A = numpy.rollaxis(A, A.ndim - 1)
  230 + A.tofile(filename)
  231 + print("\n", A.shape)
  232 +
  233 +def resample3(T, s=2):
  234 + #resample a tensor field by an integer factor s
  235 + #This function first convolves the field with a box filter and then
  236 + # re-samples to create a smaller field
  237 +
  238 + #check the format for the window size
  239 + if type(s) is not list:
  240 + s = [s] * 3
  241 + elif len(s) == 1:
  242 + s = s * 3
  243 + elif len(s) == 2:
  244 + s.insert(1, s[0])
  245 + s = numpy.array(s)
  246 +
  247 + bar = progressbar.ProgressBar()
  248 + bar.max_value = T.shape[3]
  249 +
  250 + #blur with a uniform box filter of size r
  251 + for t in range(T.shape[3]):
  252 + T[..., t] = scipy.ndimage.filters.uniform_filter(T[..., t], 2 * s)
  253 + bar.update(t+1)
  254 +
  255 + #resample at a rate of r
  256 + R = T[::s[0], ::s[1], ::s[2], :]
  257 + return R
  258 +
  259 +def color3(prefix, T, vector='largest', aniso=True):
  260 + #Saves a stack of color images corresponding to the eigenvector and optionally scaled by anisotropy
  261 +
  262 + bar = progressbar.ProgressBar()
  263 + bar.max_value = T.shape[2]
  264 +
  265 + #for each z-axis slice
  266 + for z in range(T.shape[2]):
  267 + S = T[:, :, z, :] #get the slice
  268 + V = st2vec(S, vector='smallest') #calculate the vector
  269 + C = numpy.absolute(V) #calculate the absolute value
  270 +
  271 + if aniso == True: #if the image is scaled by anisotropy
  272 + FA, Cl, Cp, Cs = anisotropy(S) #calculate the anisotropy of the slice
  273 + if vector == 'largest':
  274 + A = Cl
  275 + elif vector == 'smallest':
  276 + A = Cp
  277 + else: #otherwise just scale by 1
  278 + A = numpy.ones(T.shape[0], T.shape[1])
  279 + image = C * numpy.expand_dims(A, 3)
  280 +
  281 + filename = prefix + str(z).zfill(4) + ".bmp"
  282 + scipy.misc.imsave(filename, image)
  283 + bar.update(z + 1)
0 284 \ No newline at end of file
... ...
stim/biomodels/centerline.h
... ... @@ -3,6 +3,7 @@
3 3  
4 4 #include <vector>
5 5 #include <stim/math/vec3.h>
  6 +#include <stim/structures/kdtree.cuh>
6 7  
7 8 namespace stim{
8 9  
... ... @@ -134,6 +135,362 @@ public:
134 135 return L.back();
135 136 }
136 137  
  138 + /// stitch two centerlines
  139 + ///@param c1, c2: two centerlines
  140 + ///@param sigma: sample rate
  141 + static std::vector< stim::centerline<T> > stitch(stim::centerline<T> c1, stim::centerline<T> c2 = stim::centerline<T>()) {
  142 +
  143 + std::vector< stim::centerline<T> > result;
  144 + stim::centerline<T> new_centerline;
  145 + stim::vec3<T> new_vertex;
  146 +
  147 + // if only one centerline, stitch itself!
  148 + if (c2.size() == 0) {
  149 + size_t num = c1.size();
  150 + size_t id = 100000; // store the downsample start position
  151 + T threshold;
  152 + if (num < 4) { // if the number of vertex is less than 4, do nothing
  153 + result.push_back(c1);
  154 + return result;
  155 + }
  156 + else {
  157 + // test geometry start vertex
  158 + stim::vec3<T> v1 = c1[1] - c1[0]; // vector from c1[0] to c1[1]
  159 + for (size_t p = 2; p < num; p++) { // 90° standard???
  160 + stim::vec3<T> v2 = c1[p] - c1[0];
  161 + float cosine = v2.dot(v1);
  162 + if (cosine < 0) {
  163 + id = p;
  164 + threshold = v2.len();
  165 + break;
  166 + }
  167 + }
  168 + if (id != 100000) { // find a downsample position on the centerline
  169 + T* c;
  170 + c = (T*)malloc(sizeof(T) * (num - id) * 3);
  171 + for (size_t p = id; p < num; p++) {
  172 + for (size_t d = 0; d < 3; d++) {
  173 + c[p * 3 + d] = c1[p][d];
  174 + }
  175 + }
  176 + stim::kdtree<T, 3> kdt;
  177 + kdt.create(c, num - id, 5); // create tree
  178 +
  179 + T* query = (T*)malloc(sizeof(T) * 3);
  180 + for (size_t d = 0; d < 3; d++)
  181 + query[d] = c1[0][d];
  182 + size_t index;
  183 + T dist;
  184 +
  185 + kdt.search(query, 1, &index, &dist);
  186 +
  187 + free(query);
  188 + free(c);
  189 +
  190 + if (dist > threshold) {
  191 + result.push_back(c1);
  192 + }
  193 + else {
  194 + // the loop part
  195 + new_vertex = c1[index];
  196 + new_centerline.push_back(new_vertex);
  197 + for (size_t p = 0; p < index + 1; p++) {
  198 + new_vertex = c1[p];
  199 + new_centerline.push_back(new_vertex);
  200 + }
  201 + result.push_back(new_centerline);
  202 + new_centerline.clear();
  203 +
  204 + // the tail part
  205 + for (size_t p = index; p < num; p++) {
  206 + new_vertex = c1[p];
  207 + new_centerline.push_back(new_vertex);
  208 + }
  209 + result.push_back(new_centerline);
  210 + }
  211 + }
  212 + else { // there is one potential problem that two positions have to be stitched
  213 + // test geometry end vertex
  214 + stim::vec3<T> v1 = c1[num - 2] - c1[num - 1];
  215 + for (size_t p = num - 2; p > 0; p--) { // 90° standard
  216 + stim::vec3<T> v2 = c1[p - 1] - c1[num - 1];
  217 + float cosine = v2.dot(v1);
  218 + if (cosine < 0) {
  219 + id = p;
  220 + threshold = v2.len();
  221 + break;
  222 + }
  223 + }
  224 + if (id != 100000) { // find a downsample position
  225 + T* c;
  226 + c = (T*)malloc(sizeof(T) * (id + 1) * 3);
  227 + for (size_t p = 0; p < id + 1; p++) {
  228 + for (size_t d = 0; d < 3; d++) {
  229 + c[p * 3 + d] = c1[p][d];
  230 + }
  231 + }
  232 + stim::kdtree<T, 3> kdt;
  233 + kdt.create(c, id + 1, 5); // create tree
  234 +
  235 + T* query = (T*)malloc(sizeof(T) * 1 * 3);
  236 + for (size_t d = 0; d < 3; d++)
  237 + query[d] = c1[num - 1][d];
  238 + size_t index;
  239 + T dist;
  240 +
  241 + kdt.search(query, 1, &index, &dist);
  242 +
  243 + free(query);
  244 + free(c);
  245 +
  246 + if (dist > threshold) {
  247 + result.push_back(c1);
  248 + }
  249 + else {
  250 + // the tail part
  251 + for (size_t p = 0; p < index + 1; p++) {
  252 + new_vertex = c1[p];
  253 + new_centerline.push_back(new_vertex);
  254 + }
  255 + result.push_back(new_centerline);
  256 + new_centerline.clear();
  257 +
  258 + // the loop part
  259 + for (size_t p = index; p < num; p++) {
  260 + new_vertex = c1[p];
  261 + new_centerline.push_back(new_vertex);
  262 + }
  263 + new_vertex = c1[index];
  264 + new_centerline.push_back(new_vertex);
  265 + result.push_back(new_centerline);
  266 + }
  267 + }
  268 + else { // no stitch position
  269 + result.push_back(c1);
  270 + }
  271 + }
  272 + }
  273 + }
  274 +
  275 +
  276 + // two centerlines
  277 + else {
  278 + // find stitch position based on nearest neighbors
  279 + size_t num1 = c1.size();
  280 + T* c = (T*)malloc(sizeof(T) * num1 * 3); // c1 as reference point
  281 + for (size_t p = 0; p < num1; p++) // centerline to array
  282 + for (size_t d = 0; d < 3; d++) // because right now my kdtree code is a relatively close code, it has its own structure
  283 + c[p * 3 + d] = c1[p][d]; // I will merge it into stimlib totally in the near future
  284 +
  285 + stim::kdtree<T, 3> kdt; // kdtree object
  286 + kdt.create(c, num1, 5); // create tree
  287 +
  288 + size_t num2 = c2.size();
  289 + T* query = (T*)malloc(sizeof(T) * num2 * 3); // c2 as query point
  290 + for (size_t p = 0; p < num2; p++) {
  291 + for (size_t d = 0; d < 3; d++) {
  292 + query[p * 3 + d] = c2[p][d];
  293 + }
  294 + }
  295 + std::vector<size_t> index(num2);
  296 + std::vector<T> dist(num2);
  297 +
  298 + kdt.search(query, num2, &index[0], &dist[0]); // find the nearest neighbors in c1 for c2
  299 +
  300 + // clear up
  301 + free(query);
  302 + free(c);
  303 +
  304 + // find the average vertex distance of one centerline
  305 + T sigma1 = 0;
  306 + T sigma2 = 0;
  307 + for (size_t p = 0; p < num1 - 1; p++)
  308 + sigma1 += (c1[p] - c1[p + 1]).len();
  309 + for (size_t p = 0; p < num2 - 1; p++)
  310 + sigma2 += (c2[p] - c2[p + 1]).len();
  311 + sigma1 /= (num1 - 1);
  312 + sigma2 /= (num2 - 1);
  313 + float threshold = 4 * (sigma1 + sigma2) / 2; // better way to do this?
  314 +
  315 + T min_d = *std::min_element(dist.begin(), dist.end()); // find the minimum distance between c1 and c2
  316 +
  317 + if (min_d > threshold) { // if the minimum distance is too large
  318 + result.push_back(c1);
  319 + result.push_back(c2);
  320 +
  321 +#ifdef DEBUG
  322 + std::cout << "The distance between these two centerlines is too large" << std::endl;
  323 +#endif
  324 + }
  325 + else {
  326 + auto smallest = std::min_element(dist.begin(), dist.end());
  327 + auto i = std::distance(dist.begin(), smallest); // find the index of min-distance in distance list
  328 +
  329 + // stitch position in c1 and c2
  330 + int id1 = index[i];
  331 + int id2 = i;
  332 +
  333 + // actually there are two cases
  334 + // first one inacceptable
  335 + // second one acceptable
  336 + if (id1 != 0 && id1 != num1 - 1 && id2 != 0 && id2 != num2 - 1) { // only stitch one end vertex to another centerline
  337 + result.push_back(c1);
  338 + result.push_back(c2);
  339 + }
  340 + else {
  341 + if (id1 == 0 || id1 == num1 - 1) { // if the stitch vertex is the first or last vertex of c1
  342 + // for c2, consider two cases(one degenerate case)
  343 + if (id2 == 0 || id2 == num2 - 1) { // case 1, if stitch position is also on the end of c2
  344 + // we have to decide which centerline get a new vertex, based on direction
  345 + // for c1, computer the direction change angle
  346 + stim::vec3<T> v1, v2;
  347 + float alpha1, alpha2; // direction change angle
  348 + if (id1 == 0)
  349 + v1 = (c1[1] - c1[0]).norm();
  350 + else
  351 + v1 = (c1[num1 - 2] - c1[num1 - 1]).norm();
  352 + v2 = (c2[id2] - c1[id1]).norm();
  353 + alpha1 = v1.dot(v2);
  354 + if (id2 == 0)
  355 + v1 = (c2[1] - c2[0]).norm();
  356 + else
  357 + v1 = (c2[num2 - 2] - c2[num2 - 1]).norm();
  358 + v2 = (c1[id1] - c2[id2]).norm();
  359 + alpha2 = v1.dot(v2);
  360 + if (abs(alpha1) > abs(alpha2)) { // add the vertex to c1 in order to get smooth connection
  361 + // push back c1
  362 + if (id1 == 0) { // keep geometry information
  363 + new_vertex = c2[id2];
  364 + new_centerline.push_back(new_vertex);
  365 + for (size_t p = 0; p < num1; p++) { // stitch vertex on c2 -> geometry start vertex on c1 -> geometry end vertex on c1
  366 + new_vertex = c1[p];
  367 + new_centerline.push_back(new_vertex);
  368 + }
  369 + }
  370 + else {
  371 + for (size_t p = 0; p < num1; p++) { // stitch vertex on c2 -> geometry end vertex on c1 -> geometry start vertex on c1
  372 + new_vertex = c1[p];
  373 + new_centerline.push_back(new_vertex);
  374 + }
  375 + new_vertex = c2[id2];
  376 + new_centerline.push_back(new_vertex);
  377 + }
  378 + result.push_back(new_centerline);
  379 + new_centerline.clear();
  380 +
  381 + // push back c2
  382 + for (size_t p = 0; p < num2; p++) {
  383 + new_vertex = c2[p];
  384 + new_centerline.push_back(new_vertex);
  385 + }
  386 + result.push_back(new_centerline);
  387 + }
  388 + else { // add the vertex to c2 in order to get smooth connection
  389 + // push back c1
  390 + for (size_t p = 0; p < num1; p++) {
  391 + new_vertex = c1[p];
  392 + new_centerline.push_back(new_vertex);
  393 + }
  394 + result.push_back(new_centerline);
  395 + new_centerline.clear();
  396 +
  397 + // push back c2
  398 + if (id2 == 0) { // keep geometry information
  399 + new_vertex = c1[id1];
  400 + new_centerline.push_back(new_vertex);
  401 + for (size_t p = 0; p < num2; p++) { // stitch vertex on c2 -> geometry start vertex on c1 -> geometry end vertex on c1
  402 + new_vertex = c2[p];
  403 + new_centerline.push_back(new_vertex);
  404 + }
  405 + }
  406 + else {
  407 + for (size_t p = 0; p < num2; p++) { // stitch vertex on c2 -> geometry end vertex on c1 -> geometry start vertex on c1
  408 + new_vertex = c2[p];
  409 + new_centerline.push_back(new_vertex);
  410 + }
  411 + new_vertex = c1[id1];
  412 + new_centerline.push_back(new_vertex);
  413 + }
  414 + result.push_back(new_centerline);
  415 + }
  416 + }
  417 + else { // case 2, the stitch position is on c2
  418 + // push back c1
  419 + if (id1 == 0) { // keep geometry information
  420 + new_vertex = c2[id2];
  421 + new_centerline.push_back(new_vertex);
  422 + for (size_t p = 0; p < num1; p++) { // stitch vertex on c2 -> geometry start vertex on c1 -> geometry end vertex on c1
  423 + new_vertex = c1[p];
  424 + new_centerline.push_back(new_vertex);
  425 + }
  426 + }
  427 + else {
  428 + for (size_t p = 0; p < num1; p++) { // geometry end vertex on c1 -> geometry start vertex on c1 -> stitch vertex on c2
  429 + new_vertex = c1[p];
  430 + new_centerline.push_back(new_vertex);
  431 + }
  432 + new_vertex = c2[id2];
  433 + new_centerline.push_back(new_vertex);
  434 + }
  435 + result.push_back(new_centerline);
  436 + new_centerline.clear();
  437 +
  438 + // push back c2
  439 + for (size_t p = 0; p < id2 + 1; p++) { // first part
  440 + new_vertex = c2[p];
  441 + new_centerline.push_back(new_vertex);
  442 + }
  443 + result.push_back(new_centerline);
  444 + new_centerline.clear();
  445 +
  446 + for (size_t p = id2; p < num2; p++) { // second part
  447 + new_vertex = c2[p];
  448 + new_centerline.push_back(new_vertex);
  449 + }
  450 + result.push_back(new_centerline);
  451 + }
  452 + }
  453 + else { // if the stitch vertex is the first or last vertex of c2
  454 + // push back c2
  455 + if (id2 == 0) { // keep geometry information
  456 + new_vertex = c1[id1];
  457 + new_centerline.push_back(new_vertex);
  458 + for (size_t p = 0; p < num2; p++) { // stitch vertex on c1 -> geometry start vertex on c2 -> geometry end vertex on c2
  459 + new_vertex = c2[p];
  460 + new_centerline.push_back(new_vertex);
  461 + }
  462 + }
  463 + else {
  464 + for (size_t p = 0; p < num2; p++) { // geometry end vertex on c2 -> geometry start vertex on c2 -> stitch vertex on c1
  465 + new_vertex = c2[p];
  466 + new_centerline.push_back(new_vertex);
  467 + }
  468 + new_vertex = c1[id1];
  469 + new_centerline.push_back(new_vertex);
  470 + result.push_back(new_centerline);
  471 + new_centerline.clear();
  472 +
  473 + // push back c1
  474 + for (size_t p = 0; p < id1 + 1; p++) { // first part
  475 + new_vertex = c1[p];
  476 + new_centerline.push_back(new_vertex);
  477 + }
  478 + result.push_back(new_centerline);
  479 + new_centerline.clear();
  480 +
  481 + for (size_t p = id1; p < num1; p++) { // second part
  482 + new_vertex = c1[p];
  483 + new_centerline.push_back(new_vertex);
  484 + }
  485 + result.push_back(new_centerline);
  486 + }
  487 + }
  488 + }
  489 + }
  490 + }
  491 + return result;
  492 + }
  493 +
137 494 /// Split the fiber at the specified index. If the index is an end point, only one fiber is returned
138 495 std::vector< stim::centerline<T> > split(unsigned int idx){
139 496  
... ...
stim/envi/agilent_binary.h
... ... @@ -243,13 +243,13 @@ public:
243 243 if (device >= 0) { //if a CUDA device is specified
244 244 int dev_count;
245 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
  246 + //std::cout << "Number of CUDA devices: " << dev_count << std::endl; //output the number of CUDA devices
247 247 cudaDeviceProp prop;
248   - std::cout << "CUDA devices----" << std::endl;
  248 + //std::cout << "CUDA devices----" << std::endl;
249 249 for (int d = 0; d < dev_count; d++) { //for each CUDA device
250 250 cudaGetDeviceProperties(&prop, d); //get the property of the first device
251 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
  252 + //std::cout << d << ": [" << prop.major << "." << prop.minor << "] " << prop.name << std::endl; //display the device information
253 253 //if(cc > best_device_cc){
254 254 // best_device_cc = cc; //if this is better than the previous device, use it
255 255 // best_device_id = d;
... ... @@ -258,7 +258,7 @@ public:
258 258 if (dev_count > 0 && dev_count > device) { //if the first device is not an emulator
259 259 cudaGetDeviceProperties(&prop, device); //get the property of the requested CUDA device
260 260 if (prop.major != 9999) {
261   - std::cout << "Using device " << device << std::endl;
  261 + //std::cout << "Using device " << device << std::endl;
262 262 HANDLE_ERROR(cudaSetDevice(device));
263 263 }
264 264 }
... ...
stim/envi/bil.h
... ... @@ -462,6 +462,11 @@ public:
462 462 }*/
463 463 }
464 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 +
465 470 /// Convert the current BIL file to a BSQ file with the specified file name.
466 471  
467 472 /// @param outname is the name of the output BSQ file to be saved to disk.
... ...
stim/envi/bip.h
... ... @@ -392,6 +392,50 @@ public:
392 392 }
393 393 }
394 394  
  395 + /// This function loads a specified set of bands and saves them into a new output file
  396 + bool select(std::string outfile, std::vector<double> bandlist, unsigned char* mask = NULL, bool PROGRESS = false) {
  397 + std::ofstream target(outfile.c_str(), std::ios::binary); //open the target binary file
  398 + if (!target) {
  399 + std::cout << "ERROR opening output file: " << outfile << std::endl;
  400 + return false;
  401 + }
  402 + file.seekg(0, std::ios::beg); //move the pointer to the current file to the beginning
  403 +
  404 + size_t B = Z(); //number of spectral components
  405 + size_t XY = X() * Y(); //calculate the number of pixels
  406 + size_t Bout = bandlist.size();
  407 + size_t in_bytes = B * sizeof(T); //number of bytes in a spectrum
  408 + size_t out_bytes = Bout * sizeof(T); //number of bytes in an output spectrum
  409 +
  410 + T* in = (T*)malloc(in_bytes); //allocate space for the input spectrum
  411 + T* out = (T*)malloc(out_bytes); //allocate space for the output spectrum
  412 +
  413 + double wc; //register to store the desired wavelength
  414 + double w0, w1; //registers to store the wavelengths surrounding the given band
  415 + size_t b0, b1; //indices of the bands surrounding the specified wavelength
  416 + for (size_t xy = 0; xy < XY; xy++) { //for each pixel
  417 + //memset(out, 0, out_bytes); //set the spectrum to zero
  418 + if (mask == NULL || mask[xy]) { //if the pixel is masked
  419 + file.read((char*)in, in_bytes); //read an input spectrum
  420 + for (size_t b = 0; b < Bout; b++) { //for each band
  421 + wc = bandlist[b]; //set the desired wavelength
  422 + hsi<T>::band_bounds(wc, b0, b1); //get the surrounding bands
  423 + w0 = w[b0]; //get the wavelength for the lower band
  424 + w1 = w[b1]; //get the wavelength for the higher band
  425 + out[b] = hsi<T>::lerp(wc, in[b0], w0, in[b1], w1); //interpolate the spectral values to get the desired output value
  426 + }
  427 + }
  428 + else
  429 + file.seekg(Bout, std::ios::cur); //otherwise skip a spectrum
  430 + target.write((char*)out, out_bytes); //output the normalized spectrum
  431 + if (PROGRESS) progress = (double)(xy + 1) / (double)XY * 100; //update the progress
  432 + }
  433 +
  434 + free(in);
  435 + free(out);
  436 + return true;
  437 + }
  438 +
395 439  
396 440 /// Convert the current BIP file to a BIL file with the specified file name.
397 441  
... ...
stim/envi/bsq.h
... ... @@ -380,6 +380,30 @@ public:
380 380  
381 381 }
382 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 +
383 407 size_t readlines(T* dest, size_t start, size_t n){
384 408 return hsi<T>::read(dest, 0, start, 0, X(), n, Z());
385 409 }
... ...
stim/envi/envi.h
... ... @@ -562,6 +562,41 @@ public:
562 562 }
563 563 }
564 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 +
565 600 /// Performs piecewise linear baseline correction of a hyperspectral file/
566 601  
567 602 /// @param outfile is the file name for the baseline corrected output
... ... @@ -1333,6 +1368,39 @@ public:
1333 1368 return false;
1334 1369 }
1335 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 +
1336 1404 // Retrieve a spectrum at the specified 1D location
1337 1405  
1338 1406 /// @param ptr is a pointer to pre-allocated memory of size B*sizeof(T)
... ...
stim/envi/hsi.h
... ... @@ -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,6 +114,31 @@ 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();
... ... @@ -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/iVote/ivote2.cuh
... ... @@ -6,13 +6,14 @@
6 6 #include <stim/cuda/cudatools/error.h>
7 7 #include <stim/cuda/templates/gradient.cuh>
8 8 #include <stim/cuda/arraymath.cuh>
9   -#include <stim/iVote/ivote2/ivote2.cuh>
  9 +#include <stim/iVote/ivote2/iter_vote2.cuh>
  10 +#include <stim/iVote/ivote2/local_max.cuh>
10 11 #include <stim/math/constants.h>
11 12 #include <stim/math/vector.h>
12 13 #include <stim/visualization/colormap.h>
13 14  
14   -namespace stim {
15 15  
  16 +namespace stim {
16 17 // this function precomputes the atan2 values
17 18 template<typename T>
18 19 void atan_2(T* cpuTable, unsigned int rmax) {
... ... @@ -93,8 +94,8 @@ namespace stim {
93 94  
94 95 //this function performs the 2D iterative voting algorithm on the image stored in the gpu
95 96 template<typename T>
96   - void gpu_ivote2(T* gpuI, unsigned int rmax, size_t x, size_t y, bool invert, T t = 0, std::string outname_img = "out.bmp", std::string outname_txt = "out.txt",
97   - int iter = 8, T phi = 15.0f * (float)stim::PI / 180, int conn = 8) {
  97 + void gpu_ivote2(T* gpuI, unsigned int rmax, size_t x, size_t y, bool invert = false, T t = 0, std::string outname_img = "out.bmp", std::string outname_txt = "out.txt",
  98 + int iter = 8, T phi = 15.0f * (float)stim::PI / 180, int conn = 8, bool debug = false) {
98 99  
99 100 size_t pixels = x * y; //compute the size of input image
100 101 //
... ... @@ -118,14 +119,12 @@ namespace stim {
118 119 float* gpuVote; HANDLE_ERROR(cudaMalloc(&gpuVote, bytes)); //allocate space to store the vote image
119 120  
120 121 stim::cuda::gpu_gradient_2d<float>(gpuGrad, gpuI, x, y); //calculate the 2D gradient
121   - //if (invert) stim::cuda::gpu_cart2polar<float>(gpuGrad, x, y, stim::PI);
122   - //else stim::cuda::gpu_cart2polar<float>(gpuGrad, x, y);
123   - stim::cuda::gpu_cart2polar<float>(gpuGrad, x, y); //convert cartesian coordinate of gradient to the polar
  122 + stim::cuda::gpu_cart2polar<float>(gpuGrad, x, y); //convert cartesian coordinate of gradient to the polar
124 123  
125 124 for (int i = 0; i < iter; i++) { //for each iteration
126 125 cudaMemset(gpuVote, 0, bytes); //reset the vote image to 0
127   - stim::cuda::gpu_vote<float>(gpuVote, gpuGrad, gpuTable, phi, rmax, x, y); //perform voting
128   - stim::cuda::gpu_update_dir<float>(gpuVote, gpuGrad, gpuTable, phi, rmax, x, y); //update the voter directions
  126 + stim::cuda::gpu_vote<float>(gpuVote, gpuGrad, gpuTable, phi, rmax, x, y, debug); //perform voting
  127 + stim::cuda::gpu_update_dir<float>(gpuVote, gpuGrad, gpuTable, phi, rmax, x, y, debug); //update the voter directions
129 128 phi = phi - dphi; //decrement phi
130 129 }
131 130 stim::cuda::gpu_local_max<float>(gpuI, gpuVote, conn, x, y); //calculate the local maxima
... ... @@ -160,13 +159,13 @@ namespace stim {
160 159  
161 160  
162 161 template<typename T>
163   - void cpu_ivote2(T* cpuI, unsigned int rmax, size_t x, size_t y, bool invert, T t = 0, std::string outname_img = "out.bmp", std::string outname_txt = "out.txt",
164   - int iter = 8, T phi = 15.0f * (float)stim::PI / 180, int conn = 8) {
  162 + void cpu_ivote2(T* cpuI, unsigned int rmax, size_t x, size_t y, bool invert = false, T t = 0, std::string outname_img = "out.bmp", std::string outname_txt = "out.txt",
  163 + int iter = 8, T phi = 15.0f * (float)stim::PI / 180, int conn = 8, bool debug = false) {
165 164 size_t bytes = x*y * sizeof(T);
166 165 T* gpuI; //allocate space on the gpu to save the input image
167 166 HANDLE_ERROR(cudaMalloc(&gpuI, bytes));
168 167 HANDLE_ERROR(cudaMemcpy(gpuI, cpuI, bytes, cudaMemcpyHostToDevice)); //copy the image to the gpu
169   - stim::gpu_ivote2<T>(gpuI, rmax, x, y, invert, t, outname_img, outname_txt, iter, phi, conn); //call the gpu version of the ivote
  168 + stim::gpu_ivote2<T>(gpuI, rmax, x, y, invert, t, outname_img, outname_txt, iter, phi, conn, debug); //call the gpu version of the ivote
170 169 HANDLE_ERROR(cudaMemcpy(cpuI, gpuI, bytes, cudaMemcpyDeviceToHost)); //copy the output to the cpu
171 170 }
172 171 }
... ...
stim/iVote/ivote2/iter_vote2.cuh
1 1 #ifndef STIM_CUDA_ITER_VOTE2_H
2 2 #define STIM_CUDA_ITER_VOTE2_H
3 3  
4   -extern bool DEBUG;
  4 +//extern bool DEBUG;
5 5  
6   -#include "local_max.cuh"
7 6 #include "update_dir_bb.cuh"
8 7 #include "vote_atomic_bb.cuh"
9 8  
... ...
stim/iVote/ivote2/update_dir_bb.cuh
... ... @@ -97,7 +97,7 @@ namespace stim{
97 97 }
98 98  
99 99 template<typename T>
100   - void gpu_update_dir(T* gpuVote, T* gpuGrad, T* gpuTable, T phi, unsigned int rmax, size_t x, size_t 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){
101 101  
102 102 //calculate the number of bytes in the array
103 103 size_t bytes = x * y * sizeof(T);
... ...
stim/iVote/ivote2/vote_atomic_bb.cuh
... ... @@ -87,7 +87,7 @@ namespace stim{
87 87 /// @param x and y are the spatial dimensions of the gradient image
88 88 /// @param gradmag defines whether or not the gradient magnitude is taken into account during the vote
89 89 template<typename T>
90   - void gpu_vote(T* gpuVote, T* gpuGrad, T* gpuTable, T phi, unsigned int rmax, size_t x, size_t y, bool gradmag = true){
  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){
91 91 unsigned int max_threads = stim::maxThreadsPerBlock();
92 92 dim3 threads( (unsigned int)sqrt(max_threads), (unsigned int)sqrt(max_threads) );
93 93 dim3 blocks((unsigned int)x/threads.x + 1, (unsigned int)y/threads.y + 1);
... ... @@ -96,7 +96,7 @@ namespace stim{
96 96 if (DEBUG) std::cout<<"Shared Memory required: "<<shared_mem_req<<std::endl;
97 97 size_t shared_mem = stim::sharedMemPerBlock();
98 98 if(shared_mem_req > shared_mem){
99   - 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;
100 100 exit(1);
101 101 }
102 102  
... ...
stim/image/image.h
... ... @@ -53,6 +53,10 @@ class image{
53 53 void allocate(){
54 54 unalloc();
55 55 img = (T*) malloc( sizeof(T) * R[0] * R[1] * R[2] ); //allocate memory
  56 + if (img == NULL) {
  57 + std::cout << "stim::image ERROR - failed to allocate memory for image" << std::endl;
  58 + exit(1);
  59 + }
56 60 }
57 61  
58 62 void allocate(size_t x, size_t y, size_t c){ //allocate memory based on the resolution
... ... @@ -228,6 +232,14 @@ public:
228 232 }
229 233 }
230 234 #endif
  235 + //Copy N data points from source to dest, casting while doing so
  236 + template<typename S, typename D>
  237 + void type_copy(S* source, D* dest, size_t N) {
  238 + if (typeid(S) == typeid(D)) //if both types are the same
  239 + memcpy(dest, source, N * sizeof(S)); //just use a memcpy
  240 + for (size_t n = 0; n < N; n++) //otherwise, iterate through each element
  241 + dest[n] = (D)source[n]; //copy and cast
  242 + }
231 243 /// Load an image from a file
232 244 void load(std::string filename){
233 245 #ifdef USING_OPENCV
... ... @@ -236,13 +248,15 @@ public:
236 248 std::cout<<"ERROR stim::image::load() - unable to find image "<<filename<<std::endl;
237 249 exit(1);
238 250 }
  251 + int cv_type = cvImage.type();
239 252 int cols = cvImage.cols;
240 253 int rows = cvImage.rows;
241 254 int channels = cvImage.channels();
242 255 allocate(cols, rows, channels); //allocate space for the image
  256 + size_t img_bytes = bytes();
243 257 unsigned char* cv_ptr = (unsigned char*)cvImage.data;
244   - if(C() == 1) //if this is a single-color image, just copy the data
245   - memcpy(img, cv_ptr, bytes());
  258 + if (C() == 1) //if this is a single-color image, just copy the data
  259 + type_copy<unsigned char, T>(cv_ptr, img, size());
246 260 if(C() == 3) //if this is a 3-color image, OpenCV uses BGR interleaving
247 261 from_opencv(cv_ptr, X(), Y());
248 262 #else
... ...
stim/math/matrix.h
... ... @@ -33,32 +33,58 @@ namespace stim{
33 33 }
34 34 }
35 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 +
36 82 static void save_mat4(char* data, std::string filename, std::string varname, size_t sx, size_t sy, mat4Format format){
37   - //save the matrix file here (use the mat4 function above)
38   - //data format: https://maxwell.ict.griffith.edu.au/spl/matlab-page/matfile_format.pdf (page 32)
39   -
40   - int MOPT = 0; //initialize the MOPT type value to zero
41   - int m = 0; //little endian
42   - int o = 0; //reserved, always 0
43   - int p = format;
44   - int t = 0;
45   - MOPT = m * 1000 + o * 100 + p * 10 + t; //calculate the type value
46   - int mrows = (int)sx;
47   - int ncols = (int)sy;
48   - int imagf = 0; //assume real (for now)
49   - varname.push_back('\0'); //add a null to the string
50   - int namlen = (int)varname.size(); //calculate the name size
51   -
52   - size_t bytes = sx * sy * mat4Format_size(format);
53   - std::ofstream outfile(filename, std::ios::binary);
54   - outfile.write((char*)&MOPT, 4);
55   - outfile.write((char*)&mrows, 4);
56   - outfile.write((char*)&ncols, 4);
57   - outfile.write((char*)&imagf, 4);
58   - outfile.write((char*)&namlen, 4);
59   - outfile.write((char*)&varname[0], namlen);
60   - outfile.write((char*)data, bytes); //write the matrix data
61   - outfile.close();
  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 + }
62 88 }
63 89  
64 90 template <class T>
... ... @@ -409,8 +435,29 @@ public:
409 435 }
410 436 }
411 437  
412   - // saves the matrix as a Level-4 MATLAB file
413   - void mat4(std::string filename, std::string name = std::string("unknown"), mat4Format format = mat4_float) {
  438 + void raw(std::string filename) {
  439 + 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 + }
414 461 if (format == mat4_float) {
415 462 if (sizeof(T) == 4) format = mat4_float32;
416 463 else if (sizeof(T) == 8) format = mat4_float64;
... ... @@ -419,7 +466,40 @@ public:
419 466 exit(1);
420 467 }
421 468 }
422   - stim::save_mat4((char*)M, filename, name, rows(), cols(), format);
  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 + }
423 503 }
424 504 };
425 505  
... ...
stim/math/vec3.h
... ... @@ -243,7 +243,7 @@ public:
243 243 return false;
244 244 }
245 245  
246   -//#ifndef __NVCC__
  246 +#ifndef __NVCC__
247 247 /// Outputs the vector as a string
248 248 std::string str() const{
249 249 std::stringstream ss;
... ... @@ -261,7 +261,7 @@ public:
261 261  
262 262 return ss.str();
263 263 }
264   -//#endif
  264 +#endif
265 265  
266 266 size_t size(){ return 3; }
267 267  
... ...
stim/parser/filename.h
... ... @@ -89,6 +89,11 @@ protected:
89 89 absolute.push_back(relative[i]);
90 90 }
91 91 }
  92 + else {
  93 + if (relative[0] == ".")
  94 + relative = std::vector<std::string>(relative.begin() + 1, relative.end());
  95 + absolute = relative;
  96 + }
92 97 }
93 98  
94 99 /// Parses a directory string into a drive (NULL if not Windows) and list of hierarchical directories
... ...
stim/visualization/aabb3.h
... ... @@ -6,14 +6,14 @@
6 6  
7 7 namespace stim{
8 8  
9   - template<typename T>
10   - using aabb3 = aabbn<T, 3>;
11   -/*/// Structure for a 3D axis aligned bounding box
  9 + //template<typename T>
  10 + //using aabb3 = aabbn<T, 3>;
  11 +/// Structure for a 3D axis aligned bounding box
12 12 template<typename T>
13 13 struct aabb3 : public aabbn<T, 3>{
14 14  
15   - aabb3() : aabbn() {}
16   - aabb3(T x0, T y0, T z0, T x1, T y1, T z1){
  15 + CUDA_CALLABLE aabb3() : aabbn() {}
  16 + CUDA_CALLABLE aabb3(T x0, T y0, T z0, T x1, T y1, T z1){
17 17 low[0] = x0;
18 18 low[1] = y0;
19 19 low[2] = z0;
... ... @@ -22,11 +22,39 @@ struct aabb3 : public aabbn&lt;T, 3&gt;{
22 22 high[2] = x2;
23 23 }
24 24  
25   - aabb3 aabbn<T, 3>() {
  25 + CUDA_CALLABLE aabb3(T x, T y, T z) {
  26 + low[0] = high[0] = x;
  27 + low[1] = high[1] = y;
  28 + low[2] = high[2] = z;
  29 + }
  30 +
  31 + CUDA_CALLABLE void insert(T x, T y, T z) {
  32 + T p[3];
  33 + p[0] = x;
  34 + p[1] = y;
  35 + p[2] = z;
  36 + aabbn<T, 3>::insert(p);
  37 + }
26 38  
  39 + CUDA_CALLABLE void trim_low(T x, T y, T z) {
  40 + T p[3];
  41 + p[0] = x;
  42 + p[1] = y;
  43 + p[2] = z;
  44 + aabbn<T, 3>::trim_low(p);
27 45 }
28 46  
29   -};*/
  47 + CUDA_CALLABLE void trim_high(T x, T y, T z) {
  48 + T p[3];
  49 + p[0] = x;
  50 + p[1] = y;
  51 + p[2] = z;
  52 + aabbn<T, 3>::trim_high(p);
  53 + }
  54 +
  55 +
  56 +
  57 +};
30 58  
31 59 }
32 60  
... ...
stim/visualization/aabbn.h
... ... @@ -25,26 +25,58 @@ struct aabbn{
25 25 init(i);
26 26 }
27 27  
  28 + /// For even inputs to the constructor, the input could be one point or a set of pairs of points
28 29 CUDA_CALLABLE aabbn(T x0, T x1) {
29   - low[0] = x0;
30   - high[0] = x1;
  30 + if (D == 1) {
  31 + low[0] = x0;
  32 + high[0] = x1;
  33 + }
  34 + else if (D == 2) {
  35 + low[0] = high[0] = x0;
  36 + low[1] = high[1] = x1;
  37 + }
31 38 }
32 39  
  40 + /// In the case of 3 inputs, this must be a 3D bounding box, so initialize to a box of size 0 at (x, y, z)
  41 + /*CUDA_CALLABLE aabbn(T x, T y, T z) {
  42 + low[0] = high[0] = x;
  43 + low[1] = high[1] = y;
  44 + low[2] = high[2] = z;
  45 + }*/
  46 +
33 47 CUDA_CALLABLE aabbn(T x0, T y0, T x1, T y1) {
34   - low[0] = x0;
35   - high[0] = x1;
36   - low[1] = y0;
37   - high[1] = y1;
  48 + if (D == 2) {
  49 + low[0] = x0;
  50 + high[0] = x1;
  51 + low[1] = y0;
  52 + high[1] = y1;
  53 + }
  54 + else if(D == 4){
  55 + low[0] = high[0] = x0;
  56 + low[1] = high[1] = y0;
  57 + low[2] = high[2] = x1;
  58 + low[3] = high[3] = y1;
  59 + }
38 60 }
39 61  
40   - CUDA_CALLABLE aabbn(T x0, T y0, T z0, T x1, T y1, T z1) {
41   - low[0] = x0;
42   - high[0] = x1;
43   - low[1] = y0;
44   - high[1] = y1;
45   - low[2] = z0;
46   - high[2] = z1;
47   - }
  62 + /*CUDA_CALLABLE aabbn(T x0, T y0, T z0, T x1, T y1, T z1) {
  63 + if (D == 3) {
  64 + low[0] = x0;
  65 + high[0] = x1;
  66 + low[1] = y0;
  67 + high[1] = y1;
  68 + low[2] = z0;
  69 + high[2] = z1;
  70 + }
  71 + else if (D == 6) {
  72 + low[0] = high[0] = x0;
  73 + low[1] = high[1] = y0;
  74 + low[2] = high[2] = z0;
  75 + low[3] = high[3] = x1;
  76 + low[4] = high[4] = y1;
  77 + low[5] = high[5] = z1;
  78 + }
  79 + }*/
48 80  
49 81  
50 82 //insert a point into the bounding box, growing the box appropriately
... ...
stim/visualization/obj.h
... ... @@ -7,6 +7,7 @@
7 7 #include <stdlib.h>
8 8 #include <stim/parser/parser.h>
9 9 #include <stim/math/vector.h>
  10 +#include <stim/visualization/obj/obj_material.h>
10 11 #include <algorithm>
11 12  
12 13 #include <time.h>
... ... @@ -29,7 +30,7 @@ namespace stim{
29 30 * geometry class - contains a list of triplets used to define a geometric structure, such as a face or line
30 31 */
31 32  
32   -enum obj_type { OBJ_NONE, OBJ_LINE, OBJ_FACE, OBJ_POINTS };
  33 +enum obj_type { OBJ_NONE, OBJ_LINE, OBJ_FACE, OBJ_POINTS, OBJ_TRIANGLE_STRIP };
33 34  
34 35 template <typename T>
35 36 class obj{
... ... @@ -93,13 +94,13 @@ protected:
93 94 }; //end vertex
94 95  
95 96 //triplet used to specify geometric vertices consisting of a position vertex, texture vertex, and normal
96   - struct triplet : public std::vector<unsigned int>{
  97 + struct triplet : public std::vector<size_t>{
97 98  
98 99 //default constructor, empty triplet
99 100 triplet(){}
100 101  
101 102 //create a triplet given a parameter list (OBJ indices start at 1, so 0 can be used to indicate no value)
102   - triplet(unsigned int v, unsigned int vt = 0, unsigned int vn = 0){
  103 + triplet(size_t v, size_t vt = 0, size_t vn = 0){
103 104 push_back(v);
104 105 if(vn != 0){
105 106 push_back(vt);
... ... @@ -140,12 +141,12 @@ protected:
140 141  
141 142 if(size() == 3){
142 143 if(at(1) == 0)
143   - ss<<"\\\\"<<at(2);
  144 + ss<<"//"<<at(2);
144 145 else
145   - ss<<'\\'<<at(1)<<'\\'<<at(2);
  146 + ss<<'/'<<at(1)<<'/'<<at(2);
146 147 }
147 148 else if(size() == 2)
148   - ss<<"\\"<<at(1);
  149 + ss<<"/"<<at(1);
149 150  
150 151 return ss.str();
151 152 }
... ... @@ -223,10 +224,16 @@ protected:
223 224 std::vector<geometry> P; //list of points structures
224 225 std::vector<geometry> F; //list of faces
225 226  
  227 + //material lists
  228 + std::vector< obj_material<T> > M; //list of material descriptors
  229 + std::vector<size_t> Mf; //face index where each material begins
  230 +
226 231 //information for the current geometric object
227 232 geometry current_geo;
228 233 vertex current_vt;
229 234 vertex current_vn;
  235 + obj_material<T> current_material; //stores the current material
  236 + bool new_material; //flags if a material property has been changed since the last material was pushed
230 237  
231 238 //flags for the current geometric object
232 239 obj_type current_type;
... ... @@ -258,9 +265,9 @@ protected:
258 265 //create a triple and add it to the current geometry
259 266 void update_v(vertex vv){
260 267  
261   - unsigned int v;
262   - unsigned int vt = 0;
263   - unsigned int vn = 0;
  268 + size_t v;
  269 + size_t vt = 0;
  270 + size_t vn = 0;
264 271  
265 272 //if the current geometry is using a texture coordinate, add the current texture coordinate to the geometry
266 273 if(geo_flag_vt){
... ... @@ -303,6 +310,8 @@ protected:
303 310 geo_flag_vn = false;
304 311 vert_flag_vt = false;
305 312 vert_flag_vn = false;
  313 +
  314 + new_material = false; //initialize a new material to false (start with no material)
306 315 }
307 316  
308 317 //gets the type of token representing the entry in the OBJ file
... ... @@ -346,13 +355,107 @@ public:
346 355 void Vertex(T x, T y, T z){ update_v(vertex(x, y, z));}
347 356 void Vertex(T x, T y, T z, T w){ update_v(vertex(x, y, z, w));}
348 357  
  358 + ///Material functions
  359 + void matKa(T r, T g, T b) {
  360 + new_material = true;
  361 + current_material.ka[0] = r;
  362 + current_material.ka[1] = g;
  363 + current_material.ka[2] = b;
  364 + }
  365 + void matKa(std::string tex = std::string()) {
  366 + new_material = true;
  367 + current_material.tex_ka = tex;
  368 + }
  369 + void matKd(T r, T g, T b) {
  370 + new_material = true;
  371 + current_material.kd[0] = r;
  372 + current_material.kd[1] = g;
  373 + current_material.kd[2] = b;
  374 + }
  375 + void matKd(std::string tex = std::string()) {
  376 + new_material = true;
  377 + current_material.tex_kd = tex;
  378 + }
  379 + void matKs(T r, T g, T b) {
  380 + new_material = true;
  381 + current_material.ks[0] = r;
  382 + current_material.ks[1] = g;
  383 + current_material.ks[2] = b;
  384 + }
  385 + void matKs(std::string tex = std::string()) {
  386 + new_material = true;
  387 + current_material.tex_ks = tex;
  388 + }
  389 + void matNs(T n) {
  390 + new_material = true;
  391 + current_material.ns = n;
  392 + }
  393 + void matNs(std::string tex = std::string()) {
  394 + new_material = true;
  395 + current_material.tex_ns = tex;
  396 + }
  397 + void matIllum(int i) {
  398 + new_material = true;
  399 + current_material.illum = i;
  400 + }
  401 + void matD(std::string tex = std::string()) {
  402 + new_material = true;
  403 + current_material.tex_alpha = tex;
  404 + }
  405 + void matBump(std::string tex = std::string()) {
  406 + new_material = true;
  407 + current_material.tex_bump = tex;
  408 + }
  409 + void matDisp(std::string tex = std::string()) {
  410 + new_material = true;
  411 + current_material.tex_disp = tex;
  412 + }
  413 + void matDecal(std::string tex = std::string()) {
  414 + new_material = true;
  415 + current_material.tex_decal = tex;
  416 + }
  417 +
349 418 ///This function starts drawing of a primitive object, such as a line, face, or point set
350 419  
351 420 /// @param t is the type of object to be drawn: OBJ_POINTS, OBJ_LINE, OBJ_FACE
352 421 void Begin(obj_type t){
  422 + if (new_material) { //if a new material has been specified
  423 + if (current_material.name == "") { //if a name wasn't given, create a new one
  424 + std::stringstream ss; //create a name for it
  425 + ss << "material" << M.size(); //base it on the material number
  426 + current_material.name = ss.str();
  427 + }
  428 + Mf.push_back(F.size()); //start the material at the current face index
  429 + M.push_back(current_material); //push the current material
  430 + current_material.name = ""; //reset the name of the current material
  431 + }
353 432 current_type = t;
354 433 }
355 434  
  435 + //generates a list of faces from a list of points, assuming the input list forms a triangle strip
  436 + std::vector<geometry> genTriangleStrip(geometry s) {
  437 + if (s.size() < 3) return std::vector<geometry>(); //return an empty list if there aren't enough points to form a triangle
  438 + size_t nt = s.size() - 2; //calculate the number of triangles in the strip
  439 + std::vector<geometry> r(nt); //create a list of geometry objects, where the number of faces = the number of triangles in the strip
  440 +
  441 + r[0].push_back(s[0]);
  442 + r[0].push_back(s[1]);
  443 + r[0].push_back(s[2]);
  444 + for (size_t i = 1; i < nt; i++) {
  445 + if (i % 2) {
  446 + r[i].push_back(s[i + 1]);
  447 + r[i].push_back(s[i + 0]);
  448 + r[i].push_back(s[i + 2]);
  449 + }
  450 + else {
  451 + r[i].push_back(s[i + 0]);
  452 + r[i].push_back(s[i + 1]);
  453 + r[i].push_back(s[i + 2]);
  454 + }
  455 + }
  456 + return r;
  457 + }
  458 +
356 459 /// This function terminates drawing of a primitive object, such as a line, face, or point set
357 460 void End(){
358 461 //copy the current object to the appropriate list
... ... @@ -374,6 +477,12 @@ public:
374 477  
375 478 case OBJ_FACE:
376 479 F.push_back(current_geo);
  480 + break;
  481 +
  482 + case OBJ_TRIANGLE_STRIP:
  483 + std::vector<geometry> tstrip = genTriangleStrip(current_geo); //generate a list of faces from the current geometry
  484 + F.insert(F.end(), tstrip.begin(), tstrip.end()); //insert all of the triangles into the face list
  485 + break;
377 486 }
378 487 }
379 488 //clear everything
... ... @@ -438,10 +547,15 @@ public:
438 547 }
439 548 }
440 549  
441   - //output all of the lines
  550 + //output all of the faces
442 551 if(F.size()){
443 552 ss<<std::endl<<"#face structures"<<std::endl;
  553 + size_t mi = 0; //start the current material index at 0
444 554 for(i = 0; i < F.size(); i++){
  555 + if (mi < M.size() && Mf[mi] == i) {
  556 + ss << "usemtl " << M[mi].name << std::endl;
  557 + mi++;
  558 + }
445 559 ss<<"f "<<F[i].str()<<std::endl;
446 560 }
447 561 }
... ... @@ -449,6 +563,14 @@ public:
449 563 return ss.str(); //return the constructed string
450 564 }
451 565  
  566 + ///Output the material file as a string
  567 + std::string matstr() {
  568 + std::stringstream ss;
  569 + for (size_t i = 0; i < M.size(); i++) {
  570 + ss << M[i].str() << std::endl;
  571 + }
  572 + }
  573 +
452 574 obj(){
453 575 init(); //private function that initializes everything
454 576 }
... ... @@ -462,19 +584,42 @@ public:
462 584 /// @param filename is the name of the file to be saved
463 585 bool save(std::string filename){
464 586  
465   - std::ofstream outfile(filename.c_str());
  587 +
  588 +
  589 + std::string obj_ext = ".obj";
  590 + size_t ext_found = filename.find(obj_ext);
  591 + if (ext_found != std::string::npos) //if the extension was found
  592 + filename = filename.substr(0, ext_found);
  593 + std::string obj_filename;
  594 + std::string mtl_filename;
  595 + obj_filename = filename + ".obj";
  596 + mtl_filename = filename + ".mtl";
466 597  
467   - if(!outfile){
468   - std::cout<<"STIM::OBJ error opening file for writing"<<std::endl;
  598 +
  599 + std::ofstream outfile(obj_filename.c_str());
  600 + if (!outfile) {
  601 + std::cout << "STIM::OBJ error opening file for writing" << std::endl;
469 602 return false;
470 603 }
471 604  
  605 + if (M.size()) //if there are any materials, there will be a corresponding material file
  606 + outfile << "mtllib " << mtl_filename << std::endl; //output the material library name
  607 +
472 608 //output the OBJ data to the file
473 609 outfile<<str();
474 610  
475 611 //close the file
476 612 outfile.close();
477 613  
  614 + if (M.size()) { //if materials are used
  615 +
  616 + outfile.open(mtl_filename.c_str()); //open the material file
  617 + for (size_t i = 0; i < M.size(); i++) { //for each material
  618 + outfile << M[i].str() << std::endl; //output the material name and properties
  619 + }
  620 + outfile.close();
  621 + }
  622 +
478 623 return true;
479 624 }
480 625  
... ...
stim/visualization/obj/obj_material.h 0 → 100644
  1 +#ifndef OBJ_MATERIAL_H
  2 +#define OBJ_MATERIAL_H
  3 +
  4 +#include <sstream>
  5 +#include <cstring>
  6 +
  7 +namespace stim {
  8 +
  9 +template<typename T>
  10 +struct obj_material {
  11 + std::string name; //material name
  12 + T ka[3]; //ambient color
  13 + T kd[3]; //diffuse color
  14 + T ks[3]; //specular color
  15 + T ns; //specular exponent
  16 +
  17 + int illum; //illumination model
  18 +
  19 + std::string tex_ka; //ambient texture
  20 + std::string tex_kd; //diffuse texture
  21 + std::string tex_ks; //specular texture
  22 + std::string tex_ns; //texture map for the specular exponent
  23 + std::string tex_alpha; //texture map for the alpha component
  24 + std::string tex_bump; //bump map
  25 + std::string tex_disp; //displacement map
  26 + std::string tex_decal; //stencil decal
  27 +
  28 + obj_material() { //constructor
  29 + std::memset(ka, 0, sizeof(T) * 3);
  30 + std::memset(kd, 0, sizeof(T) * 3);
  31 + std::memset(ks, 0, sizeof(T) * 3);
  32 + ns = 10;
  33 + illum = 2;
  34 +
  35 + }
  36 + std::string str() {
  37 + std::stringstream ss;
  38 + ss << "newmtl " << name << std::endl;
  39 + ss << "Ka " << ka[0] << " " << ka[1] << " " << ka[2] << std::endl;
  40 + ss << "Kd " << kd[0] << " " << kd[1] << " " << kd[2] << std::endl;
  41 + ss << "Ks " << ks[0] << " " << ks[1] << " " << ks[2] << std::endl;
  42 + ss << "Ns " << ns << std::endl;
  43 + ss << "illum " << illum << std::endl;
  44 + if (tex_ka != "") ss << "map_Ka " << tex_ka << std::endl;
  45 + if (tex_kd != "") ss << "map_Kd " << tex_kd << std::endl;
  46 + if (tex_ks != "") ss << "map_Ks " << tex_ks << std::endl;
  47 + if (tex_ns != "") ss << "map_Ns " << tex_ns << std::endl;
  48 + if (tex_alpha != "") ss << "map_d " << tex_alpha << std::endl;
  49 + if (tex_bump != "") ss << "bump " << tex_bump << std::endl;
  50 + if (tex_disp != "") ss << "disp " << tex_disp << std::endl;
  51 + if (tex_decal != "") ss << "decal " << tex_decal << std::endl;
  52 + return ss.str();
  53 + }
  54 +};
  55 +
  56 +} //end namespace stim
  57 +
  58 +#endif
0 59 \ No newline at end of file
... ...