示例#1
0
文件: vh.c 项目: SCIInstitute/Cleaver
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);
}
示例#2
0
/*
** little helper function to do pre-blurring of a given nrrd
** of the sort that might be useful for scale-space gage use
**
** nblur has to already be allocated for "blnum" Nrrd*s, AND, they all
** have to point to valid (possibly empty) Nrrds, so they can hold the
** results of blurring. "scale" is filled with the result of
** scaleCB(d_i), for "dom" evenly-spaced samples d_i between
** scldomMin and scldomMax
*/
int
gageStackBlur(Nrrd *const nblur[], const double *scale,
              unsigned int blnum,
              const Nrrd *nin, unsigned int baseDim,
              const NrrdKernelSpec *_kspec,
              int boundary, int renormalize, int verbose) {
    char me[]="gageStackBlur", err[BIFF_STRLEN], val[AIR_STRLEN_LARGE],
              keyscl[]="scale", keykern[]="kernel";
    unsigned int blidx, axi;
    NrrdResampleContext *rsmc;
    NrrdKernelSpec *kspec;
    airArray *mop;
    int E;

    if (!(nblur && scale && nin && _kspec)) {
        sprintf(err, "%s: got NULL pointer", me);
        biffAdd(GAGE, err);
        return 1;
    }
    if (!( blnum >= 2)) {
        sprintf(err, "%s: need blnum > 2, not %u", me, blnum);
        biffAdd(GAGE, err);
        return 1;
    }
    for (blidx=0; blidx<blnum; blidx++) {
        if (!AIR_EXISTS(scale[blidx])) {
            fprintf(stderr, "%s: scale[%u] = %g doesn't exist", me, blidx,
                    scale[blidx]);
            biffAdd(GAGE, err);
            return 1;
        }
        if (blidx) {
            if (!( scale[blidx-1] < scale[blidx] )) {
                fprintf(stderr, "%s: scale[%u] = %g not < scale[%u] = %g", me,
                        blidx, scale[blidx-1], blidx+1, scale[blidx]);
                biffAdd(GAGE, err);
                return 1;
            }
        }
    }
    if (3 + baseDim != nin->dim) {
        sprintf(err, "%s: need nin->dim %u (not %u) with baseDim %u", me,
                3 + baseDim, nin->dim, baseDim);
        biffAdd(GAGE, err);
        return 1;
    }
    if (airEnumValCheck(nrrdBoundary, boundary)) {
        sprintf(err, "%s: %d not a valid %s value", me,
                boundary, nrrdBoundary->name);
        biffAdd(GAGE, err);
        return 1;
    }

    mop = airMopNew();
    kspec = nrrdKernelSpecCopy(_kspec);
    if (!kspec) {
        sprintf(err, "%s: problem copying kernel spec", me);
        biffAdd(GAGE, err);
        airMopError(mop);
        return 1;
    }
    airMopAdd(mop, kspec, (airMopper)nrrdKernelSpecNix, airMopAlways);
    /* pre-allocate output Nrrds in case not already there */
    for (blidx=0; blidx<blnum; blidx++) {
        if (!nblur[blidx]) {
            sprintf(err, "%s: got NULL nblur[%u]", me, blidx);
            biffAdd(GAGE, err);
            airMopError(mop);
            return 1;
        }
    }
    rsmc = nrrdResampleContextNew();
    airMopAdd(mop, rsmc, (airMopper)nrrdResampleContextNix, airMopAlways);

    E = 0;
    if (!E) E |= nrrdResampleDefaultCenterSet(rsmc, nrrdDefaultCenter);
    if (!E) E |= nrrdResampleNrrdSet(rsmc, nin);
    if (baseDim) {
        unsigned int bai;
        for (bai=0; bai<baseDim; bai++) {
            if (!E) E |= nrrdResampleKernelSet(rsmc, bai, NULL, NULL);
        }
    }
    for (axi=0; axi<3; axi++) {
        if (!E) E |= nrrdResampleSamplesSet(rsmc, baseDim + axi,
                                                nin->axis[baseDim + axi].size);
        if (!E) E |= nrrdResampleRangeFullSet(rsmc, baseDim + axi);
    }
    if (!E) E |= nrrdResampleBoundarySet(rsmc, boundary);
    if (!E) E |= nrrdResampleTypeOutSet(rsmc, nrrdTypeDefault);
    if (!E) E |= nrrdResampleRenormalizeSet(rsmc, renormalize);
    if (E) {
        fprintf(stderr, "%s: trouble setting up resampling\n", me);
        biffAdd(GAGE, err);
        airMopError(mop);
        return 1;
    }
    for (blidx=0; blidx<blnum; blidx++) {
        kspec->parm[0] = scale[blidx];
        for (axi=0; axi<3; axi++) {
            if (!E) E |= nrrdResampleKernelSet(rsmc, baseDim + axi,
                                                   kspec->kernel, kspec->parm);
        }
        if (verbose) {
            fprintf(stderr, "%s: resampling %u of %u (scale %g) ... ", me, blidx,
                    blnum, scale[blidx]);
            fflush(stderr);
        }
        if (!E) E |= nrrdResampleExecute(rsmc, nblur[blidx]);
        if (!E) nrrdKeyValueAdd(nblur[blidx], me, "true");
        sprintf(val, "%g", scale[blidx]);
        if (!E) nrrdKeyValueAdd(nblur[blidx], keyscl, val);
        nrrdKernelSpecSprint(val, kspec);
        if (!E) nrrdKeyValueAdd(nblur[blidx], keykern, val);
        if (E) {
            if (verbose) {
                fprintf(stderr, "problem!\n");
            }
            sprintf(err, "%s: trouble resampling %u of %u (scale %g)",
                    me, blidx, blnum, scale[blidx]);
            biffAdd(GAGE, err);
            airMopError(mop);
            return 1;
        }
        if (verbose) {
            fprintf(stderr, "done.\n");
        }
    }

    airMopOkay(mop);
    return 0;
}
示例#3
0
文件: mchist.c 项目: BRAINSia/teem
int
main(int argc, const char *argv[]) {
  const char *me;
  hestOpt *hopt;
  hestParm *hparm;
  airArray *mop;

  char **ninStr, *err, *outS, doneStr[13];
  Nrrd *nin0, *nin, *nrgb, *nout, *nhist[2], *npreout, *nhproj[3];
  float *rgb;
  float *out, *preout, *hist[2], maxSum,
    upSample, overSampleScale;
  unsigned int size0, sX, sY, sH, ninLen, ti, overSampleNum;
  NrrdResampleContext *rsmc;
  NrrdKernelSpec *ksp;

  me = argv[0];
  mop = airMopNew();
  hopt = NULL;
  hparm = hestParmNew();
  airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways);
  hparm->respFileEnable = AIR_TRUE;
  hestOptAdd(&hopt, "i", "images", airTypeString, 1, -1, &ninStr, NULL,
             "input image sequence", &ninLen, NULL, NULL);
  hestOptAdd(&hopt, "sh", "histo size", airTypeUInt, 1, 1, &sH, "500",
             "histogram size");
  hestOptAdd(&hopt, "k", "kern", airTypeOther, 1, 1, &ksp,
             "tent", "kernel for upsampling images",
             NULL, NULL, nrrdHestKernelSpec);
  hestOptAdd(&hopt, "us", "upsampling", airTypeFloat, 1, 1, &upSample,
             "1", "amount of upsampling of image");
  hestOptAdd(&hopt, "osn", "# oversmp", airTypeUInt, 1, 1, &overSampleNum,
             "1", "number of sample per (upsampled) pixel");
  hestOptAdd(&hopt, "osc", "scaling", airTypeFloat, 1, 1, &overSampleScale,
             "1", "scaling with oversampling");
  hestOptAdd(&hopt, "ms", "max sum", airTypeFloat, 1, 1, &maxSum,
             "10", "per-hue histogram summation is non-linearly and "
             "asymptotically clamped to this maximum");
  hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
             "output filename", NULL);

  hestParseOrDie(hopt, argc-1, argv+1, hparm,
                 me, mchistInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE);
  airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways);
  airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways);

  if (0 == overSampleNum) {
    fprintf(stderr, "%s: overSampleNum must be > 0\n", me);
    airMopError(mop);
    return 1;
  }
  nin0 = nrrdNew();
  airMopAdd(mop, nin0, (airMopper)nrrdNuke, airMopAlways);
  if (nrrdLoad(nin0, ninStr[0], NULL)) {
    airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways);
    fprintf(stderr, "%s: couldn't load first image:\n%s", me, err);
    airMopError(mop);
    return 1;
  }
  if (!( (3 == nin0->axis[0].size || 4 == nin0->axis[0].size)
         && 3 == nin0->dim
         && nrrdTypeUChar == nin0->type )) {
    fprintf(stderr, "%s: 1st image not 3D (3-or-4)-by-X-by-Y %s array "
            "(got %u-D %s array)\n", me,
            airEnumStr(nrrdType, nrrdTypeUChar),
            nin0->dim,
            airEnumStr(nrrdType, nin0->type));
    airMopError(mop);
    return 1;
  }
  rsmc = nrrdResampleContextNew();
  airMopAdd(mop, rsmc, (airMopper)nrrdResampleContextNix, airMopAlways);
  size0 = AIR_CAST(unsigned int, nin0->axis[0].size);
  sX = AIR_CAST(unsigned int, upSample*nin0->axis[1].size);
  sY = AIR_CAST(unsigned int, upSample*nin0->axis[2].size);
  nrgb = nrrdNew();
  airMopAdd(mop, nrgb, (airMopper)nrrdNuke, airMopAlways);
  if (nrrdResampleDefaultCenterSet(rsmc, nrrdCenterCell)
      || nrrdResampleInputSet(rsmc, nin0)
      || nrrdResampleKernelSet(rsmc, 1, ksp->kernel, ksp->parm)
      || nrrdResampleKernelSet(rsmc, 2, ksp->kernel, ksp->parm)
      || nrrdResampleSamplesSet(rsmc, 1, sX)
      || nrrdResampleSamplesSet(rsmc, 2, sY)
      || nrrdResampleRangeFullSet(rsmc, 1)
      || nrrdResampleRangeFullSet(rsmc, 2)
      || nrrdResampleTypeOutSet(rsmc, nrrdTypeFloat)
      || nrrdResampleRenormalizeSet(rsmc, AIR_TRUE)
      || nrrdResampleExecute(rsmc, nrgb)) {
    airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways);
    fprintf(stderr, "%s: error resampling slice:\n%s", me, err);
    airMopError(mop);
    return 1;
  }

  nhist[0] = nrrdNew();
  airMopAdd(mop, nhist[0], (airMopper)nrrdNuke, airMopAlways);
  nhist[1] = nrrdNew();
  airMopAdd(mop, nhist[1], (airMopper)nrrdNuke, airMopAlways);
  if (nrrdMaybeAlloc_va(nhist[0], nrrdTypeFloat, 2,
                        AIR_CAST(size_t, sH),
                        AIR_CAST(size_t, sH))
      || nrrdMaybeAlloc_va(nhist[1], nrrdTypeFloat, 2,
                           AIR_CAST(size_t, sH),
                           AIR_CAST(size_t, sH))) {
    airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways);
    fprintf(stderr, "%s: error allocating histos:\n%s", me, err);
    airMopError(mop);
    return 1;
  }
  nhist[0]->axis[0].min = nhist[0]->axis[1].min = 0.0;
  nhist[0]->axis[0].max = nhist[0]->axis[1].max = 1.0;
  nhist[1]->axis[0].min = nhist[1]->axis[1].min = 0.0;
  nhist[1]->axis[0].max = nhist[1]->axis[1].max = 1.0;
  nhproj[0] = nrrdNew();
  airMopAdd(mop, nhproj[0], (airMopper)nrrdNix, airMopAlways);
  nhproj[1] = nrrdNew();
  airMopAdd(mop, nhproj[1], (airMopper)nrrdNix, airMopAlways);
  nhproj[2] = nrrdNew();
  airMopAdd(mop, nhproj[2], (airMopper)nrrdNix, airMopAlways);

  printf("working ...       ");
  hist[0] = AIR_CAST(float *, nhist[0]->data);
  hist[1] = AIR_CAST(float *, nhist[1]->data);
  nin = nrrdNew();
  airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways);
  npreout = nrrdNew();
  airMopAdd(mop, npreout, (airMopper)nrrdNuke, airMopAlways);
  if (nrrdMaybeAlloc_va(npreout, nrrdTypeFloat, 3,
                        AIR_CAST(size_t, 3),
                        AIR_CAST(size_t, sH),
                        AIR_CAST(size_t, ninLen))) {
    airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways);
    fprintf(stderr, "%s: error allocating pre-output:\n%s", me, err);
    airMopError(mop);
    return 1;
  }
  preout = AIR_CAST(float *, npreout->data);
  for (ti=0; ti<ninLen; ti++) {
    printf("%s", airDoneStr(0, ti, ninLen, doneStr)); fflush(stdout);
    if (nrrdLoad(nin, ninStr[ti], NULL)) {
      airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways);
      fprintf(stderr, "%s: couldn't load image[%u]:\n%s", me, ti, err);
      airMopError(mop);
      return 1;
    }
    if (!nrrdSameSize(nin0, nin, AIR_TRUE)) {
      airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways);
      fprintf(stderr, "%s: nin[%u] not like nin[0]:\n%s", me, ti, err);
      airMopError(mop);
      return 1;
    }
    if (nrrdResampleInputSet(rsmc, nin)
        || nrrdResampleExecute(rsmc, nrgb)) {
      airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways);
      fprintf(stderr, "%s: trouble resampling nin[%u]:\n%s", me, ti, err);
      airMopError(mop);
      return 1;
    }
    if (nrrdWrap_va(nhproj[0], preout + 0*sH, nrrdTypeFloat, 1, AIR_CAST(size_t, sH)) ||
        nrrdWrap_va(nhproj[1], preout + 1*sH, nrrdTypeFloat, 1, AIR_CAST(size_t, sH)) ||
        nrrdWrap_va(nhproj[2], preout + 2*sH, nrrdTypeFloat, 1, AIR_CAST(size_t, sH))) {
      airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways);
      fprintf(stderr, "%s: trouble wrapping output[%u]:\n%s", me, ti, err);
      airMopError(mop);
      return 1;
    }
    rgb = AIR_CAST(float *, nrgb->data);
    imageProc(nhproj, nhist, sH,
              rgb, size0, sX*sY,
              overSampleNum, overSampleScale);
    preout += 3*sH;
  }
  printf("%s\n", airDoneStr(0, ti, ninLen, doneStr)); fflush(stdout);

  nout = nrrdNew();
  airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways);
  if (nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 3,
                        AIR_CAST(size_t, 3),
                        AIR_CAST(size_t, sH),
                        AIR_CAST(size_t, ninLen))) {
    airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways);
    fprintf(stderr, "%s: error allocating output:\n%s", me, err);
    airMopError(mop);
    return 1;
  }
  out = AIR_CAST(float *, nout->data);
  preout = AIR_CAST(float *, npreout->data);
  for (ti=0; ti<ninLen; ti++) {
    unsigned int hi;
    float hh, vv, ss, scl;
    for (hi=0; hi<sH; hi++) {
      hh = AIR_AFFINE(0, hi, sH, 0, 1);
      if (!preout[hi + 2*sH]) {
        ELL_3V_SET(out + 3*hi, 0, 0, 0);
      } else {
        ss = preout[hi + 2*sH];
        scl = ss/(maxSum + ss);
        vv = scl*preout[hi + 1*sH];
        dyeHSVtoRGB(out + 0 + 3*hi, out + 1 + 3*hi, out + 2 + 3*hi,
                    hh, preout[hi + 0*sH], vv);
      }
    }
    out += 3*sH;
    preout += 3*sH;
  }

  if (nrrdSave(outS, nout, NULL)) {
    airMopAdd(mop, err=biffGetDone(NRRD), airFree, airMopAlways);
    fprintf(stderr, "%s: error saving output:\n%s", me, err);
    airMopError(mop);
    return 1;
  }

  airMopOkay(mop);
  return 0;
}