Exemple #1
0
/*!
* \return	Returns a preprocessed object for registration.
* \ingroup	WlzRegistration
* \brief
* \param	obj			Given object.
* \param	inv			Flag, non zero if object values are
*					to be inverted.
* \param	dstErr			Destination error pointer,
*                                       may be NULL.
*/
static WlzObject *WlzRegCCorNormaliseObj2D(WlzObject *obj, int inv,
					WlzErrorNum *dstErr)
{
  WlzGreyType	gType;
  WlzPixelV	min,
  		max,
		mean,
		minN,
		maxN,
		zero;
  WlzObject	*obj0 = NULL;
  WlzErrorNum   errNum = WLZ_ERR_NONE;

  /* Normalise the object inverting the grey values if required -> obj0. */
  minN.v.ubv = 0;
  maxN.v.ubv = 255;
  zero.v.ubv = 0;
  min.type = WLZ_GREY_DOUBLE;
  max.type = WLZ_GREY_DOUBLE;
  mean.type = WLZ_GREY_UBYTE;
  minN.type = WLZ_GREY_UBYTE;
  maxN.type = WLZ_GREY_UBYTE;
  zero.type = WLZ_GREY_UBYTE;
  mean.v.ubv = 128;
  if(inv)
  {
    minN.v.ubv = 255;
    maxN.v.ubv = 0;
  }
  (void )WlzGreyStats(obj, &gType, &(min.v.dbv), &(max.v.dbv),
                      NULL, NULL, NULL, NULL, &errNum);
  if(errNum == WLZ_ERR_NONE)
  {
    WlzValueConvertPixel(&min, min, gType);
    WlzValueConvertPixel(&max, max, gType);
    WlzValueConvertPixel(&minN, minN, gType);
    WlzValueConvertPixel(&maxN, maxN, gType);
    obj0 = WlzCopyObject(obj, &errNum);
  }
  if(errNum == WLZ_ERR_NONE)
  {
    errNum = WlzSetBackground(obj0, zero);
    errNum = WlzGreySetRange(obj0, min, max, minN, maxN, 0);
  }
  if(errNum != WLZ_ERR_NONE)
  {
    (void )WlzFreeObj(obj0); obj0 = NULL;
  }
  if(dstErr)
  {
    *dstErr = errNum;
  }
  return(obj0);
}
/*!
* - Function:   WlzEffWriteMeshTransform3DWithoutDisplacementVTK
* - Returns:    none 
* - Purpose:    output the orginal mesh.	
* - Parameters:	
*     -# *fp:               pointer pointing to a specific file.
*     -#  wmt3D:            mesh transform.
* - Author:       J. Rao, R. Baldock and B. Hill
*/
static void  OutPutSectionsForTest(
                  WlzObject   *WObjS, 
		  WlzObject   *WObj2D, 
		  /* FILE        *outFile;  */
		  char        *outFileStr, 
		  int          iloop,
		  WlzErrorNum *errNum){

  WlzGreyValueWSpace *gVWSp;
  WlzErrorNum       wErrN = WLZ_ERR_NONE;

  WlzGMModel         *gM;
  WlzGMShell         *cS, *fS;
  WlzGMLoopT         *cLT, *fLT;
  WlzGMEdgeT         *cET, *fET;
  WlzDVertex2        *v1,  *v2;
  WlzIBox2            bBoxT;
  WlzObject          *tObj;
  int ix, jy, kt, intensity;

 if( ( (WObjS->type) != WLZ_CONTOUR ) )
  {
      	wErrN = WLZ_ERR_DOMAIN_TYPE;
  }


  if( ( WObj2D->type != WLZ_2D_DOMAINOBJ )  )
  {
      	wErrN = WLZ_ERR_DOMAIN_TYPE;
  }

  if(  ( WObj2D->domain.core == NULL ) )
  {
	wErrN = WLZ_ERR_DOMAIN_NULL;
  }
  
  if(wErrN == WLZ_ERR_NONE)
  {
       /* copy it */
       tObj = WlzCopyObject(WObj2D, &wErrN);
       if( wErrN  != WLZ_ERR_NONE)
       {
          printf("Something wrong with when copy obj ");
          exit(1);
       }
   

       /* get the bounding box */
       bBoxT = WlzBoundingBox2I(tObj, &wErrN); 
       if( wErrN  != WLZ_ERR_NONE)
       {
          printf("Something wrong with getting bounding box of obj ");
          exit(1);
       }
       
       /* revise the grey value of it */   
       gVWSp        = WlzGreyValueMakeWSp(tObj, &wErrN);
       if( wErrN  != WLZ_ERR_NONE)
       {
          printf("Something wrong with when make warped obj Wsp");
          exit(1);
       }
       
       for(ix = bBoxT.xMin; ix <bBoxT.xMax; ix++)
       {
          kt = 0;
          for(jy= bBoxT.yMin; jy< bBoxT.yMax; jy++)
          {
	      /*  GetGreyValue(gVWSp, 0, jy, ix, &intensity, &errNum); */
	      intensity = 255;
	      FillGreyValue(gVWSp, 0, jy, ix, intensity, &wErrN);
	      
          } 
       }

   }

  /*  visualize the data: */
  if( wErrN == WLZ_ERR_NONE)
  {
    iloop = 1;

    kt = 0;
  
    gM = WObjS->domain.ctr->model;
    /* For each shell of the model. */
    cS = fS = (WlzGMShell *) gM->child;
    do
    {
       printf("Shell  %d   Shell index %d\n", kt, cS->idx);
      /* For each loop topology element of the model. */
       cLT = fLT = (WlzGMLoopT *) cS->child;
       kt++;
       do
       {
           printf("Loop index %d\n", cLT->idx);
           /* For each edge topology element of the model. */
	   cET = fET = cLT->edgeT;
	   do
	   {
              if(cET == cET->edge->edgeT) /* Print edge end points */
	      {
	         v1 = (WlzDVertex2 *) cET->vertexT->diskT->vertex->geo.vg2D;
	         v2 = (WlzDVertex2 *) cET->opp->vertexT->diskT->vertex->geo.vg2D;
		 /*
		 printf("%lg    %lg\n",cET->vertexT->diskT->vertex->geo.vg2D->vtx.vtX, \
		                       cET->vertexT->diskT->vertex->geo.vg2D->vtx.vtY );
		 printf("%lg    %lg\n",cET->opp->vertexT->diskT->vertex->geo.vg2D->vtx.vtX, \
		                       cET->opp->vertexT->diskT->vertex->geo.vg2D->vtx.vtY );
				       */
		/* if(kt%3 == iloop) */
		{
		 ix = (int) cET->vertexT->diskT->vertex->geo.vg2D->vtx.vtX;
		 jy = (int) cET->vertexT->diskT->vertex->geo.vg2D->vtx.vtY;
		 printf("idx %d ix  %d  iy  %d\n", cET->vertexT->diskT->vertex->idx, ix, jy);
	 	 intensity = kt * 40;
	         FillGreyValue(gVWSp, 0, jy, ix, intensity, &wErrN);
		 ix = (int) cET->opp->vertexT->diskT->vertex->geo.vg2D->vtx.vtX;
		 jy = (int) cET->opp->vertexT->diskT->vertex->geo.vg2D->vtx.vtY;
                 printf("idx %d ix  %d  iy  %d\n", cET->opp->vertexT->diskT->vertex->idx, ix, jy);
                 FillGreyValue(gVWSp, 0, jy, ix, intensity, &wErrN);
		} 
              }
	      cET = cET->next;
	   } while (cET != fET);
           cLT = cLT->next;


       } while(cLT != fLT);
       cS = cS->next;
   }  while(cS != fS );
  }

 
    if((outFile = fopen(outFileStr, "w")) == NULL )
    {
      printf("cannot open the output woolz file.\n");
      exit(1);
    }

    if( ( wErrN = WlzWriteObj(outFile, tObj) ) != WLZ_ERR_NONE )
    {
       printf("output Woolz Object Error.\n");
       fclose(outFile); 
       exit(1);
    }
    
    fclose(outFile); 
    outFile = NULL;

    WlzGreyValueFreeWSp(gVWSp);


}
/*!
* \return	Object containing the point (x,y).
* \ingroup	WlzBinaryOps
* \brief	Takes a WLZ_2D_DOMAINOBJ, calls WlzLabel to split the domain
* 		and returns the one containing point(x,y).
* \param	obj		Given WLZ_2D_DOMAINOBJ object.
* \param	x		Column coordinate.
* \param	y		Line coordinate.
* \param	dstErr		Destination error code pointer, may be NULL.
*/
WlzObject *Wlz2DContains(WlzObject *obj,
                          double x,
                          double y,
                          WlzErrorNum *dstErr) {


   WlzObject    *retObj = NULL;
   WlzObject	**objArray;

   int          i, nobjs;
   /* int          maxNobjs=1024; */
   int          maxNobjs=2048;
   int 		found = 0;

   WlzErrorNum  errNum=WLZ_ERR_NONE;
   WlzConnectType connectivity = WLZ_8_CONNECTED;

   /*
   fprintf(stderr, "entering Wlz2DContains %f,%f\n", x,y);
   fflush(stderr);
   */

   if(obj->type != WLZ_2D_DOMAINOBJ) return (NULL);

   /* get array of domains */
   if(obj != NULL) {
      errNum = WlzLabel(obj,
                        &nobjs,
                        &objArray,
                        maxNobjs,
                        0,
                        connectivity);
   }
   /*
   fprintf(stderr, "got array of %d objects\n", nobjs);
   fflush(stderr);
   */

   /* select the required domain */
   /* ie the one which contains clicked point */

   if((errNum == WLZ_ERR_NONE) && (nobjs > 0)) {
      for(i=0; i<nobjs; i++) {
	 /*
	 fprintf(stderr, "checking domain # %d\n", i);
	 fflush(stderr);
	 */
	 if(WlzInsideDomain(objArray[i],
			    0.0,
			    y,
			    x,
			    &errNum)) {
	    if(errNum == WLZ_ERR_NONE) {
	       found = 1;
	       /*
	       fprintf(stderr, "domain # %d contains point\n", i);
	       fflush(stderr);
	       */
	       break;
	    } else {
	       (void )fprintf(stderr,
	                      "WlzInsideDomain, Wlz error: %d\n",
			      (int )errNum);
	       fflush(stderr);
	    }
	 } else {
	    WlzFreeObj(objArray[i]);
	 }
      }
      if((found == 1) && (errNum == WLZ_ERR_NONE)) {
	 /* retObj = objArray[i]; */
	 retObj = WlzCopyObject(objArray[i], &errNum);
	 WlzFreeObj(objArray[i]);
	 if(errNum != WLZ_ERR_NONE) {
	    (void )fprintf(stderr,
	                   "WlzCopyObject, Wlz error: %d\n",
			   (int )errNum);
	    fflush(stderr);
	 }
      }
   }

   *dstErr = errNum;
   /*
   fprintf(stderr, "leaving Wlz2DContains\n");
   fflush(stderr);
   */
   return retObj;
}
Exemple #4
0
/*!
* \return	New object or NULL on error.
* \ingroup	WlzValuesUtils
* \brief 	Transfers grey values from the source object to the
*               destination object within the intersection of the source
*               and destination. Grey values within the destination
*               object outside of the source object are unchanged.
*               It is an error if either object has a different dimension
*               or grey value type, except for when either is an empty
*               object.
* \param	dObj			Destination object which may be
* 					empty, but otherwise should be of the
* 					same dimension as the source object
* 					with valid values..
* \param	sObj			Source object which if not empty must
* 					have both a valid domain and valid
* 					values.
* \param	inplace			Overwrite the destination object's
* 					values if non zero.
* \param	dstErr			Destination error pointer, may be NULL.
*/
WlzObject			*WlzGreyTransfer(
				  WlzObject *dObj,
				  WlzObject *sObj,
				  int inplace,
				  WlzErrorNum *dstErr)
{
  WlzObject	*rObj = NULL;
  WlzErrorNum	errNum = WLZ_ERR_NONE;

  if((dObj == NULL) || (sObj == NULL))
  {
    errNum = WLZ_ERR_OBJECT_NULL;
  }
  else if(WlzIsEmpty(dObj, NULL))
  {
    rObj = WlzMakeEmpty(&errNum);
  }
  else if(WlzIsEmpty(sObj, NULL))
  {
    rObj = WlzMakeMain(dObj->type, dObj->domain, dObj->values,
                       dObj->plist, NULL, &errNum);
  }
  else if(dObj->type != sObj->type)
  {
    errNum = WLZ_ERR_OBJECT_TYPE;
  }
  else if((dObj->domain.core == NULL) || (sObj->domain.core == NULL))
  {
    errNum = WLZ_ERR_DOMAIN_NULL;
  }
  else if(sObj->values.core == NULL)
  {
    errNum = WLZ_ERR_VALUES_NULL;
  }
  else
  {
    switch(sObj->type)
    {
      case WLZ_2D_DOMAINOBJ:
      case WLZ_3D_DOMAINOBJ: /* FALLTHROUGH */
        {
	  WlzObject	*rIObj = NULL;

	  rIObj = WlzIntersect2(dObj, sObj, &errNum);
	  if((errNum == WLZ_ERR_NONE) && (WlzIsEmpty(rIObj, NULL) == 0))
	  {
	    rObj = (inplace)?
		   WlzMakeMain(dObj->type, dObj->domain, dObj->values,
			       dObj->plist, NULL, &errNum):
		   WlzCopyObject(dObj, &errNum);
	    if(errNum == WLZ_ERR_NONE)
	    {
	      /* If the destination object does not have values then
	       * create them to match the domain of the destination
	       * object. */
	      if((sObj->values.core != NULL) && (rObj->values.core == NULL))
	      {
		WlzPixelV bgdV;
		WlzGreyType gType;
		WlzObjectType gTT;
		WlzValues	newVal;

		newVal.core = NULL;
		bgdV = WlzGetBackground(sObj, &errNum);
		if(errNum == WLZ_ERR_NONE)
		{
		  gType = WlzGreyTypeFromObj(sObj, &errNum);
		}
		if(errNum == WLZ_ERR_NONE)
		{
		  gTT = WlzGreyTableType(WLZ_GREY_TAB_RAGR, gType, NULL);
		  if(rObj->type == WLZ_2D_DOMAINOBJ)
		  {
		    newVal.v = WlzNewValueTb(rObj, gTT, bgdV, &errNum);
		  }
		  else /* rObj->type == WLZ_3D_DOMAINOBJ */
		  {
		    newVal.vox = WlzNewValuesVox(rObj, gTT, bgdV, &errNum);
		  }
		}
		if(errNum == WLZ_ERR_NONE)
		{
		  rObj->values = WlzAssignValues(newVal, NULL);
		}
		if(errNum == WLZ_ERR_NONE)
		{
		  errNum = WlzGreySetValue(rObj, bgdV);
		}
	      }
	    }
	    if(errNum == WLZ_ERR_NONE)
	    {
	      if(sObj->type == WLZ_2D_DOMAINOBJ)
	      {
		WlzObject *sIObj;

		rIObj->values = WlzAssignValues(rObj->values, NULL);
		sIObj = WlzMakeMain(WLZ_2D_DOMAINOBJ,
				    rIObj->domain, sObj->values,
				    NULL, NULL, &errNum);
		if(errNum == WLZ_ERR_NONE)
		{
		  errNum = WlzGreyTransfer2D(rIObj, sIObj);
		}
		(void )WlzFreeObj(sIObj);
	      }
	      else /* sObj->type == WLZ_3D_DOMAINOBJ */
	      {
		int	p,
			  rTiled,
			  sTiled,
			  nPlanes;

		rTiled = WlzGreyTableIsTiled(rObj->values.core->type);
		sTiled = WlzGreyTableIsTiled(sObj->values.core->type);
		nPlanes = rIObj->domain.p->lastpl - rIObj->domain.p->plane1 + 1;
#ifdef _OPENMP
#pragma omp parallel for
#endif
		for(p = 0; p < nPlanes; ++p)
		{
		  if(errNum == WLZ_ERR_NONE)
		  {
		    int	     pln;
		    WlzDomain  dom;
		    WlzValues  val;
		    WlzObject  *rIObj2D = NULL,
			       *sIObj2D = NULL;
		    WlzErrorNum errNum2D = WLZ_ERR_NONE;

		    pln = p + rIObj->domain.p->plane1;
		    dom = rIObj->domain.p->domains[p];
		    val = (rTiled)?
			  rObj->values:
			  rObj->values.vox->values[pln -
						   rObj->values.vox->plane1];
		    rIObj2D = WlzMakeMain(WLZ_2D_DOMAINOBJ,
					  dom, val, NULL, NULL, &errNum2D);
		    if(errNum2D == WLZ_ERR_NONE)
		    {
		      val = (sTiled)?
			    sObj->values:
			    sObj->values.vox->values[pln -
						     sObj->values.vox->plane1];
		      sIObj2D = WlzMakeMain(WLZ_2D_DOMAINOBJ,
					    dom, val, NULL, NULL, &errNum2D);
		    }
		    if(errNum2D == WLZ_ERR_NONE)
		    {
		      errNum2D = WlzGreyTransfer2D(rIObj2D, sIObj2D);
		    }
		    (void )WlzFreeObj(rIObj2D);
		    (void )WlzFreeObj(sIObj2D);
#ifdef _OPENMP
#pragma omp critical
		    {
#endif
		      if((errNum == WLZ_ERR_NONE) && (errNum2D != WLZ_ERR_NONE))
		      {
			errNum = errNum2D;
		      }
#ifdef _OPENMP
		    }
#endif
		  }
		}
	      }
	    }
	  }
	  (void )WlzFreeObj(rIObj);
	}
	break;
      default:
	errNum = WLZ_ERR_OBJECT_TYPE;
        break;
    }
  }
  if(errNum != WLZ_ERR_NONE)
  {
    WlzFreeObj(rObj);
    rObj = NULL;
  }
  if(dstErr)
  {
    *dstErr = errNum;
  }
  return(rObj);
}
Exemple #5
0
/*!
* \return	New Woolz object.
* \ingroup	WlzAllocation
* \brief	Creates a new 2D spatial domain object by adding a
* 		rectangular buffer of values to the given current
* 		object (which may be NULL or empty).
* \param	cObj			Given current object.
* \param	og			Origin of rectangular buffer.
* \param	sz			Buffer size.
* \param	gType			Grey type which must be consistent
* 					with the current object and the
* 					buffer of values.
* \param	bufSz			Number of values in the buffer.
* \param	bufP			Given buffer of values.
* \param	dstErr			Destination error pointer, may be NULL.
*/
static WlzObject *WlzBuildObj2(WlzObject *cObj,
                               WlzIVertex2 og, WlzIVertex2 sz,
                               WlzGreyType gType, int bufSz, WlzGreyP bufP,
                               WlzErrorNum *dstErr)
{
    WlzDomain	bDom;
    WlzValues	bVal,
                nVal;
    WlzObject	*bObj = NULL,
                 *nObj = NULL;
    WlzPixelV	bgdV;
    WlzErrorNum	errNum = WLZ_ERR_NONE;

    bDom.core = NULL;
    bVal.core = NULL;
    nVal.core = NULL;
    bgdV.type = WLZ_GREY_INT;
    bgdV.v.inv = 0;
    if(cObj)
    {
        WlzGreyType cGType = WLZ_GREY_ERROR;;

        switch(cObj->type)
        {
        case WLZ_EMPTY_OBJ:
            cObj = NULL;
            break;
        case WLZ_2D_DOMAINOBJ:
            if(cObj->domain.core == NULL)
            {
                errNum = WLZ_ERR_DOMAIN_NULL;
            }
            else if(cObj->values.core == NULL)
            {
                errNum = WLZ_ERR_VALUES_NULL;
            }
            else
            {
                cGType = WlzGreyTypeFromObj(cObj, &errNum);
                bgdV = WlzGetBackground(cObj, &errNum);
            }
            if((errNum == WLZ_ERR_NONE) && (cGType != gType))
            {
                errNum = WLZ_ERR_GREY_TYPE;
            }
            break;
        default:
            errNum = WLZ_ERR_OBJECT_TYPE;
            break;
        }
    }
    /* Create new object with domain and values of given rectangular buffer. */
    if(errNum == WLZ_ERR_NONE)
    {
        bDom.i = WlzMakeIntervalDomain(WLZ_INTERVALDOMAIN_RECT,
                                       og.vtY, og.vtY + sz.vtY - 1,
                                       og.vtX, og.vtX + sz.vtX - 1,
                                       &errNum);
    }
    if(errNum == WLZ_ERR_NONE)
    {
        WlzObjectType gTT;

        gTT = WlzGreyTableType(WLZ_GREY_TAB_RECT, gType, NULL);
        bVal.r = WlzMakeRectValueTb(gTT, bDom.i->line1, bDom.i->lastln,
                                    bDom.i->kol1, sz.vtX, bgdV, bufP.inp, &errNum);
    }
    if(errNum == WLZ_ERR_NONE)
    {
        bObj = WlzMakeMain(WLZ_2D_DOMAINOBJ, bDom, bVal, NULL, NULL, &errNum);
    }
    if(errNum == WLZ_ERR_NONE)
    {
        if(cObj == NULL)
        {
            /* Just copy the buffer object. */
            nObj = WlzCopyObject(bObj, &errNum);
        }
        else
        {
            /* Compute union of current and buffer objects. */
            nObj = (cObj)? WlzUnion2(cObj, bObj, &errNum):
                   WlzMakeMain(WLZ_2D_DOMAINOBJ, bDom, nVal,
                               NULL, NULL, &errNum);
            /* Create new value table. */
            if(errNum == WLZ_ERR_NONE)
            {
                WlzObjectType gTT;

                gTT = WlzGreyTableType(WLZ_GREY_TAB_RAGR, gType, NULL);
                nVal.v = WlzNewValueTb(nObj, gTT, bgdV, &errNum);
            }
            if(errNum == WLZ_ERR_NONE)
            {
                nObj->values = WlzAssignValues(nVal, NULL);
            }
            if(errNum == WLZ_ERR_NONE)
            {
                WlzObject 	*tObj;

                /* Copy existing values to new object. */
                tObj = WlzGreyTransfer(nObj, cObj, &errNum);
                (void )WlzFreeObj(nObj);
                nObj = tObj;
                /* Then copy buffer values to new object. */
                if(errNum == WLZ_ERR_NONE)
                {
                    tObj = WlzGreyTransfer(nObj, bObj, &errNum);
                    (void )WlzFreeObj(nObj);
                    nObj = tObj;
                }
            }
        }
    }
    (void )WlzFreeObj(bObj);
    if(dstErr)
    {
        *dstErr = errNum;
    }
    return(nObj);
}
int             main(int argc, char *argv[])
{
  int		option,
		nReg = 0,
		tNReg = 0,
  		ok = 1,
		usage = 0,
		verbose = 0,
		threshSet = 0,
		centreSet = 0;
  double	minArea = 2;
  char		*inExt,
		*dbgExt,
		*inDir,
		*dbgDir,
		*inFile,
		*dbgFile,
  		*inPath = NULL,
		*dbgPath = NULL,
		*outFile = NULL;
  WlzRadDistVal distSort = WLZ_RADDISTVAL_AREA;
  WlzRadDistRec	*distData = NULL;
  WlzPixelV	thrVal;
  WlzDVertex2	centre;
  WlzCompThreshType thrMtd = WLZ_COMPTHRESH_OTSU;
  WlzThresholdType thrMod = WLZ_THRESH_HIGH;
  WlzEffFormat	inFmt = WLZEFF_FORMAT_NONE,
  		dbgFmt = WLZEFF_FORMAT_NONE;
  WlzObject	*inObj = NULL,
		*disObj = NULL,
  		*segObj = NULL;
  WlzGreyValueWSpace *disGVWSp = NULL;
  WlzObject	**regObjs = NULL;
  FILE		*fP = NULL;
  WlzErrorNum	errNum = WLZ_ERR_NONE;
  const int	maxObj = 1000000;
  char		pathBuf[FILENAME_MAX];
  const double	eps = 1.0e-06;
  const char	*errMsg;
  static char	optList[] = "hvAGDHELR:c:d:n:o:t:",
		defFile[] = "-";

  thrVal.type = WLZ_GREY_DOUBLE;
  thrVal.v.dbv = 0.0;
  outFile = defFile;
  while((usage == 0) && ok &&
        ((option = getopt(argc, argv, optList)) != -1))
  {
    switch(option)
    {
      case 'A':
        distSort = WLZ_RADDISTVAL_AREA;
	break;
      case 'D':
        distSort = WLZ_RADDISTVAL_DIST;
	break;
      case 'G':
        distSort = WLZ_RADDISTVAL_ANGLE;
	break;
      case 'H':
        thrMod = WLZ_THRESH_HIGH;
	break;
      case 'E':
        thrMod = WLZ_THRESH_EQUAL;
	break;
      case 'L':
        thrMod = WLZ_THRESH_LOW;
	break;
      case 'R':
        distSort = WLZ_RADDISTVAL_RADIUS;
	break;
      case 'h':
        usage = 1;
	break;
      case 'v':
        verbose = 1;
	break;
      case 'c':
	centreSet = 1;
        if(sscanf(optarg, "%lg,%lg", &(centre.vtX), &(centre.vtY)) != 2)
	{
	  usage = 1;
	}
        break;
      case 'd':
        dbgPath = optarg;
	break;
      case 'o':
        outFile = optarg;
	break;
      case 'n':
        if(sscanf(optarg, "%lg", &minArea) != 1)
	{
	  usage = 1;
	}
	break;
      case 't':
	threshSet = 1;
        if(sscanf(optarg, "%lg", &(thrVal.v.dbv)) != 1)
	{
	  usage = 1;
	}
	break;
      default:
        usage = 1;
	break;
    }
  }
  ok = !usage;
  if(ok)
  {
    if((optind + 1) != argc)
    {
      usage = 1;
      ok = 0;
    }
    else
    {
      inPath = *(argv + optind);
    }
  }
  if(ok && verbose)
  {
    (void )fprintf(stderr, "inPath = %s\n", inPath);
  }
  /* Parse input file path into path + name + ext. */
  if(ok)
  {
    ok = (usage = WlzRadDistParsePath(inPath, &inDir, &inFile, &inExt,
                                      &inFmt)) == 0;
  }
  if(ok && verbose)
  {
    (void )fprintf(stderr, "inDir = %s\n", inDir);
    (void )fprintf(stderr, "inFile = %s\n", inFile);
    (void )fprintf(stderr, "inExt = %s\n", (inExt)? inExt: "(null)");
    (void )fprintf(stderr, "inFmt = %s\n",
    		   WlzEffStringFromFormat(inFmt, NULL));
  }
  /* Read image. */
  if(ok)
  {
    errNum = WLZ_ERR_READ_EOF;
    if(inExt)
    {
      (void )sprintf(pathBuf, "%s/%s.%s", inDir, inFile, inExt);
    }
    else
    {
      (void )sprintf(pathBuf, "%s/%s", inDir, inFile);
    }
    if(((inObj = WlzAssignObject(WlzEffReadObj(NULL, pathBuf, inFmt,
    					       0, 0, 0,
					       &errNum), NULL)) == NULL) ||
       (inObj->type != WLZ_2D_DOMAINOBJ))
    {
      ok = 0;
      (void )WlzStringFromErrorNum(errNum, &errMsg);
      (void )fprintf(stderr,
      		     "%s: Failed to read 2D image object from file %s (%s)\n",
		     *argv, pathBuf, errMsg);
    }
  }
  if(ok && verbose)
  {
    (void )fprintf(stderr, "read input image ok.\n");
  }
  /* Convert to grey if needed, normalise 0 - 255 if needed and compute
   * threshold value unless already known. */
  if(ok)
  {
    if(WlzGreyTypeFromObj(inObj, NULL) == WLZ_GREY_RGBA)
    {
      WlzObject *ppObj;

      ppObj = WlzAssignObject(
	      WlzRGBAToModulus(inObj, &errNum), NULL);
      if(errNum == WLZ_ERR_NONE)
      {
	(void )WlzFreeObj(inObj);
	inObj = ppObj;
      }
    }
    if(threshSet == 0)
    {
      WlzObject *hObj = NULL;

      errNum = WlzGreyNormalise(inObj, 1);
      if(errNum == WLZ_ERR_NONE)
      {
        hObj = WlzHistogramObj(inObj, 256, 0.0, 1.0, &errNum);
      }
      if(errNum == WLZ_ERR_NONE)
      {
	threshSet = 1;
        errNum = WlzCompThreshold(&thrVal.v.dbv, hObj, thrMtd, 0);
      }
      (void )WlzFreeObj(hObj);
    }
    if(errNum != WLZ_ERR_NONE)
    {
      ok = 0;
      (void )WlzStringFromErrorNum(errNum, &errMsg);
      (void )fprintf(stderr, "%s: failed to normalise object (%s)\n",
		     *argv, errMsg);
    }
  }
  /* Segment the object. */
  if(ok)
  {
    if(inObj->values.core == NULL)
    {
      segObj = WlzAssignObject(inObj, NULL);
    }
    else
    {
      segObj = WlzAssignObject(
               WlzThreshold(inObj, thrVal, thrMod, &errNum), NULL);
      if(errNum != WLZ_ERR_NONE)
      {
	ok = 0;
	(void )WlzStringFromErrorNum(errNum, &errMsg);
	(void )fprintf(stderr, "%s: failed to segment image (%s)\n",
		       *argv, errMsg);
      }
    }
  }
  /* Compute object with the same domain as the input object but in which
   * the values are the minimum distance from an edge. */
  if(ok)
  {
    WlzObject	*bObj = NULL;

    bObj = WlzBoundaryDomain(inObj, &errNum);
    if(errNum == WLZ_ERR_NONE)
    {
      disObj = WlzAssignObject(       
               WlzDistanceTransform(inObj, bObj, WLZ_OCTAGONAL_DISTANCE,
	       			    0.0, 0.0, &errNum), NULL);
    }
    if(errNum == WLZ_ERR_NONE)
    {
      disGVWSp = WlzGreyValueMakeWSp(disObj, &errNum);
    }
    if(errNum != WLZ_ERR_NONE)
    {
      ok = 0;
      (void )WlzStringFromErrorNum(errNum, &errMsg);
      (void )fprintf(stderr, "%s: failed to compute distance object (%s)\n",
		     *argv, errMsg);
    }
    (void )WlzFreeObj(bObj);
  }
  /* Output the debug image if required. */
  if(ok && dbgPath)
  {
    WlzObject	*dbgObj;

    dbgObj = WlzAssignObject(WlzCopyObject(inObj, &errNum), NULL);
    if(errNum == WLZ_ERR_NONE)
    {
      WlzPixelV	iMin,
		iMax,
		oMin,
		oMax;

      if(dbgObj->values.core == NULL)
      {
        WlzValues tmpVal;

	oMax.type = WLZ_GREY_UBYTE;
	oMax.v.ubv = 255;
	tmpVal.v = WlzNewValueTb(dbgObj,
				 WlzGreyTableType(WLZ_GREY_TAB_RAGR,
				                  WLZ_GREY_UBYTE, NULL),
	                         oMax, &errNum);
        if(errNum == WLZ_ERR_NONE)
	{
	  dbgObj->values = WlzAssignValues(tmpVal, NULL);
	}
      }
      else
      {
        WlzObject *tmpObj = NULL;

	oMin.type = WLZ_GREY_UBYTE;
	oMin.v.ubv = 0;
	oMax.type = WLZ_GREY_UBYTE;
	oMax.v.ubv = 200;
	errNum = WlzGreyRange(dbgObj, &iMin, &iMax);
	if(errNum == WLZ_ERR_NONE)
	{
	  errNum = WlzGreySetRange(dbgObj, iMin, iMax, oMin, oMax, 0);
	}
	if(errNum == WLZ_ERR_NONE)
	{
	  tmpObj = WlzMakeMain(inObj->type, segObj->domain, dbgObj->values,
	                       NULL, NULL, &errNum);
	}
	if(errNum == WLZ_ERR_NONE)
	{
	  oMax.v.ubv = 255;
	  errNum = WlzGreySetValue(tmpObj, oMax);
	}
	(void )WlzFreeObj(tmpObj);
	if(errNum == WLZ_ERR_NONE)
	{
	  tmpObj = WlzConvertPix(dbgObj, WLZ_GREY_UBYTE, &errNum);
	  (void )WlzFreeObj(dbgObj);
	  dbgObj = WlzAssignObject(tmpObj, NULL);
	}
      }
    }
    if(errNum == WLZ_ERR_NONE)
    {
      (void )WlzRadDistParsePath(dbgPath, &dbgDir, &dbgFile, &dbgExt,
      			         &dbgFmt);
      if(dbgExt)
      {
	(void )sprintf(pathBuf, "%s/%s.%s", dbgDir, dbgFile, dbgExt);
      }
      else
      {
	(void )sprintf(pathBuf, "%s/%s", dbgDir, dbgFile);
      }
      errNum = WlzEffWriteObj(NULL, pathBuf, dbgObj, dbgFmt);
    }
    (void )WlzFreeObj(dbgObj);
    if(errNum != WLZ_ERR_NONE)
    {
      ok = 0;
      (void )WlzStringFromErrorNum(errNum, &errMsg);
      (void )fprintf(stderr, "%s: failed to output the debug image (%s)\n",
		     *argv, errMsg);
    }
  }
  /* Label the segmented object. */
  if(ok)
  {
    errNum = WlzLabel(segObj, &nReg, &regObjs, maxObj, 0, WLZ_8_CONNECTED);
    if(errNum != WLZ_ERR_NONE)
    {
      ok = 0;
      errNum = WLZ_ERR_MEM_ALLOC;
      (void )WlzStringFromErrorNum(errNum, &errMsg);
      (void )fprintf(stderr, "%s: failed to split into components (%s)\n",
		     *argv, errMsg);
    }
    if(ok && verbose)
    {
      (void )fprintf(stderr, "nReg = %d\n", nReg);
    }
  }
  /* Compute centre of mass if not known. */
  if(ok)
  {
    if(centreSet == 0)                          
    {
      centre = WlzCentreOfMass2D(inObj, 1, NULL, &errNum);
      if(errNum != WLZ_ERR_NONE)
      {
	ok = 0;
	(void )WlzStringFromErrorNum(errNum, &errMsg);
	(void )fprintf(stderr, "%s: failed to compute centre of mass (%s)\n",
		       *argv, errMsg);
      }
    }
    if(ok && verbose)
    {
      (void )fprintf(stderr, "centre = %lg,%lg\n", centre.vtX, centre.vtY);
    }
  }
  /* Allocate a radial distribution table. */
  if(ok)
  {
    if((distData = (WlzRadDistRec *)
                   AlcCalloc(nReg, sizeof(WlzRadDistRec))) == NULL)
    {
      ok = 0;
      errNum = WLZ_ERR_MEM_ALLOC;
      (void )WlzStringFromErrorNum(errNum, &errMsg);
      (void )fprintf(stderr, "%s: failed to allocate result lable (%s)\n",
		     *argv, errMsg);
    }
    
  }
  /* Compute the redial distribution data. */
  if(ok)
  {
    int		idR = 0,
    		idS = 0;

    while((errNum == WLZ_ERR_NONE) && (idR < nReg))
    {
      double	mass;
      WlzDVertex2 com;

      com = WlzCentreOfMass2D(regObjs[idR], 1, &mass, NULL);
      if(mass > minArea - eps)
      {
	WlzGreyValueGet(disGVWSp, 0.0, com.vtY, com.vtX);
	distData[idS].pos = com;
	distData[idS].area = mass;
	WLZ_VTX_2_SUB(com, centre, com);
	distData[idS].radius = WLZ_VTX_2_LENGTH(com);
	distData[idS].angle = ALG_M_PI + atan2(com.vtY, com.vtX);
	switch(disGVWSp->gType)
	{
	  case WLZ_GREY_LONG:
	    distData[idS].dist = *(disGVWSp->gPtr[0].lnp);
	    break;
	  case WLZ_GREY_INT:
	    distData[idS].dist = *(disGVWSp->gPtr[0].inp);
	    break;
	  case WLZ_GREY_SHORT:
	    distData[idS].dist = *(disGVWSp->gPtr[0].shp);
	    break;
	  case WLZ_GREY_UBYTE:
	    distData[idS].dist = *(disGVWSp->gPtr[0].ubp);
	    break;
	  case WLZ_GREY_FLOAT:
	    distData[idS].dist = *(disGVWSp->gPtr[0].flp);
	    break;
	  case WLZ_GREY_DOUBLE:
	    distData[idS].dist = *(disGVWSp->gPtr[0].dbp);
	    break;
	  default:
	    distData[idS].dist = 0.0;
	    break;
	}
	++idS;
      }
      ++idR;
    }
    tNReg = idS;
    switch(distSort)
    {
      case WLZ_RADDISTVAL_AREA:
        (void )qsort(distData, tNReg, sizeof(WlzRadDistRec),
		     WlzRadDistRecSortArea);
	break;
      case WLZ_RADDISTVAL_ANGLE:
        (void )qsort(distData, tNReg, sizeof(WlzRadDistRec), 
		     WlzRadDistRecSortAngle);
	break;
      case WLZ_RADDISTVAL_RADIUS:
        (void )qsort(distData, tNReg, sizeof(WlzRadDistRec),
		     WlzRadDistRecSortRadius);
	break;
      case WLZ_RADDISTVAL_DIST:
        (void )qsort(distData, tNReg, sizeof(WlzRadDistRec),
		     WlzRadDistRecSortDist);
	break;
    }
  }
  /* Output the sorted radial distribution table. */
  if(ok)
  {
    if(((fP = strcmp(outFile, "-")?
              fopen(outFile, "w"): stdout)) == NULL)
    {
      ok = 0;
      (void )fprintf(stderr, "%s: failed to open output file %s\n",
                     *argv, outFile);
    }
  }
  if(ok)
  {
    int		idR;

    for(idR = 0; idR < tNReg; ++idR)
    {
      double a;

      a = (distData[idR].angle > 0.0)?
	  0   + (180 * distData[idR].angle / ALG_M_PI):
          360 + (180 * distData[idR].angle / ALG_M_PI);
      (void )fprintf(fP, "%g %g %g %g,%g %g\n",
		     a,
                     distData[idR].radius,
		     distData[idR].area,
		     distData[idR].pos.vtX,
		     distData[idR].pos.vtY,
		     distData[idR].dist);
    }
  }
  if(strcmp(outFile, "-"))
  {
    (void )fclose(fP);
  }
  /* Tidy up. */
  AlcFree(distData);
  WlzGreyValueFreeWSp(disGVWSp);
  (void )WlzFreeObj(inObj);
  (void )WlzFreeObj(disObj);
  (void )WlzFreeObj(segObj);
  if(regObjs)
  {
    int		idR;

    for(idR = 0; idR < nReg; ++idR)
    {
      (void )WlzFreeObj(regObjs[idR]);
    }
    AlcFree(regObjs);
  }
  if(usage)
  {
    (void )fprintf(stderr,
    "Usage: %s [-h] [-v] [-A] [-D] [-G] [-H] [-E] [-L] [-R]\n"
    "\t\t[-c #,#] [-d <debug image>] [-n #]  [-o <out file>]\n"
    "\t\t[-t #] [<input image>]\n"
    "Segments the given object using a threshold value and outputs the \n"
    "radial distribution of the thresholded components.\n"
    "Version: %s\n"
    "Options:\n"
    "  -h  Help - prints this usage masseage.\n"
    "  -v  Verbose output.\n"
    "  -A  Sort output by area (default).\n"
    "  -D  Sort output by distance from boundary.\n"
    "  -G  Sort output by angle.\n"
    "  -H  Threshold high, use pixels at or above threshold (default).\n"
    "  -E  Threshold equal, use pixels at threshold.\n"
    "  -L  Threshold low, use pixels below threshold.\n"
    "  -R  Sort output by radial distance from centre.\n"
    "  -c  Centre (default is image centre).\n"
    "  -d  Debug image.\n"
    "  -n  Minimum area (default %g).\n"
    "  -t  Threshold value (default is to compute using Otsu's method).\n"
    "By default the input image object is read from the standard input and\n"
    "the radial distribution is written to the standard output.\n"
    "The image formats understood include wlz, jpg and tif.\n"
    "The output format is:\n"
    "  <angle> <dist from centre> <area> <x pos>,<y pos> <dist form boundary>\n"
    "Example:\n"
    "  %s -o out.txt -d debug.jpg in.tif\n"
    "The input image is read from in.tif, a debug image showing the\n"
    "segmented regions is written to debug.jpg and the radial distribution\n"
    "statistics are written to the file out.txt. With the output in\n"
    "out.txt, the following R code would plot the data as a set of circles\n"
    "with radius proportional to the square root of the component area:\n"
    "  data <- read.table(\"out.txt\")\n"
    "  attach(data)\n"
    "  symbols(x=data$V1, y=data$V2, circles=sqrt(data$V3))\n",
    argv[0],
    WlzVersion(),
    minArea,
    argv[0]);
  }
  return(!ok);
}
/*!
* \return	New contour object.
* \brief	Create a contour object from a 2D domain object with values,
*		together with a set of parameters.
* \param	gObj			Given 2D domain object.
* \param	binFlg			Generate contours from binary
*					(thresholded) image.
* \param	thrType			Threshold type.
* \param	thrVal			Threshold value.
* \param	medianSz		Median filter size if > 0.
* \param	smooth			Gaussian smoothing value.
* \param	cThr			Contour threshold value.
* \param	minSpx			Minimum number of simplicies per shell.
* \param	objDbgFileName		If non-null used as the name of a
*					file for the image object just prior
*					to computing the geometric model.
* \param	dstErr			Destination ptr for error, may be NULL.
*/
static WlzObject *WlzMatchICPPlaneCreateContourObj(WlzObject *gObj,
					int binFlg, WlzThresholdType thrType,
					double thrVal,
					int medianSz, double smooth,
					double cThr, int minSpx,
					int debug, char *objDbgFileName,
					WlzErrorNum *dstErr)
{
  WlzObject	*tObj0 = NULL,
		*cObj = NULL;
  WlzDomain	tDom;
  WlzValues	tVal;
  FILE		*dFP = NULL;
  WlzPixelV	thrV;
  WlzErrorNum	errNum = WLZ_ERR_NONE;
  const int	nrmFlg = 1;

  tVal.core = NULL;
  if(binFlg)
  {
    thrV.type = WLZ_GREY_DOUBLE;
    thrV.v.dbv = thrVal;
    tObj0 = WlzThreshold(gObj, thrV, thrType, &errNum);
    if(errNum == WLZ_ERR_NONE)
    {
      thrV.v.dbv = 0.0;
      errNum = WlzGreySetValue(gObj, thrV);
    }
    if(errNum == WLZ_ERR_NONE)
    {
      thrV.v.dbv = 255.0;
      errNum = WlzGreySetValue(tObj0, thrV);
    }
    (void )WlzFreeObj(tObj0);
    tObj0 = NULL;
  }
  if(medianSz > 0)
  {
    errNum = WlzRankFilter(gObj, medianSz, 0.5);
  }
  if(errNum == WLZ_ERR_NONE)
  {
    if(smooth > DBL_EPSILON)
    {
      tObj0 = WlzAssignObject(
	      WlzGauss2(gObj, smooth, smooth, 0, 0, &errNum), NULL);
    }
    else
    {
      tObj0 = WlzAssignObject(gObj, NULL);
    }
  }
  if(errNum == WLZ_ERR_NONE)
  {
    if(objDbgFileName)
    {
      if((dFP = fopen(objDbgFileName, "w")) != NULL)
      {
	(void )WlzWriteObj(dFP, tObj0);
	(void )fclose(dFP);
      }
    }
    tDom.ctr = WlzContourObj(tObj0, WLZ_CONTOUR_MTD_GRD, cThr, 1.0, nrmFlg,
			     &errNum);
  }
  WlzFreeObj(tObj0);
  if(errNum == WLZ_ERR_NONE)
  {
    cObj = WlzMakeMain(WLZ_CONTOUR, tDom, tVal, NULL, NULL, &errNum);
  }
  /* There's a bug somewhere in the deletion of small shells. Delete small
   * shells and then copy the contours. */
  if(debug && (errNum == WLZ_ERR_NONE))
  {
    errNum = WlzGMVerifyModel(cObj->domain.ctr->model, NULL);
  }
  if(errNum == WLZ_ERR_NONE)
  {
    errNum = WlzGMFilterRmSmShells(cObj->domain.ctr->model, minSpx * 3);
  }
  if(debug && (errNum == WLZ_ERR_NONE))
  {
    errNum = WlzGMVerifyModel(cObj->domain.ctr->model, NULL);
  }
  if(errNum == WLZ_ERR_NONE)
  {
    tObj0 = WlzAssignObject(WlzCopyObject(cObj, &errNum), NULL);
    WlzFreeObj(cObj);
    if(errNum == WLZ_ERR_NONE)
    {
      cObj = tObj0;
    }
    tObj0 = NULL;
  }
  if(debug && (errNum == WLZ_ERR_NONE))
  {
    errNum = WlzGMVerifyModel(cObj->domain.ctr->model, NULL);
  }
  return(cObj);
}
/*! 
* \ingroup      WlzValuesUtils
* \brief        Transfer grey values from the source object to the
 destination object. Currently it is assumed that the objects are
 of the same type (2D/3D) and have the same grey-value type.
*
* \return       Woolz object with transferred grey values
* \param    obj	destination object
* \param    srcObj	source object
* \param    dstErr	error return
* \par      Source:
*                WlzGreyTransfer.c
*/
WlzObject *WlzGreyTransfer(
  WlzObject	*obj,
  WlzObject	*srcObj,
  WlzErrorNum	*dstErr)
{
  WlzObject	*rtnObj=NULL;
  WlzObject	*obj1, *obj2;
  WlzValues	values;
  WlzIntervalWSpace	iwsp1, iwsp2;
  WlzGreyWSpace		gwsp1, gwsp2;
  int			size;
  WlzErrorNum	errNum=WLZ_ERR_NONE;

  /* check destination obj */
  if( obj == NULL ){
    errNum = WLZ_ERR_OBJECT_NULL;
  }
  else {
    switch( obj->type ){
    case WLZ_2D_DOMAINOBJ:
      if( obj->values.core == NULL ){
	errNum = WLZ_ERR_VALUES_NULL;
      } else if(WlzGreyTableIsTiled(obj->values.core->type)) {
        errNum = WLZ_ERR_VALUES_TYPE;
      }
      else {
        rtnObj = WlzCopyObject(obj, &errNum);
      }
      break;

    case WLZ_3D_DOMAINOBJ:
      return WlzGreyTransfer3d(obj, srcObj, dstErr);

    case WLZ_TRANS_OBJ:
      if((values.obj = WlzGreyTransfer(obj->values.obj, srcObj,
				       &errNum)) != NULL){
	return WlzMakeMain(WLZ_TRANS_OBJ, obj->domain, values,
			   NULL, NULL, dstErr);
      }
      break;

    case WLZ_EMPTY_OBJ:
      return WlzMakeEmpty(dstErr);

    default:
      errNum = WLZ_ERR_OBJECT_TYPE;
      break;
    }
  }

  /* check the source object */
  if( errNum == WLZ_ERR_NONE ){
    if( srcObj == NULL ){
      errNum = WLZ_ERR_OBJECT_NULL;
    }
    else {
      switch( srcObj->type ){
      case WLZ_2D_DOMAINOBJ:
	break;

      case WLZ_TRANS_OBJ:
	srcObj = srcObj->values.obj;
	break;

      case WLZ_EMPTY_OBJ:
	if( dstErr ){
	  *dstErr = errNum;
	}
	return rtnObj;

      default:
	errNum = WLZ_ERR_OBJECT_TYPE;
	break;
      }
    }
  }

  /* copy source obj values within the intersection */
  if( errNum == WLZ_ERR_NONE ){
    if((srcObj->type != WLZ_EMPTY_OBJ) ){
      if( (obj1 = WlzIntersect2(srcObj, rtnObj, &errNum)) ){
	obj1->values = WlzAssignValues(rtnObj->values, NULL);
	obj2 = WlzMakeMain(obj1->type, obj1->domain, srcObj->values,
			   NULL, NULL, NULL);

	errNum = WlzInitGreyScan(obj1, &iwsp1, &gwsp1);
	errNum = WlzInitGreyScan(obj2, &iwsp2, &gwsp2);
	switch( gwsp1.pixeltype ){
	case WLZ_GREY_INT:
	  size = sizeof(int);
	  break;
	case WLZ_GREY_SHORT:
	  size = sizeof(short);
	  break;
	case WLZ_GREY_UBYTE:
	  size = sizeof(WlzUByte);
	  break;
	case WLZ_GREY_FLOAT:
	  size = sizeof(float);
	  break;
	case WLZ_GREY_DOUBLE:
	  size = sizeof(double);
	  break;
	case WLZ_GREY_RGBA:
	  size = sizeof(WlzUInt);
	  break;
	default:
	  errNum = WLZ_ERR_GREY_TYPE;
	  break;
	}

	while((errNum == WLZ_ERR_NONE) &&
	      (errNum = WlzNextGreyInterval(&iwsp1)) == WLZ_ERR_NONE){
	  (void) WlzNextGreyInterval(&iwsp2);
	  memcpy((void *) gwsp1.u_grintptr.inp,
		 (const void *) gwsp2.u_grintptr.inp,
		 size * iwsp1.colrmn);
	}
	if( errNum == WLZ_ERR_EOO ){
	  errNum = WLZ_ERR_NONE;
	}
	WlzFreeObj(obj2);
	WlzFreeObj(obj1);
      }
      else {
	WlzFreeObj(rtnObj);
	rtnObj = NULL;
      }
    }
  }

  if( dstErr ){
    *dstErr = errNum;
  }
  return rtnObj;
}
/*! 
* \ingroup      WlzValuesUtils
* \brief        static function to implement WlzGreyTransfer for 3D objects.
*
* \return       woolz object
* \param    obj	destination object
* \param    srcObj	source object
* \param    dstErr	error return
* \par      Source:
*                WlzGreyTransfer.c
*/
static WlzObject *WlzGreyTransfer3d(
  WlzObject	*obj,
  WlzObject	*srcObj,
  WlzErrorNum	*dstErr)
{
  WlzObject	*rtnObj=NULL;
  WlzObject	*obj1, *obj2, *tmpObj;
  WlzDomain	*domains;
  WlzValues	values, *valuess;
  WlzPlaneDomain	*pdom;
  int		p;
  WlzErrorNum	errNum=WLZ_ERR_NONE;

  /* check the object - it is non-NULL and 3D but the
     domain needs checking */
  if( obj->domain.p == NULL ){
    errNum = WLZ_ERR_DOMAIN_NULL;
  }
  else {
    switch( obj->domain.p->type ){
    case WLZ_2D_DOMAINOBJ:
      /* check there is a valuetable */
      if( obj->values.core == NULL ){
	errNum = WLZ_ERR_VALUES_NULL;
      } else if(WlzGreyTableIsTiled(obj->values.core->type)) {
        errNum = WLZ_ERR_VALUES_TYPE;
      }
      break;

    default:
      errNum = WLZ_ERR_DOMAIN_TYPE;
      break;
    }
  }

  /* check the source object */
  if( errNum == WLZ_ERR_NONE ){
    if( srcObj == NULL ){
      errNum = WLZ_ERR_OBJECT_NULL;
    }
    else {
      switch( srcObj->type ){

      case WLZ_3D_DOMAINOBJ:
	if( srcObj->domain.p ){
	  switch( srcObj->domain.p->type ){
	  case WLZ_2D_DOMAINOBJ:
	    break;

	  default:
	    errNum = WLZ_ERR_DOMAIN_TYPE;
	    break;
	  }
	}
	else {
	  errNum = WLZ_ERR_DOMAIN_NULL;
	}
	break;

      case WLZ_EMPTY_OBJ:
	return WlzCopyObject(obj, dstErr);

      default:
	errNum = WLZ_ERR_OBJECT_TYPE;
	break;
      }
    }
  }

  /* now we have a 3D obj and 3D srcObject so run through the source
     and map values as required */
  if( errNum == WLZ_ERR_NONE ){
    WlzDomain	*objDoms;
    WlzValues	*objVals;

    /* attach a voxel table with empty values list */
    values.vox = WlzMakeVoxelValueTb(obj->values.vox->type,
				     obj->domain.p->plane1,
				     obj->domain.p->lastpl,
				     obj->values.vox->bckgrnd,
				     NULL, NULL);
    rtnObj = WlzMakeMain(obj->type, obj->domain, values, NULL, NULL, &errNum);

    /* set some local variables */
    pdom = rtnObj->domain.p;
    domains = rtnObj->domain.p->domains;
    valuess = rtnObj->values.vox->values;
    objDoms = obj->domain.p->domains;
    objVals = obj->values.vox->values;

    /* calculate the new valuetables */
    for(p=pdom->plane1; p <= pdom->lastpl;
	p++, domains++, valuess++, objDoms++, objVals++){
      if(((*domains).core)){
	obj1 = WlzMakeMain(WLZ_2D_DOMAINOBJ, *objDoms, *objVals,
			   NULL, NULL, &errNum);
	obj1 = WlzAssignObject(obj1, &errNum);
			   
	if((p >= srcObj->domain.p->plane1) &&
	   (p <= srcObj->domain.p->lastpl) &&
	   (srcObj->domain.p->domains[p-srcObj->domain.p->plane1].core)){
	  obj2 = 
	    WlzMakeMain(WLZ_2D_DOMAINOBJ,
			srcObj->domain.p->domains[p-srcObj->domain.p->plane1],
			srcObj->values.vox->values[p-srcObj->domain.p->plane1],
			NULL, NULL, &errNum);
	}
	else {
	  obj2 = WlzMakeEmpty(NULL);
	}
	obj2 = WlzAssignObject(obj2, &errNum);

	tmpObj = WlzGreyTransfer(obj1, obj2, &errNum);
	*valuess = WlzAssignValues(tmpObj->values, &errNum);
	WlzFreeObj(obj1);
	WlzFreeObj(obj2);
	WlzFreeObj(tmpObj);
      }
    }
  }

  if( dstErr ){
    *dstErr = errNum;
  }
  return rtnObj;
}