int main(int argc, char *argv[]) { char *me, *errS, *outS; hestOpt *hopt=NULL; hestParm *hparm; airArray *mop; Nrrd *nin, *nout; NrrdKernelSpec *ksp; mossSampler *msp; double mat[6], **matList, *origInfo, origMat[6], origInvMat[6], ox, oy, min[2], max[2]; int d, bound, ax0, size[2]; unsigned int matListLen, _bkgLen, i, avgNum; float *bkg, *_bkg, scale[4]; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hparm->elideSingleEnumType = AIR_TRUE; hparm->elideSingleOtherType = AIR_TRUE; hparm->elideSingleOtherDefault = AIR_FALSE; hparm->elideMultipleNonExistFloatDefault = AIR_TRUE; hparm->respFileEnable = AIR_TRUE; hestOptAdd(&hopt, "i", "image", airTypeOther, 1, 1, &nin, "-", "input image", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "0", "origin", airTypeOther, 1, 1, &origInfo, "p:0,0", "where to location (0,0) prior to applying transforms.\n " "\b\bo \"u:<float>,<float>\" locate origin in a unit box " "[0,1]x[0,1] which covers the original image\n " "\b\bo \"p:<float>,<float>\" locate origin at a particular " "pixel location, in the index space of the image", NULL, NULL, mossHestOrigin); hestOptAdd(&hopt, "t", "xform0", airTypeOther, 1, -1, &matList, NULL, "transform(s) to apply to image. Transforms " "are applied in the order in which they appear.\n " "\b\bo \"identity\": no geometric transform, just resampling\n " "\b\bo \"translate:x,y\": shift image by vector (x,y), as " "measured in pixels\n " "\b\bo \"rotate:ang\": rotate CCW by ang degrees\n " "\b\bo \"scale:xs,ys\": scale by xs in X, and ys in Y\n " "\b\bo \"shear:fix,amnt\": shear by amnt, keeping fixed " "the pixels along a direction <fix> degrees from the X axis\n " "\b\bo \"flip:ang\": flip along axis an angle <ang> degrees from " "the X axis\n " "\b\bo \"a,b,tx,c,d,ty\": specify the transform explicitly " "in row-major order (opposite of PostScript) ", &matListLen, NULL, mossHestTransform); hestOptAdd(&hopt, "k", "kernel", airTypeOther, 1, 1, &ksp, "cubic:0,0.5", "reconstruction kernel", NULL, NULL, nrrdHestKernelSpec); hestOptAdd(&hopt, "min", "xMin yMin", airTypeDouble, 2, 2, min, "nan nan", "lower bounding corner of output image. Default (by not " "using this option) is the lower corner of input image. "); hestOptAdd(&hopt, "max", "xMax yMax", airTypeDouble, 2, 2, max, "nan nan", "upper bounding corner of output image. Default (by not " "using this option) is the upper corner of input image. "); hestOptAdd(&hopt, "b", "boundary", airTypeEnum, 1, 1, &bound, "bleed", "what to do when sampling outside original image.\n " "\b\bo \"bleed\": copy values at image border outward\n " "\b\bo \"wrap\": do wrap-around on image locations\n " "\b\bo \"pad\": use a given background value (via \"-bg\")", NULL, nrrdBoundary); hestOptAdd(&hopt, "bg", "bg0 bg1", airTypeFloat, 1, -1, &_bkg, "nan", "background color to use with boundary behavior \"pad\". " "Defaults to all zeroes.", &_bkgLen); hestOptAdd(&hopt, "s", "xSize ySize", airTypeOther, 2, 2, scale, "x1 x1", "For each axis, information about how many samples in output:\n " "\b\bo \"x<float>\": number of output samples is some scaling of " " the number input of samples; multiplied by <float>\n " "\b\bo \"<int>\": specify exact number of samples", NULL, NULL, &unrrduHestScaleCB); hestOptAdd(&hopt, "a", "avg #", airTypeUInt, 1, 1, &avgNum, "0", "number of averages (if there there is only one " "rotation)"); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, "-", "file to write output nrrd to"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, ilkInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); msp = mossSamplerNew(); airMopAdd(mop, msp, (airMopper)mossSamplerNix, airMopAlways); msp->boundary = bound; if (mossSamplerKernelSet(msp, ksp->kernel, ksp->parm)) { fprintf(stderr, "%s: trouble with sampler:\n%s\n", me, errS = biffGetDone(MOSS)); free(errS); airMopError(mop); return 1; } if (nrrdBoundaryPad == bound) { if (_bkgLen != MOSS_NCOL(nin)) { fprintf(stderr, "%s: got %d background colors, image has " _AIR_SIZE_T_CNV " colors\n", me, _bkgLen, MOSS_NCOL(nin)); airMopError(mop); return 1; } else { bkg = _bkg; } } else { /* maybe warn user if they gave a background that won't be used? */ /* No- because hest is stupid, and right now we always parse the single (default) "nan" for this argument! */ bkg = NULL; } ax0 = MOSS_AXIS0(nin); if (!( AIR_EXISTS(nin->axis[ax0+0].min) && AIR_EXISTS(nin->axis[ax0+0].max))) { nrrdAxisInfoMinMaxSet(nin, ax0+0, mossDefCenter); } if (!( AIR_EXISTS(nin->axis[ax0+1].min) && AIR_EXISTS(nin->axis[ax0+1].max))) { nrrdAxisInfoMinMaxSet(nin, ax0+1, mossDefCenter); } min[0] = AIR_EXISTS(min[0]) ? min[0] : nin->axis[ax0+0].min; max[0] = AIR_EXISTS(max[0]) ? max[0] : nin->axis[ax0+0].max; min[1] = AIR_EXISTS(min[1]) ? min[1] : nin->axis[ax0+1].min; max[1] = AIR_EXISTS(max[1]) ? max[1] : nin->axis[ax0+1].max; for (d=0; d<2; d++) { switch((int)scale[0 + 2*d]) { case 0: /* same number of samples as input */ size[d] = nin->axis[ax0+d].size; break; case 1: /* scaling of input # samples */ size[d] = (int)(scale[1 + 2*d]*nin->axis[ax0+d].size); break; case 2: /* explicit # of samples */ size[d] = (int)(scale[1 + 2*d]); break; } } /* find origin-based pre- and post- translate */ if (0 == origInfo[0]) { /* absolute pixel position */ mossMatTranslateSet(origMat, -origInfo[1], -origInfo[2]); } else { /* in unit box [0,1]x[0,1] */ ox = AIR_AFFINE(0.0, origInfo[1], 1.0, nin->axis[ax0+0].min, nin->axis[ax0+0].max); oy = AIR_AFFINE(0.0, origInfo[2], 1.0, nin->axis[ax0+1].min, nin->axis[ax0+1].max); mossMatTranslateSet(origMat, -ox, -oy); } mossMatInvert(origInvMat, origMat); /* form complete transform */ mossMatIdentitySet(mat); mossMatLeftMultiply(mat, origMat); for (i=0; i<matListLen; i++) { mossMatLeftMultiply(mat, matList[i]); } mossMatLeftMultiply(mat, origInvMat); if (!AIR_EXISTS(nin->axis[ax0+0].min) || !AIR_EXISTS(nin->axis[ax0+0].max)) { nrrdAxisInfoMinMaxSet(nin, ax0+0, mossDefCenter); } if (!AIR_EXISTS(nin->axis[ax0+1].min) || !AIR_EXISTS(nin->axis[ax0+1].max)) { nrrdAxisInfoMinMaxSet(nin, ax0+1, mossDefCenter); } if (avgNum > 1) { unsigned int ai; double angleMax, angle, mrot[6]; Nrrd *ntmp, *nacc; NrrdIter *itA, *itB; int E; ntmp = nrrdNew(); airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); nacc = nrrdNew(); airMopAdd(mop, nacc, (airMopper)nrrdNuke, airMopAlways); itA = nrrdIterNew(); airMopAdd(mop, itA, (airMopper)nrrdIterNix, airMopAlways); itB = nrrdIterNew(); airMopAdd(mop, itB, (airMopper)nrrdIterNix, airMopAlways); E = 0; angleMax = atan2(mat[3], mat[0]); fprintf(stderr, "%s: %u angles ", me, avgNum); for (ai=0; ai<avgNum; ai++) { fprintf(stderr, "."); fflush(stderr); angle = (180/AIR_PI)*AIR_AFFINE(0, ai, avgNum-1, angleMax, -angleMax); mossMatIdentitySet(mat); mossMatLeftMultiply(mat, origMat); mossMatRotateSet(mrot, angle); mossMatLeftMultiply(mat, mrot); mossMatLeftMultiply(mat, origInvMat); if (mossLinearTransform(ntmp, nin, bkg, mat, msp, min[0], max[0], min[1], max[1], size[0], size[1])) { fprintf(stderr, "%s: problem doing transform:\n%s\n", me, errS = biffGetDone(MOSS)); free(errS); airMopError(mop); return 1; } if (!ai) { if (!E) E |= nrrdCopy(nacc, ntmp); } else { if (!E) E |= nrrdArithBinaryOp(nacc, nrrdBinaryOpAdd, nacc, ntmp); } if (E) { break; } } fprintf(stderr, "\n"); nrrdIterSetNrrd(itA, nacc); nrrdIterSetValue(itB, avgNum); if (!E) E |= nrrdArithIterBinaryOp(nout, nrrdBinaryOpDivide, itA, itB); if (E) { fprintf(stderr, "%s: problem making output:\n%s\n", me, errS = biffGetDone(NRRD)); free(errS); airMopError(mop); return 1; } } else { if (mossLinearTransform(nout, nin, bkg, mat, msp, min[0], max[0], min[1], max[1], size[0], size[1])) { fprintf(stderr, "%s: problem doing transform:\n%s\n", me, errS = biffGetDone(MOSS)); free(errS); airMopError(mop); return 1; } } if (nrrdSave(outS, nout, NULL)) { fprintf(stderr, "%s: problem saving output:\n%s\n", me, errS = biffGetDone(NRRD)); free(errS); airMopError(mop); return 1; } airMopOkay(mop); exit(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; } } }