1 |
|
|
/* |
2 |
|
|
Teem: Tools to process and visualize scientific data and images . |
3 |
|
|
Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago |
4 |
|
|
Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann |
5 |
|
|
Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah |
6 |
|
|
|
7 |
|
|
This library is free software; you can redistribute it and/or |
8 |
|
|
modify it under the terms of the GNU Lesser General Public License |
9 |
|
|
(LGPL) as published by the Free Software Foundation; either |
10 |
|
|
version 2.1 of the License, or (at your option) any later version. |
11 |
|
|
The terms of redistributing and/or modifying this software also |
12 |
|
|
include exceptions to the LGPL that facilitate static linking. |
13 |
|
|
|
14 |
|
|
This library is distributed in the hope that it will be useful, |
15 |
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 |
|
|
Lesser General Public License for more details. |
18 |
|
|
|
19 |
|
|
You should have received a copy of the GNU Lesser General Public License |
20 |
|
|
along with this library; if not, write to Free Software Foundation, Inc., |
21 |
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 |
|
|
*/ |
23 |
|
|
|
24 |
|
|
#include "nrrd.h" |
25 |
|
|
#include "privateNrrd.h" |
26 |
|
|
|
27 |
|
|
/* |
28 |
|
|
** learned: even when using doubles, because of limited floating point |
29 |
|
|
** precision, you can get different results between quantizing |
30 |
|
|
** unrescaled (value directly from nrrd, map domain set to nrrd range, |
31 |
|
|
** as with early behavior of unu rmap) and rescaled (value from nrrd |
32 |
|
|
** scaled to fit in existing map domain, as with unu imap -r) value, |
33 |
|
|
** to the exact same index space. |
34 |
|
|
*/ |
35 |
|
|
|
36 |
|
|
/* |
37 |
|
|
** I won't try to support mapping individual values through a |
38 |
|
|
** colormap, as with a function evaluation on a single passed value. |
39 |
|
|
** That will be handled in an upcoming library... |
40 |
|
|
*/ |
41 |
|
|
|
42 |
|
|
/* |
43 |
|
|
** this identifies the different kinds of 1D maps, useful for the |
44 |
|
|
** functions in this file only |
45 |
|
|
*/ |
46 |
|
|
enum { |
47 |
|
|
kindLut=0, |
48 |
|
|
kindRmap=1, |
49 |
|
|
kindImap=2 |
50 |
|
|
}; |
51 |
|
|
|
52 |
|
|
double |
53 |
|
|
_nrrdApplyDomainMin(const Nrrd *nmap, int ramps, int mapAxis) { |
54 |
|
|
double ret; |
55 |
|
|
|
56 |
|
|
AIR_UNUSED(ramps); |
57 |
|
|
ret = nmap->axis[mapAxis].min; |
58 |
|
|
ret = AIR_EXISTS(ret) ? ret : 0; |
59 |
|
|
return ret; |
60 |
|
|
} |
61 |
|
|
|
62 |
|
|
double |
63 |
|
|
_nrrdApplyDomainMax(const Nrrd *nmap, int ramps, int mapAxis) { |
64 |
|
|
double ret; |
65 |
|
|
|
66 |
|
|
ret = nmap->axis[mapAxis].max; |
67 |
|
|
if (!AIR_EXISTS(ret)) { |
68 |
|
|
ret = AIR_CAST(double, nmap->axis[mapAxis].size); |
69 |
|
|
ret = ramps ? ret-1 : ret; |
70 |
|
|
} |
71 |
|
|
return ret; |
72 |
|
|
} |
73 |
|
|
|
74 |
|
|
/* |
75 |
|
|
** _nrrdApply1DSetUp() |
76 |
|
|
** |
77 |
|
|
** some error checking and initializing needed for 1D LUTS, regular, |
78 |
|
|
** and irregular maps. The intent is that if this succeeds, then |
79 |
|
|
** there is no need for any further error checking. |
80 |
|
|
** |
81 |
|
|
** The only thing this function DOES is allocate the output nrrd, and |
82 |
|
|
** set meta information. The rest is just error checking. |
83 |
|
|
** |
84 |
|
|
** The given NrrdRange has to be fleshed out by the caller: it can't |
85 |
|
|
** be NULL, and both range->min and range->max must exist. |
86 |
|
|
*/ |
87 |
|
|
int |
88 |
|
|
_nrrdApply1DSetUp(Nrrd *nout, const Nrrd *nin, const NrrdRange *range, |
89 |
|
|
const Nrrd *nmap, int kind, int typeOut, |
90 |
|
|
int rescale, int multi) { |
91 |
|
|
static const char me[]="_nrrdApply1DSetUp"; |
92 |
|
|
char *mapcnt; |
93 |
|
|
char nounStr[][AIR_STRLEN_SMALL]={"lut", |
94 |
|
|
"regular map", |
95 |
|
|
"irregular map"}; |
96 |
|
|
char mnounStr[][AIR_STRLEN_SMALL]={"multi lut", |
97 |
|
|
"multi regular map", |
98 |
|
|
"multi irregular map"}; |
99 |
|
|
/* wishful thinking */ |
100 |
|
|
char verbStr[][AIR_STRLEN_SMALL]={"lut", |
101 |
|
|
"rmap", |
102 |
|
|
"imap"}; |
103 |
|
|
char mverbStr[][AIR_STRLEN_SMALL]={"mlut", |
104 |
|
|
"mrmap", |
105 |
|
|
"mimap"}; /* wishful thinking */ |
106 |
|
|
int mapAxis, copyMapAxis0=AIR_FALSE, axisMap[NRRD_DIM_MAX]; |
107 |
|
|
unsigned int ax, dim, entLen; |
108 |
|
|
size_t size[NRRD_DIM_MAX]; |
109 |
|
|
double domMin, domMax; |
110 |
|
|
|
111 |
|
|
if (nout == nin) { |
112 |
|
|
biffAddf(NRRD, "%s: due to laziness, nout==nin always disallowed", me); |
113 |
|
|
return 1; |
114 |
|
|
} |
115 |
|
|
if (airEnumValCheck(nrrdType, typeOut)) { |
116 |
|
|
biffAddf(NRRD, "%s: invalid requested output type %d", me, typeOut); |
117 |
|
|
return 1; |
118 |
|
|
} |
119 |
|
|
if (nrrdTypeBlock == nin->type || nrrdTypeBlock == typeOut) { |
120 |
|
|
biffAddf(NRRD, "%s: input or requested output type is %s, need scalar", |
121 |
|
|
me, airEnumStr(nrrdType, nrrdTypeBlock)); |
122 |
|
|
return 1; |
123 |
|
|
} |
124 |
|
|
if (rescale) { |
125 |
|
|
if (!range) { |
126 |
|
|
biffAddf(NRRD, "%s: want rescaling but didn't get a range", me); |
127 |
|
|
return 1; |
128 |
|
|
} |
129 |
|
|
if (!( AIR_EXISTS(range->min) && AIR_EXISTS(range->max) )) { |
130 |
|
|
biffAddf(NRRD, "%s: want rescaling but not both " |
131 |
|
|
"range->{min,max} %g %g exist", me, range->min, range->max); |
132 |
|
|
return 1; |
133 |
|
|
} |
134 |
|
|
/* HEY: consider adding an error check for range->min == range->max |
135 |
|
|
here; the code below now guards |
136 |
|
|
AIR_AFFINE(range->min, val, range->max, domMin, domMax) |
137 |
|
|
against producing NaNs, but maybe that's too clever */ |
138 |
|
|
} |
139 |
|
|
if (kindLut == kind || kindRmap == kind) { |
140 |
|
|
if (!multi) { |
141 |
|
|
mapAxis = nmap->dim - 1; |
142 |
|
|
if (!(0 == mapAxis || 1 == mapAxis)) { |
143 |
|
|
biffAddf(NRRD, "%s: dimension of %s should be 1 or 2, not %d", |
144 |
|
|
me, nounStr[kind], nmap->dim); |
145 |
|
|
return 1; |
146 |
|
|
} |
147 |
|
|
copyMapAxis0 = (1 == mapAxis); |
148 |
|
|
} else { |
149 |
|
|
mapAxis = nmap->dim - nin->dim - 1; |
150 |
|
|
if (!(0 == mapAxis || 1 == mapAxis)) { |
151 |
|
|
biffAddf(NRRD, "%s: dimension of %s should be %d or %d, not %d", |
152 |
|
|
me, mnounStr[kind], |
153 |
|
|
nin->dim + 1, nin->dim + 2, nmap->dim); |
154 |
|
|
return 1; |
155 |
|
|
} |
156 |
|
|
copyMapAxis0 = (1 == mapAxis); |
157 |
|
|
/* need to make sure the relevant sizes match */ |
158 |
|
|
for (ax=0; ax<nin->dim; ax++) { |
159 |
|
|
unsigned int taxi = mapAxis + 1 + ax; |
160 |
|
|
if (taxi > NRRD_DIM_MAX-1) { |
161 |
|
|
biffAddf(NRRD, "%s: test axis index %u exceeds NRRD_DIM_MAX-1 %u", |
162 |
|
|
me, taxi, NRRD_DIM_MAX-1); |
163 |
|
|
return 1; |
164 |
|
|
} |
165 |
|
|
if (nin->axis[ax].size != nmap->axis[taxi].size) { |
166 |
|
|
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; |
167 |
|
|
biffAddf(NRRD, "%s: input and mmap don't have compatible sizes: " |
168 |
|
|
"nin->axis[%d].size (%s) " |
169 |
|
|
"!= nmap->axis[%d].size (%s): ", me, |
170 |
|
|
ax, airSprintSize_t(stmp1, nin->axis[ax].size), |
171 |
|
|
mapAxis + 1 + ax, |
172 |
|
|
airSprintSize_t(stmp2, nmap->axis[taxi].size)); |
173 |
|
|
return 1; |
174 |
|
|
} |
175 |
|
|
} |
176 |
|
|
} |
177 |
|
|
domMin = _nrrdApplyDomainMin(nmap, AIR_FALSE, mapAxis); |
178 |
|
|
domMax = _nrrdApplyDomainMax(nmap, AIR_FALSE, mapAxis); |
179 |
|
|
if (!( domMin < domMax )) { |
180 |
|
|
biffAddf(NRRD, "%s: (axis %d) domain min (%g) not less than max (%g)", |
181 |
|
|
me, mapAxis, domMin, domMax); |
182 |
|
|
return 1; |
183 |
|
|
} |
184 |
|
|
if (nrrdHasNonExist(nmap)) { |
185 |
|
|
biffAddf(NRRD, "%s: %s nrrd has non-existent values", |
186 |
|
|
me, multi ? mnounStr[kind] : nounStr[kind]); |
187 |
|
|
return 1; |
188 |
|
|
} |
189 |
|
|
entLen = mapAxis ? AIR_CAST(unsigned int, nmap->axis[0].size) : 1; |
190 |
|
|
} else { |
191 |
|
|
if (multi) { |
192 |
|
|
biffAddf(NRRD, "%s: sorry, multi irregular maps not implemented", me); |
193 |
|
|
return 1; |
194 |
|
|
} |
195 |
|
|
/* its an irregular map */ |
196 |
|
|
if (nrrd1DIrregMapCheck(nmap)) { |
197 |
|
|
biffAddf(NRRD, "%s: problem with irregular map", me); |
198 |
|
|
return 1; |
199 |
|
|
} |
200 |
|
|
/* mapAxis has no meaning for irregular maps, but we'll pretend ... */ |
201 |
|
|
mapAxis = nmap->axis[0].size == 2 ? 0 : 1; |
202 |
|
|
copyMapAxis0 = AIR_TRUE; |
203 |
|
|
entLen = AIR_CAST(unsigned int, nmap->axis[0].size-1); |
204 |
|
|
} |
205 |
|
|
if (mapAxis + nin->dim > NRRD_DIM_MAX) { |
206 |
|
|
biffAddf(NRRD, "%s: input nrrd dim %d through non-scalar %s exceeds " |
207 |
|
|
"NRRD_DIM_MAX %d", |
208 |
|
|
me, nin->dim, |
209 |
|
|
multi ? mnounStr[kind] : nounStr[kind], NRRD_DIM_MAX); |
210 |
|
|
return 1; |
211 |
|
|
} |
212 |
|
|
nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size+mapAxis); |
213 |
|
|
if (mapAxis) { |
214 |
|
|
size[0] = entLen; |
215 |
|
|
axisMap[0] = -1; |
216 |
|
|
} |
217 |
|
|
for (dim=0; dim<nin->dim; dim++) { |
218 |
|
|
axisMap[dim+mapAxis] = dim; |
219 |
|
|
} |
220 |
|
|
/* |
221 |
|
|
fprintf(stderr, "##%s: pre maybe alloc: nout->data = %p\n", me, nout->data); |
222 |
|
|
for (dim=0; dim<mapAxis + nin->dim; dim++) { |
223 |
|
|
fprintf(stderr, " size[%d] = %d\n", d, (int)size[d]); |
224 |
|
|
} |
225 |
|
|
fprintf(stderr, " nout->dim = %d; nout->type = %d = %s; sizes = %d,%d\n", |
226 |
|
|
nout->dim, nout->type, |
227 |
|
|
airEnumStr(nrrdType, nout->type)); |
228 |
|
|
fprintf(stderr, " typeOut = %d = %s\n", typeOut, |
229 |
|
|
airEnumStr(nrrdType, typeOut)); |
230 |
|
|
*/ |
231 |
|
|
if (nrrdMaybeAlloc_nva(nout, typeOut, mapAxis + nin->dim, size)) { |
232 |
|
|
biffAddf(NRRD, "%s: couldn't allocate output nrrd", me); |
233 |
|
|
return 1; |
234 |
|
|
} |
235 |
|
|
/* |
236 |
|
|
fprintf(stderr, " nout->dim = %d; nout->type = %d = %s\n", |
237 |
|
|
nout->dim, nout->type, |
238 |
|
|
airEnumStr(nrrdType, nout->type), |
239 |
|
|
nout->axis[0].size, nout->axis[1].size); |
240 |
|
|
for (d=0; d<nout->dim; d++) { |
241 |
|
|
fprintf(stderr, " size[%d] = %d\n", d, (int)nout->axis[d].size); |
242 |
|
|
} |
243 |
|
|
fprintf(stderr, "##%s: post maybe alloc: nout->data = %p\n", me, nout->data); |
244 |
|
|
*/ |
245 |
|
|
if (nrrdAxisInfoCopy(nout, nin, axisMap, NRRD_AXIS_INFO_NONE)) { |
246 |
|
|
biffAddf(NRRD, "%s: trouble copying axis info", me); |
247 |
|
|
return 1; |
248 |
|
|
} |
249 |
|
|
if (copyMapAxis0) { |
250 |
|
|
_nrrdAxisInfoCopy(nout->axis + 0, nmap->axis + 0, |
251 |
|
|
NRRD_AXIS_INFO_SIZE_BIT); |
252 |
|
|
} |
253 |
|
|
|
254 |
|
|
mapcnt = _nrrdContentGet(nmap); |
255 |
|
|
if (nrrdContentSet_va(nout, multi ? mverbStr[kind] : verbStr[kind], |
256 |
|
|
nin, "%s", mapcnt)) { |
257 |
|
|
biffAddf(NRRD, "%s:", me); |
258 |
|
|
free(mapcnt); return 1; |
259 |
|
|
} |
260 |
|
|
free(mapcnt); |
261 |
|
|
if (nrrdBasicInfoCopy(nout, nin, |
262 |
|
|
NRRD_BASIC_INFO_DATA_BIT |
263 |
|
|
| NRRD_BASIC_INFO_TYPE_BIT |
264 |
|
|
| NRRD_BASIC_INFO_BLOCKSIZE_BIT |
265 |
|
|
| NRRD_BASIC_INFO_DIMENSION_BIT |
266 |
|
|
| NRRD_BASIC_INFO_CONTENT_BIT |
267 |
|
|
| (nrrdStateKeyValuePairsPropagate |
268 |
|
|
? 0 |
269 |
|
|
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { |
270 |
|
|
biffAddf(NRRD, "%s:", me); |
271 |
|
|
return 1; |
272 |
|
|
} |
273 |
|
|
return 0; |
274 |
|
|
} |
275 |
|
|
|
276 |
|
|
/* |
277 |
|
|
** _nrrdApply1DLutOrRegMap() |
278 |
|
|
** |
279 |
|
|
** the guts of nrrdApply1DLut and nrrdApply1DRegMap |
280 |
|
|
** |
281 |
|
|
** yikes, does NOT use biff, since we're only supposed to be called |
282 |
|
|
** after copious error checking. |
283 |
|
|
** |
284 |
|
|
** FOR INSTANCE, this allows nout == nin, which could be a big |
285 |
|
|
** problem if mapAxis == 1. |
286 |
|
|
** |
287 |
|
|
** we don't need a typeOut arg because nout has already been allocated |
288 |
|
|
** as some specific type; we'll look at that. |
289 |
|
|
** |
290 |
|
|
** NOTE: non-existent values get passed through regular maps and luts |
291 |
|
|
** "unchanged". However, if the output type is integral, the results |
292 |
|
|
** are probaby undefined. HEY: there is currently no warning message |
293 |
|
|
** or error handling based on nrrdStateDisallowIntegerNonExist, but |
294 |
|
|
** there really should be. |
295 |
|
|
*/ |
296 |
|
|
int |
297 |
|
|
_nrrdApply1DLutOrRegMap(Nrrd *nout, const Nrrd *nin, const NrrdRange *range, |
298 |
|
|
const Nrrd *nmap, int ramps, int rescale, int multi) { |
299 |
|
|
/* static const char me[]="_nrrdApply1DLutOrRegMap"; */ |
300 |
|
|
char *inData, *outData, *mapData, *entData0, *entData1; |
301 |
|
|
size_t N, I; |
302 |
|
|
double (*inLoad)(const void *v), (*mapLup)(const void *v, size_t I), |
303 |
|
|
(*outInsert)(void *v, size_t I, double d), |
304 |
|
|
val, mapIdxFrac, domMin, domMax; |
305 |
|
|
unsigned int i, mapAxis, mapLen, mapIdx, entSize, entLen, inSize, outSize; |
306 |
|
|
|
307 |
|
|
if (!multi) { |
308 |
|
|
mapAxis = nmap->dim - 1; /* axis of nmap containing entries */ |
309 |
|
|
} else { |
310 |
|
|
mapAxis = nmap->dim - nin->dim - 1; |
311 |
|
|
} |
312 |
|
|
mapData = (char *)nmap->data; /* map data, as char* */ |
313 |
|
|
/* low end of map domain */ |
314 |
|
|
domMin = _nrrdApplyDomainMin(nmap, ramps, mapAxis); |
315 |
|
|
/* high end of map domain */ |
316 |
|
|
domMax = _nrrdApplyDomainMax(nmap, ramps, mapAxis); |
317 |
|
|
mapLen = AIR_CAST(unsigned int, nmap->axis[mapAxis].size); /* number of entries in map */ |
318 |
|
|
mapLup = nrrdDLookup[nmap->type]; /* how to get doubles out of map */ |
319 |
|
|
inData = (char *)nin->data; /* input data, as char* */ |
320 |
|
|
inLoad = nrrdDLoad[nin->type]; /* how to get doubles out of nin */ |
321 |
|
|
inSize = AIR_CAST(unsigned int, nrrdElementSize(nin)); /* size of one input value */ |
322 |
|
|
outData = (char *)nout->data; /* output data, as char* */ |
323 |
|
|
outInsert = nrrdDInsert[nout->type]; /* putting doubles into output */ |
324 |
|
|
entLen = (mapAxis /* number of elements in one entry */ |
325 |
|
|
? AIR_CAST(unsigned int, nmap->axis[0].size) |
326 |
|
|
: 1); |
327 |
|
|
outSize = entLen*AIR_CAST(unsigned int, nrrdElementSize(nout)); /* size of entry in output */ |
328 |
|
|
entSize = entLen*AIR_CAST(unsigned int, nrrdElementSize(nmap)); /* size of entry in map */ |
329 |
|
|
|
330 |
|
|
N = nrrdElementNumber(nin); /* the number of values to be mapped */ |
331 |
|
|
if (ramps) { |
332 |
|
|
/* regular map */ |
333 |
|
|
for (I=0; I<N; I++) { |
334 |
|
|
/* ... |
335 |
|
|
if (!(I % 100)) fprintf(stderr, "I = %d\n", (int)I); |
336 |
|
|
... */ |
337 |
|
|
val = inLoad(inData); |
338 |
|
|
/* ... |
339 |
|
|
fprintf(stderr, "##%s: val = \na% 31.15f --> ", me, val); |
340 |
|
|
... */ |
341 |
|
|
if (rescale) { |
342 |
|
|
val = (range->min != range->max |
343 |
|
|
? AIR_AFFINE(range->min, val, range->max, domMin, domMax) |
344 |
|
|
: domMin); |
345 |
|
|
/* ... |
346 |
|
|
fprintf(stderr, "\nb% 31.15f (min,max = %g,%g)--> ", val, |
347 |
|
|
range->min, range->max); |
348 |
|
|
... */ |
349 |
|
|
} |
350 |
|
|
/* ... |
351 |
|
|
fprintf(stderr, "\nc% 31.15f --> clamp(%g,%g), %d --> ", |
352 |
|
|
val, domMin, domMax, mapLen); |
353 |
|
|
... */ |
354 |
|
|
if (AIR_EXISTS(val)) { |
355 |
|
|
val = AIR_CLAMP(domMin, val, domMax); |
356 |
|
|
mapIdxFrac = AIR_AFFINE(domMin, val, domMax, 0, mapLen-1); |
357 |
|
|
/* ... |
358 |
|
|
fprintf(stderr, "mapIdxFrac = \nd% 31.15f --> ", |
359 |
|
|
mapIdxFrac); |
360 |
|
|
... */ |
361 |
|
|
mapIdx = (unsigned int)mapIdxFrac; |
362 |
|
|
mapIdx -= mapIdx == mapLen-1; |
363 |
|
|
mapIdxFrac -= mapIdx; |
364 |
|
|
/* ... |
365 |
|
|
fprintf(stderr, "%s: (%d,\ne% 31.15f) --> \n", |
366 |
|
|
me, mapIdx, mapIdxFrac); |
367 |
|
|
... */ |
368 |
|
|
entData0 = mapData + mapIdx*entSize; |
369 |
|
|
entData1 = mapData + (mapIdx+1)*entSize; |
370 |
|
|
for (i=0; i<entLen; i++) { |
371 |
|
|
val = ((1-mapIdxFrac)*mapLup(entData0, i) + |
372 |
|
|
mapIdxFrac*mapLup(entData1, i)); |
373 |
|
|
outInsert(outData, i, val); |
374 |
|
|
/* ... |
375 |
|
|
fprintf(stderr, "f% 31.15f\n", val); |
376 |
|
|
... */ |
377 |
|
|
} |
378 |
|
|
} else { |
379 |
|
|
/* copy non-existent values from input to output */ |
380 |
|
|
for (i=0; i<entLen; i++) { |
381 |
|
|
outInsert(outData, i, val); |
382 |
|
|
} |
383 |
|
|
} |
384 |
|
|
inData += inSize; |
385 |
|
|
outData += outSize; |
386 |
|
|
if (multi) { |
387 |
|
|
mapData += mapLen*entSize; |
388 |
|
|
} |
389 |
|
|
} |
390 |
|
|
} else { |
391 |
|
|
/* lookup table */ |
392 |
|
|
for (I=0; I<N; I++) { |
393 |
|
|
val = inLoad(inData); |
394 |
|
|
if (rescale) { |
395 |
|
|
val = (range->min != range->max |
396 |
|
|
? AIR_AFFINE(range->min, val, range->max, domMin, domMax) |
397 |
|
|
: domMin); |
398 |
|
|
} |
399 |
|
|
if (AIR_EXISTS(val)) { |
400 |
|
|
mapIdx = airIndexClamp(domMin, val, domMax, mapLen); |
401 |
|
|
entData0 = mapData + mapIdx*entSize; |
402 |
|
|
for (i=0; i<entLen; i++) { |
403 |
|
|
outInsert(outData, i, mapLup(entData0, i)); |
404 |
|
|
} |
405 |
|
|
} else { |
406 |
|
|
/* copy non-existent values from input to output */ |
407 |
|
|
for (i=0; i<entLen; i++) { |
408 |
|
|
outInsert(outData, i, val); |
409 |
|
|
} |
410 |
|
|
} |
411 |
|
|
inData += inSize; |
412 |
|
|
outData += outSize; |
413 |
|
|
if (multi) { |
414 |
|
|
mapData += mapLen*entSize; |
415 |
|
|
} |
416 |
|
|
} |
417 |
|
|
} |
418 |
|
|
|
419 |
|
|
return 0; |
420 |
|
|
} |
421 |
|
|
|
422 |
|
|
/* |
423 |
|
|
******** nrrdApply1DLut |
424 |
|
|
** |
425 |
|
|
** A "lut" is a simple lookup table: the data points are evenly spaced, |
426 |
|
|
** with cell-centering assumed, and there is no interpolation except |
427 |
|
|
** nearest neighbor. The axis min and max are used to determine the |
428 |
|
|
** range of values that can be mapped with the lut. |
429 |
|
|
** |
430 |
|
|
** Of the three kinds of 1-D maps, only luts can have output type block. |
431 |
|
|
** |
432 |
|
|
** If the lut nrrd is 1-D, then the output is a scalar nrrd with the |
433 |
|
|
** same dimension as the input. If the lut nrrd is 2-D, then each |
434 |
|
|
** value in the input is mapped to a vector of values from the lut, |
435 |
|
|
** which is always a scanline along axis 0. |
436 |
|
|
** |
437 |
|
|
** This allows lut length to be simply 1. |
438 |
|
|
*/ |
439 |
|
|
int |
440 |
|
|
nrrdApply1DLut(Nrrd *nout, const Nrrd *nin, |
441 |
|
|
const NrrdRange *_range, const Nrrd *nlut, |
442 |
|
|
int typeOut, int rescale) { |
443 |
|
|
static const char me[]="nrrdApply1DLut"; |
444 |
|
|
NrrdRange *range; |
445 |
|
|
airArray *mop; |
446 |
|
|
|
447 |
|
|
if (!(nout && nlut && nin)) { |
448 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
449 |
|
|
return 1; |
450 |
|
|
} |
451 |
|
|
mop = airMopNew(); |
452 |
|
|
if (_range) { |
453 |
|
|
range = nrrdRangeCopy(_range); |
454 |
|
|
nrrdRangeSafeSet(range, nin, nrrdBlind8BitRangeState); |
455 |
|
|
} else { |
456 |
|
|
range = nrrdRangeNewSet(nin, nrrdBlind8BitRangeState); |
457 |
|
|
} |
458 |
|
|
airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); |
459 |
|
|
if (_nrrdApply1DSetUp(nout, nin, range, nlut, kindLut, typeOut, |
460 |
|
|
rescale, AIR_FALSE /* multi */) |
461 |
|
|
|| _nrrdApply1DLutOrRegMap(nout, nin, range, nlut, AIR_FALSE /* ramps */, |
462 |
|
|
rescale, AIR_FALSE /* multi */)) { |
463 |
|
|
biffAddf(NRRD, "%s:", me); |
464 |
|
|
airMopError(mop); return 1; |
465 |
|
|
} |
466 |
|
|
airMopOkay(mop); |
467 |
|
|
return 0; |
468 |
|
|
} |
469 |
|
|
|
470 |
|
|
int |
471 |
|
|
nrrdApplyMulti1DLut(Nrrd *nout, const Nrrd *nin, |
472 |
|
|
const NrrdRange *_range, const Nrrd *nmlut, |
473 |
|
|
int typeOut, int rescale) { |
474 |
|
|
static const char me[]="nrrdApplyMulti1DLut"; |
475 |
|
|
NrrdRange *range; |
476 |
|
|
airArray *mop; |
477 |
|
|
|
478 |
|
|
if (!(nout && nmlut && nin)) { |
479 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
480 |
|
|
return 1; |
481 |
|
|
} |
482 |
|
|
mop = airMopNew(); |
483 |
|
|
if (_range) { |
484 |
|
|
range = nrrdRangeCopy(_range); |
485 |
|
|
nrrdRangeSafeSet(range, nin, nrrdBlind8BitRangeState); |
486 |
|
|
} else { |
487 |
|
|
range = nrrdRangeNewSet(nin, nrrdBlind8BitRangeState); |
488 |
|
|
} |
489 |
|
|
airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); |
490 |
|
|
if (_nrrdApply1DSetUp(nout, nin, range, nmlut, kindLut, typeOut, |
491 |
|
|
rescale, AIR_TRUE /* multi */) |
492 |
|
|
|| _nrrdApply1DLutOrRegMap(nout, nin, range, nmlut, |
493 |
|
|
AIR_FALSE /* ramps */, |
494 |
|
|
rescale, AIR_TRUE /* multi */)) { |
495 |
|
|
biffAddf(NRRD, "%s:", me); |
496 |
|
|
airMopError(mop); return 1; |
497 |
|
|
} |
498 |
|
|
airMopOkay(mop); |
499 |
|
|
return 0; |
500 |
|
|
} |
501 |
|
|
|
502 |
|
|
/* |
503 |
|
|
******** nrrdApply1DRegMap |
504 |
|
|
** |
505 |
|
|
** A regular map has data points evenly spaced, with node-centering and |
506 |
|
|
** and linear interpolation assumed. As with luts, the axis min and |
507 |
|
|
** max determine the range of values that are mapped. This function |
508 |
|
|
** is used in nrrdHistoEq() and is the basis of the very popular "unu rmap". |
509 |
|
|
** |
510 |
|
|
** If the lut nrrd is 1-D, then the output is a scalar nrrd with the |
511 |
|
|
** same dimension as the input. If the lut nrrd is 2-D, then each |
512 |
|
|
** value in the input is mapped to a linear weighting of vectors |
513 |
|
|
** from the map; the vectors are the scanlines along axis 0. |
514 |
|
|
** |
515 |
|
|
** NB: this function makes NO provisions for non-existent input values. |
516 |
|
|
** There won't be any memory errors, but the results are undefined. |
517 |
|
|
** |
518 |
|
|
** This allows map length to be simply 1. |
519 |
|
|
*/ |
520 |
|
|
int |
521 |
|
|
nrrdApply1DRegMap(Nrrd *nout, const Nrrd *nin, |
522 |
|
|
const NrrdRange *_range, const Nrrd *nmap, |
523 |
|
|
int typeOut, int rescale) { |
524 |
|
|
static const char me[]="nrrdApply1DRegMap"; |
525 |
|
|
NrrdRange *range; |
526 |
|
|
airArray *mop; |
527 |
|
|
|
528 |
|
|
if (!(nout && nmap && nin)) { |
529 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
530 |
|
|
return 1; |
531 |
|
|
} |
532 |
|
|
mop = airMopNew(); |
533 |
|
|
if (_range) { |
534 |
|
|
range = nrrdRangeCopy(_range); |
535 |
|
|
nrrdRangeSafeSet(range, nin, nrrdBlind8BitRangeState); |
536 |
|
|
} else { |
537 |
|
|
range = nrrdRangeNewSet(nin, nrrdBlind8BitRangeState); |
538 |
|
|
} |
539 |
|
|
airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); |
540 |
|
|
if (_nrrdApply1DSetUp(nout, nin, range, nmap, kindRmap, typeOut, |
541 |
|
|
rescale, AIR_FALSE /* multi */) |
542 |
|
|
|| _nrrdApply1DLutOrRegMap(nout, nin, range, nmap, AIR_TRUE /* ramps */, |
543 |
|
|
rescale, AIR_FALSE /* multi */)) { |
544 |
|
|
biffAddf(NRRD, "%s:", me); |
545 |
|
|
airMopError(mop); return 1; |
546 |
|
|
} |
547 |
|
|
airMopOkay(mop); |
548 |
|
|
return 0; |
549 |
|
|
} |
550 |
|
|
|
551 |
|
|
int |
552 |
|
|
nrrdApplyMulti1DRegMap(Nrrd *nout, const Nrrd *nin, |
553 |
|
|
const NrrdRange *_range, const Nrrd *nmmap, |
554 |
|
|
int typeOut, int rescale) { |
555 |
|
|
static const char me[]="nrrdApplyMulti1DRegMap"; |
556 |
|
|
NrrdRange *range; |
557 |
|
|
airArray *mop; |
558 |
|
|
|
559 |
|
|
if (!(nout && nmmap && nin)) { |
560 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
561 |
|
|
return 1; |
562 |
|
|
} |
563 |
|
|
mop = airMopNew(); |
564 |
|
|
if (_range) { |
565 |
|
|
range = nrrdRangeCopy(_range); |
566 |
|
|
nrrdRangeSafeSet(range, nin, nrrdBlind8BitRangeState); |
567 |
|
|
} else { |
568 |
|
|
range = nrrdRangeNewSet(nin, nrrdBlind8BitRangeState); |
569 |
|
|
} |
570 |
|
|
airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); |
571 |
|
|
if (_nrrdApply1DSetUp(nout, nin, range, nmmap, kindRmap, typeOut, |
572 |
|
|
rescale, AIR_TRUE /* multi */) |
573 |
|
|
|| _nrrdApply1DLutOrRegMap(nout, nin, range, nmmap, AIR_TRUE /* ramps */, |
574 |
|
|
rescale, AIR_TRUE /* multi */)) { |
575 |
|
|
biffAddf(NRRD, "%s:", me); |
576 |
|
|
airMopError(mop); return 1; |
577 |
|
|
} |
578 |
|
|
airMopOkay(mop); |
579 |
|
|
return 0; |
580 |
|
|
} |
581 |
|
|
|
582 |
|
|
/* |
583 |
|
|
******** nrrd1DIrregMapCheck() |
584 |
|
|
** |
585 |
|
|
** return zero only for the valid forms of 1D irregular map. |
586 |
|
|
** imap must be 2D, both sizes >= 2, non-block-type, no non-existent |
587 |
|
|
** values in range. If the first point's position is non-existent, |
588 |
|
|
** than the first three points positions must be -inf, NaN, and +inf, |
589 |
|
|
** and none of the other points locations can be non-existent, and |
590 |
|
|
** they must increase monotonically. There must be at least two |
591 |
|
|
** points with existent positions. |
592 |
|
|
*/ |
593 |
|
|
int |
594 |
|
|
nrrd1DIrregMapCheck(const Nrrd *nmap) { |
595 |
|
|
static const char me[]="nrrd1DIrregMapCheck"; |
596 |
|
|
double (*mapLup)(const void *v, size_t I); |
597 |
|
|
int i, entLen, mapLen, baseI; |
598 |
|
|
size_t min[2], max[2]; |
599 |
|
|
Nrrd *nrange; |
600 |
|
|
|
601 |
|
|
if (!nmap) { |
602 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
603 |
|
|
return 1; |
604 |
|
|
} |
605 |
|
|
if (nrrdCheck(nmap)) { |
606 |
|
|
biffAddf(NRRD, "%s: ", me); |
607 |
|
|
return 1; |
608 |
|
|
} |
609 |
|
|
if (nrrdTypeBlock == nmap->type) { |
610 |
|
|
biffAddf(NRRD, "%s: map is %s type, need scalar", |
611 |
|
|
me, airEnumStr(nrrdType, nrrdTypeBlock)); |
612 |
|
|
return 1; |
613 |
|
|
} |
614 |
|
|
if (2 != nmap->dim) { |
615 |
|
|
biffAddf(NRRD, "%s: map needs to have dimension 2, not %d", |
616 |
|
|
me, nmap->dim); |
617 |
|
|
return 1; |
618 |
|
|
} |
619 |
|
|
entLen = AIR_CAST(unsigned int,nmap->axis[0].size); |
620 |
|
|
mapLen = AIR_CAST(unsigned int,nmap->axis[1].size); |
621 |
|
|
if (!( entLen >= 2 && mapLen >= 2 )) { |
622 |
|
|
biffAddf(NRRD, "%s: both map's axes sizes should be >= 2 (not %d,%d)", |
623 |
|
|
me, entLen, mapLen); |
624 |
|
|
return 1; |
625 |
|
|
} |
626 |
|
|
min[0] = 1; max[0] = nmap->axis[0].size-1; |
627 |
|
|
min[1] = 0; max[1] = nmap->axis[1].size-1; |
628 |
|
|
if (nrrdCrop(nrange=nrrdNew(), nmap, min, max)) { |
629 |
|
|
biffAddf(NRRD, "%s: couldn't crop to isolate range of map", me); |
630 |
|
|
nrrdNuke(nrange); return 1; |
631 |
|
|
} |
632 |
|
|
if (nrrdHasNonExist(nrange)) { |
633 |
|
|
biffAddf(NRRD, "%s: map has non-existent values in its range", me); |
634 |
|
|
nrrdNuke(nrange); return 1; |
635 |
|
|
} |
636 |
|
|
nrrdNuke(nrange); |
637 |
|
|
mapLup = nrrdDLookup[nmap->type]; |
638 |
|
|
if (AIR_EXISTS(mapLup(nmap->data, 0))) { |
639 |
|
|
baseI = 0; |
640 |
|
|
} else { |
641 |
|
|
baseI = 3; |
642 |
|
|
if (!( mapLen >= 5 )) { |
643 |
|
|
biffAddf(NRRD, "%s: length of map w/ non-existent locations must " |
644 |
|
|
"be >= 5 (not %d)", me, mapLen); |
645 |
|
|
return 1; |
646 |
|
|
} |
647 |
|
|
if (!( airFP_NEG_INF == airFPClass_d(mapLup(nmap->data, 0*entLen)) && |
648 |
|
|
airFP_QNAN == airFPClass_d(mapLup(nmap->data, 1*entLen)) && |
649 |
|
|
airFP_POS_INF == airFPClass_d(mapLup(nmap->data, 2*entLen)) )) { |
650 |
|
|
biffAddf(NRRD, "%s: 1st entry's position non-existent, but position " |
651 |
|
|
"of 1st three entries (%g:%d,%g:%d,%g:%d) not " |
652 |
|
|
"-inf, NaN, and +inf", me, |
653 |
|
|
mapLup(nmap->data, 0*entLen), |
654 |
|
|
airFPClass_d(mapLup(nmap->data, 0*entLen)), |
655 |
|
|
mapLup(nmap->data, 1*entLen), |
656 |
|
|
airFPClass_d(mapLup(nmap->data, 1*entLen)), |
657 |
|
|
mapLup(nmap->data, 2*entLen), |
658 |
|
|
airFPClass_d(mapLup(nmap->data, 2*entLen))); |
659 |
|
|
return 1; |
660 |
|
|
} |
661 |
|
|
} |
662 |
|
|
for (i=baseI; i<mapLen; i++) { |
663 |
|
|
if (!AIR_EXISTS(mapLup(nmap->data, i*entLen))) { |
664 |
|
|
biffAddf(NRRD, "%s: entry %d has non-existent position", me, i); |
665 |
|
|
return 1; |
666 |
|
|
} |
667 |
|
|
} |
668 |
|
|
for (i=baseI; i<mapLen-1; i++) { |
669 |
|
|
if (!( mapLup(nmap->data, i*entLen) < mapLup(nmap->data, (i+1)*entLen) )) { |
670 |
|
|
biffAddf(NRRD, "%s: map entry %d pos (%g) not < entry %d pos (%g)", |
671 |
|
|
me, i, mapLup(nmap->data, i*entLen), |
672 |
|
|
i+1, mapLup(nmap->data, (i+1)*entLen)); |
673 |
|
|
return 1; |
674 |
|
|
} |
675 |
|
|
} |
676 |
|
|
return 0; |
677 |
|
|
} |
678 |
|
|
|
679 |
|
|
/* |
680 |
|
|
******** nrrd1DIrregAclCheck() |
681 |
|
|
** |
682 |
|
|
** returns zero only on valid accelerators for 1D irregular mappings |
683 |
|
|
*/ |
684 |
|
|
int |
685 |
|
|
nrrd1DIrregAclCheck(const Nrrd *nacl) { |
686 |
|
|
static const char me[]="nrrd1DIrregAclCheck"; |
687 |
|
|
|
688 |
|
|
if (!nacl) { |
689 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
690 |
|
|
return 1; |
691 |
|
|
} |
692 |
|
|
if (nrrdCheck(nacl)) { |
693 |
|
|
biffAddf(NRRD, "%s: ", me); |
694 |
|
|
return 1; |
695 |
|
|
} |
696 |
|
|
if (nrrdTypeUShort != nacl->type) { |
697 |
|
|
biffAddf(NRRD, "%s: type should be %s, not %s", me, |
698 |
|
|
airEnumStr(nrrdType, nrrdTypeUShort), |
699 |
|
|
airEnumStr(nrrdType, nacl->type)); |
700 |
|
|
return 1; |
701 |
|
|
} |
702 |
|
|
if (2 != nacl->dim) { |
703 |
|
|
biffAddf(NRRD, "%s: dimension should be 2, not %d", me, nacl->dim); |
704 |
|
|
return 1; |
705 |
|
|
} |
706 |
|
|
if (!( nacl->axis[0].size == 2 && nacl->axis[1].size >= 2 )) { |
707 |
|
|
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; |
708 |
|
|
biffAddf(NRRD, "%s: sizes (%s,%s) not (2,>=2)", me, |
709 |
|
|
airSprintSize_t(stmp1, nacl->axis[0].size), |
710 |
|
|
airSprintSize_t(stmp2, nacl->axis[1].size)); |
711 |
|
|
return 1; |
712 |
|
|
} |
713 |
|
|
|
714 |
|
|
return 0; |
715 |
|
|
} |
716 |
|
|
|
717 |
|
|
/* |
718 |
|
|
** _nrrd1DIrregMapDomain() |
719 |
|
|
** |
720 |
|
|
** ALLOCATES an array of doubles storing the existent control point |
721 |
|
|
** locations, and sets its length in *poslenP. If there are the three |
722 |
|
|
** points with non-existent locations, these are ignored. |
723 |
|
|
** |
724 |
|
|
** Assumes that nrrd1DIrregMapCheck has been called on "nmap". |
725 |
|
|
*/ |
726 |
|
|
double * |
727 |
|
|
_nrrd1DIrregMapDomain(int *posLenP, int *baseIP, const Nrrd *nmap) { |
728 |
|
|
static const char me[]="_nrrd1DIrregMapDomain"; |
729 |
|
|
int i, entLen, baseI, posLen; |
730 |
|
|
double *pos, (*mapLup)(const void *v, size_t I); |
731 |
|
|
|
732 |
|
|
mapLup = nrrdDLookup[nmap->type]; |
733 |
|
|
baseI = AIR_EXISTS(mapLup(nmap->data, 0)) ? 0 : 3; |
734 |
|
|
if (baseIP) { |
735 |
|
|
*baseIP = baseI; |
736 |
|
|
} |
737 |
|
|
entLen = AIR_CAST(unsigned int,nmap->axis[0].size); |
738 |
|
|
posLen = AIR_CAST(unsigned int,nmap->axis[1].size) - baseI; |
739 |
|
|
if (posLenP) { |
740 |
|
|
*posLenP = posLen; |
741 |
|
|
} |
742 |
|
|
pos = (double*)malloc(posLen * sizeof(double)); |
743 |
|
|
if (!pos) { |
744 |
|
|
biffAddf(NRRD, "%s: couldn't allocate %d doubles\n", me, posLen); |
745 |
|
|
return NULL; |
746 |
|
|
} |
747 |
|
|
for (i=0; i<posLen; i++) { |
748 |
|
|
pos[i] = mapLup(nmap->data, (baseI+i)*entLen); |
749 |
|
|
} |
750 |
|
|
return pos; |
751 |
|
|
} |
752 |
|
|
|
753 |
|
|
/* |
754 |
|
|
** _nrrd1DIrregFindInterval() |
755 |
|
|
** |
756 |
|
|
** The hard part of doing 1D irregular mapping: given an array of |
757 |
|
|
** control point locations, and a value, find which interval the value |
758 |
|
|
** lies in. The lowest and highest possible indices are given in |
759 |
|
|
** "loI" and "hiI". Results are undefined if these do not in fact |
760 |
|
|
** bound the location of correct interval, or if loI > hiI, or if the |
761 |
|
|
** query positon "p" is not in the domain vector "pos". Intervals are |
762 |
|
|
** identified by the integral index of the LOWER of the two control |
763 |
|
|
** points spanning the interval. |
764 |
|
|
** |
765 |
|
|
** This imposes the same structure of half-open intervals that |
766 |
|
|
** is done by airIndex(). That is, a value p is in interval i |
767 |
|
|
** if pos[i] <= p < pos[i+1] for all but the last interval, and |
768 |
|
|
** pos[i] <= p <= pos[i+1] for the last interval (in which case |
769 |
|
|
** i == hiI) |
770 |
|
|
*/ |
771 |
|
|
int |
772 |
|
|
_nrrd1DIrregFindInterval(const double *pos, double p, int loI, int hiI) { |
773 |
|
|
int midI; |
774 |
|
|
|
775 |
|
|
/* |
776 |
|
|
fprintf(stderr, "##%s(%g): hi: %d/%g-%g | %d/%g-%g\n", |
777 |
|
|
"_nrrd1DIrregFindInterval", p, |
778 |
|
|
loI, pos[loI], pos[loI+1], |
779 |
|
|
hiI, pos[hiI], pos[hiI+1]); |
780 |
|
|
*/ |
781 |
|
|
while (loI < hiI) { |
782 |
|
|
midI = (loI + hiI)/2; |
783 |
|
|
if ( pos[midI] <= p && ((midI < hiI && p < pos[midI+1]) || |
784 |
|
|
(midI == hiI && p <= pos[midI+1])) ) { |
785 |
|
|
/* p is between (pos[midI],pos[midI+1]): we're done */ |
786 |
|
|
loI = hiI = midI; |
787 |
|
|
} else if (pos[midI] > p) { |
788 |
|
|
/* p is below interval midI: midI-1 is valid upper bound */ |
789 |
|
|
hiI = midI-1; |
790 |
|
|
} else { |
791 |
|
|
/* p is above interval midI: midI+1 is valid lower bound */ |
792 |
|
|
loI = midI+1; |
793 |
|
|
} |
794 |
|
|
/* |
795 |
|
|
fprintf(stderr, "##%s(%g): %d/%g-%g | %d/%g-%g | %d/%g-%g\n", |
796 |
|
|
"_nrrd1DIrregFindInterval", p, |
797 |
|
|
loI, pos[loI], pos[loI+1], |
798 |
|
|
midI, pos[midI], pos[midI+1], |
799 |
|
|
hiI, pos[hiI], pos[hiI+1]); |
800 |
|
|
*/ |
801 |
|
|
} |
802 |
|
|
return loI; |
803 |
|
|
} |
804 |
|
|
|
805 |
|
|
/* |
806 |
|
|
******** nrrd1DIrregAclGenerate() |
807 |
|
|
** |
808 |
|
|
** Generates the "acl" that is used to speed up the action of |
809 |
|
|
** nrrdApply1DIrregMap(). Basically, the domain of the map |
810 |
|
|
** is quantized into "acllen" bins, and for each bin, the |
811 |
|
|
** lowest and highest possible map interval is stored. This |
812 |
|
|
** either obviates or speeds up the task of finding which |
813 |
|
|
** interval contains a given value. |
814 |
|
|
** |
815 |
|
|
** Assumes that nrrd1DIrregMapCheck has been called on "nmap". |
816 |
|
|
*/ |
817 |
|
|
int |
818 |
|
|
nrrd1DIrregAclGenerate(Nrrd *nacl, const Nrrd *nmap, size_t aclLen) { |
819 |
|
|
static const char me[]="nrrd1DIrregAclGenerate"; |
820 |
|
|
int posLen; |
821 |
|
|
unsigned int ii; |
822 |
|
|
unsigned short *acl; |
823 |
|
|
double lo, hi, min, max, *pos; |
824 |
|
|
|
825 |
|
|
if (!(nacl && nmap)) { |
826 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
827 |
|
|
return 1; |
828 |
|
|
} |
829 |
|
|
if (!(aclLen >= 2)) { |
830 |
|
|
char stmp[AIR_STRLEN_SMALL]; |
831 |
|
|
biffAddf(NRRD, "%s: given acl length (%s) is too small", me, |
832 |
|
|
airSprintSize_t(stmp, aclLen)); |
833 |
|
|
return 1; |
834 |
|
|
} |
835 |
|
|
if (nrrdMaybeAlloc_va(nacl, nrrdTypeUShort, 2, |
836 |
|
|
AIR_CAST(size_t, 2), AIR_CAST(size_t, aclLen))) { |
837 |
|
|
biffAddf(NRRD, "%s: ", me); |
838 |
|
|
return 1; |
839 |
|
|
} |
840 |
|
|
acl = (unsigned short *)nacl->data; |
841 |
|
|
pos = _nrrd1DIrregMapDomain(&posLen, NULL, nmap); |
842 |
|
|
if (!pos) { |
843 |
|
|
biffAddf(NRRD, "%s: couldn't determine domain", me); |
844 |
|
|
return 1; |
845 |
|
|
} |
846 |
|
|
nacl->axis[1].min = min = pos[0]; |
847 |
|
|
nacl->axis[1].max = max = pos[posLen-1]; |
848 |
|
|
for (ii=0; ii<=aclLen-1; ii++) { |
849 |
|
|
lo = AIR_AFFINE(0, ii, aclLen, min, max); |
850 |
|
|
hi = AIR_AFFINE(0, ii+1, aclLen, min, max); |
851 |
|
|
acl[0 + 2*ii] = _nrrd1DIrregFindInterval(pos, lo, 0, posLen-2); |
852 |
|
|
acl[1 + 2*ii] = _nrrd1DIrregFindInterval(pos, hi, 0, posLen-2); |
853 |
|
|
} |
854 |
|
|
free(pos); |
855 |
|
|
|
856 |
|
|
return 0; |
857 |
|
|
} |
858 |
|
|
|
859 |
|
|
/* |
860 |
|
|
******** nrrdApply1DIrregMap() |
861 |
|
|
** |
862 |
|
|
** Linear interpolation between irregularly spaced control points. |
863 |
|
|
** Obviously, the location of the control point has to be given |
864 |
|
|
** explicitly. The map nrrd must have dimension 2, and each |
865 |
|
|
** control point is represented by a scanline along axis 0. The |
866 |
|
|
** first value is the position of the control point, and the remaining |
867 |
|
|
** value(s) are linearly weighted according to the position of the |
868 |
|
|
** input value among the control point locations. |
869 |
|
|
** |
870 |
|
|
** To allow "coloring" of non-existent values -inf, NaN, and +inf, if |
871 |
|
|
** the very first value of the map (the location of the first control |
872 |
|
|
** point) is non-existent, then the first three control point locations |
873 |
|
|
** must be -inf, NaN, and +inf, in that order, and the information |
874 |
|
|
** about these points will be used for corresponding input values. |
875 |
|
|
** Doing this makes everything slower, however, because airFPClass_f() |
876 |
|
|
** is called on every single value. |
877 |
|
|
** |
878 |
|
|
** This assumes that nrrd1DIrregMapCheck has been called on "nmap", |
879 |
|
|
** and that nrrd1DIrregAclCheck has been called on "nacl" (if it is |
880 |
|
|
** non-NULL). |
881 |
|
|
*/ |
882 |
|
|
int |
883 |
|
|
nrrdApply1DIrregMap(Nrrd *nout, const Nrrd *nin, const NrrdRange *_range, |
884 |
|
|
const Nrrd *nmap, const Nrrd *nacl, |
885 |
|
|
int typeOut, int rescale) { |
886 |
|
|
static const char me[]="nrrdApply1DIrregMap"; |
887 |
|
|
size_t N, I; |
888 |
|
|
int i, *acl, entLen, posLen, aclLen, mapIdx, aclIdx, |
889 |
|
|
entSize, colSize, inSize, lo, hi, baseI; |
890 |
|
|
double val, *pos, domMin, domMax, mapIdxFrac, |
891 |
|
|
(*mapLup)(const void *v, size_t I), |
892 |
|
|
(*inLoad)(const void *v), (*outInsert)(void *v, size_t I, double d); |
893 |
|
|
char *inData, *outData, *entData0, *entData1; |
894 |
|
|
NrrdRange *range; |
895 |
|
|
airArray *mop; |
896 |
|
|
|
897 |
|
|
/* |
898 |
|
|
fprintf(stderr, "!%s: rescale = %d\n", me, rescale); |
899 |
|
|
*/ |
900 |
|
|
if (!(nout && nmap && nin)) { |
901 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
902 |
|
|
return 1; |
903 |
|
|
} |
904 |
|
|
mop = airMopNew(); |
905 |
|
|
if (_range) { |
906 |
|
|
range = nrrdRangeCopy(_range); |
907 |
|
|
nrrdRangeSafeSet(range, nin, nrrdBlind8BitRangeState); |
908 |
|
|
} else { |
909 |
|
|
range = nrrdRangeNewSet(nin, nrrdBlind8BitRangeState); |
910 |
|
|
} |
911 |
|
|
airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); |
912 |
|
|
if (_nrrdApply1DSetUp(nout, nin, range, nmap, |
913 |
|
|
kindImap, typeOut, rescale, AIR_FALSE /* multi */)) { |
914 |
|
|
biffAddf(NRRD, "%s:", me); |
915 |
|
|
airMopError(mop); return 1; |
916 |
|
|
} |
917 |
|
|
if (nacl && nrrd1DIrregAclCheck(nacl)) { |
918 |
|
|
biffAddf(NRRD, "%s: given acl isn't valid", me); |
919 |
|
|
airMopError(mop); return 1; |
920 |
|
|
} |
921 |
|
|
|
922 |
|
|
if (nacl) { |
923 |
|
|
acl = (int *)nacl->data; |
924 |
|
|
aclLen = AIR_CAST(unsigned int,nacl->axis[1].size); |
925 |
|
|
} else { |
926 |
|
|
acl = NULL; |
927 |
|
|
aclLen = 0; |
928 |
|
|
} |
929 |
|
|
pos = _nrrd1DIrregMapDomain(&posLen, &baseI, nmap); |
930 |
|
|
if (!pos) { |
931 |
|
|
biffAddf(NRRD, "%s: couldn't determine domain", me); |
932 |
|
|
airMopError(mop); return 1; |
933 |
|
|
} |
934 |
|
|
airMopAdd(mop, pos, airFree, airMopAlways); |
935 |
|
|
mapLup = nrrdDLookup[nmap->type]; |
936 |
|
|
|
937 |
|
|
inData = (char *)nin->data; |
938 |
|
|
inLoad = nrrdDLoad[nin->type]; |
939 |
|
|
inSize = AIR_CAST(unsigned int,nrrdElementSize(nin)); |
940 |
|
|
mapLup = nrrdDLookup[nmap->type]; |
941 |
|
|
entLen = AIR_CAST(unsigned int,nmap->axis[0].size); /* entLen is really 1 + entry length */ |
942 |
|
|
entSize = entLen*AIR_CAST(unsigned int,nrrdElementSize(nmap)); |
943 |
|
|
colSize = (entLen-1)*AIR_CAST(unsigned int,nrrdTypeSize[typeOut]); |
944 |
|
|
outData = (char *)nout->data; |
945 |
|
|
outInsert = nrrdDInsert[nout->type]; |
946 |
|
|
domMin = pos[0]; |
947 |
|
|
domMax = pos[posLen-1]; |
948 |
|
|
/* |
949 |
|
|
fprintf(stderr, "!%s: domMin, domMax = %g, %g\n", me, domMin, domMax); |
950 |
|
|
*/ |
951 |
|
|
|
952 |
|
|
N = nrrdElementNumber(nin); |
953 |
|
|
for (I=0; |
954 |
|
|
I<N; |
955 |
|
|
I++, inData += inSize, outData += colSize) { |
956 |
|
|
val = inLoad(inData); |
957 |
|
|
/* |
958 |
|
|
fprintf(stderr, "!%s: (%d) val = % 31.15f\n", me, (int)I, val); |
959 |
|
|
*/ |
960 |
|
|
if (!AIR_EXISTS(val)) { |
961 |
|
|
/* got a non-existent value */ |
962 |
|
|
if (baseI) { |
963 |
|
|
/* and we know how to deal with them */ |
964 |
|
|
switch (airFPClass_d(val)) { |
965 |
|
|
case airFP_NEG_INF: |
966 |
|
|
mapIdx = 0; |
967 |
|
|
break; |
968 |
|
|
case airFP_SNAN: |
969 |
|
|
case airFP_QNAN: |
970 |
|
|
mapIdx = 1; |
971 |
|
|
break; |
972 |
|
|
case airFP_POS_INF: |
973 |
|
|
mapIdx = 2; |
974 |
|
|
break; |
975 |
|
|
default: |
976 |
|
|
mapIdx = 0; |
977 |
|
|
fprintf(stderr, "%s: PANIC: non-existent value/class %g/%d " |
978 |
|
|
"not handled\n", |
979 |
|
|
me, val, airFPClass_d(val)); |
980 |
|
|
exit(1); |
981 |
|
|
} |
982 |
|
|
entData0 = (char*)(nmap->data) + mapIdx*entSize; |
983 |
|
|
for (i=1; i<entLen; i++) { |
984 |
|
|
outInsert(outData, i-1, mapLup(entData0, i)); |
985 |
|
|
} |
986 |
|
|
continue; /* we're done! (with this value) */ |
987 |
|
|
} else { |
988 |
|
|
/* we don't know how to properly deal with this non-existent value: |
989 |
|
|
we use the first entry, and then fall through to code below */ |
990 |
|
|
mapIdx = 0; |
991 |
|
|
mapIdxFrac = 0.0; |
992 |
|
|
} |
993 |
|
|
} else { |
994 |
|
|
/* we have an existent value */ |
995 |
|
|
if (rescale) { |
996 |
|
|
val = (range->min != range->max |
997 |
|
|
? AIR_AFFINE(range->min, val, range->max, domMin, domMax) |
998 |
|
|
: domMin); |
999 |
|
|
} |
1000 |
|
|
val = AIR_CLAMP(domMin, val, domMax); |
1001 |
|
|
if (acl) { |
1002 |
|
|
aclIdx = airIndex(domMin, val, domMax, aclLen); |
1003 |
|
|
lo = acl[0 + 2*aclIdx]; |
1004 |
|
|
hi = acl[1 + 2*aclIdx]; |
1005 |
|
|
} else { |
1006 |
|
|
lo = 0; |
1007 |
|
|
hi = posLen-2; |
1008 |
|
|
} |
1009 |
|
|
if (lo < hi) { |
1010 |
|
|
mapIdx = _nrrd1DIrregFindInterval(pos, val, lo, hi); |
1011 |
|
|
} else { |
1012 |
|
|
/* acl did its job ==> lo == hi */ |
1013 |
|
|
mapIdx = lo; |
1014 |
|
|
} |
1015 |
|
|
/* |
1016 |
|
|
fprintf(stderr, "!%s: --> val = %g; lo,hi = %d,%d, mapIdx = %d\n", |
1017 |
|
|
me, val, lo, hi, mapIdx); |
1018 |
|
|
*/ |
1019 |
|
|
} |
1020 |
|
|
mapIdxFrac = AIR_AFFINE(pos[mapIdx], val, pos[mapIdx+1], 0.0, 1.0); |
1021 |
|
|
/* |
1022 |
|
|
fprintf(stderr, "!%s: mapIdxFrac = %g\n", me, mapIdxFrac); |
1023 |
|
|
*/ |
1024 |
|
|
entData0 = (char*)(nmap->data) + (baseI+mapIdx)*entSize; |
1025 |
|
|
entData1 = (char*)(nmap->data) + (baseI+mapIdx+1)*entSize; |
1026 |
|
|
for (i=1; i<entLen; i++) { |
1027 |
|
|
val = ((1-mapIdxFrac)*mapLup(entData0, i) + |
1028 |
|
|
mapIdxFrac*mapLup(entData1, i)); |
1029 |
|
|
/* |
1030 |
|
|
fprintf(stderr, "!%s: %g * %g + %g * %g = %g\n", me, |
1031 |
|
|
1-mapIdxFrac, mapLup(entData0, i), |
1032 |
|
|
mapIdxFrac, mapLup(entData1, i), val); |
1033 |
|
|
*/ |
1034 |
|
|
outInsert(outData, i-1, val); |
1035 |
|
|
} |
1036 |
|
|
} |
1037 |
|
|
airMopOkay(mop); |
1038 |
|
|
return 0; |
1039 |
|
|
} |
1040 |
|
|
|
1041 |
|
|
/* |
1042 |
|
|
******** nrrdApply1DSubstitution |
1043 |
|
|
** |
1044 |
|
|
** A "subst" is a substitution table, i.e. a list of pairs that |
1045 |
|
|
** describes what values should be substituted with what (substitution |
1046 |
|
|
** rules). So, nsubst must be a scalar 2xN array. The output is a |
1047 |
|
|
** copy of the input with values substituted using this table. |
1048 |
|
|
** |
1049 |
|
|
** Unlike with lookup tables and maps (irregular and regular), we |
1050 |
|
|
** aren't at liberty to change the dimensionality of the data (can't |
1051 |
|
|
** substitute a grayscale with a color). The ability to apply |
1052 |
|
|
** substitutions to non-scalar data will be in a different function. |
1053 |
|
|
** Also unlike lookup tables and maps, the output type is the SAME as |
1054 |
|
|
** the input type; the output does NOT inherit the type of the |
1055 |
|
|
** substitution |
1056 |
|
|
*/ |
1057 |
|
|
int |
1058 |
|
|
nrrdApply1DSubstitution(Nrrd *nout, const Nrrd *nin, const Nrrd *_nsubst) { |
1059 |
|
|
static const char me[]="nrrdApply1DSubstitution"; |
1060 |
|
|
double (*lup)(const void *, size_t); |
1061 |
|
|
double (*ins)(void *, size_t, double); |
1062 |
|
|
Nrrd *nsubst; |
1063 |
|
|
double val, *subst; |
1064 |
|
|
size_t ii, jj, num, asize0, asize1; |
1065 |
|
|
int changed; |
1066 |
|
|
airArray *mop; |
1067 |
|
|
|
1068 |
|
|
if (!(nout && _nsubst && nin)) { |
1069 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
1070 |
|
|
return 1; |
1071 |
|
|
} |
1072 |
|
|
if (nrrdTypeBlock == nin->type || nrrdTypeBlock == _nsubst->type) { |
1073 |
|
|
biffAddf(NRRD, "%s: input or substitution type is %s, need scalar", |
1074 |
|
|
me, airEnumStr(nrrdType, nrrdTypeBlock)); |
1075 |
|
|
return 1; |
1076 |
|
|
} |
1077 |
|
|
if (2 != _nsubst->dim) { |
1078 |
|
|
biffAddf(NRRD, "%s: substitution table has to be 2-D, not %d-D", |
1079 |
|
|
me, _nsubst->dim); |
1080 |
|
|
return 1; |
1081 |
|
|
} |
1082 |
|
|
/* nrrdAxisInfoGet_va(_nsubst, nrrdAxisInfoSize, &asize0, &asize1); */ |
1083 |
|
|
asize0 = _nsubst->axis[0].size; |
1084 |
|
|
asize1 = _nsubst->axis[1].size; |
1085 |
|
|
if (2 != asize0) { |
1086 |
|
|
biffAddf(NRRD, "%s: substitution table has to be 2xN, not %uxN", |
1087 |
|
|
me, AIR_CAST(unsigned int, asize0)); |
1088 |
|
|
return 1; |
1089 |
|
|
} |
1090 |
|
|
if (nout != nin) { |
1091 |
|
|
if (nrrdCopy(nout, nin)) { |
1092 |
|
|
biffAddf(NRRD, "%s: couldn't initialize by copy to output", me); |
1093 |
|
|
return 1; |
1094 |
|
|
} |
1095 |
|
|
} |
1096 |
|
|
|
1097 |
|
|
mop = airMopNew(); |
1098 |
|
|
nsubst = nrrdNew(); |
1099 |
|
|
airMopAdd(mop, nsubst, (airMopper)nrrdNuke, airMopAlways); |
1100 |
|
|
if (nrrdConvert(nsubst, _nsubst, nrrdTypeDouble)) { |
1101 |
|
|
biffAddf(NRRD, "%s: couldn't create double copy of substitution table", |
1102 |
|
|
me); |
1103 |
|
|
airMopError(mop); return 1; |
1104 |
|
|
} |
1105 |
|
|
lup = nrrdDLookup[nout->type]; |
1106 |
|
|
ins = nrrdDInsert[nout->type]; |
1107 |
|
|
subst = (double *)nsubst->data; |
1108 |
|
|
num = nrrdElementNumber(nout); |
1109 |
|
|
for (ii=0; ii<num; ii++) { |
1110 |
|
|
val = lup(nout->data, ii); |
1111 |
|
|
changed = AIR_FALSE; |
1112 |
|
|
for (jj=0; jj<asize1; jj++) { |
1113 |
|
|
if (val == subst[jj*2+0]) { |
1114 |
|
|
val = subst[jj*2+1]; |
1115 |
|
|
changed = AIR_TRUE; |
1116 |
|
|
} |
1117 |
|
|
} |
1118 |
|
|
if (changed) { |
1119 |
|
|
ins(nout->data, ii, val); |
1120 |
|
|
} |
1121 |
|
|
} |
1122 |
|
|
|
1123 |
|
|
airMopOkay(mop); |
1124 |
|
|
return 0; |
1125 |
|
|
} |