void _biffFindMaxAndSum(unsigned int *maxP, unsigned int *sumP, _biffEntry *ent) { unsigned int ii, len; if (!ent->num) { /* there's a key, but no error messages. Odd. */ *maxP = 1; *sumP = 1; return; } *maxP = *sumP = 0; for (ii=0; ii<ent->num; ii++) { len = (unsigned int)(strlen(ent->err[ii]) + strlen(ent->key) + strlen("[] \n")); *sumP += len; *maxP = AIR_MAX(*maxP, len); } *sumP += 1; *maxP += 1; return; }
void _nrrdMeasureHistoSum(void *ans, int ansType, const void *line, int lineType, size_t len, double axmin, double axmax) { double sum, hits, val, (*lup)(const void*, size_t); size_t ii; if (!(AIR_EXISTS(axmin) && AIR_EXISTS(axmax))) { axmin = -0.5; axmax = len-0.5; } lup = nrrdDLookup[lineType]; sum = 0; for (ii=0; ii<len; ii++) { val = NRRD_CELL_POS(axmin, axmax, len, ii); hits = lup(line, ii); hits = AIR_MAX(hits, 0); sum += hits*val; } nrrdDStore[ansType](ans, sum); }
/* ** computes (r1 - r0)/(log(r1) - log(r0)) */ double _tenQGL_blah(double rr0, double rr1) { double bb, ret; if (rr1 > rr0) { /* the bb calculation below could blow up, so we recurse with flipped order */ ret = _tenQGL_blah(rr1, rr0); } else { /* rr1 <= rr0 --> rr1/rr0 <= 1 --> rr1/rr0 - 1 <= 0 --> bb <= 0 */ /* and rr1 >= 0 --> rr1/rr0 >= 0 --> rr1/rr0 - 1 >= -1 --> bb >= -1 */ bb = rr0 ? (rr1/rr0 - 1) : 0; if (bb > -0.0001) { ret = rr0*(1 + bb*(0.5001249976477329 - bb*(7.0/6 + bb*(1.0/6 - bb/720.0)))); } else { /* had trouble finding a high-quality approximation for b near -1 */ bb = AIR_MAX(bb, -1 + 100*FLT_EPSILON); ret = rr0*bb/log(bb + 1); } } return ret; }
static int _nrrdEncodingHex_write(FILE *file, const void *_data, size_t elNum, const Nrrd *nrrd, NrrdIoState *nio) { /* static const char me[]="_nrrdEncodingHex_write"; */ const unsigned char *data; size_t byteIdx, byteNum; unsigned int bytesPerLine; bytesPerLine = AIR_MAX(1, nio->charsPerLine/2); data = AIR_CAST(const unsigned char*, _data); byteNum = elNum*nrrdElementSize(nrrd); for (byteIdx=0; byteIdx<byteNum; byteIdx++) { fprintf(file, "%c%c", _nrrdWriteHexTable[(*data)>>4], _nrrdWriteHexTable[(*data)&15]); if (bytesPerLine-1 == byteIdx % bytesPerLine) { fprintf(file, "\n"); } data++; } /* just to be sure, we always end with a carraige return */ fprintf(file, "\n"); 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; } } }
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; }
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; }
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; }
/* ** this is responsible for the header-relative path processing ** ** NOTE: if the filename is "-", then because it does not start with '/', ** it would normally be prefixed by nio->path, so it needs special handling ** ** NOTE: this should work okay with nio->headerStringRead, I think ... */ int nrrdIoStateDataFileIterNext(FILE **fileP, NrrdIoState *nio, int reading) { char me[]="nrrdIoStateDataFileIterNext", *err; char *fname=NULL; int ii, needPath; unsigned int num, fi; size_t maxl; airArray *mop; mop = airMopNew(); airMopAdd(mop, (void*)fileP, (airMopper)airSetNull, airMopOnError); if (!fileP) { if ((err = (char*)malloc(BIFF_STRLEN))) { sprintf(err, "%s: got NULL pointer", me); biffAdd(NRRD, err); free(err); } airMopError(mop); return 1; } if (!_nrrdDataFNNumber(nio)) { if ((err = (char*)malloc(BIFF_STRLEN))) { sprintf(err, "%s: there appear to be zero datafiles!", me); biffAdd(NRRD, err); free(err); } airMopError(mop); return 1; } nio->dataFNIndex++; if (nio->dataFNIndex >= (int)_nrrdDataFNNumber(nio)) { /* there is no next data file, but we don't make that an error */ nio->dataFNIndex = _nrrdDataFNNumber(nio); airMopOkay(mop); *fileP = NULL; return 0; } /* HEY: some of this error checking is done far more often than needed */ if (nio->dataFNFormat || nio->dataFNArr->len) { needPath = AIR_FALSE; maxl = 0; if (nio->dataFNFormat) { needPath = _NEED_PATH(nio->dataFNFormat); /* assuming 10-digit integers is plenty big */ maxl = 10 + strlen(nio->dataFNFormat); } else { for (fi=0; fi<nio->dataFNArr->len; fi++) { needPath |= _NEED_PATH(nio->dataFN[fi]); maxl = AIR_MAX(maxl, strlen(nio->dataFN[fi])); } } if (needPath && !airStrlen(nio->path)) { if ((err = (char*)malloc(BIFF_STRLEN))) { sprintf(err, "%s: need nio->path for header-relative datafiles", me); biffAdd(NRRD, err); free(err); } airMopError(mop); return 1; } fname = (char*)malloc(airStrlen(nio->path) + strlen("/") + maxl + 1); if (!fname) { if ((err = (char*)malloc(BIFF_STRLEN))) { sprintf(err, "%s: couldn't allocate filename buffer", me); biffAdd(NRRD, err); free(err); } airMopError(mop); return 1; } airMopAdd(mop, fname, airFree, airMopAlways); } if (nio->dataFNFormat) { /* ---------------------------------------------------------- */ /* --------- base.%d <min> <max> <step> [<dim>] ------------- */ /* ---------------------------------------------------------- */ num = 0; for (ii = nio->dataFNMin; ((nio->dataFNStep > 0 && ii <= nio->dataFNMax) || (nio->dataFNStep < 0 && ii >= nio->dataFNMax)); ii += nio->dataFNStep) { if ((int)num == nio->dataFNIndex) { /* HEY scrutinize cast */ break; } num += 1; } if (_NEED_PATH(nio->dataFNFormat)) { strcpy(fname, nio->path); strcat(fname, "/"); sprintf(fname + strlen(nio->path) + strlen("/"), nio->dataFNFormat, ii); } else { sprintf(fname, nio->dataFNFormat, ii); } } else if (nio->dataFNArr->len) { /* ---------------------------------------------------------- */ /* ------------------- LIST or single ----------------------- */ /* ---------------------------------------------------------- */ if (_NEED_PATH(nio->dataFN[nio->dataFNIndex])) { sprintf(fname, "%s/%s", nio->path, nio->dataFN[nio->dataFNIndex]); } else { strcpy(fname, nio->dataFN[nio->dataFNIndex]); } } /* else data file is attached */ if (nio->dataFNFormat || nio->dataFNArr->len) { *fileP = airFopen(fname, reading ? stdin : stdout, reading ? "rb" : "wb"); if (!(*fileP)) { if ((err = (char*)malloc(strlen(fname) + BIFF_STRLEN))) { sprintf(err, "%s: couldn't open \"%s\" (data file %d of %d) for %s", me, fname, nio->dataFNIndex+1, (int)_nrrdDataFNNumber(nio), reading ? "reading" : "writing"); biffAdd(NRRD, err); free(err); } airMopError(mop); return 1; } } else { /* data file is attached */ if (nio->headerStringRead) { /* except we were never reading from a file to begin with, but this isn't an error */ *fileP = NULL; } else { *fileP = nio->headerFile; } } airMopOkay(mop); return 0; }
/* ** _nrrdSprintFieldInfo ** ** this prints "<prefix><field>: <info>" into *strP (after allocating it for ** big enough, usually with a stupidly big margin of error), in a form ** suitable to be written to NRRD or other image headers. This will always ** print something (for valid inputs), even stupid <info>s like ** "(unknown endian)". It is up to the caller to decide which fields ** are worth writing, via _nrrdFieldInteresting(). ** ** NOTE: some of these fields make sense in non-NRRD files (e.g. all ** the per-axis information), but many only make sense in NRRD files. ** This is just one example of NRRD-format-specific stuff that is not ** in formatNRRD.c */ void _nrrdSprintFieldInfo(char **strP, char *prefix, const Nrrd *nrrd, NrrdIoState *nio, int field) { char me[]="_nrrdSprintFieldInfo", buff[AIR_STRLEN_MED], *fnb; double colvec[NRRD_SPACE_DIM_MAX]; const char *fs; unsigned int ii, dd, uintStrlen = 11, size_tStrlen = 33, doubleStrlen = 513; int fslen, fdlen, endi, maxl; if (!( strP && prefix && nrrd && AIR_IN_CL(1, nrrd->dim, NRRD_DIM_MAX) && AIR_IN_OP(nrrdField_unknown, field, nrrdField_last) )) { return; } if (!_nrrdFieldInteresting(nrrd, nio, field)) { *strP = airStrdup(""); } fs = airEnumStr(nrrdField, field); fslen = strlen(prefix) + strlen(fs) + strlen(": ") + 1; switch (field) { case nrrdField_comment: case nrrdField_keyvalue: fprintf(stderr, "%s: CONFUSION: why are you calling me on \"%s\"?\n", me, airEnumStr(nrrdField, nrrdField_comment)); *strP = airStrdup(""); break; case nrrdField_content: airOneLinify(nrrd->content); *strP = (char *)calloc(fslen + strlen(nrrd->content), sizeof(char)); sprintf(*strP, "%s%s: %s", prefix, fs, nrrd->content); break; case nrrdField_number: *strP = (char *)calloc(fslen + size_tStrlen, sizeof(char)); sprintf(*strP, "%s%s: " _AIR_SIZE_T_CNV, prefix, fs, nrrdElementNumber(nrrd)); break; case nrrdField_type: *strP = (char *)calloc(fslen + strlen(airEnumStr(nrrdType, nrrd->type)), sizeof(char)); sprintf(*strP, "%s%s: %s", prefix, fs, airEnumStr(nrrdType, nrrd->type)); break; case nrrdField_block_size: *strP = (char *)calloc(fslen + size_tStrlen, sizeof(char)); sprintf(*strP, "%s%s: " _AIR_SIZE_T_CNV, prefix, fs, nrrd->blockSize); break; case nrrdField_dimension: *strP = (char *)calloc(fslen + uintStrlen, sizeof(char)); sprintf(*strP, "%s%s: %d", prefix, fs, nrrd->dim); break; case nrrdField_space: *strP = (char *)calloc(fslen + strlen(airEnumStr(nrrdSpace, nrrd->space)), sizeof(char)); sprintf(*strP, "%s%s: %s", prefix, fs, airEnumStr(nrrdSpace, nrrd->space)); break; case nrrdField_space_dimension: *strP = (char *)calloc(fslen + uintStrlen, sizeof(char)); sprintf(*strP, "%s%s: %d", prefix, fs, nrrd->spaceDim); break; /* ---- begin per-axis fields ---- */ case nrrdField_sizes: *strP = (char *)calloc(fslen + nrrd->dim*(size_tStrlen + 1), sizeof(char)); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { sprintf(buff, " " _AIR_SIZE_T_CNV, nrrd->axis[ii].size); strcat(*strP, buff); } break; case nrrdField_spacings: *strP = (char *)calloc(fslen + nrrd->dim*(doubleStrlen + 1), sizeof(char)); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { airSinglePrintf(NULL, buff, " %g", nrrd->axis[ii].spacing); strcat(*strP, buff); } break; case nrrdField_thicknesses: *strP = (char *)calloc(fslen + nrrd->dim*(doubleStrlen + 1), sizeof(char)); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { airSinglePrintf(NULL, buff, " %g", nrrd->axis[ii].thickness); strcat(*strP, buff); } break; case nrrdField_axis_mins: *strP = (char *)calloc(fslen + nrrd->dim*(doubleStrlen + 1), sizeof(char)); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { airSinglePrintf(NULL, buff, " %g", nrrd->axis[ii].min); strcat(*strP, buff); } break; case nrrdField_axis_maxs: *strP = (char *)calloc(fslen + nrrd->dim*(doubleStrlen + 1), sizeof(char)); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { airSinglePrintf(NULL, buff, " %g", nrrd->axis[ii].max); strcat(*strP, buff); } break; case nrrdField_space_directions: *strP = (char *)calloc(fslen + nrrd->dim*nrrd->spaceDim*(doubleStrlen + strlen("(,) ")), sizeof(char)); sprintf(*strP, "%s%s: ", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { _nrrdStrcatSpaceVector(*strP, nrrd->spaceDim, nrrd->axis[ii].spaceDirection); if (ii < nrrd->dim-1) { strcat(*strP, " "); } } break; case nrrdField_centers: fdlen = 0; for (ii=0; ii<nrrd->dim; ii++) { fdlen += 1 + airStrlen(nrrd->axis[ii].center ? airEnumStr(nrrdCenter, nrrd->axis[ii].center) : NRRD_UNKNOWN); } *strP = (char *)calloc(fslen + fdlen, sizeof(char)); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { sprintf(buff, " %s", (nrrd->axis[ii].center ? airEnumStr(nrrdCenter, nrrd->axis[ii].center) : NRRD_UNKNOWN)); strcat(*strP, buff); } break; case nrrdField_kinds: fdlen = 0; for (ii=0; ii<nrrd->dim; ii++) { fdlen += 1 + airStrlen(nrrd->axis[ii].kind ? airEnumStr(nrrdKind, nrrd->axis[ii].kind) : NRRD_UNKNOWN); } *strP = (char *)calloc(fslen + fdlen, sizeof(char)); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { sprintf(buff, " %s", (nrrd->axis[ii].kind ? airEnumStr(nrrdKind, nrrd->axis[ii].kind) : NRRD_UNKNOWN)); strcat(*strP, buff); } break; case nrrdField_labels: fdlen = 0; for (ii=0; ii<nrrd->dim; ii++) { fdlen += airStrlen(nrrd->axis[ii].label) + 4; } *strP = (char *)calloc(fslen + fdlen, sizeof(char)); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { strcat(*strP, " \""); if (airStrlen(nrrd->axis[ii].label)) { strcat(*strP, nrrd->axis[ii].label); } strcat(*strP, "\""); } break; case nrrdField_units: fdlen = 0; for (ii=0; ii<nrrd->dim; ii++) { fdlen += airStrlen(nrrd->axis[ii].units) + 4; } *strP = (char *)calloc(fslen + fdlen, sizeof(char)); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->dim; ii++) { strcat(*strP, " \""); if (airStrlen(nrrd->axis[ii].units)) { strcat(*strP, nrrd->axis[ii].units); } strcat(*strP, "\""); } break; /* ---- end per-axis fields ---- */ case nrrdField_min: case nrrdField_max: /* we're basically a no-op, now that these fields became meaningless */ *strP = (char *)calloc(fslen + doubleStrlen, sizeof(char)); sprintf(*strP, "%s%s: 0.0", prefix, fs); strcat(*strP, buff); break; case nrrdField_old_min: *strP = (char *)calloc(fslen + doubleStrlen, sizeof(char)); sprintf(*strP, "%s%s: ", prefix, fs); airSinglePrintf(NULL, buff, "%g", nrrd->oldMin); strcat(*strP, buff); break; case nrrdField_old_max: *strP = (char *)calloc(fslen + doubleStrlen, sizeof(char)); sprintf(*strP, "%s%s: ", prefix, fs); airSinglePrintf(NULL, buff, "%g", nrrd->oldMax); strcat(*strP, buff); break; case nrrdField_endian: if (airEndianUnknown != nio->endian) { /* we know a specific endianness because either it was recorded as part of "unu make -h", or it was set (and data was possibly altered) as part of "unu save" */ endi = nio->endian; } else { /* we record our current architecture's endian because we're going to writing out data */ endi = AIR_ENDIAN; } *strP = (char *)calloc(fslen + strlen(airEnumStr(airEndian, endi)), sizeof(char)); sprintf(*strP, "%s%s: %s", prefix, fs, airEnumStr(airEndian, endi)); break; case nrrdField_encoding: *strP = (char *)calloc(fslen + strlen(nio->encoding->name), sizeof(char)); sprintf(*strP, "%s%s: %s", prefix, fs, nio->encoding->name); break; case nrrdField_line_skip: *strP = (char *)calloc(fslen + uintStrlen, sizeof(char)); sprintf(*strP, "%s%s: %d", prefix, fs, nio->lineSkip); break; case nrrdField_byte_skip: *strP = (char *)calloc(fslen + uintStrlen, sizeof(char)); sprintf(*strP, "%s%s: %d", prefix, fs, nio->byteSkip); break; case nrrdField_sample_units: airOneLinify(nrrd->sampleUnits); *strP = (char *)calloc(fslen + strlen(nrrd->sampleUnits), sizeof(char)); sprintf(*strP, "%s%s: \"%s\"", prefix, fs, nrrd->sampleUnits); break; case nrrdField_space_units: fdlen = 0; for (ii=0; ii<nrrd->spaceDim; ii++) { fdlen += airStrlen(nrrd->spaceUnits[ii]) + 4; } *strP = (char *)calloc(fslen + fdlen, sizeof(char)); sprintf(*strP, "%s%s:", prefix, fs); for (ii=0; ii<nrrd->spaceDim; ii++) { strcat(*strP, " \""); if (airStrlen(nrrd->spaceUnits[ii])) { strcat(*strP, nrrd->spaceUnits[ii]); } strcat(*strP, "\""); } break; case nrrdField_space_origin: *strP = (char *)calloc(fslen + nrrd->spaceDim*(doubleStrlen + strlen("(,) ")), sizeof(char)); sprintf(*strP, "%s%s: ", prefix, fs); _nrrdStrcatSpaceVector(*strP, nrrd->spaceDim, nrrd->spaceOrigin); break; case nrrdField_measurement_frame: *strP = (char *)calloc(fslen + (nrrd->spaceDim* nrrd->spaceDim*(doubleStrlen + strlen("(,) "))), sizeof(char)); sprintf(*strP, "%s%s: ", prefix, fs); for (dd=0; dd<nrrd->spaceDim; dd++) { for (ii=0; ii<nrrd->spaceDim; ii++) { colvec[ii] = nrrd->measurementFrame[dd][ii]; } _nrrdStrcatSpaceVector(*strP, nrrd->spaceDim, colvec); if (dd < nrrd->spaceDim-1) { strcat(*strP, " "); } } break; case nrrdField_data_file: /* NOTE: this comes last (nrrdField_data_file is the highest-valued member of the nrrdField* enum) because the "LIST" form of the data file specification requires that the following lines be the filenames */ /* error checking elsewhere: assumes there is data file info */ if (nio->dataFNFormat) { *strP = (char *)calloc(fslen + strlen(nio->dataFNFormat) + 4*uintStrlen, sizeof(char)); if (nio->dataFileDim == nrrd->dim-1) { sprintf(*strP, "%s%s: %s %d %d %d", prefix, fs, nio->dataFNFormat, nio->dataFNMin, nio->dataFNMax, nio->dataFNStep); } else { sprintf(*strP, "%s%s: %s %d %d %d %d", prefix, fs, nio->dataFNFormat, nio->dataFNMin, nio->dataFNMax, nio->dataFNStep, nio->dataFileDim); } } else if (nio->dataFNArr->len > 1) { maxl = 0; for (ii=0; ii<nio->dataFNArr->len; ii++) { maxl = AIR_MAX(maxl, (int)strlen(nio->dataFN[ii])); } *strP = (char *)calloc(fslen + strlen(NRRD_LIST_FLAG) + uintStrlen + nio->dataFNArr->len * (maxl + 1), sizeof(char)); fnb = (char *)calloc(fslen + strlen(NRRD_LIST_FLAG) + uintStrlen + maxl + 1, sizeof(char)); if (nio->dataFileDim == nrrd->dim-1) { sprintf(*strP, "%s%s: LIST\n", prefix, fs); } else { sprintf(*strP, "%s%s: LIST %d\n", prefix, fs, nio->dataFileDim); } for (ii=0; ii<nio->dataFNArr->len; ii++) { sprintf(fnb, "%s%s", nio->dataFN[ii], ii<nio->dataFNArr->len-1 ? "\n" : ""); strcat(*strP, fnb); } free(fnb); } else { /* there is some ambiguity between a "LIST" of length one, and a single explicit data filename, but that's harmless */ *strP = (char *)calloc(fslen + strlen("./") + strlen(nio->dataFN[0]) + 1, sizeof(char)); sprintf(*strP, "%s%s: %s%s", prefix, fs, /* this is a favor to older readers that can deal with this NRRD file because its being saved in a NRRD0003 (or below) version, so we don't want to confuse them by not having the old explicit header-relative flag */ (_nrrdFormatNRRD_whichVersion(nrrd, nio) < 4 ? "./" : ""), nio->dataFN[0]); } break; default: fprintf(stderr, "%s: CONFUSION: field %d unrecognized\n", me, field); break; } return; }
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 {
void _tenDwiGageAnswer(gageContext *ctx, gagePerVolume *pvl) { char me[]="_tenDwiGageAnswer"; unsigned int dwiIdx; tenDwiGageKindData *kindData; tenDwiGagePvlData *pvlData; double *dwiAll, dwiMean=0, tentmp[7]; kindData = AIR_CAST(tenDwiGageKindData *, pvl->kind->data); pvlData = AIR_CAST(tenDwiGagePvlData *, pvl->data); dwiAll = pvl->directAnswer[tenDwiGageAll]; if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageAll)) { /* done if doV */ if (ctx->verbose) { for (dwiIdx=0; dwiIdx<pvl->kind->valLen; dwiIdx++) { fprintf(stderr, "%s(%d+%g,%d+%g,%d+%g): dwi[%u] = %g\n", me, ctx->point.xi, ctx->point.xf, ctx->point.yi, ctx->point.yf, ctx->point.zi, ctx->point.zf, dwiIdx, dwiAll[dwiIdx]); } fprintf(stderr, "%s: type(ngrad) = %d = %s\n", me, kindData->ngrad->type, airEnumStr(nrrdType, kindData->ngrad->type)); } } /* if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageB0)) { if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageJustDWI)) { done if doV } */ /* HEY this isn't valid for multiple b-values */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageADC)) { double logdwi, logb0; logb0 = log(AIR_MAX(kindData->valueMin, pvl->directAnswer[tenDwiGageB0][0])); for (dwiIdx=1; dwiIdx<pvl->kind->valLen; dwiIdx++) { logdwi = log(AIR_MAX(kindData->valueMin, pvl->directAnswer[tenDwiGageJustDWI][dwiIdx-1])); pvl->directAnswer[tenDwiGageADC][dwiIdx-1] = (logb0 - logdwi)/kindData->bval; } } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageMeanDWIValue)) { dwiMean = 0; for (dwiIdx=1; dwiIdx<pvl->kind->valLen; dwiIdx++) { dwiMean += dwiAll[dwiIdx]; } dwiMean /= pvl->kind->valLen; pvl->directAnswer[tenDwiGageMeanDWIValue][0] = dwiMean; } /* note: the gage interface to tenEstimate functionality allows you exactly one kind of tensor estimation (per kind), so the function call to do the estimation is actually repeated over and over again; the copy into the answer buffer is what changes... */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLLS)) { tenEstimate1TensorSingle_d(pvlData->tec1, tentmp, dwiAll); TEN_T_COPY(pvl->directAnswer[tenDwiGageTensorLLS], tentmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLLSError)) { pvl->directAnswer[tenDwiGageTensorLLSError][0] = pvlData->tec1->errorDwi; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLLSErrorLog)) { pvl->directAnswer[tenDwiGageTensorLLSErrorLog][0] = pvlData->tec1->errorLogDwi; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorWLS)) { tenEstimate1TensorSingle_d(pvlData->tec1, tentmp, dwiAll); TEN_T_COPY(pvl->directAnswer[tenDwiGageTensorWLS], tentmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorNLS)) { tenEstimate1TensorSingle_d(pvlData->tec1, tentmp, dwiAll); TEN_T_COPY(pvl->directAnswer[tenDwiGageTensorNLS], tentmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorMLE)) { tenEstimate1TensorSingle_d(pvlData->tec1, tentmp, dwiAll); TEN_T_COPY(pvl->directAnswer[tenDwiGageTensorMLE], tentmp); } /* HEY: have to implement all the different kinds of errors */ /* BEGIN sneakiness ........ */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensor)) { gageItemEntry *item; item = pvl->kind->table + tenDwiGageTensor; TEN_T_COPY(pvl->directAnswer[tenDwiGageTensor], pvl->directAnswer[item->prereq[0]]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorError)) { gageItemEntry *item; item = pvl->kind->table + tenDwiGageTensorError; pvl->directAnswer[tenDwiGageTensorError][0] = pvl->directAnswer[item->prereq[0]][0]; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorErrorLog)) { gageItemEntry *item; item = pvl->kind->table + tenDwiGageTensorErrorLog; pvl->directAnswer[tenDwiGageTensorErrorLog][0] = pvl->directAnswer[item->prereq[0]][0]; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLikelihood)) { gageItemEntry *item; item = pvl->kind->table + tenDwiGageTensorLikelihood; pvl->directAnswer[tenDwiGageTensorLikelihood][0] = pvl->directAnswer[item->prereq[0]][0]; } /* END sneakiness ........ */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageFA)) { pvl->directAnswer[tenDwiGageFA][0] = pvl->directAnswer[tenDwiGageTensor][0] * tenAnisoTen_d(pvl->directAnswer[tenDwiGageTensor], tenAniso_FA); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorAllDWIError)) { const double *grads; int gradcount; double *ten, d; int i; /* HEY: should switch to tenEstimate-based DWI simulation */ ten = pvl->directAnswer[tenDwiGageTensor]; gradcount = pvl->kind->valLen -1; /* Dont count b0 */ grads = ((const double*) kindData->ngrad->data) +3; /* Ignore b0 grad */ for( i=0; i < gradcount; i++ ) { d = dwiAll[0]*exp(- pvlData->tec1->bValue * TEN_T3V_CONTR(ten, grads + 3*i)); pvl->directAnswer[tenDwiGageTensorAllDWIError][i] = dwiAll[i+1] - d; } } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorQSeg)) { const double *grads; int gradcount; double *twoten; unsigned int valIdx, E; twoten = pvl->directAnswer[tenDwiGage2TensorQSeg]; gradcount = pvl->kind->valLen -1; /* Dont count b0 */ grads = ((const double*) kindData->ngrad->data) +3; /* Ignore b0 grad */ if (dwiAll[0] != 0) { /* S0 = 0 */ _tenQball(pvlData->tec2->bValue, gradcount, dwiAll, grads, pvlData->qvals); _tenQvals2points(gradcount, pvlData->qvals, grads, pvlData->qpoints); _tenSegsamp2(gradcount, pvlData->qvals, grads, pvlData->qpoints, pvlData->wght + 1, pvlData->dists ); } else { /* stupid; should really return right here since data is garbage */ for (valIdx=1; valIdx < AIR_CAST(unsigned int, gradcount+1); valIdx++) { pvlData->wght[valIdx] = valIdx % 2; } } E = 0; for (valIdx=1; valIdx<pvl->kind->valLen; valIdx++) { if (!E) E |= tenEstimateSkipSet(pvlData->tec2, valIdx, pvlData->wght[valIdx]); } if (!E) E |= tenEstimateUpdate(pvlData->tec2); if (!E) E |= tenEstimate1TensorSingle_d(pvlData->tec2, twoten + 0, dwiAll); for (valIdx=1; valIdx<pvl->kind->valLen; valIdx++) { if (!E) E |= tenEstimateSkipSet(pvlData->tec2, valIdx, 1 - pvlData->wght[valIdx]); } if (!E) E |= tenEstimateUpdate(pvlData->tec2); if (!E) E |= tenEstimate1TensorSingle_d(pvlData->tec2, twoten + 7, dwiAll); if (E) { fprintf(stderr, "!%s: (trouble) %s\n", me, biffGetDone(TEN)); } /* hack: confidence for two-tensor fit */ twoten[0] = (twoten[0] + twoten[7])/2; twoten[7] = 0.5; /* fraction that is the first tensor (initial value) */ /* twoten[1 .. 6] = first tensor */ /* twoten[8 .. 13] = second tensor */ /* Compute fraction between tensors if not garbage in this voxel */ if (twoten[0] > 0.5) { double exp0,exp1,d,e=0,g=0, a=0,b=0; int i; for( i=0; i < gradcount; i++ ) { exp0 = exp(-pvlData->tec2->bValue * TEN_T3V_CONTR(twoten + 0, grads + 3*i)); exp1 = exp(-pvlData->tec2->bValue * TEN_T3V_CONTR(twoten + 7, grads + 3*i)); d = dwiAll[i+1] / dwiAll[0]; e = exp0 - exp1; g = d - exp1; a += .5*e*e; b += e*g; } twoten[7] = AIR_CLAMP(0, 0.5*(b/a), 1); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorQSegError)) { const double *grads; int gradcount; double *twoten, d; int i; /* HEY: should switch to tenEstimate-based DWI simulation */ if (dwiAll[0] != 0) { /* S0 = 0 */ twoten = pvl->directAnswer[tenDwiGage2TensorQSeg]; gradcount = pvl->kind->valLen -1; /* Dont count b0 */ grads = ((const double*) kindData->ngrad->data) +3; /* Ignore b0 grad */ pvl->directAnswer[tenDwiGage2TensorQSegError][0] = 0; for( i=0; i < gradcount; i++ ) { d = twoten[7]*exp(-pvlData->tec2->bValue * TEN_T3V_CONTR(twoten + 0, grads + 3*i)); d += (1 - twoten[7])*exp(-pvlData->tec2->bValue *TEN_T3V_CONTR(twoten + 7, grads + 3*i)); d = dwiAll[i+1]/dwiAll[0] - d; pvl->directAnswer[tenDwiGage2TensorQSegError][0] += d*d; } pvl->directAnswer[tenDwiGage2TensorQSegError][0] = sqrt( pvl->directAnswer[tenDwiGage2TensorQSegError][0] ); } else { /* HEY: COMPLETELY WRONG!! An error is not defined! */ pvl->directAnswer[tenDwiGage2TensorQSegError][0] = 0; } /* printf("%f\n",pvl->directAnswer[tenDwiGage2TensorQSegError][0]); */ } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorQSegAndError)) { double *twoten, *err, *twotenerr; twoten = pvl->directAnswer[tenDwiGage2TensorQSeg]; err = pvl->directAnswer[tenDwiGage2TensorQSegError]; twotenerr = pvl->directAnswer[tenDwiGage2TensorQSegAndError]; TEN_T_COPY(twotenerr + 0, twoten + 0); TEN_T_COPY(twotenerr + 7, twoten + 7); twotenerr[14] = err[0]; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorPeled)) { #if TEEM_LEVMAR #define PARAMS 4 double *twoTen, Cp /* , residual, AICSingFit, AICTwoFit */; /* Vars for the NLLS */ double guess[PARAMS], loBnd[PARAMS], upBnd[PARAMS], opts[LM_OPTS_SZ], *grad, *egrad, tenA[7], tenB[7], matA[9], matB[9], matTmp[9], rott[9]; unsigned int gi; int lmret; /* Pointer to the location where the two tensor will be written */ twoTen = pvl->directAnswer[tenDwiGage2TensorPeled]; /* Estimate the DWI error, error is given as standard deviation */ pvlData->tec2->recordErrorDwi = AIR_FALSE; /* Estimate the single tensor */ tenEstimate1TensorSingle_d(pvlData->tec2, pvlData->ten1, dwiAll); /* Get the eigenValues and eigen vectors for this tensor */ tenEigensolve_d(pvlData->ten1Eval, pvlData->ten1Evec, pvlData->ten1); /* Get westins Cp */ Cp = tenAnisoEval_d(pvlData->ten1Eval, tenAniso_Cp1); /* Calculate the residual, need the variance to sqr it */ /* residual = pvlData->tec2->errorDwi*pvlData->tec2->errorDwi; */ /* Calculate the AIC for single tensor fit */ /* AICSingFit = _tenComputeAIC(residual, pvlData->tec2->dwiNum, 6); */ /* the CP-based test is gone; caller's responsibility */ /* rotate DW gradients by inverse of eigenvector column matrix and place into pvlData->nten1EigenGrads (which has been allocated by _tenDwiGagePvlDataNew()) */ grad = AIR_CAST(double *, kindData->ngrad->data); egrad = AIR_CAST(double *, pvlData->nten1EigenGrads->data); for (gi=0; gi<kindData->ngrad->axis[1].size; gi++) { /* yes, this is also transforming some zero-length (B0) gradients; that's harmless */ ELL_3MV_MUL(egrad, pvlData->ten1Evec, grad); grad += 3; egrad += 3; } /* Lower and upper bounds for the NLLS routine */ loBnd[0] = 0.0; loBnd[1] = 0.0; loBnd[2] = -AIR_PI/2; loBnd[3] = -AIR_PI/2; upBnd[0] = pvlData->ten1Eval[0]*5; upBnd[1] = 1.0; upBnd[2] = AIR_PI/2; upBnd[3] = AIR_PI/2; /* Starting point for the NLLS */ guess[0] = pvlData->ten1Eval[0]; guess[1] = 0.5; guess[2] = AIR_PI/4; guess[3] = -AIR_PI/4; /* guess[2] = AIR_AFFINE(0, airDrandMT_r(pvlData->randState), 1, AIR_PI/6, AIR_PI/3); guess[3] = AIR_AFFINE(0, airDrandMT_r(pvlData->randState), 1, -AIR_PI/6, -AIR_PI/3); */ /* Fill in the constraints for the LM optimization, the threshold of error difference */ opts[0] = pvlData->levmarTau; opts[1] = pvlData->levmarEps1; opts[2] = pvlData->levmarEps2; opts[3] = pvlData->levmarEps3; /* Very imp to set this opt, note that only forward differences are used to approx Jacobian */ opts[4] = pvlData->levmarDelta; /* run NLLS, results are stored back into guess[] */ pvlData->levmarUseFastExp = AIR_FALSE; lmret = dlevmar_bc_dif(_tenLevmarPeledCB, guess, pvlData->tec2->dwi, PARAMS, pvlData->tec2->dwiNum, loBnd, upBnd, pvlData->levmarMaxIter, opts, pvlData->levmarInfo, NULL, NULL, pvlData); if (-1 == lmret) { ctx->errNum = 1; sprintf(ctx->errStr, "%s: dlevmar_bc_dif() failed!", me); } else { /* Get the AIC for the two tensor fit, use the levmarinfo to get the residual */ /* residual = pvlData->levmarInfo[1]/pvlData->tec2->dwiNum; AICTwoFit = _tenComputeAIC(residual, pvlData->tec2->dwiNum, 12); */ /* Form the tensors using the estimated pp, returned in guess */ _tenPeledRotate2D(tenA, guess[0], pvlData->ten1Eval[2], guess[2]); _tenPeledRotate2D(tenB, guess[0], pvlData->ten1Eval[2], guess[3]); TEN_T2M(matA, tenA); TEN_T2M(matB, tenB); ELL_3M_TRANSPOSE(rott, pvlData->ten1Evec); ELL_3M_MUL(matTmp, matA, pvlData->ten1Evec); ELL_3M_MUL(matA, rott, matTmp); ELL_3M_MUL(matTmp, matB, pvlData->ten1Evec); ELL_3M_MUL(matB, rott, matTmp); /* Copy two two tensors */ /* guess[1] is population fraction of first tensor */ if (guess[1] > 0.5) { twoTen[7] = guess[1]; TEN_M2T(twoTen + 0, matA); TEN_M2T(twoTen + 7, matB); } else { twoTen[7] = 1 - guess[1]; TEN_M2T(twoTen + 0, matB); TEN_M2T(twoTen + 7, matA); } twoTen[0] = 1; } #undef PARAMS #else double *twoTen; twoTen = pvl->directAnswer[tenDwiGage2TensorPeled]; TEN_T_SET(twoTen + 0, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); TEN_T_SET(twoTen + 7, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); fprintf(stderr, "%s: sorry, not compiled with TEEM_LEVMAR\n", me); #endif }
/* ** This does (non-optionally) use biff, to report convergence failures ** ** we do in fact require non-NULL tip, because it holds the buffers we need */ int _tenQGLInterpNEvec(double evecOut[9], const double *evecIn, /* size 9 -by- NN */ const double *wght, /* size NN */ unsigned int NN, tenInterpParm *tip) { static const char me[]="_tenQGLInterpNEvec"; double qOut[4], maxWght, len, /* odsum, */ dsum, rot[9]; unsigned int ii, centerIdx=0, fix, qiter; if (!( evecOut && evecIn && tip )) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } /* convert to quaternions */ for (ii=0; ii<NN; ii++) { ELL_3M_TRANSPOSE(rot, evecIn + 9*ii); ell_3m_to_q_d(tip->qIn + 4*ii, rot); } /* HEY: what should this be used for? variable odsum set but not used */ /* odsum = _tenQGL_q_interdot(¢erIdx, tip->qIn, tip->qInter, NN); */ /* find quaternion with maximal weight, use it as is (decree that its the right representative), and then align rest with that. This is actually principled; symmetry allows it */ centerIdx = 0; if (wght) { maxWght = wght[centerIdx]; for (ii=1; ii<NN; ii++) { if (wght[ii] > maxWght) { centerIdx = ii; maxWght = wght[centerIdx]; } } } for (ii=0; ii<NN; ii++) { if (ii == centerIdx) { continue; } _tenQGL_q_align(tip->qIn + 4*ii, tip->qIn + 4*centerIdx, tip->qIn + 4*ii); } dsum = _tenQGL_q_interdot(¢erIdx, tip->qIn, tip->qInter, NN); /* try to settle on tightest set of representatives */ qiter = 0; do { fix = 0; for (ii=0; ii<NN; ii++) { unsigned int ff; if (ii == centerIdx) { continue; } ff = _tenQGL_q_align(tip->qIn + 4*ii, tip->qIn + 4*centerIdx, tip->qIn + 4*ii); fix = AIR_MAX(fix, ff); } dsum = _tenQGL_q_interdot(¢erIdx, tip->qIn, tip->qInter, NN); if (tip->maxIter && qiter > tip->maxIter) { biffAddf(TEN, "%s: q tightening unconverged after %u iters; " "interdot = %g -> maxfix = %u; center = %u\n", me, tip->maxIter, dsum, fix, centerIdx); return 1; } qiter++; } while (fix); /* fprintf(stderr, "!%s: dsum %g --%u--> %g\n", me, odsum, qiter, dsum); */ /* make sure they're normalized */ for (ii=0; ii<NN; ii++) { ELL_4V_NORM(tip->qIn + 4*ii, tip->qIn + 4*ii, len); } /* compute iterated weighted mean, stored in qOut */ if (ell_q_avgN_d(qOut, &qiter, tip->qIn, tip->qBuff, wght, NN, tip->convEps, tip->maxIter)) { biffMovef(TEN, ELL, "%s: problem doing quaternion mean", me); return 1; } /* fprintf(stderr, "!%s: q avg converged in %u\n", me, qiter); */ /* finish, convert back to evec */ ell_q_to_3m_d(rot, qOut); ELL_3M_TRANSPOSE(evecOut, rot); return 0; }
int main(int argc, char *argv[]) { char *me, *outS; hestOpt *hopt; hestParm *hparm; airArray *mop; char *err, done[13]; Nrrd *nin, *nblur, *nout; NrrdKernelSpec *kb0, *kb1, *k00, *k11, *k22; NrrdResampleContext *rsmc; int E; unsigned int sx, sy, sz, xi, yi, zi, ai; gageContext *ctx; gagePerVolume *pvl; const double *gvec, *gmag, *evec0, *eval; double (*ins)(void *v, size_t I, double d); double (*lup)(const void *v, size_t I); double dotmax, dotpow, gmmax, evalshift, gmpow, _dotmax, _gmmax, scl, clamp; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input volume", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "kb0", "kernel", airTypeOther, 1, 1, &kb0, "guass:3,5", "kernel to use for pre-process blurring", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "kb1", "kernel", airTypeOther, 1, 1, &kb1, "cubic:1.5,1,0", "kernel to use for pos-process blurring", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k00", "kernel", airTypeOther, 1, 1, &k00, "cubic:1,0", "k00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k11", "kernel", airTypeOther, 1, 1, &k11, "cubicd:1,0", "k00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "k22", "kernel", airTypeOther, 1, 1, &k22, "cubicdd:1,0", "k00", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "dotmax", "dot", airTypeDouble, 1, 1, &dotmax, "5", "max effective value of dot(gvec, evec0)"); hestOptAdd(&hopt, "evs", "shift", airTypeDouble, 1, 1, &evalshift, "0", "negative shift to avoid changing mostly flat regions"); hestOptAdd(&hopt, "clamp", "clamp", airTypeDouble, 1, 1, &clamp, "nan", "if it exists, data value can't be forced below this"); hestOptAdd(&hopt, "dotpow", "pow", airTypeDouble, 1, 1, &dotpow, "1", "exponent for dot"); hestOptAdd(&hopt, "gmmax", "dot", airTypeDouble, 1, 1, &gmmax, "2", "max effective value of gmag"); hestOptAdd(&hopt, "gmpow", "pow", airTypeDouble, 1, 1, &gmpow, "1", "exponent for gmag"); hestOptAdd(&hopt, "scl", "scale", airTypeDouble, 1, 1, &scl, "0.1", "how much to scale hack to decrease input value"); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, "-", "fixed volume output"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, vhInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (!( 3 == nin->dim && nrrdTypeBlock != nin->type )) { fprintf(stderr, "%s: need a 3-D scalar nrrd (not %u-D %s)", me, nin->dim, airEnumStr(nrrdType, nin->type)); airMopError(mop); return 1; } nblur = nrrdNew(); airMopAdd(mop, nblur, (airMopper)nrrdNuke, airMopAlways); if (nrrdCopy(nblur, nin)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't allocate output:\n%s", me, err); airMopError(mop); return 1; } fprintf(stderr, "%s: pre-blurring ... ", me); fflush(stderr); rsmc = nrrdResampleContextNew(); airMopAdd(mop, rsmc, (airMopper)nrrdResampleContextNix, airMopAlways); E = AIR_FALSE; if (!E) E |= nrrdResampleDefaultCenterSet(rsmc, nrrdCenterCell); if (!E) E |= nrrdResampleNrrdSet(rsmc, nin); for (ai=0; ai<3; ai++) { if (!E) E |= nrrdResampleKernelSet(rsmc, ai, kb0->kernel, kb0->parm); if (!E) E |= nrrdResampleSamplesSet(rsmc, ai, nin->axis[ai].size); if (!E) E |= nrrdResampleRangeFullSet(rsmc, ai); } if (!E) E |= nrrdResampleBoundarySet(rsmc, nrrdBoundaryBleed); if (!E) E |= nrrdResampleTypeOutSet(rsmc, nrrdTypeDefault); if (!E) E |= nrrdResampleRenormalizeSet(rsmc, AIR_TRUE); if (!E) E |= nrrdResampleExecute(rsmc, nblur); if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error resampling nrrd:\n%s", me, err); airMopError(mop); return 1; } fprintf(stderr, "done.\n"); ctx = gageContextNew(); airMopAdd(mop, ctx, (airMopper)gageContextNix, airMopAlways); gageParmSet(ctx, gageParmRenormalize, AIR_TRUE); gageParmSet(ctx, gageParmCheckIntegrals, AIR_TRUE); E = 0; if (!E) E |= !(pvl = gagePerVolumeNew(ctx, nblur, gageKindScl)); if (!E) E |= gagePerVolumeAttach(ctx, pvl); if (!E) E |= gageKernelSet(ctx, gageKernel00, k00->kernel, k00->parm); if (!E) E |= gageKernelSet(ctx, gageKernel11, k11->kernel, k11->parm); if (!E) E |= gageKernelSet(ctx, gageKernel22, k22->kernel, k22->parm); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclGradVec); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclGradMag); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclHessEvec0); if (!E) E |= gageQueryItemOn(ctx, pvl, gageSclHessEval); if (!E) E |= gageUpdate(ctx); if (E) { airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } gvec = gageAnswerPointer(ctx, pvl, gageSclGradVec); gmag = gageAnswerPointer(ctx, pvl, gageSclGradMag); evec0 = gageAnswerPointer(ctx, pvl, gageSclHessEvec0); eval = gageAnswerPointer(ctx, pvl, gageSclHessEval); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); if (nrrdCopy(nout, nin)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't allocate output:\n%s", me, err); airMopError(mop); return 1; } if (!(nout->type == nin->type && nblur->type == nin->type)) { fprintf(stderr, "%s: whoa, types (%s %s %s) not all equal\n", me, airEnumStr(nrrdType, nin->type), airEnumStr(nrrdType, nblur->type), airEnumStr(nrrdType, nout->type)); } ins = nrrdDInsert[nout->type]; lup = nrrdDLookup[nout->type]; sx = nin->axis[0].size; sy = nin->axis[1].size; sz = nin->axis[2].size; gageProbe(ctx, 0, 0, 0); _dotmax = ELL_3V_DOT(gvec, evec0); _gmmax = *gmag; fprintf(stderr, "%s: hacking ", me); fflush(stderr); for (zi=0; zi<sz; zi++) { fprintf(stderr, "%s", airDoneStr(0, zi, sz-1, done)); fflush(stderr); for (yi=0; yi<sy; yi++) { for (xi=0; xi<sx; xi++) { size_t si; double dot, evl, gm, shift, in, out, mode; gageProbe(ctx, xi, yi, zi); si = xi + sx*(yi + sy*zi); dot = ELL_3V_DOT(gvec, evec0); _dotmax = AIR_MAX(_dotmax, dot); dot = AIR_ABS(dot); dot = 1 - AIR_MIN(dot, dotmax)/dotmax; dot = pow(dot, dotpow); evl = AIR_MAX(0, eval[0] - evalshift); mode = airMode3_d(eval); evl *= AIR_AFFINE(-1, mode, 1, 0, 1); _gmmax = AIR_MAX(_gmmax, *gmag); gm = 1 - AIR_MIN(*gmag, gmmax)/gmmax; gm = pow(gm, gmpow); shift = scl*gm*evl*dot; if (AIR_EXISTS(clamp)) { in = lup(nin->data, si); out = in - shift; out = AIR_MAX(out, clamp); shift = AIR_MAX(0, in - out); } ins(nout->data, si, shift); } } } fprintf(stderr, "\n"); fprintf(stderr, "%s: max dot seen: %g\n", me, _dotmax); fprintf(stderr, "%s: max gm seen: %g\n", me, _gmmax); fprintf(stderr, "%s: post-blurring ... ", me); fflush(stderr); E = AIR_FALSE; if (!E) E |= nrrdResampleNrrdSet(rsmc, nout); for (ai=0; ai<3; ai++) { if (!E) E |= nrrdResampleKernelSet(rsmc, ai, kb1->kernel, kb1->parm); if (!E) E |= nrrdResampleSamplesSet(rsmc, ai, nout->axis[ai].size); if (!E) E |= nrrdResampleRangeFullSet(rsmc, ai); } if (!E) E |= nrrdResampleExecute(rsmc, nblur); if (E) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error resampling nrrd:\n%s", me, err); airMopError(mop); return 1; } fprintf(stderr, "done.\n"); for (zi=0; zi<sz; zi++) { for (yi=0; yi<sy; yi++) { for (xi=0; xi<sx; xi++) { size_t si; double in, shift; si = xi + sx*(yi + sy*zi); in = lup(nin->data, si); shift = lup(nblur->data, si); ins(nout->data, si, in - shift); } } } if (nrrdSave(outS, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't save output:\n%s", me, err); airMopError(mop); return 1; } airMopOkay(mop); exit(0); }
/* ** _gageShapeSet ** ** we are serving two masters here. If ctx is non-NULL, we are being called ** from within gage, and we are to be lax or strict according to the settings ** of ctx->parm.requireAllSpacings and ctx->parm.requireEqualCenters. If ** ctx is NULL, gageShapeSet was called, in which case we go with lax ** behavior (nothing "required") ** ** This function has subsumed the old gageVolumeCheck, and hence has ** become this weird beast- part error checker and part (gageShape) ** initializer. Oh well... */ int _gageShapeSet(const gageContext *ctx, gageShape *shape, const Nrrd *nin, unsigned int baseDim) { char me[]="_gageShapeSet", err[BIFF_STRLEN]; int ai, cx, cy, cz, defCenter, statCalc[3]; unsigned int minsize, sx, sy, sz; const NrrdAxisInfo *ax[3]; double maxLen, defSpacing, vecA[4], vecB[3], vecC[3], vecD[4], matA[9], spcCalc[3], vecCalc[3][NRRD_SPACE_DIM_MAX], orig[NRRD_SPACE_DIM_MAX]; /* ------ basic error checking */ if (!( shape && nin )) { sprintf(err, "%s: got NULL pointer", me); biffAdd(GAGE, err); if (shape) { gageShapeReset(shape); } return 1; } if (nrrdCheck(nin)) { sprintf(err, "%s: basic nrrd validity check failed", me); biffMove(GAGE, err, NRRD); gageShapeReset(shape); return 1; } if (nrrdTypeBlock == nin->type) { sprintf(err, "%s: need a non-block type nrrd", me); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } if (!(nin->dim == 3 + baseDim)) { sprintf(err, "%s: nrrd should be %u-D, not %u-D", me, 3 + baseDim, nin->dim); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } ax[0] = &(nin->axis[baseDim+0]); ax[1] = &(nin->axis[baseDim+1]); ax[2] = &(nin->axis[baseDim+2]); /* if (1) { unsigned int ai; for (ai=0; ai<3; ai++) { fprintf(stderr, "!%s: ax[%u] spc = %g, sd = %g %g %g\n", me, ai, ax[ai]->spacing, ax[ai]->spaceDirection[0], ax[ai]->spaceDirection[1], ax[ai]->spaceDirection[2]); } } */ statCalc[0] = nrrdSpacingCalculate(nin, baseDim + 0, spcCalc + 0, vecCalc[0]); statCalc[1] = nrrdSpacingCalculate(nin, baseDim + 1, spcCalc + 1, vecCalc[1]); statCalc[2] = nrrdSpacingCalculate(nin, baseDim + 2, spcCalc + 2, vecCalc[2]); /* fprintf(stderr, "!%s: axis stat %d %d %d\n", me, statCalc[0], statCalc[1], statCalc[2]); */ /* see if nrrdSpacingCalculate ever *failed* */ if (nrrdSpacingStatusUnknown == statCalc[0] || nrrdSpacingStatusUnknown == statCalc[1] || nrrdSpacingStatusUnknown == statCalc[2]) { sprintf(err, "%s: nrrdSpacingCalculate trouble on axis %d, %d, or %d", me, baseDim + 0, baseDim + 1, baseDim + 2); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } /* see if nrrdSpacingCalculate encountered an axis with no space direction in a nrrd that nominally has a surrounding space */ if (nrrdSpacingStatusScalarWithSpace == statCalc[0] || nrrdSpacingStatusScalarWithSpace == statCalc[1] || nrrdSpacingStatusScalarWithSpace == statCalc[2]) { sprintf(err, "%s: nrrdSpacingCalculate weirdness on axis %d, %d, or %d", me, baseDim + 0, baseDim + 1, baseDim + 2); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } if (!( ( nrrdSpacingStatusDirection == statCalc[0] && nrrdSpacingStatusDirection == statCalc[1] && nrrdSpacingStatusDirection == statCalc[2]) || ( nrrdSpacingStatusDirection != statCalc[0] && nrrdSpacingStatusDirection != statCalc[1] && nrrdSpacingStatusDirection != statCalc[2]) )) { sprintf(err, "%s: inconsistent space directions use " "in axis %d, %d, and %d", me, baseDim + 0, baseDim + 1, baseDim + 2); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } if ( nrrdSpacingStatusDirection == statCalc[0] && nrrdSpacingStatusDirection == statCalc[1] && nrrdSpacingStatusDirection == statCalc[2]) { /* this will get reset to false in case of error */ shape->fromOrientation = AIR_TRUE; } else { shape->fromOrientation = AIR_FALSE; } /* oh yea, we should make sure the space dimension is right! */ if (shape->fromOrientation && 3 != nin->spaceDim) { sprintf(err, "%s: orientation space dimension %d != 3", me, nin->spaceDim); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } /* ------ find centering (set shape->center) */ /* HEY: when we have full orientation information (via spaceDirections and spaceOrigin) the centering information is moot for determining shape, but until all usage of gageShape stuff is properly overhauled to take orientation into account, we'll still set shape->center */ cx = ax[0]->center; cy = ax[1]->center; cz = ax[2]->center; if (ctx && ctx->parm.requireEqualCenters) { if (!( cx == cy && cx == cz )) { sprintf(err, "%s: axes %d,%d,%d centerings (%s,%s,%s) not equal", me, baseDim+0, baseDim+1, baseDim+2, airEnumStr(nrrdCenter, cx), airEnumStr(nrrdCenter, cy), airEnumStr(nrrdCenter, cz)); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } } else { if ( (nrrdCenterUnknown != cx && nrrdCenterUnknown != cy && cx != cy) || (nrrdCenterUnknown != cy && nrrdCenterUnknown != cz && cy != cz) || (nrrdCenterUnknown != cx && nrrdCenterUnknown != cz && cx != cz) ) { sprintf(err, "%s: two known centerings (of %s,%s,%s) are unequal", me, airEnumStr(nrrdCenter, cx), airEnumStr(nrrdCenter, cy), airEnumStr(nrrdCenter, cz)); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } } defCenter = ctx ? ctx->parm.defaultCenter : shape->defaultCenter; shape->center = (nrrdCenterUnknown != cx ? cx : (nrrdCenterUnknown != cy ? cy : (nrrdCenterUnknown != cz ? cz : defCenter))); /* ------ find sizes (set shape->size[0,1,2]) */ sx = ax[0]->size; sy = ax[1]->size; sz = ax[2]->size; minsize = (nrrdCenterCell == shape->center ? 1 : 2); /* HEY: perhaps this should be relaxed if we have full orientation info */ if (!(sx >= minsize && sy >= minsize && sz >= minsize )) { sprintf(err, "%s: sizes (%u,%u,%u) must all be >= %u " "(min number of %s-centered samples)", me, sx, sy, sz, minsize, airEnumStr(nrrdCenter, shape->center)); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } shape->size[0] = sx; shape->size[1] = sy; shape->size[2] = sz; /* ------ find spacings (set shape->spacing[0,1,2]) */ if (shape->fromOrientation) { shape->spacing[0] = AIR_ABS(spcCalc[0]); shape->spacing[1] = AIR_ABS(spcCalc[1]); shape->spacing[2] = AIR_ABS(spcCalc[2]); for (ai=0; ai<=2; ai++) { shape->volHalfLen[ai] = AIR_NAN; shape->voxLen[ai] = AIR_NAN; } } else { double xs, ys, zs; unsigned int num[3]; xs = ax[0]->spacing; ys = ax[1]->spacing; zs = ax[2]->spacing; if (ctx && ctx->parm.requireAllSpacings) { if (!( AIR_EXISTS(xs) && AIR_EXISTS(ys) && AIR_EXISTS(zs) )) { sprintf(err, "%s: spacings for axes %d,%d,%d don't all exist", me, baseDim+0, baseDim+1, baseDim+2); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } } /* there is no shape->defaultSpacing, we'll go out on a limb ... */ defSpacing = ctx ? ctx->parm.defaultSpacing : nrrdDefaultSpacing; xs = AIR_EXISTS(xs) ? xs : defSpacing; ys = AIR_EXISTS(ys) ? ys : defSpacing; zs = AIR_EXISTS(zs) ? zs : defSpacing; if (!( xs != 0 && ys != 0 && zs != 0 )) { sprintf(err, "%s: spacings (%g,%g,%g) for axes %d,%d,%d not all " "non-zero", me, xs, ys, zs, baseDim+0, baseDim+1, baseDim+2); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } /* ------ learn lengths for bounding nrrd in bi-unit cube (set shape->volHalfLen[0,1,2] and shape->voxLen[0,1,2]) */ shape->spacing[0] = AIR_ABS(xs); shape->spacing[1] = AIR_ABS(ys); shape->spacing[2] = AIR_ABS(zs); maxLen = 0.0; for (ai=0; ai<=2; ai++) { num[ai] = (nrrdCenterNode == shape->center ? shape->size[ai]-1 : shape->size[ai]); shape->volHalfLen[ai] = num[ai]*shape->spacing[ai]; maxLen = AIR_MAX(maxLen, shape->volHalfLen[ai]); } /* Thu Dec 13 02:45:01 EST 2007 fixed long-standing bug in handling vols without full orientation info: spacing[ai] was never scaled to account for being crammed into the bi-unit cube!! */ for (ai=0; ai<=2; ai++) { shape->volHalfLen[ai] /= maxLen; shape->spacing[ai] = shape->voxLen[ai] = 2*shape->volHalfLen[ai]/num[ai]; } } /* ------ set transform matrices */ if (shape->fromOrientation) { /* find translation vector (we check above that spaceDim == 3) */ nrrdSpaceOriginGet(nin, orig); if (!( AIR_EXISTS(orig[0]) && AIR_EXISTS(orig[1]) && AIR_EXISTS(orig[2]) )) { /* don't have origin, so set it to come from the middle of volume */ ELL_3V_SET(orig, 0.0f, 0.0f, 0.0f); ELL_3V_SCALE_INCR(orig, -(shape->size[0] - 1.0f)*spcCalc[0]/2.0f, vecCalc[0]); ELL_3V_SCALE_INCR(orig, -(shape->size[1] - 1.0f)*spcCalc[1]/2.0f, vecCalc[1]); ELL_3V_SCALE_INCR(orig, -(shape->size[2] - 1.0f)*spcCalc[2]/2.0f, vecCalc[2]); } vecD[3] = 0; ELL_3V_SCALE(vecD, spcCalc[0], vecCalc[0]); ELL_4MV_COL0_SET(shape->ItoW, vecD); ELL_3V_SCALE(vecD, spcCalc[1], vecCalc[1]); ELL_4MV_COL1_SET(shape->ItoW, vecD); ELL_3V_SCALE(vecD, spcCalc[2], vecCalc[2]); ELL_4MV_COL2_SET(shape->ItoW, vecD); vecD[3] = 1; ELL_3V_COPY(vecD, orig); ELL_4MV_COL3_SET(shape->ItoW, vecD); /* fprintf(stderr, "%s: %g (%g,%g,%g)\n", me, spcCalc[0], vecCalc[0][0], vecCalc[0][1], vecCalc[0][2]); fprintf(stderr, "%s: %g (%g,%g,%g)\n", me, spcCalc[1], vecCalc[1][0], vecCalc[1][1], vecCalc[1][2]); fprintf(stderr, "%s: %g (%g,%g,%g)\n", me, spcCalc[2], vecCalc[2][0], vecCalc[2][1], vecCalc[2][2]); */ } else { ELL_3V_SET(vecC, 0, 0, 0); _gageShapeUnitItoW(shape, vecA, vecC); ELL_3V_SET(vecC, 1, 0, 0); _gageShapeUnitItoW(shape, vecB, vecC); ELL_3V_SUB(vecD, vecB, vecA); vecD[3] = 0; ELL_4MV_COL0_SET(shape->ItoW, vecD); ELL_3V_SET(vecC, 0, 1, 0); _gageShapeUnitItoW(shape, vecB, vecC); ELL_3V_SUB(vecD, vecB, vecA); vecD[3] = 0; ELL_4MV_COL1_SET(shape->ItoW, vecD); ELL_3V_SET(vecC, 0, 0, 1); _gageShapeUnitItoW(shape, vecB, vecC); ELL_3V_SUB(vecD, vecB, vecA); vecD[3] = 0; ELL_4MV_COL2_SET(shape->ItoW, vecD); vecA[3] = 1; ELL_4MV_COL3_SET(shape->ItoW, vecA); } ell_4m_inv_d(shape->WtoI, shape->ItoW); ELL_34M_EXTRACT(matA, shape->ItoW); ell_3m_inv_d(shape->ItoWSubInv, matA); ELL_3M_TRANSPOSE(shape->ItoWSubInvTransp, shape->ItoWSubInv); return 0; }
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; }
static double _nrrdTernaryOpMax(double a, double b, double c) { b = AIR_MAX(b, c); return AIR_MAX(a, b); }
static double _nrrdBinaryOpMax(double a, double b) {return AIR_MAX(a,b);}
/* ******** unrrduCmdMain ** ** A "main" function for unu-like programs, which is very similar to ** teem/src/bin/unu.c:main(), and ** teem/src/bin/tend.c:main(), and ** teem/src/limn/test/lpu.c:main(). ** With more time (and a major Teem release), this function may change, ** and those programs may use this. ** ** A sneaky but basic issue is the const-correctness of how the hestParm ** is used; we'd like to take a const hestParm* to communicate parameters ** the caller has set, but the show-stopper is that unrrduCmd->main() ** takes a non-const hestParm, and it has to be that way, because some ** unu commands alter the given hparm (which probably shouldn't happen). ** Until that's fixed, we have a non-const hestParm* coming in here. */ int unrrduCmdMain(int argc, const char **argv, const char *cmd, const char *title, const unrrduCmd *const *cmdList, hestParm *_hparm, FILE *fusage) { int i, ret; const char *me; char *argv0 = NULL; hestParm *hparm; airArray *mop; me = argv[0]; /* parse environment variables first, in case they break nrrdDefault* or nrrdState* variables in a way that nrrdSanity() should see */ nrrdDefaultGetenv(); nrrdStateGetenv(); /* unu does some unu-specific environment-variable handling here */ nrrdSanityOrDie(me); mop = airMopNew(); if (_hparm) { hparm = _hparm; } else { hparm = hestParmNew(); airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hparm->elideSingleEnumType = AIR_TRUE; hparm->elideSingleOtherType = AIR_TRUE; hparm->elideSingleOtherDefault = AIR_FALSE; hparm->elideSingleNonExistFloatDefault = AIR_TRUE; hparm->elideMultipleNonExistFloatDefault = AIR_TRUE; hparm->elideSingleEmptyStringDefault = AIR_TRUE; hparm->elideMultipleEmptyStringDefault = AIR_TRUE; hparm->cleverPluralizeOtherY = AIR_TRUE; /* learning columns from current window; if ioctl is available if (1) { struct winsize ws; ioctl(1, TIOCGWINSZ, &ws); hparm->columns = ws.ws_col - 1; } */ hparm->columns = 78; } /* if there are no arguments, then we give general usage information */ if (1 >= argc) { /* this is like unrrduUsageUnu() */ unsigned int ii, maxlen = 0; char *buff, *fmt, tdash[] = "--- %s ---"; for (ii=0; cmdList[ii]; ii++) { if (cmdList[ii]->hidden) { continue; } maxlen = AIR_MAX(maxlen, AIR_UINT(strlen(cmdList[ii]->name))); } if (!maxlen) { fprintf(fusage, "%s: problem: maxlen = %u\n", me, maxlen); airMopError(mop); return 1; } buff = AIR_CALLOC(strlen(tdash) + strlen(title) + 1, char); airMopAdd(mop, buff, airFree, airMopAlways); sprintf(buff, tdash, title); fmt = AIR_CALLOC(hparm->columns + strlen(buff) + 1, char); /* generous */ airMopAdd(mop, buff, airFree, airMopAlways); sprintf(fmt, "%%%us\n", AIR_UINT((hparm->columns-strlen(buff))/2 + strlen(buff) - 1)); fprintf(fusage, fmt, buff); for (ii=0; cmdList[ii]; ii++) { unsigned int cc, len; if (cmdList[ii]->hidden) { continue; } len = AIR_UINT(strlen(cmdList[ii]->name)); strcpy(buff, ""); for (cc=len; cc<maxlen; cc++) strcat(buff, " "); strcat(buff, cmd); strcat(buff, " "); strcat(buff, cmdList[ii]->name); strcat(buff, " ... "); len = strlen(buff); fprintf(fusage, "%s", buff); _hestPrintStr(fusage, len, len, hparm->columns, cmdList[ii]->info, AIR_FALSE); } 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, *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 baneGkms_txfMain(int argc, char **argv, char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *perr, err[BIFF_STRLEN]; Nrrd *nout; airArray *mop; int pret, E, res[2], vi, gi, step; float min[2], max[2], top[2], v0, g0, *data, v, g, gwidth, width, mwidth, tvl, tvr, vl, vr, tmp, maxa; hestOptAdd(&opt, "r", "Vres Gres", airTypeInt, 2, 2, res, "256 256", "resolution of the transfer function in value and gradient " "magnitude"); hestOptAdd(&opt, "min", "Vmin Gmin", airTypeFloat, 2, 2, min, "0.0 0.0", "minimum value and grad mag in txf"); hestOptAdd(&opt, "max", "Vmax Gmax", airTypeFloat, 2, 2, max, NULL, "maximum value and grad mag in txf"); hestOptAdd(&opt, "v", "base value", airTypeFloat, 1, 1, &v0, NULL, "data value at which to position bottom of triangle"); hestOptAdd(&opt, "g", "gthresh", airTypeFloat, 1, 1, &g0, "0.0", "lowest grad mag to receive opacity"); hestOptAdd(&opt, "gw", "gwidth", airTypeFloat, 1, 1, &gwidth, "0.0", "range of grad mag values over which to apply threshold " "at low gradient magnitudes"); hestOptAdd(&opt, "top", "Vtop Gtop", airTypeFloat, 2, 2, top, NULL, "data value and grad mag at center of top of triangle"); hestOptAdd(&opt, "w", "value width", airTypeFloat, 1, 1, &width, NULL, "range of values to be spanned at top of triangle"); hestOptAdd(&opt, "mw", "value width", airTypeFloat, 1, 1, &mwidth, "0", "range of values to be spanned at BOTTOM of triangle"); hestOptAdd(&opt, "step", NULL, airTypeInt, 0, 0, &step, NULL, "instead of assigning opacity inside a triangular region, " "make it more like a step function, in which opacity never " "decreases in increasing data value"); hestOptAdd(&opt, "a", "max opac", airTypeFloat, 1, 1, &maxa, "1.0", "highest opacity to assign"); hestOptAdd(&opt, "o", "opacOut", airTypeString, 1, 1, &out, NULL, "output opacity function filename"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_baneGkms_txfInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); E = 0; if (!E) E |= nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 3, AIR_CAST(size_t, 1), AIR_CAST(size_t, res[0]), AIR_CAST(size_t, res[1])); if (!E) E |= !(nout->axis[0].label = airStrdup("A")); if (!E) E |= !(nout->axis[1].label = airStrdup("gage(scalar:v)")); if (!E) nrrdAxisInfoSet_va(nout, nrrdAxisInfoMin, AIR_NAN, (double)min[0], (double)min[1]); if (!E) nrrdAxisInfoSet_va(nout, nrrdAxisInfoMax, AIR_NAN, (double)max[0], (double)max[1]); if (!E) E |= !(nout->axis[2].label = airStrdup("gage(scalar:gm)")); if (E) { sprintf(err, "%s: trouble creating opacity function nrrd", me); biffMove(BANE, err, NRRD); airMopError(mop); return 1; } data = (float *)nout->data; tvl = top[0] - width/2; tvr = top[0] + width/2; mwidth /= 2; for (gi=0; gi<res[1]; gi++) { g = AIR_CAST(float, NRRD_CELL_POS(min[1], max[1], res[1], gi)); for (vi=0; vi<res[0]; vi++) { v = AIR_CAST(float, NRRD_CELL_POS(min[0], max[0], res[0], vi)); vl = AIR_CAST(float, AIR_AFFINE(0, g, top[1], v0-mwidth, tvl)); vr = AIR_CAST(float, AIR_AFFINE(0, g, top[1], v0+mwidth, tvr)); if (g > top[1]) { data[vi + res[0]*gi] = 0; continue; } tmp = AIR_CAST(float, (v - vl)/(0.00001 + vr - vl)); tmp = 1 - AIR_ABS(2*tmp - 1); if (step && v > (vr + vl)/2) { tmp = 1; } tmp = AIR_MAX(0, tmp); data[vi + res[0]*gi] = tmp*maxa; tmp = AIR_CAST(float, AIR_AFFINE(g0 - gwidth/2, g, g0 + gwidth/2, 0.0, 1.0)); tmp = AIR_CLAMP(0, tmp, 1); data[vi + res[0]*gi] *= tmp; } } if (nrrdSave(out, nout, NULL)) { sprintf(err, "%s: trouble saving opacity function", me); biffMove(BANE, err, NRRD); airMopError(mop); return 1; } airMopOkay(mop); return 0; }
int gageDeconvolveSeparable(Nrrd *nout, const Nrrd *nin, const gageKind *kind, const NrrdKernelSpec *ksp, int typeOut) { static const char me[]="gageDeconvolveSeparable"; double *line, (*lup)(const void *, size_t), (*ins)(void *, size_t, double); airArray *mop; size_t lineLen, sx, sy, sz, idx, ii, jj; unsigned int vi, valLen; if (!(nout && nin && kind && ksp)) { biffAddf(GAGE, "%s: got NULL pointer", me); return 1; } if (!(nrrdTypeDefault == typeOut || !airEnumValCheck(nrrdType, typeOut))) { biffAddf(GAGE, "%s: typeOut %d not valid", me, typeOut); return 1; } if (!gageDeconvolveSeparableKnown(ksp)) { biffAddf(GAGE, "%s: separable deconv not known for %s kernel", me, ksp->kernel->name); return 1; } if (gageKindVolumeCheck(kind, nin)) { biffAddf(GAGE, "%s: given volume doesn't fit %s kind", me, kind->name); return 1; } if (nrrdTypeDefault == typeOut ? nrrdCopy(nout, nin) : nrrdConvert(nout, nin, typeOut)) { biffMovef(GAGE, NRRD, "%s: problem allocating output", me); return 1; } if (deconvTrivial(ksp)) { /* if there's no real work for the deconvolution, then by copying the values we're already done; bye */ return 0; } valLen = kind->valLen; sx = nin->axis[kind->baseDim + 0].size; sy = nin->axis[kind->baseDim + 1].size; sz = nin->axis[kind->baseDim + 2].size; lineLen = sx; lineLen = AIR_MAX(lineLen, sy); lineLen = AIR_MAX(lineLen, sz); lup = nrrdDLookup[nin->type]; ins = nrrdDInsert[nout->type]; mop = airMopNew(); line = AIR_CALLOC(lineLen*valLen, double); airMopAdd(mop, line, airFree, airMopAlways); /* process along X scanlines */ for (jj=0; jj<sy*sz; jj++) { /* xi = 0, yi = jj%sy, zi = jj/sy ==> xi + sx*(yi + sy*zi) == 0 + sx*(jj%sy + sy*(jj/sy)) == 0 + sx*jj */ idx = 0 + valLen*(0 + sx*jj); for (ii=0; ii<sx; ii++) { for (vi=0; vi<valLen; vi++) { line[ii + sx*vi] = lup(nin->data, idx + vi + valLen*ii); } } for (vi=0; vi<valLen; vi++) { deconvLine(line + sx*vi, sx, ksp); } for (ii=0; ii<sx; ii++) { for (vi=0; vi<valLen; vi++) { ins(nout->data, idx + vi + valLen*ii, line[ii + sx*vi]); } } } /* process along Y scanlines */ for (jj=0; jj<sx*sz; jj++) { /* xi = jj%sx, yi = 0, zi = jj/sx ==> xi + sx*(yi + sy*zi) == jj%sx + sx*(0 + sy*jj/sx) */ idx = 0 + valLen*((jj%sx) + sx*(0 + sy*(jj/sx))); for (ii=0; ii<sy; ii++) { for (vi=0; vi<valLen; vi++) { line[ii + sy*vi] = lup(nin->data, idx + vi + valLen*sx*ii); } } for (vi=0; vi<valLen; vi++) { deconvLine(line + sy*vi, sy, ksp); } for (ii=0; ii<sx; ii++) { for (vi=0; vi<valLen; vi++) { ins(nout->data, idx + vi + valLen*sx*ii, line[ii + sy*vi]); } } } /* process along Z scanlines */ for (jj=0; jj<sx*sy; jj++) { /* xi = jj%sx, yi = jj/sx, zi = 0 ==> xi + sx*(yi + sy*zi) == jj%sx + sx*(jj/sx + sy*0) == jj%sx + sx*(jj/sx) == jj */ idx = 0 + valLen*jj; for (ii=0; ii<sz; ii++) { for (vi=0; vi<valLen; vi++) { line[ii + sz*vi] = lup(nin->data, idx + vi + valLen*sx*sy*ii); } } for (vi=0; vi<valLen; vi++) { deconvLine(line + sz*vi, sz, ksp); } for (ii=0; ii<sx; ii++) { for (vi=0; vi<valLen; vi++) { ins(nout->data, idx + vi + valLen*sx*sy*ii, line[ii + sz*vi]); } } } airMopOkay(mop); return 0; }
int main(int argc, char *argv[]) { char *me, *outS; hestOpt *hopt; hestParm *hparm; airArray *mop; char *err; Nrrd *nin, *nhist; double vmin, vmax, rmax, val, cent[2], rad; int bins[2], sx, sy, xi, yi, ridx, hidx, rbins, hbins; NrrdRange *range; double (*lup)(const void *v, size_t I), *hist; 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 image", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "b", "rbins hbins", airTypeInt, 2, 2, bins, NULL, "# of histogram bins: radial and value"); hestOptAdd(&hopt, "min", "value", airTypeDouble, 1, 1, &vmin, "nan", "Value at low end of histogram. Defaults to lowest value " "found in input nrrd."); hestOptAdd(&hopt, "max", "value", airTypeDouble, 1, 1, &vmax, "nan", "Value at high end of histogram. Defaults to highest value " "found in input nrrd."); hestOptAdd(&hopt, "rmax", "max radius", airTypeDouble, 1, 1, &rmax, "nan", "largest radius to include in histogram"); hestOptAdd(&hopt, "c", "center x, y", airTypeDouble, 2, 2, cent, NULL, "The center point around which to build radial histogram"); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, "-", "file to write histogram to"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, histradInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); if (2 != nin->dim) { fprintf(stderr, "%s: need 2-D input (not %d-D)\n", me, nin->dim); airMopError(mop); return 1; } rbins = bins[0]; hbins = bins[1]; nhist = nrrdNew(); airMopAdd(mop, nhist, (airMopper)nrrdNuke, airMopAlways); if (nrrdMaybeAlloc_va(nhist, nrrdTypeDouble, 2, AIR_CAST(size_t, rbins), AIR_CAST(size_t, hbins))) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't allocate histogram:\n%s", me, err); airMopError(mop); return 1; } if (!( AIR_EXISTS(vmin) && AIR_EXISTS(vmax) )) { range = nrrdRangeNewSet(nin, nrrdStateBlind8BitRange); airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); vmin = AIR_EXISTS(vmin) ? vmin : range->min; vmax = AIR_EXISTS(vmax) ? vmax : range->max; } #define DIST(x0, y0, x1, y1) (sqrt((x0-x1)*(x0-x1) + (y0-y1)*(y0-y1))) sx = nin->axis[0].size; sy = nin->axis[1].size; if (!AIR_EXISTS(rmax)) { rmax = 0; rmax = AIR_MAX(rmax, DIST(cent[0], cent[1], 0, 0)); rmax = AIR_MAX(rmax, DIST(cent[0], cent[1], sx-1, 0)); rmax = AIR_MAX(rmax, DIST(cent[0], cent[1], 0, sy-1)); rmax = AIR_MAX(rmax, DIST(cent[0], cent[1], sx-1, sy-1)); } lup = nrrdDLookup[nin->type]; hist = (double*)(nhist->data); for (xi=0; xi<sx; xi++) { for (yi=0; yi<sy; yi++) { rad = DIST(cent[0], cent[1], xi, yi); if (!AIR_IN_OP(0, rad, rmax)) { continue; } val = lup(nin->data, xi + sx*yi); if (!AIR_IN_OP(vmin, val, vmax)) { continue; } ridx = airIndex(0, rad, rmax, rbins); hidx = airIndex(vmin, val, vmax, hbins); hist[ridx + rbins*hidx] += 1; } } nhist->axis[0].min = 0; nhist->axis[0].max = rmax; nhist->axis[1].min = vmin; nhist->axis[1].max = vmax; if (nrrdSave(outS, nhist, 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); }
void _gageSclAnswer (gageContext *ctx, gagePerVolume *pvl) { char me[]="_gageSclAnswer"; double gmag=0, *hess, *norm, *gvec, *gten, *k1, *k2, curv=0, sHess[9]={0,0,0,0,0,0,0,0,0}; double tmpMat[9], tmpVec[3], hevec[9], heval[3]; double len, gp1[3], gp2[3], *nPerp, ncTen[9], nProj[9]={0,0,0,0,0,0,0,0,0}; double alpha = 0.5; double beta = 0.5; double gamma = 5; double cc = 1e-6; #define FD_MEDIAN_MAX 16 int fd, nidx, xi, yi, zi; double *fw, iv3wght[2*FD_MEDIAN_MAX*FD_MEDIAN_MAX*FD_MEDIAN_MAX], wghtSum, wght; /* convenience pointers for work below */ hess = pvl->directAnswer[gageSclHessian]; gvec = pvl->directAnswer[gageSclGradVec]; norm = pvl->directAnswer[gageSclNormal]; nPerp = pvl->directAnswer[gageSclNPerp]; gten = pvl->directAnswer[gageSclGeomTens]; k1 = pvl->directAnswer[gageSclK1]; k2 = pvl->directAnswer[gageSclK2]; if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclValue)) { /* done if doV */ if (ctx->verbose) { fprintf(stderr, "%s: val = % 15.7f\n", me, (double)(pvl->directAnswer[gageSclValue][0])); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclGradVec)) { /* done if doD1 */ if (ctx->verbose) { fprintf(stderr, "%s: gvec = ", me); ell_3v_print_d(stderr, gvec); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclGradMag)) { /* this is the true value of gradient magnitude */ gmag = pvl->directAnswer[gageSclGradMag][0] = sqrt(ELL_3V_DOT(gvec, gvec)); } /* NB: it would seem that gageParmGradMagMin is completely ignored ... */ if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclNormal)) { if (gmag) { ELL_3V_SCALE(norm, 1/gmag, gvec); /* polishing ... len = sqrt(ELL_3V_DOT(norm, norm)); ELL_3V_SCALE(norm, 1/len, norm); */ } else { ELL_3V_COPY(norm, gageZeroNormal); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclNPerp)) { /* nPerp = I - outer(norm, norm) */ /* NB: this sets both nPerp and nProj */ ELL_3MV_OUTER(nProj, norm, norm); ELL_3M_SCALE(nPerp, -1, nProj); nPerp[0] += 1; nPerp[4] += 1; nPerp[8] += 1; } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessian)) { /* done if doD2 */ if (ctx->verbose) { fprintf(stderr, "%s: hess = \n", me); ell_3m_print_d(stderr, hess); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclLaplacian)) { pvl->directAnswer[gageSclLaplacian][0] = hess[0] + hess[4] + hess[8]; if (ctx->verbose) { fprintf(stderr, "%s: lapl = %g + %g + %g = %g\n", me, hess[0], hess[4], hess[8], pvl->directAnswer[gageSclLaplacian][0]); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessFrob)) { pvl->directAnswer[gageSclHessFrob][0] = ELL_3M_FROB(hess); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessEval)) { /* HEY: look at the return value for root multiplicity? */ ell_3m_eigensolve_d(heval, hevec, hess, AIR_TRUE); ELL_3V_COPY(pvl->directAnswer[gageSclHessEval], heval); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessEvec)) { ELL_3M_COPY(pvl->directAnswer[gageSclHessEvec], hevec); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessRidgeness)) { double A, B, S; if (heval[1] >0 || heval[2]>0) { pvl->directAnswer[gageSclHessRidgeness][0] = 0; } else if (AIR_ABS(heval[1])<1e-10 || AIR_ABS(heval[2])<1e-10) { pvl->directAnswer[gageSclHessRidgeness][0] = 0; } else { double *ans; A = AIR_ABS(heval[1])/AIR_ABS(heval[2]); B = AIR_ABS(heval[0])/sqrt(AIR_ABS(heval[1]*heval[2])); S = sqrt(heval[0]*heval[0] + heval[1]*heval[1] + heval[2]*heval[2]); ans = pvl->directAnswer[gageSclHessRidgeness]; ans[0] = (1-exp(-A*A/(2*alpha*alpha))) * exp(-B*B/(2*beta*beta)) * (1-exp(-S*S/(2*gamma*gamma))) * exp(-2*cc*cc/(AIR_ABS(heval[1])*heval[2]*heval[2])); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessValleyness)) { double A, B, S; if (heval[0] <0 || heval[1]<0) { pvl->directAnswer[gageSclHessValleyness][0] = 0; } else if (AIR_ABS(heval[0])<1e-10 || AIR_ABS(heval[1])<1e-10) { pvl->directAnswer[gageSclHessValleyness][0] = 0; } else { double *ans; A = AIR_ABS(heval[1])/AIR_ABS(heval[0]); B = AIR_ABS(heval[2])/sqrt(AIR_ABS(heval[1]*heval[0])); S = sqrt(heval[0]*heval[0] + heval[1]*heval[1] + heval[2]*heval[2]); ans = pvl->directAnswer[gageSclHessValleyness]; ans[0] = (1-exp(-A*A/(2*alpha*alpha))) * exp(-B*B/(2*beta*beta)) * (1-exp(-S*S/(2*gamma*gamma))) * exp(-2*cc*cc/(AIR_ABS(heval[1])*heval[0]*heval[0])); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessMode)) { pvl->directAnswer[gageSclHessMode][0] = airMode3_d(heval); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageScl2ndDD)) { ELL_3MV_MUL(tmpVec, hess, norm); pvl->directAnswer[gageScl2ndDD][0] = ELL_3V_DOT(norm, tmpVec); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclGeomTens)) { if (gmag > ctx->parm.gradMagCurvMin) { /* parm.curvNormalSide applied here to determine the sense of the normal when doing all curvature calculations */ ELL_3M_SCALE(sHess, -(ctx->parm.curvNormalSide)/gmag, hess); /* gten = nPerp * sHess * nPerp */ ELL_3M_MUL(tmpMat, sHess, nPerp); ELL_3M_MUL(gten, nPerp, tmpMat); if (ctx->verbose) { fprintf(stderr, "%s: gten: \n", me); ell_3m_print_d(stderr, gten); ELL_3MV_MUL(tmpVec, gten, norm); len = ELL_3V_LEN(tmpVec); fprintf(stderr, "%s: should be small: %30.15f\n", me, (double)len); ell_3v_perp_d(gp1, norm); ELL_3MV_MUL(tmpVec, gten, gp1); len = ELL_3V_LEN(tmpVec); fprintf(stderr, "%s: should be bigger: %30.15f\n", me, (double)len); ELL_3V_CROSS(gp2, gp1, norm); ELL_3MV_MUL(tmpVec, gten, gp2); len = ELL_3V_LEN(tmpVec); fprintf(stderr, "%s: should (also) be bigger: %30.15f\n", me, (double)len); } } else { ELL_3M_ZERO_SET(gten); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclTotalCurv)) { curv = pvl->directAnswer[gageSclTotalCurv][0] = ELL_3M_FROB(gten); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclShapeTrace)) { pvl->directAnswer[gageSclShapeTrace][0] = (curv ? ELL_3M_TRACE(gten)/curv : 0); } if ( (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclK1)) || (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclK2)) ){ double T, N, D; T = ELL_3M_TRACE(gten); N = curv; D = 2*N*N - T*T; /* if (D < -0.0000001) { fprintf(stderr, "%s: %g %g\n", me, T, N); fprintf(stderr, "%s: !!! D curv determinant % 22.10f < 0.0\n", me, D); fprintf(stderr, "%s: gten: \n", me); ell_3m_print_d(stderr, gten); } */ D = AIR_MAX(D, 0); D = sqrt(D); k1[0] = 0.5*(T + D); k2[0] = 0.5*(T - D); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclMeanCurv)) { pvl->directAnswer[gageSclMeanCurv][0] = (*k1 + *k2)/2; } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclGaussCurv)) { pvl->directAnswer[gageSclGaussCurv][0] = (*k1)*(*k2); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclShapeIndex)) { pvl->directAnswer[gageSclShapeIndex][0] = -(2/AIR_PI)*atan2(*k1 + *k2, *k1 - *k2); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclCurvDir1)) { /* HEY: this only works when K1, K2, 0 are all well mutually distinct, since these are the eigenvalues of the geometry tensor, and this code assumes that the eigenspaces are all one-dimensional */ ELL_3M_COPY(tmpMat, gten); ELL_3M_DIAG_SET(tmpMat, gten[0] - *k1, gten[4]- *k1, gten[8] - *k1); ell_3m_1d_nullspace_d(tmpVec, tmpMat); ELL_3V_COPY(pvl->directAnswer[gageSclCurvDir1], tmpVec); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclCurvDir2)) { /* HEY: this only works when K1, K2, 0 are all well mutually distinct, since these are the eigenvalues of the geometry tensor, and this code assumes that the eigenspaces are all one-dimensional */ ELL_3M_COPY(tmpMat, gten); ELL_3M_DIAG_SET(tmpMat, gten[0] - *k2, gten[4] - *k2, gten[8] - *k2); ell_3m_1d_nullspace_d(tmpVec, tmpMat); ELL_3V_COPY(pvl->directAnswer[gageSclCurvDir2], tmpVec); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclFlowlineCurv)) { if (gmag >= ctx->parm.gradMagCurvMin) { /* because of the gageSclGeomTens prerequisite, sHess, nPerp, and nProj are all already set */ /* ncTen = nPerp * sHess * nProj */ ELL_3M_MUL(tmpMat, sHess, nProj); ELL_3M_MUL(ncTen, nPerp, tmpMat); } else { ELL_3M_ZERO_SET(ncTen); } /* there used to be a wrong extra sqrt() here */ pvl->directAnswer[gageSclFlowlineCurv][0] = ELL_3M_FROB(ncTen); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclMedian)) { /* this item is currently a complete oddball in that it does not benefit from anything done in the "filter" stage, which is in fact a waste of time if the query consists only of this item */ fd = 2*ctx->radius; if (fd > FD_MEDIAN_MAX) { fprintf(stderr, "%s: PANIC: current filter diameter = %d " "> FD_MEDIAN_MAX = %d\n", me, fd, FD_MEDIAN_MAX); exit(1); } fw = ctx->fw + fd*3*gageKernel00; /* HEY: this needs some optimization help */ wghtSum = 0; nidx = 0; for (xi=0; xi<fd; xi++) { for (yi=0; yi<fd; yi++) { for (zi=0; zi<fd; zi++) { iv3wght[0 + 2*nidx] = pvl->iv3[nidx]; iv3wght[1 + 2*nidx] = fw[xi + 0*fd]*fw[yi + 1*fd]*fw[zi + 2*fd]; wghtSum += iv3wght[1 + 2*nidx]; nidx++; } } } qsort(iv3wght, fd*fd*fd, 2*sizeof(double), nrrdValCompare[nrrdTypeDouble]); wght = 0; for (nidx=0; nidx<fd*fd*fd; nidx++) { wght += iv3wght[1 + 2*nidx]; if (wght > wghtSum/2) { break; } } pvl->directAnswer[gageSclMedian][0] = iv3wght[0 + 2*nidx]; } return; }
void hestGlossary(FILE *f, hestOpt *opt, hestParm *_parm) { int i, j, len, maxlen, numOpts; char buff[2*AIR_STRLEN_HUGE], tmpS[AIR_STRLEN_HUGE]; hestParm *parm; parm = !_parm ? hestParmNew() : _parm; if (_hestPanic(opt, NULL, parm)) { /* we can't continue; the opt array is botched */ parm = !_parm ? hestParmFree(parm) : NULL; return; } numOpts = _hestNumOpts(opt); maxlen = 0; if (numOpts) { fprintf(f, "\n"); } for (i=0; i<numOpts; i++) { strcpy(buff, ""); _hestSetBuff(buff, opt + i, parm, AIR_TRUE, AIR_FALSE); maxlen = AIR_MAX((int)strlen(buff), maxlen); } if (parm && parm->respFileEnable) { sprintf(buff, "%cfile ...", parm->respFileFlag); len = strlen(buff); for (j=len; j<maxlen; j++) { fprintf(f, " "); } fprintf(f, "%s = ", buff); strcpy(buff, "response file(s) containing command-line arguments"); _hestPrintStr(f, maxlen + 3, maxlen + 3, parm->columns, buff, AIR_FALSE); } for (i=0; i<numOpts; i++) { strcpy(buff, ""); _hestSetBuff(buff, opt + i, parm, AIR_TRUE, AIR_FALSE); airOneLinify(buff); len = strlen(buff); for (j=len; j<maxlen; j++) { fprintf(f, " "); } fprintf(f, "%s", buff); strcpy(buff, ""); #if 1 if (opt[i].flag && strchr(opt[i].flag, parm->multiFlagSep)) { /* there is a long-form flag as well as short */ _hestSetBuff(buff, opt + i, parm, AIR_FALSE, AIR_TRUE); strcat(buff, " = "); fprintf(f, " , "); } else { /* there is only a short-form flag */ fprintf(f, " = "); } #else fprintf(f, " = "); #endif if (opt[i].info) { strcat(buff, opt[i].info); } if ((opt[i].min || _hestMax(opt[i].max)) && (!( 2 == opt[i].kind && airTypeEnum == opt[i].type && parm->elideSingleEnumType )) && (!( 2 == opt[i].kind && airTypeOther == opt[i].type && parm->elideSingleOtherType )) ) { /* if there are newlines in the info, then we want to clarify the type by printing it on its own line */ if (opt[i].info && strchr(opt[i].info, '\n')) { strcat(buff, "\n "); } else { strcat(buff, " "); } strcat(buff, "("); if (opt[i].min == 0 && _hestMax(opt[i].max) == 1) { strcat(buff, "optional\t"); } else { if ((int)opt[i].min == _hestMax(opt[i].max) && _hestMax(opt[i].max) > 1) { /* HEY scrutinize casts */ sprintf(tmpS, "%d\t", _hestMax(opt[i].max)); strcat(buff, tmpS); } else if ((int)opt[i].min < _hestMax(opt[i].max)) { /* HEY scrutinize casts */ if (-1 == opt[i].max) { sprintf(tmpS, "%d\tor\tmore\t", opt[i].min); } else { sprintf(tmpS, "%d..%d\t", opt[i].min, _hestMax(opt[i].max)); } strcat(buff, tmpS); } } sprintf(tmpS, "%s%s", (airTypeEnum == opt[i].type ? opt[i].enm->name : (airTypeOther == opt[i].type ? opt[i].CB->type : airTypeStr[opt[i].type])), (_hestMax(opt[i].max) > 1 ? (airTypeOther == opt[i].type && 'y' == opt[i].CB->type[airStrlen(opt[i].CB->type)-1] && parm->cleverPluralizeOtherY ? "\bies" : "s") : "")); strcat(buff, tmpS); strcat(buff, ")"); } /* fprintf(stderr, "!%s: parm->elideSingleOtherDefault = %d\n", "hestGlossary", parm->elideSingleOtherDefault); */ if (opt[i].dflt && (opt[i].min || _hestMax(opt[i].max)) && (!( 2 == opt[i].kind && (airTypeFloat == opt[i].type || airTypeDouble == opt[i].type) && !AIR_EXISTS(airAtod(opt[i].dflt)) && parm->elideSingleNonExistFloatDefault )) && (!( (3 == opt[i].kind || 5 == opt[i].kind) && (airTypeFloat == opt[i].type || airTypeDouble == opt[i].type) && !AIR_EXISTS(airAtod(opt[i].dflt)) && parm->elideMultipleNonExistFloatDefault )) && (!( 2 == opt[i].kind && airTypeOther == opt[i].type && parm->elideSingleOtherDefault )) && (!( 2 == opt[i].kind && airTypeString == opt[i].type && parm->elideSingleEmptyStringDefault && 0 == airStrlen(opt[i].dflt) )) && (!( (3 == opt[i].kind || 5 == opt[i].kind) && airTypeString == opt[i].type && parm->elideMultipleEmptyStringDefault && 0 == airStrlen(opt[i].dflt) )) ) { /* if there are newlines in the info, then we want to clarify the default by printing it on its own line */ if (opt[i].info && strchr(opt[i].info, '\n')) { strcat(buff, "\n "); } else { strcat(buff, "; "); } strcat(buff, "default:\t"); strcpy(tmpS, opt[i].dflt); airStrtrans(tmpS, ' ', '\t'); strcat(buff, "\""); strcat(buff, tmpS); strcat(buff, "\""); } _hestPrintStr(f, maxlen + 3, maxlen + 3, parm->columns, buff, AIR_FALSE); } parm = !_parm ? hestParmFree(parm) : NULL; return; }
/* ** _gageShapeSet ** ** we are serving two masters here. If ctx is non-NULL, we are being called ** from within gage, and we are to be lax or strict according to the settings ** of ctx->parm.requireAllSpacings and ctx->parm.requireEqualCenters. If ** ctx is NULL, gageShapeSet was called, in which case we go with lax ** behavior (nothing "required") ** ** This function has subsumed the contents of the old gageVolumeCheck, ** and hence has become this weird beast- part error checker and part ** (gageShape) initializer. Oh well... */ int _gageShapeSet(const gageContext *ctx, gageShape *shape, const Nrrd *nin, unsigned int baseDim) { static const char me[]="_gageShapeSet"; int ai, cx, cy, cz, statCalc[3], status, ofspc; unsigned int minsize; const NrrdAxisInfo *ax[3]; double vecA[4], vecB[3], vecC[3], vecD[4], matA[9], spcCalc[3], vecCalc[3][NRRD_SPACE_DIM_MAX], orig[NRRD_SPACE_DIM_MAX]; airArray *mop; /* fprintf(stderr, "!%s: ctx = %p (%s, %s)\n", me, ctx, (ctx ? (ctx->shape->fromOrientation ? "YES from orient" : "not from orient") : "???"), (ctx ? (ctx->parm.orientationFromSpacing ? "YES ofs" : "not ofs") : "???")); */ /* ------ basic error checking */ mop = airMopNew(); airMopAdd(mop, shape, _mopShapeReset, airMopOnError); if (!( shape && nin )) { biffAddf(GAGE, "%s: got NULL pointer", me); airMopError(mop); return 1; } if (nrrdCheck(nin)) { biffMovef(GAGE, NRRD, "%s: basic nrrd validity check failed", me); airMopError(mop); return 1; } if (nrrdTypeBlock == nin->type) { biffAddf(GAGE, "%s: need a non-block type nrrd", me); airMopError(mop); return 1; } if (!(nin->dim == 3 + baseDim)) { biffAddf(GAGE, "%s: nrrd should be %u-D, not %u-D", me, 3 + baseDim, nin->dim); airMopError(mop); return 1; } ax[0] = &(nin->axis[baseDim+0]); ax[1] = &(nin->axis[baseDim+1]); ax[2] = &(nin->axis[baseDim+2]); statCalc[0] = nrrdSpacingCalculate(nin, baseDim + 0, spcCalc + 0, vecCalc[0]); statCalc[1] = nrrdSpacingCalculate(nin, baseDim + 1, spcCalc + 1, vecCalc[1]); statCalc[2] = nrrdSpacingCalculate(nin, baseDim + 2, spcCalc + 2, vecCalc[2]); /* see if nrrdSpacingCalculate ever *failed* */ if (nrrdSpacingStatusUnknown == statCalc[0] || nrrdSpacingStatusUnknown == statCalc[1] || nrrdSpacingStatusUnknown == statCalc[2]) { biffAddf(GAGE, "%s: nrrdSpacingCalculate trouble on axis %d, %d, or %d", me, baseDim + 0, baseDim + 1, baseDim + 2); airMopError(mop); return 1; } if (!( statCalc[0] == statCalc[1] && statCalc[1] == statCalc[2] )) { biffAddf(GAGE, "%s: inconsistent spacing information on axes " "%u (%s), %u (%s), and %u (%s)", me, baseDim + 0, airEnumDesc(nrrdSpacingStatus, statCalc[0]), baseDim + 1, airEnumDesc(nrrdSpacingStatus, statCalc[1]), baseDim + 2, airEnumDesc(nrrdSpacingStatus, statCalc[2])); airMopError(mop); return 1; } /* this simplifies reasoning in the code that follows */ status = statCalc[0]; /* zero spacing would be problematic */ if (0 == spcCalc[0] && 0 == spcCalc[1] && 0 == spcCalc[2]) { biffAddf(GAGE, "%s: spacings (%g,%g,%g) for axes %d,%d,%d not all " "non-zero", me, spcCalc[1], spcCalc[1], spcCalc[2], baseDim+0, baseDim+1, baseDim+2); airMopError(mop); return 1; } /* error checking based on status */ if (nrrdSpacingStatusScalarWithSpace == status) { biffAddf(GAGE, "%s: sorry, can't handle per-axis spacing that isn't part " "of a surrounding world space (%s)", me, airEnumStr(nrrdSpacingStatus, status)); airMopError(mop); return 1; } /* we no longer allow a nrrd to come in with no spacing info at all */ if (nrrdSpacingStatusNone == status) { biffAddf(GAGE, "%s: sorry, need some spacing info for spatial axes " "%u, %u, %u", me, baseDim+0, baseDim+1, baseDim+2); airMopError(mop); return 1; } /* actually, there shouldn't be any other options for spacing status besides these too; this is just being careful */ if (!( nrrdSpacingStatusDirection == status || nrrdSpacingStatusScalarNoSpace == status )) { biffAddf(GAGE, "%s: sorry, can only handle spacing status %d (%s) " "or %d (%s), not %d (%s)", me, nrrdSpacingStatusDirection, airEnumStr(nrrdSpacingStatus, nrrdSpacingStatusDirection), nrrdSpacingStatusScalarNoSpace, airEnumStr(nrrdSpacingStatus, nrrdSpacingStatusScalarNoSpace), status, airEnumStr(nrrdSpacingStatus, status)); airMopError(mop); return 1; } if (nrrdSpacingStatusDirection == status) { shape->fromOrientation = AIR_TRUE; if (3 != nin->spaceDim) { biffAddf(GAGE, "%s: orientation space dimension %d != 3", me, nin->spaceDim); airMopError(mop); return 1; } } else { shape->fromOrientation = AIR_FALSE; } /* ------ find centering (set shape->center) */ /* NOTE: when the volume is being crammed in a bi-unit cube, the centering will actually affect the positions of the samples. Otherwise, (having full orientation, or using orientationFromSpacing), the centering will only affect the probe-able bounds of the volume, but the sample positions in space don't depend on centering */ cx = ax[0]->center; cy = ax[1]->center; cz = ax[2]->center; if (!( cx == cy && cy == cz )) { biffAddf(GAGE, "%s: axes %d,%d,%d centerings (%s,%s,%s) not all equal", me, baseDim+0, baseDim+1, baseDim+2, airEnumStr(nrrdCenter, cx), airEnumStr(nrrdCenter, cy), airEnumStr(nrrdCenter, cz)); airMopError(mop); return 1; } /* Hopefully, ctx->parm.defaultCenter == shape->defaultCenter; and this worry will be moot if ctx->parm.defaultCenter goes away */ shape->center = (nrrdCenterUnknown != cx ? cx /* cx == cy == cz, by above */ : (ctx ? ctx->parm.defaultCenter : shape->defaultCenter)); /* ------ find sizes (set shape->size[0,1,2]) */ shape->size[0] = ax[0]->size; shape->size[1] = ax[1]->size; shape->size[2] = ax[2]->size; minsize = (nrrdCenterCell == shape->center ? 1 : 2); /* this can't be relaxed in the face of having full orientation info, because even then, you can't have a non-zero probe-able volume if there's only one sample along a node-centered axis */ if (!(shape->size[0] >= minsize && shape->size[1] >= minsize && shape->size[2] >= minsize )) { biffAddf(GAGE, "%s: sizes (%u,%u,%u) must all be >= %u " "(min number of %s-centered samples)", me, shape->size[0], shape->size[1], shape->size[2], minsize, airEnumStr(nrrdCenter, shape->center)); airMopError(mop); return 1; } /* ------ find spacings[0,1,2] and ItoW matrix */ /* Hopefully, ctx->parm.orientationFromSpacing and shape->orientationFromSpacing don't represent competing interests; this worry will be moot if ctx->parm.orientationFromSpacing goes away */ ofspc = ((ctx && ctx->parm.orientationFromSpacing) || shape->orientationFromSpacing); if (shape->fromOrientation || ofspc) { if (ofspc) { /* need abs() in case an axis had negative spacing */ ELL_3V_ABS(shape->spacing, spcCalc); ELL_3V_SET(vecCalc[0], airSgn(spcCalc[0]), 0.0, 0.0); ELL_3V_SET(vecCalc[1], 0.0, airSgn(spcCalc[1]), 0.0); ELL_3V_SET(vecCalc[2], 0.0, 0.0, airSgn(spcCalc[2])); } else { ELL_3V_COPY(shape->spacing, spcCalc); /* vecCalc set by nrrdSpacingCalculate */ } if (shape->fromOrientation) { /* if the spaceOrigin isn't set, this will be all NaNs */ nrrdSpaceOriginGet(nin, orig); } else { /* sorry, if you want to specify an image origin that over-rides the behavior of centering the volume at (0,0,0), then it has to be done through the full orientation info. That is, we don't want to use nrrdOriginCalculate() because otherwise the logic gets too complicated */ ELL_3V_SET(orig, AIR_NAN, AIR_NAN, AIR_NAN); } if (!ELL_3V_EXISTS(orig)) { /* don't have origin, for whatever reason; center volume on (0,0,0) */ ELL_3V_SET(orig, 0.0, 0.0, 0.0); ELL_3V_SCALE_INCR(orig, -(shape->size[0] - 1.0)*shape->spacing[0]/2.0, vecCalc[0]); ELL_3V_SCALE_INCR(orig, -(shape->size[1] - 1.0)*shape->spacing[1]/2.0, vecCalc[1]); ELL_3V_SCALE_INCR(orig, -(shape->size[2] - 1.0)*shape->spacing[2]/2.0, vecCalc[2]); } vecD[3] = 0; ELL_3V_SCALE(vecD, spcCalc[0], vecCalc[0]); ELL_4MV_COL0_SET(shape->ItoW, vecD); ELL_3V_SCALE(vecD, spcCalc[1], vecCalc[1]); ELL_4MV_COL1_SET(shape->ItoW, vecD); ELL_3V_SCALE(vecD, spcCalc[2], vecCalc[2]); ELL_4MV_COL2_SET(shape->ItoW, vecD); vecD[3] = 1; ELL_3V_COPY(vecD, orig); ELL_4MV_COL3_SET(shape->ItoW, vecD); /* fprintf(stderr, "%s: %g (%g,%g,%g)\n", me, spcCalc[0], vecCalc[0][0], vecCalc[0][1], vecCalc[0][2]); fprintf(stderr, "%s: %g (%g,%g,%g)\n", me, spcCalc[1], vecCalc[1][0], vecCalc[1][1], vecCalc[1][2]); fprintf(stderr, "%s: %g (%g,%g,%g)\n", me, spcCalc[2], vecCalc[2][0], vecCalc[2][1], vecCalc[2][2]); */ /* fprintf(stderr, "%s: ItoW = %g %g %g %g\n", me, shape->ItoW[ 0], shape->ItoW[ 1], shape->ItoW[ 2], shape->ItoW[ 3]); fprintf(stderr, "%s: %g %g %g %g\n", me, shape->ItoW[ 4], shape->ItoW[ 5], shape->ItoW[ 6], shape->ItoW[ 7]); fprintf(stderr, "%s: %g %g %g %g\n", me, shape->ItoW[ 8], shape->ItoW[ 9], shape->ItoW[10], shape->ItoW[11]); fprintf(stderr, "%s: %g %g %g %g\n", me, shape->ItoW[12], shape->ItoW[13], shape->ItoW[14], shape->ItoW[15]); */ } else { /* not (shape->fromOrientation || ofspc) */ double maxLen, volHalfLen[3]; size_t num[3]; /* ------ learn lengths for bounding nrrd in bi-unit cube */ ELL_3V_ABS(shape->spacing, spcCalc); maxLen = 0.0; for (ai=0; ai<=2; ai++) { num[ai] = (nrrdCenterNode == shape->center ? shape->size[ai]-1 : shape->size[ai]); volHalfLen[ai] = num[ai]*shape->spacing[ai]; maxLen = AIR_MAX(maxLen, volHalfLen[ai]); } /* Thu Dec 13 02:45:01 EST 2007 fixed long-standing bug in handling vols without full orientation info: spacing[ai] was never scaled to account for being crammed into the bi-unit cube!! */ for (ai=0; ai<=2; ai++) { volHalfLen[ai] /= maxLen; shape->spacing[ai] = 2*volHalfLen[ai]/num[ai]; } ELL_3V_SET(vecC, 0, 0, 0); shapeUnitItoW(shape, vecA, vecC, volHalfLen); ELL_3V_SET(vecC, 1, 0, 0); shapeUnitItoW(shape, vecB, vecC, volHalfLen); ELL_3V_SUB(vecD, vecB, vecA); vecD[3] = 0; ELL_4MV_COL0_SET(shape->ItoW, vecD); ELL_3V_SET(vecC, 0, 1, 0); shapeUnitItoW(shape, vecB, vecC, volHalfLen); ELL_3V_SUB(vecD, vecB, vecA); vecD[3] = 0; ELL_4MV_COL1_SET(shape->ItoW, vecD); ELL_3V_SET(vecC, 0, 0, 1); shapeUnitItoW(shape, vecB, vecC, volHalfLen); ELL_3V_SUB(vecD, vecB, vecA); vecD[3] = 0; ELL_4MV_COL2_SET(shape->ItoW, vecD); vecA[3] = 1; ELL_4MV_COL3_SET(shape->ItoW, vecA); } /* ------ set the rest of the matrices */ ell_4m_inv_d(shape->WtoI, shape->ItoW); ELL_34M_EXTRACT(matA, shape->ItoW); ell_3m_inv_d(shape->ItoWSubInv, matA); ELL_3M_TRANSPOSE(shape->ItoWSubInvTransp, shape->ItoWSubInv); airMopOkay(mop); return 0; }
/* ******** nrrdSanity() ** ** makes sure that all the basic assumptions of nrrd hold for ** the architecture/etc which we're currently running on. ** ** returns 1 if all is okay, 0 if there is a problem */ int nrrdSanity(void) { char me[]="nrrdSanity", err[BIFF_STRLEN]; int aret, type; size_t maxsize; airLLong tmpLLI; airULLong tmpULLI; static int _nrrdSanity = 0; if (_nrrdSanity) { /* we've been through this once before and things looked okay ... */ /* Is this thread-safe? I think so. If we assume that any two threads are going to compute the same value, isn't it the case that, at worse, both of them will go through all the tests and then set _nrrdSanity to the same thing? */ return 1; } aret = airSanity(); if (aret != airInsane_not) { sprintf(err, "%s: airSanity() failed: %s", me, airInsaneErr(aret)); biffAdd(NRRD, err); return 0; } if (airEnumValCheck(nrrdEncodingType, nrrdDefaultWriteEncodingType)) { sprintf(err, "%s: nrrdDefaultWriteEncodingType (%d) not in valid " "range [%d,%d]", me, nrrdDefaultWriteEncodingType, nrrdEncodingTypeUnknown+1, nrrdEncodingTypeLast-1); biffAdd(NRRD, err); return 0; } if (airEnumValCheck(nrrdCenter, nrrdDefaultCenter)) { sprintf(err, "%s: nrrdDefaultCenter (%d) not in valid range [%d,%d]", me, nrrdDefaultCenter, nrrdCenterUnknown+1, nrrdCenterLast-1); biffAdd(NRRD, err); return 0; } /* ---- BEGIN non-NrrdIO */ if (!( nrrdTypeDefault == nrrdDefaultResampleType || !airEnumValCheck(nrrdType, nrrdDefaultResampleType) )) { sprintf(err, "%s: nrrdDefaultResampleType (%d) not in valid range [%d,%d]", me, nrrdDefaultResampleType, nrrdTypeUnknown, nrrdTypeLast-1); biffAdd(NRRD, err); return 0; } if (airEnumValCheck(nrrdBoundary, nrrdDefaultResampleBoundary)) { sprintf(err, "%s: nrrdDefaultResampleBoundary (%d) " "not in valid range [%d,%d]", me, nrrdDefaultResampleBoundary, nrrdBoundaryUnknown+1, nrrdBoundaryLast-1); biffAdd(NRRD, err); return 0; } if (airEnumValCheck(nrrdType, nrrdStateMeasureType)) { sprintf(err, "%s: nrrdStateMeasureType (%d) not in valid range [%d,%d]", me, nrrdStateMeasureType, nrrdTypeUnknown+1, nrrdTypeLast-1); biffAdd(NRRD, err); return 0; } if (airEnumValCheck(nrrdType, nrrdStateMeasureHistoType)) { sprintf(err, "%s: nrrdStateMeasureHistoType (%d) not in valid range [%d,%d]", me, nrrdStateMeasureType, nrrdTypeUnknown+1, nrrdTypeLast-1); biffAdd(NRRD, err); return 0; } /* ---- END non-NrrdIO */ if (!( nrrdTypeSize[nrrdTypeChar] == sizeof(char) && nrrdTypeSize[nrrdTypeUChar] == sizeof(unsigned char) && nrrdTypeSize[nrrdTypeShort] == sizeof(short) && nrrdTypeSize[nrrdTypeUShort] == sizeof(unsigned short) && nrrdTypeSize[nrrdTypeInt] == sizeof(int) && nrrdTypeSize[nrrdTypeUInt] == sizeof(unsigned int) && nrrdTypeSize[nrrdTypeLLong] == sizeof(airLLong) && nrrdTypeSize[nrrdTypeULLong] == sizeof(airULLong) && nrrdTypeSize[nrrdTypeFloat] == sizeof(float) && nrrdTypeSize[nrrdTypeDouble] == sizeof(double) )) { sprintf(err, "%s: sizeof() for nrrd types has problem: " "expected (%d,%d,%d,%d,%d,%d,%d,%d,%d,%d) " "but got (%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)", me, (int)nrrdTypeSize[nrrdTypeChar], (int)nrrdTypeSize[nrrdTypeUChar], (int)nrrdTypeSize[nrrdTypeShort], (int)nrrdTypeSize[nrrdTypeUShort], (int)nrrdTypeSize[nrrdTypeInt], (int)nrrdTypeSize[nrrdTypeUInt], (int)nrrdTypeSize[nrrdTypeLLong], (int)nrrdTypeSize[nrrdTypeULLong], (int)nrrdTypeSize[nrrdTypeFloat], (int)nrrdTypeSize[nrrdTypeDouble], (int)sizeof(char), (int)sizeof(unsigned char), (int)sizeof(short), (int)sizeof(unsigned short), (int)sizeof(int), (int)sizeof(unsigned int), (int)sizeof(airLLong), (int)sizeof(airULLong), (int)sizeof(float), (int)sizeof(double)); biffAdd(NRRD, err); return 0; } /* check on NRRD_TYPE_SIZE_MAX */ maxsize = 0; for (type=nrrdTypeUnknown+1; type<=nrrdTypeLast-2; type++) { maxsize = AIR_MAX(maxsize, nrrdTypeSize[type]); } if (maxsize != NRRD_TYPE_SIZE_MAX) { sprintf(err, "%s: actual max type size is %d != %d == NRRD_TYPE_SIZE_MAX", me, (int)maxsize, NRRD_TYPE_SIZE_MAX); biffAdd(NRRD, err); return 0; } /* check on NRRD_TYPE_BIGGEST */ if (maxsize != sizeof(NRRD_TYPE_BIGGEST)) { sprintf(err, "%s: actual max type size is %d != " "%d == sizeof(NRRD_TYPE_BIGGEST)", me, (int)maxsize, (int)sizeof(NRRD_TYPE_BIGGEST)); biffAdd(NRRD, err); return 0; } /* nrrd-defined type min/max values */ tmpLLI = NRRD_LLONG_MAX; if (tmpLLI != NRRD_LLONG_MAX) { sprintf(err, "%s: long long int can't hold NRRD_LLONG_MAX (" AIR_ULLONG_FMT ")", me, NRRD_LLONG_MAX); biffAdd(NRRD, err); return 0; } tmpLLI += 1; if (NRRD_LLONG_MIN != tmpLLI) { sprintf(err, "%s: long long int min (" AIR_LLONG_FMT ") or max (" AIR_LLONG_FMT ") incorrect", me, NRRD_LLONG_MIN, NRRD_LLONG_MAX); biffAdd(NRRD, err); return 0; } tmpULLI = NRRD_ULLONG_MAX; if (tmpULLI != NRRD_ULLONG_MAX) { sprintf(err, "%s: unsigned long long int can't hold NRRD_ULLONG_MAX (" AIR_ULLONG_FMT ")", me, NRRD_ULLONG_MAX); biffAdd(NRRD, err); return 0; } tmpULLI += 1; if (tmpULLI != 0) { sprintf(err, "%s: unsigned long long int max (" AIR_ULLONG_FMT ") incorrect", me, NRRD_ULLONG_MAX); biffAdd(NRRD, err); return 0; } if (_nrrdCheckEnums()) { sprintf(err, "%s: problem with enum definition", me); biffAdd(NRRD, err); return 0; } if (!( NRRD_DIM_MAX >= 3 )) { sprintf(err, "%s: NRRD_DIM_MAX == %d seems awfully small, doesn't it?", me, NRRD_DIM_MAX); biffAdd(NRRD, err); return 0; } if (!nrrdTypeIsIntegral[nrrdTypeBlock]) { sprintf(err, "%s: nrrdTypeInteger[nrrdTypeBlock] is not true, things " "could get wacky", me); biffAdd(NRRD, err); return 0; } /* HEY: any other assumptions built into Teem? */ _nrrdSanity = 1; return 1; }
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 limnPolyDataCylinder(limnPolyData *pld, unsigned int infoBitFlag, unsigned int thetaRes, int sharpEdge) { char me[]="limnPolyDataCylinder", err[BIFF_STRLEN]; unsigned int vertNum, primNum, primIdx, indxNum, thetaIdx, vertIdx, blah; double theta, cth, sth, sq2; /* sanity bounds */ thetaRes = AIR_MAX(3, thetaRes); vertNum = sharpEdge ? 4*thetaRes : 2*thetaRes; primNum = 3; indxNum = 2*thetaRes + 2*(thetaRes+1); /* 2 fans + 1 strip */ if (limnPolyDataAlloc(pld, infoBitFlag, vertNum, indxNum, primNum)) { sprintf(err, "%s: couldn't allocate output", me); biffAdd(LIMN, err); return 1; } vertIdx = 0; for (blah=0; blah < (sharpEdge ? 2u : 1u); blah++) { for (thetaIdx=0; thetaIdx<thetaRes; thetaIdx++) { theta = AIR_AFFINE(0, thetaIdx, thetaRes, 0, 2*AIR_PI); ELL_4V_SET_TT(pld->xyzw + 4*vertIdx, float, cos(theta), sin(theta), 1, 1); /* fprintf(stderr, "!%s: vert[%u] = %g %g %g\n", me, vertIdx, (pld->xyzw + 4*vertIdx)[0], (pld->xyzw + 4*vertIdx)[1], (pld->xyzw + 4*vertIdx)[2]); */ ++vertIdx; } } for (blah=0; blah < (sharpEdge ? 2u : 1u); blah++) { for (thetaIdx=0; thetaIdx<thetaRes; thetaIdx++) { theta = AIR_AFFINE(0, thetaIdx, thetaRes, 0, 2*AIR_PI); ELL_4V_SET_TT(pld->xyzw + 4*vertIdx, float, cos(theta), sin(theta), -1, 1); /* fprintf(stderr, "!%s: vert[%u] = %g %g %g\n", me, vertIdx, (pld->xyzw + 4*vertIdx)[0], (pld->xyzw + 4*vertIdx)[1], (pld->xyzw + 4*vertIdx)[2]); */ ++vertIdx; } } primIdx = 0; vertIdx = 0; /* fan on top */ for (thetaIdx=0; thetaIdx<thetaRes; thetaIdx++) { pld->indx[vertIdx++] = thetaIdx; } pld->type[primIdx] = limnPrimitiveTriangleFan; pld->icnt[primIdx] = thetaRes; primIdx++; /* single strip around */ for (thetaIdx=0; thetaIdx<thetaRes; thetaIdx++) { pld->indx[vertIdx++] = (sharpEdge ? 1 : 0)*thetaRes + thetaIdx; pld->indx[vertIdx++] = (sharpEdge ? 2 : 1)*thetaRes + thetaIdx; } pld->indx[vertIdx++] = (sharpEdge ? 1 : 0)*thetaRes; pld->indx[vertIdx++] = (sharpEdge ? 2 : 1)*thetaRes; pld->type[primIdx] = limnPrimitiveTriangleStrip; pld->icnt[primIdx] = 2*(thetaRes+1); primIdx++; /* fan on bottom */ for (thetaIdx=0; thetaIdx<thetaRes; thetaIdx++) { pld->indx[vertIdx++] = (sharpEdge ? 3 : 1)*thetaRes + thetaIdx; } pld->type[primIdx] = limnPrimitiveTriangleFan; pld->icnt[primIdx] = thetaRes; primIdx++; if ((1 << limnPolyDataInfoNorm) & infoBitFlag) { sq2 = sqrt(2.0); if (sharpEdge) { for (thetaIdx=0; thetaIdx<thetaRes; thetaIdx++) { theta = AIR_AFFINE(0, thetaIdx, thetaRes, 0, 2*AIR_PI); cth = cos(theta); sth = sin(theta); ELL_3V_SET_TT(pld->norm + 3*(thetaIdx + 0*thetaRes), float, 0, 0, 1); ELL_3V_SET_TT(pld->norm + 3*(thetaIdx + 1*thetaRes), float, cth, sth, 0); ELL_3V_SET_TT(pld->norm + 3*(thetaIdx + 2*thetaRes), float, cth, sth, 0); ELL_3V_SET_TT(pld->norm + 3*(thetaIdx + 3*thetaRes), float, 0, 0, -1); } } else { for (thetaIdx=0; thetaIdx<thetaRes; thetaIdx++) {
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; }