static tenFiberContext * _tenFiberContextCommonNew(const Nrrd *vol, int useDwi, double thresh, double soft, double valueMin, int ten1method, int ten2method) { char me[]="_tenFiberContextCommonNew", err[BIFF_STRLEN]; tenFiberContext *tfx; gageKind *kind; if (!( tfx = (tenFiberContext *)calloc(1, sizeof(tenFiberContext)) )) { sprintf(err, "%s: couldn't allocate new context", me); biffAdd(TEN, err); return NULL; } if (useDwi) { Nrrd *ngrad=NULL, *nbmat=NULL; double bval=0; unsigned int *skip, skipNum; tfx->useDwi = AIR_TRUE; /* default fiber type */ tfx->fiberType = tenDwiFiberTypeUnknown; if (tenDWMRIKeyValueParse(&ngrad, &nbmat, &bval, &skip, &skipNum, vol)) { sprintf(err, "%s: trouble parsing DWI info", me ); biffAdd(TEN, err); return NULL; } if (skipNum) { sprintf(err, "%s: sorry, can't do DWI skipping here", me); biffAdd(TEN, err); return NULL; } kind = tenDwiGageKindNew(); if (tenDwiGageKindSet(kind, thresh, soft, bval, valueMin, ngrad, NULL, ten1method, ten2method, 42)) { sprintf(err, "%s: trouble setting DWI kind", me); biffAdd(TEN, err); return NULL; } } else { /* it should be a tensor volume */ tfx->useDwi = AIR_FALSE; /* default fiber type */ tfx->fiberType = tenFiberTypeUnknown; if (tenTensorCheck(vol, nrrdTypeUnknown, AIR_TRUE, AIR_TRUE)) { sprintf(err, "%s: didn't get a tensor volume", me); biffAdd(TEN, err); return NULL; } kind = tenGageKind; } if ( !(tfx->gtx = gageContextNew()) || !(tfx->pvl = gagePerVolumeNew(tfx->gtx, vol, kind)) || (gagePerVolumeAttach(tfx->gtx, tfx->pvl)) ) { sprintf(err, "%s: gage trouble", me); biffMove(TEN, err, GAGE); free(tfx); return NULL; } tfx->nin = vol; tfx->ksp = nrrdKernelSpecNew(); if (nrrdKernelSpecParse(tfx->ksp, tenDefFiberKernel)) { sprintf(err, "%s: couldn't parse tenDefFiberKernel \"%s\"", me, tenDefFiberKernel); biffMove(TEN, err, NRRD); return NULL; } if (tenFiberKernelSet(tfx, tfx->ksp->kernel, tfx->ksp->parm)) { sprintf(err, "%s: couldn't set default kernel", me); biffAdd(TEN, err); return NULL; } /* looks to GK like GK says that we must set some stop criterion */ tfx->intg = tenDefFiberIntg; tfx->anisoStopType = tenDefFiberAnisoStopType; tfx->anisoSpeedType = tenAnisoUnknown; tfx->stop = 0; tfx->anisoThresh = tenDefFiberAnisoThresh; /* so I'm not using the normal default mechanism, shoot me */ tfx->anisoSpeedFunc[0] = 0; tfx->anisoSpeedFunc[1] = 0; tfx->anisoSpeedFunc[2] = 0; tfx->maxNumSteps = tenDefFiberMaxNumSteps; tfx->minNumSteps = 0; tfx->useIndexSpace = tenDefFiberUseIndexSpace; tfx->verbose = 0; tfx->stepSize = tenDefFiberStepSize; tfx->maxHalfLen = tenDefFiberMaxHalfLen; tfx->minWholeLen = 0.0; tfx->confThresh = 0.5; /* why do I even bother setting these- they'll only get read if the right tenFiberStopSet has been called, in which case they'll be set... */ tfx->minRadius = 1; /* above lament applies here as well */ tfx->minFraction = 0.5; /* and here */ tfx->wPunct = tenDefFiberWPunct; GAGE_QUERY_RESET(tfx->query); tfx->mframe[0] = vol->measurementFrame[0][0]; tfx->mframe[1] = vol->measurementFrame[1][0]; tfx->mframe[2] = vol->measurementFrame[2][0]; tfx->mframe[3] = vol->measurementFrame[0][1]; tfx->mframe[4] = vol->measurementFrame[1][1]; tfx->mframe[5] = vol->measurementFrame[2][1]; tfx->mframe[6] = vol->measurementFrame[0][2]; tfx->mframe[7] = vol->measurementFrame[1][2]; tfx->mframe[8] = vol->measurementFrame[2][2]; if (ELL_3M_EXISTS(tfx->mframe)) { tfx->mframeUse = AIR_TRUE; ELL_3M_TRANSPOSE(tfx->mframeT, tfx->mframe); } else { tfx->mframeUse = AIR_FALSE; } tfx->gageAnisoStop = NULL; tfx->gageAnisoSpeed = NULL; tfx->ten2AnisoStop = AIR_NAN; /* ... don't really see the point of initializing the ten2 stuff here; its properly done in tenFiberTraceSet() ... */ tfx->radius = AIR_NAN; return tfx; }
int main(int argc, char *argv[]) { gageKind *kind; char *me, *whatS, *err, *outS, *stackSavePath; hestParm *hparm; hestOpt *hopt = NULL; NrrdKernelSpec *k00, *k11, *k22, *kSS, *kSSblur; float pos[3], lineInfo[4]; double gmc, rangeSS[2], posSS, *scalePos; unsigned int ansLen, numSS, ninSSIdx, lineStepNum; int what, E=0, renorm, SSrenorm, SSuniform, verbose; const double *answer; Nrrd *nin, **ninSS=NULL, *nout=NULL; gageContext *ctx; gagePerVolume *pvl; limnPolyData *lpld=NULL; airArray *mop; int worldSpace; Nrrd *ngrad=NULL, *nbmat=NULL; double bval, eps; unsigned int *skip, skipNum; mop = airMopNew(); me = argv[0]; hparm = hestParmNew(); airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hparm->elideSingleOtherType = AIR_TRUE; hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "k", "kind", airTypeOther, 1, 1, &kind, NULL, "\"kind\" of volume (\"scalar\", \"vector\", or \"tensor\")", NULL, NULL, &probeKindHestCB); hestOptAdd(&hopt, "p", "x y z", airTypeFloat, 3, 3, pos, NULL, "the position in space at which to probe"); hestOptAdd(&hopt, "wsp", NULL, airTypeInt, 0, 0, &worldSpace, NULL, "if using this option, position (\"-p\") will be in world " "space, instead of index space (the default)"); hestOptAdd(&hopt, "pi", "lpld in", airTypeOther, 1, 1, &lpld, "", "input polydata (overrides \"-p\")", NULL, NULL, limnHestPolyDataLMPD); hestOptAdd(&hopt, "pl", "x y z s", airTypeFloat, 4, 4, lineInfo, "0 0 0 0", "probe along line, instead of at point. " "The \"-p\" three coords are the line start point. " "If \"s\" is zero, (x,y,z) is the line end point. " "If \"s\" is non-zero, (x,y,z) is the line direction, " "which is scaled to have length \"s\", " "and then used as the step between line samples. "); hestOptAdd(&hopt, "pln", "num", airTypeUInt, 1, 1, &lineStepNum, "0", "if non-zero, number of steps of probing to do along line, " "which overrides \"-p\" and \"-pi\""); hestOptAdd(&hopt, "v", "verbosity", airTypeInt, 1, 1, &verbose, "1", "verbosity level"); hestOptAdd(&hopt, "q", "query", airTypeString, 1, 1, &whatS, NULL, "the quantity (scalar, vector, or matrix) to learn by probing"); hestOptAdd(&hopt, "eps", "epsilon", airTypeDouble, 1, 1, &eps, "0", "if non-zero, and if query is a scalar, epsilon around probe " "location where we will do discrete differences to find the " "gradient and hessian (for debugging)"); hestOptAdd(&hopt, "k00", "kern00", airTypeOther, 1, 1, &k00, "tent", "kernel for gageKernel00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k11", "kern11", airTypeOther, 1, 1, &k11, "cubicd:1,0", "kernel for gageKernel11", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k22", "kern22", airTypeOther, 1, 1, &k22, "cubicdd:1,0", "kernel for gageKernel22", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "ssn", "SS #", airTypeUInt, 1, 1, &numSS, "0", "how many scale-space samples to evaluate, or, " "0 to turn-off all scale-space behavior"); hestOptAdd(&hopt, "ssr", "scale range", airTypeDouble, 2, 2, rangeSS, "nan nan", "range of scales in scale-space"); hestOptAdd(&hopt, "sss", "scale save path", airTypeString, 1, 1, &stackSavePath, "", "give a non-empty path string (like \"./\") to save out " "the pre-blurred volumes computed for the stack"); hestOptAdd(&hopt, "ssp", "SS pos", airTypeDouble, 1, 1, &posSS, "0", "position at which to sample in scale-space"); hestOptAdd(&hopt, "kssblur", "kernel", airTypeOther, 1, 1, &kSSblur, "dgauss:1,5", "blurring kernel, to sample scale space", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "kss", "kernel", airTypeOther, 1, 1, &kSS, "tent", "kernel for reconstructing from scale space samples", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "ssrn", "ssrn", airTypeInt, 1, 1, &SSrenorm, "0", "enable derivative normalization based on scale space"); hestOptAdd(&hopt, "ssu", NULL, airTypeInt, 0, 0, &SSuniform, NULL, "do uniform samples along sigma, and not (by default) " "samples according to the logarithm of diffusion time"); hestOptAdd(&hopt, "rn", NULL, airTypeInt, 0, 0, &renorm, NULL, "renormalize kernel weights at each new sample location. " "\"Accurate\" kernels don't need this; doing it always " "makes things go slower"); hestOptAdd(&hopt, "gmc", "min gradmag", airTypeDouble, 1, 1, &gmc, "0.0", "For curvature-based queries, use zero when gradient " "magnitude is below this"); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output array, when probing on polydata vertices"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, probeInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); what = airEnumVal(kind->enm, whatS); if (!what) { /* 0 indeed always means "unknown" for any gageKind */ fprintf(stderr, "%s: couldn't parse \"%s\" as measure of \"%s\" volume\n", me, whatS, kind->name); hestUsage(stderr, hopt, me, hparm); hestGlossary(stderr, hopt, hparm); airMopError(mop); return 1; } if (ELL_4V_LEN(lineInfo) && !lineStepNum) { fprintf(stderr, "%s: gave line info (\"-pl\") but not " "# samples (\"-pln\")", me); hestUsage(stderr, hopt, me, hparm); hestGlossary(stderr, hopt, hparm); airMopError(mop); return 1; } /* special set-up required for DWI kind */ if (!strcmp(TEN_DWI_GAGE_KIND_NAME, kind->name)) { if (tenDWMRIKeyValueParse(&ngrad, &nbmat, &bval, &skip, &skipNum, nin)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing DWI info:\n%s\n", me, err); airMopError(mop); return 1; } if (skipNum) { fprintf(stderr, "%s: sorry, can't do DWI skipping in tenDwiGage", me); airMopError(mop); return 1; } /* this could stand to use some more command-line arguments */ if (tenDwiGageKindSet(kind, 50, 1, bval, 0.001, ngrad, nbmat, tenEstimate1MethodLLS, tenEstimate2MethodQSegLLS, /* randSeed */ 7919)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing DWI info:\n%s\n", me, err); airMopError(mop); return 1; } } ansLen = kind->table[what].answerLength; /* for setting up pre-blurred scale-space samples */ if (numSS) { unsigned int vi; ninSS = AIR_CAST(Nrrd **, calloc(numSS, sizeof(Nrrd *))); scalePos = AIR_CAST(double *, calloc(numSS, sizeof(double))); if (!(ninSS && scalePos)) { fprintf(stderr, "%s: couldn't allocate ninSS", me); airMopError(mop); return 1; } for (ninSSIdx=0; ninSSIdx<numSS; ninSSIdx++) { ninSS[ninSSIdx] = nrrdNew(); airMopAdd(mop, ninSS[ninSSIdx], (airMopper)nrrdNuke, airMopAlways); } if (SSuniform) { for (vi=0; vi<numSS; vi++) { scalePos[vi] = AIR_AFFINE(0, vi, numSS-1, rangeSS[0], rangeSS[1]); } } else { double rangeTau[2], tau; rangeTau[0] = gageTauOfSig(rangeSS[0]); rangeTau[1] = gageTauOfSig(rangeSS[1]); for (vi=0; vi<numSS; vi++) { tau = AIR_AFFINE(0, vi, numSS-1, rangeTau[0], rangeTau[1]); scalePos[vi] = gageSigOfTau(tau); } } if (verbose > 2) { fprintf(stderr, "%s: sampling scale range %g--%g %suniformly:\n", me, rangeSS[0], rangeSS[1], SSuniform ? "" : "non-"); for (vi=0; vi<numSS; vi++) { fprintf(stderr, " scalePos[%u] = %g\n", vi, scalePos[vi]); } } if (gageStackBlur(ninSS, scalePos, numSS, nin, kind->baseDim, kSSblur, nrrdBoundaryBleed, AIR_TRUE, verbose)) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble pre-computing blurrings:\n%s\n", me, err); airMopError(mop); return 1; } if (airStrlen(stackSavePath)) { char fnform[AIR_STRLEN_LARGE]; sprintf(fnform, "%s/blur-%%02u.nrrd", stackSavePath); fprintf(stderr, "%s: |%s|\n", me, fnform); if (nrrdSaveMulti(fnform, AIR_CAST(const Nrrd *const *, ninSS), numSS, 0, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble saving blurrings:\n%s\n", me, err); airMopError(mop); return 1; } } } else {
int tend_estimMain(int argc, char **argv, char *me, hestParm *hparm) { int pret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; Nrrd **nin, *nin4d, *nbmat, *nterr, *nB0, *nout; char *outS, *terrS, *bmatS, *eb0S; float soft, scale, sigma; int dwiax, EE, knownB0, oldstuff, estmeth, verbose, fixneg; unsigned int ninLen, axmap[4], wlsi, *skip, skipNum, skipIdx; double valueMin, thresh; Nrrd *ngradKVP=NULL, *nbmatKVP=NULL; double bKVP, bval; tenEstimateContext *tec; hestOptAdd(&hopt, "old", NULL, airTypeInt, 0, 0, &oldstuff, NULL, "instead of the new tenEstimateContext code, use " "the old tenEstimateLinear code"); hestOptAdd(&hopt, "sigma", "sigma", airTypeFloat, 1, 1, &sigma, "nan", "Rician noise parameter"); hestOptAdd(&hopt, "v", "verbose", airTypeInt, 1, 1, &verbose, "0", "verbosity level"); hestOptAdd(&hopt, "est", "estimate method", airTypeEnum, 1, 1, &estmeth, "lls", "estimation method to use. \"lls\": linear-least squares", NULL, tenEstimate1Method); hestOptAdd(&hopt, "wlsi", "WLS iters", airTypeUInt, 1, 1, &wlsi, "1", "when using weighted-least-squares (\"-est wls\"), how " "many iterations to do after the initial weighted fit."); hestOptAdd(&hopt, "fixneg", NULL, airTypeInt, 0, 0, &fixneg, NULL, "after estimating the tensor, ensure that there are no negative " "eigenvalues by adding (to all eigenvalues) the amount by which " "the smallest is negative (corresponding to increasing the " "non-DWI image value)."); hestOptAdd(&hopt, "ee", "filename", airTypeString, 1, 1, &terrS, "", "Giving a filename here allows you to save out the tensor " "estimation error: a value which measures how much error there " "is between the tensor model and the given diffusion weighted " "measurements for each sample. By default, no such error " "calculation is saved."); hestOptAdd(&hopt, "eb", "filename", airTypeString, 1, 1, &eb0S, "", "In those cases where there is no B=0 reference image given " "(\"-knownB0 false\"), " "giving a filename here allows you to save out the B=0 image " "which is estimated from the data. By default, this image value " "is estimated but not saved."); hestOptAdd(&hopt, "t", "thresh", airTypeDouble, 1, 1, &thresh, "nan", "value at which to threshold the mean DWI value per pixel " "in order to generate the \"confidence\" mask. By default, " "the threshold value is calculated automatically, based on " "histogram analysis."); hestOptAdd(&hopt, "soft", "soft", airTypeFloat, 1, 1, &soft, "0", "how fuzzy the confidence boundary should be. By default, " "confidence boundary is perfectly sharp"); hestOptAdd(&hopt, "scale", "scale", airTypeFloat, 1, 1, &scale, "1", "After estimating the tensor, scale all of its elements " "(but not the confidence value) by this amount. Can help with " "downstream numerical precision if values are very large " "or small."); hestOptAdd(&hopt, "mv", "min val", airTypeDouble, 1, 1, &valueMin, "1.0", "minimum plausible value (especially important for linear " "least squares estimation)"); hestOptAdd(&hopt, "B", "B-list", airTypeString, 1, 1, &bmatS, NULL, "6-by-N list of B-matrices characterizing " "the diffusion weighting for each " "image. \"tend bmat\" is one source for such a matrix; see " "its usage info for specifics on how the coefficients of " "the B-matrix are ordered. " "An unadorned plain text file is a great way to " "specify the B-matrix.\n **OR**\n " "Can say just \"-B kvp\" to try to learn B matrices from " "key/value pair information in input images."); hestOptAdd(&hopt, "b", "b", airTypeDouble, 1, 1, &bval, "nan", "\"b\" diffusion-weighting factor (units of sec/mm^2)"); hestOptAdd(&hopt, "knownB0", "bool", airTypeBool, 1, 1, &knownB0, NULL, "Determines of the B=0 non-diffusion-weighted reference image " "is known, or if it has to be estimated along with the tensor " "elements.\n " "\b\bo if \"true\": in the given list of diffusion gradients or " "B-matrices, there are one or more with zero norm, which are " "simply averaged to find the B=0 reference image value\n " "\b\bo if \"false\": there may or may not be diffusion-weighted " "images among the input; the B=0 image value is going to be " "estimated along with the diffusion model"); hestOptAdd(&hopt, "i", "dwi0 dwi1", airTypeOther, 1, -1, &nin, "-", "all the diffusion-weighted images (DWIs), as seperate 3D nrrds, " "**OR**: One 4D nrrd of all DWIs stacked along axis 0", &ninLen, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output tensor volume"); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_estimInfoL); JUSTPARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); nbmat = nrrdNew(); airMopAdd(mop, nbmat, (airMopper)nrrdNuke, airMopAlways); /* figure out B-matrix */ if (strcmp("kvp", airToLower(bmatS))) { /* its NOT coming from key/value pairs */ if (!AIR_EXISTS(bval)) { fprintf(stderr, "%s: need to specify scalar b-value\n", me); airMopError(mop); return 1; } if (nrrdLoad(nbmat, bmatS, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble loading B-matrix:\n%s\n", me, err); airMopError(mop); return 1; } nin4d = nin[0]; skip = NULL; skipNum = 0; } else { /* it IS coming from key/value pairs */ if (1 != ninLen) { fprintf(stderr, "%s: require a single 4-D DWI volume for " "key/value pair based calculation of B-matrix\n", me); airMopError(mop); return 1; } if (oldstuff) { if (knownB0) { fprintf(stderr, "%s: sorry, key/value-based DWI info not compatible " "with older implementation of knownB0\n", me); airMopError(mop); return 1; } } if (tenDWMRIKeyValueParse(&ngradKVP, &nbmatKVP, &bKVP, &skip, &skipNum, nin[0])) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing DWI info:\n%s\n", me, err); airMopError(mop); return 1; } if (AIR_EXISTS(bval)) { fprintf(stderr, "%s: WARNING: key/value pair derived b-value %g " "over-riding %g from command-line", me, bKVP, bval); } bval = bKVP; if (ngradKVP) { airMopAdd(mop, ngradKVP, (airMopper)nrrdNuke, airMopAlways); if (tenBMatrixCalc(nbmat, ngradKVP)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble finding B-matrix:\n%s\n", me, err); airMopError(mop); return 1; } } else { airMopAdd(mop, nbmatKVP, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(nbmat, nbmatKVP, nrrdTypeDouble)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble converting B-matrix:\n%s\n", me, err); airMopError(mop); return 1; } } /* this will work because of the impositions of tenDWMRIKeyValueParse */ dwiax = ((nrrdKindList == nin[0]->axis[0].kind || nrrdKindVector == nin[0]->axis[0].kind) ? 0 : ((nrrdKindList == nin[0]->axis[1].kind || nrrdKindVector == nin[0]->axis[1].kind) ? 1 : ((nrrdKindList == nin[0]->axis[2].kind || nrrdKindVector == nin[0]->axis[2].kind) ? 2 : 3))); if (0 == dwiax) { nin4d = nin[0]; } else { axmap[0] = dwiax; axmap[1] = 1 > dwiax ? 1 : 0; axmap[2] = 2 > dwiax ? 2 : 1; axmap[3] = 3 > dwiax ? 3 : 2; nin4d = nrrdNew(); airMopAdd(mop, nin4d, (airMopper)nrrdNuke, airMopAlways); if (nrrdAxesPermute(nin4d, nin[0], axmap)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble creating DWI volume:\n%s\n", me, err); airMopError(mop); return 1; } } } nterr = NULL; nB0 = NULL; if (!oldstuff) { if (1 != ninLen) { fprintf(stderr, "%s: sorry, currently need single 4D volume " "for new implementation\n", me); airMopError(mop); return 1; } if (!AIR_EXISTS(thresh)) { if (tend_estimThresholdFind(&thresh, nbmat, nin4d)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble finding threshold:\n%s\n", me, err); airMopError(mop); return 1; } /* HACK to lower threshold a titch */ thresh *= 0.93; fprintf(stderr, "%s: using mean DWI threshold %g\n", me, thresh); } tec = tenEstimateContextNew(); tec->progress = AIR_TRUE; airMopAdd(mop, tec, (airMopper)tenEstimateContextNix, airMopAlways); EE = 0; if (!EE) tenEstimateVerboseSet(tec, verbose); if (!EE) tenEstimateNegEvalShiftSet(tec, fixneg); if (!EE) EE |= tenEstimateMethodSet(tec, estmeth); if (!EE) EE |= tenEstimateBMatricesSet(tec, nbmat, bval, !knownB0); if (!EE) EE |= tenEstimateValueMinSet(tec, valueMin); for (skipIdx=0; skipIdx<skipNum; skipIdx++) { /* fprintf(stderr, "%s: skipping %u\n", me, skip[skipIdx]); */ if (!EE) EE |= tenEstimateSkipSet(tec, skip[skipIdx], AIR_TRUE); } switch(estmeth) { case tenEstimate1MethodLLS: if (airStrlen(terrS)) { tec->recordErrorLogDwi = AIR_TRUE; /* tec->recordErrorDwi = AIR_TRUE; */ } break; case tenEstimate1MethodNLS: if (airStrlen(terrS)) { tec->recordErrorDwi = AIR_TRUE; } break; case tenEstimate1MethodWLS: if (!EE) tec->WLSIterNum = wlsi; if (airStrlen(terrS)) { tec->recordErrorDwi = AIR_TRUE; } break; case tenEstimate1MethodMLE: if (!(AIR_EXISTS(sigma) && sigma > 0.0)) { fprintf(stderr, "%s: can't do %s w/out sigma > 0 (not %g)\n", me, airEnumStr(tenEstimate1Method, tenEstimate1MethodMLE), sigma); airMopError(mop); return 1; } if (!EE) EE |= tenEstimateSigmaSet(tec, sigma); if (airStrlen(terrS)) { tec->recordLikelihoodDwi = AIR_TRUE; } break; } if (!EE) EE |= tenEstimateThresholdSet(tec, thresh, soft); if (!EE) EE |= tenEstimateUpdate(tec); if (EE) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble setting up estimation:\n%s\n", me, err); airMopError(mop); return 1; } if (tenEstimate1TensorVolume4D(tec, nout, &nB0, airStrlen(terrS) ? &nterr : NULL, nin4d, nrrdTypeFloat)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble doing estimation:\n%s\n", me, err); airMopError(mop); return 1; } if (airStrlen(terrS)) { airMopAdd(mop, nterr, (airMopper)nrrdNuke, airMopAlways); } } else { EE = 0; if (1 == ninLen) { EE = tenEstimateLinear4D(nout, airStrlen(terrS) ? &nterr : NULL, &nB0, nin4d, nbmat, knownB0, thresh, soft, bval); } else { EE = tenEstimateLinear3D(nout, airStrlen(terrS) ? &nterr : NULL, &nB0, (const Nrrd**)nin, ninLen, nbmat, knownB0, thresh, soft, bval); } if (EE) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble making tensor volume:\n%s\n", me, err); airMopError(mop); return 1; } } if (nterr) { /* it was allocated by tenEstimate*, we have to clean it up */ airMopAdd(mop, nterr, (airMopper)nrrdNuke, airMopAlways); } if (nB0) { /* it was allocated by tenEstimate*, we have to clean it up */ airMopAdd(mop, nB0, (airMopper)nrrdNuke, airMopAlways); } if (1 != scale) { if (tenSizeScale(nout, nout, scale)) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble doing scaling:\n%s\n", me, err); airMopError(mop); return 1; } } if (nterr) { if (nrrdSave(terrS, nterr, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing error image:\n%s\n", me, err); airMopError(mop); return 1; } } if (!knownB0 && airStrlen(eb0S)) { if (nrrdSave(eb0S, nB0, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing estimated B=0 image:\n%s\n", me, err); airMopError(mop); return 1; } } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; }
int tenExperSpecFromKeyValueSet(tenExperSpec *espec, const Nrrd *ndwi) { static const char me[]="tenExperSpecFromKeyValueSet"; unsigned int *skip, skipNum, ii, imgNum, dwiax; Nrrd *ngrad, *nbmat; airArray *mop; double len, singleBval, *bval, *grad; for (dwiax=0; dwiax<ndwi->dim; dwiax++) { if (nrrdKindList == ndwi->axis[dwiax].kind || nrrdKindVector == ndwi->axis[dwiax].kind) { break; } } if (ndwi->dim == dwiax) { biffAddf(TEN, "%s: need dwis to have a kind %s or %s axis", me, airEnumStr(nrrdKind, nrrdKindList), airEnumStr(nrrdKind, nrrdKindVector)); return 1; } else { if (0 != dwiax) { biffAddf(TEN, "%s: need dwis (kind %s or %s) along axis 0, not %u", me, airEnumStr(nrrdKind, nrrdKindList), airEnumStr(nrrdKind, nrrdKindVector), dwiax); return 1; } } for (ii=dwiax+1; ii<ndwi->dim; ii++) { if (nrrdKindList == ndwi->axis[ii].kind || nrrdKindVector == ndwi->axis[ii].kind) { break; } } if (ii < ndwi->dim) { biffAddf(TEN, "%s: saw on %u another %s or %s kind axis, after 0", me, ii, airEnumStr(nrrdKind, nrrdKindList), airEnumStr(nrrdKind, nrrdKindVector)); return 1; } if (tenDWMRIKeyValueParse(&ngrad, &nbmat, &singleBval, &skip, &skipNum, ndwi)) { biffAddf(TEN, "%s: trouble parsing DWI info from key/value pairs", me); return 1; } mop = airMopNew(); if (ngrad) { airMopAdd(mop, ngrad, (airMopper)nrrdNuke, airMopAlways); } if (nbmat) { airMopAdd(mop, nbmat, (airMopper)nrrdNuke, airMopAlways); } if (skip) { airMopAdd(mop, skip, airFree, airMopAlways); } if (nbmat) { biffAddf(TEN, "%s: sorry, currently can't handle B-matrices here", me); airMopError(mop); return 1; } if (skipNum) { biffAddf(TEN, "%s: sorry, currently can't handle skipping (%u) here", me, skipNum); airMopError(mop); return 1; } imgNum = ngrad->axis[1].size; bval = AIR_CALLOC(imgNum, double); airMopAdd(mop, bval, airFree, airMopAlways); grad = AIR_CAST(double *, ngrad->data); for (ii=0; ii<imgNum; ii++) { len = ELL_3V_LEN(grad + 3*ii); bval[ii] = singleBval*len*len; if (len) { ELL_3V_SCALE(grad + 3*ii, 1/len, grad + 3*ii); } else { ELL_3V_SET(grad + 3*ii, 0, 0, -1); } } if (tenExperSpecGradBValSet(espec, AIR_FALSE, bval, grad, imgNum)) { biffAddf(TEN, "%s: trouble", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; }
int tend_epiregMain(int argc, const char **argv, const char *me, hestParm *hparm) { int pret, rret; hestOpt *hopt = NULL; char *perr, *err; airArray *mop; char *outS, *buff; char *gradS; NrrdKernelSpec *ksp; Nrrd **nin, **nout3D, *nout4D, *ngrad, *ngradKVP, *nbmatKVP; unsigned int ni, ninLen, *skip, skipNum; int ref, noverbose, progress, nocc, baseNum; float bw[2], thr, fitFrac; double bvalue; hestOptAdd(&hopt, "i", "dwi0 dwi1", airTypeOther, 1, -1, &nin, NULL, "all the diffusion-weighted images (DWIs), as separate 3D nrrds, " "**OR**: one 4D nrrd of all DWIs stacked along axis 0", &ninLen, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "g", "grads", airTypeString, 1, 1, &gradS, NULL, "array of gradient directions, in the same order as the " "associated DWIs were given to \"-i\", " "**OR** \"-g kvp\" signifies that gradient directions should " "be read from the key/value pairs of the DWI", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "r", "reference", airTypeInt, 1, 1, &ref, "-1", "which of the DW volumes (zero-based numbering) should be used " "as the standard, to which all other images are transformed. " "Using -1 (the default) means that 9 intrinsic parameters " "governing the relationship between the gradient direction " "and the resulting distortion are estimated and fitted, " "ensuring good registration with the non-diffusion-weighted " "T2 image (which is never explicitly used in registration). " "Otherwise, by picking a specific DWI, no distortion parameter " "estimation is done. "); hestOptAdd(&hopt, "nv", NULL, airTypeInt, 0, 0, &noverbose, NULL, "turn OFF verbose mode, and " "have no idea what stage processing is at."); hestOptAdd(&hopt, "p", NULL, airTypeInt, 0, 0, &progress, NULL, "save out intermediate steps of processing"); hestOptAdd(&hopt, "bw", "x,y blur", airTypeFloat, 2, 2, bw, "1.0 2.0", "standard devs in X and Y directions of gaussian filter used " "to blur the DWIs prior to doing segmentation. This blurring " "does not effect the final resampling of registered DWIs. " "Use \"0.0 0.0\" to say \"no blurring\""); hestOptAdd(&hopt, "t", "DWI thresh", airTypeFloat, 1, 1, &thr, "nan", "Threshold value to use on DWIs, " "to do initial separation of brain and non-brain. By default, " "the threshold is determined automatically by histogram " "analysis. "); hestOptAdd(&hopt, "ncc", NULL, airTypeInt, 0, 0, &nocc, NULL, "do *NOT* do connected component (CC) analysis, after " "thresholding and before moment calculation. Doing CC analysis " "usually gives better results because it converts the " "thresholding output into something much closer to a " "real segmentation"); hestOptAdd(&hopt, "f", "fit frac", airTypeFloat, 1, 1, &fitFrac, "0.70", "(only meaningful with \"-r -1\") When doing linear fitting " "of the intrinsic distortion parameters, it is good " "to ignore the slices for which the segmentation was poor. A " "heuristic is used to rank the slices according to segmentation " "quality. This option controls how many of the (best) slices " "contribute to the fitting. Use \"0\" to disable distortion " "parameter fitting. "); hestOptAdd(&hopt, "k", "kernel", airTypeOther, 1, 1, &ksp, "cubic:0,0.5", "kernel for resampling DWIs along the phase-encoding " "direction during final registration stage", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "s", "start #", airTypeInt, 1, 1, &baseNum, "1", "first number to use in numbered sequence of output files."); hestOptAdd(&hopt, "o", "output/prefix", airTypeString, 1, 1, &outS, "-", "For separate 3D DWI volume inputs: prefix for output filenames; " "will save out one (registered) " "DWI for each input DWI, using the same type as the input. " "**OR**: For single 4D DWI input: output file name. "); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); USAGE(_tend_epiregInfoL); JUSTPARSE(); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (strcmp("kvp", gradS)) { /* they're NOT coming from key/value pairs */ if (nrrdLoad(ngrad=nrrdNew(), gradS, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble loading gradient list:\n%s\n", me, err); airMopError(mop); return 1; } } else { if (1 != ninLen) { fprintf(stderr, "%s: can do key/value pairs only from single nrrd", me); airMopError(mop); return 1; } /* they are coming from key/value pairs */ if (tenDWMRIKeyValueParse(&ngradKVP, &nbmatKVP, &bvalue, &skip, &skipNum, nin[0])) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing gradient list:\n%s\n", me, err); airMopError(mop); return 1; } if (nbmatKVP) { fprintf(stderr, "%s: sorry, can only use gradients, not b-matrices", me); airMopError(mop); return 1; } ngrad = ngradKVP; } airMopAdd(mop, ngrad, (airMopper)nrrdNuke, airMopAlways); nout3D = AIR_CALLOC(ninLen, Nrrd *); airMopAdd(mop, nout3D, airFree, airMopAlways); nout4D = nrrdNew(); airMopAdd(mop, nout4D, (airMopper)nrrdNuke, airMopAlways); buff = AIR_CALLOC(airStrlen(outS) + 10, char); airMopAdd(mop, buff, airFree, airMopAlways); if (!( nout3D && nout4D && buff )) { fprintf(stderr, "%s: couldn't allocate buffers", me); airMopError(mop); return 1; } for (ni=0; ni<ninLen; ni++) { nout3D[ni]=nrrdNew(); airMopAdd(mop, nout3D[ni], (airMopper)nrrdNuke, airMopAlways); } if (1 == ninLen) { rret = tenEpiRegister4D(nout4D, nin[0], ngrad, ref, bw[0], bw[1], fitFrac, thr, !nocc, ksp->kernel, ksp->parm, progress, !noverbose); } else { rret = tenEpiRegister3D(nout3D, nin, ninLen, ngrad, ref, bw[0], bw[1], fitFrac, thr, !nocc, ksp->kernel, ksp->parm, progress, !noverbose); } if (rret) { airMopAdd(mop, err=biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble doing epireg:\n%s\n", me, err); airMopError(mop); return 1; } if (1 == ninLen) { if (nrrdSave(outS, nout4D, NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing \"%s\":\n%s\n", me, outS, err); airMopError(mop); return 1; } } else { for (ni=0; ni<ninLen; ni++) { if (ninLen+baseNum > 99) { sprintf(buff, "%s%05d.nrrd", outS, ni+baseNum); } else if (ninLen+baseNum > 9) { sprintf(buff, "%s%02d.nrrd", outS, ni+baseNum); } else { sprintf(buff, "%s%d.nrrd", outS, ni+baseNum); } if (nrrdSave(buff, nout3D[ni], NULL)) { airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble writing \"%s\":\n%s\n", me, buff, err); airMopError(mop); return 1; } } } airMopOkay(mop); return 0; }
int main(int argc, const char *argv[]) { gageKind *kind; const char *me; char *whatS, *err, *outS, *stackReadFormat, *stackSaveFormat; hestParm *hparm; hestOpt *hopt = NULL; NrrdKernelSpec *k00, *k11, *k22, *kSS, *kSSblur; int what, E=0, renorm, SSuniform, SSoptim, verbose, zeroZ, orientationFromSpacing, SSnormd; unsigned int iBaseDim, oBaseDim, axi, numSS, ninSSIdx, seed; const double *answer; Nrrd *nin, *nout, **ninSS=NULL; Nrrd *ngrad=NULL, *nbmat=NULL; size_t ai, ansLen, idx, xi, yi, zi, six, siy, siz, sox, soy, soz; double bval=0, gmc, rangeSS[2], wrlSS, idxSS=AIR_NAN, dsix, dsiy, dsiz, dsox, dsoy, dsoz; gageContext *ctx; gagePerVolume *pvl=NULL; double t0, t1, x, y, z, scale[3], rscl[3], min[3], maxOut[3], maxIn[3]; airArray *mop; unsigned int hackZi, *skip, skipNum; double (*ins)(void *v, size_t I, double d); gageStackBlurParm *sbp; char hackKeyStr[]="TEEM_VPROBE_HACK_ZI", *hackValStr; int otype, hackSet; char stmp[4][AIR_STRLEN_SMALL]; me = argv[0]; /* parse environment variables first, in case they break nrrdDefault* or nrrdState* variables in a way that nrrdSanity() should see */ nrrdDefaultGetenv(); nrrdStateGetenv(); /* no harm done in making sure we're sane */ if (!nrrdSanity()) { fprintf(stderr, "******************************************\n"); fprintf(stderr, "******************************************\n"); fprintf(stderr, "\n"); fprintf(stderr, " %s: nrrd sanity check FAILED.\n", me); fprintf(stderr, "\n"); fprintf(stderr, " This means that either nrrd can't work on this " "platform, or (more likely)\n"); fprintf(stderr, " there was an error in the compilation options " "and variable definitions\n"); fprintf(stderr, " for how Teem was built here.\n"); fprintf(stderr, "\n"); fprintf(stderr, " %s\n", err = biffGetDone(NRRD)); fprintf(stderr, "\n"); fprintf(stderr, "******************************************\n"); fprintf(stderr, "******************************************\n"); free(err); return 1; } mop = airMopNew(); hparm = hestParmNew(); airMopAdd(mop, hparm, AIR_CAST(airMopper, hestParmFree), airMopAlways); hparm->elideSingleOtherType = AIR_TRUE; hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "k", "kind", airTypeOther, 1, 1, &kind, NULL, "\"kind\" of volume (\"scalar\", \"vector\", " "\"tensor\", or \"dwi\")", NULL, NULL, meetHestGageKind); hestOptAdd(&hopt, "v", "verbosity", airTypeInt, 1, 1, &verbose, "1", "verbosity level"); hestOptAdd(&hopt, "q", "query", airTypeString, 1, 1, &whatS, NULL, "the quantity (scalar, vector, or matrix) to learn by probing"); hestOptAdd(&hopt, "s", "sclX sclY sxlZ", airTypeDouble, 3, 3, scale, "1.0 1.0 1.0", "scaling factor for resampling on each axis " "(>1.0 : supersampling)"); hestOptAdd(&hopt, "k00", "kern00", airTypeOther, 1, 1, &k00, "tent", "kernel for gageKernel00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k11", "kern11", airTypeOther, 1, 1, &k11, "cubicd:1,0", "kernel for gageKernel11", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k22", "kern22", airTypeOther, 1, 1, &k22, "cubicdd:1,0", "kernel for gageKernel22", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "seed", "N", airTypeUInt, 1, 1, &seed, "42", "RNG seed; mostly for debugging"); hestOptAdd(&hopt, "zz", "bool", airTypeBool, 1, 1, &zeroZ, "false", "enable \"zeroZ\" behavior in gage that partially " "implements working with 3D images as if they are 2D"); hestOptAdd(&hopt, "ssn", "SS #", airTypeUInt, 1, 1, &numSS, "0", "how many scale-space samples to evaluate, or, " "0 to turn-off all scale-space behavior"); hestOptAdd(&hopt, "ssr", "scale range", airTypeDouble, 2, 2, rangeSS, "nan nan", "range of scales in scale-space"); hestOptAdd(&hopt, "ssrf", "SS read format", airTypeString, 1, 1, &stackReadFormat, "", "printf-style format (including a \"%u\") for the " "filenames from which to *read* " "pre-blurred volumes computed for the stack"); hestOptAdd(&hopt, "sssf", "SS save format", airTypeString, 1, 1, &stackSaveFormat, "", "printf-style format (including a \"%u\") for the " "filenames in which to *save* " "pre-blurred volumes computed for the stack"); hestOptAdd(&hopt, "ssw", "SS pos", airTypeDouble, 1, 1, &wrlSS, "0", "\"world\"-space position (true sigma) " "at which to sample in scale-space"); hestOptAdd(&hopt, "kssb", "kernel", airTypeOther, 1, 1, &kSSblur, "dgauss:1,5", "blurring kernel, to sample scale space", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "kssr", "kernel", airTypeOther, 1, 1, &kSS, "hermite", "kernel for reconstructing from scale space samples", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "ssu", NULL, airTypeInt, 0, 0, &SSuniform, NULL, "do uniform samples along sigma, and not (by default) " "samples according to the effective diffusion scale"); hestOptAdd(&hopt, "sso", NULL, airTypeInt, 0, 0, &SSoptim, NULL, "if not using \"-ssu\", use pre-computed optimal " "sigmas when possible"); hestOptAdd(&hopt, "ssnd", NULL, airTypeInt, 0, 0, &SSnormd, NULL, "normalize derivatives by scale"); hestOptAdd(&hopt, "rn", NULL, airTypeInt, 0, 0, &renorm, NULL, "renormalize kernel weights at each new sample location. " "\"Accurate\" kernels don't need this; doing it always " "makes things go slower"); hestOptAdd(&hopt, "gmc", "min gradmag", airTypeDouble, 1, 1, &gmc, "0.0", "For curvature-based queries, use zero when gradient " "magnitude is below this"); hestOptAdd(&hopt, "ofs", "ofs", airTypeInt, 0, 0, &orientationFromSpacing, NULL, "If only per-axis spacing is available, use that to " "contrive full orientation info"); hestOptAdd(&hopt, "t", "type", airTypeEnum, 1, 1, &otype, "float", "type of output volume", NULL, nrrdType); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output volume"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, probeInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, AIR_CAST(airMopper, hestOptFree), airMopAlways); airMopAdd(mop, hopt, AIR_CAST(airMopper, hestParseFree), airMopAlways); what = airEnumVal(kind->enm, whatS); if (!what) { /* 0 indeed always means "unknown" for any gageKind */ fprintf(stderr, "%s: couldn't parse \"%s\" as measure of \"%s\" volume\n", me, whatS, kind->name); hestUsage(stderr, hopt, me, hparm); hestGlossary(stderr, hopt, hparm); airMopError(mop); return 1; } /* special set-up required for DWI kind */ if (!strcmp(TEN_DWI_GAGE_KIND_NAME, kind->name)) { if (tenDWMRIKeyValueParse(&ngrad, &nbmat, &bval, &skip, &skipNum, nin)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing DWI info:\n%s\n", me, err); airMopError(mop); return 1; } if (skipNum) { fprintf(stderr, "%s: sorry, can't do DWI skipping in tenDwiGage", me); airMopError(mop); return 1; } /* this could stand to use some more command-line arguments */ if (tenDwiGageKindSet(kind, 50, 1, bval, 0.001, ngrad, nbmat, tenEstimate1MethodLLS, tenEstimate2MethodQSegLLS, seed)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing DWI info:\n%s\n", me, err); airMopError(mop); return 1; } } /* for setting up pre-blurred scale-space samples */ if (numSS) { unsigned int vi; sbp = gageStackBlurParmNew(); airMopAdd(mop, sbp, (airMopper)gageStackBlurParmNix, airMopAlways); ninSS = AIR_CAST(Nrrd **, calloc(numSS, sizeof(Nrrd *))); if (!ninSS) { fprintf(stderr, "%s: couldn't allocate ninSS", me); airMopError(mop); return 1; } for (ninSSIdx=0; ninSSIdx<numSS; ninSSIdx++) { ninSS[ninSSIdx] = nrrdNew(); airMopAdd(mop, ninSS[ninSSIdx], (airMopper)nrrdNuke, airMopAlways); } if (gageStackBlurParmScaleSet(sbp, numSS, rangeSS[0], rangeSS[1], SSuniform, SSoptim) || gageStackBlurParmKernelSet(sbp, kSSblur) || gageStackBlurParmRenormalizeSet(sbp, AIR_TRUE) || gageStackBlurParmBoundarySet(sbp, nrrdBoundaryBleed, AIR_NAN) || gageStackBlurParmVerboseSet(sbp, verbose)) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble with stack blur info:\n%s\n", me, err); airMopError(mop); return 1; } if (airStrlen(stackReadFormat)) { if (nrrdLoadMulti(ninSS, numSS, stackReadFormat, 0, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble loading blurrings:\n%s\n", me, err); airMopError(mop); return 1; } if (gageStackBlurCheck(AIR_CAST(const Nrrd *const*, ninSS), sbp, nin, kind)) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } } else { if (gageStackBlur(ninSS, sbp, nin, kind)) {
int main(int argc, char *argv[]) { gageKind *kind; char *me, *outS, *whatS, *err, hackKeyStr[]="TEEM_VPROBE_HACK_ZI", *hackValStr, *stackSavePath; hestParm *hparm; hestOpt *hopt = NULL; NrrdKernelSpec *k00, *k11, *k22, *kSS, *kSSblur; int what, E=0, otype, renorm, hackSet, SSrenorm, SSuniform, verbose; unsigned int iBaseDim, oBaseDim, axi, numSS, ninSSIdx, seed; const double *answer; Nrrd *nin, *nout, **ninSS=NULL; Nrrd *ngrad=NULL, *nbmat=NULL; size_t ai, ansLen, idx, xi, yi, zi, six, siy, siz, sox, soy, soz; double bval=0, gmc, rangeSS[2], wrlSS, idxSS=AIR_NAN, *scalePos; gageContext *ctx; gagePerVolume *pvl=NULL; double t0, t1, x, y, z, scale[3], rscl[3], min[3], maxOut[3], maxIn[3]; airArray *mop; unsigned int hackZi, *skip, skipNum; double (*ins)(void *v, size_t I, double d); mop = airMopNew(); me = argv[0]; hparm = hestParmNew(); airMopAdd(mop, hparm, AIR_CAST(airMopper, hestParmFree), airMopAlways); hparm->elideSingleOtherType = AIR_TRUE; hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "k", "kind", airTypeOther, 1, 1, &kind, NULL, "\"kind\" of volume (\"scalar\", \"vector\", " "\"tensor\", or \"dwi\")", NULL, NULL, &probeKindHestCB); hestOptAdd(&hopt, "v", "verbosity", airTypeInt, 1, 1, &verbose, "1", "verbosity level"); hestOptAdd(&hopt, "q", "query", airTypeString, 1, 1, &whatS, NULL, "the quantity (scalar, vector, or matrix) to learn by probing"); hestOptAdd(&hopt, "s", "sclX sclY sxlZ", airTypeDouble, 3, 3, scale, "1.0 1.0 1.0", "scaling factor for resampling on each axis " "(>1.0 : supersampling)"); hestOptAdd(&hopt, "k00", "kern00", airTypeOther, 1, 1, &k00, "tent", "kernel for gageKernel00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k11", "kern11", airTypeOther, 1, 1, &k11, "cubicd:1,0", "kernel for gageKernel11", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k22", "kern22", airTypeOther, 1, 1, &k22, "cubicdd:1,0", "kernel for gageKernel22", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "seed", "N", airTypeUInt, 1, 1, &seed, "42", "RNG seed; mostly for debugging"); hestOptAdd(&hopt, "ssn", "SS #", airTypeUInt, 1, 1, &numSS, "0", "how many scale-space samples to evaluate, or, " "0 to turn-off all scale-space behavior"); hestOptAdd(&hopt, "ssr", "scale range", airTypeDouble, 2, 2, rangeSS, "nan nan", "range of scales in scale-space"); hestOptAdd(&hopt, "sss", "scale save path", airTypeString, 1, 1, &stackSavePath, "", "give a non-empty path string (like \"./\") to save out " "the pre-blurred volumes computed for the stack"); hestOptAdd(&hopt, "ssw", "SS pos", airTypeDouble, 1, 1, &wrlSS, "0", "\"world\"-space position (true sigma) " "at which to sample in scale-space"); hestOptAdd(&hopt, "kssblur", "kernel", airTypeOther, 1, 1, &kSSblur, "dgauss:1,5", "blurring kernel, to sample scale space", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "kss", "kernel", airTypeOther, 1, 1, &kSS, "tent", "kernel for reconstructing from scale space samples", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "ssrn", "ssrn", airTypeInt, 1, 1, &SSrenorm, "0", "enable derivative normalization based on scale space"); hestOptAdd(&hopt, "ssu", NULL, airTypeInt, 0, 0, &SSuniform, NULL, "do uniform samples along sigma, and not (by default) " "samples according to the logarithm of diffusion time"); hestOptAdd(&hopt, "rn", NULL, airTypeInt, 0, 0, &renorm, NULL, "renormalize kernel weights at each new sample location. " "\"Accurate\" kernels don't need this; doing it always " "makes things go slower"); hestOptAdd(&hopt, "gmc", "min gradmag", airTypeDouble, 1, 1, &gmc, "0.0", "For curvature-based queries, use zero when gradient " "magnitude is below this"); hestOptAdd(&hopt, "t", "type", airTypeEnum, 1, 1, &otype, "float", "type of output volume", NULL, nrrdType); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output volume"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, probeInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, AIR_CAST(airMopper, hestOptFree), airMopAlways); airMopAdd(mop, hopt, AIR_CAST(airMopper, hestParseFree), airMopAlways); what = airEnumVal(kind->enm, whatS); if (!what) { /* 0 indeed always means "unknown" for any gageKind */ fprintf(stderr, "%s: couldn't parse \"%s\" as measure of \"%s\" volume\n", me, whatS, kind->name); hestUsage(stderr, hopt, me, hparm); hestGlossary(stderr, hopt, hparm); airMopError(mop); return 1; } /* special set-up required for DWI kind */ if (!strcmp(TEN_DWI_GAGE_KIND_NAME, kind->name)) { if (tenDWMRIKeyValueParse(&ngrad, &nbmat, &bval, &skip, &skipNum, nin)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing DWI info:\n%s\n", me, err); airMopError(mop); return 1; } if (skipNum) { fprintf(stderr, "%s: sorry, can't do DWI skipping in tenDwiGage", me); airMopError(mop); return 1; } /* this could stand to use some more command-line arguments */ if (tenDwiGageKindSet(kind, 50, 1, bval, 0.001, ngrad, nbmat, tenEstimate1MethodLLS, tenEstimate2MethodQSegLLS, seed)) { airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways); fprintf(stderr, "%s: trouble parsing DWI info:\n%s\n", me, err); airMopError(mop); return 1; } } /* for setting up pre-blurred scale-space samples */ if (numSS) { unsigned int vi; ninSS = AIR_CAST(Nrrd **, calloc(numSS, sizeof(Nrrd *))); scalePos = AIR_CAST(double *, calloc(numSS, sizeof(double))); if (!(ninSS && scalePos)) { fprintf(stderr, "%s: couldn't allocate ninSS", me); airMopError(mop); return 1; } for (ninSSIdx=0; ninSSIdx<numSS; ninSSIdx++) { ninSS[ninSSIdx] = nrrdNew(); airMopAdd(mop, ninSS[ninSSIdx], (airMopper)nrrdNuke, airMopAlways); } if (SSuniform) { for (vi=0; vi<numSS; vi++) { scalePos[vi] = AIR_AFFINE(0, vi, numSS-1, rangeSS[0], rangeSS[1]); } } else { double rangeTau[2], tau; rangeTau[0] = gageTauOfSig(rangeSS[0]); rangeTau[1] = gageTauOfSig(rangeSS[1]); for (vi=0; vi<numSS; vi++) { tau = AIR_AFFINE(0, vi, numSS-1, rangeTau[0], rangeTau[1]); scalePos[vi] = gageSigOfTau(tau); } } if (verbose > 2) { fprintf(stderr, "%s: sampling scale range %g--%g %suniformly:\n", me, rangeSS[0], rangeSS[1], SSuniform ? "" : "non-"); for (vi=0; vi<numSS; vi++) { fprintf(stderr, " scalePos[%u] = %g\n", vi, scalePos[vi]); } } if (gageStackBlur(ninSS, scalePos, numSS, nin, kind->baseDim, kSSblur, nrrdBoundaryBleed, AIR_TRUE, verbose)) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble pre-computing blurrings:\n%s\n", me, err); airMopError(mop); return 1; } if (airStrlen(stackSavePath)) { char fnform[AIR_STRLEN_LARGE]; sprintf(fnform, "%s/blur-%%02u.nrrd", stackSavePath); fprintf(stderr, "%s: |%s|\n", me, fnform); if (nrrdSaveMulti(fnform, AIR_CAST(const Nrrd *const *, ninSS), numSS, 0, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble saving blurrings:\n%s\n", me, err); airMopError(mop); return 1; } } /* there's actually work to do here, weirdly: gageProbe can either work in index space, or in world space, but vprobe has basically always been index-space-centric. For doing any kind scale/stack space hacking for which vprobe is suited, its nicer to have the stack position be in the stack's "world" space, not the (potentially non-uniform) index space. So here, we have to actually replicate work that is done by _gageProbeSpace() in converting from world to index space */ for (vi=0; vi<numSS-1; vi++) { if (AIR_IN_CL(scalePos[vi], wrlSS, scalePos[vi+1])) { idxSS = vi + AIR_AFFINE(scalePos[vi], wrlSS, scalePos[vi+1], 0, 1); if (verbose > 1) { fprintf(stderr, "%s: scale pos %g -> idx %g = %u + %g\n", me, wrlSS, idxSS, vi, AIR_AFFINE(scalePos[vi], wrlSS, scalePos[vi+1], 0, 1)); } break; } } if (vi == numSS-1) { fprintf(stderr, "%s: scale pos %g outside range %g=%g, %g=%g\n", me, wrlSS, rangeSS[0], scalePos[0], rangeSS[1], scalePos[numSS-1]); airMopError(mop); return 1; } } else {