int unrrdu_cksumDoit(const char *me, char *inS, int endian, int printendian, FILE *fout) { Nrrd *nrrd; airArray *mop; unsigned int crc; char stmp[AIR_STRLEN_SMALL], ends[AIR_STRLEN_SMALL]; size_t nn; mop = airMopNew(); airMopAdd(mop, nrrd=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (nrrdLoad(nrrd, inS, NULL)) { biffMovef(me, NRRD, "%s: trouble loading \"%s\"", me, inS); airMopError(mop); return 1; } crc = nrrdCRC32(nrrd, endian); nn = nrrdElementNumber(nrrd)*nrrdElementSize(nrrd); sprintf(ends, "(%s)", airEnumStr(airEndian, endian)); fprintf(fout, "%u%s %s%s%s\n", crc, printendian ? ends : "", airSprintSize_t(stmp, nn), strcmp("-", inS) ? " " : "", strcmp("-", inS) ? inS : ""); airMopOkay(mop); return 0; }
int _miteNtxfAlphaAdjust(miteRender *mrr, miteUser *muu) { static const char me[]="_miteNtxfAlphaAdjust"; int ni, ei, ri, nnum, rnum; Nrrd *ntxf; mite_t *data, alpha, frac; if (_miteNtxfCopy(mrr, muu)) { biffAddf(MITE, "%s: trouble copying/converting transfer functions", me); return 1; } frac = muu->rayStep/muu->refStep; for (ni=0; ni<mrr->ntxfNum; ni++) { ntxf = mrr->ntxf[ni]; if (!strchr(ntxf->axis[0].label, miteRangeChar[miteRangeAlpha])) { continue; } /* else this txf sets opacity */ data = (mite_t *)ntxf->data; rnum = ntxf->axis[0].size; nnum = nrrdElementNumber(ntxf)/rnum; for (ei=0; ei<nnum; ei++) { for (ri=0; ri<rnum; ri++) { if (ntxf->axis[0].label[ri] == miteRangeChar[miteRangeAlpha]) { alpha = data[ri + rnum*ei]; data[ri + rnum*ei] = 1 - pow(AIR_MAX(0, 1-alpha), frac); } } } } return 0; }
int _baneClipAnswer_Percentile(int *countP, Nrrd *hvol, double *clipParm) { static const char me[]="_baneClipAnswer_Percentile"; Nrrd *ncopy; int *hits, clip; size_t num, sum, out, outsofar, hi; if (nrrdCopy(ncopy=nrrdNew(), hvol)) { biffMovef(BANE, NRRD, "%s: couldn't create copy of histovol", me); return 1; } hits = (int *)ncopy->data; num = nrrdElementNumber(ncopy); qsort(hits, num, sizeof(int), nrrdValCompare[nrrdTypeInt]); sum = 0; for (hi=0; hi<num; hi++) { sum += hits[hi]; } out = (size_t)(sum*clipParm[0]/100); outsofar = 0; hi = num-1; do { outsofar += hits[hi--]; } while (outsofar < out); clip = hits[hi]; nrrdNuke(ncopy); *countP = clip; return 0; }
void demoIO(char *filename) { char me[]="demoIO", newname[]="foo.nrrd", *err, *key, *val; int kvn, kvi; Nrrd *nin; /* create a nrrd; at this point this is just an empty container */ nin = nrrdNew(); /* read in the nrrd from file */ if (nrrdLoad(nin, filename, NULL)) { err = biffGetDone(NRRD); fprintf(stderr, "%s: trouble reading \"%s\":\n%s", me, filename, err); free(err); return; } /* say something about the array */ printf("%s: \"%s\" is a %d-dimensional nrrd of type %d (%s)\n", me, filename, nin->dim, nin->type, airEnumStr(nrrdType, nin->type)); printf("%s: the array contains %d elements, each %d bytes in size\n", me, (int)nrrdElementNumber(nin), (int)nrrdElementSize(nin)); /* print out the key/value pairs present */ kvn = nrrdKeyValueSize(nin); if (kvn) { for (kvi=0; kvi<kvn; kvi++) { nrrdKeyValueIndex(nin, &key, &val, kvi); printf("%s: key:value %d = %s:%s\n", me, kvi, key, val); free(key); free(val); key = val = NULL; } } /* modify key/value pairs, and write out the nrrd to a different file */ nrrdKeyValueClear(nin); nrrdKeyValueAdd(nin, "new key", "precious value"); if (nrrdSave(newname, nin, NULL)) { err = biffGetDone(NRRD); fprintf(stderr, "%s: trouble writing \"%s\":\n%s", me, newname, err); free(err); return; } /* blow away both the Nrrd struct *and* the memory at nin->data (nrrdNix() frees the struct but not the data, nrrdEmpty() frees the data but not the struct) */ nrrdNuke(nin); return; }
/* ******** nrrdAlloc_nva() ** ** allocates data array and sets information. If this is a block type ** nrrd, it is necessary to set nrrd->blockSize PRIOR to calling ** this function. ** ** This function will always allocate more memory (via calloc), but ** it will free() nrrd->data if it is non-NULL when passed in. ** ** This function takes the same "don't mess with peripheral information" ** attitude as nrrdWrap(). ** ** Note to Gordon: don't get clever and change ANY axis-specific ** information here. It may be very convenient to set that before ** nrrdAlloc or nrrdMaybeAlloc ** ** Note: This function DOES use biff */ int nrrdAlloc_nva(Nrrd *nrrd, int type, unsigned int dim, const size_t *size) { char me[]="nrrdAlloc_nva", err[BIFF_STRLEN]; size_t num; int esize; if (!(nrrd && size)) { sprintf(err, "%s: got NULL pointer", me); biffAdd(NRRD, err); return 1; } if (airEnumValCheck(nrrdType, type)) { sprintf(err, "%s: type (%d) is invalid", me, type); biffAdd(NRRD, err); return 1; } if (nrrdTypeBlock == type) { if (!(0 < nrrd->blockSize)) { sprintf(err, "%s: given nrrd->blockSize " _AIR_SIZE_T_CNV " invalid", me, nrrd->blockSize); biffAdd(NRRD, err); return 1; } } if (!AIR_IN_CL(1, dim, NRRD_DIM_MAX)) { sprintf(err, "%s: dim (%d) not in valid range [1,%d]", me, dim, NRRD_DIM_MAX); biffAdd(NRRD, err); return 1; } nrrd->data = airFree(nrrd->data); if (nrrdWrap_nva(nrrd, NULL, type, dim, size)) { sprintf(err, "%s:", me); biffAdd(NRRD, err); return 1 ; } num = nrrdElementNumber(nrrd); esize = nrrdElementSize(nrrd); nrrd->data = calloc(num, esize); if (!(nrrd->data)) { sprintf(err, "%s: calloc(" _AIR_SIZE_T_CNV ",%d) failed", me, num, esize); biffAdd(NRRD, err); return 1 ; } return 0; }
/* ******** nrrdDescribe ** ** writes verbose description of nrrd to given file */ void nrrdDescribe(FILE *file, const Nrrd *nrrd) { unsigned int ai; char stmp[AIR_STRLEN_SMALL]; if (file && nrrd) { fprintf(file, "Nrrd at 0x%p:\n", AIR_CVOIDP(nrrd)); fprintf(file, "Data at 0x%p is %s elements of type %s.\n", nrrd->data, airSprintSize_t(stmp, nrrdElementNumber(nrrd)), airEnumStr(nrrdType, nrrd->type)); if (nrrdTypeBlock == nrrd->type) { fprintf(file, "The blocks have size %s\n", airSprintSize_t(stmp, nrrd->blockSize)); } if (airStrlen(nrrd->content)) { fprintf(file, "Content = \"%s\"\n", nrrd->content); } fprintf(file, "%d-dimensional array, with axes:\n", nrrd->dim); for (ai=0; ai<nrrd->dim; ai++) { if (airStrlen(nrrd->axis[ai].label)) { fprintf(file, "%d: (\"%s\") ", ai, nrrd->axis[ai].label); } else { fprintf(file, "%d: ", ai); } fprintf(file, "%s-centered, size=%s, ", airEnumStr(nrrdCenter, nrrd->axis[ai].center), airSprintSize_t(stmp, nrrd->axis[ai].size)); airSinglePrintf(file, NULL, "spacing=%lg, \n", nrrd->axis[ai].spacing); airSinglePrintf(file, NULL, "thickness=%lg, \n", nrrd->axis[ai].thickness); airSinglePrintf(file, NULL, " axis(Min,Max) = (%lg,", nrrd->axis[ai].min); airSinglePrintf(file, NULL, "%lg)\n", nrrd->axis[ai].max); if (airStrlen(nrrd->axis[ai].units)) { fprintf(file, "units=%s, \n", nrrd->axis[ai].units); } } /* airSinglePrintf(file, NULL, "The min, max values are %lg", nrrd->min); airSinglePrintf(file, NULL, ", %lg\n", nrrd->max); */ airSinglePrintf(file, NULL, "The old min, old max values are %lg", nrrd->oldMin); airSinglePrintf(file, NULL, ", %lg\n", nrrd->oldMax); /* fprintf(file, "hasNonExist = %d\n", nrrd->hasNonExist); */ if (nrrd->cmtArr->len) { fprintf(file, "Comments:\n"); for (ai=0; ai<nrrd->cmtArr->len; ai++) { fprintf(file, "%s\n", nrrd->cmt[ai]); } } fprintf(file, "\n"); } }
int tenTripleConvert(Nrrd *nout, int dstType, const Nrrd *nin, int srcType) { static const char me[]="tenTripleConvert"; size_t II, NN; double (*ins)(void *, size_t, double), (*lup)(const void *, size_t); if (!( nout && nin )) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if ( airEnumValCheck(tenTripleType, dstType) || airEnumValCheck(tenTripleType, srcType) ) { biffAddf(TEN, "%s: got invalid %s dst (%d) or src (%d)", me, tenTripleType->name, dstType, srcType); return 1; } if (3 != nin->axis[0].size) { char stmp[AIR_STRLEN_SMALL]; biffAddf(TEN, "%s: need axis[0].size 3, not %s", me, airSprintSize_t(stmp, nin->axis[0].size)); return 1; } if (nrrdTypeBlock == nin->type) { biffAddf(TEN, "%s: input has non-scalar %s type", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (nrrdCopy(nout, nin)) { biffMovef(TEN, NRRD, "%s: couldn't initialize output", me); return 1; } lup = nrrdDLookup[nin->type]; ins = nrrdDInsert[nout->type]; NN = nrrdElementNumber(nin)/3; for (II=0; II<NN; II++) { double src[3], dst[3]; src[0] = lup(nin->data, 0 + 3*II); src[1] = lup(nin->data, 1 + 3*II); src[2] = lup(nin->data, 2 + 3*II); tenTripleConvertSingle_d(dst, dstType, src, srcType); ins(nout->data, 0 + 3*II, dst[0]); ins(nout->data, 1 + 3*II, dst[1]); ins(nout->data, 2 + 3*II, dst[2]); } return 0; }
int _baneClipAnswer_PeakRatio(int *countP, Nrrd *hvol, double *clipParm) { int *hits, maxhits; size_t idx, num; hits = (int *)hvol->data; maxhits = 0; num = nrrdElementNumber(hvol); for (idx=0; idx<num; idx++) { maxhits = AIR_MAX(maxhits, hits[idx]); } *countP = (int)(maxhits*clipParm[0]); return 0; }
unsigned int nrrdCRC32(const Nrrd *nin, int endian) { size_t nn; /* NULL nrrd or data */ if (!nin || !(nin->data) || !(nn = nrrdElementSize(nin)*nrrdElementNumber(nin)) || airEnumValCheck(airEndian, endian)) { return 0; } return airCRC32(AIR_CAST(const unsigned char *, nin->data), nn, nrrdElementSize(nin), endian == airMyEndian() ? AIR_FALSE : AIR_TRUE); }
int _nrrdCopy(Nrrd *nout, const Nrrd *nin, int bitflag) { char me[]="_nrrdCopy", err[BIFF_STRLEN]; size_t size[NRRD_DIM_MAX]; if (!(nin && nout)) { sprintf(err, "%s: got NULL pointer", me); biffAdd(NRRD, err); return 1; } if (nout == nin) { /* its not the case that we have nothing to do- the semantics of copying cannot be achieved if the input and output nrrd are the same; this is an error */ sprintf(err, "%s: nout==nin disallowed", me); biffAdd(NRRD, err); return 1; } if (!nrrdElementSize(nin)) { sprintf(err, "%s: input nrrd reports zero element size!", me); biffAdd(NRRD, err); return 1; } nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size); if (nin->data) { if (nrrdMaybeAlloc_nva(nout, nin->type, nin->dim, size)) { sprintf(err, "%s: couldn't allocate data", me); biffAdd(NRRD, err); return 1; } memcpy(nout->data, nin->data, nrrdElementNumber(nin)*nrrdElementSize(nin)); } else { /* someone is trying to copy structs without data, fine fine fine */ if (nrrdWrap_nva(nout, NULL, nin->type, nin->dim, size)) { sprintf(err, "%s: couldn't allocate data", me); biffAdd(NRRD, err); return 1; } } nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_SIZE_BIT); /* if nin->data non-NULL (second branch above), this will harmlessly unset and set type and dim */ nrrdBasicInfoInit(nout, NRRD_BASIC_INFO_DATA_BIT | bitflag); if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | bitflag)) { sprintf(err, "%s: trouble copying basic info", me); biffAdd(NRRD, err); return 1; } return 0; }
int nrrdArithUnaryOp(Nrrd *nout, int op, const Nrrd *nin) { static const char me[]="nrrdArithUnaryOp"; size_t N, I; int size[NRRD_DIM_MAX]; double (*insert)(void *v, size_t I, double d), (*lookup)(const void *v, size_t I), (*uop)(double), val; if (!(nout && nin)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nrrdTypeBlock == nin->type) { biffAddf(NRRD, "%s: can't operate on type %s", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (airEnumValCheck(nrrdUnaryOp, op)) { biffAddf(NRRD, "%s: unary op %d invalid", me, op); return 1; } if (nout != nin) { if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s:", me); return 1; } } nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size); uop = _nrrdUnaryOp[op]; N = nrrdElementNumber(nin); lookup = nrrdDLookup[nin->type]; insert = nrrdDInsert[nin->type]; for (I=0; I<N; I++) { val = lookup(nin->data, I); insert(nout->data, I, uop(val)); } if (nrrdContentSet_va(nout, airEnumStr(nrrdUnaryOp, op), nin, "")) { biffAddf(NRRD, "%s:", me); return 1; } nrrdBasicInfoInit(nout, NRRD_BASIC_INFO_ALL ^ (NRRD_BASIC_INFO_OLDMIN_BIT | NRRD_BASIC_INFO_OLDMAX_BIT)); return 0; }
int nrrdArithAffine(Nrrd *nout, double minIn, const Nrrd *nin, double maxIn, double minOut, double maxOut, int clamp) { static const char me[]="nrrdArithAffine"; size_t I, N; double (*ins)(void *v, size_t I, double d), (*lup)(const void *v, size_t I), mmin, mmax; if ( !nout || nrrdCheck(nin) ) { biffAddf(NRRD, "%s: got NULL pointer or invalid input", me); return 1; } if (nout != nin) { if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s: couldn't initialize output", me); return 1; } } N = nrrdElementNumber(nin); ins = nrrdDInsert[nout->type]; lup = nrrdDLookup[nin->type]; mmin = AIR_MIN(minOut, maxOut); mmax = AIR_MAX(minOut, maxOut); for (I=0; I<N; I++) { double val; val = lup(nin->data, I); val = AIR_AFFINE(minIn, val, maxIn, minOut, maxOut); if (clamp) { val = AIR_CLAMP(mmin, val, mmax); } ins(nout->data, I, val); } /* HEY: it would be much better if the ordering here was the same as in AIR_AFFINE, but that's not easy with the way the content functions are now set up */ if (nrrdContentSet_va(nout, "affine", nin, "%g,%g,%g,%g", minIn, maxIn, minOut, maxOut)) { biffAddf(NRRD, "%s:", me); } return 0; }
int _baneClipAnswer_TopN(int *countP, Nrrd *hvol, double *clipParm) { static const char me[]="_baneClipAnwer_TopN"; Nrrd *copy; int *hits, tmp; size_t num; if (nrrdCopy(copy=nrrdNew(), hvol)) { biffMovef(BANE, NRRD, "%s: couldn't create copy of histovol", me); return 1; } hits = (int *)copy->data; num = nrrdElementNumber(copy); qsort(hits, num, sizeof(int), nrrdValCompare[nrrdTypeInt]); tmp = AIR_CLAMP(0, (int)clipParm[0], (int)num-1); *countP = hits[num-tmp-1]; nrrdNuke(copy); return 0; }
int limnSplineNrrdEvaluate(Nrrd *nout, limnSpline *spline, Nrrd *nin) { char me[]="limnSplineNrrdEvaluate", err[BIFF_STRLEN]; double tt, *out, (*lup)(const void *, size_t); int odim, infoSize; size_t I, M, size[NRRD_DIM_MAX+1]; if (!(nout && spline && nin)) { sprintf(err, "%s: got NULL pointer", me); biffAdd(LIMN, err); return 1; } if (limnSplineInfoScalar == spline->info) { nrrdAxisInfoGet_va(nin, nrrdAxisInfoSize, size); infoSize = 1; odim = nin->dim; } else { nrrdAxisInfoGet_va(nin, nrrdAxisInfoSize, size+1); infoSize = size[0] = limnSplineInfoSize[spline->info]; odim = 1 + nin->dim; } if (nrrdMaybeAlloc_nva(nout, nrrdTypeDouble, odim, size)) { sprintf(err, "%s: output allocation failed", me); biffMove(LIMN, err, NRRD); return 1; } lup = nrrdDLookup[nin->type]; out = (double*)(nout->data); M = nrrdElementNumber(nin); for (I=0; I<M; I++) { tt = lup(nin->data, I); limnSplineEvaluate(out, spline, tt); out += infoSize; } /* HEY: peripheral info copying? */ return 0; }
/* this strongly assumes that nrrdFitsInFormat() was true */ int _nrrdFormatVTK_write(FILE *file, const Nrrd *_nrrd, NrrdIoState *nio) { static const char me[]="_nrrdFormatVTK_write"; int i, sx, sy, sz, sax; double xs, ys, zs, xm, ym, zm; char type[AIR_STRLEN_MED], name[AIR_STRLEN_SMALL]; Nrrd *nrrd; airArray *mop; /* HEY: should this copy be done more conservatively */ mop = airMopNew(); airMopAdd(mop, nrrd=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); if (nrrdCopy(nrrd, _nrrd)) { biffAddf(NRRD, "%s: couldn't make private copy", me); airMopError(mop); return 1; } if (!( 3 == nrrd->dim || (4 == nrrd->dim && (3 == nrrd->axis[0].size || 9 == nrrd->axis[0].size)) )) { biffAddf(NRRD, "%s: doesn't seem to be scalar, vector, or matrix", me); airMopError(mop); return 1; } sax = nrrd->dim - 3; xs = nrrd->axis[sax+0].spacing; ys = nrrd->axis[sax+1].spacing; zs = nrrd->axis[sax+2].spacing; if (!( AIR_EXISTS(xs) && AIR_EXISTS(ys) && AIR_EXISTS(zs) )) { xs = ys = zs = 1.0; } xm = nrrd->axis[sax+0].min; ym = nrrd->axis[sax+1].min; zm = nrrd->axis[sax+2].min; if (!( AIR_EXISTS(xm) && AIR_EXISTS(ym) && AIR_EXISTS(zm) )) { xm = ym = zm = 0.0; } sx = AIR_CAST(int, nrrd->axis[sax+0].size); sy = AIR_CAST(int, nrrd->axis[sax+1].size); sz = AIR_CAST(int, nrrd->axis[sax+2].size); switch(nrrd->type) { case nrrdTypeUChar: strcpy(type, "unsigned_char"); break; case nrrdTypeChar: strcpy(type, "char"); break; case nrrdTypeUShort: strcpy(type, "unsigned_short"); break; case nrrdTypeShort: strcpy(type, "short"); break; case nrrdTypeUInt: strcpy(type, "unsigned_int"); break; case nrrdTypeInt: strcpy(type, "int"); break; case nrrdTypeFloat: strcpy(type, "float"); break; case nrrdTypeDouble: strcpy(type, "double"); break; default: biffAddf(NRRD, "%s: can't put %s-type nrrd into VTK", me, airEnumStr(nrrdType, nrrd->type)); airMopError(mop); return 1; } fprintf(file, "%s\n", MAGIC3); /* there is a file-format-imposed limit on the length of the "content" */ if (nrrd->content) { /* when the "250" below was previously "255", vtk didn't deal */ for (i=0; i<=250 && nrrd->content[i]; i++) { fputc(nrrd->content[i], file); } fputc('\n', file); } else { fprintf(file, NRRD_UNKNOWN "\n"); } if (nrrdEncodingRaw == nio->encoding) { fprintf(file, "BINARY\n"); } else { fprintf(file, "ASCII\n"); } fprintf(file, "DATASET STRUCTURED_POINTS\n"); fprintf(file, "DIMENSIONS %d %d %d\n", sx, sy, sz); fprintf(file, "ORIGIN %g %g %g\n", xm, ym, zm); fprintf(file, "SPACING %g %g %g\n", xs, ys, zs); fprintf(file, "POINT_DATA %d\n", sx*sy*sz); airSrandMT(AIR_CAST(unsigned int, airTime())); sprintf(name, "nrrd%05d", airRandInt(100000)); if (3 == nrrd->dim) { fprintf(file, "SCALARS %s %s\n", name, type); fprintf(file, "LOOKUP_TABLE default\n"); } else { /* 4 == nrrd->dim */ if (3 == nrrd->axis[0].size) { fprintf(file, "VECTORS %s %s\n", name, type); } else { fprintf(file, "TENSORS %s %s\n", name, type); } } if (1 < nrrdElementSize(nrrd) && nio->encoding->endianMatters && airMyEndian() != airEndianBig) { /* encoding exposes endianness, and we're not big, as req.d by VTK */ nrrdSwapEndian(nrrd); } if (nio->encoding->write(file, nrrd->data, nrrdElementNumber(nrrd), nrrd, nio)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; }
int main(int argc, const char **argv) { const char *me; char *err; hestOpt *hopt=NULL; hestParm *hparm; airArray *mop; /* variables learned via hest */ Nrrd *nin; float camfr[3], camat[3], camup[3], camnc, camfc, camFOV; int camortho, hitandquit; unsigned int camsize[2]; double isovalue, sliso, isomin, isomax; /* boilerplate hest code */ me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hparm->respFileEnable = AIR_TRUE; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); /* setting up the command-line options */ hparm->respFileEnable = AIR_TRUE; hestOptAdd(&hopt, "i", "volume", airTypeOther, 1, 1, &nin, NULL, "input volume to isosurface", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "v", "isovalue", airTypeDouble, 1, 1, &isovalue, "nan", "isovalue at which to run Marching Cubes"); hestOptAdd(&hopt, "fr", "x y z", airTypeFloat, 3, 3, camfr, "3 4 5", "look-from point"); hestOptAdd(&hopt, "at", "x y z", airTypeFloat, 3, 3, camat, "0 0 0", "look-at point"); hestOptAdd(&hopt, "up", "x y z", airTypeFloat, 3, 3, camup, "0 0 1", "up direction"); hestOptAdd(&hopt, "nc", "dist", airTypeFloat, 1, 1, &(camnc), "-2", "at-relative near clipping distance"); hestOptAdd(&hopt, "fc", "dist", airTypeFloat, 1, 1, &(camfc), "2", "at-relative far clipping distance"); hestOptAdd(&hopt, "fov", "angle", airTypeFloat, 1, 1, &(camFOV), "20", "vertical field-of-view, in degrees. Full vertical " "extent of image plane subtends this angle."); hestOptAdd(&hopt, "sz", "s0 s1", airTypeUInt, 2, 2, &(camsize), "640 480", "# samples (horz vert) of image plane. "); hestOptAdd(&hopt, "ortho", NULL, airTypeInt, 0, 0, &(camortho), NULL, "use orthographic instead of (the default) " "perspective projection "); hestOptAdd(&hopt, "haq", NULL, airTypeBool, 0, 0, &(hitandquit), NULL, "save a screenshot rather than display the viewer"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, "demo program", AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); /* learn value range, and set initial isovalue if needed */ NrrdRange *range = nrrdRangeNewSet(nin, AIR_FALSE); airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); isomin = range->min; isomax = range->max; if (!AIR_EXISTS(isovalue)) { isovalue = (isomin + isomax)/2; } /* first, make sure we can isosurface ok */ limnPolyData *lpld = limnPolyDataNew(); seekContext *sctx = seekContextNew(); airMopAdd(mop, sctx, (airMopper)seekContextNix, airMopAlways); sctx->pldArrIncr = nrrdElementNumber(nin); seekVerboseSet(sctx, 0); seekNormalsFindSet(sctx, AIR_TRUE); if (seekDataSet(sctx, nin, NULL, 0) || seekTypeSet(sctx, seekTypeIsocontour) || seekIsovalueSet(sctx, isovalue) || seekUpdate(sctx) || seekExtract(sctx, lpld)) { airMopAdd(mop, err=biffGetDone(SEEK), airFree, airMopAlways); fprintf(stderr, "trouble with isosurfacing:\n%s", err); airMopError(mop); return 1; } if (!lpld->xyzwNum) { fprintf(stderr, "%s: warning: No isocontour generated at isovalue %g\n", me, isovalue); } /* then create empty scene */ Hale::init(); Hale::Scene scene; /* then create viewer (in order to create the OpenGL context) */ Hale::Viewer viewer(camsize[0], camsize[1], "Iso", &scene); viewer.lightDir(glm::vec3(-1.0f, 1.0f, 3.0f)); viewer.camera.init(glm::vec3(camfr[0], camfr[1], camfr[2]), glm::vec3(camat[0], camat[1], camat[2]), glm::vec3(camup[0], camup[1], camup[2]), camFOV, (float)camsize[0]/camsize[1], camnc, camfc, camortho); viewer.refreshCB((Hale::ViewerRefresher)render); viewer.refreshData(&viewer); sliso = isovalue; viewer.slider(&sliso, isomin, isomax); viewer.current(); /* then create geometry, and add it to scene */ Hale::Polydata hply(lpld, true, // hply now owns lpld Hale::ProgramLib(Hale::preprogramAmbDiff2SideSolid)); scene.add(&hply); scene.drawInit(); render(&viewer); if (hitandquit) { seekIsovalueSet(sctx, isovalue); seekUpdate(sctx); seekExtract(sctx, lpld); hply.rebuffer(); render(&viewer); glfwWaitEvents(); render(&viewer); viewer.snap(); Hale::done(); airMopOkay(mop); return 0; } while(!Hale::finishing){ glfwWaitEvents(); if (viewer.sliding() && sliso != isovalue) { isovalue = sliso; printf("%s: isosurfacing at %g\n", me, isovalue); seekIsovalueSet(sctx, isovalue); seekUpdate(sctx); seekExtract(sctx, lpld); hply.rebuffer(); } render(&viewer); } /* clean exit; all okay */ Hale::done(); airMopOkay(mop); return 0; }
/* ** _nrrdSprintFieldInfo ** ** this prints "<prefix><field>: <info>" into *strP (after allocating it for ** big enough, usually with a stupidly big margin of error), in a form ** suitable to be written to NRRD or other image headers. This will always ** print something (for valid inputs), even stupid <info>s like ** "(unknown endian)". It is up to the caller to decide which fields ** are worth writing, via _nrrdFieldInteresting(). ** ** NOTE: some of these fields make sense in non-NRRD files (e.g. all ** the per-axis information), but many only make sense in NRRD files. ** This is just one example of NRRD-format-specific stuff that is not ** in formatNRRD.c */ void _nrrdSprintFieldInfo(char **strP, char *prefix, const Nrrd *nrrd, NrrdIoState *nio, int field) { char me[]="_nrrdSprintFieldInfo", buff[AIR_STRLEN_MED], *fnb; double colvec[NRRD_SPACE_DIM_MAX]; const char *fs; unsigned int ii, dd, uintStrlen = 11, size_tStrlen = 33, doubleStrlen = 513; int fslen, fdlen, endi, maxl; if (!( strP && prefix && nrrd && AIR_IN_CL(1, nrrd->dim, NRRD_DIM_MAX) && AIR_IN_OP(nrrdField_unknown, field, nrrdField_last) )) { return; } if (!_nrrdFieldInteresting(nrrd, nio, field)) { *strP = airStrdup(""); } fs = airEnumStr(nrrdField, field); fslen = strlen(prefix) + strlen(fs) + strlen(": ") + 1; switch (field) { case nrrdField_comment: case nrrdField_keyvalue: fprintf(stderr, "%s: CONFUSION: why are you calling me on \"%s\"?\n", me, airEnumStr(nrrdField, nrrdField_comment)); *strP = airStrdup(""); break; case nrrdField_content: airOneLinify(nrrd->content); *strP = (char *)calloc(fslen + strlen(nrrd->content), sizeof(char)); sprintf(*strP, "%s%s: %s", prefix, fs, nrrd->content); break; case nrrdField_number: *strP = (char *)calloc(fslen + size_tStrlen, sizeof(char)); sprintf(*strP, "%s%s: " _AIR_SIZE_T_CNV, prefix, fs, nrrdElementNumber(nrrd)); break; case nrrdField_type: *strP = (char *)calloc(fslen + strlen(airEnumStr(nrrdType, nrrd->type)), sizeof(char)); sprintf(*strP, "%s%s: %s", prefix, fs, airEnumStr(nrrdType, nrrd->type)); break; case nrrdField_block_size: *strP = (char *)calloc(fslen + size_tStrlen, sizeof(char)); sprintf(*strP, "%s%s: " _AIR_SIZE_T_CNV, prefix, fs, nrrd->blockSize); break; case nrrdField_dimension: *strP = (char *)calloc(fslen + uintStrlen, sizeof(char)); sprintf(*strP, "%s%s: %d", prefix, fs, nrrd->dim); break; case nrrdField_space: *strP = (char *)calloc(fslen + strlen(airEnumStr(nrrdSpace, nrrd->space)), sizeof(char)); sprintf(*strP, "%s%s: %s", prefix, fs, airEnumStr(nrrdSpace, nrrd->space)); break; case nrrdField_space_dimension: *strP = (char *)calloc(fslen + uintStrlen, sizeof(char)); sprintf(*strP, "%s%s: %d", prefix, fs, nrrd->spaceDim); break; /* ---- begin per-axis fields ---- */ case nrrdField_sizes: *strP = (char *)calloc(fslen + nrrd->dim*(size_tStrlen + 1), sizeof(char)); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { sprintf(buff, " " _AIR_SIZE_T_CNV, nrrd->axis[ii].size); strcat(*strP, buff); } break; case nrrdField_spacings: *strP = (char *)calloc(fslen + nrrd->dim*(doubleStrlen + 1), sizeof(char)); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { airSinglePrintf(NULL, buff, " %g", nrrd->axis[ii].spacing); strcat(*strP, buff); } break; case nrrdField_thicknesses: *strP = (char *)calloc(fslen + nrrd->dim*(doubleStrlen + 1), sizeof(char)); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { airSinglePrintf(NULL, buff, " %g", nrrd->axis[ii].thickness); strcat(*strP, buff); } break; case nrrdField_axis_mins: *strP = (char *)calloc(fslen + nrrd->dim*(doubleStrlen + 1), sizeof(char)); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { airSinglePrintf(NULL, buff, " %g", nrrd->axis[ii].min); strcat(*strP, buff); } break; case nrrdField_axis_maxs: *strP = (char *)calloc(fslen + nrrd->dim*(doubleStrlen + 1), sizeof(char)); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { airSinglePrintf(NULL, buff, " %g", nrrd->axis[ii].max); strcat(*strP, buff); } break; case nrrdField_space_directions: *strP = (char *)calloc(fslen + nrrd->dim*nrrd->spaceDim*(doubleStrlen + strlen("(,) ")), sizeof(char)); sprintf(*strP, "%s%s: ", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { _nrrdStrcatSpaceVector(*strP, nrrd->spaceDim, nrrd->axis[ii].spaceDirection); if (ii < nrrd->dim-1) { strcat(*strP, " "); } } break; case nrrdField_centers: fdlen = 0; for (ii=0; ii<nrrd->dim; ii++) { fdlen += 1 + airStrlen(nrrd->axis[ii].center ? airEnumStr(nrrdCenter, nrrd->axis[ii].center) : NRRD_UNKNOWN); } *strP = (char *)calloc(fslen + fdlen, sizeof(char)); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { sprintf(buff, " %s", (nrrd->axis[ii].center ? airEnumStr(nrrdCenter, nrrd->axis[ii].center) : NRRD_UNKNOWN)); strcat(*strP, buff); } break; case nrrdField_kinds: fdlen = 0; for (ii=0; ii<nrrd->dim; ii++) { fdlen += 1 + airStrlen(nrrd->axis[ii].kind ? airEnumStr(nrrdKind, nrrd->axis[ii].kind) : NRRD_UNKNOWN); } *strP = (char *)calloc(fslen + fdlen, sizeof(char)); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { sprintf(buff, " %s", (nrrd->axis[ii].kind ? airEnumStr(nrrdKind, nrrd->axis[ii].kind) : NRRD_UNKNOWN)); strcat(*strP, buff); } break; case nrrdField_labels: fdlen = 0; for (ii=0; ii<nrrd->dim; ii++) { fdlen += airStrlen(nrrd->axis[ii].label) + 4; } *strP = (char *)calloc(fslen + fdlen, sizeof(char)); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { strcat(*strP, " \""); if (airStrlen(nrrd->axis[ii].label)) { strcat(*strP, nrrd->axis[ii].label); } strcat(*strP, "\""); } break; case nrrdField_units: fdlen = 0; for (ii=0; ii<nrrd->dim; ii++) { fdlen += airStrlen(nrrd->axis[ii].units) + 4; } *strP = (char *)calloc(fslen + fdlen, sizeof(char)); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { strcat(*strP, " \""); if (airStrlen(nrrd->axis[ii].units)) { strcat(*strP, nrrd->axis[ii].units); } strcat(*strP, "\""); } break; /* ---- end per-axis fields ---- */ case nrrdField_min: case nrrdField_max: /* we're basically a no-op, now that these fields became meaningless */ *strP = (char *)calloc(fslen + doubleStrlen, sizeof(char)); sprintf(*strP, "%s%s: 0.0", prefix, fs); strcat(*strP, buff); break; case nrrdField_old_min: *strP = (char *)calloc(fslen + doubleStrlen, sizeof(char)); sprintf(*strP, "%s%s: ", prefix, fs); airSinglePrintf(NULL, buff, "%g", nrrd->oldMin); strcat(*strP, buff); break; case nrrdField_old_max: *strP = (char *)calloc(fslen + doubleStrlen, sizeof(char)); sprintf(*strP, "%s%s: ", prefix, fs); airSinglePrintf(NULL, buff, "%g", nrrd->oldMax); strcat(*strP, buff); break; case nrrdField_endian: if (airEndianUnknown != nio->endian) { /* we know a specific endianness because either it was recorded as part of "unu make -h", or it was set (and data was possibly altered) as part of "unu save" */ endi = nio->endian; } else { /* we record our current architecture's endian because we're going to writing out data */ endi = AIR_ENDIAN; } *strP = (char *)calloc(fslen + strlen(airEnumStr(airEndian, endi)), sizeof(char)); sprintf(*strP, "%s%s: %s", prefix, fs, airEnumStr(airEndian, endi)); break; case nrrdField_encoding: *strP = (char *)calloc(fslen + strlen(nio->encoding->name), sizeof(char)); sprintf(*strP, "%s%s: %s", prefix, fs, nio->encoding->name); break; case nrrdField_line_skip: *strP = (char *)calloc(fslen + uintStrlen, sizeof(char)); sprintf(*strP, "%s%s: %d", prefix, fs, nio->lineSkip); break; case nrrdField_byte_skip: *strP = (char *)calloc(fslen + uintStrlen, sizeof(char)); sprintf(*strP, "%s%s: %d", prefix, fs, nio->byteSkip); break; case nrrdField_sample_units: airOneLinify(nrrd->sampleUnits); *strP = (char *)calloc(fslen + strlen(nrrd->sampleUnits), sizeof(char)); sprintf(*strP, "%s%s: \"%s\"", prefix, fs, nrrd->sampleUnits); break; case nrrdField_space_units: fdlen = 0; for (ii=0; ii<nrrd->spaceDim; ii++) { fdlen += airStrlen(nrrd->spaceUnits[ii]) + 4; } *strP = (char *)calloc(fslen + fdlen, sizeof(char)); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->spaceDim; ii++) { strcat(*strP, " \""); if (airStrlen(nrrd->spaceUnits[ii])) { strcat(*strP, nrrd->spaceUnits[ii]); } strcat(*strP, "\""); } break; case nrrdField_space_origin: *strP = (char *)calloc(fslen + nrrd->spaceDim*(doubleStrlen + strlen("(,) ")), sizeof(char)); sprintf(*strP, "%s%s: ", prefix, fs); _nrrdStrcatSpaceVector(*strP, nrrd->spaceDim, nrrd->spaceOrigin); break; case nrrdField_measurement_frame: *strP = (char *)calloc(fslen + (nrrd->spaceDim* nrrd->spaceDim*(doubleStrlen + strlen("(,) "))), sizeof(char)); sprintf(*strP, "%s%s: ", prefix, fs); for (dd=0; dd<nrrd->spaceDim; dd++) { for (ii=0; ii<nrrd->spaceDim; ii++) { colvec[ii] = nrrd->measurementFrame[dd][ii]; } _nrrdStrcatSpaceVector(*strP, nrrd->spaceDim, colvec); if (dd < nrrd->spaceDim-1) { strcat(*strP, " "); } } break; case nrrdField_data_file: /* NOTE: this comes last (nrrdField_data_file is the highest-valued member of the nrrdField* enum) because the "LIST" form of the data file specification requires that the following lines be the filenames */ /* error checking elsewhere: assumes there is data file info */ if (nio->dataFNFormat) { *strP = (char *)calloc(fslen + strlen(nio->dataFNFormat) + 4*uintStrlen, sizeof(char)); if (nio->dataFileDim == nrrd->dim-1) { sprintf(*strP, "%s%s: %s %d %d %d", prefix, fs, nio->dataFNFormat, nio->dataFNMin, nio->dataFNMax, nio->dataFNStep); } else { sprintf(*strP, "%s%s: %s %d %d %d %d", prefix, fs, nio->dataFNFormat, nio->dataFNMin, nio->dataFNMax, nio->dataFNStep, nio->dataFileDim); } } else if (nio->dataFNArr->len > 1) { maxl = 0; for (ii=0; ii<nio->dataFNArr->len; ii++) { maxl = AIR_MAX(maxl, (int)strlen(nio->dataFN[ii])); } *strP = (char *)calloc(fslen + strlen(NRRD_LIST_FLAG) + uintStrlen + nio->dataFNArr->len * (maxl + 1), sizeof(char)); fnb = (char *)calloc(fslen + strlen(NRRD_LIST_FLAG) + uintStrlen + maxl + 1, sizeof(char)); if (nio->dataFileDim == nrrd->dim-1) { sprintf(*strP, "%s%s: LIST\n", prefix, fs); } else { sprintf(*strP, "%s%s: LIST %d\n", prefix, fs, nio->dataFileDim); } for (ii=0; ii<nio->dataFNArr->len; ii++) { sprintf(fnb, "%s%s", nio->dataFN[ii], ii<nio->dataFNArr->len-1 ? "\n" : ""); strcat(*strP, fnb); } free(fnb); } else { /* there is some ambiguity between a "LIST" of length one, and a single explicit data filename, but that's harmless */ *strP = (char *)calloc(fslen + strlen("./") + strlen(nio->dataFN[0]) + 1, sizeof(char)); sprintf(*strP, "%s%s: %s%s", prefix, fs, /* this is a favor to older readers that can deal with this NRRD file because its being saved in a NRRD0003 (or below) version, so we don't want to confuse them by not having the old explicit header-relative flag */ (_nrrdFormatNRRD_whichVersion(nrrd, nio) < 4 ? "./" : ""), nio->dataFN[0]); } break; default: fprintf(stderr, "%s: CONFUSION: field %d unrecognized\n", me, field); break; } return; }
/* ******** nrrdSpatialResample() ** ** general-purpose array-resampler: resamples a nrrd of any type ** (except block) and any dimension along any or all of its axes, with ** any combination of up- or down-sampling along the axes, with any ** kernel (specified by callback), with potentially a different kernel ** for each axis. Whether or not to resample along axis d is ** controlled by the non-NULL-ity of info->kernel[ai]. Where to sample ** on the axis is controlled by info->min[ai] and info->max[ai]; these ** specify a range of "positions" aka "world space" positions, as ** determined by the per-axis min and max of the input nrrd, which must ** be set for every resampled axis. ** ** we cyclically permute those axes being resampled, and never touch ** the position (in axis ordering) of axes along which we are not ** resampling. This strategy is certainly not the most intelligent ** one possible, but it does mean that the axis along which we're ** currently resampling-- the one along which we'll have to look at ** multiple adjecent samples-- is that resampling axis which is ** currently most contiguous in memory. It may make sense to precede ** the resampling with an axis permutation which bubbles all the ** resampled axes to the front (most contiguous) end of the axis list, ** and then puts them back in place afterwards, depending on the cost ** of such axis permutation overhead. */ int nrrdSpatialResample(Nrrd *nout, const Nrrd *nin, const NrrdResampleInfo *info) { char me[]="nrrdSpatialResample", func[]="resample", err[BIFF_STRLEN]; nrrdResample_t *array[NRRD_DIM_MAX], /* intermediate copies of the input data undergoing resampling; we don't need a full- fledged nrrd for these. Only about two of these arrays will be allocated at a time; intermediate results will be free()d when not needed */ *_inVec, /* current input vector being resampled; not necessarily contiguous in memory (if strideIn != 1) */ *inVec, /* buffer for input vector; contiguous */ *_outVec; /* output vector in context of volume; never contiguous */ double tmpF; double ratio, /* factor by which or up or downsampled */ ratios[NRRD_DIM_MAX]; /* record of "ratio" for all resampled axes, used to compute new spacing in output */ Nrrd *floatNin; /* if the input nrrd type is not nrrdResample_t, then we convert it and keep it here */ unsigned int ai, pi, /* current pass */ topLax, permute[NRRD_DIM_MAX], /* how to permute axes of last pass to get axes for current pass */ ax[NRRD_DIM_MAX+1][NRRD_DIM_MAX], /* axis ordering on each pass */ passes; /* # of passes needed to resample all axes */ int i, s, e, topRax, /* the lowest index of an axis which is resampled. If all axes are being resampled, then this is 0. If for some reason the "x" axis (fastest stride) is not being resampled, but "y" is, then topRax is 1 */ botRax, /* index of highest axis being resampled */ typeIn, typeOut; /* types of input and output of resampling */ size_t sz[NRRD_DIM_MAX+1][NRRD_DIM_MAX]; /* how many samples along each axis, changing on each pass */ /* all these variables have to do with the spacing of elements in memory for the current pass of resampling, and they (except strideIn) are re-set at the beginning of each pass */ nrrdResample_t *weight; /* sample weights */ unsigned int ci[NRRD_DIM_MAX+1], co[NRRD_DIM_MAX+1]; int sizeIn, sizeOut, /* lengths of input and output vectors */ dotLen, /* # input samples to dot with weights to get one output sample */ doRound, /* actually do rounding on output: we DO NOT round when info->round but the output type is not integral */ *index; /* dotLen*sizeOut 2D array of input indices */ size_t I, /* swiss-army int */ strideIn, /* the stride between samples in the input "scanline" being resampled */ strideOut, /* stride between samples in output "scanline" from resampling */ L, LI, LO, numLines, /* top secret */ numOut; /* # of _samples_, total, in output volume; this is for allocating the output */ airArray *mop; /* for cleaning up */ if (!(nout && nin && info)) { sprintf(err, "%s: got NULL pointer", me); biffAdd(NRRD, err); return 1; } if (nrrdBoundaryUnknown == info->boundary) { sprintf(err, "%s: need to specify a boundary behavior", me); biffAdd(NRRD, err); return 1; } typeIn = nin->type; typeOut = nrrdTypeDefault == info->type ? typeIn : info->type; if (_nrrdResampleCheckInfo(nin, info)) { sprintf(err, "%s: problem with arguments", me); biffAdd(NRRD, err); return 1; } _nrrdResampleComputePermute(permute, ax, sz, &topRax, &botRax, &passes, nin, info); topLax = topRax ? 0 : 1; /* not sure where else to put this: (want to put it before 0 == passes branch) We have to assume some centering when doing resampling, and it would be stupid to not record it in the outgoing nrrd, since the value of nrrdDefaultCenter could always change. */ for (ai=0; ai<nin->dim; ai++) { if (info->kernel[ai]) { nout->axis[ai].center = _nrrdCenter(nin->axis[ai].center); } } if (0 == passes) { /* actually, no resampling was desired. Copy input to output, but with the clamping that we normally do at the end of resampling */ nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, sz[0]); if (nrrdMaybeAlloc_nva(nout, typeOut, nin->dim, sz[0])) { sprintf(err, "%s: couldn't allocate output", me); biffAdd(NRRD, err); return 1; } numOut = nrrdElementNumber(nout); for (I=0; I<numOut; I++) { tmpF = nrrdDLookup[nin->type](nin->data, I); tmpF = nrrdDClamp[typeOut](tmpF); nrrdDInsert[typeOut](nout->data, I, tmpF); } nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_NONE); /* HEY: need to create textual representation of resampling parameters */ if (nrrdContentSet_va(nout, func, nin, "")) { sprintf(err, "%s:", me); biffAdd(NRRD, err); return 1; } if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { sprintf(err, "%s:", me); biffAdd(NRRD, err); return 1; } return 0; } mop = airMopNew(); /* convert input nrrd to nrrdResample_t if necessary */ if (nrrdResample_nrrdType != typeIn) { if (nrrdConvert(floatNin = nrrdNew(), nin, nrrdResample_nrrdType)) { sprintf(err, "%s: couldn't create float copy of input", me); biffAdd(NRRD, err); airMopError(mop); return 1; } array[0] = (nrrdResample_t*)floatNin->data; airMopAdd(mop, floatNin, (airMopper)nrrdNuke, airMopAlways); } else { floatNin = NULL; array[0] = (nrrdResample_t*)nin->data; } /* compute strideIn; this is actually the same for every pass because (strictly speaking) in every pass we are resampling the same axis, and axes with lower indices are constant length */ strideIn = 1; for (ai=0; ai<(unsigned int)topRax; ai++) { /* HEY scrutinize casts */ strideIn *= nin->axis[ai].size; } /* printf("%s: strideIn = " _AIR_SIZE_T_CNV "\n", me, strideIn); */ /* go! */ for (pi=0; pi<passes; pi++) { /* printf("%s: --- pass %d --- \n", me, pi); */ numLines = strideOut = 1; for (ai=0; ai<nin->dim; ai++) { if (ai < (unsigned int)botRax) { /* HEY scrutinize cast */ strideOut *= sz[pi+1][ai]; } if (ai != (unsigned int)topRax) { /* HEY scrutinize cast */ numLines *= sz[pi][ai]; } } sizeIn = sz[pi][topRax]; sizeOut = sz[pi+1][botRax]; numOut = numLines*sizeOut; /* for the rest of the loop body, d is the original "dimension" for the axis being resampled */ ai = ax[pi][topRax]; /* printf("%s(%d): numOut = " _AIR_SIZE_T_CNV "\n", me, pi, numOut); printf("%s(%d): numLines = " _AIR_SIZE_T_CNV "\n", me, pi, numLines); printf("%s(%d): stride: In=%d, Out=%d\n", me, pi, (int)strideIn, (int)strideOut); printf("%s(%d): sizeIn = %d\n", me, pi, sizeIn); printf("%s(%d): sizeOut = %d\n", me, pi, sizeOut); */ /* we can free the input to the previous pass (if its not the given data) */ if (pi > 0) { if (pi == 1) { if (array[0] != nin->data) { airMopSub(mop, floatNin, (airMopper)nrrdNuke); floatNin = nrrdNuke(floatNin); array[0] = NULL; /* printf("%s: pi %d: freeing array[0]\n", me, pi); */ } } else { airMopSub(mop, array[pi-1], airFree); array[pi-1] = (nrrdResample_t*)airFree(array[pi-1]); /* printf("%s: pi %d: freeing array[%d]\n", me, pi, pi-1); */ } } /* allocate output volume */ array[pi+1] = (nrrdResample_t*)calloc(numOut, sizeof(nrrdResample_t)); if (!array[pi+1]) { sprintf(err, "%s: couldn't create array of " _AIR_SIZE_T_CNV " nrrdResample_t's for output of pass %d", me, numOut, pi); biffAdd(NRRD, err); airMopError(mop); return 1; } airMopAdd(mop, array[pi+1], airFree, airMopAlways); /* printf("%s: allocated array[%d]\n", me, pi+1); */ /* allocate contiguous input scanline buffer, we alloc one more than needed to provide a place for the pad value. That is, in fact, the over-riding reason to copy a scanline to a local array: so that there is a simple consistent (non-branchy) way to incorporate the pad values */ inVec = (nrrdResample_t *)calloc(sizeIn+1, sizeof(nrrdResample_t)); airMopAdd(mop, inVec, airFree, airMopAlways); inVec[sizeIn] = AIR_CAST(nrrdResample_t, info->padValue); dotLen = _nrrdResampleMakeWeightIndex(&weight, &index, &ratio, nin, info, ai); if (!dotLen) { sprintf(err, "%s: trouble creating weight and index vector arrays", me); biffAdd(NRRD, err); airMopError(mop); return 1; } ratios[ai] = ratio; airMopAdd(mop, weight, airFree, airMopAlways); airMopAdd(mop, index, airFree, airMopAlways); /* the skinny: resample all the scanlines */ _inVec = array[pi]; _outVec = array[pi+1]; memset(ci, 0, (NRRD_DIM_MAX+1)*sizeof(int)); memset(co, 0, (NRRD_DIM_MAX+1)*sizeof(int)); for (L=0; L<numLines; L++) { /* calculate the index to get to input and output scanlines, according the coordinates of the start of the scanline */ NRRD_INDEX_GEN(LI, ci, sz[pi], nin->dim); NRRD_INDEX_GEN(LO, co, sz[pi+1], nin->dim); _inVec = array[pi] + LI; _outVec = array[pi+1] + LO; /* read input scanline into contiguous array */ for (i=0; i<sizeIn; i++) { inVec[i] = _inVec[i*strideIn]; } /* do the weighting */ for (i=0; i<sizeOut; i++) { tmpF = 0.0; /* fprintf(stderr, "%s: i = %d (tmpF=0)\n", me, (int)i); */ for (s=0; s<dotLen; s++) { tmpF += inVec[index[s + dotLen*i]]*weight[s + dotLen*i]; /* fprintf(stderr, " tmpF += %g*%g == %g\n", inVec[index[s + dotLen*i]], weight[s + dotLen*i], tmpF); */ } _outVec[i*strideOut] = tmpF; /* fprintf(stderr, "--> out[%d] = %g\n", i*strideOut, _outVec[i*strideOut]); */ } /* update the coordinates for the scanline starts. We don't use the usual NRRD_COORD macros because we're subject to the unusual constraint that ci[topRax] and co[permute[topRax]] must stay exactly zero */ e = topLax; ci[e]++; co[permute[e]]++; while (L < numLines-1 && ci[e] == sz[pi][e]) { ci[e] = co[permute[e]] = 0; e++; e += e == topRax; ci[e]++; co[permute[e]]++; } } /* pass-specific clean up */ airMopSub(mop, weight, airFree); airMopSub(mop, index, airFree); airMopSub(mop, inVec, airFree); weight = (nrrdResample_t*)airFree(weight); index = (int*)airFree(index); inVec = (nrrdResample_t*)airFree(inVec); } /* clean up second-to-last array and scanline buffers */ if (passes > 1) { airMopSub(mop, array[passes-1], airFree); array[passes-1] = (nrrdResample_t*)airFree(array[passes-1]); /* printf("%s: now freeing array[%d]\n", me, passes-1); */ } else if (array[passes-1] != nin->data) { airMopSub(mop, floatNin, (airMopper)nrrdNuke); floatNin = nrrdNuke(floatNin); } array[passes-1] = NULL; /* create output nrrd and set axis info */ if (nrrdMaybeAlloc_nva(nout, typeOut, nin->dim, sz[passes])) { sprintf(err, "%s: couldn't allocate final output nrrd", me); biffAdd(NRRD, err); airMopError(mop); return 1; } airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopOnError); nrrdAxisInfoCopy(nout, nin, NULL, (NRRD_AXIS_INFO_SIZE_BIT | NRRD_AXIS_INFO_MIN_BIT | NRRD_AXIS_INFO_MAX_BIT | NRRD_AXIS_INFO_SPACING_BIT | NRRD_AXIS_INFO_SPACEDIRECTION_BIT /* see below */ | NRRD_AXIS_INFO_THICKNESS_BIT | NRRD_AXIS_INFO_KIND_BIT)); for (ai=0; ai<nin->dim; ai++) { if (info->kernel[ai]) { /* we do resample this axis */ nout->axis[ai].spacing = nin->axis[ai].spacing/ratios[ai]; /* no way to usefully update thickness: we could be doing blurring but maintaining the number of samples: thickness increases, or we could be downsampling, in which the relationship between the sampled and the skipped regions of space becomes complicated: no single scalar can represent it, or we could be upsampling, in which the notion of "skip" could be rendered meaningless */ nout->axis[ai].thickness = AIR_NAN; nout->axis[ai].min = info->min[ai]; nout->axis[ai].max = info->max[ai]; /* HEY: this is currently a bug: all this code was written long before there were space directions, so min/max are always set, regardless of whethere there are incoming space directions which then disallows output space directions on the same axes _nrrdSpaceVecScale(nout->axis[ai].spaceDirection, 1.0/ratios[ai], nin->axis[ai].spaceDirection); */ nout->axis[ai].kind = _nrrdKindAltered(nin->axis[ai].kind, AIR_TRUE); } else { /* this axis remains untouched */ nout->axis[ai].min = nin->axis[ai].min; nout->axis[ai].max = nin->axis[ai].max; nout->axis[ai].spacing = nin->axis[ai].spacing; nout->axis[ai].thickness = nin->axis[ai].thickness; nout->axis[ai].kind = nin->axis[ai].kind; } } /* HEY: need to create textual representation of resampling parameters */ if (nrrdContentSet_va(nout, func, nin, "")) { sprintf(err, "%s:", me); biffAdd(NRRD, err); return 1; } /* copy the resampling final result into the output nrrd, maybe rounding as we go to make sure that 254.9999 is saved as 255 in uchar output, and maybe clamping as we go to insure that integral results don't have unexpected wrap-around. */ if (info->round) { if (nrrdTypeInt == typeOut || nrrdTypeUInt == typeOut || nrrdTypeLLong == typeOut || nrrdTypeULLong == typeOut) { fprintf(stderr, "%s: WARNING: possible erroneous output with " "rounding of %s output type due to int-based implementation " "of rounding\n", me, airEnumStr(nrrdType, typeOut)); } doRound = nrrdTypeIsIntegral[typeOut]; } else { doRound = AIR_FALSE; } numOut = nrrdElementNumber(nout); for (I=0; I<numOut; I++) { tmpF = array[passes][I]; if (doRound) { tmpF = AIR_CAST(nrrdResample_t, AIR_ROUNDUP(tmpF)); } if (info->clamp) { tmpF = nrrdDClamp[typeOut](tmpF); } nrrdDInsert[typeOut](nout->data, I, tmpF); } if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | NRRD_BASIC_INFO_TYPE_BIT | NRRD_BASIC_INFO_BLOCKSIZE_BIT | NRRD_BASIC_INFO_DIMENSION_BIT | NRRD_BASIC_INFO_CONTENT_BIT | NRRD_BASIC_INFO_COMMENTS_BIT | (nrrdStateKeyValuePairsPropagate ? 0 : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { sprintf(err, "%s:", me); biffAdd(NRRD, err); return 1; } /* enough already */ airMopOkay(mop); return 0; }
/* ******** nrrdApply1DIrregMap() ** ** Linear interpolation between irregularly spaced control points. ** Obviously, the location of the control point has to be given ** explicitly. The map nrrd must have dimension 2, and each ** control point is represented by a scanline along axis 0. The ** first value is the position of the control point, and the remaining ** value(s) are linearly weighted according to the position of the ** input value among the control point locations. ** ** To allow "coloring" of non-existant values -inf, NaN, and +inf, if ** the very first value of the map (the location of the first control ** point) is non-existant, then the first three control point locations ** must be -inf, NaN, and +inf, in that order, and the information ** about these points will be used for corresponding input values. ** Doing this makes everything slower, however, because airFPClass_f() ** is called on every single value. ** ** This assumes that nrrd1DIrregMapCheck has been called on "nmap", ** and that nrrd1DIrregAclCheck has been called on "nacl" (if it is ** non-NULL). */ int nrrdApply1DIrregMap(Nrrd *nout, const Nrrd *nin, const NrrdRange *_range, const Nrrd *nmap, const Nrrd *nacl, int typeOut, int rescale) { char me[]="nrrdApply1DIrregMap", err[BIFF_STRLEN]; size_t N, I; int i, *acl, entLen, posLen, aclLen, mapIdx, aclIdx, entSize, colSize, inSize, lo, hi, baseI; double val, *pos, domMin, domMax, mapIdxFrac, (*mapLup)(const void *v, size_t I), (*inLoad)(const void *v), (*outInsert)(void *v, size_t I, double d); char *inData, *outData, *entData0, *entData1; NrrdRange *range; airArray *mop; if (!(nout && nmap && nin)) { sprintf(err, "%s: got NULL pointer", me); biffAdd(NRRD, err); return 1; } mop = airMopNew(); if (_range) { range = nrrdRangeCopy(_range); nrrdRangeSafeSet(range, nin, nrrdBlind8BitRangeState); } else { range = nrrdRangeNewSet(nin, nrrdBlind8BitRangeState); } airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); if (_nrrdApply1DSetUp(nout, nin, range, nmap, kindImap, typeOut, rescale, AIR_FALSE)) { sprintf(err, "%s:", me); biffAdd(NRRD, err); airMopError(mop); return 1; } if (nacl && nrrd1DIrregAclCheck(nacl)) { sprintf(err, "%s: given acl isn't valid", me); biffAdd(NRRD, err); airMopError(mop); return 1; } if (nacl) { acl = (int *)nacl->data; aclLen = nacl->axis[1].size; } else { acl = NULL; aclLen = 0; } pos = _nrrd1DIrregMapDomain(&posLen, &baseI, nmap); if (!pos) { sprintf(err, "%s: couldn't determine domain", me); biffAdd(NRRD, err); airMopError(mop); return 1; } airMopAdd(mop, pos, airFree, airMopAlways); mapLup = nrrdDLookup[nmap->type]; inData = (char *)nin->data; inLoad = nrrdDLoad[nin->type]; inSize = nrrdElementSize(nin); mapLup = nrrdDLookup[nmap->type]; entLen = nmap->axis[0].size; /* entLen is really 1 + entry length */ entSize = entLen*nrrdElementSize(nmap); colSize = (entLen-1)*nrrdTypeSize[typeOut]; outData = (char *)nout->data; outInsert = nrrdDInsert[nout->type]; domMin = pos[0]; domMax = pos[posLen-1]; N = nrrdElementNumber(nin); for (I=0; I<N; I++, inData += inSize, outData += colSize) { val = inLoad(inData); /* _VV = ( (AIR_EXISTS(val) && (21 == (int)(-val))) || 22400 < I ); */ /* if (_VV) fprintf(stderr, "##%s: (%d) val = % 31.15f\n", me, (int)I, val); */ if (!AIR_EXISTS(val)) { /* got a non-existant value */ if (baseI) { /* and we know how to deal with them */ switch (airFPClass_d(val)) { case airFP_NEG_INF: mapIdx = 0; break; case airFP_SNAN: case airFP_QNAN: mapIdx = 1; break; case airFP_POS_INF: mapIdx = 2; break; default: mapIdx = 0; fprintf(stderr, "%s: PANIC: non-existant value/class %g/%d " "not handled\n", me, val, airFPClass_d(val)); exit(1); } entData0 = (char*)(nmap->data) + mapIdx*entSize; for (i=1; i<entLen; i++) { outInsert(outData, i-1, mapLup(entData0, i)); } continue; /* we're done! (with this value) */ } else { /* we don't know how to properly deal with this non-existant value: we use the first entry, and then fall through to code below */ mapIdx = 0; mapIdxFrac = 0.0; } } else { /* we have an existant value */ if (rescale) { val = AIR_AFFINE(range->min, val, range->max, domMin, domMax); /* if (_VV) fprintf(stderr, " rescaled --> % 31.15f\n", val); */ } val = AIR_CLAMP(domMin, val, domMax); if (acl) { aclIdx = airIndex(domMin, val, domMax, aclLen); lo = acl[0 + 2*aclIdx]; hi = acl[1 + 2*aclIdx]; } else { lo = 0; hi = posLen-2; } if (lo < hi) { mapIdx = _nrrd1DIrregFindInterval(pos, val, lo, hi); } else { /* acl did its job ==> lo == hi */ mapIdx = lo; } } mapIdxFrac = AIR_AFFINE(pos[mapIdx], val, pos[mapIdx+1], 0.0, 1.0); /* if (_VV) fprintf(stderr, "##%s: val=\n% 31.15f --> " "mapIdx,frac = %d,\n% 31.15f\n", me, val, mapIdx, mapIdxFrac); */ entData0 = (char*)(nmap->data) + (baseI+mapIdx)*entSize; entData1 = (char*)(nmap->data) + (baseI+mapIdx+1)*entSize; /* if (_VV) fprintf(stderr, "##%s: 2; %d/\n% 31.15f --> entLen=%d " "baseI=%d -->\n", me, mapIdx, mapIdxFrac, entLen, baseI); */ for (i=1; i<entLen; i++) { val = ((1-mapIdxFrac)*mapLup(entData0, i) + mapIdxFrac*mapLup(entData1, i)); /* if (_VV) fprintf(stderr, "% 31.15f\n", val); */ outInsert(outData, i-1, val); } /* if (_VV) fprintf(stderr, "##%s: 3\n", me); */ } airMopOkay(mop); return 0; }
/* ******** nrrdApply1DSubstitution ** ** A "subst" is a substitution table, i.e. a list of pairs that ** describes what values should be substituted with what (substitution ** rules). So, nsubst must be a scalar 2xN array. The output is a ** copy of the input with values substituted using this table. ** ** Unlike with lookup tables and maps (irregular and regular), we ** aren't at liberty to change the dimensionality of the data (can't ** substitute a grayscale with a color). The ability to apply ** substitutions to non-scalar data will be in a different function. ** Also unlike lookup tables and maps, the output type is the SAME as ** the input type; the output does NOT inherit the type of the ** substitution */ int nrrdApply1DSubstitution(Nrrd *nout, const Nrrd *nin, const Nrrd *_nsubst) { char me[]="nrrdApply1DSubstitution", err[BIFF_STRLEN]; double (*lup)(const void *, size_t); double (*ins)(void *, size_t, double); Nrrd *nsubst; double val, *subst; size_t ii, num; int jj, asize0, asize1, changed; airArray *mop; if (!(nout && _nsubst && nin)) { sprintf(err, "%s: got NULL pointer", me); biffAdd(NRRD, err); return 1; } if (nrrdTypeBlock == nin->type || nrrdTypeBlock == _nsubst->type) { sprintf(err, "%s: input or substitution type is %s, need scalar", me, airEnumStr(nrrdType, nrrdTypeBlock)); biffAdd(NRRD, err); return 1; } if (2 != _nsubst->dim) { sprintf(err, "%s: substitution table has to be 2-D, not %d-D", me, _nsubst->dim); biffAdd(NRRD, err); return 1; } nrrdAxisInfoGet_va(_nsubst, nrrdAxisInfoSize, &asize0, &asize1); if (2 != asize0) { sprintf(err, "%s: substitution table has to be 2xN, not %dxN", me, asize0); biffAdd(NRRD, err); return 1; } if (nout != nin) { if (nrrdCopy(nout, nin)) { sprintf(err, "%s: couldn't initialize by copy to output", me); biffAdd(NRRD, err); return 1; } } mop = airMopNew(); nsubst = nrrdNew(); airMopAdd(mop, nsubst, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(nsubst, _nsubst, nrrdTypeDouble)) { sprintf(err, "%s: couldn't create double copy of substitution table", me); biffAdd(NRRD, err); airMopError(mop); return 1; } lup = nrrdDLookup[nout->type]; ins = nrrdDInsert[nout->type]; subst = (double *)nsubst->data; num = nrrdElementNumber(nout); for (ii=0; ii<num; ii++) { val = lup(nout->data, ii); changed = AIR_FALSE; for (jj=0; jj<asize1; jj++) { if (val == subst[jj*2+0]) { val = subst[jj*2+1]; changed = AIR_TRUE; } } if (changed) { ins(nout->data, ii, val); } } airMopOkay(mop); return 0; }
int alanInit(alanContext *actx, const Nrrd *nlevInit, const Nrrd *nparmInit) { char me[]="alanInit", err[BIFF_STRLEN]; alan_t *levInit=NULL, *lev0, *parmInit=NULL, *parm; size_t I, N; if (_alanCheck(actx)) { sprintf(err, "%s: ", me); biffAdd(ALAN, err); return 1; } if (!( actx->_nlev[0] && actx->_nlev[0] && actx->nparm )) { sprintf(err, "%s: _nlev[0,1] not allocated: call alanUpdate", me); biffAdd(ALAN, err); return 1; } if (nlevInit) { if (nrrdCheck(nlevInit)) { sprintf(err, "%s: given nlevInit has problems", me); biffMove(ALAN, err, NRRD); return 1; } if (!( alan_nt == nlevInit->type && nlevInit->dim == 1 + actx->dim && actx->_nlev[0]->axis[0].size == nlevInit->axis[0].size && actx->size[0] == nlevInit->axis[1].size && actx->size[1] == nlevInit->axis[2].size && (2 == actx->dim || actx->size[2] == nlevInit->axis[3].size) )) { sprintf(err, "%s: type/size mismatch with given nlevInit", me); biffAdd(ALAN, err); return 1; } levInit = (alan_t*)(nlevInit->data); } if (nparmInit) { if (nrrdCheck(nparmInit)) { sprintf(err, "%s: given nparmInit has problems", me); biffMove(ALAN, err, NRRD); return 1; } if (!( alan_nt == nparmInit->type && nparmInit->dim == 1 + actx->dim && 3 == nparmInit->axis[0].size && actx->size[0] == nparmInit->axis[1].size && actx->size[1] == nparmInit->axis[2].size && (2 == actx->dim || actx->size[2] == nparmInit->axis[3].size) )) { sprintf(err, "%s: type/size mismatch with given nparmInit", me); biffAdd(ALAN, err); return 1; } parmInit = (alan_t*)(nparmInit->data); } #define RAND AIR_AFFINE(0, airDrandMT(), 1, -actx->randRange, actx->randRange) N = nrrdElementNumber(actx->_nlev[0])/actx->_nlev[0]->axis[0].size; lev0 = (alan_t*)(actx->_nlev[0]->data); parm = (alan_t*)(actx->nparm->data); for (I=0; I<N; I++) { if (levInit) { lev0[0 + 2*I] = levInit[0 + 2*I]; lev0[1 + 2*I] = levInit[1 + 2*I]; } else { /* NOTE: the random number stuff here is OUTSIDE the multi-threaded segment of the program- only the init thread does this */ lev0[0 + 2*I] = AIR_CAST(alan_t, actx->initA + RAND); lev0[1 + 2*I] = AIR_CAST(alan_t, actx->initB + RAND); } if (parmInit) { parm[0 + 3*I] = parmInit[0 + 3*I]; parm[1 + 3*I] = parmInit[1 + 3*I]; parm[2 + 3*I] = parmInit[2 + 3*I]; } else { parm[0 + 3*I] = actx->deltaT; parm[1 + 3*I] = actx->alpha; parm[2 + 3*I] = actx->beta; } } return 0; }
/* ******** nrrdFFT ** ** First pass at a wrapper around FFTW. This was implemented out of need for a ** specific project; and better decisions and different interfaces will become ** apparent with time and experience; these can be in Teem 2.0. ** ** currently *requires* that input be complex-valued, in that axis 0 has to ** have size 2. nrrdKindComplex would be sensible for input axis 0 but we don't ** require it, though it is set on the output. */ int nrrdFFT(Nrrd *nout, const Nrrd *_nin, unsigned int *axes, unsigned int axesNum, int sign, int rescale, int rigor) { static const char me[]="nrrdFFT"; size_t inSize[NRRD_DIM_MAX], II, NN, nprod; double *inData, *outData; airArray *mop; Nrrd *nin; unsigned int axi, axisDo[NRRD_DIM_MAX]; fftw_plan plan; void *dataBef; unsigned int txfRank, howRank, flags; size_t stride; fftw_iodim txfDims[NRRD_DIM_MAX], howDims[NRRD_DIM_MAX]; if (!(nout && _nin && axes)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!( _nin->dim > 1 && 2 == _nin->axis[0].size )) { biffAddf(NRRD, "%s: nin doesn't look like a complex-valued array", me); return 1; } if (!( axesNum >= 1 )) { biffAddf(NRRD, "%s: axesNum 0, no axes to transform?", me); return 1; } for (axi=0; axi<_nin->dim; axi++) { axisDo[axi] = 0; } for (axi=0; axi<axesNum; axi++) { if (0 == axes[axi]) { biffAddf(NRRD, "%s: can't transform axis 0 (axes[%u]) for " "real/complex values", me, axi); return 1; } if (!( axes[axi] < _nin->dim )) { biffAddf(NRRD, "%s: axis %u (axes[%u]) out of range [1,%u]", me, axes[axi], axi, _nin->dim-1); return 1; } axisDo[axes[axi]]++; if (2 == axisDo[axes[axi]]) { biffAddf(NRRD, "%s: axis %u (axes[%u]) already transformed", me, axes[axi], axi); return 1; } } NN = nrrdElementNumber(_nin); /* We always make a new buffer to hold the double-type copy of input for two reasons: if input is not double we have to convert it, and we want input to be const, and we can't have const with the plan creation over-writing the input (except with FFTW_ESTIMATE). Given that, we might as well use the memory-alignment-savvy fftw_malloc; the freeing is handled by both _nrrdFftwFreeWrapper and nrrdNix. */ /* (NN = 2 * number of complex values) */ inData = AIR_CAST(double *, fftw_malloc(NN*sizeof(double))); if (!inData) { biffAddf(NRRD, "%s: couldn't allocate input data copy", me); return 1; } mop = airMopNew(); airMopAdd(mop, inData, _nrrdFftwFreeWrapper, airMopAlways); nin = nrrdNew(); airMopAdd(mop, nin, (airMopper)nrrdNix /* NOT Nuke */, airMopAlways); nrrdAxisInfoGet_nva(_nin, nrrdAxisInfoSize, inSize); /* we don't copy data yet; it may be over-written during plan creation */ if (nrrdWrap_nva(nin, inData, nrrdTypeDouble, _nin->dim, inSize)) { biffAddf(NRRD, "%s: couldn't wrap or copy input", me); airMopError(mop); return 1; } /* But on the output, we just use regular malloc, because we don't (yet) have a way of telling nrrd to use fftw_malloc/fftw_free instead of the generic malloc/free, and we don't want two whole copies of the output (one that is memory-aligned, internal to this function, and one that isn't, in nout) */ if (nrrdMaybeAlloc_nva(nout, nrrdTypeDouble, _nin->dim, inSize)) { biffAddf(NRRD, "%s: couldn't allocate output", me); airMopError(mop); return 1; } outData = AIR_CAST(double *, nout->data); /* As far as GLK can tell, the guru interface is needed, and the "advanced" fftw_plan_many_dft won't work, because its simplistic accounting of stride can't handle having non-contiguous non-transformed axes (e.g. transforming only axes 2 and not 1, 3 in a 3-D complex-valued array) */ txfRank = howRank = 0; stride = 1; nprod = 1; for (axi=1; axi<nin->dim; axi++) { if (axisDo[axi]) { txfDims[txfRank].n = AIR_CAST(int, inSize[axi]); txfDims[txfRank].is = txfDims[txfRank].os = AIR_CAST(int, stride); nprod *= inSize[axi]; txfRank++; } else { howDims[howRank].n = AIR_CAST(int, inSize[axi]); howDims[howRank].is = howDims[howRank].os = AIR_CAST(int, stride); howRank++; } stride *= inSize[axi]; }
/* ******** nrrdHisto() ** ** makes a 1D histogram of a given size and type ** ** pre-NrrdRange policy: ** this looks at nin->min and nin->max to see if they are both non-NaN. ** If so, it uses these as the range of the histogram, otherwise it ** finds the min and max present in the volume. If nin->min and nin->max ** are being used as the histogram range, then values which fall outside ** this are ignored (they don't contribute to the histogram). ** ** post-NrrdRange policy: */ int nrrdHisto(Nrrd *nout, const Nrrd *nin, const NrrdRange *_range, const Nrrd *nwght, size_t bins, int type) { static const char me[]="nrrdHisto", func[]="histo"; size_t I, num, idx; airArray *mop; NrrdRange *range; double min, max, eps, val, count, incr, (*lup)(const void *v, size_t I); if (!(nin && nout)) { /* _range and nwght can be NULL */ biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (nout == nin) { biffAddf(NRRD, "%s: nout==nin disallowed", me); return 1; } if (!(bins > 0)) { biffAddf(NRRD, "%s: bins value (" _AIR_SIZE_T_CNV ") invalid", me, bins); return 1; } if (airEnumValCheck(nrrdType, type) || nrrdTypeBlock == type) { biffAddf(NRRD, "%s: invalid nrrd type %d", me, type); return 1; } if (nwght) { if (nout==nwght) { biffAddf(NRRD, "%s: nout==nwght disallowed", me); return 1; } if (nrrdTypeBlock == nwght->type) { biffAddf(NRRD, "%s: nwght type %s invalid", me, airEnumStr(nrrdType, nrrdTypeBlock)); return 1; } if (!nrrdSameSize(nin, nwght, AIR_TRUE)) { biffAddf(NRRD, "%s: nwght size mismatch with nin", me); return 1; } lup = nrrdDLookup[nwght->type]; } else { lup = NULL; } if (nrrdMaybeAlloc_va(nout, type, 1, bins)) { biffAddf(NRRD, "%s: failed to alloc histo array (len " _AIR_SIZE_T_CNV ")", me, bins); return 1; } mop = airMopNew(); /* nout->axis[0].size set */ nout->axis[0].spacing = AIR_NAN; nout->axis[0].thickness = AIR_NAN; if (nout && AIR_EXISTS(nout->axis[0].min) && AIR_EXISTS(nout->axis[0].max)) { /* HEY: total hack to externally nail down min and max of histogram: use the min and max already set on axis[0] */ /* HEY: shouldn't this blatent hack be further restricted by also checking the existence of range->min and range->max ? */ min = nout->axis[0].min; max = nout->axis[0].max; } else { if (_range) { range = nrrdRangeCopy(_range); nrrdRangeSafeSet(range, nin, nrrdBlind8BitRangeState); } else { range = nrrdRangeNewSet(nin, nrrdBlind8BitRangeState); } airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); min = range->min; max = range->max; nout->axis[0].min = min; nout->axis[0].max = max; } eps = (min == max ? 1.0 : 0.0); nout->axis[0].center = nrrdCenterCell; /* nout->axis[0].label set below */ /* make histogram */ num = nrrdElementNumber(nin); for (I=0; I<num; I++) { val = nrrdDLookup[nin->type](nin->data, I); if (AIR_EXISTS(val)) { if (val < min || val > max+eps) { /* value is outside range; ignore it */ continue; } if (AIR_IN_CL(min, val, max)) { idx = airIndex(min, val, max+eps, AIR_CAST(unsigned int, bins)); /* printf("!%s: %d: index(%g, %g, %g, %d) = %d\n", me, (int)I, min, val, max, bins, idx); */ /* count is a double in order to simplify clamping the hit values to the representable range for nout->type */ count = nrrdDLookup[nout->type](nout->data, idx); incr = nwght ? lup(nwght->data, I) : 1; count = nrrdDClamp[nout->type](count + incr); nrrdDInsert[nout->type](nout->data, idx, count); } } } if (nrrdContentSet_va(nout, func, nin, "%d", bins)) { biffAddf(NRRD, "%s:", me); airMopError(mop); return 1; } nout->axis[0].label = (char *)airFree(nout->axis[0].label); nout->axis[0].label = (char *)airStrdup(nout->content); if (!nrrdStateKindNoop) { nout->axis[0].kind = nrrdKindDomain; } airMopOkay(mop); return 0; }
int _nrrdFormatVTK_read(FILE *file, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdReadVTK"; char *three[3]; int sx, sy, sz, ret, N; double xm=0.0, ym=0.0, zm=0.0, xs=1.0, ys=1.0, zs=1.0; airArray *mop; unsigned int llen; if (!_nrrdFormatVTK_contentStartsLike(nio)) { biffAddf(NRRD, "%s: this doesn't look like a %s file", me, nrrdFormatVTK->name); return 1; } #define GETLINE(what) \ do { \ ret = _nrrdOneLine(&llen, nio, file); \ } while (!ret && (1 == llen)); \ if (ret || !llen) { \ biffAddf(NRRD, "%s: couldn't get " #what " line", me); \ return 1; \ } /* read in content */ GETLINE(content); if (strcmp(NRRD_UNKNOWN, nio->line)) { if (!(nrrd->content = airStrdup(nio->line))) { biffAddf(NRRD, "%s: couldn't read or copy content string", me); return 1; } } GETLINE(encoding); airToUpper(nio->line); if (!strcmp("ASCII", nio->line)) { nio->encoding = nrrdEncodingAscii; } else if (!strcmp("BINARY", nio->line)) { nio->encoding = nrrdEncodingRaw; } else { biffAddf(NRRD, "%s: encoding \"%s\" wasn't \"ASCII\" or \"BINARY\"", me, nio->line); return 1; } GETLINE(DATASET); airToUpper(nio->line); if (!strstr(nio->line, "STRUCTURED_POINTS")) { biffAddf(NRRD, "%s: sorry, only STRUCTURED_POINTS data is nrrd-ready", me); return 1; } GETLINE(DIMENSIONS); airToUpper(nio->line); if (!strstr(nio->line, "DIMENSIONS") || 3 != sscanf(nio->line, "DIMENSIONS %d %d %d", &sx, &sy, &sz)) { biffAddf(NRRD, "%s: couldn't parse DIMENSIONS line (\"%s\")", me, nio->line); return 1; } GETLINE(next); airToUpper(nio->line); while (!strstr(nio->line, "POINT_DATA")) { if (strstr(nio->line, "ORIGIN")) { if (3 != sscanf(nio->line, "ORIGIN %lf %lf %lf", &xm, &ym, &zm)) { biffAddf(NRRD, "%s: couldn't parse ORIGIN line (\"%s\")", me, nio->line); return 1; } } else if (strstr(nio->line, "SPACING")) { if (3 != sscanf(nio->line, "SPACING %lf %lf %lf", &xs, &ys, &zs)) { biffAddf(NRRD, "%s: couldn't parse SPACING line (\"%s\")", me, nio->line); return 1; } } else if (strstr(nio->line, "ASPECT_RATIO")) { if (3 != sscanf(nio->line, "ASPECT_RATIO %lf %lf %lf", &xs, &ys, &zs)) { biffAddf(NRRD, "%s: couldn't parse ASPECT_RATIO line (\"%s\")", me, nio->line); return 1; } } GETLINE(next); airToUpper(nio->line); } if (1 != sscanf(nio->line, "POINT_DATA %d", &N)) { biffAddf(NRRD, "%s: couldn't parse POINT_DATA line (\"%s\")", me, nio->line); return 1; } if (N != sx*sy*sz) { biffAddf(NRRD, "%s: product of sizes (%d*%d*%d == %d) != # elements (%d)", me, sx, sy, sz, sx*sy*sz, N); return 1; } GETLINE(attribute declaration); mop = airMopNew(); if (3 != airParseStrS(three, nio->line, AIR_WHITESPACE, 3, AIR_FALSE)) { biffAddf(NRRD, "%s: didn't see three words in attribute declaration \"%s\"", me, nio->line); return 1; } airMopAdd(mop, three[0], airFree, airMopAlways); airMopAdd(mop, three[1], airFree, airMopAlways); airMopAdd(mop, three[2], airFree, airMopAlways); airToLower(three[2]); if (!strcmp(three[2], "bit")) { if (nrrdEncodingAscii == nio->encoding) { fprintf(stderr, "%s: WARNING: \"bit\"-type data will be read in as " "unsigned char\n", me); nrrd->type = nrrdTypeUChar; } else { biffAddf(NRRD, "%s: can't read in \"bit\"-type data as BINARY", me); return 1; } } else if (!strcmp(three[2], "unsigned_char")) { nrrd->type = nrrdTypeUChar; } else if (!strcmp(three[2], "char")) { nrrd->type = nrrdTypeChar; } else if (!strcmp(three[2], "unsigned_short")) { nrrd->type = nrrdTypeUShort; } else if (!strcmp(three[2], "short")) { nrrd->type = nrrdTypeShort; } else if (!strcmp(three[2], "unsigned_int")) { nrrd->type = nrrdTypeUInt; } else if (!strcmp(three[2], "int")) { nrrd->type = nrrdTypeInt; } else if (!strcmp(three[2], "float")) { nrrd->type = nrrdTypeFloat; } else if (!strcmp(three[2], "double")) { nrrd->type = nrrdTypeDouble; } else { /* "unsigned_long" and "long" fall in here- I don't know what the VTK people mean by these types, since always mean different things on 32-bit versus 64-bit architectures */ biffAddf(NRRD, "%s: type \"%s\" not recognized", me, three[2]); airMopError(mop); return 1; } airToUpper(three[0]); if (!strncmp("SCALARS", three[0], strlen("SCALARS"))) { GETLINE(LOOKUP_TABLE); airToUpper(nio->line); if (strcmp(nio->line, "LOOKUP_TABLE DEFAULT")) { biffAddf(NRRD, "%s: sorry, can only deal with default LOOKUP_TABLE", me); airMopError(mop); return 1; } nrrd->dim = 3; nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoSize, AIR_CAST(size_t, sx), AIR_CAST(size_t, sy), AIR_CAST(size_t, sz)); nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoSpacing, xs, ys, zs); nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoMin, xm, ym, zm); } else if (!strncmp("VECTORS", three[0], strlen("VECTORS"))) { nrrd->dim = 4; nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoSize, AIR_CAST(size_t, 3), AIR_CAST(size_t, sx), AIR_CAST(size_t, sy), AIR_CAST(size_t, sz)); nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoSpacing, AIR_NAN, xs, ys, zs); nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoMin, AIR_NAN, xm, ym, zm); nrrd->axis[0].kind = nrrdKind3Vector; } else if (!strncmp("TENSORS", three[0], strlen("TENSORS"))) { nrrd->dim = 4; nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoSize, AIR_CAST(size_t, 9), AIR_CAST(size_t, sx), AIR_CAST(size_t, sy), AIR_CAST(size_t, sz)); nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoSpacing, AIR_NAN, xs, ys, zs); nrrdAxisInfoSet_va(nrrd, nrrdAxisInfoMin, AIR_NAN, xm, ym, zm); nrrd->axis[0].kind = nrrdKind3DMatrix; } else { biffAddf(NRRD, "%s: sorry, can only deal with SCALARS, VECTORS, and TENSORS " "currently, so couldn't parse attribute declaration \"%s\"", me, nio->line); airMopError(mop); return 1; } if (!nio->skipData) { if (_nrrdCalloc(nrrd, nio, file)) { biffAddf(NRRD, "%s: couldn't allocate memory for data", me); return 1; } if (nio->encoding->read(file, nrrd->data, nrrdElementNumber(nrrd), nrrd, nio)) { biffAddf(NRRD, "%s:", me); return 1; } if (1 < nrrdElementSize(nrrd) && nio->encoding->endianMatters && airMyEndian() != airEndianBig) { /* encoding exposes endianness, and its big, but we aren't */ nrrdSwapEndian(nrrd); } } else { nrrd->data = NULL; } airMopOkay(mop); return 0; }
/* ** _nrrdApply2DLutOrRegMap() ** ** the guts of nrrdApply2DLut and nrrdApply2DRegMap ** ** yikes, does NOT use biff, since we're only supposed to be called ** after copious error checking. ** ** FOR INSTANCE, this allows nout == nin, which could be a big ** problem if mapAxis == 1. ** ** we don't need a typeOut arg because nout has already been allocated ** as some specific type; we'll look at that. ** ** NOTE: non-existant values get passed through regular maps and luts ** "unchanged". However, if the output type is integral, the results ** are probaby undefined. HEY: there is currently no warning message ** or error handling based on nrrdStateDisallowIntegerNonExist, but ** there really should be. */ int _nrrdApply2DLutOrRegMap(Nrrd *nout, const Nrrd *nin, const NrrdRange *range0, const NrrdRange *range1, const Nrrd *nmap, int ramps, int rescale0, int rescale1) { char me[]="_nrrdApply2DLutOrRegMap"; char *inData, *outData, *mapData, *entData; size_t N, I; double (*inLoad)(const void *v), (*mapLup)(const void *v, size_t I), (*outInsert)(void *v, size_t I, double d), val0, val1, domMin0, domMax0, domMin1, domMax1; unsigned int i, mapAxis, mapLen0, mapLen1, mapIdx0, mapIdx1, entSize, entLen, inSize, outSize; mapAxis = nmap->dim - 2; /* axis of nmap containing entries */ mapData = (char *)nmap->data; /* map data, as char* */ /* low end of map domain */ domMin0 = _nrrdApplyDomainMin(nmap, ramps, mapAxis + 0); domMin1 = _nrrdApplyDomainMin(nmap, ramps, mapAxis + 1); /* high end of map domain */ domMax0 = _nrrdApplyDomainMax(nmap, ramps, mapAxis + 0); domMax1 = _nrrdApplyDomainMax(nmap, ramps, mapAxis + 1); mapLen0 = nmap->axis[mapAxis+0].size; /* number of entries in map axis 0 */ mapLen1 = nmap->axis[mapAxis+1].size; /* number of entries in map axis 1 */ mapLup = nrrdDLookup[nmap->type]; /* how to get doubles out of map */ inData = (char *)nin->data; /* input data, as char* */ inLoad = nrrdDLoad[nin->type]; /* how to get doubles out of nin */ inSize = nrrdElementSize(nin); /* size of one input value */ outData = (char *)nout->data; /* output data, as char* */ outInsert = nrrdDInsert[nout->type]; /* putting doubles into output */ entLen = (mapAxis /* number of elements in one entry */ ? nmap->axis[0].size : 1); outSize = entLen*nrrdElementSize(nout); /* size of entry in output */ entSize = entLen*nrrdElementSize(nmap); /* size of entry in map */ /* fprintf(stderr, "!%s: entLen = %u, mapLen = %u,%u\n", me, entLen, mapLen0, mapLen1); */ N = nrrdElementNumber(nin)/2; /* number of value pairs to be mapped */ /* _VV = 1; */ if (ramps) { fprintf(stderr, "%s: PANIC: unimplemented\n", me); exit(1); } else { /* lookup table */ for (I=0; I<N; I++) { val0 = inLoad(inData + 0*inSize); val1 = inLoad(inData + 1*inSize); if (rescale0) { val0 = AIR_AFFINE(range0->min, val0, range0->max, domMin0, domMax0); } if (rescale1) { val1 = AIR_AFFINE(range1->min, val1, range1->max, domMin1, domMax1); } if (AIR_EXISTS(val0) && AIR_EXISTS(val1)) { mapIdx0 = airIndexClamp(domMin0, val0, domMax0, mapLen0); mapIdx1 = airIndexClamp(domMin1, val1, domMax1, mapLen1); entData = mapData + entSize*(mapIdx0 + mapLen0*mapIdx1); for (i=0; i<entLen; i++) { outInsert(outData, i, mapLup(entData, i)); } } else { /* copy non-existant values from input to output */ for (i=0; i<entLen; i++) { outInsert(outData, i, val0 + val1); /* HEY this is weird */ } } inData += 2*inSize; outData += outSize; } } return 0; }
/* ******** tenBVecNonLinearFit ** ** Assuming that axis 0 represents a sequence of DWI measurements at a ** range of b values (as described by bb[i]), do non-linear least-squares ** fitting of those measurements, governed by weights ww[i] (with at ** most iterMax interations, or terminated when L2 norm change < eps). ** ** Based on model fit amp*exp(-b*dec), output nrrd's axis 0 has three values: ** 0: amp ** 1: dec ** 2: error of fit ** and all other axes are unchanged from input. Output type is always double. */ int tenBVecNonLinearFit(Nrrd *nout, const Nrrd *nin, double *bb, double *ww, int iterMax, double eps) { char me[]="tenBVecNonLinearFit", err[BIFF_STRLEN]; int map[NRRD_DIM_MAX], vecSize, iter; size_t ii, size[NRRD_DIM_MAX], vecI, vecNum; char *vec; double *out, ss[AIR_STRLEN_SMALL], amp, dec, d_amp, d_dec, error, diff, (*vecLup)(const void *v, size_t I); if (!( nout && nin && bb && ww )) { sprintf(err, "%s: got NULL pointer", me); biffAdd(TEN, err); return 1; } if (!( nin->dim >= 2 )) { sprintf(err, "%s: nin->dim (%d) not >= 2", me, nin->dim); biffAdd(TEN, err); return 1; } if (!( nin->axis[0].size < AIR_STRLEN_SMALL )) { sprintf(err, "%s: sorry need nin->axis[0].size (" _AIR_SIZE_T_CNV ") < %d", me, nin->axis[0].size, AIR_STRLEN_SMALL); biffAdd(TEN, err); return 1; } /* allocate/set-up output */ nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size); size[0] = 3; if (nrrdMaybeAlloc_nva(nout, nrrdTypeDouble, nin->dim, size)) { sprintf(err, "%s: couldn't allocate output", me); biffMove(TEN, err, NRRD); return 1; } for (ii=1; ii<nin->dim; ii++) { map[ii] = ii; } map[0] = -1; if (nrrdAxisInfoCopy(nout, nin, map, NRRD_AXIS_INFO_NONE)) { sprintf(err, "%s: couldn't copy axis info", me); biffMove(TEN, err, NRRD); return 1; } /* process all b vectors */ vecSize = nin->axis[0].size*nrrdTypeSize[nin->type]; vecNum = nrrdElementNumber(nin)/nin->axis[0].size; vecLup = nrrdDLookup[nin->type]; vec = (char*)nin->data; out = (double*)nout->data; for (vecI=0; vecI<vecNum; vecI++) { /* copy DWI signal values */ for (ii=0; ii<nin->axis[0].size; ii++) { ss[ii] = vecLup(vec, ii); } /* start with linear fit */ tenBVecNonLinearFit_linear(&, &dec, bb, ss, ww, nin->axis[0].size); error = tenBVecNonLinearFit_error(bb, ss, ww, nin->axis[0].size, amp, dec); /* possibly refine with gauss-newton */ if (iterMax > 0) { iter = 0; do { iter++; tenBVecNonLinearFit_GNstep(&d_amp, &d_dec, bb, ss, ww, nin->axis[0].size, amp, dec); amp += 0.3*d_amp; dec += 0.3*d_dec; diff = d_amp*d_amp + d_dec*d_dec; } while (iter < iterMax && diff > eps); } error = tenBVecNonLinearFit_error(bb, ss, ww, nin->axis[0].size, amp, dec); out[0] = amp; out[1] = dec; out[2] = error; vec += vecSize; out += 3; } return 0; }
/* ******** nrrdMaybeAlloc_nva ** ** calls nrrdAlloc_nva if the requested space is different than ** what is currently held ** ** also subscribes to the "don't mess with peripheral information" philosophy */ int nrrdMaybeAlloc_nva(Nrrd *nrrd, int type, unsigned int dim, const size_t *size) { char me[]="nrrdMaybeAlloc_nva", err[BIFF_STRLEN]; size_t sizeWant, sizeHave, numWant, elementSizeWant; int need; unsigned int ai; if (!nrrd) { sprintf(err, "%s: got NULL pointer", me); biffAdd(NRRD, err); return 1; } if (airEnumValCheck(nrrdType, type)) { sprintf(err, "%s: type (%d) is invalid", me, type); biffAdd(NRRD, err); return 1; } if (nrrdTypeBlock == type) { if (nrrdTypeBlock == nrrd->type) { sprintf(err, "%s: can't change from one block nrrd to another", me); biffAdd(NRRD, err); return 1; } if (!(0 < nrrd->blockSize)) { sprintf(err, "%s: given nrrd->blockSize " _AIR_SIZE_T_CNV " invalid", me, nrrd->blockSize); biffAdd(NRRD, err); return 1; } elementSizeWant = nrrd->blockSize; } else { elementSizeWant = nrrdTypeSize[type]; } if (_nrrdSizeCheck(size, dim, AIR_TRUE)) { sprintf(err, "%s:", me); biffAdd(NRRD, err); return 1; } if (!(nrrd->data)) { need = 1; } else { numWant = 1; for (ai=0; ai<dim; ai++) { numWant *= size[ai]; } if (!nrrdElementSize(nrrd)) { sprintf(err, "%s: nrrd reports zero element size!", me); biffAdd(NRRD, err); return 1; } sizeHave = nrrdElementNumber(nrrd) * nrrdElementSize(nrrd); /* fprintf(stderr, "##%s: sizeHave = %d * %d = %d\n", me, (int)(nrrdElementNumber(nrrd)), (int)(nrrdElementSize(nrrd)), (int)sizeHave); */ sizeWant = numWant * elementSizeWant; /* fprintf(stderr, "##%s: sizeWant = %d * %d = %d\n", me, (int)(numWant), (int)(elementSizeWant), (int)sizeWant); */ need = sizeHave != sizeWant; /* fprintf(stderr, "##%s: need = %d\n", me, need); */ } if (need) { if (nrrdAlloc_nva(nrrd, type, dim, size)) { sprintf(err, "%s:", me); biffAdd(NRRD, err); return 1; } } else { if (nrrdWrap_nva(nrrd, nrrd->data, type, dim, size)) { sprintf(err, "%s:", me); biffAdd(NRRD, err); return 1; } /* but we do have to initialize memory! */ memset(nrrd->data, 0, nrrdElementNumber(nrrd)*nrrdElementSize(nrrd)); } return 0; }
int main() { airArray *mop, *submop; char *err; int typi; unsigned int supi, probePass, cti /* context copy index */, pvlIdx[NRRD_TYPE_MAX+1], sx, sy, sz, subnum; size_t sizes[3] = {42,61,50} /* one of these must be even */, ii, nn; Nrrd *norigScl, *nucharScl, *nunquant, *nqdiff, *nconvScl[NRRD_TYPE_MAX+1]; unsigned char *ucharScl; gageContext *gctx[2][KERN_SIZE_MAX+1]; gagePerVolume *gpvl[2][NRRD_TYPE_MAX+1][KERN_SIZE_MAX+1]; const double *vansScl[2][NRRD_TYPE_MAX+1][KERN_SIZE_MAX+1], *gansScl[2][NRRD_TYPE_MAX+1][KERN_SIZE_MAX+1], *hansScl[2][NRRD_TYPE_MAX+1][KERN_SIZE_MAX+1]; double *origScl, omin, omax, dsx, dsy, dsz, spcOrig[NRRD_SPACE_DIM_MAX] = {0.0, 0.0, 0.0}, spcVec[3][NRRD_SPACE_DIM_MAX] = { {1.1, 0.0, 0.0}, {0.0, 2.2, 0.0}, {0.0, 0.0, 3.3}}; mop = airMopNew(); #define NRRD_NEW(name, mop) \ (name) = nrrdNew(); \ airMopAdd((mop), (name), (airMopper)nrrdNuke, airMopAlways) /* --------------------------------------------------------------- */ /* Creating initial volume */ NRRD_NEW(norigScl, mop); if (nrrdMaybeAlloc_nva(norigScl, nrrdTypeDouble, 3, sizes)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "trouble allocating:\n%s", err); airMopError(mop); return 1; } origScl = AIR_CAST(double *, norigScl->data); nn = nrrdElementNumber(norigScl); airSrandMT(42*42); for (ii=0; ii<nn/2; ii++) { airNormalRand(origScl + 2*ii + 0, origScl + 2*ii + 1); } /* learn real range */ omin = omax = origScl[0]; for (ii=1; ii<nn; ii++) { omin = AIR_MIN(omin, origScl[ii]); omax = AIR_MAX(omax, origScl[ii]); } ELL_3V_SET(spcOrig, 0.0, 0.0, 0.0); if (nrrdSpaceSet(norigScl, nrrdSpaceRightAnteriorSuperior) || nrrdSpaceOriginSet(norigScl, spcOrig)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "trouble setting space:\n%s", err); airMopError(mop); return 1; } nrrdAxisInfoSet_nva(norigScl, nrrdAxisInfoSpaceDirection, spcVec); dsx = AIR_CAST(double, sizes[0]); dsy = AIR_CAST(double, sizes[1]); dsz = AIR_CAST(double, sizes[2]); sx = AIR_CAST(unsigned int, sizes[0]); sy = AIR_CAST(unsigned int, sizes[1]); sz = AIR_CAST(unsigned int, sizes[2]); subnum = AIR_CAST(unsigned int, PROBE_NUM*0.9); /* --------------------------------------------------------------- */ /* Quantizing to 8-bits and checking */ submop = airMopNew(); NRRD_NEW(nucharScl, mop); NRRD_NEW(nunquant, submop); NRRD_NEW(nqdiff, submop); if (nrrdQuantize(nucharScl, norigScl, NULL, 8) || nrrdUnquantize(nunquant, nucharScl, nrrdTypeDouble) || nrrdArithBinaryOp(nqdiff, nrrdBinaryOpSubtract, norigScl, nunquant)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "trouble quantizing and back:\n%s", err); airMopError(submop); airMopError(mop); return 1; } if (!( nucharScl->oldMin == omin && nucharScl->oldMax == omax )) { fprintf(stderr, "quantization range [%g,%g] != real range [%g,%g]\n", nucharScl->oldMin, nucharScl->oldMax, omin, omax); airMopError(submop); airMopError(mop); return 1; } { double *qdiff, *unquant; /* empirically determined tolerance, which had to be increased in order to work under valgrind (!)- perhaps because of a difference in the use of 80-bit registers */ double epsilon=0.50000000000004; qdiff = AIR_CAST(double *, nqdiff->data); unquant = AIR_CAST(double *, nunquant->data); for (ii=0; ii<nn; ii++) { double dd; /* with infinite precision, the max difference between original and quantized values should be exactly half the width (in value) of 1/256 of value range ==> dd = 0.5 */ dd = qdiff[ii]*256/(omax - omin); if (AIR_ABS(dd) > epsilon) { unsigned int ui; ui = AIR_CAST(unsigned int, ii); fprintf(stderr, "|orig[%u]=%.17g - unquant=%.17g|*256/%.17g " "= %.17g > %.17g!\n", ui, origScl[ii], unquant[ii], omax - omin, AIR_ABS(dd), epsilon); airMopError(submop); airMopError(mop); return 1; } } }
/* ** _nrrdApply1DLutOrRegMap() ** ** the guts of nrrdApply1DLut and nrrdApply1DRegMap ** ** yikes, does NOT use biff, since we're only supposed to be called ** after copious error checking. ** ** FOR INSTANCE, this allows nout == nin, which could be a big ** problem if mapAxis == 1. ** ** we don't need a typeOut arg because nout has already been allocated ** as some specific type; we'll look at that. ** ** NOTE: non-existant values get passed through regular maps and luts ** "unchanged". However, if the output type is integral, the results ** are probaby undefined. HEY: there is currently no warning message ** or error handling based on nrrdStateDisallowIntegerNonExist, but ** there really should be. */ int _nrrdApply1DLutOrRegMap(Nrrd *nout, const Nrrd *nin, const NrrdRange *range, const Nrrd *nmap, int ramps, int rescale, int multi) { /* char me[]="_nrrdApply1DLutOrRegMap"; */ char *inData, *outData, *mapData, *entData0, *entData1; size_t N, I; double (*inLoad)(const void *v), (*mapLup)(const void *v, size_t I), (*outInsert)(void *v, size_t I, double d), val, mapIdxFrac, domMin, domMax; unsigned int i, mapAxis, mapLen, mapIdx, entSize, entLen, inSize, outSize; if (!multi) { mapAxis = nmap->dim - 1; /* axis of nmap containing entries */ } else { mapAxis = nmap->dim - nin->dim - 1; } mapData = (char *)nmap->data; /* map data, as char* */ /* low end of map domain */ domMin = _nrrdApplyDomainMin(nmap, ramps, mapAxis); /* high end of map domain */ domMax = _nrrdApplyDomainMax(nmap, ramps, mapAxis); mapLen = nmap->axis[mapAxis].size; /* number of entries in map */ mapLup = nrrdDLookup[nmap->type]; /* how to get doubles out of map */ inData = (char *)nin->data; /* input data, as char* */ inLoad = nrrdDLoad[nin->type]; /* how to get doubles out of nin */ inSize = nrrdElementSize(nin); /* size of one input value */ outData = (char *)nout->data; /* output data, as char* */ outInsert = nrrdDInsert[nout->type]; /* putting doubles into output */ entLen = (mapAxis /* number of elements in one entry */ ? nmap->axis[0].size : 1); outSize = entLen*nrrdElementSize(nout); /* size of entry in output */ entSize = entLen*nrrdElementSize(nmap); /* size of entry in map */ N = nrrdElementNumber(nin); /* the number of values to be mapped */ if (ramps) { /* regular map */ for (I=0; I<N; I++) { /* ... if (!(I % 100)) fprintf(stderr, "I = %d\n", (int)I); ... */ val = inLoad(inData); /* ... fprintf(stderr, "##%s: val = \na% 31.15f --> ", me, val); ... */ if (rescale) { val = AIR_AFFINE(range->min, val, range->max, domMin, domMax); /* ... fprintf(stderr, "\nb% 31.15f (min,max = %g,%g)--> ", val, range->min, range->max); ... */ } /* ... fprintf(stderr, "\nc% 31.15f --> clamp(%g,%g), %d --> ", val, domMin, domMax, mapLen); ... */ if (AIR_EXISTS(val)) { val = AIR_CLAMP(domMin, val, domMax); mapIdxFrac = AIR_AFFINE(domMin, val, domMax, 0, mapLen-1); /* ... fprintf(stderr, "mapIdxFrac = \nd% 31.15f --> ", mapIdxFrac); ... */ mapIdx = (unsigned int)mapIdxFrac; mapIdx -= mapIdx == mapLen-1; mapIdxFrac -= mapIdx; /* ... fprintf(stderr, "%s: (%d,\ne% 31.15f) --> \n", me, mapIdx, mapIdxFrac); ... */ entData0 = mapData + mapIdx*entSize; entData1 = mapData + (mapIdx+1)*entSize; for (i=0; i<entLen; i++) { val = ((1-mapIdxFrac)*mapLup(entData0, i) + mapIdxFrac*mapLup(entData1, i)); outInsert(outData, i, val); /* ... fprintf(stderr, "f% 31.15f\n", val); ... */ } } else { /* copy non-existant values from input to output */ for (i=0; i<entLen; i++) { outInsert(outData, i, val); } } inData += inSize; outData += outSize; if (multi) { mapData += mapLen*entSize; } } } else { /* lookup table */ for (I=0; I<N; I++) { val = inLoad(inData); if (rescale) { val = AIR_AFFINE(range->min, val, range->max, domMin, domMax); } if (AIR_EXISTS(val)) { mapIdx = airIndexClamp(domMin, val, domMax, mapLen); entData0 = mapData + mapIdx*entSize; for (i=0; i<entLen; i++) { outInsert(outData, i, mapLup(entData0, i)); } } else { /* copy non-existant values from input to output */ for (i=0; i<entLen; i++) { outInsert(outData, i, val); } } inData += inSize; outData += outSize; if (multi) { mapData += mapLen*entSize; } } } return 0; }
/* ** _nrrdSprintFieldInfo ** ** this prints "<prefix><field>: <info>" into *strP (after allocating it for ** big enough, usually with a stupidly big margin of error), in a form ** suitable to be written to NRRD or other image headers. This will always ** print something (for valid inputs), even stupid <info>s like ** "(unknown endian)". It is up to the caller to decide which fields ** are worth writing, via _nrrdFieldInteresting(). ** ** NOTE: some of these fields make sense in non-NRRD files (e.g. all ** the per-axis information), but many only make sense in NRRD files. ** This is just one example of NRRD-format-specific stuff that is not ** in formatNRRD.c */ void _nrrdSprintFieldInfo(char **strP, const char *prefix, const Nrrd *nrrd, NrrdIoState *nio, int field) { static const char me[]="_nrrdSprintFieldInfo"; char buff[AIR_STRLEN_MED], *fnb, stmp[AIR_STRLEN_SMALL], *strtmp=NULL; double colvec[NRRD_SPACE_DIM_MAX]; const char *fs; unsigned int ii, dd, uintStrlen = 11, size_tStrlen = 33, doubleStrlen = 513; size_t fslen, fdlen, maxl; int endi; if (!( strP && prefix && nrrd && AIR_IN_CL(1, nrrd->dim, NRRD_DIM_MAX) && AIR_IN_OP(nrrdField_unknown, field, nrrdField_last) )) { return; } /* As of Sun Dec 2 01:57:48 CST 2012 (revision 5832) the only places where this function is called is when it has been guarded by "if (_nrrdFieldInteresting())" (except for in formatText.c when its called on the dimension field, which is always interesting). So, the following: if (!_nrrdFieldInteresting(nrrd, nio, field)) { *strP = airStrdup(""); } was redundant and confusingly created the appearance of a memory leak waiting to happen. We now let the default switch statement set *strP to NULL (all the other cases set it), to smoke out errors in how this function is called */ fs = airEnumStr(nrrdField, field); fslen = strlen(prefix) + strlen(fs) + strlen(": ") + 1; switch (field) { case nrrdField_comment: case nrrdField_keyvalue: fprintf(stderr, "%s: CONFUSION: why are you calling me on \"%s\"?\n", me, airEnumStr(nrrdField, nrrdField_comment)); *strP = airStrdup(""); break; case nrrdField_content: strtmp = airOneLinify(airStrdup(nrrd->content)); *strP = AIR_CALLOC(fslen + strlen(strtmp), char); sprintf(*strP, "%s%s: %s", prefix, fs, strtmp); airFree(strtmp); strtmp = NULL; break; case nrrdField_number: *strP = AIR_CALLOC(fslen + size_tStrlen, char); sprintf(*strP, "%s%s: %s", prefix, fs, airSprintSize_t(stmp, nrrdElementNumber(nrrd))); break; case nrrdField_type: *strP = AIR_CALLOC(fslen + strlen(airEnumStr(nrrdType, nrrd->type)), char); sprintf(*strP, "%s%s: %s", prefix, fs, airEnumStr(nrrdType, nrrd->type)); break; case nrrdField_block_size: *strP = AIR_CALLOC(fslen + size_tStrlen, char); sprintf(*strP, "%s%s: %s", prefix, fs, airSprintSize_t(stmp, nrrd->blockSize)); break; case nrrdField_dimension: *strP = AIR_CALLOC(fslen + uintStrlen, char); sprintf(*strP, "%s%s: %d", prefix, fs, nrrd->dim); break; case nrrdField_space: *strP = AIR_CALLOC(fslen + strlen(airEnumStr(nrrdSpace, nrrd->space)), char); sprintf(*strP, "%s%s: %s", prefix, fs, airEnumStr(nrrdSpace, nrrd->space)); break; case nrrdField_space_dimension: *strP = AIR_CALLOC(fslen + uintStrlen, char); sprintf(*strP, "%s%s: %d", prefix, fs, nrrd->spaceDim); break; /* ---- begin per-axis fields ---- */ case nrrdField_sizes: *strP = AIR_CALLOC(fslen + nrrd->dim*(size_tStrlen + 1), char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { sprintf(buff, " %s", airSprintSize_t(stmp, nrrd->axis[ii].size)); strcat(*strP, buff); } break; case nrrdField_spacings: *strP = AIR_CALLOC(fslen + nrrd->dim*(doubleStrlen + 1), char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { airSinglePrintf(NULL, buff, " %.17g", nrrd->axis[ii].spacing); strcat(*strP, buff); } break; case nrrdField_thicknesses: *strP = AIR_CALLOC(fslen + nrrd->dim*(doubleStrlen + 1), char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { airSinglePrintf(NULL, buff, " %.17g", nrrd->axis[ii].thickness); strcat(*strP, buff); } break; case nrrdField_axis_mins: *strP = AIR_CALLOC(fslen + nrrd->dim*(doubleStrlen + 1), char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { airSinglePrintf(NULL, buff, " %.17g", nrrd->axis[ii].min); strcat(*strP, buff); } break; case nrrdField_axis_maxs: *strP = AIR_CALLOC(fslen + nrrd->dim*(doubleStrlen + 1), char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { airSinglePrintf(NULL, buff, " %.17g", nrrd->axis[ii].max); strcat(*strP, buff); } break; case nrrdField_space_directions: *strP = AIR_CALLOC(fslen + nrrd->dim*nrrd->spaceDim*(doubleStrlen + strlen("(,) ")), char); sprintf(*strP, "%s%s: ", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { _nrrdStrcatSpaceVector(*strP, nrrd->spaceDim, nrrd->axis[ii].spaceDirection); if (ii < nrrd->dim-1) { strcat(*strP, " "); } } break; case nrrdField_centers: fdlen = 0; for (ii=0; ii<nrrd->dim; ii++) { fdlen += 1 + airStrlen(nrrd->axis[ii].center ? airEnumStr(nrrdCenter, nrrd->axis[ii].center) : NRRD_UNKNOWN); } *strP = AIR_CALLOC(fslen + fdlen, char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { sprintf(buff, " %s", (nrrd->axis[ii].center ? airEnumStr(nrrdCenter, nrrd->axis[ii].center) : NRRD_UNKNOWN)); strcat(*strP, buff); } break; case nrrdField_kinds: fdlen = 0; for (ii=0; ii<nrrd->dim; ii++) { fdlen += 1 + airStrlen(nrrd->axis[ii].kind ? airEnumStr(nrrdKind, nrrd->axis[ii].kind) : NRRD_UNKNOWN); } *strP = AIR_CALLOC(fslen + fdlen, char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { sprintf(buff, " %s", (nrrd->axis[ii].kind ? airEnumStr(nrrdKind, nrrd->axis[ii].kind) : NRRD_UNKNOWN)); strcat(*strP, buff); } break; case nrrdField_labels: case nrrdField_units: #define LABEL_OR_UNITS (nrrdField_labels == field \ ? nrrd->axis[ii].label \ : nrrd->axis[ii].units) fdlen = 0; for (ii=0; ii<nrrd->dim; ii++) { /* The "2*" is because at worst every character needs escaping. The "+ 3" for the |" "| between each part */ fdlen += 2*airStrlen(LABEL_OR_UNITS) + 3; } fdlen += 1; /* for '\0' */ *strP = AIR_CALLOC(fslen + fdlen, char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { strcat(*strP, " \""); if (airStrlen(nrrd->axis[ii].label)) { _nrrdWriteEscaped(NULL, *strP, LABEL_OR_UNITS, "\"", _NRRD_WHITESPACE_NOTAB); } strcat(*strP, "\""); } #undef LABEL_OR_UNITS break; /* ---- end per-axis fields ---- */ case nrrdField_min: case nrrdField_max: /* we're basically a no-op, now that these fields became meaningless */ *strP = AIR_CALLOC(fslen + doubleStrlen, char); sprintf(*strP, "%s%s: 0.0", prefix, fs); strcat(*strP, buff); break; case nrrdField_old_min: *strP = AIR_CALLOC(fslen + doubleStrlen, char); sprintf(*strP, "%s%s: ", prefix, fs); airSinglePrintf(NULL, buff, "%.17g", nrrd->oldMin); strcat(*strP, buff); break; case nrrdField_old_max: *strP = AIR_CALLOC(fslen + doubleStrlen, char); sprintf(*strP, "%s%s: ", prefix, fs); airSinglePrintf(NULL, buff, "%.17g", nrrd->oldMax); strcat(*strP, buff); break; case nrrdField_endian: if (airEndianUnknown != nio->endian) { /* we know a specific endianness because either it was recorded as part of "unu make -h", or it was set (and data was possibly altered) as part of "unu save" */ endi = nio->endian; } else { /* we record our current architecture's endian because we're going to writing out data */ endi = airMyEndian(); } *strP = AIR_CALLOC(fslen + strlen(airEnumStr(airEndian, endi)), char); sprintf(*strP, "%s%s: %s", prefix, fs, airEnumStr(airEndian, endi)); break; case nrrdField_encoding: *strP = AIR_CALLOC(fslen + strlen(nio->encoding->name), char); sprintf(*strP, "%s%s: %s", prefix, fs, nio->encoding->name); break; case nrrdField_line_skip: *strP = AIR_CALLOC(fslen + uintStrlen, char); sprintf(*strP, "%s%s: %d", prefix, fs, nio->lineSkip); break; case nrrdField_byte_skip: *strP = AIR_CALLOC(fslen + uintStrlen, char); sprintf(*strP, "%s%s: %ld", prefix, fs, nio->byteSkip); break; case nrrdField_sample_units: strtmp = airOneLinify(airStrdup(nrrd->sampleUnits)); *strP = AIR_CALLOC(fslen + strlen(strtmp), char); sprintf(*strP, "%s%s: \"%s\"", prefix, fs, strtmp); airFree(strtmp); strtmp = NULL; break; case nrrdField_space_units: fdlen = 0; for (ii=0; ii<nrrd->spaceDim; ii++) { /* The "2*" is because at worst every character needs escaping. See note in formatNRRD.c about how even though its not part of the format, we have worst-case scenario of having to escape a space units which is nothing but ". The "+ 3" for the |" "| between each part */ fdlen += 2*airStrlen(nrrd->spaceUnits[ii]) + 3; } fdlen += 1; /* for '\0' */ *strP = AIR_CALLOC(fslen + fdlen, char); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->spaceDim; ii++) { strcat(*strP, " \""); if (airStrlen(nrrd->spaceUnits[ii])) { _nrrdWriteEscaped(NULL, *strP, nrrd->spaceUnits[ii], "\"", _NRRD_WHITESPACE_NOTAB); } strcat(*strP, "\""); } break; case nrrdField_space_origin: *strP = AIR_CALLOC(fslen + nrrd->spaceDim*(doubleStrlen + strlen("(,) ")), char); sprintf(*strP, "%s%s: ", prefix, fs); _nrrdStrcatSpaceVector(*strP, nrrd->spaceDim, nrrd->spaceOrigin); break; case nrrdField_measurement_frame: *strP = AIR_CALLOC(fslen + (nrrd->spaceDim* nrrd->spaceDim*(doubleStrlen + strlen("(,) "))), char); sprintf(*strP, "%s%s: ", prefix, fs); for (dd=0; dd<nrrd->spaceDim; dd++) { for (ii=0; ii<nrrd->spaceDim; ii++) { colvec[ii] = nrrd->measurementFrame[dd][ii]; } _nrrdStrcatSpaceVector(*strP, nrrd->spaceDim, colvec); if (dd < nrrd->spaceDim-1) { strcat(*strP, " "); } } break; case nrrdField_data_file: /* NOTE: this comes last (nrrdField_data_file is the highest-valued member of the nrrdField* enum) because the "LIST" form of the data file specification requires that the following lines be the filenames */ /* error checking elsewhere: assumes there is data file info */ if (nio->dataFNFormat) { *strP = AIR_CALLOC(fslen + strlen(nio->dataFNFormat) + 4*uintStrlen, char); if (nio->dataFileDim == nrrd->dim-1) { sprintf(*strP, "%s%s: %s %d %d %d", prefix, fs, nio->dataFNFormat, nio->dataFNMin, nio->dataFNMax, nio->dataFNStep); } else { sprintf(*strP, "%s%s: %s %d %d %d %u", prefix, fs, nio->dataFNFormat, nio->dataFNMin, nio->dataFNMax, nio->dataFNStep, nio->dataFileDim); } } else if (nio->dataFNArr->len > 1) {