1 /*
2 Teem: Tools to process and visualize scientific data and images .
3 Copyright (C) 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 ******** nrrdSlice()
29 **
30 ** slices a nrrd along a given axis, at a given position.
31 **
32 ** This is a newer version of the procedure, which is simpler, faster,
33 ** and requires less memory overhead than the first one. It is based
34 ** on the observation that any slice is a periodic square-wave pattern
35 ** in the original data (viewed as a one- dimensional array). The
36 ** characteristics of that periodic pattern are how far from the
37 ** beginning it starts (offset), the length of the "on" part (length),
38 ** the period (period), and the number of periods (numper).
39 */
40 int
nrrdSlice(Nrrd * nout,const Nrrd * cnin,unsigned int saxi,size_t pos)41 nrrdSlice(Nrrd *nout, const Nrrd *cnin, unsigned int saxi, size_t pos) {
42 static const char me[]="nrrdSlice", func[]="slice";
43 size_t
44 I,
45 rowLen, /* length of segment */
46 colStep, /* distance between start of each segment */
47 colLen, /* number of periods */
48 szOut[NRRD_DIM_MAX];
49 unsigned int ai, outdim;
50 int map[NRRD_DIM_MAX];
51 const char *src;
52 char *dest, stmp[2][AIR_STRLEN_SMALL];
53 airArray *mop;
54 Nrrd *nin;
55
56 if (!(cnin && nout)) {
57 biffAddf(NRRD, "%s: got NULL pointer", me);
58 return 1;
59 }
60 if (nout == cnin) {
61 biffAddf(NRRD, "%s: nout==nin disallowed", me);
62 return 1;
63 }
64 if (1 == cnin->dim) {
65 if (0 != saxi) {
66 biffAddf(NRRD, "%s: slice axis must be 0, not %u, for 1-D array",
67 me, saxi);
68 return 1;
69 }
70 } else {
71 if (!( saxi < cnin->dim )) {
72 biffAddf(NRRD, "%s: slice axis %d out of bounds (0 to %d)",
73 me, saxi, cnin->dim-1);
74 return 1;
75 }
76 }
77 if (!( pos < cnin->axis[saxi].size )) {
78 biffAddf(NRRD, "%s: position %s out of bounds (0 to %s)", me,
79 airSprintSize_t(stmp[0], pos),
80 airSprintSize_t(stmp[1], cnin->axis[saxi].size-1));
81 return 1;
82 }
83 /* this shouldn't actually be necessary .. */
84 if (!nrrdElementSize(cnin)) {
85 biffAddf(NRRD, "%s: nrrd reports zero element size!", me);
86 return 1;
87 }
88
89 /* HEY: copy and paste from measure.c/nrrdProject */
90 mop = airMopNew();
91 if (1 == cnin->dim) {
92 /* There are more efficient ways of dealing with this case; this way is
93 easy to implement because it leaves most of the established code below
94 only superficially changed; uniformly replacing nin with (nin ? nin :
95 cnin), even if pointlessly so; this expression that can't be assigned
96 to a new variable because of the difference in const. */
97 nin = nrrdNew();
98 airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways);
99 if (nrrdAxesInsert(nin, cnin, 1)) {
100 biffAddf(NRRD, "%s: trouble inserting axis on 1-D array", me);
101 airMopError(mop); return 1;
102 }
103 } else {
104 nin = NULL;
105 }
106
107 /* set up control variables */
108 rowLen = colLen = 1;
109 for (ai=0; ai<(nin ? nin : cnin)->dim; ai++) {
110 if (ai < saxi) {
111 rowLen *= (nin ? nin : cnin)->axis[ai].size;
112 } else if (ai > saxi) {
113 colLen *= (nin ? nin : cnin)->axis[ai].size;
114 }
115 }
116 rowLen *= nrrdElementSize(nin ? nin : cnin);
117 colStep = rowLen*(nin ? nin : cnin)->axis[saxi].size;
118
119 outdim = (nin ? nin : cnin)->dim-1;
120 for (ai=0; ai<outdim; ai++) {
121 map[ai] = AIR_INT(ai) + (ai >= saxi);
122 szOut[ai] = (nin ? nin : cnin)->axis[map[ai]].size;
123 }
124 nout->blockSize = (nin ? nin : cnin)->blockSize;
125 if (nrrdMaybeAlloc_nva(nout, (nin ? nin : cnin)->type, outdim, szOut)) {
126 biffAddf(NRRD, "%s: failed to create slice", me);
127 airMopError(mop); return 1;
128 }
129
130 /* the skinny */
131 src = AIR_CAST(const char *, (nin ? nin : cnin)->data);
132 dest = AIR_CAST(char *, nout->data);
133 src += rowLen*pos;
134 for (I=0; I<colLen; I++) {
135 /* HEY: replace with AIR_MEMCPY() or similar, when applicable */
136 memcpy(dest, src, rowLen);
137 src += colStep;
138 dest += rowLen;
139 }
140
141 /* copy the peripheral information */
142 if (nrrdAxisInfoCopy(nout, (nin ? nin : cnin), map, NRRD_AXIS_INFO_NONE)) {
143 biffAddf(NRRD, "%s:", me);
144 airMopError(mop); return 1;
145 }
146 if (nrrdContentSet_va(nout, func, cnin /* hide possible axinsert*/,
147 "%d,%d", saxi, pos)) {
148 biffAddf(NRRD, "%s:", me);
149 airMopError(mop); return 1;
150 }
151 if (nrrdBasicInfoCopy(nout, (nin ? nin : cnin),
152 NRRD_BASIC_INFO_DATA_BIT
153 | NRRD_BASIC_INFO_TYPE_BIT
154 | NRRD_BASIC_INFO_BLOCKSIZE_BIT
155 | NRRD_BASIC_INFO_DIMENSION_BIT
156 | NRRD_BASIC_INFO_SPACEORIGIN_BIT
157 | NRRD_BASIC_INFO_CONTENT_BIT
158 | NRRD_BASIC_INFO_COMMENTS_BIT
159 | (nrrdStateKeyValuePairsPropagate
160 ? 0
161 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) {
162 biffAddf(NRRD, "%s:", me);
163 airMopError(mop); return 1;
164 }
165 /* translate origin if this was a spatial axis, otherwise copy */
166 /* note that if there is no spatial info at all, this is all harmless */
167 if (AIR_EXISTS((nin ? nin : cnin)->axis[saxi].spaceDirection[0])) {
168 nrrdSpaceVecScaleAdd2(nout->spaceOrigin,
169 1.0, (nin ? nin : cnin)->spaceOrigin,
170 AIR_CAST(double, pos),
171 (nin ? nin : cnin)->axis[saxi].spaceDirection);
172 } else {
173 nrrdSpaceVecCopy(nout->spaceOrigin, (nin ? nin : cnin)->spaceOrigin);
174 }
175 airMopOkay(mop);
176 return 0;
177 }
178
179 /*
180 ******** nrrdCrop()
181 **
182 ** select some sub-volume inside a given nrrd, producing an output
183 ** nrrd with the same dimensions, but with equal or smaller sizes
184 ** along each axis.
185 */
186 int
nrrdCrop(Nrrd * nout,const Nrrd * nin,size_t * min,size_t * max)187 nrrdCrop(Nrrd *nout, const Nrrd *nin, size_t *min, size_t *max) {
188 static const char me[]="nrrdCrop", func[] = "crop";
189 char buff1[NRRD_DIM_MAX*30], buff2[AIR_STRLEN_SMALL];
190 unsigned int ai;
191 size_t I,
192 lineSize, /* #bytes in one scanline to be copied */
193 typeSize, /* size of data type */
194 cIn[NRRD_DIM_MAX], /* coords for line start, in input */
195 cOut[NRRD_DIM_MAX], /* coords for line start, in output */
196 szIn[NRRD_DIM_MAX],
197 szOut[NRRD_DIM_MAX],
198 idxIn, idxOut, /* linear indices for input and output */
199 numLines; /* number of scanlines in output nrrd */
200 char *dataIn, *dataOut, stmp[3][AIR_STRLEN_SMALL];
201
202 /* errors */
203 if (!(nout && nin && min && max)) {
204 biffAddf(NRRD, "%s: got NULL pointer", me);
205 return 1;
206 }
207 if (nout == nin) {
208 biffAddf(NRRD, "%s: nout==nin disallowed", me);
209 return 1;
210 }
211 for (ai=0; ai<nin->dim; ai++) {
212 if (!(min[ai] <= max[ai])) {
213 biffAddf(NRRD, "%s: axis %d min (%s) not <= max (%s)", me, ai,
214 airSprintSize_t(stmp[0], min[ai]),
215 airSprintSize_t(stmp[1], max[ai]));
216 return 1;
217 }
218 if (!( min[ai] < nin->axis[ai].size && max[ai] < nin->axis[ai].size )) {
219 biffAddf(NRRD, "%s: axis %d min (%s) or max (%s) out of bounds [0,%s]",
220 me, ai, airSprintSize_t(stmp[0], min[ai]),
221 airSprintSize_t(stmp[1], max[ai]),
222 airSprintSize_t(stmp[2], nin->axis[ai].size-1));
223 return 1;
224 }
225 }
226 /* this shouldn't actually be necessary .. */
227 if (!nrrdElementSize(nin)) {
228 biffAddf(NRRD, "%s: nrrd reports zero element size!", me);
229 return 1;
230 }
231
232 /* allocate */
233 nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, szIn);
234 numLines = 1;
235 for (ai=0; ai<nin->dim; ai++) {
236 szOut[ai] = max[ai] - min[ai] + 1;
237 if (ai) {
238 numLines *= szOut[ai];
239 }
240 }
241 nout->blockSize = nin->blockSize;
242 if (nrrdMaybeAlloc_nva(nout, nin->type, nin->dim, szOut)) {
243 biffAddf(NRRD, "%s:", me);
244 return 1;
245 }
246 lineSize = szOut[0]*nrrdElementSize(nin);
247
248 /* the skinny */
249 typeSize = nrrdElementSize(nin);
250 dataIn = (char *)nin->data;
251 dataOut = (char *)nout->data;
252 memset(cOut, 0, NRRD_DIM_MAX*sizeof(*cOut));
253 /*
254 printf("!%s: nin->dim = %d\n", me, nin->dim);
255 printf("!%s: min = %d %d %d\n", me, min[0], min[1], min[2]);
256 printf("!%s: szIn = %d %d %d\n", me, szIn[0], szIn[1], szIn[2]);
257 printf("!%s: szOut = %d %d %d\n", me, szOut[0], szOut[1], szOut[2]);
258 printf("!%s: lineSize = %d\n", me, lineSize);
259 printf("!%s: typeSize = %d\n", me, typeSize);
260 printf("!%s: numLines = %d\n", me, (int)numLines);
261 */
262 for (I=0; I<numLines; I++) {
263 for (ai=0; ai<nin->dim; ai++) {
264 cIn[ai] = cOut[ai] + min[ai];
265 }
266 NRRD_INDEX_GEN(idxOut, cOut, szOut, nin->dim);
267 NRRD_INDEX_GEN(idxIn, cIn, szIn, nin->dim);
268 /*
269 printf("!%s: %5d: cOut=(%3d,%3d,%3d) --> idxOut = %5d\n",
270 me, (int)I, cOut[0], cOut[1], cOut[2], (int)idxOut);
271 printf("!%s: %5d: cIn=(%3d,%3d,%3d) --> idxIn = %5d\n",
272 me, (int)I, cIn[0], cIn[1], cIn[2], (int)idxIn);
273 */
274 memcpy(dataOut + idxOut*typeSize, dataIn + idxIn*typeSize, lineSize);
275 /* the lowest coordinate in cOut[] will stay zero, since we are
276 copying one (1-D) scanline at a time */
277 NRRD_COORD_INCR(cOut, szOut, nin->dim, 1);
278 }
279 if (nrrdAxisInfoCopy(nout, nin, NULL, (NRRD_AXIS_INFO_SIZE_BIT |
280 NRRD_AXIS_INFO_MIN_BIT |
281 NRRD_AXIS_INFO_MAX_BIT ))) {
282 biffAddf(NRRD, "%s:", me);
283 return 1;
284 }
285 for (ai=0; ai<nin->dim; ai++) {
286 nrrdAxisInfoPosRange(&(nout->axis[ai].min), &(nout->axis[ai].max),
287 nin, ai, AIR_CAST(double, min[ai]),
288 AIR_CAST(double, max[ai]));
289 /* do the safe thing first */
290 nout->axis[ai].kind = _nrrdKindAltered(nin->axis[ai].kind, AIR_FALSE);
291 /* try cleverness */
292 if (!nrrdStateKindNoop) {
293 if (nout->axis[ai].size == nin->axis[ai].size) {
294 /* we can safely copy kind; the samples didn't change */
295 nout->axis[ai].kind = nin->axis[ai].kind;
296 } else if (nrrdKind4Color == nin->axis[ai].kind
297 && 3 == szOut[ai]) {
298 nout->axis[ai].kind = nrrdKind3Color;
299 } else if (nrrdKind4Vector == nin->axis[ai].kind
300 && 3 == szOut[ai]) {
301 nout->axis[ai].kind = nrrdKind3Vector;
302 } else if ((nrrdKind4Vector == nin->axis[ai].kind
303 || nrrdKind3Vector == nin->axis[ai].kind)
304 && 2 == szOut[ai]) {
305 nout->axis[ai].kind = nrrdKind2Vector;
306 } else if (nrrdKindRGBAColor == nin->axis[ai].kind
307 && 0 == min[ai]
308 && 2 == max[ai]) {
309 nout->axis[ai].kind = nrrdKindRGBColor;
310 } else if (nrrdKind2DMaskedSymMatrix == nin->axis[ai].kind
311 && 1 == min[ai]
312 && max[ai] == szIn[ai]-1) {
313 nout->axis[ai].kind = nrrdKind2DSymMatrix;
314 } else if (nrrdKind2DMaskedMatrix == nin->axis[ai].kind
315 && 1 == min[ai]
316 && max[ai] == szIn[ai]-1) {
317 nout->axis[ai].kind = nrrdKind2DMatrix;
318 } else if (nrrdKind3DMaskedSymMatrix == nin->axis[ai].kind
319 && 1 == min[ai]
320 && max[ai] == szIn[ai]-1) {
321 nout->axis[ai].kind = nrrdKind3DSymMatrix;
322 } else if (nrrdKind3DMaskedMatrix == nin->axis[ai].kind
323 && 1 == min[ai]
324 && max[ai] == szIn[ai]-1) {
325 nout->axis[ai].kind = nrrdKind3DMatrix;
326 }
327 }
328 }
329 strcpy(buff1, "");
330 for (ai=0; ai<nin->dim; ai++) {
331 sprintf(buff2, "%s[%s,%s]", (ai ? "x" : ""),
332 airSprintSize_t(stmp[0], min[ai]),
333 airSprintSize_t(stmp[1], max[ai]));
334 strcat(buff1, buff2);
335 }
336 if (nrrdContentSet_va(nout, func, nin, "%s", buff1)) {
337 biffAddf(NRRD, "%s:", me);
338 return 1;
339 }
340 if (nrrdBasicInfoCopy(nout, nin,
341 NRRD_BASIC_INFO_DATA_BIT
342 | NRRD_BASIC_INFO_TYPE_BIT
343 | NRRD_BASIC_INFO_BLOCKSIZE_BIT
344 | NRRD_BASIC_INFO_DIMENSION_BIT
345 | NRRD_BASIC_INFO_SPACEORIGIN_BIT
346 | NRRD_BASIC_INFO_CONTENT_BIT
347 | NRRD_BASIC_INFO_COMMENTS_BIT
348 | (nrrdStateKeyValuePairsPropagate
349 ? 0
350 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) {
351 biffAddf(NRRD, "%s:", me);
352 return 1;
353 }
354 /* copy origin, then shift it along the spatial axes */
355 nrrdSpaceVecCopy(nout->spaceOrigin, nin->spaceOrigin);
356 for (ai=0; ai<nin->dim; ai++) {
357 if (AIR_EXISTS(nin->axis[ai].spaceDirection[0])) {
358 nrrdSpaceVecScaleAdd2(nout->spaceOrigin,
359 1.0, nout->spaceOrigin,
360 AIR_CAST(double, min[ai]),
361 nin->axis[ai].spaceDirection);
362 }
363 }
364
365
366 return 0;
367 }
368
369 /* ---- BEGIN non-NrrdIO */
370
371 /*
372 ******** nrrdSliceSelect
373 **
374 ** selects slices along axis "axi" from "nin", according to whether
375 ** line[i] is above or below thresh:
376 **
377 ** line[i] >= thresh: slice i goes into noutAbove
378 ** line[i] < thresh: slice i goes into noutBelow
379 **
380 ** Either noutAbove or noutBelow (but not both) can be passed
381 ** as NULL if you aren't interested in the output. It is a
382 ** biff-able error if the threshold is outside the range of
383 ** errors and a non-Null nrrd was passed for the correspondingly
384 ** empty output.
385 */
386 int
nrrdSliceSelect(Nrrd * noutAbove,Nrrd * noutBelow,const Nrrd * nin,unsigned int saxi,Nrrd * _nline,double thresh)387 nrrdSliceSelect(Nrrd *noutAbove, Nrrd *noutBelow, const Nrrd *nin,
388 unsigned int saxi, Nrrd *_nline, double thresh) {
389 static const char me[]="nrrdSliceSelect";
390 airArray *mop;
391 Nrrd *nline, *nslice;
392 NrrdRange *rng;
393 double *line;
394 size_t II, LL, numAbove, numBelow, stride,
395 sizeAbove[NRRD_DIM_MAX], sizeBelow[NRRD_DIM_MAX];
396 unsigned int aa, bb;
397 int axmap[NRRD_DIM_MAX];
398 char *above, *below, stmp[2][AIR_STRLEN_SMALL];
399
400 if (!( (noutAbove || noutBelow) && nin && _nline )) {
401 biffAddf(NRRD, "%s: got NULL pointer", me);
402 return 1;
403 }
404 if (!AIR_EXISTS(thresh)) {
405 biffAddf(NRRD, "%s: thresh %g doesn't exist", me, thresh);
406 return 1;
407 }
408 if (!(saxi < nin->dim)) {
409 biffAddf(NRRD, "%s: can't select axis %u of a %u-D input nrrd", me,
410 saxi, nin->dim);
411 return 1;
412 }
413 if (nrrdCheck(nin) || nrrdCheck(_nline)) {
414 biffAddf(NRRD, "%s: basic problems with nin or nline", me);
415 return 1;
416 }
417 if (nrrdTypeBlock == nin->type) {
418 /* no good reason for this except that GLK forgets out to
419 set the blocksize in output */
420 biffAddf(NRRD, "%s: sorry, can't handle type %s input", me,
421 airEnumStr(nrrdType, nrrdTypeBlock));
422 return 1;
423 }
424 if (!( nrrdTypeBlock != _nline->type
425 && 1 == _nline->dim)) {
426 biffAddf(NRRD, "%s: nline must be 1-D array of scalars (not %u-D of %s)",
427 me, _nline->dim, airEnumStr(nrrdType, _nline->type));
428 return 1;
429 }
430 if (!( _nline->axis[0].size == nin->axis[saxi].size )) {
431 biffAddf(NRRD, "%s: line length (%s) != axis[%u].size (%s)", me,
432 airSprintSize_t(stmp[0], _nline->axis[0].size), saxi,
433 airSprintSize_t(stmp[1], nin->axis[saxi].size));
434 return 1;
435 }
436 if (1 == nin->dim) {
437 biffAddf(NRRD, "%s: sorry, slice-based implementation requires input "
438 "dimension > 1", me);
439 return 1;
440 }
441
442 mop = airMopNew();
443 rng = nrrdRangeNewSet(_nline, AIR_FALSE);
444 airMopAdd(mop, rng, (airMopper)nrrdRangeNix, airMopAlways);
445 if (rng->hasNonExist) {
446 biffAddf(NRRD, "%s: had non-existent values in line", me);
447 airMopError(mop); return 1;
448 }
449
450 nslice = nrrdNew();
451 airMopAdd(mop, nslice, (airMopper)nrrdNix, airMopAlways);
452 nline = nrrdNew();
453 airMopAdd(mop, nline, (airMopper)nrrdNuke, airMopAlways);
454 if (nrrdConvert(nline, _nline, nrrdTypeDouble)) {
455 biffAddf(NRRD, "%s: problem copying line", me);
456 airMopError(mop); return 1;
457 }
458
459 line = AIR_CAST(double *, nline->data);
460 LL = nline->axis[0].size;
461 numAbove = numBelow = 0;
462 for (II=0; II<LL; II++) {
463 if (line[II] >= thresh) {
464 numAbove++;
465 } else {
466 numBelow++;
467 }
468 }
469 if (noutAbove && !numAbove) {
470 biffAddf(NRRD, "%s: want slices for val >= thresh %g, "
471 "but highest value is %g < %g\n", me, thresh,
472 rng->max, thresh);
473 airMopError(mop); return 1;
474 }
475 if (noutBelow && !numBelow) {
476 biffAddf(NRRD, "%s: want slices for val < thresh %g, "
477 "but lowest value is %g >= %g\n", me, thresh,
478 rng->min, thresh);
479 airMopError(mop); return 1;
480 }
481
482 nslice->dim = nin->dim-1;
483 nslice->type = nin->type;
484 bb = 0;
485 stride = nrrdElementSize(nin);
486 for (aa=0; aa<nin->dim; aa++) {
487 sizeAbove[aa] = sizeBelow[aa] = nin->axis[aa].size;
488 if (aa != saxi) {
489 axmap[aa] = aa;
490 nslice->axis[bb].size = nin->axis[aa].size;
491 if (aa < saxi) {
492 stride *= nin->axis[aa].size;
493 }
494 bb++;
495 } else {
496 axmap[aa] = -1;
497 }
498 }
499 sizeAbove[saxi] = numAbove;
500 sizeBelow[saxi] = numBelow;
501 if ((noutAbove
502 && nrrdMaybeAlloc_nva(noutAbove, nin->type, nin->dim, sizeAbove))
503 ||
504 (noutBelow
505 && nrrdMaybeAlloc_nva(noutBelow, nin->type, nin->dim, sizeBelow))) {
506 biffAddf(NRRD, "%s: trouble allocating output", me);
507 airMopError(mop); return 1;
508 }
509 if (noutAbove) {
510 above = AIR_CAST(char *, noutAbove->data);
511 } else {
512 above = NULL;
513 }
514 if (noutBelow) {
515 below = AIR_CAST(char *, noutBelow->data);
516 } else {
517 below = NULL;
518 }
519
520 /* the skinny */
521 for (II=0; II<LL; II++) {
522 if (line[II] >= thresh) {
523 if (noutAbove) {
524 nslice->data = above;
525 if (nrrdSlice(nslice, nin, saxi, II)) {
526 biffAddf(NRRD, "%s: trouble slicing (above) at %s", me,
527 airSprintSize_t(stmp[0], II));
528 airMopError(mop); return 1;
529 }
530 above += stride;
531 }
532 } else {
533 if (noutBelow) {
534 nslice->data = below;
535 if (nrrdSlice(nslice, nin, saxi, II)) {
536 biffAddf(NRRD, "%s: trouble slicing (below) at %s", me,
537 airSprintSize_t(stmp[0], II));
538 airMopError(mop); return 1;
539 }
540 below += stride;
541 }
542 }
543 }
544
545 if (noutAbove) {
546 nrrdAxisInfoCopy(noutAbove, nin, axmap, NRRD_AXIS_INFO_NONE);
547 if (nrrdBasicInfoCopy(noutAbove, nin,
548 NRRD_BASIC_INFO_DATA_BIT
549 | NRRD_BASIC_INFO_TYPE_BIT
550 | NRRD_BASIC_INFO_DIMENSION_BIT
551 | NRRD_BASIC_INFO_CONTENT_BIT
552 | NRRD_BASIC_INFO_COMMENTS_BIT
553 | (nrrdStateKeyValuePairsPropagate
554 ? 0
555 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) {
556 biffAddf(NRRD, "%s:", me);
557 return 1;
558 }
559 }
560 if (noutBelow) {
561 nrrdAxisInfoCopy(noutBelow, nin, axmap, NRRD_AXIS_INFO_NONE);
562 if (nrrdBasicInfoCopy(noutBelow, nin,
563 NRRD_BASIC_INFO_DATA_BIT
564 | NRRD_BASIC_INFO_TYPE_BIT
565 | NRRD_BASIC_INFO_DIMENSION_BIT
566 | NRRD_BASIC_INFO_CONTENT_BIT
567 | NRRD_BASIC_INFO_COMMENTS_BIT
568 | (nrrdStateKeyValuePairsPropagate
569 ? 0
570 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) {
571 biffAddf(NRRD, "%s:", me);
572 return 1;
573 }
574 }
575
576 airMopOkay(mop);
577 return 0;
578 }
579
580 /*
581 ******** nrrdSample_nva()
582 **
583 ** given coordinates within a nrrd, copies the
584 ** single element into given *val
585 */
586 int
nrrdSample_nva(void * val,const Nrrd * nrrd,const size_t * coord)587 nrrdSample_nva(void *val, const Nrrd *nrrd, const size_t *coord) {
588 static const char me[]="nrrdSample_nva";
589 size_t I, size[NRRD_DIM_MAX], typeSize;
590 unsigned int ai;
591 char stmp[2][AIR_STRLEN_SMALL];
592
593 if (!(nrrd && coord && val)) {
594 biffAddf(NRRD, "%s: got NULL pointer", me);
595 return 1;
596 }
597 /* this shouldn't actually be necessary .. */
598 if (!nrrdElementSize(nrrd)) {
599 biffAddf(NRRD, "%s: nrrd reports zero element size!", me);
600 return 1;
601 }
602
603 typeSize = nrrdElementSize(nrrd);
604 nrrdAxisInfoGet_nva(nrrd, nrrdAxisInfoSize, size);
605 for (ai=0; ai<nrrd->dim; ai++) {
606 if (!( coord[ai] < size[ai] )) {
607 biffAddf(NRRD, "%s: coordinate %s on axis %d out of bounds (0 to %s)",
608 me, airSprintSize_t(stmp[0], coord[ai]),
609 ai, airSprintSize_t(stmp[1], size[ai]-1));
610 return 1;
611 }
612 }
613
614 NRRD_INDEX_GEN(I, coord, size, nrrd->dim);
615
616 memcpy(val, (char*)(nrrd->data) + I*typeSize, typeSize);
617 return 0;
618 }
619
620 /*
621 ******** nrrdSample_va()
622 **
623 ** var-args version of nrrdSample_nva()
624 */
625 int
nrrdSample_va(void * val,const Nrrd * nrrd,...)626 nrrdSample_va(void *val, const Nrrd *nrrd, ...) {
627 static const char me[]="nrrdSample_va";
628 unsigned int ai;
629 size_t coord[NRRD_DIM_MAX];
630 va_list ap;
631
632 if (!(nrrd && val)) {
633 biffAddf(NRRD, "%s: got NULL pointer", me);
634 return 1;
635 }
636
637 va_start(ap, nrrd);
638 for (ai=0; ai<nrrd->dim; ai++) {
639 coord[ai] = va_arg(ap, size_t);
640 }
641 va_end(ap);
642
643 if (nrrdSample_nva(val, nrrd, coord)) {
644 biffAddf(NRRD, "%s:", me);
645 return 1;
646 }
647 return 0;
648 }
649
650 /*
651 ******** nrrdSimpleCrop()
652 **
653 */
654 int
nrrdSimpleCrop(Nrrd * nout,const Nrrd * nin,unsigned int crop)655 nrrdSimpleCrop(Nrrd *nout, const Nrrd *nin, unsigned int crop) {
656 static const char me[]="nrrdSimpleCrop";
657 unsigned int ai;
658 size_t min[NRRD_DIM_MAX], max[NRRD_DIM_MAX];
659
660 if (!(nout && nin)) {
661 biffAddf(NRRD, "%s: got NULL pointer", me);
662 return 1;
663 }
664 for (ai=0; ai<nin->dim; ai++) {
665 min[ai] = crop;
666 max[ai] = nin->axis[ai].size-1 - crop;
667 }
668 if (nrrdCrop(nout, nin, min, max)) {
669 biffAddf(NRRD, "%s:", me);
670 return 1;
671 }
672 return 0;
673 }
674
675 int
nrrdCropAuto(Nrrd * nout,const Nrrd * nin,size_t _min[NRRD_DIM_MAX],size_t _max[NRRD_DIM_MAX],const unsigned int * keep,unsigned int keepNum,int measr,double frac,int offset)676 nrrdCropAuto(Nrrd *nout, const Nrrd *nin,
677 size_t _min[NRRD_DIM_MAX], size_t _max[NRRD_DIM_MAX],
678 const unsigned int *keep, unsigned int keepNum,
679 int measr, double frac, int offset) {
680 static const char me[]="nrrdCropAuto";
681 size_t min[NRRD_DIM_MAX], max[NRRD_DIM_MAX], NN, II;
682 int cropdo[NRRD_DIM_MAX];
683 airArray *mop;
684 Nrrd *nperm, *nline;
685 unsigned int axi;
686 double *line;
687
688 if (!( nout && nin )) {
689 biffAddf(NRRD, "%s: got NULL pointer", me);
690 return 1;
691 }
692 if (keepNum && !keep) {
693 biffAddf(NRRD, "%s: non-zero keepNum %u but NULL keep", me, keepNum);
694 return 1;
695 }
696 if (airEnumValCheck(nrrdMeasure, measr)) {
697 biffAddf(NRRD, "%s: invalid %s measr %d", me,
698 nrrdMeasure->name, measr);
699 return 1;
700 }
701 if (!( AIR_EXISTS(frac) && frac >= 0.0 && frac < 0.5 )) {
702 biffAddf(NRRD, "%s: frac %g not in interval [0.0,0.5)", me, frac);
703 return 1;
704 }
705 for (axi=0; axi<nin->dim; axi++) {
706 cropdo[axi] = AIR_TRUE;
707 }
708 for (axi=0; axi<keepNum; axi++) {
709 if (!( keep[axi] < nin->dim )) {
710 biffAddf(NRRD, "%s: keep[%u] %u out of range [0,%u]", me,
711 axi, keep[axi], nin->dim-1);
712 return 1;
713 }
714 if (!cropdo[keep[axi]]) {
715 biffAddf(NRRD, "%s: keep[%u] %u redundant", me, axi, keep[axi]);
716 return 1;
717 }
718 cropdo[keep[axi]] = AIR_FALSE;
719 }
720 if (keepNum == nin->dim) {
721 /* weird- wanted to keep all axes and crop none; that's easy */
722 if (nrrdCopy(nout, nin)) {
723 biffAddf(NRRD, "%s: trouble copying for trivial case", me);
724 return 1;
725 }
726 return 0;
727 }
728
729 /* else there's work to do */
730 mop = airMopNew();
731 nperm = nrrdNew();
732 airMopAdd(mop, nperm, (airMopper)nrrdNuke, airMopAlways);
733 nline = nrrdNew();
734 airMopAdd(mop, nline, (airMopper)nrrdNuke, airMopAlways);
735 for (axi=0; axi<nin->dim; axi++) {
736 double wsum, part;
737 min[axi] = 0;
738 max[axi] = nin->axis[axi].size-1;
739 if (!cropdo[axi]) {
740 continue;
741 }
742 /* else some analysis is required for this axis */
743 /* NN is product of axes NOT being cropped */
744 NN = nrrdElementNumber(nin)/nin->axis[axi].size;
745 if (nrrdAxesSwap(nperm, nin, axi, nin->dim-1)
746 || nrrdReshape_va(nperm, nperm, 2, NN, nin->axis[axi].size)
747 || nrrdProject(nline, nperm, 0, measr, nrrdTypeDouble)) {
748 biffAddf(NRRD, "%s: trouble forming projection line", me);
749 airMopError(mop); return 1;
750 }
751 /* find sum of array */
752 line = AIR_CAST(double *, nline->data);
753 wsum = part = 0.0;
754 for (II=0; II<nin->axis[axi].size; II++) {
755 wsum += line[II];
756 }
757 /* sum bottom of array until hit fraction */
758 for (II=0; II<nin->axis[axi].size; II++) {
759 part += line[II];
760 if (part/wsum > frac) {
761 min[axi] = II;
762 break;
763 }
764 }
765 if (II == nin->axis[axi].size) {
766 biffAddf(NRRD, "%s: confusion on bottom of axis %u", me, axi);
767 airMopError(mop); return 1;
768 }
769 /* sum top of array until hit fraction */
770 part = 0.0;
771 for (II=nin->axis[axi].size; II>0; II--) {
772 part += line[II-1];
773 if (part/wsum > frac) {
774 max[axi] = II-1;
775 break;
776 }
777 }
778 if (II == 0) {
779 biffAddf(NRRD, "%s: confusion on top of axis %u", me, axi);
780 airMopError(mop); return 1;
781 }
782 /*
783 fprintf(stderr, "!%s: axis %u [%u,%u] --> ", me, axi,
784 AIR_CAST(unsigned int, min[axi]),
785 AIR_CAST(unsigned int, max[axi]));
786 */
787 /* adjust based on offset */
788 if (offset > 0) {
789 if (min[axi] < AIR_CAST(size_t, offset)) {
790 /* desired outwards offset is more than cropping set */
791 min[axi] = 0;
792 } else {
793 min[axi] -= offset;
794 }
795 max[axi] += offset;
796 max[axi] = AIR_MIN(max[axi], nin->axis[axi].size-1);
797 }
798 /*
799 fprintf(stderr, "[%u,%u]\n",
800 AIR_CAST(unsigned int, min[axi]),
801 AIR_CAST(unsigned int, max[axi]));
802 */
803 }
804
805 /* can now do the crop */
806 if (nrrdCrop(nout, nin, min, max)) {
807 biffAddf(NRRD, "%s: trouble doing the crop", me);
808 return 1;
809 }
810 /* save the extents */
811 for (axi=0; axi<nin->dim; axi++) {
812 if (_min) {
813 _min[axi] = min[axi];
814 }
815 if (_max) {
816 _max[axi] = max[axi];
817 }
818 }
819 airMopOkay(mop);
820 return 0;
821 }
822
823 /* ---- END non-NrrdIO */
824