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 unrrdu_2opMain(int argc, char **argv, char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *err, *seedS; NrrdIter *in1, *in2; Nrrd *nout, *ntmp=NULL; int op, type, E, pret; airArray *mop; unsigned int seed; hestOptAdd(&opt, NULL, "operator", airTypeEnum, 1, 1, &op, NULL, "Binary operator. Possibilities include:\n " "\b\bo \"+\", \"-\", \"x\", \"/\": " "add, subtract, multiply, divide\n " "\b\bo \"^\": exponentiation (pow)\n " "\b\bo \"spow\": signed exponentiation: sgn(x)pow(abs(x),p)\n " "\b\bo \"%\": integer modulo\n " "\b\bo \"fmod\": same as fmod() in C\n " "\b\bo \"atan2\": same as atan2() in C\n " "\b\bo \"min\", \"max\": minimum, maximum\n " "\b\bo \"lt\", \"lte\", \"gt\", \"gte\": same as C's <, <=, >, <=\n " "\b\bo \"eq\", \"neq\": same as C's == and !=\n " "\b\bo \"comp\": -1, 0, or 1 if 1st value is less than, " "equal to, or greater than 2nd value\n " "\b\bo \"if\": if 1st value is non-zero, use it, " "else use 2nd value\n " "\b\bo \"exists\": if 1st value exists, use it, " "else use 2nd value\n " "\b\bo \"nrand\": scale unit-stdv Gaussian noise by 2nd value " "and add to first value", NULL, nrrdBinaryOp); hestOptAdd(&opt, NULL, "in1", airTypeOther, 1, 1, &in1, NULL, "First input. Can be a single value or a nrrd.", NULL, NULL, nrrdHestIter); hestOptAdd(&opt, NULL, "in2", airTypeOther, 1, 1, &in2, NULL, "Second input. Can be a single value or a nrrd.", NULL, NULL, nrrdHestIter); hestOptAdd(&opt, "s,seed", "seed", airTypeString, 1, 1, &seedS, "", "seed value for RNG for nrand, so that you " "can get repeatable results between runs, or, " "by not using this option, the RNG seeding will be " "based on the current time"); hestOptAdd(&opt, "t,type", "type", airTypeOther, 1, 1, &type, "default", "type to convert all INPUT nrrds to, prior to " "doing operation, useful for doing, for instance, the difference " "between two unsigned char nrrds. This will also determine " "output type. By default (not using this option), the types of " "the input nrrds are left unchanged.", NULL, NULL, &unrrduHestMaybeTypeCB); OPT_ADD_NOUT(out, "output nrrd"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_2opInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); /* fprintf(stderr, "%s: op = %d\n", me, op); fprintf(stderr, "%s: in1->left = %d, in2->left = %d\n", me, (int)(in1->left), (int)(in2->left)); */ if (nrrdTypeDefault != type) { /* they wanted to convert nrrds to some other type first */ E = 0; if (in1->ownNrrd) { if (!E) E |= nrrdConvert(ntmp=nrrdNew(), in1->ownNrrd, type); if (!E) nrrdIterSetOwnNrrd(in1, ntmp); } if (in2->ownNrrd) { if (!E) E |= nrrdConvert(ntmp=nrrdNew(), in2->ownNrrd, type); if (!E) nrrdIterSetOwnNrrd(in2, 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; } /* this will still leave a nrrd in the NrrdIter for nrrdIterNix() (called by hestParseFree() called be airMopOkay()) to clear up */ } if (nrrdBinaryOpNormalRandScaleAdd == op) { if (airStrlen(seedS)) { if (1 != sscanf(seedS, "%u", &seed)) { fprintf(stderr, "%s: couldn't parse seed \"%s\" as uint\n", me, seedS); airMopError(mop); return 1; } else { airSrandMT(seed); } } else { /* got no request for specific seed */ airSrandMT(AIR_CAST(unsigned int, airTime())); } } if (nrrdArithIterBinaryOp(nout, op, in1, in2)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error doing binary operation:\n%s", me, err); airMopError(mop); return 1; } SAVE(out, nout, NULL); airMopOkay(mop); return 0; }