Compare View
Commits (21)
-
changing the ivote directory from stim/cuda/ to stim/ and add a cuda header file… … to include ivote2 function See merge request !33
Showing
24 changed files
Show diff stats
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 | + | ... | ... |
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
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<T, 3>{ |
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 |