/* ** little helper function to do pre-blurring of a given nrrd ** of the sort that might be useful for scale-space gage use ** ** nblur has to already be allocated for "blnum" Nrrd*s, AND, they all ** have to point to valid (possibly empty) Nrrds, so they can hold the ** results of blurring. "scale" is filled with the result of ** scaleCB(d_i), for "dom" evenly-spaced samples d_i between ** scldomMin and scldomMax */ int gageStackBlur(Nrrd *const nblur[], const double *scale, unsigned int blnum, const Nrrd *nin, unsigned int baseDim, const NrrdKernelSpec *_kspec, int boundary, int renormalize, int verbose) { char me[]="gageStackBlur", err[BIFF_STRLEN], val[AIR_STRLEN_LARGE], keyscl[]="scale", keykern[]="kernel"; unsigned int blidx, axi; NrrdResampleContext *rsmc; NrrdKernelSpec *kspec; airArray *mop; int E; if (!(nblur && scale && nin && _kspec)) { sprintf(err, "%s: got NULL pointer", me); biffAdd(GAGE, err); return 1; } if (!( blnum >= 2)) { sprintf(err, "%s: need blnum > 2, not %u", me, blnum); biffAdd(GAGE, err); return 1; } for (blidx=0; blidx<blnum; blidx++) { if (!AIR_EXISTS(scale[blidx])) { fprintf(stderr, "%s: scale[%u] = %g doesn't exist", me, blidx, scale[blidx]); biffAdd(GAGE, err); return 1; } if (blidx) { if (!( scale[blidx-1] < scale[blidx] )) { fprintf(stderr, "%s: scale[%u] = %g not < scale[%u] = %g", me, blidx, scale[blidx-1], blidx+1, scale[blidx]); biffAdd(GAGE, err); return 1; } } } if (3 + baseDim != nin->dim) { sprintf(err, "%s: need nin->dim %u (not %u) with baseDim %u", me, 3 + baseDim, nin->dim, baseDim); biffAdd(GAGE, err); return 1; } if (airEnumValCheck(nrrdBoundary, boundary)) { sprintf(err, "%s: %d not a valid %s value", me, boundary, nrrdBoundary->name); biffAdd(GAGE, err); return 1; } mop = airMopNew(); kspec = nrrdKernelSpecCopy(_kspec); if (!kspec) { sprintf(err, "%s: problem copying kernel spec", me); biffAdd(GAGE, err); airMopError(mop); return 1; } airMopAdd(mop, kspec, (airMopper)nrrdKernelSpecNix, airMopAlways); /* pre-allocate output Nrrds in case not already there */ for (blidx=0; blidx<blnum; blidx++) { if (!nblur[blidx]) { sprintf(err, "%s: got NULL nblur[%u]", me, blidx); biffAdd(GAGE, err); airMopError(mop); return 1; } } rsmc = nrrdResampleContextNew(); airMopAdd(mop, rsmc, (airMopper)nrrdResampleContextNix, airMopAlways); E = 0; if (!E) E |= nrrdResampleDefaultCenterSet(rsmc, nrrdDefaultCenter); if (!E) E |= nrrdResampleNrrdSet(rsmc, nin); if (baseDim) { unsigned int bai; for (bai=0; bai<baseDim; bai++) { if (!E) E |= nrrdResampleKernelSet(rsmc, bai, NULL, NULL); } } for (axi=0; axi<3; axi++) { if (!E) E |= nrrdResampleSamplesSet(rsmc, baseDim + axi, nin->axis[baseDim + axi].size); if (!E) E |= nrrdResampleRangeFullSet(rsmc, baseDim + axi); } if (!E) E |= nrrdResampleBoundarySet(rsmc, boundary); if (!E) E |= nrrdResampleTypeOutSet(rsmc, nrrdTypeDefault); if (!E) E |= nrrdResampleRenormalizeSet(rsmc, renormalize); if (E) { fprintf(stderr, "%s: trouble setting up resampling\n", me); biffAdd(GAGE, err); airMopError(mop); return 1; } for (blidx=0; blidx<blnum; blidx++) { kspec->parm[0] = scale[blidx]; for (axi=0; axi<3; axi++) { if (!E) E |= nrrdResampleKernelSet(rsmc, baseDim + axi, kspec->kernel, kspec->parm); } if (verbose) { fprintf(stderr, "%s: resampling %u of %u (scale %g) ... ", me, blidx, blnum, scale[blidx]); fflush(stderr); } if (!E) E |= nrrdResampleExecute(rsmc, nblur[blidx]); if (!E) nrrdKeyValueAdd(nblur[blidx], me, "true"); sprintf(val, "%g", scale[blidx]); if (!E) nrrdKeyValueAdd(nblur[blidx], keyscl, val); nrrdKernelSpecSprint(val, kspec); if (!E) nrrdKeyValueAdd(nblur[blidx], keykern, val); if (E) { if (verbose) { fprintf(stderr, "problem!\n"); } sprintf(err, "%s: trouble resampling %u of %u (scale %g)", me, blidx, blnum, scale[blidx]); biffAdd(GAGE, err); airMopError(mop); return 1; } if (verbose) { fprintf(stderr, "done.\n"); } } airMopOkay(mop); return 0; }
int unrrdu_resampleMain(int argc, char **argv, char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, *nout; int type, bb, pret, norenorm, older, E, defaultCenter, verbose; unsigned int scaleLen, ai, samplesOut; airArray *mop; float *scale; double padVal; NrrdResampleInfo *info; NrrdResampleContext *rsmc; NrrdKernelSpec *unuk; mop = airMopNew(); info = nrrdResampleInfoNew(); airMopAdd(mop, info, (airMopper)nrrdResampleInfoNix, airMopAlways); hparm->elideSingleOtherDefault = AIR_FALSE; hestOptAdd(&opt, "old", NULL, airTypeInt, 0, 0, &older, NULL, "instead of using the new nrrdResampleContext implementation, " "use the old nrrdSpatialResample implementation"); hestOptAdd(&opt, "s,size", "sz0", airTypeOther, 1, -1, &scale, NULL, "For each axis, information about how many samples in output:\n " "\b\bo \"=\": leave this axis completely untouched: no " "resampling whatsoever\n " "\b\bo \"x<float>\": multiply the number of input samples by " "<float>, and round to the nearest integer, to get the number " "of output samples. Use \"x1\" to resample the axis but leave " "the number of samples unchanged\n " "\b\bo \"<int>\": exact number of output samples", &scaleLen, NULL, &unrrduHestScaleCB); hestOptAdd(&opt, "k,kernel", "kern", airTypeOther, 1, 1, &unuk, "cubic:0,0.5", "The kernel to use for resampling. " "Kernels logically live in the input index space for upsampling, " "and in the output index space for downsampling. " "Possibilities include:\n " "\b\bo \"box\": nearest neighbor interpolation\n " "\b\bo \"cheap\": nearest neighbor interpolation for upsampling, " "and non-blurring sub-sampling (pick subset of input samples) " "on downsampling\n " "\b\bo \"tent\": linear interpolation\n " "\b\bo \"cubic:B,C\": Mitchell/Netravali BC-family of " "cubics:\n " "\t\t\"cubic:1,0\": B-spline; maximal blurring\n " "\t\t\"cubic:0,0.5\": Catmull-Rom; good interpolating kernel\n " "\b\bo \"quartic:A\": 1-parameter family of " "interpolating quartics (\"quartic:0.0834\" is most accurate)\n " "\b\bo \"hann:R\": Hann (cosine bell) windowed sinc, radius R\n " "\b\bo \"black:R\": Blackman windowed sinc, radius R\n " "\b\bo \"gauss:S,C\": Gaussian blurring, with standard deviation " "S and cut-off at C standard deviations", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&opt, "nrn", NULL, airTypeInt, 0, 0, &norenorm, NULL, "don't do per-pass kernel weight renormalization. " "Doing the renormalization is not a big performance hit, and " "is sometimes needed to avoid \"grating\" on non-integral " "down-sampling. Disabling the renormalization is needed for " "correct results with artificially narrow kernels. "); hestOptAdd(&opt, "b,boundary", "behavior", airTypeEnum, 1, 1, &bb, "bleed", "How to handle samples beyond the input bounds:\n " "\b\bo \"pad\": use some specified value\n " "\b\bo \"bleed\": extend border values outward\n " "\b\bo \"wrap\": wrap-around to other side", NULL, nrrdBoundary); hestOptAdd(&opt, "v,value", "value", airTypeDouble, 1, 1, &padVal, "0.0", "for \"pad\" boundary behavior, pad with this value"); hestOptAdd(&opt, "t,type", "type", airTypeOther, 1, 1, &type, "default", "type to save OUTPUT as. By default (not using this option), " "the output type is the same as the input type", NULL, NULL, &unrrduHestMaybeTypeCB); hestOptAdd(&opt, "cheap", NULL, airTypeInt, 0, 0, &(info->cheap), NULL, "DEPRECATED: the \"-k cheap\" option is the new (and more " "reliable) way to access this functionality. \"-cheap\" is " "only here for legacy use in combination with \"-old\".\n " "When downsampling (reducing number of samples), don't " "try to do correct filtering by scaling kernel to match " "new (stretched) index space; keep it in old index space. " "When used in conjunction with \"-k box\", this can implement " "subsampling which chooses every Nth value. "); hestOptAdd(&opt, "c,center", "center", airTypeEnum, 1, 1, &defaultCenter, (nrrdCenterCell == nrrdDefaultCenter ? "cell" : "node"), "(not available with \"-old\") " "default centering of axes when input nrrd " "axes don't have a known centering: \"cell\" or \"node\" ", NULL, nrrdCenter); hestOptAdd(&opt, "verbose", NULL, airTypeInt, 0, 0, &verbose, NULL, "(not available with \"-old\") " "turn on verbosity "); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_resampleInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (scaleLen != nin->dim) { fprintf(stderr, "%s: # sampling sizes (%d) != input nrrd dimension (%d)\n", me, scaleLen, nin->dim); airMopError(mop); return 1; } if (!older) { rsmc = nrrdResampleContextNew(); rsmc->verbose = verbose; airMopAdd(mop, rsmc, (airMopper)nrrdResampleContextNix, airMopAlways); E = AIR_FALSE; if (!E) E |= nrrdResampleDefaultCenterSet(rsmc, defaultCenter); if (!E) E |= nrrdResampleNrrdSet(rsmc, nin); for (ai=0; ai<nin->dim; ai++) { switch((int)scale[0 + 2*ai]) { case 0: /* no resampling */ if (!E) E |= nrrdResampleKernelSet(rsmc, ai, NULL, NULL); break; case 1: /* scaling of input # samples */ if (!E) E |= nrrdResampleKernelSet(rsmc, ai, unuk->kernel, unuk->parm); samplesOut = AIR_ROUNDUP(scale[1 + 2*ai]*nin->axis[ai].size); if (!E) E |= nrrdResampleSamplesSet(rsmc, ai, samplesOut); break; case 2: /* explicit # of samples */ if (!E) E |= nrrdResampleKernelSet(rsmc, ai, unuk->kernel, unuk->parm); samplesOut = (size_t)scale[1 + 2*ai]; if (!E) E |= nrrdResampleSamplesSet(rsmc, ai, samplesOut); break; } if (!E) E |= nrrdResampleRangeFullSet(rsmc, ai); } if (!E) E |= nrrdResampleBoundarySet(rsmc, bb); if (!E) E |= nrrdResampleTypeOutSet(rsmc, type); if (!E) E |= nrrdResamplePadValueSet(rsmc, padVal); if (!E) E |= nrrdResampleRenormalizeSet(rsmc, !norenorm); if (!E) E |= nrrdResampleExecute(rsmc, nout); if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error resampling nrrd:\n%s", me, err); airMopError(mop); return 1; } } else { for (ai=0; ai<nin->dim; ai++) { /* this may be over-written below */ info->kernel[ai] = unuk->kernel; switch((int)scale[0 + 2*ai]) { case 0: /* no resampling */ info->kernel[ai] = NULL; break; case 1: /* scaling of input # samples */ info->samples[ai] = AIR_ROUNDUP(scale[1 + 2*ai]*nin->axis[ai].size); break; case 2: /* explicit # of samples */ info->samples[ai] = (size_t)scale[1 + 2*ai]; break; } memcpy(info->parm[ai], unuk->parm, NRRD_KERNEL_PARMS_NUM*sizeof(double)); if (info->kernel[ai] && (!( AIR_EXISTS(nin->axis[ai].min) && AIR_EXISTS(nin->axis[ai].max))) ) { nrrdAxisInfoMinMaxSet(nin, ai, (nin->axis[ai].center ? nin->axis[ai].center : nrrdDefaultCenter)); } info->min[ai] = nin->axis[ai].min; info->max[ai] = nin->axis[ai].max; } info->boundary = bb; info->type = type; info->padValue = padVal; info->renormalize = !norenorm; if (nrrdSpatialResample(nout, nin, info)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error resampling nrrd:\n%s", me, err); airMopError(mop); return 1; } } SAVE(out, nout, NULL); airMopOkay(mop); return 0; }
int main(int argc, char *argv[]) { char *me, *outS; hestOpt *hopt; hestParm *hparm; airArray *mop; char *err, done[13]; Nrrd *nin, *nblur, *nout; NrrdKernelSpec *kb0, *kb1, *k00, *k11, *k22; NrrdResampleContext *rsmc; int E; unsigned int sx, sy, sz, xi, yi, zi, ai; gageContext *ctx; gagePerVolume *pvl; const double *gvec, *gmag, *evec0, *eval; double (*ins)(void *v, size_t I, double d); double (*lup)(const void *v, size_t I); double dotmax, dotpow, gmmax, evalshift, gmpow, _dotmax, _gmmax, scl, clamp; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "kb0", "kernel", airTypeOther, 1, 1, &kb0, "guass:3,5", "kernel to use for pre-process blurring", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "kb1", "kernel", airTypeOther, 1, 1, &kb1, "cubic:1.5,1,0", "kernel to use for pos-process blurring", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k00", "kernel", airTypeOther, 1, 1, &k00, "cubic:1,0", "k00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k11", "kernel", airTypeOther, 1, 1, &k11, "cubicd:1,0", "k00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k22", "kernel", airTypeOther, 1, 1, &k22, "cubicdd:1,0", "k00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "dotmax", "dot", airTypeDouble, 1, 1, &dotmax, "5", "max effective value of dot(gvec, evec0)"); hestOptAdd(&hopt, "evs", "shift", airTypeDouble, 1, 1, &evalshift, "0", "negative shift to avoid changing mostly flat regions"); hestOptAdd(&hopt, "clamp", "clamp", airTypeDouble, 1, 1, &clamp, "nan", "if it exists, data value can't be forced below this"); hestOptAdd(&hopt, "dotpow", "pow", airTypeDouble, 1, 1, &dotpow, "1", "exponent for dot"); hestOptAdd(&hopt, "gmmax", "dot", airTypeDouble, 1, 1, &gmmax, "2", "max effective value of gmag"); hestOptAdd(&hopt, "gmpow", "pow", airTypeDouble, 1, 1, &gmpow, "1", "exponent for gmag"); hestOptAdd(&hopt, "scl", "scale", airTypeDouble, 1, 1, &scl, "0.1", "how much to scale hack to decrease input value"); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, "-", "fixed volume output"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, vhInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (!( 3 == nin->dim && nrrdTypeBlock != nin->type )) { fprintf(stderr, "%s: need a 3-D scalar nrrd (not %u-D %s)", me, nin->dim, airEnumStr(nrrdType, nin->type)); airMopError(mop); return 1; } nblur = nrrdNew(); airMopAdd(mop, nblur, (airMopper)nrrdNuke, airMopAlways); if (nrrdCopy(nblur, nin)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't allocate output:\n%s", me, err); airMopError(mop); return 1; } fprintf(stderr, "%s: pre-blurring ... ", me); fflush(stderr); rsmc = nrrdResampleContextNew(); airMopAdd(mop, rsmc, (airMopper)nrrdResampleContextNix, airMopAlways); E = AIR_FALSE; if (!E) E |= nrrdResampleDefaultCenterSet(rsmc, nrrdCenterCell); if (!E) E |= nrrdResampleNrrdSet(rsmc, nin); for (ai=0; ai<3; ai++) { if (!E) E |= nrrdResampleKernelSet(rsmc, ai, kb0->kernel, kb0->parm); if (!E) E |= nrrdResampleSamplesSet(rsmc, ai, nin->axis[ai].size); if (!E) E |= nrrdResampleRangeFullSet(rsmc, ai); } if (!E) E |= nrrdResampleBoundarySet(rsmc, nrrdBoundaryBleed); if (!E) E |= nrrdResampleTypeOutSet(rsmc, nrrdTypeDefault); if (!E) E |= nrrdResampleRenormalizeSet(rsmc, AIR_TRUE); if (!E) E |= nrrdResampleExecute(rsmc, nblur); if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error resampling nrrd:\n%s", me, err); airMopError(mop); return 1; } fprintf(stderr, "done.\n"); ctx = gageContextNew(); airMopAdd(mop, ctx, (airMopper)gageContextNix, airMopAlways); gageParmSet(ctx, gageParmRenormalize, AIR_TRUE); gageParmSet(ctx, gageParmCheckIntegrals, AIR_TRUE); E = 0; if (!E) E |= !(pvl = gagePerVolumeNew(ctx, nblur, gageKindScl)); if (!E) E |= gagePerVolumeAttach(ctx, pvl); if (!E) E |= gageKernelSet(ctx, gageKernel00, k00->kernel, k00->parm); if (!E) E |= gageKernelSet(ctx, gageKernel11, k11->kernel, k11->parm); if (!E) E |= gageKernelSet(ctx, gageKernel22, k22->kernel, k22->parm); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclGradVec); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclGradMag); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclHessEvec0); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclHessEval); if (!E) E |= gageUpdate(ctx); if (E) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } gvec = gageAnswerPointer(ctx, pvl, gageSclGradVec); gmag = gageAnswerPointer(ctx, pvl, gageSclGradMag); evec0 = gageAnswerPointer(ctx, pvl, gageSclHessEvec0); eval = gageAnswerPointer(ctx, pvl, gageSclHessEval); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdCopy(nout, nin)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't allocate output:\n%s", me, err); airMopError(mop); return 1; } if (!(nout->type == nin->type && nblur->type == nin->type)) { fprintf(stderr, "%s: whoa, types (%s %s %s) not all equal\n", me, airEnumStr(nrrdType, nin->type), airEnumStr(nrrdType, nblur->type), airEnumStr(nrrdType, nout->type)); } ins = nrrdDInsert[nout->type]; lup = nrrdDLookup[nout->type]; sx = nin->axis[0].size; sy = nin->axis[1].size; sz = nin->axis[2].size; gageProbe(ctx, 0, 0, 0); _dotmax = ELL_3V_DOT(gvec, evec0); _gmmax = *gmag; fprintf(stderr, "%s: hacking ", me); fflush(stderr); for (zi=0; zi<sz; zi++) { fprintf(stderr, "%s", airDoneStr(0, zi, sz-1, done)); fflush(stderr); for (yi=0; yi<sy; yi++) { for (xi=0; xi<sx; xi++) { size_t si; double dot, evl, gm, shift, in, out, mode; gageProbe(ctx, xi, yi, zi); si = xi + sx*(yi + sy*zi); dot = ELL_3V_DOT(gvec, evec0); _dotmax = AIR_MAX(_dotmax, dot); dot = AIR_ABS(dot); dot = 1 - AIR_MIN(dot, dotmax)/dotmax; dot = pow(dot, dotpow); evl = AIR_MAX(0, eval[0] - evalshift); mode = airMode3_d(eval); evl *= AIR_AFFINE(-1, mode, 1, 0, 1); _gmmax = AIR_MAX(_gmmax, *gmag); gm = 1 - AIR_MIN(*gmag, gmmax)/gmmax; gm = pow(gm, gmpow); shift = scl*gm*evl*dot; if (AIR_EXISTS(clamp)) { in = lup(nin->data, si); out = in - shift; out = AIR_MAX(out, clamp); shift = AIR_MAX(0, in - out); } ins(nout->data, si, shift); } } } fprintf(stderr, "\n"); fprintf(stderr, "%s: max dot seen: %g\n", me, _dotmax); fprintf(stderr, "%s: max gm seen: %g\n", me, _gmmax); fprintf(stderr, "%s: post-blurring ... ", me); fflush(stderr); E = AIR_FALSE; if (!E) E |= nrrdResampleNrrdSet(rsmc, nout); for (ai=0; ai<3; ai++) { if (!E) E |= nrrdResampleKernelSet(rsmc, ai, kb1->kernel, kb1->parm); if (!E) E |= nrrdResampleSamplesSet(rsmc, ai, nout->axis[ai].size); if (!E) E |= nrrdResampleRangeFullSet(rsmc, ai); } if (!E) E |= nrrdResampleExecute(rsmc, nblur); if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error resampling nrrd:\n%s", me, err); airMopError(mop); return 1; } fprintf(stderr, "done.\n"); for (zi=0; zi<sz; zi++) { for (yi=0; yi<sy; yi++) { for (xi=0; xi<sx; xi++) { size_t si; double in, shift; si = xi + sx*(yi + sy*zi); in = lup(nin->data, si); shift = lup(nblur->data, si); ins(nout->data, si, in - shift); } } } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't save output:\n%s", me, err); airMopError(mop); return 1; } airMopOkay(mop); exit(0); }