void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { char me[]="nrrdSave", *filename, *errPtr, errBuff[AIR_STRLEN_MED]; int filenameLen, ntype; size_t sizeZ[NRRD_DIM_MAX]; unsigned int dim, axIdx; Nrrd *nrrd; airArray *mop; const mxArray *filenameMx, *arrayMx; if (!(2 == nrhs && mxIsChar(prhs[0]) )) { sprintf(errBuff, "%s: requires two args: one string, one array", me); mexErrMsgTxt(errBuff); } filenameMx = prhs[0]; arrayMx = prhs[1]; if (mxIsComplex(arrayMx)) { sprintf(errBuff, "%s: sorry, array must be real", me); mexErrMsgTxt(errBuff); } ntype = typeMtoN(mxGetClassID(arrayMx)); if (nrrdTypeUnknown == ntype) { sprintf(errBuff, "%s: sorry, can't handle type %s", me, mxGetClassName(arrayMx)); mexErrMsgTxt(errBuff); } dim = mxGetNumberOfDimensions(arrayMx); if (!( 1 <= dim && dim <= NRRD_DIM_MAX )) { sprintf(errBuff, "%s: number of array dimensions %d outside range [1,%d]", me, dim, NRRD_DIM_MAX); mexErrMsgTxt(errBuff); } filenameLen = mxGetM(filenameMx)*mxGetN(filenameMx)+1; filename = mxCalloc(filenameLen, sizeof(mxChar)); /* managed by Matlab */ mxGetString(filenameMx, filename, filenameLen); for (axIdx=0; axIdx<dim; axIdx++) { sizeZ[axIdx] = mxGetDimensions(arrayMx)[axIdx]; } nrrd = nrrdNew(); mop = airMopNew(); airMopAdd(mop, nrrd, (airMopper)nrrdNix, airMopAlways); if (nrrdWrap_nva(nrrd, mxGetPr(arrayMx), ntype, dim, sizeZ) || nrrdSave(filename, nrrd, NULL)) { errPtr = biffGetDone(NRRD); airMopAdd(mop, errPtr, airFree, airMopAlways); sprintf(errBuff, "%s: error saving NRRD:\n%s", me, errPtr); airMopError(mop); mexErrMsgTxt(errBuff); } airMopOkay(mop); return; }
/* ******** nrrdAlloc_nva() ** ** allocates data array and sets information. If this is a block type ** nrrd, it is necessary to set nrrd->blockSize PRIOR to calling ** this function. ** ** This function will always allocate more memory (via calloc), but ** it will free() nrrd->data if it is non-NULL when passed in. ** ** This function takes the same "don't mess with peripheral information" ** attitude as nrrdWrap(). ** ** Note to Gordon: don't get clever and change ANY axis-specific ** information here. It may be very convenient to set that before ** nrrdAlloc or nrrdMaybeAlloc ** ** Note: This function DOES use biff */ int nrrdAlloc_nva(Nrrd *nrrd, int type, unsigned int dim, const size_t *size) { char me[]="nrrdAlloc_nva", err[BIFF_STRLEN]; size_t num; int esize; if (!(nrrd && size)) { sprintf(err, "%s: got NULL pointer", me); biffAdd(NRRD, err); return 1; } if (airEnumValCheck(nrrdType, type)) { sprintf(err, "%s: type (%d) is invalid", me, type); biffAdd(NRRD, err); return 1; } if (nrrdTypeBlock == type) { if (!(0 < nrrd->blockSize)) { sprintf(err, "%s: given nrrd->blockSize " _AIR_SIZE_T_CNV " invalid", me, nrrd->blockSize); biffAdd(NRRD, err); return 1; } } if (!AIR_IN_CL(1, dim, NRRD_DIM_MAX)) { sprintf(err, "%s: dim (%d) not in valid range [1,%d]", me, dim, NRRD_DIM_MAX); biffAdd(NRRD, err); return 1; } nrrd->data = airFree(nrrd->data); if (nrrdWrap_nva(nrrd, NULL, type, dim, size)) { sprintf(err, "%s:", me); biffAdd(NRRD, err); return 1 ; } num = nrrdElementNumber(nrrd); esize = nrrdElementSize(nrrd); nrrd->data = calloc(num, esize); if (!(nrrd->data)) { sprintf(err, "%s: calloc(" _AIR_SIZE_T_CNV ",%d) failed", me, num, esize); biffAdd(NRRD, err); return 1 ; } return 0; }
int _nrrdCopy(Nrrd *nout, const Nrrd *nin, int bitflag) { char me[]="_nrrdCopy", err[BIFF_STRLEN]; size_t size[NRRD_DIM_MAX]; if (!(nin && nout)) { sprintf(err, "%s: got NULL pointer", me); biffAdd(NRRD, err); return 1; } if (nout == nin) { /* its not the case that we have nothing to do- the semantics of copying cannot be achieved if the input and output nrrd are the same; this is an error */ sprintf(err, "%s: nout==nin disallowed", me); biffAdd(NRRD, err); return 1; } if (!nrrdElementSize(nin)) { sprintf(err, "%s: input nrrd reports zero element size!", me); biffAdd(NRRD, err); return 1; } nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size); if (nin->data) { if (nrrdMaybeAlloc_nva(nout, nin->type, nin->dim, size)) { sprintf(err, "%s: couldn't allocate data", me); biffAdd(NRRD, err); return 1; } memcpy(nout->data, nin->data, nrrdElementNumber(nin)*nrrdElementSize(nin)); } else { /* someone is trying to copy structs without data, fine fine fine */ if (nrrdWrap_nva(nout, NULL, nin->type, nin->dim, size)) { sprintf(err, "%s: couldn't allocate data", me); biffAdd(NRRD, err); return 1; } } nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_SIZE_BIT); /* if nin->data non-NULL (second branch above), this will harmlessly unset and set type and dim */ nrrdBasicInfoInit(nout, NRRD_BASIC_INFO_DATA_BIT | bitflag); if (nrrdBasicInfoCopy(nout, nin, NRRD_BASIC_INFO_DATA_BIT | bitflag)) { sprintf(err, "%s: trouble copying basic info", me); biffAdd(NRRD, err); return 1; } return 0; }
/* ******** nrrdWrap_va() ** ** Minimal var args wrapper around nrrdWrap_nva, with the advantage of ** taking all the axes sizes as the var args. ** ** This is THE BEST WAY to wrap a nrrd around existing raster data, ** assuming that the dimension is known at compile time. ** ** If successful, returns 0, otherwise, 1. ** This does use biff. */ int nrrdWrap_va(Nrrd *nrrd, void *data, int type, unsigned int dim, ...) { char me[]="nrrdWrap_va", err[BIFF_STRLEN]; va_list ap; size_t size[NRRD_DIM_MAX]; unsigned int ai; if (!(nrrd && data)) { sprintf(err, "%s: got NULL pointer", me); biffAdd(NRRD, err); return 1; } va_start(ap, dim); for (ai=0; ai<dim; ai++) { size[ai] = va_arg(ap, size_t); } va_end(ap); return nrrdWrap_nva(nrrd, data, type, dim, size); }
/* ******** nrrdMaybeAlloc_nva ** ** calls nrrdAlloc_nva if the requested space is different than ** what is currently held ** ** also subscribes to the "don't mess with peripheral information" philosophy */ int nrrdMaybeAlloc_nva(Nrrd *nrrd, int type, unsigned int dim, const size_t *size) { char me[]="nrrdMaybeAlloc_nva", err[BIFF_STRLEN]; size_t sizeWant, sizeHave, numWant, elementSizeWant; int need; unsigned int ai; if (!nrrd) { sprintf(err, "%s: got NULL pointer", me); biffAdd(NRRD, err); return 1; } if (airEnumValCheck(nrrdType, type)) { sprintf(err, "%s: type (%d) is invalid", me, type); biffAdd(NRRD, err); return 1; } if (nrrdTypeBlock == type) { if (nrrdTypeBlock == nrrd->type) { sprintf(err, "%s: can't change from one block nrrd to another", me); biffAdd(NRRD, err); return 1; } if (!(0 < nrrd->blockSize)) { sprintf(err, "%s: given nrrd->blockSize " _AIR_SIZE_T_CNV " invalid", me, nrrd->blockSize); biffAdd(NRRD, err); return 1; } elementSizeWant = nrrd->blockSize; } else { elementSizeWant = nrrdTypeSize[type]; } if (_nrrdSizeCheck(size, dim, AIR_TRUE)) { sprintf(err, "%s:", me); biffAdd(NRRD, err); return 1; } if (!(nrrd->data)) { need = 1; } else { numWant = 1; for (ai=0; ai<dim; ai++) { numWant *= size[ai]; } if (!nrrdElementSize(nrrd)) { sprintf(err, "%s: nrrd reports zero element size!", me); biffAdd(NRRD, err); return 1; } sizeHave = nrrdElementNumber(nrrd) * nrrdElementSize(nrrd); /* fprintf(stderr, "##%s: sizeHave = %d * %d = %d\n", me, (int)(nrrdElementNumber(nrrd)), (int)(nrrdElementSize(nrrd)), (int)sizeHave); */ sizeWant = numWant * elementSizeWant; /* fprintf(stderr, "##%s: sizeWant = %d * %d = %d\n", me, (int)(numWant), (int)(elementSizeWant), (int)sizeWant); */ need = sizeHave != sizeWant; /* fprintf(stderr, "##%s: need = %d\n", me, need); */ } if (need) { if (nrrdAlloc_nva(nrrd, type, dim, size)) { sprintf(err, "%s:", me); biffAdd(NRRD, err); return 1; } } else { if (nrrdWrap_nva(nrrd, nrrd->data, type, dim, size)) { sprintf(err, "%s:", me); biffAdd(NRRD, err); return 1; } /* but we do have to initialize memory! */ memset(nrrd->data, 0, nrrdElementNumber(nrrd)*nrrdElementSize(nrrd)); } return 0; }
/* ******** nrrdFFT ** ** First pass at a wrapper around FFTW. This was implemented out of need for a ** specific project; and better decisions and different interfaces will become ** apparent with time and experience; these can be in Teem 2.0. ** ** currently *requires* that input be complex-valued, in that axis 0 has to ** have size 2. nrrdKindComplex would be sensible for input axis 0 but we don't ** require it, though it is set on the output. */ int nrrdFFT(Nrrd *nout, const Nrrd *_nin, unsigned int *axes, unsigned int axesNum, int sign, int rescale, int rigor) { static const char me[]="nrrdFFT"; size_t inSize[NRRD_DIM_MAX], II, NN, nprod; double *inData, *outData; airArray *mop; Nrrd *nin; unsigned int axi, axisDo[NRRD_DIM_MAX]; fftw_plan plan; void *dataBef; unsigned int txfRank, howRank, flags; size_t stride; fftw_iodim txfDims[NRRD_DIM_MAX], howDims[NRRD_DIM_MAX]; if (!(nout && _nin && axes)) { biffAddf(NRRD, "%s: got NULL pointer", me); return 1; } if (!( _nin->dim > 1 && 2 == _nin->axis[0].size )) { biffAddf(NRRD, "%s: nin doesn't look like a complex-valued array", me); return 1; } if (!( axesNum >= 1 )) { biffAddf(NRRD, "%s: axesNum 0, no axes to transform?", me); return 1; } for (axi=0; axi<_nin->dim; axi++) { axisDo[axi] = 0; } for (axi=0; axi<axesNum; axi++) { if (0 == axes[axi]) { biffAddf(NRRD, "%s: can't transform axis 0 (axes[%u]) for " "real/complex values", me, axi); return 1; } if (!( axes[axi] < _nin->dim )) { biffAddf(NRRD, "%s: axis %u (axes[%u]) out of range [1,%u]", me, axes[axi], axi, _nin->dim-1); return 1; } axisDo[axes[axi]]++; if (2 == axisDo[axes[axi]]) { biffAddf(NRRD, "%s: axis %u (axes[%u]) already transformed", me, axes[axi], axi); return 1; } } NN = nrrdElementNumber(_nin); /* We always make a new buffer to hold the double-type copy of input for two reasons: if input is not double we have to convert it, and we want input to be const, and we can't have const with the plan creation over-writing the input (except with FFTW_ESTIMATE). Given that, we might as well use the memory-alignment-savvy fftw_malloc; the freeing is handled by both _nrrdFftwFreeWrapper and nrrdNix. */ /* (NN = 2 * number of complex values) */ inData = AIR_CAST(double *, fftw_malloc(NN*sizeof(double))); if (!inData) { biffAddf(NRRD, "%s: couldn't allocate input data copy", me); return 1; } mop = airMopNew(); airMopAdd(mop, inData, _nrrdFftwFreeWrapper, airMopAlways); nin = nrrdNew(); airMopAdd(mop, nin, (airMopper)nrrdNix /* NOT Nuke */, airMopAlways); nrrdAxisInfoGet_nva(_nin, nrrdAxisInfoSize, inSize); /* we don't copy data yet; it may be over-written during plan creation */ if (nrrdWrap_nva(nin, inData, nrrdTypeDouble, _nin->dim, inSize)) { biffAddf(NRRD, "%s: couldn't wrap or copy input", me); airMopError(mop); return 1; } /* But on the output, we just use regular malloc, because we don't (yet) have a way of telling nrrd to use fftw_malloc/fftw_free instead of the generic malloc/free, and we don't want two whole copies of the output (one that is memory-aligned, internal to this function, and one that isn't, in nout) */ if (nrrdMaybeAlloc_nva(nout, nrrdTypeDouble, _nin->dim, inSize)) { biffAddf(NRRD, "%s: couldn't allocate output", me); airMopError(mop); return 1; } outData = AIR_CAST(double *, nout->data); /* As far as GLK can tell, the guru interface is needed, and the "advanced" fftw_plan_many_dft won't work, because its simplistic accounting of stride can't handle having non-contiguous non-transformed axes (e.g. transforming only axes 2 and not 1, 3 in a 3-D complex-valued array) */ txfRank = howRank = 0; stride = 1; nprod = 1; for (axi=1; axi<nin->dim; axi++) { if (axisDo[axi]) { txfDims[txfRank].n = AIR_CAST(int, inSize[axi]); txfDims[txfRank].is = txfDims[txfRank].os = AIR_CAST(int, stride); nprod *= inSize[axi]; txfRank++; } else { howDims[howRank].n = AIR_CAST(int, inSize[axi]); howDims[howRank].is = howDims[howRank].os = AIR_CAST(int, stride); howRank++; } stride *= inSize[axi]; }