Exemplo n.º 1
0
/*!
* \return	The filtered object, maybe NULL on error.
* \ingroup	WlzValueFilters
* \brief	WlzSeqPar performs a sequential or parallel local
*               transform. A distance transform is an example of a
*               sequential transform and a laplacian is an example of
*               a parallel transform.
*               Only WLZ_2D_DOMAINOBJ objects with values may be passed
*               to WlzSeqPar().
* \note		If the point to be transformed is at line l and col k,
*               there is an array of pointers spWSpace->adrptr[-7:7],
*               whose i'th entry gives the address of the point
*               (l + (i * spWSpace->ldelta), k), but which is only
*               meaningful for lines within bdrSz of the point.
*               For example: spWSpace->adrptr[-3] and
*               spWSpace->adrptr[3] are undefined if bdrSz < 3.
* \param	srcObj			The given WLZ_2D_DOMAINOBJ object.
* \param	newObjFlag		If zero then the given object is
*					overwritten otherwise a new object is
*					created.
* \param	sequentialFlag		If non zero the transform is
*                                       seqential and transformed
*                                       values are used in calculating
*                                       the neighbouring values.
*                                       If zero the transform always
*                                       works on the original grey
*                                       values.
* \param	rasterDir		Direction of raster scan.
* \param	bdrSz			Local transform kernel half-size,
*					must be in range 0 - 7.
*                                       The usual 8 immediate neighbors
*                                       correspond to bdrSz == 1.
* \param	bkgVal			Background grey value.
* \param	transformData		Data supplied to the transform
*                                       function.
* \param	transformFn		Supplied transform function.
* \param	dstErr			Destination error pointer, may
*                                       be NULL.
*/
WlzObject	*WlzSeqPar(WlzObject *srcObj,
			   int newObjFlag, int sequentialFlag,
			   WlzRasterDir rasterDir, int bdrSz, int bkgVal,
			   void *transformData,
			   int (*transformFn)(WlzSeqParWSpace *, void *),
			   WlzErrorNum *dstErr)
{
  int 		tI0,
  		curLine,
  		howManyLn,
		bdrP1,
		numBufs,
		kol,
		kol1,
		lineSz, 		/* lineSz is the maximally sized line,
					   including borders */
		lineOffset, 		/* lineOffset is difference in line
					   numbers between that being
					   transformed and that needed in the
					   input buffer for use in the
					   transform */
		nextLine,
		inLine,
		needed,
		idx,
		itop,
		minLine;
  WlzErrorNum	errNum = WLZ_ERR_NONE;
  int 		*lineBufSpace = NULL, 	/* lineBufSpace is the current line
  					   buffer */
  		*firstColP,
  		*lastColP,
		*dstBuf,
		*lastLnP,
		*tIP0;
  WlzIntervalDomain *jdp;
  WlzObject 	*dstObj = NULL;
  WlzSeqParWSpace spWSpace;		/* Work space which is passed on to
  					   the transform function. */
  WlzIntervalWSpace srcIWsp,
  		dstIWsp;
  WlzGreyWSpace srcGWsp,
  		dstGWsp;
  int		*lineBufs[15],      	/* lineBufs is the array of line
  					   buffers needed to obtain
					   neighbouring points. */
		*adrbase[15];

  WLZ_DBG((WLZ_DBG_LVL_FN|WLZ_DBG_LVL_1),
  	  ("WlzSeqPar FE %p %d %d %d %d %d %p %p %p\n",
	   srcObj, newObjFlag, sequentialFlag, (int )rasterDir, bdrSz, bkgVal,
	   transformData, transformFn, dstErr));
  if(srcObj == NULL)
  {
    errNum = WLZ_ERR_OBJECT_NULL;
    if(dstErr)
    {
      *dstErr = errNum;
    }
    return(NULL);
  }
  if((srcObj->type != WLZ_2D_DOMAINOBJ) || (srcObj->values.core == NULL) ||
     (srcObj->domain.core == NULL))
  {
    errNum = WLZ_ERR_OBJECT_TYPE;
    if(dstErr)
    {
      *dstErr = errNum;
    }
    return(NULL);
  }
  /*
   * Make an array spWSpace.adrptr of pointers with range [-7,7]
   */
  spWSpace.adrptr = adrbase + 7;
  spWSpace.brdrsz = bdrSz;
  jdp = srcObj->domain.i;
  bdrP1 = -bdrSz - 1;
  /*
   * The buffer must contain both the current line and bdrSz lines each side.
   */
  numBufs = (bdrSz * 2) + 1;
  kol1 = kol = jdp->kol1;
  lineSz = jdp->lastkl - kol + numBufs;
  /*
   * Some pointers to first/last line and column positions
   * to accomodate different rasters
   */
  firstColP = &dstIWsp.lftpos;
  lastColP = &dstIWsp.rgtpos;
  lastLnP = &jdp->lastln;
  /*
   * spWSpace.kdelta is increment to adrs when processing points
   * if positive, procede left to right in lines
   * if negative procede right to left
   * spWSpace.ldelta is increment to line number
   * if positive, the raster is proceeding in increasing line numbers
   * if negative the raster is procceding with decreasing line numbers
   */
  spWSpace.kdelta = 1;
  spWSpace.ldelta = 1;
  /*
   * Alter parameters for right to left rasters
   */
  if(!sequentialFlag)
  {
    rasterDir = WLZ_RASTERDIR_ILIC;
  }
  if((rasterDir == WLZ_RASTERDIR_ILDC) || (rasterDir == WLZ_RASTERDIR_DLDC))
  {
    tIP0 = firstColP;
    firstColP = lastColP;
    lastColP = tIP0;
    spWSpace.kdelta = -1;
  }
  /*
   * Alter parameters for bottom to top rasters
   */
  if((rasterDir == WLZ_RASTERDIR_DLIC) || (rasterDir == WLZ_RASTERDIR_DLDC))
  {
    spWSpace.ldelta = -1;
    lastLnP = &jdp->line1;
  }
  lineOffset = bdrSz * spWSpace.ldelta;
  /* 
   * Allocate space to buffers 
   */
  lineBufSpace = (int *)AlcCalloc(lineSz * (numBufs + 1), sizeof(int));
  for(idx = 0; idx < numBufs; ++idx)
  {
    lineBufs[idx] = lineBufSpace + (idx + 1) * lineSz;
  }
  /*
   * Either use existing object or create a new grey table object
   * according to the newObjFlag.
   * If a new object is required then it is created with the SAME interval
   * list and property list and a DIFFERENT but IDENTICAL grey table.
   */
  if(newObjFlag)
  {
    dstObj = WlzNewGrey(srcObj, &errNum);
  }
  else
  {
    dstObj = srcObj;
  }
  /*
   * Use nxxiv in transplant mode, placing and taking grey values
   * from the line buffers
   * srcIWsp is the workspace for entering values into the line buffers
   * dstIWsp is the workspace for processing the intervals and replacing
   * the transformed values into the grey table
   */
  srcGWsp.gvio = 1;		  /* output (since we are specifying tranpl) */
  dstGWsp.gvio = 0;						    /* input */
  errNum = WlzInitGreyRasterScan(srcObj, &srcIWsp, &srcGWsp, rasterDir, 1);
  if(errNum == WLZ_ERR_NONE)
  {
    errNum = WlzInitGreyRasterScan(dstObj, &dstIWsp, &dstGWsp, rasterDir, 1);
  }
  if(errNum != WLZ_ERR_NONE)
  {
    if(dstErr)
    {
      *dstErr = errNum;
    }
    return(NULL);
  }
  /*
   * curLine is the line currently being processed
   * inLine is the last line entered into the line buffers
   * initialize it to an imaginary line just before the first border line
   */
  (void )WlzNextInterval(&srcIWsp);
  nextLine = srcIWsp.linpos;
  inLine = srcIWsp.linpos - (spWSpace.ldelta * (bdrSz + 1));
  /*
   * Process the next interval
   */
  while((errNum == WLZ_ERR_NONE) &&
        ((errNum = WlzNextGreyInterval(&dstIWsp)) == WLZ_ERR_NONE))
  {
    /*
     * When processing curLine, the line buffer must contain
     * lines upto needed=curLine+border width
     */
    curLine = dstIWsp.linpos;
    needed = curLine + lineOffset;
    /*
     * Next action depends which lines are aleady in the line buffers
     */
    howManyLn = (needed - inLine) * spWSpace.ldelta;
    WLZ_DBG((WLZ_DBG_LVL_3),
  	    ("WlzSeqPar 01 %d %d\n",
	     inLine, curLine));
    if(howManyLn < 0)
    {
      /*
       * This is an error it is impossible to have input a line before needed
       */
      errNum = WLZ_ERR_DOMAIN_DATA;
      if(dstErr)
      {
        *dstErr = errNum;
      }
      return(NULL);
    }
    else if(howManyLn > 0)
    {
      /*
       * If howManyLn > 0 we need to input some lines to input buffer
       * routine to fill the input buffers used to determine the value
       * of the transform needed = maximal(minimal)line needed to be filled
       * into the buffer
       * if backwards raster, needed = curLine - bdrSz
       * if forwards raster, needed = curLine + bdrSz
       * nextLine = line number of the next interval to be filed by nxxiv
       */
      while(1)
      {
	if((needed - nextLine) * spWSpace.ldelta < 0)
	{
	  /*
	   * Next line needed less far away than next line available
	   * from nxxint fill required buffer lines with bkgVal and return
	   * minLine = WlzSeqParPosMod(inLine, numBufs), i.e. the remainder
	   * of inLine when divided by numBufs.
	   * It thus can be used as the offset into the array of
	   * logical line addresses to determine which line in the
	   * circular buffer inLine is being stored
	   */
	  while(inLine != needed)
	  {
	    inLine += spWSpace.ldelta;
	    minLine = WlzSeqParPosMod(inLine, numBufs);
	    WlzValueSetInt(lineBufs[minLine], bkgVal, lineSz);
	  }
	  break;
	}
	/*
	 * Line(s) needed include some real lines, i.e. obtained from nxxiv
	 * first fill buffer with bkgVal
	 */
	while(inLine != nextLine)
	{
	  inLine += spWSpace.ldelta;
	  minLine = WlzSeqParPosMod(inLine, numBufs);
	  WlzValueSetInt(lineBufs[minLine], bkgVal, lineSz);
	}
	/*
	 * nxxint interval to be put into buffer calculate adrs in
	 * buffer by adding leftcol to logical adrs stored in
	 * lineBufs[minLine]. then insert with nxxiv
	 */
	do
	{
	  srcGWsp.u_grintptr.inp = lineBufs[minLine] +
	  			   srcIWsp.lftpos + bdrSz - kol1;
	  WlzGreyInterval(&srcIWsp);
	  /*
	   * Call nxxint to get next interval if in same line store in buffer
	   */
	  if(WlzNextInterval(&srcIWsp) != 0)
	  {
	    /*
	     * When nxxint has finished all interval set a high value into
	     * nextLine, so all future lines input will be dummy lines
	     */
	    nextLine = (*lastLnP) - (bdrP1 * spWSpace.ldelta);
	    goto filledLABEL;
	  }
	} while(srcIWsp.nwlpos == 0);
	/*
	 * If next interval in new line,store new line number and
	 * jump to start of routine to see what sort of line buffer
	 * processing is required next
	 */
	nextLine = srcIWsp.linpos;
      }
    }
    /*
     * Line buffers filled for processing of this line either from above call
     * or buffer filled for previous intervals processed on this line
     * kol is the first point in the interval to be processed
     * itop is the last point to be processed
     */
filledLABEL: 					    /* LABEL! see goto above */
    kol = *firstColP;
    itop = *lastColP;
    /*
     * First load spWSpace.adrptr with addresses of required neighbors
     */
    idx = bdrP1;
    while(idx++ < bdrSz)
    {
      tI0 = idx * spWSpace.ldelta;
      spWSpace.adrptr[idx] = lineBufs[WlzSeqParPosMod(curLine + tI0,
      						      numBufs)] +
			     kol + bdrSz - kol1;
    }
    /*
     * Then set the pointer where the transformed value will be placed
     * if parallel mode, adrs is in current line buffer
     * if sequential mode, adrs is in input buffer
     * dstGWsp.grintptr is the location in the nxxiv workspace
     * giving the location from which nxxiv will transplant
     * values into the grey table
     */
    if(sequentialFlag)
    {
      dstBuf = spWSpace.adrptr[0];
    }
    else
    {
      dstBuf = lineBufSpace + bdrSz;
    }
    dstGWsp.u_grintptr.inp = dstBuf + dstIWsp.lftpos - kol;
    /*
     * Procede thru interval,calling the transforming function
     * at each point and storing the result where required
     */
    kol -= spWSpace.kdelta;
    while(kol != itop)
    {
      kol += spWSpace.kdelta;
      *dstBuf = (*transformFn)(&spWSpace, transformData);
      /*
       * after each point, update the neighbor adrs and the output adrs
       */
      idx = bdrP1;
      while(idx++ < bdrSz)
      {
	spWSpace.adrptr[idx] += spWSpace.kdelta;
      }
      dstBuf += spWSpace.kdelta;
    }
    /*
     * Having finished this interval proceed to the next
     */
  }
  if(errNum == WLZ_ERR_EOO)
  {
    errNum = WLZ_ERR_NONE;
  }
  if(lineBufSpace)
  {
    AlcFree((void *)lineBufSpace);
  }
  if(dstErr)
  {
    *dstErr = errNum;
  }
  WLZ_DBG((WLZ_DBG_LVL_FN|WLZ_DBG_LVL_1),
  	  ("WlzSeqPar FX %p\n",
	   dstObj));
  return(dstObj);
}
Exemplo n.º 2
0
/*!
* \return	New Woolz domain object with maximal domain and grey
*		values which encode the gradient's direction or NULL
*		on error.
* \ingroup	WlzFeatures
* \brief	Computes the maximal domain and gradient direction of
*               given Woolz 2D domain object.
* \note         All the objects domains are known to be the same.
* \param	grdM			Gradient magnitude.
* \param	grdY			Gradient (partial derivative)
*                                       through lines.
* \param	grdX			Gradient (partial derivative)
*                                       through columns.
* \param	minThrV			Minimum gradient value to
*                                       consider.
* \param	dstErr			Destination error pointer, may
*                                       be null.
*/
static WlzObject *WlzNMSuppress2D(WlzObject *grdM,
				  WlzObject *grdY, WlzObject *grdX,
				  WlzPixelV minThrV, WlzErrorNum *dstErr)
{
  int		idN,
		inLen,
		outLen,
  		inLnIdx = 0;
  WlzGreyType	gType,
  		bufType;
  WlzIVertex2	bufSz,
  		inPos,
		outPos,
		orgPos;
  WlzValues	tmpVal;
  WlzDomain	dstDom,
  		grdDom;
  WlzIntervalWSpace tmpIWSp = {0},
  		grdMIWSp = {0},
  		grdYIWSp = {0},
		grdXIWSp = {0};
  WlzGreyWSpace tmpGWSp,
  		grdMGWSp,
  		grdYGWSp,
		grdXGWSp;
  WlzPixelV	zeroV;
  WlzGreyP	grdMBufGP,
  		grdYBufGP,
		grdXBufGP;
  WlzDynItvPool pool;
  WlzObject	*dstObj = NULL,
  		*tmpObj = NULL;
  void 		*grdYBuf = NULL,
		*grdXBuf = NULL;
  void		**grdMBuf = NULL;
  WlzErrorNum	errNum = WLZ_ERR_NONE;

  tmpVal.core = NULL;
  pool.itvBlock = NULL;
  dstDom.core = NULL;
  if((grdM->type != WLZ_2D_DOMAINOBJ) ||
     (grdY->type != WLZ_2D_DOMAINOBJ) ||
     (grdX->type != WLZ_2D_DOMAINOBJ))
  {
    errNum = WLZ_ERR_OBJECT_NULL;
  }
  else if((grdM->domain.core == NULL) ||
          (grdY->domain.core == NULL) ||
          (grdX->domain.core == NULL))
  {
    errNum = WLZ_ERR_DOMAIN_NULL;
  }
  else if((grdM->values.core == NULL) ||
  	  (grdY->values.core == NULL) ||
	  (grdX->values.core == NULL))
  {
    errNum = WLZ_ERR_VALUES_NULL;
  }
  else
  {
    /* Find required buffer type (WLZ_GREY_DOUBLE or WLZ_GREY_INT). */
    bufType = WLZ_GREY_INT;
    gType = WlzGreyTableTypeToGreyType(grdM->values.core->type, &errNum);
    if(errNum == WLZ_ERR_NONE)
    {
      if((gType == WLZ_GREY_FLOAT) || (gType == WLZ_GREY_DOUBLE))
      {
	bufType = WLZ_GREY_DOUBLE;
      }
      else
      {
	gType = WlzGreyTableTypeToGreyType(grdY->values.core->type, &errNum);
	if(errNum == WLZ_ERR_NONE)
	{
	  if((gType == WLZ_GREY_FLOAT) || (gType == WLZ_GREY_DOUBLE))
	  {
	    bufType = WLZ_GREY_DOUBLE;
	  }
	  else
	  {
	    gType = WlzGreyTableTypeToGreyType(grdX->values.core->type,
	    				       &errNum);
	    if(errNum == WLZ_ERR_NONE)
	    {
	      if((gType == WLZ_GREY_FLOAT) || (gType == WLZ_GREY_DOUBLE))
	      {
		bufType = WLZ_GREY_DOUBLE;
	      }
	    }
	  }
	}
      }
    }
  }
  /* Convert minimum gradient threshold value. */
  if(errNum == WLZ_ERR_NONE)
  {
    if(bufType == WLZ_GREY_INT)
    {
      errNum = WlzValueConvertPixel(&minThrV, minThrV, WLZ_GREY_INT);
    }
    else /* bufType == WLZ_GREY_DOUBLE */
    {
      errNum = WlzValueConvertPixel(&minThrV, minThrV, WLZ_GREY_DOUBLE);
    }
  }
  if(errNum == WLZ_ERR_NONE)
  {
    grdDom = grdM->domain;
    /* Make destination object with WLZ_GREY_UBYTE greys. */
    zeroV.type = WLZ_GREY_UBYTE;
    zeroV.v.inv = 0;
    tmpVal.v = WlzNewValueTb(grdM, WlzGreyTableType(WLZ_GREY_TAB_RAGR,
						    WLZ_GREY_UBYTE, NULL),
			     zeroV, &errNum);
    if(errNum == WLZ_ERR_NONE)
    {
      /* Use the input domain while calculating the new maximal domain. */
      tmpObj = WlzMakeMain(WLZ_2D_DOMAINOBJ, grdM->domain, tmpVal,
			   NULL, NULL, &errNum);
    }
  }
  /* Initialize the memory pool with some size of block. Any +ve number
   * greater than the maximum number of intervals in any destination line
   * would work but the fewer allocations then the more efficient the code,
   * hence this attempt to guess the required number of intervals in the
   * destination domain. */
  if(errNum == WLZ_ERR_NONE)
  {
    pool.itvsInBlock = (((grdDom.i->lastkl - grdDom.i->kol1 + 1) *
			(grdDom.i->lastln - grdDom.i->line1 + 1)) / 64) +
		       grdDom.i->lastkl - grdDom.i->kol1 + 1024;
  }
  /* Make gradient buffers. */
  if(errNum == WLZ_ERR_NONE)
  {
    bufSz.vtY = 3;
    bufSz.vtX = grdDom.i->lastkl - grdDom.i->kol1 + 1;
    if(bufType == WLZ_GREY_INT)
    {
      if((AlcInt2Malloc((int ***)&grdMBuf,
			bufSz.vtY, bufSz.vtX) != ALC_ER_NONE) ||
	 ((grdYBuf = AlcMalloc(sizeof(int) * bufSz.vtX)) == NULL) ||
	 ((grdXBuf = AlcMalloc(sizeof(int) * bufSz.vtX)) == NULL))
      {
	errNum = WLZ_ERR_MEM_ALLOC;
      }
      else
      {
	grdYBufGP.inp = (int *)grdYBuf;
	grdXBufGP.inp = (int *)grdXBuf;
      }
    }
    else /* bufType == WLZ_GREY_DOUBLE */
    {
      if((AlcDouble2Malloc((double ***)&grdMBuf,
			   bufSz.vtY, bufSz.vtX) != ALC_ER_NONE) ||
	 ((grdYBuf = AlcMalloc(sizeof(double) * bufSz.vtX)) == NULL) ||
	 ((grdXBuf = AlcMalloc(sizeof(double) * bufSz.vtX)) == NULL))
      {
	errNum = WLZ_ERR_MEM_ALLOC;
      }
      else
      {
	grdYBufGP.dbp = (double *)grdYBuf;
	grdXBufGP.dbp = (double *)grdXBuf;
      }
    }
  }
  /* Make destination interval domain with interval lines but not intervals. */
  if(errNum == WLZ_ERR_NONE)
  {
    dstDom.i = WlzMakeIntervalDomain(WLZ_INTERVALDOMAIN_INTVL,
    				     grdDom.i->line1, grdDom.i->lastln,
				     grdDom.i->kol1, grdDom.i->lastkl,
				     &errNum);
  }
  if(errNum == WLZ_ERR_NONE)
  {
    /* Scan down through the gradient objects. */
    if(((errNum = WlzInitGreyScan(tmpObj,
    				  &tmpIWSp, &tmpGWSp)) == WLZ_ERR_NONE) &&
       ((errNum = WlzInitGreyScan(grdM,
    				  &grdMIWSp, &grdMGWSp)) == WLZ_ERR_NONE) &&
       ((errNum = WlzInitGreyScan(grdY,
    				  &grdYIWSp, &grdYGWSp)) == WLZ_ERR_NONE) &&
       ((errNum = WlzInitGreyScan(grdX,
    				  &grdXIWSp, &grdXGWSp)) == WLZ_ERR_NONE))
    {
      orgPos.vtX = grdDom.i->kol1;
      orgPos.vtY = grdDom.i->line1;
      while((errNum == WLZ_ERR_NONE) &&
            ((errNum = WlzNextGreyInterval(&grdMIWSp)) == WLZ_ERR_NONE))
      {
        inLen = grdMIWSp.rgtpos - grdMIWSp.lftpos + 1;
	inPos.vtX = grdMIWSp.lftpos - orgPos.vtX;
	/* Process any lines between this and the last by clearing the
	 * gradient magnitude buffer . */
	if(grdMIWSp.nwlpos > 0)
	{
	  idN = (grdMIWSp.nwlpos >= 3)? 3: grdMIWSp.nwlpos;
	  while(--idN >= 0)
	  {
	    inPos.vtY = grdMIWSp.linpos - orgPos.vtY - idN;
	    inLnIdx = (3 + inPos.vtY) % 3;
	    if(bufType == WLZ_GREY_INT)
	    {
	      WlzValueSetInt(*((int **)grdMBuf + inLnIdx), 0,
	      		     bufSz.vtX);
	    }
	    else /* bufType == WLZ_GREY_DOUBLE */
	    {
	      WlzValueSetDouble(*((double **)grdMBuf + inLnIdx), 0,
	      			bufSz.vtX);
	    }
	  }
	}
	/* Copy intervals to values buffers. */
	if(bufType == WLZ_GREY_INT)
	{
	  grdMBufGP.inp = *((int **)grdMBuf + inLnIdx);
	}
	else /* bufType == WLZ_GREY_DOUBLE */
	{
	  grdMBufGP.dbp = *((double **)grdMBuf + inLnIdx);
	}
	WlzValueCopyGreyToGrey(grdMBufGP, inPos.vtX, bufType,
			       grdMGWSp.u_grintptr, 0, grdMGWSp.pixeltype,
			       inLen);
	if(grdMIWSp.intrmn == 0)
	{
	  while((errNum == WLZ_ERR_NONE) &&
	        (tmpIWSp.linpos < grdMIWSp.linpos))
	  {
	    outPos.vtY = tmpIWSp.linpos - orgPos.vtY;
	    if(outPos.vtY >= 0)
	    {
	      outLen = tmpIWSp.rgtpos - tmpIWSp.lftpos + 1;
	      outPos.vtX = tmpIWSp.lftpos - orgPos.vtX;
	      WlzValueCopyGreyToGrey(grdYBufGP, 0, bufType,
				     grdYGWSp.u_grintptr, 0,
				     grdYGWSp.pixeltype, outLen);
	      WlzValueCopyGreyToGrey(grdXBufGP, 0, bufType,
				     grdXGWSp.u_grintptr, 0,
				     grdXGWSp.pixeltype, outLen);
	      if(bufType == WLZ_GREY_INT)
	      {
		errNum = WlzNMSuppress2DBufI(dstDom.i,
					     (int **)grdMBuf,
					     (int *)grdYBuf,
					     (int *)grdXBuf,
					     &pool,
					     tmpGWSp.u_grintptr.ubp,
					     outLen, outPos, orgPos,
					     minThrV.v.inv);
	      }
	      else /* bufType == WLZ_GREY_DOUBLE */
	      {
		errNum = WlzNMSuppress2DBufD(dstDom.i,
				    	     (double **)grdMBuf,
				    	     (double *)grdYBuf,
					     (double *)grdXBuf,
					     &pool,
					     tmpGWSp.u_grintptr.ubp,
					     outLen, outPos, orgPos,
					     minThrV.v.dbv);
	      }
	    }
	    if(errNum == WLZ_ERR_NONE)
	    {
	      errNum = WlzNextGreyInterval(&tmpIWSp);
	    }
	    if(errNum == WLZ_ERR_NONE)
	    {
	      errNum = WlzNextGreyInterval(&grdYIWSp);
	    }
	    if(errNum == WLZ_ERR_NONE)
	    {
	      errNum = WlzNextGreyInterval(&grdXIWSp);
	    }
	  }
        }
      }
      if(errNum == WLZ_ERR_EOO)
      {
        errNum = WLZ_ERR_NONE;
      }
    }
    if(tmpIWSp.gryptr == &tmpGWSp)
    {
      (void )WlzEndGreyScan(&tmpIWSp, &tmpGWSp);
    }
    if(grdMIWSp.gryptr == &grdMGWSp)
    {
      (void )WlzEndGreyScan(&grdMIWSp, &grdMGWSp);
    }
    if(grdYIWSp.gryptr == &grdYGWSp)
    {
      (void )WlzEndGreyScan(&grdYIWSp, &grdYGWSp);
    }
    if(grdXIWSp.gryptr == &grdXGWSp)
    {
      (void )WlzEndGreyScan(&grdXIWSp, &grdXGWSp);
    }
  }
  if(errNum == WLZ_ERR_NONE)
  {
    if((errNum = WlzStandardIntervalDomain(dstDom.i)) == WLZ_ERR_NONE)
    {
      dstObj = WlzMakeMain(WLZ_2D_DOMAINOBJ, dstDom, tmpVal,
			   NULL, NULL, &errNum);
    }
  }
  if(tmpObj)
  {
    WlzFreeObj(tmpObj);
  }
  if(errNum != WLZ_ERR_NONE)
  {
    if(tmpObj == NULL)
    {
      if(dstDom.core)
      {
        (void )WlzFreeDomain(dstDom);
      }
      if(tmpVal.core)
      {
        (void )WlzFreeValues(tmpVal);
      }
    }
  }
  if(grdMBuf)
  {
    Alc2Free(grdMBuf);
  }
  if(grdYBuf)
  {
    AlcFree(grdYBuf);
  }
  if(grdXBuf)
  {
    AlcFree(grdXBuf);
  }
  if(dstErr)
  {
    *dstErr = errNum;
  }
  return(dstObj);
}