size_t airDioRead(int fd, void *_ptr, size_t size) { size_t red, totalred; int align, min, max, flags; size_t remain, part; char *ptr; if (!( _ptr && airNoDio_okay == airDioTest(fd, _ptr, size) )) { return 0; } flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags | FDIRECT); airDioInfo(&align, &min, &max, fd); remain = size; totalred = 0; ptr = (char*)_ptr; do { part = AIR_MIN(remain, max); red = read(fd, ptr, part); totalred += red; if (red != part) { break; } ptr += red; remain -= red; } while (remain); fcntl(fd, F_SETFL, flags); return totalred; }
/* ** _baneRangeZeroCentered_Answer ** ** Unlike the last version of this function, this is conservative: we ** choose the smallest zero-centered range that includes the original ** min and max. Previously the average of the min and max magnitude ** were used. */ int _baneRangeZeroCentered_Answer(double *ominP, double *omaxP, double imin, double imax) { char me[]="_baneRangeZeroCentered_Answer", err[BIFF_STRLEN]; if (!( AIR_EXISTS(imin) && AIR_EXISTS(imax) )) { sprintf(err, "%s: imin and imax don't both exist", me); biffAdd(BANE, err); return 1; } imin = AIR_MIN(imin, 0); imax = AIR_MAX(imax, 0); /* now the signs of imin and imax aren't wrong */ *ominP = AIR_MIN(-imax, imin); *omaxP = AIR_MAX(imax, -imin); return 0; }
void incTest(char *me, int num, baneRange *range) { double *val, tmp, incParm[BANE_PARM_NUM], omin, omax, rmin, rmax; baneInc *inc; Nrrd *hist; int i, j; airSrand48(); val = (double*)malloc(num*sizeof(double)); /* from <http://www.itl.nist.gov/div898/handbook/index.htm>: the standard dev of a uniform distribution between A and B is sqrt((B-A)^2/12) */ for (j=0; j<num; j++) { tmp = AIR_AFFINE(0.0, airDrand48(), 1.0, -1.0, 1.0); /* val[j] = tmp*tmp*tmp; */ val[j] = tmp; } rmin = rmax = val[0]; for (j=0; j<num; j++) { rmin = AIR_MIN(rmin, val[j]); rmax = AIR_MAX(rmax, val[j]); } fprintf(stderr, "incTest: real min,max = %g,%g\n", rmin, rmax); for (i=1; i<baneIncLast; i++) { /* NOTE: THIS IS BROKEN !!! */ inc = baneIncNew(i, NULL, incParm); printf("%s: inclusion %s ------\n", me, inc->name); switch(i) { case baneIncAbsolute: ELL_3V_SET(incParm, -0.8, 1.5, AIR_NAN); break; case baneIncRangeRatio: ELL_3V_SET(incParm, 0.99, AIR_NAN, AIR_NAN); break; case baneIncPercentile: ELL_3V_SET(incParm, 1024, 10, AIR_NAN); break; case baneIncStdv: ELL_3V_SET(incParm, 1.0, AIR_NAN, AIR_NAN); break; } fprintf(stderr, "!%s: THIS IS BROKEN!!!\n", "incTest"); /* if (inc->passA) { for (j=0; j<num; j++) inc->process[0](hist, val[j], incParm); } if (inc->passB) { for (j=0; j<num; j++) inc->process[1](hist, val[j], incParm); } inc->ans(&omin, &omax, hist, incParm, range); */ printf(" --> (%g,%g)\n", omin, omax); } free(val); }
void _miteStageRun(miteThread *mtt, miteUser *muu) { static const char me[]="_miteStageRun"; int stageIdx, ri, rii; unsigned int txfIdx, finalIdx; miteStage *stage; mite_t *rangeData; double *dbg=NULL; finalIdx = 0; if (mtt->verbose) { dbg = muu->debug + muu->debugIdx; } for (stageIdx=0; stageIdx<mtt->stageNum; stageIdx++) { stage = &(mtt->stage[stageIdx]); if (stage->qn) { /* its a vector-valued txf domain variable */ txfIdx = stage->qn(stage->val); /* right now, we can't store vector-valued txf domain variables */ } else { /* its a scalar txf domain variable */ txfIdx = airIndexClamp(stage->min, *(stage->val), stage->max, stage->size); if (mtt->verbose) { fprintf(stderr, "!%s: %s=%g in [%g,%g]/%u -> %u\n", me, stage->label, *(stage->val), stage->min, stage->max, stage->size, txfIdx); dbg[0 + 2*stageIdx] = *(stage->val); } } finalIdx = stage->size*finalIdx + txfIdx; if (mtt->verbose) { dbg[1 + 2*stageIdx] = txfIdx; } if (stage->data) { rangeData = stage->data + stage->rangeNum*finalIdx; for (rii=0; rii<stage->rangeNum; rii++) { ri = stage->rangeIdx[rii]; switch(stage->op) { case miteStageOpMin: mtt->range[ri] = AIR_MIN(mtt->range[ri], rangeData[rii]); break; case miteStageOpMax: mtt->range[ri] = AIR_MAX(mtt->range[ri], rangeData[rii]); break; case miteStageOpAdd: mtt->range[ri] += rangeData[rii]; break; case miteStageOpMultiply: default: mtt->range[ri] *= rangeData[rii]; break; } } finalIdx = 0; } } return; }
/* ** minsmooth(x, w, M) is like min(x,M), but starting at value M-w, values ** are lowered (via erf), so that the output is asymptotic to M */ static double _nrrdTernaryOpMinSmooth(double x, double width, double max) { double tran; tran = max - width; return (tran < max /* using the function as intended */ ? (x < tran ? x : airErf((x-tran)*0.886226925452758/(max - tran))*(max - tran) + tran) : AIR_MIN(x, max)); /* transition in wrong place; revert to simple max() */ }
void pullBinsAllNeighborSet(pullContext *pctx) { /* char me[]="pullBinsAllNeighborSet"; */ pullBin *nei[3*3*3]; unsigned int neiNum, xi, yi, zi, xx, yy, zz, xmax, ymax, zmax, binIdx; int xmin, ymin, zmin; if (pctx->binSingle) { neiNum = 0; nei[neiNum++] = pctx->bin + 0; _pullBinNeighborSet(pctx->bin + 0, nei, neiNum); } else { for (zi=0; zi<pctx->binsEdge[2]; zi++) { zmin = AIR_MAX(0, (int)zi-1); zmax = AIR_MIN(zi+1, pctx->binsEdge[2]-1); for (yi=0; yi<pctx->binsEdge[1]; yi++) { ymin = AIR_MAX(0, (int)yi-1); ymax = AIR_MIN(yi+1, pctx->binsEdge[1]-1); for (xi=0; xi<pctx->binsEdge[0]; xi++) { xmin = AIR_MAX(0, (int)xi-1); xmax = AIR_MIN(xi+1, pctx->binsEdge[0]-1); neiNum = 0; for (zz=zmin; zz<=zmax; zz++) { for (yy=ymin; yy<=ymax; yy++) { for (xx=xmin; xx<=xmax; xx++) { binIdx = xx + pctx->binsEdge[0]*(yy + pctx->binsEdge[1]*zz); /* fprintf(stderr, "!%s: nei[%u](%u,%u,%u) = (%u,%u,%u) = %u\n", me, neiNum, xi, yi, zi, xx, yy, zz, binIdx); */ nei[neiNum++] = pctx->bin + binIdx; } } } _pullBinNeighborSet(pctx->bin + xi + pctx->binsEdge[0] *(yi + pctx->binsEdge[1]*zi), nei, neiNum); } } } } return; }
static int constraintSatIso(pullTask *task, pullPoint *point, double stepMax, unsigned int iterMax, /* output */ int *constrFailP) { static const char me[]="constraintSatIso"; double step, /* current step size */ val, aval, /* last and current function values */ hack, /* how to control re-tries in the context of a single for-loop, instead of a nested do-while loop */ grad[4], dir[3], len, state[1 + 1 + 3 + 3]; unsigned int iter = 0; /* 0: initial probe, 1..iterMax: probes in loop */ PROBE(val, aval, grad); SAVE(state, aval, val, grad, point->pos); hack = 1; for (iter=1; iter<=iterMax; iter++) { /* consider? http://en.wikipedia.org/wiki/Halley%27s_method */ NORMALIZE(dir, grad, len); if (!len) { /* no gradient; back off */ hack *= task->pctx->sysParm.backStepScale; RESTORE(aval, val, grad, point->pos, state); continue; } step = -val/len; /* the newton-raphson step */ step = step > 0 ? AIR_MIN(stepMax, step) : AIR_MAX(-stepMax, step); ELL_3V_SCALE_INCR(point->pos, hack*step, dir); _pullPointHistAdd(point, pullCondConstraintSatA); PROBE(val, aval, grad); if (aval <= state[0]) { /* we're no further from the root */ if (AIR_ABS(step) < stepMax*task->pctx->sysParm.constraintStepMin) { /* we have converged! */ break; } SAVE(state, aval, val, grad, point->pos); hack = 1; } else { /* oops, try again, don't update dir or len, reset val */ hack *= task->pctx->sysParm.backStepScale; RESTORE(aval, val, grad, point->pos, state); } } if (iter > iterMax) { *constrFailP = pullConstraintFailIterMaxed; } else { *constrFailP = AIR_FALSE; } return 0; }
/* ******** airArrayLenPreSet() ** ** allocates the array to hold up to given length, without ** actually changing the length. In order for this to be ** useful, this also turns on noReallocWhenSmaller ** ** NB: this used to have a "boolean" return to indicate allocation ** error, but nothing in Teem actually did the error checking. Now ** conscientious users can look at NULL-ity of a->data to detect such ** an error. */ void airArrayLenPreSet(airArray *a, unsigned int newlen) { /* char me[]="airArrayLenPreSet"; */ unsigned int newsize; void *newdata; if (!a) { return; } if (newlen == 0) { /* there is no pre-set length, turn off noReallocWhenSmaller */ a->noReallocWhenSmaller = AIR_FALSE; } else { newsize = (newlen-1)/a->incr + 1; /* fprintf(stderr, "!%s: newlen = %u, incr = %u -> newsize = %u\n", me, newlen, a->incr, newsize); fprintf(stderr, "!%s: a->size = %u, a->len = %u, a->unit = %u\n", me, a->size, a->len, a->unit); */ if (newsize > a->size) { newdata = calloc(newsize*a->incr, a->unit); /* fprintf(stderr, "!%s: a->data = %p, newdata = %p\n", me, a->data, newdata); */ if (!newdata) { free(a->data); _airSetData(a, NULL); return; } if (a->data) { memcpy(newdata, a->data, AIR_MIN(a->len*a->unit, newsize*a->incr*a->unit)); free(a->data); } _airSetData(a, newdata); a->size = newsize; } a->noReallocWhenSmaller = AIR_TRUE; } /* fprintf(stderr, "!%s: returning data %p\n", me, a->data); */ return; }
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; }
static int constraintSatHght(pullTask *task, pullPoint *point, int tang1Use, int tang2Use, int negtang1Use, int negtang2Use, double stepMax, unsigned int iterMax, int *constrFailP) { static const char me[]="constraintSatHght"; double val, grad[3], hess[9], posproj[9], negproj[9], state[1+3+9+9+9+3], hack, step, d1, d2, pdir[3], plen, pgrad[3]; #if PRAYING double _tmpv[3]={0,0,0}; #endif int havePos, haveNeg, haveNada; unsigned int iter = 0; /* 0: initial probe, 1..iterMax: probes in loop */ /* http://en.wikipedia.org/wiki/Newton%27s_method_in_optimization */ havePos = tang1Use || tang2Use; haveNeg = negtang1Use || negtang2Use; haveNada = !havePos && !haveNeg; #if PRAYING { double stpmin; /* HEY: shouldn't stpmin also be used later in this function? */ stpmin = task->pctx->voxelSizeSpace*task->pctx->sysParm.constraintStepMin; fprintf(stderr, "!%s(%u): starting at %g %g %g %g\n", me, point->idtag, point->pos[0], point->pos[1], point->pos[2], point->pos[3]); fprintf(stderr, "!%s: pt %d %d nt %d %d (nada %d) " "stepMax %g, iterMax %u\n", me, tang1Use, tang2Use, negtang1Use, negtang2Use, haveNada, stepMax, iterMax); fprintf(stderr, "!%s: stpmin = %g = voxsize %g * parm.stepmin %g\n", me, stpmin, task->pctx->voxelSizeSpace, task->pctx->sysParm.constraintStepMin); } #endif _pullPointHistAdd(point, pullCondOld); PROBE(val, grad, hess, posproj, negproj); #if PRAYING PRINT("initial probe"); #endif SAVE(state, val, grad, hess, posproj, negproj, point->pos); hack = 1; for (iter=1; iter<=iterMax; iter++) { #if PRAYING fprintf(stderr, "!%s: =============== begin iter %u\n", me, iter); #endif /* HEY: no opportunistic increase of hack? */ if (havePos || haveNada) { POSNORM(d1, d2, pdir, plen, pgrad, grad, hess, posproj); if (!plen) { /* this use to be a biff error, which got to be annoying */ *constrFailP = pullConstraintFailProjGradZeroA; return 0; } step = (d2 <= 0 ? -plen : -d1/d2); #if PRAYING fprintf(stderr, "!%s: (+) iter %u step = (%g <= 0 ? %g : %g) --> %g\n", me, iter, d2, -plen, -d1/d2, step); #endif step = step > 0 ? AIR_MIN(stepMax, step) : AIR_MAX(-stepMax, step); if (AIR_ABS(step) < stepMax*task->pctx->sysParm.constraintStepMin) { /* no further iteration needed; we're converged */ #if PRAYING fprintf(stderr, " |step| %g < %g*%g = %g ==> converged!\n", AIR_ABS(step), stepMax, task->pctx->sysParm.constraintStepMin, stepMax*task->pctx->sysParm.constraintStepMin); #endif if (!haveNeg) { break; } else { goto nextstep; } } /* else we have to take a significant step */ #if PRAYING fprintf(stderr, " -> step %g, |pdir| = %g\n", step, ELL_3V_LEN(pdir)); ELL_3V_COPY(_tmpv, point->pos); fprintf(stderr, " -> pos (%g,%g,%g,%g) += %g * %g * (%g,%g,%g)\n", point->pos[0], point->pos[1], point->pos[2], point->pos[3], hack, step, pdir[0], pdir[1], pdir[2]); #endif ELL_3V_SCALE_INCR(point->pos, hack*step, pdir); #if PRAYING ELL_3V_SUB(_tmpv, _tmpv, point->pos); fprintf(stderr, " -> moved to %g %g %g %g\n", point->pos[0], point->pos[1], point->pos[2], point->pos[3]); fprintf(stderr, " (moved %g)\n", ELL_3V_LEN(_tmpv)); #endif _pullPointHistAdd(point, pullCondConstraintSatA); PROBE(val, grad, hess, posproj, negproj); #if PRAYING fprintf(stderr, " (+) probed at (%g,%g,%g,%g)\n", point->pos[0], point->pos[1], point->pos[2], point->pos[3]); PRINT("after move"); fprintf(stderr, " val(%g,%g,%g,%g)=%g %s state[0]=%g\n", point->pos[0], point->pos[1], point->pos[2], point->pos[3], val, val <= state[0] ? "<=" : ">", state[0]); #endif if (val <= state[0]) { /* we made progress */ #if PRAYING fprintf(stderr, " (+) progress!\n"); #endif SAVE(state, val, grad, hess, posproj, negproj, point->pos); hack = 1; } else { /* oops, we went uphill instead of down; try again */ #if PRAYING fprintf(stderr, " val *increased*; backing hack from %g to %g\n", hack, hack*task->pctx->sysParm.backStepScale); #endif hack *= task->pctx->sysParm.backStepScale; RESTORE(val, grad, hess, posproj, negproj, point->pos, state); #if PRAYING fprintf(stderr, " restored to pos (%g,%g,%g,%g)\n", point->pos[0], point->pos[1], point->pos[2], point->pos[3]); #endif } } nextstep: if (haveNeg) { /* HEY: copy and paste from above, minus fluff */ NEGNORM(d1, d2, pdir, plen, pgrad, grad, hess, negproj); if (!plen && !haveNeg) { /* this use to be a biff error, which got to be annoying */ *constrFailP = pullConstraintFailProjGradZeroA; return 0; } step = (d2 <= 0 ? -plen : -d1/d2); #if PRAYING fprintf(stderr, "!%s: -+) iter %u step = (%g <= 0 ? %g : %g) --> %g\n", me, iter, d2, -plen, -d1/d2, step); #endif step = step > 0 ? AIR_MIN(stepMax, step) : AIR_MAX(-stepMax, step); if (AIR_ABS(step) < stepMax*task->pctx->sysParm.constraintStepMin) { #if PRAYING fprintf(stderr, " |step| %g < %g*%g = %g ==> converged!\n", AIR_ABS(step), stepMax, task->pctx->sysParm.constraintStepMin, stepMax*task->pctx->sysParm.constraintStepMin); #endif /* no further iteration needed; we're converged */ break; } /* else we have to take a significant step */ #if PRAYING fprintf(stderr, " -> step %g, |pdir| = %g\n", step, ELL_3V_LEN(pdir)); ELL_3V_COPY(_tmpv, point->pos); fprintf(stderr, " -> pos (%g,%g,%g,%g) += %g * %g * (%g,%g,%g)\n", point->pos[0], point->pos[1], point->pos[2], point->pos[3], hack, step, pdir[0], pdir[1], pdir[2]); #endif ELL_3V_SCALE_INCR(point->pos, hack*step, pdir); #if PRAYING ELL_3V_SUB(_tmpv, _tmpv, point->pos); fprintf(stderr, " -> moved to %g %g %g %g\n", point->pos[0], point->pos[1], point->pos[2], point->pos[3]); fprintf(stderr, " (moved %g)\n", ELL_3V_LEN(_tmpv)); #endif _pullPointHistAdd(point, pullCondConstraintSatA); PROBE(val, grad, hess, posproj, negproj); #if PRAYING fprintf(stderr, " (-) probed at (%g,%g,%g,%g)\n", point->pos[0], point->pos[1], point->pos[2], point->pos[3]); PRINT("after move"); fprintf(stderr, " val(%g,%g,%g,%g)=%g %s state[0]=%g\n", point->pos[0], point->pos[1], point->pos[2], point->pos[3], val, val >= state[0] ? ">=" : "<", state[0]); #endif if (val >= state[0]) { /* we made progress */ #if PRAYING fprintf(stderr, " (-) progress!\n"); #endif SAVE(state, val, grad, hess, posproj, negproj, point->pos); hack = 1; } else { /* oops, we went uphill instead of down; try again */ #if PRAYING fprintf(stderr, " val *increased*; backing hack from %g to %g\n", hack, hack*task->pctx->sysParm.backStepScale); #endif hack *= task->pctx->sysParm.backStepScale; RESTORE(val, grad, hess, posproj, negproj, point->pos, state); #if PRAYING fprintf(stderr, " restored to pos (%g,%g,%g,%g)\n", point->pos[0], point->pos[1], point->pos[2], point->pos[3]); #endif } } } if (iter > iterMax) { *constrFailP = pullConstraintFailIterMaxed; } else { *constrFailP = AIR_FALSE; } /* printf("!%s: %d %s\n", me, *constrFailP, *constrFailP ? "FAILED!" : "ok"); */ return 0; }
int unrrdu_affineMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; NrrdIter *minIn, *in, *maxIn, *minOut, *maxOut, *args[5]; Nrrd *nout, *ntmp=NULL; int type, E, pret, clamp; airArray *mop; unsigned int ai, nn; hestOptAdd(&opt, NULL, "minIn", airTypeOther, 1, 1, &minIn, NULL, "Lower end of input value range.", NULL, NULL, nrrdHestIter); hestOptAdd(&opt, NULL, "in", airTypeOther, 1, 1, &in, NULL, "Input value.", NULL, NULL, nrrdHestIter); hestOptAdd(&opt, NULL, "maxIn", airTypeOther, 1, 1, &maxIn, NULL, "Upper end of input value range.", NULL, NULL, nrrdHestIter); hestOptAdd(&opt, NULL, "minOut", airTypeOther, 1, 1, &minOut, NULL, "Lower end of output value range.", NULL, NULL, nrrdHestIter); hestOptAdd(&opt, NULL, "maxOut", airTypeOther, 1, 1, &maxOut, NULL, "Upper end of output value range.", NULL, NULL, nrrdHestIter); hestOptAdd(&opt, "t,type", "type", airTypeOther, 1, 1, &type, "default", "type to convert all nrrd inputs to, prior to " "doing operation. This also determines output type. " "By default (not using this option), the types of the input " "nrrds are left unchanged.", NULL, NULL, &unrrduHestMaybeTypeCB); hestOptAdd(&opt, "clamp", "bool", airTypeBool, 1, 1, &clamp, "false", "clamp output values to specified output range"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_affineInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); args[0] = minIn; args[1] = in; args[2] = maxIn; args[3] = minOut; args[4] = maxOut; nn = 0; for (ai=0; ai<5; ai++) { nn += !!args[ai]->ownNrrd; } if (nrrdTypeDefault != type) { /* they wanted to convert nrrds to some other type first */ E = 0; for (ai=0; ai<5; ai++) { if (args[ai]->ownNrrd) { if (!E) E |= nrrdConvert(ntmp=nrrdNew(), args[ai]->ownNrrd, type); if (!E) nrrdIterSetOwnNrrd(args[ai], ntmp); } } if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error converting input nrrd(s):\n%s", me, err); airMopError(mop); return 1; } } if (0 == nn) { /* actually, there are no nrrds; we represent the functionality of the previous stand-alone binary "affine" */ double valOut; valOut = AIR_AFFINE(minIn->val, in->val, maxIn->val, minOut->val, maxOut->val); if (clamp) { double mmin = AIR_MIN(minOut->val, maxOut->val); double mmax = AIR_MAX(minOut->val, maxOut->val); valOut = AIR_CLAMP(mmin, valOut, mmax); } printf("%g\n", valOut); } else { /* we have a nrrd output */ if (1 == nn && in->ownNrrd) { E = nrrdArithAffine(nout, minIn->val, in->ownNrrd, maxIn->val, minOut->val, maxOut->val, clamp); } else { E = nrrdArithIterAffine(nout, minIn, in, maxIn, minOut, maxOut, clamp); } if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error doing ternary operation:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); } airMopOkay(mop); return 0; }
int main(int argc, const char **argv) { /* stock variables */ char me[] = BKEY; hestOpt *hopt=NULL; hestParm *hparm; airArray *mop; /* variables specific to this program */ int negskip, progress; Nrrd *nref, *nin; size_t *size, ii, nn, tick, pad[2]; unsigned int axi, refCRC, gotCRC, sizeNum; char *berr, *outS[2], stmp[AIR_STRLEN_SMALL], doneStr[AIR_STRLEN_SMALL]; airRandMTState *rng; unsigned int seed, *rdata, printbytes; unsigned char *dataUC; double time0, time1; FILE *fout; /* start-up */ mop = airMopNew(); hparm = hestParmNew(); airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); /* learn things from hest */ hestOptAdd(&hopt, "seed", "N", airTypeUInt, 1, 1, &seed, "42", "seed for RNG"); hestOptAdd(&hopt, "s", "sz0", airTypeSize_t, 1, -1, &size, NULL, "sizes of desired output", &sizeNum); hestOptAdd(&hopt, "p", "pb pa", airTypeSize_t, 2, 2, pad, "0 0", "bytes of padding before, and after, the data segment " "in the written data"); hestOptAdd(&hopt, "ns", "bool", airTypeInt, 0, 0, &negskip, NULL, "skipping should be relative to end of file"); hestOptAdd(&hopt, "pb", "print", airTypeUInt, 1, 1, &printbytes, "0", "bytes to print at beginning and end of data, to help " "debug problems"); hestOptAdd(&hopt, "o", "out.data out.nhdr", airTypeString, 2, 2, outS, NULL, "output filenames of data and header"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, tskipInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); /* generate reference nrrd data */ nref = nrrdNew(); airMopAdd(mop, nref, (airMopper)nrrdNuke, airMopAlways); if (nrrdMaybeAlloc_nva(nref, nrrdTypeUInt, sizeNum, size)) { airMopAdd(mop, berr=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error allocating data: %s\n", me, berr); airMopError(mop); return 1; } rng = airRandMTStateNew(seed); airMopAdd(mop, rng, (airMopper)airRandMTStateNix, airMopAlways); nn = nrrdElementNumber(nref); rdata = AIR_CAST(unsigned int *, nref->data); fprintf(stderr, "generating data: . . . "); fflush(stderr); time0 = airTime(); progress = AIR_FALSE; tick = nn/100; for (ii=0; ii<nn; ii++) { rdata[ii] = airUIrandMT_r(rng); if (ii && tick && !(ii % tick)) { time1 = airTime(); if (time1 - time0 > 1.0) { /* if it took more than a second to do 1% of the thing, would be good to generate some progress indication */ progress = AIR_TRUE; } if (progress) { fprintf(stderr, "%s", airDoneStr(0, ii, nn, doneStr)); fflush(stderr); } } } if (progress) { fprintf(stderr, "%s\n", airDoneStr(0, ii, nn, doneStr)); fflush(stderr); } else { fprintf(stderr, "\n"); } fprintf(stderr, "finding reference (big-endian) CRC: "); fflush(stderr); refCRC = nrrdCRC32(nref, airEndianBig); fprintf(stderr, "%u\n", refCRC); /* write data, with padding */ fprintf(stderr, "saving data . . . "); fflush(stderr); if (!(fout = fopen(outS[0], "wb" COMMIT))) { fprintf(stderr, "\n%s: couldn't open %s for writing: %s\n", me, outS[0], strerror(errno)); airMopError(mop); return 1; } airMopAdd(mop, fout, (airMopper)airFclose, airMopAlways); for (ii=0; ii<pad[0]; ii++) { if (EOF == fputc(1, fout)) { fprintf(stderr, "\n%s: error doing pre-padding\n", me); airMopError(mop); return 1; } } if (nn != fwrite(nref->data, nrrdElementSize(nref), nn, fout)) { fprintf(stderr, "\n%s: error writing data\n", me); airMopError(mop); return 1; } for (ii=0; ii<pad[1]; ii++) { if (EOF == fputc(2, fout)) { fprintf(stderr, "\n%s: error doing post-padding\n", me); airMopError(mop); return 1; } } if (EOF == fflush(fout)) { fprintf(stderr, "\n%s: error fflushing data: %s\n", me, strerror(errno)); } fprintf(stderr, "\n"); if (printbytes) { size_t bi, rpb, nn; char stmp[AIR_STRLEN_SMALL]; nn = nrrdElementSize(nref)*nrrdElementNumber(nref); rpb = AIR_MIN(printbytes, nn); dataUC = AIR_CAST(unsigned char *, nref->data); fprintf(stderr, "CORRECT %s bytes at beginning:\n", airSprintSize_t(stmp, rpb)); for (bi=0; bi<rpb; bi++) { fprintf(stderr, "%x ", dataUC[bi]); } fprintf(stderr, "...\n"); fprintf(stderr, "CORRECT %s bytes at end:\n", airSprintSize_t(stmp, rpb)); fprintf(stderr, "..."); for (bi=nn - rpb; bi<nn; bi++) { fprintf(stderr, " %x", dataUC[bi]); } fprintf(stderr, "\n"); } airMopSingleOkay(mop, fout); airMopSingleOkay(mop, nref); nref = NULL; /* write header; for now just writing the header directly */ fprintf(stderr, "writing header . . . \n"); if (!(fout = fopen(outS[1], "w"))) { fprintf(stderr, "%s: couldn't open %s for writing: %s\n", me, outS[1], strerror(errno)); airMopError(mop); return 1; } airMopAdd(mop, fout, (airMopper)airFclose, airMopAlways); fprintf(fout, "NRRD0005\n"); fprintf(fout, "type: unsigned int\n"); fprintf(fout, "dimension: %u\n", sizeNum); fprintf(fout, "sizes:"); for (axi=0; axi<sizeNum; axi++) { fprintf(fout, " %s", airSprintSize_t(stmp, size[axi])); } fprintf(fout, "\n"); fprintf(fout, "endian: %s\n", airEnumStr(airEndian, airMyEndian())); fprintf(fout, "encoding: %s\n", airEnumStr(nrrdEncodingType, nrrdEncodingTypeRaw)); if (!negskip) { if (pad[0]) { fprintf(fout, "byte skip: %s\n", airSprintSize_t(stmp, pad[0])); } } else { fprintf(fout, "byte skip: -%s\n", airSprintSize_t(stmp, pad[1]+1)); } fprintf(fout, "data file: %s\n", outS[0]); airMopSingleOkay(mop, fout); /* read it in, make sure it checks out */ fprintf(stderr, "reading data . . . \n"); nin = nrrdNew(); airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways); if (nrrdLoad(nin, outS[1], NULL)) { airMopAdd(mop, berr=biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error reading back in: %s\n", me, berr); airMopError(mop); return 1; } if (printbytes) { size_t bi, rpb, nn; char stmp[AIR_STRLEN_SMALL]; nn = nrrdElementSize(nin)*nrrdElementNumber(nin); rpb = AIR_MIN(printbytes, nn); dataUC = AIR_CAST(unsigned char *, nin->data); fprintf(stderr, "FOUND %s bytes at beginning:\n", airSprintSize_t(stmp, rpb)); for (bi=0; bi<rpb; bi++) { fprintf(stderr, "%x ", dataUC[bi]); } fprintf(stderr, "...\n"); fprintf(stderr, "FOUND %s bytes at end:\n", airSprintSize_t(stmp, rpb)); fprintf(stderr, "..."); for (bi=nn - rpb; bi<nn; bi++) { fprintf(stderr, " %x", dataUC[bi]); } fprintf(stderr, "\n"); } fprintf(stderr, "finding new CRC . . . \n"); gotCRC = nrrdCRC32(nin, airEndianBig); if (refCRC != gotCRC) { fprintf(stderr, "%s: got CRC %u but wanted %u\n", me, gotCRC, refCRC); airMopError(mop); return 1; } fprintf(stderr, "(all ok)\n"); /* HEY: to test gzip reading, we really want to do a system call to gzip compress the data, and write a new header to point to the compressed data, and make sure we can read in that just the same */ airMopOkay(mop); return 0; }
/* ** nio->byteSkip < 0 functionality contributed by Katharina Quintus */ int _nrrdEncodingGzip_read(FILE *file, void *_data, size_t elNum, Nrrd *nrrd, NrrdIoState *nio) { char me[]="_nrrdEncodingGzip_read", err[BIFF_STRLEN]; #if TEEM_ZLIB size_t sizeData, sizeRed, sizeChunk; int error; long int bi; unsigned int read; char *data; gzFile gzfin; ptrHack hack; sizeData = nrrdElementSize(nrrd)*elNum; /* Create the gzFile for reading in the gzipped data. */ if ((gzfin = _nrrdGzOpen(file, "rb")) == Z_NULL) { /* there was a problem */ sprintf(err, "%s: error opening gzFile", me); biffAdd(NRRD, err); return 1; } /* keeps track of how many bytes have been successfully read in */ sizeRed = 0; /* zlib can only handle data sizes up to UINT_MAX ==> if there's more than UINT_MAX bytes to read in, we read in in chunks */ sizeChunk = AIR_MIN(sizeData, UINT_MAX); if (nio->byteSkip < 0) { /* We don't know the size of the size to skip before the data, so decompress the data first into a temporary memory buffer. Then the byteskipping is then just memcpy-ing the appropriate region of memory from "buff" into the given "_data" pointer */ char *buff; airArray *buffArr; /* setting the airArray increment to twice the chunk size means that for headers that are small compared to the data, the airArray never actually has to reallocate. The unit is 1 because we are managing the reading in terms of bytes (sizeof(char)==1 by definition) */ buff = NULL; hack.c = &buff; buffArr = airArrayNew(hack.v, NULL, 1, 2*sizeChunk); airArrayLenSet(buffArr, sizeChunk); if (!( buffArr && buffArr->data )) { sprintf(err, "%s: couldn't initialize airArray\n", me); biffAdd(NRRD, err); return 1; } /* we keep reading in chunks as long as there hasn't been an error, and we haven't hit EOF (EOF signified by read == 0). Unlike the code below (for positive byteskip), we are obligated to read until the bitter end, and can't update sizeChunk to encompass only the required data. Cast on third arg ok because of AIR_MIN use above */ while (!(error = _nrrdGzRead(gzfin, buff + sizeRed, AIR_CAST(unsigned int, sizeChunk), &read)) && read > 0) { sizeRed += read; if (read >= sizeChunk) { /* we were able to read as much data as we requested, maybe there is more, so we need to make our temp buffer bigger */ airArrayLenIncr(buffArr, sizeChunk); if (!buffArr->data) { sprintf(err, "%s: couldn't re-allocate data buffer", me); biffAdd(NRRD, err); return 1; } } } if (error) { sprintf(err, "%s: error reading from gzFile", me); biffAdd(NRRD, err); return 1; } if (sizeRed < sizeData + (-nio->byteSkip - 1)) { sprintf(err, "%s: expected " _AIR_SIZE_T_CNV " bytes and received only " _AIR_SIZE_T_CNV " bytes", me, AIR_CAST(size_t, sizeData + (-nio->byteSkip - 1)), sizeRed); biffAdd(NRRD, err); return 1; } /* also handles nio->byteSkip == -N-1 signifying extra N bytes at end */ memcpy(_data, buff + sizeRed - sizeData - (-nio->byteSkip - 1), sizeData); airArrayNuke(buffArr); } else {
int tenGlyphGen(limnObject *glyphsLimn, echoScene *glyphsEcho, tenGlyphParm *parm, const Nrrd *nten, const Nrrd *npos, const Nrrd *nslc) { static const char me[]="tenGlyphGen"; gageShape *shape; airArray *mop; float *tdata, eval[3], evec[9], *cvec, rotEvec[9], mA_f[16], absEval[3], glyphScl[3]; double pI[3], pW[3], cl, cp, sRot[16], mA[16], mB[16], msFr[9], tmpvec[3], R, G, B, qA, qB, qC, glyphAniso, sliceGray; unsigned int duh; int slcCoord[3], idx, glyphIdx, axis, numGlyphs, svRGBAfl=AIR_FALSE; limnLook *look; int lookIdx; echoObject *eglyph, *inst, *list=NULL, *split, *esquare; echoPos_t eM[16], originOffset[3], edge0[3], edge1[3]; char stmp[AIR_STRLEN_SMALL]; /* int eret; double tmp1[3], tmp2[3]; */ if (!( (glyphsLimn || glyphsEcho) && nten && parm)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } mop = airMopNew(); shape = gageShapeNew(); shape->defaultCenter = nrrdCenterCell; airMopAdd(mop, shape, (airMopper)gageShapeNix, airMopAlways); if (npos) { if (!( 2 == nten->dim && 7 == nten->axis[0].size )) { biffAddf(TEN, "%s: nten isn't 2-D 7-by-N array", me); airMopError(mop); return 1; } if (!( 2 == npos->dim && 3 == npos->axis[0].size && nten->axis[1].size == npos->axis[1].size )) { biffAddf(TEN, "%s: npos isn't 2-D 3-by-%s array", me, airSprintSize_t(stmp, nten->axis[1].size)); airMopError(mop); return 1; } if (!( nrrdTypeFloat == nten->type && nrrdTypeFloat == npos->type )) { biffAddf(TEN, "%s: nten and npos must be %s, not %s and %s", me, airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nten->type), airEnumStr(nrrdType, npos->type)); airMopError(mop); return 1; } } else { if (tenTensorCheck(nten, nrrdTypeFloat, AIR_TRUE, AIR_TRUE)) { biffAddf(TEN, "%s: didn't get a valid DT volume", me); airMopError(mop); return 1; } } if (tenGlyphParmCheck(parm, nten, npos, nslc)) { biffAddf(TEN, "%s: trouble", me); airMopError(mop); return 1; } if (!npos) { if (gageShapeSet(shape, nten, tenGageKind->baseDim)) { biffMovef(TEN, GAGE, "%s: trouble", me); airMopError(mop); return 1; } } if (parm->doSlice) { ELL_3V_COPY(edge0, shape->spacing); ELL_3V_COPY(edge1, shape->spacing); edge0[parm->sliceAxis] = edge1[parm->sliceAxis] = 0.0; switch(parm->sliceAxis) { case 0: edge0[1] = edge1[2] = 0; ELL_4M_ROTATE_Y_SET(sRot, AIR_PI/2); break; case 1: edge0[0] = edge1[2] = 0; ELL_4M_ROTATE_X_SET(sRot, AIR_PI/2); break; case 2: default: edge0[0] = edge1[1] = 0; ELL_4M_IDENTITY_SET(sRot); break; } ELL_3V_COPY(originOffset, shape->spacing); ELL_3V_SCALE(originOffset, -0.5, originOffset); originOffset[parm->sliceAxis] *= -2*parm->sliceOffset; } if (glyphsLimn) { /* create limnLooks for diffuse and ambient-only shading */ /* ??? */ /* hack: save old value of setVertexRGBAFromLook, and set to true */ svRGBAfl = glyphsLimn->setVertexRGBAFromLook; glyphsLimn->setVertexRGBAFromLook = AIR_TRUE; } if (glyphsEcho) { list = echoObjectNew(glyphsEcho, echoTypeList); } if (npos) { numGlyphs = AIR_UINT(nten->axis[1].size); } else { numGlyphs = shape->size[0] * shape->size[1] * shape->size[2]; } /* find measurement frame transform */ if (3 == nten->spaceDim && AIR_EXISTS(nten->measurementFrame[0][0])) { /* msFr nten->measurementFrame ** 0 1 2 [0][0] [1][0] [2][0] ** 3 4 5 [0][1] [1][1] [2][1] ** 6 7 8 [0][2] [1][2] [2][2] */ msFr[0] = nten->measurementFrame[0][0]; msFr[3] = nten->measurementFrame[0][1]; msFr[6] = nten->measurementFrame[0][2]; msFr[1] = nten->measurementFrame[1][0]; msFr[4] = nten->measurementFrame[1][1]; msFr[7] = nten->measurementFrame[1][2]; msFr[2] = nten->measurementFrame[2][0]; msFr[5] = nten->measurementFrame[2][1]; msFr[8] = nten->measurementFrame[2][2]; } else { ELL_3M_IDENTITY_SET(msFr); } for (idx=0; idx<numGlyphs; idx++) { tdata = (float*)(nten->data) + 7*idx; if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: hello %g %g %g %g %g %g %g\n", me, idx, numGlyphs, tdata[0], tdata[1], tdata[2], tdata[3], tdata[4], tdata[5], tdata[6]); } if (!( TEN_T_EXISTS(tdata) )) { /* there's nothing we can do here */ if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: non-existent data\n", me, idx, numGlyphs); } continue; } if (npos) { ELL_3V_COPY(pW, (float*)(npos->data) + 3*idx); if (!( AIR_EXISTS(pW[0]) && AIR_EXISTS(pW[1]) && AIR_EXISTS(pW[2]) )) { /* position doesn't exist- perhaps because its from the push library, which might kill points by setting coords to nan */ continue; } } else { NRRD_COORD_GEN(pI, shape->size, 3, idx); /* this does take into account full orientation */ gageShapeItoW(shape, pW, pI); if (parm->nmask) { if (!( nrrdFLookup[parm->nmask->type](parm->nmask->data, idx) >= parm->maskThresh )) { if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: doesn't meet mask thresh\n", me, idx, numGlyphs); } continue; } } } tenEigensolve_f(eval, evec, tdata); /* transform eigenvectors by measurement frame */ ELL_3MV_MUL(tmpvec, msFr, evec + 0); ELL_3V_COPY_TT(evec + 0, float, tmpvec); ELL_3MV_MUL(tmpvec, msFr, evec + 3); ELL_3V_COPY_TT(evec + 3, float, tmpvec); ELL_3MV_MUL(tmpvec, msFr, evec + 6); ELL_3V_COPY_TT(evec + 6, float, tmpvec); ELL_3V_CROSS(tmpvec, evec + 0, evec + 3); if (0 > ELL_3V_DOT(tmpvec, evec + 6)) { ELL_3V_SCALE(evec + 6, -1, evec + 6); } ELL_3M_TRANSPOSE(rotEvec, evec); if (parm->doSlice && pI[parm->sliceAxis] == parm->slicePos) { /* set sliceGray */ if (nslc) { /* we aren't masked by confidence, as anisotropy slice is */ for (duh=0; duh<parm->sliceAxis; duh++) { slcCoord[duh] = (int)(pI[duh]); } for (duh=duh<parm->sliceAxis; duh<2; duh++) { slcCoord[duh] = (int)(pI[duh+1]); } /* HEY: GLK has no idea what's going here */ slcCoord[0] = (int)(pI[0]); slcCoord[1] = (int)(pI[1]); slcCoord[2] = (int)(pI[2]); sliceGray = nrrdFLookup[nslc->type](nslc->data, slcCoord[0] + nslc->axis[0].size*slcCoord[1]); } else { if (!( tdata[0] >= parm->confThresh )) { if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d (slice): conf %g < thresh %g\n", me, idx, numGlyphs, tdata[0], parm->confThresh); } continue; } sliceGray = tenAnisoEval_f(eval, parm->sliceAnisoType); } if (parm->sliceGamma > 0) { sliceGray = AIR_AFFINE(0, sliceGray, 1, parm->sliceBias, 1); sliceGray = pow(sliceGray, 1.0/parm->sliceGamma); } else { sliceGray = AIR_AFFINE(0, sliceGray, 1, 0, 1-parm->sliceBias); sliceGray = 1.0 - pow(sliceGray, -1.0/parm->sliceGamma); } /* make slice contribution */ /* HEY: this is *NOT* aware of shape->fromOrientation */ if (glyphsLimn) { lookIdx = limnObjectLookAdd(glyphsLimn); look = glyphsLimn->look + lookIdx; ELL_4V_SET_TT(look->rgba, float, sliceGray, sliceGray, sliceGray, 1); ELL_3V_SET(look->kads, 1, 0, 0); look->spow = 0; glyphIdx = limnObjectSquareAdd(glyphsLimn, lookIdx); ELL_4M_IDENTITY_SET(mA); ell_4m_post_mul_d(mA, sRot); if (!npos) { ELL_4M_SCALE_SET(mB, shape->spacing[0], shape->spacing[1], shape->spacing[2]); } ell_4m_post_mul_d(mA, mB); ELL_4M_TRANSLATE_SET(mB, pW[0], pW[1], pW[2]); ell_4m_post_mul_d(mA, mB); ELL_4M_TRANSLATE_SET(mB, originOffset[0], originOffset[1], originOffset[2]); ell_4m_post_mul_d(mA, mB); ELL_4M_COPY_TT(mA_f, float, mA); limnObjectPartTransform(glyphsLimn, glyphIdx, mA_f); } if (glyphsEcho) { esquare = echoObjectNew(glyphsEcho,echoTypeRectangle); ELL_3V_ADD2(((echoRectangle*)esquare)->origin, pW, originOffset); ELL_3V_COPY(((echoRectangle*)esquare)->edge0, edge0); ELL_3V_COPY(((echoRectangle*)esquare)->edge1, edge1); echoColorSet(esquare, AIR_CAST(echoCol_t, sliceGray), AIR_CAST(echoCol_t, sliceGray), AIR_CAST(echoCol_t, sliceGray), 1); /* this is pretty arbitrary- but I want shadows to have some effect. Previously, the material was all ambient: (A,D,S) = (1,0,0), which avoided all shadow effects. */ echoMatterPhongSet(glyphsEcho, esquare, 0.4f, 0.6f, 0, 40); echoListAdd(list, esquare); } } if (parm->onlyPositive) { if (eval[2] < 0) { /* didn't have all positive eigenvalues, its outta here */ if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: not all evals %g %g %g > 0\n", me, idx, numGlyphs, eval[0], eval[1], eval[2]); } continue; } } if (!( tdata[0] >= parm->confThresh )) { if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: conf %g < thresh %g\n", me, idx, numGlyphs, tdata[0], parm->confThresh); } continue; } if (!( tenAnisoEval_f(eval, parm->anisoType) >= parm->anisoThresh )) { if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: aniso[%d] %g < thresh %g\n", me, idx, numGlyphs, parm->anisoType, tenAnisoEval_f(eval, parm->anisoType), parm->anisoThresh); } continue; } glyphAniso = tenAnisoEval_f(eval, parm->colAnisoType); /* fprintf(stderr, "%s: eret = %d; evals = %g %g %g\n", me, eret, eval[0], eval[1], eval[2]); ELL_3V_CROSS(tmp1, evec+0, evec+3); tmp2[0] = ELL_3V_LEN(tmp1); ELL_3V_CROSS(tmp1, evec+0, evec+6); tmp2[1] = ELL_3V_LEN(tmp1); ELL_3V_CROSS(tmp1, evec+3, evec+6); tmp2[2] = ELL_3V_LEN(tmp1); fprintf(stderr, "%s: crosses = %g %g %g\n", me, tmp2[0], tmp2[1], tmp2[2]); */ /* set transform (in mA) */ ELL_3V_ABS(absEval, eval); ELL_4M_IDENTITY_SET(mA); /* reset */ ELL_3V_SCALE(glyphScl, parm->glyphScale, absEval); /* scale by evals */ ELL_4M_SCALE_SET(mB, glyphScl[0], glyphScl[1], glyphScl[2]); ell_4m_post_mul_d(mA, mB); ELL_43M_INSET(mB, rotEvec); /* rotate by evecs */ ell_4m_post_mul_d(mA, mB); ELL_4M_TRANSLATE_SET(mB, pW[0], pW[1], pW[2]); /* translate */ ell_4m_post_mul_d(mA, mB); /* set color (in R,G,B) */ cvec = evec + 3*(AIR_CLAMP(0, parm->colEvec, 2)); R = AIR_ABS(cvec[0]); /* standard mapping */ G = AIR_ABS(cvec[1]); B = AIR_ABS(cvec[2]); /* desaturate by colMaxSat */ R = AIR_AFFINE(0.0, parm->colMaxSat, 1.0, parm->colIsoGray, R); G = AIR_AFFINE(0.0, parm->colMaxSat, 1.0, parm->colIsoGray, G); B = AIR_AFFINE(0.0, parm->colMaxSat, 1.0, parm->colIsoGray, B); /* desaturate some by anisotropy */ R = AIR_AFFINE(0.0, parm->colAnisoModulate, 1.0, R, AIR_AFFINE(0.0, glyphAniso, 1.0, parm->colIsoGray, R)); G = AIR_AFFINE(0.0, parm->colAnisoModulate, 1.0, G, AIR_AFFINE(0.0, glyphAniso, 1.0, parm->colIsoGray, G)); B = AIR_AFFINE(0.0, parm->colAnisoModulate, 1.0, B, AIR_AFFINE(0.0, glyphAniso, 1.0, parm->colIsoGray, B)); /* clamp and do gamma */ R = AIR_CLAMP(0.0, R, 1.0); G = AIR_CLAMP(0.0, G, 1.0); B = AIR_CLAMP(0.0, B, 1.0); R = pow(R, parm->colGamma); G = pow(G, parm->colGamma); B = pow(B, parm->colGamma); /* find axis, and superquad exponents qA and qB */ if (eval[2] > 0) { /* all evals positive */ cl = AIR_MIN(0.99, tenAnisoEval_f(eval, tenAniso_Cl1)); cp = AIR_MIN(0.99, tenAnisoEval_f(eval, tenAniso_Cp1)); if (cl > cp) { axis = 0; qA = pow(1-cp, parm->sqdSharp); qB = pow(1-cl, parm->sqdSharp); } else { axis = 2; qA = pow(1-cl, parm->sqdSharp); qB = pow(1-cp, parm->sqdSharp); } qC = qB; } else if (eval[0] < 0) { /* all evals negative */ float aef[3]; aef[0] = absEval[2]; aef[1] = absEval[1]; aef[2] = absEval[0]; cl = AIR_MIN(0.99, tenAnisoEval_f(aef, tenAniso_Cl1)); cp = AIR_MIN(0.99, tenAnisoEval_f(aef, tenAniso_Cp1)); if (cl > cp) { axis = 2; qA = pow(1-cp, parm->sqdSharp); qB = pow(1-cl, parm->sqdSharp); } else { axis = 0; qA = pow(1-cl, parm->sqdSharp); qB = pow(1-cp, parm->sqdSharp); } qC = qB; } else { #define OOSQRT2 0.70710678118654752440 #define OOSQRT3 0.57735026918962576451 /* double poleA[3]={OOSQRT3, OOSQRT3, OOSQRT3}; */ double poleB[3]={1, 0, 0}; double poleC[3]={OOSQRT2, OOSQRT2, 0}; double poleD[3]={OOSQRT3, -OOSQRT3, -OOSQRT3}; double poleE[3]={OOSQRT2, 0, -OOSQRT2}; double poleF[3]={OOSQRT3, OOSQRT3, -OOSQRT3}; double poleG[3]={0, -OOSQRT2, -OOSQRT2}; double poleH[3]={0, 0, -1}; /* double poleI[3]={-OOSQRT3, -OOSQRT3, -OOSQRT3}; */ double funk[3]={0,4,2}, thrn[3]={1,4,4}; double octa[3]={0,2,2}, cone[3]={1,2,2}; double evalN[3], tmp, bary[3]; double qq[3]; ELL_3V_NORM(evalN, eval, tmp); if (eval[1] >= -eval[2]) { /* inside B-F-C */ ell_3v_barycentric_spherical_d(bary, poleB, poleF, poleC, evalN); ELL_3V_SCALE_ADD3(qq, bary[0], octa, bary[1], thrn, bary[2], cone); axis = 2; } else if (eval[0] >= -eval[2]) { /* inside B-D-F */ if (eval[1] >= 0) { /* inside B-E-F */ ell_3v_barycentric_spherical_d(bary, poleB, poleE, poleF, evalN); ELL_3V_SCALE_ADD3(qq, bary[0], octa, bary[1], funk, bary[2], thrn); axis = 2; } else { /* inside B-D-E */ ell_3v_barycentric_spherical_d(bary, poleB, poleD, poleE, evalN); ELL_3V_SCALE_ADD3(qq, bary[0], cone, bary[1], thrn, bary[2], funk); axis = 0; } } else if (eval[0] < -eval[1]) { /* inside D-G-H */ ell_3v_barycentric_spherical_d(bary, poleD, poleG, poleH, evalN); ELL_3V_SCALE_ADD3(qq, bary[0], thrn, bary[1], cone, bary[2], octa); axis = 0; } else if (eval[1] < 0) { /* inside E-D-H */ ell_3v_barycentric_spherical_d(bary, poleE, poleD, poleH, evalN); ELL_3V_SCALE_ADD3(qq, bary[0], funk, bary[1], thrn, bary[2], octa); axis = 0; } else { /* inside F-E-H */ ell_3v_barycentric_spherical_d(bary, poleF, poleE, poleH, evalN); ELL_3V_SCALE_ADD3(qq, bary[0], thrn, bary[1], funk, bary[2], cone); axis = 2; } qA = qq[0]; qB = qq[1]; qC = qq[2]; #undef OOSQRT2 #undef OOSQRT3 } /* add the glyph */ if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: the glyph stays!\n", me, idx, numGlyphs); } if (glyphsLimn) { lookIdx = limnObjectLookAdd(glyphsLimn); look = glyphsLimn->look + lookIdx; ELL_4V_SET_TT(look->rgba, float, R, G, B, 1); ELL_3V_SET(look->kads, parm->ADSP[0], parm->ADSP[1], parm->ADSP[2]); look->spow = 0; switch(parm->glyphType) { case tenGlyphTypeBox: glyphIdx = limnObjectCubeAdd(glyphsLimn, lookIdx); break; case tenGlyphTypeSphere: glyphIdx = limnObjectPolarSphereAdd(glyphsLimn, lookIdx, axis, 2*parm->facetRes, parm->facetRes); break; case tenGlyphTypeCylinder: glyphIdx = limnObjectCylinderAdd(glyphsLimn, lookIdx, axis, parm->facetRes); break; case tenGlyphTypeSuperquad: default: glyphIdx = limnObjectPolarSuperquadFancyAdd(glyphsLimn, lookIdx, axis, AIR_CAST(float, qA), AIR_CAST(float, qB), AIR_CAST(float, qC), 0, 2*parm->facetRes, parm->facetRes); break; } ELL_4M_COPY_TT(mA_f, float, mA); limnObjectPartTransform(glyphsLimn, glyphIdx, mA_f); } if (glyphsEcho) { switch(parm->glyphType) { case tenGlyphTypeBox: eglyph = echoObjectNew(glyphsEcho, echoTypeCube); /* nothing else to set */ break; case tenGlyphTypeSphere: eglyph = echoObjectNew(glyphsEcho, echoTypeSphere); echoSphereSet(eglyph, 0, 0, 0, 1); break; case tenGlyphTypeCylinder: eglyph = echoObjectNew(glyphsEcho, echoTypeCylinder); echoCylinderSet(eglyph, axis); break; case tenGlyphTypeSuperquad: default: eglyph = echoObjectNew(glyphsEcho, echoTypeSuperquad); echoSuperquadSet(eglyph, axis, qA, qB); break; } echoColorSet(eglyph, AIR_CAST(echoCol_t, R), AIR_CAST(echoCol_t, G), AIR_CAST(echoCol_t, B), 1); echoMatterPhongSet(glyphsEcho, eglyph, parm->ADSP[0], parm->ADSP[1], parm->ADSP[2], parm->ADSP[3]); inst = echoObjectNew(glyphsEcho, echoTypeInstance); ELL_4M_COPY(eM, mA); echoInstanceSet(inst, eM, eglyph); echoListAdd(list, inst); } } if (glyphsLimn) { glyphsLimn->setVertexRGBAFromLook = svRGBAfl; } if (glyphsEcho) { split = echoListSplit3(glyphsEcho, list, 10); echoObjectAdd(glyphsEcho, split); } airMopOkay(mop); return 0; }
/* ******** tenGradientDistribute ** ** Takes the given list of gradients, normalizes their lengths, ** optionally jitters their positions, does point repulsion, and then ** (optionally) selects a combination of directions with minimum vector sum. ** ** The complicated part of this is the point repulsion, which uses a ** gradient descent with variable set size. The progress of the system ** is measured by decrease in potential (when its measurement doesn't ** overflow to infinity) or an increase in the minimum angle. When a ** step results in negative progress, the step size is halved, and the ** iteration is attempted again. Based on the observation that at ** some points the step size must be made very small to get progress, ** the step size is cautiously increased ("nudged") at every ** iteration, to try to avoid using an overly small step. The amount ** by which the step is nudged is halved everytime the step is halved, ** to avoid endless cycling through step sizes. */ int tenGradientDistribute(Nrrd *nout, const Nrrd *nin, tenGradientParm *tgparm) { static const char me[]="tenGradientDistribute"; char filename[AIR_STRLEN_SMALL]; unsigned int ii, num, iter, oldIdx, newIdx, edgeShrink; airArray *mop; Nrrd *npos[2]; double *pos, len, meanVelocity, pot, potNew, potD, edge, edgeMin, angle, angleNew; int E; if (!nout || tenGradientCheck(nin, nrrdTypeUnknown, 2) || !tgparm) { biffAddf(TEN, "%s: got NULL pointer or invalid input", me); return 1; } num = AIR_UINT(nin->axis[1].size); mop = airMopNew(); npos[0] = nrrdNew(); npos[1] = nrrdNew(); airMopAdd(mop, npos[0], (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, npos[1], (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(npos[0], nin, nrrdTypeDouble) || nrrdConvert(npos[1], nin, nrrdTypeDouble)) { biffMovef(TEN, NRRD, "%s: trouble allocating temp buffers", me); airMopError(mop); return 1; } pos = (double*)(npos[0]->data); for (ii=0; ii<num; ii++) { ELL_3V_NORM(pos, pos, len); pos += 3; } if (tgparm->jitter) { if (tenGradientJitter(npos[0], npos[0], tgparm->jitter)) { biffAddf(TEN, "%s: problem jittering input", me); airMopError(mop); return 1; } } /* initialize things prior to first iteration; have to make sure that loop body tests pass 1st time around */ meanVelocity = 2*tgparm->minVelocity; potD = -2*tgparm->minPotentialChange; oldIdx = 0; newIdx = 1; tgparm->step = tgparm->initStep; tgparm->nudge = 0.1; tenGradientMeasure(&pot, &angle, NULL, npos[oldIdx], tgparm, AIR_TRUE); for (iter = 0; ((!!tgparm->minIteration && iter < tgparm->minIteration) || (iter < tgparm->maxIteration && (!tgparm->minPotentialChange || !AIR_EXISTS(potD) || -potD > tgparm->minPotentialChange) && (!tgparm->minVelocity || meanVelocity > tgparm->minVelocity) && tgparm->step > FLT_MIN)); iter++) { /* copy positions from old to new */ memcpy(npos[newIdx]->data, npos[oldIdx]->data, 3*num*sizeof(double)); edge = tenGradientIdealEdge(num, tgparm->single); edgeShrink = 0; /* try to do a position update, which will fail if repulsion values explode, from having an insufficiently small edge normalization, so retry with smaller edge next time */ do { E = _tenGradientUpdate(&meanVelocity, &edgeMin, npos[newIdx], edge, tgparm); if (E) { if (edgeShrink > tgparm->maxEdgeShrink) { biffAddf(TEN, "%s: %u > %u edge shrinks (%g), update still failed", me, edgeShrink, tgparm->maxEdgeShrink, edge); airMopError(mop); return 1; } edgeShrink++; /* re-initialize positions (HEY ugly code logic) */ memcpy(npos[newIdx]->data, npos[oldIdx]->data, 3*num*sizeof(double)); edge = edgeMin; } } while (E); tenGradientMeasure(&potNew, &angleNew, NULL, npos[newIdx], tgparm, AIR_TRUE); if ((AIR_EXISTS(pot) && AIR_EXISTS(potNew) && potNew <= pot) || angleNew >= angle) { /* there was progress of some kind, either through potential decrease, or angle increase */ potD = 2*(potNew - pot)/(potNew + pot); if (!(iter % tgparm->report) && tgparm->verbose) { fprintf(stderr, "%s(%d): . . . . . . step = %g, edgeShrink = %u\n" " velo = %g<>%g, phi = %g ~ %g<>%g, angle = %g ~ %g\n", me, iter, tgparm->step, edgeShrink, meanVelocity, tgparm->minVelocity, pot, potD, tgparm->minPotentialChange, angle, angleNew - angle); } if (tgparm->snap && !(iter % tgparm->snap)) { sprintf(filename, "%05d.nrrd", iter/tgparm->snap); if (tgparm->verbose) { fprintf(stderr, "%s(%d): . . . . . . saving %s\n", me, iter, filename); } if (nrrdSave(filename, npos[newIdx], NULL)) { char *serr; serr = biffGetDone(NRRD); if (tgparm->verbose) { /* perhaps shouldn't have this check */ fprintf(stderr, "%s: iter=%d, couldn't save snapshot:\n%s" "continuing ...\n", me, iter, serr); } free(serr); } } tgparm->step *= 1 + tgparm->nudge; tgparm->step = AIR_MIN(tgparm->initStep, tgparm->step); pot = potNew; angle = angleNew; /* swap buffers */ newIdx = 1 - newIdx; oldIdx = 1 - oldIdx; } else { /* oops, did not make progress; back off and try again */ if (tgparm->verbose) { fprintf(stderr, "%s(%d): ######## step %g --> %g\n" " phi = %g --> %g ~ %g, angle = %g --> %g\n", me, iter, tgparm->step, tgparm->step/2, pot, potNew, potD, angle, angleNew); } tgparm->step /= 2; tgparm->nudge /= 2; } } /* when the for-loop test fails, we stop before computing the next iteration (which starts with copying from npos[oldIdx] to npos[newIdx]) ==> the final results are in npos[oldIdx] */ if (tgparm->verbose) { fprintf(stderr, "%s: .......................... done distribution:\n" " (%d && %d) || (%d \n" " && (%d || %d || %d) \n" " && (%d || %d) \n" " && %d) is false\n", me, !!tgparm->minIteration, iter < tgparm->minIteration, iter < tgparm->maxIteration, !tgparm->minPotentialChange, !AIR_EXISTS(potD), AIR_ABS(potD) > tgparm->minPotentialChange, !tgparm->minVelocity, meanVelocity > tgparm->minVelocity, tgparm->step > FLT_MIN); fprintf(stderr, " iter=%d, velo = %g<>%g, phi = %g ~ %g<>%g;\n", iter, meanVelocity, tgparm->minVelocity, pot, potD, tgparm->minPotentialChange); fprintf(stderr, " minEdge = %g; idealEdge = %g\n", 2*sin(angle/2), tenGradientIdealEdge(num, tgparm->single)); } tenGradientMeasure(&pot, NULL, NULL, npos[oldIdx], tgparm, AIR_FALSE); tgparm->potential = pot; tenGradientMeasure(&pot, &angle, &edge, npos[oldIdx], tgparm, AIR_TRUE); tgparm->potentialNorm = pot; tgparm->angle = angle; tgparm->edge = edge; tgparm->itersUsed = iter; if ((tgparm->minMeanImprovement || tgparm->minMean) && !tgparm->single) { if (tgparm->verbose) { fprintf(stderr, "%s: optimizing balance:\n", me); } if (tenGradientBalance(nout, npos[oldIdx], tgparm)) { biffAddf(TEN, "%s: failed to minimize vector sum of gradients", me); airMopError(mop); return 1; } if (tgparm->verbose) { fprintf(stderr, "%s: .......................... done balancing.\n", me); } } else { if (tgparm->verbose) { fprintf(stderr, "%s: .......................... (no balancing)\n", me); } if (nrrdConvert(nout, npos[oldIdx], nrrdTypeDouble)) { biffMovef(TEN, NRRD, "%s: couldn't set output", me); airMopError(mop); return 1; } } airMopOkay(mop); return 0; }
void tenGradientMeasure(double *pot, double *minAngle, double *minEdge, const Nrrd *npos, tenGradientParm *tgparm, int edgeNormalize) { /* static const char me[]="tenGradientMeasure"; */ double diff[3], *pos, atmp=0, ptmp, edge, len; unsigned int ii, jj, num; /* allow minAngle NULL */ if (!(pot && npos && tgparm )) { return; } num = AIR_UINT(npos->axis[1].size); pos = AIR_CAST(double *, npos->data); edge = (edgeNormalize ? tenGradientIdealEdge(num, tgparm->single) : 1.0); *pot = 0; if (minAngle) { *minAngle = AIR_PI; } if (minEdge) { *minEdge = 2; } for (ii=0; ii<num; ii++) { for (jj=0; jj<ii; jj++) { ELL_3V_SUB(diff, pos + 3*ii, pos + 3*jj); len = ELL_3V_LEN(diff); if (minEdge) { *minEdge = AIR_MIN(*minEdge, len); } if (tgparm->expo) { ptmp = airIntPow(edge/len, tgparm->expo); } else { ptmp = pow(edge/len, tgparm->expo_d); } *pot += ptmp; if (minAngle) { atmp = ell_3v_angle_d(pos + 3*ii, pos + 3*jj); *minAngle = AIR_MIN(atmp, *minAngle); } if (!tgparm->single) { *pot += ptmp; ELL_3V_ADD2(diff, pos + 3*ii, pos + 3*jj); len = ELL_3V_LEN(diff); if (minEdge) { *minEdge = AIR_MIN(*minEdge, len); } if (tgparm->expo) { *pot += 2*airIntPow(edge/len, tgparm->expo); } else { *pot += 2*pow(edge/len, tgparm->expo_d); } if (minAngle) { *minAngle = AIR_MIN(AIR_PI-atmp, *minAngle); } } } } return; }
static int theFunc(Nrrd *nout, const Nrrd *nin, int func, funcParm *parm) { static const char me[]="theFunc"; float *tin, *tout, eval[3], evec[9], weight[3], size, mean; size_t NN, II; unsigned int ri; if (!AIR_IN_OP(funcUnknown, func, funcLast)) { biffAddf(TEN, "%s: given func %d out of range [%d,%d]", me, func, funcUnknown+1, funcLast-1); return 1; } if (!(nout && nin && parm)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (tenTensorCheck(nin, nrrdTypeFloat, AIR_FALSE, AIR_TRUE)) { biffAddf(TEN, "%s: didn't get a tensor nrrd", me); return 1; } if (nout != nin) { if (nrrdCopy(nout, nin)) { biffMovef(TEN, NRRD, "%s: couldn't allocate output", me); return 1; } } tin = (float*)(nin->data); tout = (float*)(nout->data); NN = nrrdElementNumber(nin)/7; switch(func) { case funcSizeNormalize: ELL_3V_COPY_TT(weight, float, parm->weight); size = weight[0] + weight[1] + weight[2]; if (!size) { biffAddf(TEN, "%s: some of eigenvalue weights is zero", me); return 1; } weight[0] /= size; weight[1] /= size; weight[2] /= size; for (II=0; II<=NN-1; II++) { tenEigensolve_f(eval, evec, tin); size = (weight[0]*AIR_ABS(eval[0]) + weight[1]*AIR_ABS(eval[1]) + weight[2]*AIR_ABS(eval[2])); ELL_3V_SET_TT(eval, float, AIR_AFFINE(0, parm->amount, 1, eval[0], parm->target*eval[0]/size), AIR_AFFINE(0, parm->amount, 1, eval[1], parm->target*eval[1]/size), AIR_AFFINE(0, parm->amount, 1, eval[2], parm->target*eval[2]/size)); tenMakeSingle_f(tout, tin[0], eval, evec); tin += 7; tout += 7; } break; case funcSizeScale: for (II=0; II<=NN-1; II++) { TEN_T_SET_TT(tout, float, tin[0], parm->amount*tin[1], parm->amount*tin[2], parm->amount*tin[3], parm->amount*tin[4], parm->amount*tin[5], parm->amount*tin[6]); tin += 7; tout += 7; } break; case funcAnisoScale: for (II=0; II<=NN-1; II++) { tenEigensolve_f(eval, evec, tin); if (parm->fixDet) { eval[0] = AIR_MAX(eval[0], 0.00001f); eval[1] = AIR_MAX(eval[1], 0.00001f); eval[2] = AIR_MAX(eval[2], 0.00001f); ELL_3V_SET_TT(eval, float, log(eval[0]), log(eval[1]), log(eval[2])); } mean = (eval[0] + eval[1] + eval[2])/3.0f; ELL_3V_SET_TT(eval, float, AIR_LERP(parm->scale, mean, eval[0]), AIR_LERP(parm->scale, mean, eval[1]), AIR_LERP(parm->scale, mean, eval[2])); if (parm->fixDet) { ELL_3V_SET_TT(eval, float, exp(eval[0]), exp(eval[1]), exp(eval[2])); } if (eval[2] < 0 && parm->makePositive) { eval[0] = AIR_MAX(eval[0], 0.0f); eval[1] = AIR_MAX(eval[1], 0.0f); eval[2] = AIR_MAX(eval[2], 0.0f); } tenMakeSingle_f(tout, tin[0], eval, evec); tin += 7; tout += 7; } break; case funcEigenvalueClamp: for (II=0; II<=NN-1; II++) { tenEigensolve_f(eval, evec, tin); if (AIR_EXISTS(parm->min)) { ELL_3V_SET_TT(eval, float, AIR_MAX(eval[0], parm->min), AIR_MAX(eval[1], parm->min), AIR_MAX(eval[2], parm->min)); } if (AIR_EXISTS(parm->max)) { ELL_3V_SET_TT(eval, float, AIR_MIN(eval[0], parm->max), AIR_MIN(eval[1], parm->max), AIR_MIN(eval[2], parm->max)); } tenMakeSingle_f(tout, tin[0], eval, evec); tin += 7; tout += 7; }
int baneGkms_pvgMain(int argc, char **argv, char *me, hestParm *hparm) { hestOpt *opt = NULL; char *outS, *perr, err[BIFF_STRLEN], *mapS; Nrrd *ninfo, *nposA, *nposB, *ndon, *npvg; NrrdIoState *nio; airArray *mop; int i, pret, invert, sv, sg, smlI; float *pos, p, min, max, sml, newsml, newmin, newmax; NrrdRange *range; hestOptAdd(&opt, "inv", NULL, airTypeInt, 0, 0, &invert, NULL, "Draw on white background, instead of black"); hestOptAdd(&opt, "m", "mapOut", airTypeString, 1, 1, &mapS, "", "save out the colormap used here, so that it can be applied " "to other nrrds with \"unu imap -r\""); hestOptAdd(&opt, "i", "infoIn", airTypeOther, 1, 1, &ninfo, NULL, "input info file (from \"gkms info\")", NULL, NULL, nrrdHestNrrd); hestOptAdd(&opt, "o", "imageOut", airTypeString, 1, 1, &outS, NULL, "output image, in PPM format"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_baneGkms_pvgInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); airMopAdd(mop, ndon=_baneGkmsDonNew(invert), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nposA=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nposB=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, npvg=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nio=nrrdIoStateNew(), (airMopper)nrrdIoStateNix, airMopAlways); if (airStrlen(mapS)) { if (nrrdSave(mapS, ndon, NULL)) { sprintf(err, "%s: trouble saving colormap", me); biffMove(BANE, err, NRRD); airMopError(mop); return 1; } } /* we use sigma = 1.0: different sigmas will scale the position, which will not change the coloring gthresh = 0.0: we want to see everything, and Simian has taught us that there can be boundaries at extremely low gradients */ if (banePosCalc(nposA, 1.0, 0.0, ninfo)) { sprintf(err, "%s: trouble calculating position", me); biffAdd(BANE, err); airMopError(mop); return 1; } sv = nposA->axis[0].size; sg = nposA->axis[1].size; pos = (float *)nposA->data; /* find min, max, sml, smlI: histo-eq will warp values around such that min-->min and max-->max, but 0-->??. So, find smallest magnitide position (sml) as a stand-in for 0.0 and its index (smlI) */ sml = 0; smlI = 0; min = max = AIR_NAN; for (i=0; i<sv*sg; i++) { p = pos[i]; if (!AIR_EXISTS(p)) continue; if (!AIR_EXISTS(min)) { min = max = p; sml = AIR_ABS(p); smlI = i; continue; } min = AIR_MIN(p, min); max = AIR_MAX(p, max); if (AIR_ABS(p) < sml) { sml = AIR_ABS(p); smlI = i; } } if (!AIR_EXISTS(min)) { sprintf(err, "%s: didn't see any real data in position array", me); biffAdd(BANE, err); airMopError(mop); return 1; } if (nrrdHistoEq(nposB, nposA, NULL, PVG_HISTEQ_BINS, 3, 1.0)) { sprintf(err, "%s: trouble doing histo-eq on p(v,g)", me); biffMove(BANE, err, NRRD); airMopError(mop); return 1; } /* warp position values that pos[smlI] gets mapped back to zero, and so that [newmin,newmax] is centered on zero */ pos = (float *)nposB->data; newsml = pos[smlI]; if (min < -max) { newmin = min; newmax = -min; } else { newmin = -max; newmax = max; } for (i=0; i<sv*sg; i++) { if (!AIR_EXISTS(pos[i])) { continue; } if (pos[i] < newsml) { pos[i] = AIR_CAST(float, AIR_AFFINE(min, pos[i], newsml, newmin, 0.0)); } else {
static double _nrrdTernaryOpMin(double a, double b, double c) { b = AIR_MIN(b, c); return AIR_MIN(a, b); }
static double _nrrdBinaryOpMin(double a, double b) {return AIR_MIN(a,b);}
void * _alanTuringWorker(void *_task) { alan_t *tendata, *ten, react, conf, Dxx, Dxy, Dyy, /* Dxz, Dyz, */ *tpx, *tmx, *tpy, *tmy, /* *tpz, *tmz, */ *lev0, *lev1, *parm, deltaT, alpha, beta, A, B, *v[27], lapA, lapB, corrA, corrB, deltaA, deltaB, diffA, diffB, change; int dim, iter, stop, startW, endW, idx, px, mx, py, my, pz, mz, startY, endY, startZ, endZ, sx, sy, sz, x, y, z; alanTask *task; task = (alanTask *)_task; dim = task->actx->dim; sx = task->actx->size[0]; sy = task->actx->size[1]; sz = (2 == dim ? 1 : task->actx->size[2]); parm = (alan_t*)(task->actx->nparm->data); diffA = AIR_CAST(alan_t, task->actx->diffA/pow(task->actx->deltaX, dim)); diffB = AIR_CAST(alan_t, task->actx->diffB/pow(task->actx->deltaX, dim)); startW = task->idx*sy/task->actx->numThreads; endW = (task->idx+1)*sy/task->actx->numThreads; tendata = task->actx->nten ? (alan_t *)task->actx->nten->data : NULL; react = task->actx->react; if (2 == dim) { startZ = 0; endZ = 1; startY = startW; endY = endW; } else { startZ = startW; endZ = endW; startY = 0; endY = sy; } for (iter = 0; (alanStopNot == task->actx->stop && (0 == task->actx->maxIteration || iter < task->actx->maxIteration)); iter++) { if (0 == task->idx) { task->actx->iter = iter; task->actx->nlev = task->actx->_nlev[(iter+1) % 2]; } lev0 = (alan_t*)(task->actx->_nlev[iter % 2]->data); lev1 = (alan_t*)(task->actx->_nlev[(iter+1) % 2]->data); stop = alanStopNot; change = 0; conf = 1; /* if you have no data; this will stay 1 */ for (z = startZ; z < endZ; z++) { if (task->actx->wrap) { pz = AIR_MOD(z+1, sz); mz = AIR_MOD(z-1, sz); } else { pz = AIR_MIN(z+1, sz-1); mz = AIR_MAX(z-1, 0); } for (y = startY; y < endY; y++) { if (task->actx->wrap) { py = AIR_MOD(y+1, sy); my = AIR_MOD(y-1, sy); } else { py = AIR_MIN(y+1, sy-1); my = AIR_MAX(y-1, 0); } for (x = 0; x < sx; x++) { if (task->actx->wrap) { px = AIR_MOD(x+1, sx); mx = AIR_MOD(x-1, sx); } else { px = AIR_MIN(x+1, sx-1); mx = AIR_MAX(x-1, 0); } idx = x + sx*(y + sy*z); A = lev0[0 + 2*idx]; B = lev0[1 + 2*idx]; deltaT = parm[0 + 3*idx]; alpha = parm[1 + 3*idx]; beta = parm[2 + 3*idx]; lapA = lapB = corrA = corrB = 0; if (2 == dim) { /* ** 0 1 2 ----> X ** 3 4 5 ** 6 7 8 ** | ** v Y */ v[1] = lev0 + 2*( x + sx*(my)); v[3] = lev0 + 2*(mx + sx*( y)); v[5] = lev0 + 2*(px + sx*( y)); v[7] = lev0 + 2*( x + sx*(py)); if (tendata) { /* ** 0 1 2 Dxy/2 Dyy -Dxy/2 ** 3 4 5 Dxx -2*(Dxx + Dyy) Dxx ** 6 7 8 -Dxy/2 Dyy Dxy/2 */ v[0] = lev0 + 2*(mx + sx*(my)); v[2] = lev0 + 2*(px + sx*(my)); v[6] = lev0 + 2*(mx + sx*(py)); v[8] = lev0 + 2*(px + sx*(py)); ten = tendata + 4*idx; conf = AIR_CAST(alan_t, (AIR_CLAMP(0.3, ten[0], 1) - 0.3)/0.7); if (conf) { Dxx = ten[1]; Dxy = ten[2]; Dyy = ten[3]; lapA = (Dxy*(v[0][0] + v[8][0] - v[2][0] - v[6][0])/2 + Dxx*(v[3][0] + v[5][0]) + Dyy*(v[1][0] + v[7][0]) - 2*(Dxx + Dyy)*A); lapB = (Dxy*(v[0][1] + v[8][1] - v[2][1] - v[6][1])/2 + Dxx*(v[3][1] + v[5][1]) + Dyy*(v[1][1] + v[7][1]) - 2*(Dxx + Dyy)*B); if (!(task->actx->homogAniso)) { tpx = tendata + 4*(px + sx*( y + sy*( z))); tmx = tendata + 4*(mx + sx*( y + sy*( z))); tpy = tendata + 4*( x + sx*(py + sy*( z))); tmy = tendata + 4*( x + sx*(my + sy*( z))); corrA = ((tpx[1]-tmx[1])*(v[5][0]-v[3][0])/4+ /* Dxx,x*A,x */ (tpx[2]-tmx[2])*(v[7][0]-v[1][0])/4+ /* Dxy,x*A,y */ (tpy[2]-tmy[2])*(v[5][0]-v[3][0])/4+ /* Dxy,y*A,x */ (tpy[3]-tmy[3])*(v[7][0]-v[1][0])); /* Dyy,y*A,y */ corrB = ((tpx[1]-tmx[1])*(v[5][1]-v[3][1])/4+ /* Dxx,x*B,x */ (tpx[2]-tmx[2])*(v[7][1]-v[1][1])/4+ /* Dxy,x*B,y */ (tpy[2]-tmy[2])*(v[5][1]-v[3][1])/4+ /* Dxy,y*B,x */ (tpy[3]-tmy[3])*(v[7][1]-v[1][1])); /* Dyy,y*B,y */ } } else { /* no confidence; you diffuse */ lapA = v[1][0] + v[3][0] + v[5][0] + v[7][0] - 4*A; lapB = v[1][1] + v[3][1] + v[5][1] + v[7][1] - 4*B; } } else { /* no data; you diffuse */ lapA = v[1][0] + v[3][0] + v[5][0] + v[7][0] - 4*A; lapB = v[1][1] + v[3][1] + v[5][1] + v[7][1] - 4*B; } } else { /* 3 == dim */ /* ** 0 1 2 ---- X ** 3 4 5 ** 6 7 8 ** / ** / 9 10 11 ** Y 12 13 14 ** 15 16 17 ** ** 18 19 20 ** 21 22 23 ** 24 25 26 ** | ** | ** Z */ v[ 4] = lev0 + 2*( x + sx*( y + sy*(mz))); v[10] = lev0 + 2*( x + sx*(my + sy*( z))); v[12] = lev0 + 2*(mx + sx*( y + sy*( z))); v[14] = lev0 + 2*(px + sx*( y + sy*( z))); v[16] = lev0 + 2*( x + sx*(py + sy*( z))); v[22] = lev0 + 2*( x + sx*( y + sy*(pz))); if (tendata) { if (!(task->actx->homogAniso)) { } } else { lapA = (v[ 4][0] + v[10][0] + v[12][0] + v[14][0] + v[16][0] + v[22][0] - 6*A); lapB = (v[ 4][1] + v[10][1] + v[12][1] + v[14][1] + v[16][1] + v[22][1] - 6*B); } } deltaA = deltaT*(react*conf*task->actx->K*(alpha - A*B) + diffA*(lapA + corrA)); if (AIR_ABS(deltaA) > task->actx->maxPixelChange) { stop = alanStopDiverged; } change += AIR_ABS(deltaA); deltaB = deltaT*(react*conf*task->actx->K*(A*B - B - beta) + diffB*(lapB + corrB)); if (!( AIR_EXISTS(deltaA) && AIR_EXISTS(deltaB) )) { stop = alanStopNonExist; } A += deltaA; B = AIR_MAX(0, B + deltaB); lev1[0 + 2*idx] = A; lev1[1 + 2*idx] = B; } } } /* add change to global sum in a threadsafe way */ airThreadMutexLock(task->actx->changeMutex); task->actx->averageChange += change/(sx*sy*sz); task->actx->changeCount += 1; if (task->actx->changeCount == task->actx->numThreads) { /* I must be the last thread to reach this point; all others must have passed the mutex unlock, and are sitting at the barrier */ if (alanStopNot != stop) { /* there was some problem in going from lev0 to lev1, which we deal with now by setting actx->stop */ task->actx->stop = stop; } else if (task->actx->averageChange < task->actx->minAverageChange) { /* we converged */ task->actx->stop = alanStopConverged; } else { /* we keep going */ _alanPerIteration(task->actx, iter); if (task->actx->perIteration) { task->actx->perIteration(task->actx, iter); } } task->actx->averageChange = 0; task->actx->changeCount = 0; } airThreadMutexUnlock(task->actx->changeMutex); /* force all threads to line up here, once per iteration */ airThreadBarrierWait(task->actx->iterBarrier); } if (iter == task->actx->maxIteration) { /* HEY: all threads will agree on this, right? */ task->actx->stop = alanStopMaxIteration; } /* else: the non-alanStopNot value of task->actx->stop made us stop */ return _task; }
int nrrdArithIterAffine(Nrrd *nout, NrrdIter *minIn, NrrdIter *in, NrrdIter *maxIn, NrrdIter *minOut, NrrdIter *maxOut, int clamp) { static const char me[]="nrrdArithInterAffine"; double (*ins)(void *v, size_t I, double d), mini, vin, maxi, mino, maxo, vout; const Nrrd *nin; char *contA, *contB, *contC, *contD, *contE; size_t I, N; if (!(nout && minIn && in && maxIn && minOut && maxOut)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } nin = (_NRRD_ITER_NRRD(in) ? _NRRD_ITER_NRRD(in) : (_NRRD_ITER_NRRD(minIn) ? _NRRD_ITER_NRRD(minIn) : (_NRRD_ITER_NRRD(maxIn) ? _NRRD_ITER_NRRD(maxIn) : (_NRRD_ITER_NRRD(minOut) ? _NRRD_ITER_NRRD(minOut) : _NRRD_ITER_NRRD(maxOut))))); if (!nin) { biffAddf(NRRD, "%s: can't operate solely on fixed values", me); return 1; } if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s: couldn't initialize output", me); return 1; } N = nrrdElementNumber(nin); ins = nrrdDInsert[nout->type]; for (I=0; I<N; I++) { mini = nrrdIterValue(minIn); vin = nrrdIterValue(in); maxi = nrrdIterValue(maxIn); mino = nrrdIterValue(minOut); maxo = nrrdIterValue(maxOut); vout = AIR_AFFINE(mini, vin, maxi, mino, maxo); if (clamp) { double mmin = AIR_MIN(mino, maxo); double mmax = AIR_MAX(mino, maxo); vout = AIR_CLAMP(mmin, vout, mmax); } ins(nout->data, I, vout); } contA = nrrdIterContent(in); contB = nrrdIterContent(minIn); contC = nrrdIterContent(maxIn); contD = nrrdIterContent(maxOut); contE = nrrdIterContent(maxOut); /* HEY: same annoyance about order of arguments as in function above */ if (_nrrdContentSet_va(nout, "affine", contA, "%s,%s,%s,%s", contB, contC, contD, contE)) { biffAddf(NRRD, "%s:", me); free(contA); free(contB); free(contC); free(contD); free(contE); return 1; } free(contA); free(contB); free(contC); free(contD); free(contE); 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; } } }
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); }
/* ** Do asynchronous update of positions in "npos', based on force ** calculations wherein the distances are normalized "edge". Using a ** small "edge" allows forces to either underflow to zero, or be ** finite, instead of exploding to infinity, for high exponents. ** ** The smallest seen edge length is recorded in "*edgeMin", which is ** initialized to the given "edge". This allows, for example, the ** caller to try again with a smaller edge normalization. ** ** The mean velocity of the points through the update is recorded in ** "*meanVel". ** ** Based on the observation that when using large exponents, numerical ** difficulties arise from the (force-based) update of the positions ** of the two (or few) closest particles, this function puts a speed ** limit (variable "limit") on the distance a particle may move during ** update, expressed as a fraction of the normalizing edge length. ** "limit" has been set heuristically, according to the exponent (we ** have to clamp speeds more aggresively with higher exponents), as ** well as (even more heuristically) according to the number of times ** the step size has been decreased. This latter factor has to be ** bounded, so that the update is not unnecessarily bounded when the ** step size gets very small at the last stages of computation. ** Without the step-size-based speed limit, the step size would ** sometimes (e.g. num=200, expo=300) have to reduced to a miniscule ** value, which slows subsequent convergence terribly. ** ** this function is not static, though it could be, so that mac's ** "Sampler" app can profile this */ int _tenGradientUpdate(double *meanVel, double *edgeMin, Nrrd *npos, double edge, tenGradientParm *tgparm) { /* static const char me[]="_tenGradientUpdate"; */ double *pos, newpos[3], grad[3], ngrad[3], dir[3], len, rep, step, diff[3], limit, expo; int num, ii, jj, E; E = 0; pos = AIR_CAST(double *, npos->data); num = AIR_UINT(npos->axis[1].size); *meanVel = 0; *edgeMin = edge; expo = tgparm->expo ? tgparm->expo : tgparm->expo_d; limit = expo*AIR_MIN(sqrt(expo), log(1 + tgparm->initStep/tgparm->step)); for (ii=0; ii<num; ii++) { ELL_3V_SET(grad, 0, 0, 0); for (jj=0; jj<num; jj++) { if (ii == jj) { continue; } ELL_3V_SUB(dir, pos + 3*ii, pos + 3*jj); ELL_3V_NORM(dir, dir, len); *edgeMin = AIR_MIN(*edgeMin, len); if (tgparm->expo) { rep = airIntPow(edge/len, tgparm->expo+1); } else { rep = pow(edge/len, tgparm->expo_d+1); } ELL_3V_SCALE_INCR(grad, rep/num, dir); if (!tgparm->single) { ELL_3V_ADD2(dir, pos + 3*ii, pos + 3*jj); ELL_3V_NORM(dir, dir, len); *edgeMin = AIR_MIN(*edgeMin, len); if (tgparm->expo) { rep = airIntPow(edge/len, tgparm->expo+1); } else { rep = pow(edge/len, tgparm->expo_d+1); } ELL_3V_SCALE_INCR(grad, rep/num, dir); } } ELL_3V_NORM(ngrad, grad, len); if (!( AIR_EXISTS(len) )) { /* things blew up, either in incremental force additions, or in the attempt at normalization */ E = 1; *meanVel = AIR_NAN; break; } if (0 == len) { /* if the length of grad[] underflowed to zero, we can legitimately zero out ngrad[] */ ELL_3V_SET(ngrad, 0, 0, 0); } step = AIR_MIN(len*tgparm->step, edge/limit); ELL_3V_SCALE_ADD2(newpos, 1.0, pos + 3*ii, step, ngrad); ELL_3V_NORM(newpos, newpos, len); ELL_3V_SUB(diff, pos + 3*ii, newpos); *meanVel += ELL_3V_LEN(diff); ELL_3V_COPY(pos + 3*ii, newpos); } *meanVel /= num; return E; }
int main(int argc, const char *argv[]) { const char *me; char *err, *outS; hestOpt *hopt=NULL; airArray *mop; limnPolyData *pld, *pldSub; gageContext *gctx=NULL; gagePerVolume *pvl; Nrrd *nin, *nmeas; double kparm[3], strength, scaling[3]; seekContext *sctx; FILE *file; unsigned int ncc; size_t samples[3]; gageKind *kind; char *itemGradS; /* , *itemEvalS[2], *itemEvecS[2]; */ int itemGrad; /* , itemEval[2], itemEvec[2]; */ int E; me = argv[0]; hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input volume to analyze", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "k", "kind", airTypeOther, 1, 1, &kind, NULL, "\"kind\" of volume (\"scalar\", \"vector\", \"tensor\")", NULL, NULL, &probeKindHestCB); hestOptAdd(&hopt, "s", "strength", airTypeDouble, 1, 1, &strength, "0.01", "strength"); hestOptAdd(&hopt, "gi", "grad item", airTypeString, 1, 1, &itemGradS, NULL, "item for gradient vector"); hestOptAdd(&hopt, "c", "scaling", airTypeDouble, 3, 3, scaling, "1 1 1", "amount by which to up/down-sample on each spatial axis"); hestOptAdd(&hopt, "n", "# CC", airTypeUInt, 1, 1, &ncc, "0", "if non-zero, number of CC to save"); hestOptAdd(&hopt, "o", "output LMPD", airTypeString, 1, 1, &outS, "out.lmpd", "output file to save LMPD into"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); itemGrad = airEnumVal(kind->enm, itemGradS); pld = limnPolyDataNew(); airMopAdd(mop, pld, (airMopper)limnPolyDataNix, airMopAlways); pldSub = limnPolyDataNew(); airMopAdd(mop, pldSub, (airMopper)limnPolyDataNix, airMopAlways); file = airFopen(outS, stdout, "w"); airMopAdd(mop, file, (airMopper)airFclose, airMopAlways); sctx = seekContextNew(); airMopAdd(mop, sctx, (airMopper)seekContextNix, airMopAlways); gctx = gageContextNew(); airMopAdd(mop, gctx, (airMopper)gageContextNix, airMopAlways); ELL_3V_SET(kparm, 1, 1.0, 0.0); if (!(pvl = gagePerVolumeNew(gctx, nin, kind)) || gagePerVolumeAttach(gctx, pvl) || gageKernelSet(gctx, gageKernel00, nrrdKernelBCCubic, kparm) || gageKernelSet(gctx, gageKernel11, nrrdKernelBCCubicD, kparm) || gageKernelSet(gctx, gageKernel22, nrrdKernelBCCubicDD, kparm) || gageQueryItemOn(gctx, pvl, itemGrad) || gageQueryItemOn(gctx, pvl, gageSclHessEval) || gageQueryItemOn(gctx, pvl, gageSclHessEval2) || gageQueryItemOn(gctx, pvl, gageSclHessEvec) || gageQueryItemOn(gctx, pvl, gageSclHessEvec2) || gageUpdate(gctx)) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } seekVerboseSet(sctx, 10); E = 0; if (!E) E |= seekDataSet(sctx, NULL, gctx, 0); ELL_3V_SET(samples, scaling[0]*nin->axis[kind->baseDim + 0].size, scaling[1]*nin->axis[kind->baseDim + 1].size, scaling[2]*nin->axis[kind->baseDim + 2].size); if (!E) E |= seekSamplesSet(sctx, samples); if (!E) E |= seekItemGradientSet(sctx, itemGrad); if (!E) E |= seekItemEigensystemSet(sctx, gageSclHessEval, gageSclHessEvec); if (!E) E |= seekItemNormalSet(sctx, gageSclHessEvec2); if (!E) E |= seekStrengthUseSet(sctx, AIR_TRUE); if (!E) E |= seekStrengthSet(sctx, -1, strength); if (!E) E |= seekItemStrengthSet(sctx, gageSclHessEval2); if (!E) E |= seekNormalsFindSet(sctx, AIR_TRUE); if (!E) E |= seekTypeSet(sctx, seekTypeRidgeSurface); if (!E) E |= seekUpdate(sctx); if (!E) E |= seekExtract(sctx, pld); if (E) { airMopAdd(mop, err = biffGetDone(SEEK), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } fprintf(stderr, "%s: extraction time = %g\n", me, sctx->time); nmeas = nrrdNew(); airMopAdd(mop, nmeas, (airMopper)nrrdNuke, airMopAlways); if (limnPolyDataVertexWindingFix(pld, AIR_TRUE) || limnPolyDataVertexWindingFlip(pld) || limnPolyDataVertexNormals(pld) || limnPolyDataCCFind(pld) || limnPolyDataPrimitiveArea(nmeas, pld) || limnPolyDataPrimitiveSort(pld, nmeas)) { err = biffGetDone(LIMN); fprintf(stderr, "%s: trouble sorting:\n%s", me, err); free(err); } if (ncc > 1) { double *meas; unsigned int ccIdx; nrrdSave("meas.nrrd", nmeas, NULL); ncc = AIR_MIN(ncc, nmeas->axis[0].size); meas = AIR_CAST(double *, nmeas->data); for (ccIdx=ncc; ccIdx<nmeas->axis[0].size; ccIdx++) { meas[ccIdx] = 0.0; } if (!E) E |= limnPolyDataPrimitiveSelect(pldSub, pld, nmeas); if (!E) E |= limnPolyDataWriteLMPD(file, pldSub); } else {
/* ******** airArrayLenSet() ** ** Set the length of the array, allocating or freeing as needed ** ** returns 1 on error, otherwise 0 if okay ** possible errors: bogus arguments, or couldn't allocate new memory segment ** ** In case we can't allocate the new space, the old space is left untouched, ** however if the new length is smaller, the free/done callbacks will ** have been called on invalidated elements ** ** NB: this used to have a "boolean" return to indicate allocation ** error, but almost nothing in Teem actually did the error checking. ** Now conscientious users can look at NULL-ity of a->data to detect ** such an error. */ void airArrayLenSet(airArray *a, unsigned int newlen) { unsigned int newsize; int ii; void *addr, *newdata; if (!a) { /* user is a moron, what can you do */ return; } if (newlen == a->len) { /* nothing to do */ return; } /* call freeCB/doneCB on all the elements which are going bye-bye */ if (newlen < a->len && (a->freeCB || a->doneCB)) { for (ii=a->len-1; ii>=(int)newlen; ii--) { addr = (char*)(a->data) + ii*a->unit; if (a->freeCB) { (a->freeCB)(*((void**)addr)); } else { (a->doneCB)(addr); } } } newsize = newlen ? (newlen-1)/a->incr + 1 : 0; if (newsize != a->size) { /* we have to change the size of the array */ if (newsize) { /* array should be bigger or smaller, but not zero-length */ if (newsize > a->size || (newsize < a->size && !(a->noReallocWhenSmaller)) ) { newdata = calloc(newsize*a->incr, a->unit); if (!newdata) { free(a->data); _airSetData(a, NULL); return; } memcpy(newdata, a->data, AIR_MIN(a->len*a->unit, newsize*a->incr*a->unit)); free(a->data); _airSetData(a, newdata); a->size = newsize; } } else { /* array should be zero-length */ free(a->data); _airSetData(a, NULL); a->size = newsize; } } /* else new size is still within current allocated length, and neither "size" nor "data" need to change */ /* call allocCB/initCB on newly created elements */ if (newlen > a->len && (a->allocCB || a->initCB)) { for (ii=newlen; ii<(int)(a->len); ii++) { addr = (char*)(a->data) + ii*a->unit; if (a->allocCB) { *((void**)addr) = (a->allocCB)(); } else { (a->initCB)(addr); } } } _airLenSet(a, newlen); return; }
int unrrdu_mlutMain(int argc, char **argv, char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err; Nrrd *nin, **_nmlut, *nmlut, *nout; airArray *mop; int typeOut, rescale, pret, blind8BitRange; unsigned int _nmlutLen, mapAxis; double min, max; NrrdRange *range=NULL; hestOptAdd(&opt, "m,map", "mlut", airTypeOther, 1, -1, &_nmlut, NULL, "one nrrd of lookup tables to map input nrrd through, or, " "list of nrrds which contain the individual entries of " "the lookup table at each voxel, which will be joined together.", &_nmlutLen, NULL, nrrdHestNrrd); hestOptAdd(&opt, "r,rescale", NULL, airTypeInt, 0, 0, &rescale, NULL, "rescale the input values from the input range to the " "lut domain. The lut domain is either explicitly " "defined by the axis min,max along axis 0 or 1, or, it " "is implicitly defined as zero to the length of that axis " "minus one."); hestOptAdd(&opt, "min,minimum", "value", airTypeDouble, 1, 1, &min, "nan", "Low end of input range. Defaults to lowest value " "found in input nrrd. Explicitly setting this is useful " "only with rescaling (\"-r\")"); hestOptAdd(&opt, "max,maximum", "value", airTypeDouble, 1, 1, &max, "nan", "High end of input range. Defaults to highest value " "found in input nrrd. Explicitly setting this is useful " "only with rescaling (\"-r\")"); hestOptAdd(&opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange, nrrdStateBlind8BitRange ? "true" : "false", "Whether to know the range of 8-bit data blindly " "(uchar is always [0,255], signed char is [-128,127]). " "Explicitly setting this is useful only with rescaling (\"-r\")"); hestOptAdd(&opt, "t,type", "type", airTypeOther, 1, 1, &typeOut, "default", "specify the type (\"int\", \"float\", etc.) of the " "output nrrd. " "By default (not using this option), the output type " "is the lut's type.", NULL, NULL, &unrrduHestMaybeTypeCB); OPT_ADD_NIN(nin, "input nrrd"); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_mlutInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); /* by the end of this block we need to have nmlut and mapAxis */ if (1 == _nmlutLen) { /* we got the mlut as a single nrrd */ nmlut = _nmlut[0]; mapAxis = nmlut->dim - nin->dim - 1; /* its not our job to do real error checking ... */ mapAxis = AIR_MIN(mapAxis, nmlut->dim - 1); } else { /* we have to join together multiple nrrds to get the mlut */ nmlut = nrrdNew(); airMopAdd(mop, nmlut, (airMopper)nrrdNuke, airMopAlways); /* assume that mlut component nrrds are all compatible sizes, nrrdJoin will fail if they aren't */ mapAxis = _nmlut[0]->dim - nin->dim; if (nrrdJoin(nmlut, (const Nrrd**)_nmlut, _nmlutLen, mapAxis, AIR_TRUE)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble joining mlut:\n%s", me, err); airMopError(mop); return 1; } /* set these if they were given, they'll be NaN otherwise */ nmlut->axis[mapAxis].min = min; nmlut->axis[mapAxis].max = max; } if (!( AIR_EXISTS(nmlut->axis[mapAxis].min) && AIR_EXISTS(nmlut->axis[mapAxis].max) )) { rescale = AIR_TRUE; } if (rescale) { range = nrrdRangeNew(min, max); airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); nrrdRangeSafeSet(range, nin, blind8BitRange); } if (nrrdTypeDefault == typeOut) { typeOut = nmlut->type; } if (nrrdApplyMulti1DLut(nout, nin, range, nmlut, typeOut, rescale)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble applying multi-LUT:\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, *ndist, *nmask, *nupdate, *nout; NrrdKernelSpec *k00, *k11, *k22; int E; unsigned int sx, sy, sz, xi, yi, zi, si, iter, maxIter; gageContext *ctx; gagePerVolume *pvl; double dt, *dist, *mask, *update, thresh, eps, rmsMin; const double *valu, *gmag, *mcrv; 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, "k00", "kernel", airTypeOther, 1, 1, &k00, "tent", "k00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k11", "kernel", airTypeOther, 1, 1, &k11, "cubicd:0,0.5", "k00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k22", "kernel", airTypeOther, 1, 1, &k22, "cubicdd:1,0", "k00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "dt", "step", airTypeDouble, 1, 1, &dt, "0.17", "time step size"); hestOptAdd(&hopt, "th", "val", airTypeDouble, 1, 1, &thresh, "0.0", "the value to use for thresholding the input " "volume, to create the binary constraint image."); hestOptAdd(&hopt, "eps", "val", airTypeDouble, 1, 1, &eps, "0.05", "width of value bracket around threshold, to constrain the " "the update from letting to value, originally on either " "side of the threshold, from getting too close."); hestOptAdd(&hopt, "rms", "thresh", airTypeDouble, 1, 1, &rmsMin, "0.01", "RMS change below this terminates updates."); hestOptAdd(&hopt, "mi", "max", airTypeUInt, 1, 1, &maxIter, "100", "maximum # iterations"); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, "-", "fixed volume output"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, aaliasInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); ndist = nrrdNew(); nmask = nrrdNew(); nupdate = nrrdNew(); airMopAdd(mop, ndist, (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nmask, (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nupdate, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(ndist, nin, nrrdTypeDouble) || nrrdConvert(nmask, nin, nrrdTypeDouble) || nrrdConvert(nupdate, nin, nrrdTypeDouble)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't allocate buffers:\n%s", me, err); airMopError(mop); return 1; } dist = AIR_CAST(double *, ndist->data); mask = AIR_CAST(double *, nmask->data); update = AIR_CAST(double *, nupdate->data); 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, ndist, 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, gageSclValue); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclGradMag); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclMeanCurv); 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; } valu = gageAnswerPointer(ctx, pvl, gageSclValue); gmag = gageAnswerPointer(ctx, pvl, gageSclGradMag); mcrv = gageAnswerPointer(ctx, pvl, gageSclMeanCurv); sx = nin->axis[0].size; sy = nin->axis[1].size; sz = nin->axis[2].size; for (iter=0; iter<maxIter; iter++) { double rms; unsigned count; fprintf(stderr, "%s: iter %u: ", me, iter); 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++) { si = xi + sx*(yi + sy*zi); gageProbe(ctx, xi, yi, zi); update[si] = -dt*(*gmag)*(*mcrv); } } } rms = 0; count = 0; for (si=0; si<sx*sy*sz; si++) { double newval; if (update[si]) { /* NOTE: this update behavior is only slightly different than what's described in Equation 18 of the paper. That update rule ensures that the value never changes "sign" (with respect to the threshold value), by clamping the values above or below to 0.0. But worst-case scenario is that two adjacent samples that were on other side of the threshold (i.e. 0), could then equal the threshold, which would confuse a marching-cubes type algorithm. So the "eps" enforces a small value range around the treshold, and keeps the values on either side of it. */ newval = dist[si] + update[si]; if (mask[si] > thresh) { newval = AIR_MAX(newval, thresh+eps/2); } else { newval = AIR_MIN(newval, thresh-eps/2); } rms += (dist[si] - newval)*(dist[si] - newval); dist[si] = newval; count++; } } fprintf(stderr, "%s", airDoneStr(0, zi, sz-1, done)); rms /= count; rms = sqrt(rms); if (rms < rmsMin) { fprintf(stderr, "\n%s: RMS %g below threshold %g\n", me, rms, rmsMin); break; } else { fprintf(stderr, " rms = %g > %g\n", rms, rmsMin); } } if (iter == maxIter) { fprintf(stderr, "%s: hit max iter %u\n", me, maxIter); } nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdCopy(nout, ndist)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't allocate output:\n%s", me, err); airMopError(mop); return 1; } 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); }
int main(int argc, char *argv[]) { char *me; hestOpt *hopt=NULL; airArray *mop; char *errS, *outS, *covarS; Nrrd *_nodf, *nvec, *nhist, *ncovar; int bins; size_t size[NRRD_DIM_MAX]; float min; mop = airMopNew(); me = argv[0]; hestOptAdd(&hopt, "i", "odf", airTypeOther, 1, 1, &_nodf, NULL, "ODF volume to analyze", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "v", "odf", airTypeOther, 1, 1, &nvec, NULL, "list of vectors by which odf is sampled", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "min", "min", airTypeFloat, 1, 1, &min, "0.0", "ODF values below this are ignored, and per-voxel ODF is " "normalized to have sum 1.0. Use \"nan\" to subtract out " "the per-voxel min."); hestOptAdd(&hopt, "b", "bins", airTypeInt, 1, 1, &bins, "128", "number of bins in histograms"); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output file"); hestOptAdd(&hopt, "co", "covariance out", airTypeString, 1, 1, &covarS, "covar.nrrd", "covariance output file"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (!( nrrdTypeFloat == nvec->type )) { fprintf(stderr, "%s vector type (%s) not %s\n", me, airEnumStr(nrrdType, nvec->type), airEnumStr(nrrdType, nrrdTypeFloat)); airMopError(mop); return 1; } if (!( 2 == nvec->dim && 3 == nvec->axis[0].size )) { fprintf(stderr, "%s: nvec not a 2-D 3-by-N array\n", me); airMopError(mop); return 1; } if (!( _nodf->axis[0].size == nvec->axis[1].size )) { fprintf(stderr, "%s mismatch of _nodf->axis[0].size (%d) vs. " "nvec->axis[1].size (%d)\n", me, (int)_nodf->axis[0].size, (int)nvec->axis[1].size); airMopError(mop); return 1; } nrrdAxisInfoGet_nva(_nodf, nrrdAxisInfoSize, size); size[0] = bins; nhist = nrrdNew(); airMopAdd(mop, nhist, (airMopper)nrrdNuke, airMopAlways); if (nrrdMaybeAlloc_nva(nhist, nrrdTypeFloat, _nodf->dim, size)) { airMopAdd(mop, errS = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble allocating output:\n%s", me, errS); airMopError(mop); return 1; } ncovar = nrrdNew(); airMopAdd(mop, ncovar, (airMopper)nrrdNuke, airMopAlways); if (nrrdMaybeAlloc_va(ncovar, nrrdTypeFloat, 2, AIR_CAST(size_t, bins), AIR_CAST(size_t, bins))) { airMopAdd(mop, errS = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble allocating covariance output:\n%s", me, errS); airMopError(mop); return 1; } { /* we modify the lengths of the vectors here */ int NN, VV, ii, jj=0, kk, *anglut; float *odf, *hist, *covar, *vec, *vi, *vj, tmp, pvmin; double *mean; Nrrd *nodf, *nanglut; VV = nvec->axis[1].size; NN = nrrdElementNumber(_nodf)/VV; nanglut = nrrdNew(); airMopAdd(mop, nanglut, (airMopper)nrrdNuke, airMopAlways); if (nrrdMaybeAlloc_va(nanglut, nrrdTypeInt, 2, AIR_CAST(size_t, VV), AIR_CAST(size_t, VV))) { airMopAdd(mop, errS = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble allocating lookup table:\n%s", me, errS); airMopError(mop); return 1; } if (nrrdTypeFloat == _nodf->type) { nodf = _nodf; } else { nodf = nrrdNew(); airMopAdd(mop, nodf, (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(nodf, _nodf, nrrdTypeFloat)) { airMopAdd(mop, errS = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: trouble converting input:\n%s", me, errS); airMopError(mop); return 1; } } /* normalize lengths (MODIFIES INPUT) */ vec = (float*)nvec->data; for (ii=0; ii<=jj; ii++) { vi = vec + 3*ii; ELL_3V_NORM(vi, vi, tmp); } /* pre-compute pair-wise angles */ anglut = (int*)nanglut->data; for (jj=0; jj<VV; jj++) { vj = vec + 3*jj; for (ii=0; ii<=jj; ii++) { vi = vec + 3*ii; tmp = ELL_3V_DOT(vi, vj); tmp = AIR_ABS(tmp); tmp = acos(tmp)/(AIR_PI/2.0); anglut[ii + VV*jj] = airIndex(0.0, tmp, 1.0, bins); } } /* process all samples (MODIFIES INPUT if input was already float) */ odf = (float*)nodf->data; hist = (float*)nhist->data; for (kk=0; kk<NN; kk++) { if (!(kk % 100)) { fprintf(stderr, "%d/%d\n", kk, NN); } tmp = 0; if (AIR_EXISTS(min)) { for (ii=0; ii<VV; ii++) { odf[ii] = AIR_MAX(0.0, odf[ii]-min); tmp += odf[ii]; } } else { /* we do the more sketchy per-voxel min subtraction */ pvmin = airFPGen_f(airFP_POS_INF); for (ii=0; ii<VV; ii++) { pvmin = AIR_MIN(pvmin, odf[ii]); } for (ii=0; ii<VV; ii++) { odf[ii] -= pvmin; tmp += odf[ii]; } } if (tmp) { /* something left after subtracting out baseline isotropic */ for (ii=0; ii<VV; ii++) { odf[ii] /= tmp; } /* odf[] is normalized to 1.0 sum */ for (jj=0; jj<VV; jj++) { for (ii=0; ii<=jj; ii++) { tmp = odf[ii]*odf[jj]; hist[anglut[ii + VV*jj]] += tmp; } } } odf += VV; hist += bins; } odf = NULL; hist = NULL; /* find mean value of each bin (needed for covariance) */ mean = (double*)calloc(bins, sizeof(double)); if (!mean) { fprintf(stderr, "%s: couldn't allocate mean array", me); airMopError(mop); return 1; } hist = (float*)nhist->data; for (kk=0; kk<NN; kk++) { for (ii=0; ii<bins; ii++) { mean[ii] += hist[ii]; } hist += bins; } hist = NULL; for (ii=0; ii<bins; ii++) { mean[ii] /= NN; } /* make covariance matrix of from all histograms */ covar = (float*)ncovar->data; hist = (float*)nhist->data; for (kk=0; kk<NN; kk++) { for (jj=0; jj<bins; jj++) { for (ii=0; ii<jj; ii++) { tmp = (hist[ii] - mean[ii])*(hist[jj] - mean[jj]); covar[ii + bins*jj] += tmp; covar[jj + bins*ii] += tmp; } covar[jj + bins*jj] += (hist[jj] - mean[jj])*(hist[jj] - mean[jj]); } hist += bins; } hist = NULL; } if (nrrdSave(outS, nhist, NULL)) { airMopAdd(mop, errS = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't save output:\n%s\n", me, errS); airMopError(mop); return 1; } if (nrrdSave(covarS, ncovar, NULL)) { airMopAdd(mop, errS = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't save covariance output:\n%s\n", me, errS); airMopError(mop); return 1; } airMopOkay(mop); return 0; }