Beispiel #1
0
/*!
* \return	Object read from file.
* \ingroup	WlzExtFF
* \brief	Reads a 2D Woolz object from the given file using the given
* 		(2D) file format.
* \param	fP			Given file.
* \param	fFmt			Given file format (must be a 2D file
* 					format).
* \param	dstErr			Destination error number ptr, may be
* 					NULL.
*/
static WlzObject *WlzEffReadObjStack2D(FILE *fP, WlzEffFormat fFmt,
				       WlzErrorNum *dstErr)
{
  WlzObject	*obj = NULL;
  unsigned char **data = NULL;
  WlzIVertex2	imgSz,
  		imgOrg;
  WlzErrorNum	errNum = WLZ_ERR_NONE;

  imgSz.vtX = 0;
  imgSz.vtY = 0;
  imgOrg.vtX = 0;
  imgOrg.vtY = 0;
  errNum = WlzEffReadObjStackData2D(fP, fFmt, &imgSz, &data);
  if(errNum == WLZ_ERR_NONE)
  {
    obj = WlzFromArray2D((void **)data, imgSz, imgOrg,
    			 WLZ_GREY_UBYTE, WLZ_GREY_UBYTE,
			 0.0, 1.0, 0, 1, &errNum);
  }
  if(data)
  {
    if(obj)
    {
      AlcFree(data); 			    /* **data used by object */
    }
    else
    {
      Alc2Free((void **)data);
    }
  }
  if(dstErr)
  {
    *dstErr = errNum;
  }
  return(obj);
}
Beispiel #2
0
/*!
* \return	Translation.
* \ingroup	WlzRegistration
* \brief	Registers the given 2D domain objects using a
*               frequency domain cross correlation, to find
*               the translation which has the highest cross
*               correlation value.
* \param	tObj			The target object. Must have
*                                       been assigned.
* \param	sObj			The source object to be
*                                       registered with target object.
*                                       Must have been assigned.
* \param	initTr			Initial affine transform
*                                       to be applied to the source
*                                       object prior to registration.
* \param        maxTran    		Maximum translation.
* \param	maxTran			Maximum translation.
* \param	winFn			Window function.
* \param	noise			Use Gaussian noise if non-zero.
* \param	dstCCor			Destination ptr for the cross
*                                       correlation value, may be NULL.
* \param	dstErr			Destination error pointer,
*                                       may be NULL.
*/
static WlzDVertex2 WlzRegCCorObjs2DTran(WlzObject *tObj, WlzObject *sObj,
					WlzAffineTransform *initTr,
					WlzDVertex2 maxTran,
					WlzWindowFnType winFn, int noise,
					double *dstCCor, WlzErrorNum *dstErr)
{
  int		oIdx;
  double	cCor = 0.0;
  double	sSq[2];
  double	**oAr[2];
  WlzIBox2	aBox;
  WlzIBox2	oBox[2],
  		pBox[2];
  WlzIVertex2	aSz,
  		aOrg,
		centre,
		radius,
		tran;
  WlzDVertex2	dstTran;
  WlzObject	*oObj[2],
  		*pObj[2];
  WlzErrorNum	errNum = WLZ_ERR_NONE;

  dstTran.vtX = 0.0;
  dstTran.vtY = 0.0;
  oAr[0] = oAr[1] = NULL;
  oObj[0] = oObj[1] = NULL;
  pObj[0] = pObj[1] = NULL;
  oObj[0] = WlzAssignObject(tObj, NULL);
  /* Transform source object. */
  if((initTr == NULL) || WlzAffineTransformIsIdentity(initTr, NULL))
  {
    oObj[1] = WlzAssignObject(sObj, NULL);
  }
  else
  {
    oObj[1] = WlzAssignObject(
              WlzAffineTransformObj(sObj, initTr, WLZ_INTERPOLATION_NEAREST,
				    &errNum), NULL);
  }
  /* Preprocess the objects. */
  if(errNum == WLZ_ERR_NONE)
  {
    oIdx = 0;
    while((errNum == WLZ_ERR_NONE) && (oIdx < 2))
    {
      oBox[oIdx] = WlzBoundingBox2I(oObj[oIdx], &errNum);
      ++oIdx;
    }
  }
  if(errNum == WLZ_ERR_NONE)
  {
    oIdx = 0;
    while((errNum == WLZ_ERR_NONE) && (oIdx < 2))
    {
      centre.vtX = (oBox[oIdx].xMin + oBox[oIdx].xMax) / 2;
      centre.vtY = (oBox[oIdx].yMin + oBox[oIdx].yMax) / 2;
      radius.vtX = (oBox[oIdx].xMax - oBox[oIdx].xMin) / 2;
      radius.vtY = (oBox[oIdx].yMax - oBox[oIdx].yMin) / 2;
      pObj[oIdx] = WlzAssignObject(
                   WlzRegCCorPProcessObj2D(oObj[oIdx], winFn, centre, radius,
      					   &errNum), NULL);
      ++oIdx;
    }
  }
  /* Create double arrays. */
  if(errNum == WLZ_ERR_NONE)
  {
    oIdx = 0;
    while((errNum == WLZ_ERR_NONE) && (oIdx < 2))
    {
      pBox[oIdx] = WlzBoundingBox2I(pObj[oIdx], &errNum);
      ++oIdx;
    }
  }
  if(errNum == WLZ_ERR_NONE)
  {
    aBox.xMin = WLZ_MIN(pBox[0].xMin, pBox[1].xMin) - (int )(maxTran.vtX) + 1;
    aBox.yMin = WLZ_MIN(pBox[0].yMin, pBox[1].yMin) - (int )(maxTran.vtY) + 1;
    aBox.xMax = WLZ_MAX(pBox[0].xMax, pBox[1].xMax) + (int )(maxTran.vtX) + 1;
    aBox.yMax = WLZ_MAX(pBox[0].yMax, pBox[1].yMax) + (int )(maxTran.vtY) + 1;
    aOrg.vtX = aBox.xMin;
    aOrg.vtY = aBox.yMin;
    aSz.vtX = aBox.xMax - aBox.xMin + 1;
    aSz.vtY = aBox.yMax - aBox.yMin + 1;
    (void )AlgBitNextPowerOfTwo((unsigned int *)&(aSz.vtX), aSz.vtX);
    (void )AlgBitNextPowerOfTwo((unsigned int *)&(aSz.vtY), aSz.vtY);
    oIdx = 0;
    while((errNum == WLZ_ERR_NONE) && (oIdx < 2))
    {
      errNum = WlzToArray2D((void ***)&(oAr[oIdx]), pObj[oIdx], aSz, aOrg,
      			    noise, WLZ_GREY_DOUBLE);
      ++oIdx;
    }
  }
  if((dstCCor != NULL) && (errNum == WLZ_ERR_NONE))
  {
    oIdx = 0;
    while((errNum == WLZ_ERR_NONE) && (oIdx < 2))
    {
      WlzArrayStats2D((void **)(oAr[oIdx]), aSz, WLZ_GREY_DOUBLE, NULL, NULL,
		      NULL, &(sSq[oIdx]), NULL, NULL);
      ++oIdx;
    }
  }
#ifdef WLZ_REGCCOR_DEBUG
  if(errNum == WLZ_ERR_NONE)
  {
    FILE	*fP = NULL;
    WlzObject	*cCObjT = NULL;
    
    cCObjT = WlzFromArray2D((void **)(oAr[0]), aSz, aOrg,
			    WLZ_GREY_DOUBLE, WLZ_GREY_DOUBLE,
			    0.0, 1.0, 0, 0, &errNum);
    if(cCObjT)
    {
      if((fP = fopen("oObjT0.wlz", "w")) != NULL)
      {
	(void )WlzWriteObj(fP, cCObjT);
	(void )fclose(fP);
      }
      (void )WlzFreeObj(cCObjT);
    }
  }
  if(errNum == WLZ_ERR_NONE)
  {
    FILE	*fP = NULL;
    WlzObject	*cCObjT = NULL;
    
    cCObjT = WlzFromArray2D((void **)(oAr[1]), aSz, aOrg,
			    WLZ_GREY_DOUBLE, WLZ_GREY_DOUBLE,
			    0.0, 1.0, 0, 0, &errNum);
    if(cCObjT)
    {
      if((fP = fopen("oObjT1.wlz", "w")) != NULL)
      {
	(void )WlzWriteObj(fP, cCObjT);
	(void )fclose(fP);
      }
      (void )WlzFreeObj(cCObjT);
    }
  }
#endif /* WLZ_REGCCOR_DEBUG */
  /* Cross correlate. */
  if(errNum == WLZ_ERR_NONE)
  {
    (void )AlgCrossCorrelate2D(oAr[0], oAr[1], aSz.vtX, aSz.vtY);
    AlgCrossCorrPeakXY(&(tran.vtX), &(tran.vtY), &cCor, oAr[0],
		       aSz.vtX, aSz.vtY, maxTran.vtX, maxTran.vtY);
  }
#ifdef WLZ_REGCCOR_DEBUG
  if(errNum == WLZ_ERR_NONE)
  {
    FILE	*fP = NULL;
    WlzObject	*cCObjT = NULL;
    
    oIdx = 0;
    while((errNum == WLZ_ERR_NONE) && (oIdx < 2))
    {
      (void )WlzGreyStats(pObj[oIdx], NULL, NULL, NULL, NULL, &(sSq[oIdx]),
			  NULL, NULL, &errNum);
      ++oIdx;
    }
    if(errNum == WLZ_ERR_NONE)
    {
      cCObjT = WlzFromArray2D((void **)(oAr[0]), aSz, aOrg,
			      WLZ_GREY_DOUBLE, WLZ_GREY_DOUBLE,
			      0.0,
			      255.0 / (1.0 + (sqrt(sSq[0] * sSq[1]) *
			      		      aSz.vtX * aSz.vtY)),
			      0, 0, &errNum);
    }
    if(cCObjT)
    {
      if((fP = fopen("cCObjT.wlz", "w")) != NULL)
      {
	(void )WlzWriteObj(fP, cCObjT);
	(void )fclose(fP);
      }
      (void )WlzFreeObj(cCObjT);
    }
  }
#endif /* WLZ_REGCCOR_DEBUG */
  for(oIdx = 0; oIdx < 2; ++oIdx)
  {
    (void )WlzFreeObj(oObj[oIdx]);
    (void )WlzFreeObj(pObj[oIdx]);
    AlcDouble2Free(oAr[oIdx]);
  }
  if(errNum == WLZ_ERR_NONE)
  {
    dstTran.vtX = tran.vtX;
    dstTran.vtY = tran.vtY;
    if(dstCCor)
    {
      cCor = cCor / (1.0 + (sqrt(sSq[0] * sSq[1]) * aSz.vtX * aSz.vtY));
      *dstCCor = cCor;
    }
  }
  if(dstErr)
  {
    *dstErr = errNum;
  }
  return(dstTran);
}
Beispiel #3
0
/*!
* \return	Angle of roatation.
* \ingroup	WlzRegistration
* \brief	Polar samples then registers the given 2D domain objects
*               using a frequency domain cross correlation, to find
*               the angle of rotation about the given centre of rotation
*               which has the highest cross correlation value.
*		The rotation is always about the objects cente of mass.
* \param	tObj			The target object. Must have
*                                       been assigned.
* \param	sObj			The source object to be
*                                       registered with target object.
*                                       Must have been assigned.
* \param	initTr			Initial affine transform
*                                       to be applied to the source
*                                       object prior to registration.
* \param	maxRot			Maximum rotation.
* \param	winFn			Window function.
* \param	noise			Use Gaussian noise if non-zero.
* \param	dstErr			Destination error pointer,
*                                       may be NULL.
*/
static double	WlzRegCCorObjs2DRot(WlzObject *tObj, WlzObject *sObj,
				    WlzAffineTransform *initTr, double maxRot,
				    WlzWindowFnType winFn, int noise,
				    WlzErrorNum *dstErr)
{
  int		oIdx,
  		angCnt;
  double	angInc,
  		dstRot = 0.0;
  WlzIBox2	aBox;
  WlzIBox2	oBox[2];
  WlzIVertex2	rot,
  		aSz,
  		aOrg,
		winRad,
		winOrg,
		rotPad,
		rotCentreI;
  double	**oAr[2];
  WlzObject	*oObj[2],
  		*pObj[2],
		*wObj[2];
  WlzErrorNum	errNum = WLZ_ERR_NONE;
  const int	rotCnt = 500;
  const double	distInc = 1.0;

  /* Assign the target and transform source objects. */
  oAr[0] = oAr[1] = NULL;
  oAr[0] = oAr[1] = NULL;
  oObj[0] = oObj[1] = NULL;
  pObj[0] = pObj[1] = NULL;
  wObj[0] = wObj[1] = NULL;
  oObj[0] = WlzAssignObject(tObj, NULL);
  if((initTr == NULL) || WlzAffineTransformIsIdentity(initTr, NULL))
  {
    oObj[1] = WlzAssignObject(sObj, NULL);
  }
  else
  {
    oObj[1] = WlzAssignObject(
    	      WlzAffineTransformObj(sObj, initTr, WLZ_INTERPOLATION_NEAREST,
				   &errNum), NULL);
  }
  if(errNum == WLZ_ERR_NONE)
  {
    oIdx = 0;
    while((errNum == WLZ_ERR_NONE) && (oIdx < 2))
    {
      tObj = WlzAutoCor(oObj[oIdx], &errNum);
      (void )WlzFreeObj(oObj[oIdx]);
      oObj[oIdx] = WlzAssignObject(tObj, NULL);
      ++oIdx;
    }
  }
  /* Compute rectangular to polar transformation. */
  if(errNum == WLZ_ERR_NONE)
  {
    angInc = (2.0 * (maxRot + WLZ_M_PI)) / rotCnt;
    angCnt = (2.0 * WLZ_M_PI) / angInc;
    oIdx = 0;
    while((errNum == WLZ_ERR_NONE) && (oIdx < 2))
    {
      rotCentreI.vtX = 0;
      rotCentreI.vtY = 0;
      if(errNum == WLZ_ERR_NONE)
      {
	pObj[oIdx] = WlzAssignObject(
		     WlzPolarSample(oObj[oIdx], rotCentreI, angInc, distInc,
				    angCnt, 0, &errNum), NULL);
      }
      ++oIdx;
    }
  }
  /* Preproccess the objects using windows or noise. */
  if(errNum == WLZ_ERR_NONE)
  {
    oIdx = 0;
    while((errNum == WLZ_ERR_NONE) && (oIdx < 2))
    {
      oBox[oIdx] = WlzBoundingBox2I(pObj[oIdx], &errNum);
      if(errNum == WLZ_ERR_NONE)
      {
	winOrg.vtX = (oBox[oIdx].xMax + oBox[oIdx].xMin) / 2;
	winOrg.vtY = (oBox[oIdx].yMax + oBox[oIdx].yMin) / 2;
	winRad.vtX = (oBox[oIdx].xMax - oBox[oIdx].xMin) / 2;
	winRad.vtY = (oBox[oIdx].yMax - oBox[oIdx].yMin) / 2;
	wObj[oIdx] = WlzAssignObject(
		     WlzRegCCorPProcessObj2D(pObj[oIdx], winFn,
					     winOrg, winRad, 
					     &errNum), NULL);
      }
      ++oIdx;
    }
  }
  /* Create 2D double arrays from the polar sampled objects. */
  if(errNum == WLZ_ERR_NONE)
  {
    oIdx = 0;
    while((errNum == WLZ_ERR_NONE) && (oIdx < 2))
    {
      oBox[oIdx] = WlzBoundingBox2I(wObj[oIdx], &errNum);
      ++oIdx;
    }
  }
  if(errNum == WLZ_ERR_NONE)
  {
    aBox.xMin = WLZ_MIN(oBox[0].xMin, oBox[1].xMin);
    aBox.yMin = WLZ_MIN(oBox[0].yMin, oBox[1].yMin);
    aBox.xMax = WLZ_MAX(oBox[0].xMax, oBox[1].xMax);
    aBox.yMax = WLZ_MAX(oBox[0].yMax, oBox[1].yMax);
    rotPad.vtX = (aBox.xMax - aBox.xMin) / 2;
    rotPad.vtY = 1 + WLZ_NINT(maxRot / angInc);
    aBox.yMin -= rotPad.vtY;
    aBox.yMax += rotPad.vtY;
    aOrg.vtX = aBox.xMin;
    aOrg.vtY = aBox.yMin;
    aSz.vtX = aBox.xMax - aBox.xMin + 1;
    aSz.vtY = aBox.yMax - aBox.yMin + 1;
    (void )AlgBitNextPowerOfTwo((unsigned int *)&(aSz.vtX), aSz.vtX);
    (void )AlgBitNextPowerOfTwo((unsigned int *)&(aSz.vtY), aSz.vtY);
    oIdx = 0;
    while((errNum == WLZ_ERR_NONE) && (oIdx < 2))
    {
      errNum = WlzToArray2D((void ***)&(oAr[oIdx]), wObj[oIdx], aSz, aOrg,
      			    noise, WLZ_GREY_DOUBLE);
      ++oIdx;
    }
  }
#ifdef WLZ_REGCCOR_DEBUG
  if(errNum == WLZ_ERR_NONE)
  {
    FILE	*fP = NULL;
    WlzObject	*aObj = NULL;

    aObj = WlzFromArray2D((void **)(oAr[0]), aSz, aOrg,
    			  WLZ_GREY_DOUBLE, WLZ_GREY_DOUBLE, 0.0, 1.0,
			  0, 0, &errNum);
    if((fP = fopen("cObjR0.wlz", "w")) != NULL) 
    {
      (void )WlzWriteObj(fP, aObj); 
      (void )fclose(fP);
    }
    WlzFreeObj(aObj);
    aObj = WlzFromArray2D((void **)(oAr[1]), aSz, aOrg,
    			  WLZ_GREY_DOUBLE, WLZ_GREY_DOUBLE, 0.0, 1.0,
			  0, 0, &errNum);
    if((fP = fopen("cObjR1.wlz", "w")) != NULL)
    {
      (void )WlzWriteObj(fP, aObj);
      (void )fclose(fP);
    }
    WlzFreeObj(aObj);
  }
#endif /* WLZ_REGCCOR_DEBUG */
  /* Cross correlate. */
  if(errNum == WLZ_ERR_NONE)
  {
    (void )AlgCrossCorrelate2D(oAr[0], oAr[1], aSz.vtX, aSz.vtY);
    AlgCrossCorrPeakXY(&(rot.vtX), &(rot.vtY), NULL, oAr[0],
		       aSz.vtX, aSz.vtY, rotPad.vtX, rotPad.vtY);
    dstRot = rot.vtY * angInc;
    /* dstRot = -(rot.vtY) * angInc; */
  }
#ifdef WLZ_REGCCOR_DEBUG
  if(errNum == WLZ_ERR_NONE)
  {
    double	sSq[2];
    FILE	*fP = NULL;
    WlzObject	*cCObjR = NULL;
    
    oIdx = 0;
    while((errNum == WLZ_ERR_NONE) && (oIdx < 2))
    {
      (void )WlzGreyStats(wObj[oIdx], NULL, NULL, NULL, NULL, &(sSq[oIdx]),
			  NULL, NULL, &errNum);
      ++oIdx;
    }
    if(errNum == WLZ_ERR_NONE)
    {
      cCObjR = WlzFromArray2D((void **)(oAr[0]), aSz, aOrg,
			      WLZ_GREY_DOUBLE, WLZ_GREY_DOUBLE,
			      0.0,
			      255.0 / (1.0 + (sqrt(sSq[0] * sSq[1]) *
			      		      aSz.vtX * aSz.vtY)),
			      0, 0, &errNum);
    }
    if(cCObjR)
    {
      if((fP = fopen("cCObjR.wlz", "w")) != NULL)
      {
	(void )WlzWriteObj(fP, cCObjR);
	(void )fclose(fP);
      }
      (void )WlzFreeObj(cCObjR);
    }
  }
#endif /* WLZ_REGCCOR_DEBUG */
  for(oIdx = 0; oIdx < 2; ++oIdx)
  {
    (void )WlzFreeObj(oObj[oIdx]);
    (void )WlzFreeObj(pObj[oIdx]);
    (void )WlzFreeObj(wObj[oIdx]);
    AlcDouble2Free(oAr[oIdx]);
  }
  if(dstErr)
  {
    *dstErr = errNum;
  }
  return(dstRot);
}
Beispiel #4
0
/*!
* \return	Autocorrelated object or NULL on error.
* \ingroup	WlzRegistration
* \brief	Computes the autocorrelation of the given 2D object, see
*		WlzAutoCor().
* \param	gObj			Given object.
* \param	dstErr			Destination error pointer, may be NULL.
*/
static WlzObject *WlzAutoCor2D(WlzObject *gObj, WlzErrorNum *dstErr)
{
  WlzIVertex2	aSz,
		wSz,
		aOrg,
  		wOrg;
  WlzIBox2	box;
  double	**wAr = NULL,
  		**aAr = NULL;
  WlzObject     *aObj = NULL;
  WlzErrorNum   errNum = WLZ_ERR_NONE;

  if(gObj == NULL)
  {
    errNum = WLZ_ERR_OBJECT_NULL;
  }
  else if(gObj->domain.core == NULL)
  {
    errNum = WLZ_ERR_DOMAIN_NULL;
  }
  else if(gObj->values.core == NULL)
  {
    errNum = WLZ_ERR_VALUES_NULL;
  }
  if(errNum == WLZ_ERR_NONE)
  {
    box = WlzBoundingBox2I(gObj, &errNum);
    aSz.vtX = box.xMax - box.xMin + 1;
    aSz.vtY = box.yMax - box.yMin + 1;
    /* Make sure aSz is even in x and y. */
    if((aSz.vtX & 1) != 0)
    {
      aSz.vtX += 1;
    }
    if((aSz.vtY & 1) != 0)
    {
      aSz.vtY += 1;
    }
    wOrg.vtX = box.xMin - (aSz.vtX / 2);
    wOrg.vtY = box.yMin - (aSz.vtY / 2);
    wSz.vtX = aSz.vtX * 2;
    wSz.vtY = aSz.vtY * 2;
    (void )AlgBitNextPowerOfTwo((unsigned int *)&(wSz.vtX), wSz.vtX);
    (void )AlgBitNextPowerOfTwo((unsigned int *)&(wSz.vtY), wSz.vtY);
    errNum = WlzToArray2D((void ***)&wAr, gObj, wSz, wOrg, 0, WLZ_GREY_DOUBLE);
  }
  if(errNum == WLZ_ERR_NONE)
  {
    (void )AlgAutoCorrelate2D(wAr, wSz.vtX, wSz.vtY);
    if(AlcDouble2Malloc(&aAr, aSz.vtY, aSz.vtX) != ALC_ER_NONE)
    {
      errNum = WLZ_ERR_MEM_ALLOC;
    }
  }
  if(errNum == WLZ_ERR_NONE)
  {
    WlzAutoCorRearrange2D(aAr, aSz, wAr, wSz);
    aOrg.vtX = -(aSz.vtX / 2);
    aOrg.vtY = -(aSz.vtY / 2);
    aObj = WlzFromArray2D((void **)aAr, aSz, aOrg,
                          WLZ_GREY_DOUBLE, WLZ_GREY_DOUBLE, 0.0, 1.0,
			  0, 0, &errNum);
  }
  if(errNum != WLZ_ERR_NONE)
  {
    if(aObj != NULL)
    {
      (void )WlzFreeObj(aObj);
    }
  }
  (void )Alc2Free((void **)wAr);
  (void )Alc2Free((void **)aAr);
  if(dstErr)
  {
    *dstErr = errNum;
  }
  return(aObj);
}
/*! 
* \return       New object with the rojection.
* \ingroup      WlzTransform
* \brief        Use the view transform to define a projection from
*		3D to 2D and then project the object onto this plane.
*		The object supplied to this function must be a 3D
*		spatial domain object (WLZ_3D_DOMAINOBJ) with either
*		no values or for integration WLZ_GREY_UBYTE values.
*		Integration will assign each output pixel the sum of
*		all input voxels mapped via either the domain density
*		or the voxel density.
*		The integration is controled by the integrate parameter
*		with valid values:
*		WLZ_PROJECT_INT_MODE_NONE - a "shadow domain" without values
*               is computed,
*		WLZ_PROJECT_INT_MODE_DOMAIN - the voxels of the domain are
*		integrated using
*		\f[
		p = \frac{1}{255} n d
                \f]
*		WLZ_PROJECT_INT_MODE_VALUES - the voxel values are integrated
*		using
*		\f[
		p = \frac{1}{255} \sum{l\left[v\right]}.
		\f]
*		Where
*		  \f$p\f$ is the projected image value,
*		  \f$n\f$ is the number of voxels projected for \f$p\f$,
*		  \f$d\f$ is the density of domain voxels,
*		  \f$l\f$ is the voxel value density look up table and
*		  \f$v\f$ is a voxel value.
* \param	obj			The given object.
* \param	vStr			Given view structure defining the
* 					projection plane.
* \param	intMod			This may take three values:
* 					WLZ_PROJECT_INT_MODE_NONE,
* 					WLZ_PROJECT_INT_MODE_DOMAIN or
* 					WLZ_PROJECT_INT_MODE_VALUES.
* \param	denDom			Density of domain voxels this value
* 					is not used unless the integration
* 					mode is WLZ_PROJECT_INT_MODE_DOMAIN.
* \param	denVal			Density look up table for object
* 					voxel density values which must be
* 					an array of 256 values. This may be
* 					NULL if the integration mode is not
* 					WLZ_PROJECT_INT_MODE_VALUES.
* \param	depth			If greater than zero, the projection
* 					depth perpendicular to the viewing
* 					plane.
* \param	dstErr			Destination error pointer, may be NULL.
*/
WlzObject 	*WlzProjectObjToPlane(WlzObject *obj,
  				WlzThreeDViewStruct *vStr,
  				WlzProjectIntMode intMod,
				WlzUByte denDom, WlzUByte *denVal,
				double depth, WlzErrorNum *dstErr)
{
  int		nThr = 1,
		itvVal = 0;
  WlzIVertex2	prjSz;
  WlzIBox2	prjBox = {0};
  double	pln[4];
  WlzObject	*bufObj = NULL,
  		*prjObj = NULL;
  WlzThreeDViewStruct *vStr1 = NULL;
  double	**vMat = NULL;
  WlzValues	nullVal;
  WlzErrorNum	errNum = WLZ_ERR_NONE;
  WlzAffineTransform *rescaleTr = NULL;
  WlzGreyValueWSpace **gVWSp = NULL;
  void		***prjAry = NULL;
  const double	eps = 0.000001;
#ifdef WLZ_DEBUG_PROJECT3D_TIME
struct timeval	times[3];
#endif /* WLZ_DEBUG_PROJECT3D_TIME */

  nullVal.core = NULL;
  if(obj == NULL)
  {
    errNum = WLZ_ERR_OBJECT_NULL;
  }
  else if(obj->type != WLZ_3D_DOMAINOBJ)
  {
    errNum = WLZ_ERR_OBJECT_TYPE;
  }
  else if(obj->domain.core == NULL)
  {
    errNum = WLZ_ERR_DOMAIN_NULL;
  }
  else if(obj->domain.core->type != WLZ_PLANEDOMAIN_DOMAIN)
  {
    errNum = WLZ_ERR_DOMAIN_TYPE;
  }
  else if(vStr == NULL)
  {
    errNum = WLZ_ERR_TRANSFORM_NULL;
  }
  else if((intMod == WLZ_PROJECT_INT_MODE_VALUES) &&
          (obj->values.core == NULL))
  {
    errNum = WLZ_ERR_VALUES_NULL;
  }
#ifdef WLZ_DEBUG_PROJECT3D_TIME
  gettimeofday(times + 0, NULL);
#endif /* WLZ_DEBUG_PROJECT3D_TIME */
  /* Create new view transform without voxel scaling. The voxel scaling
   * is done after the projection. */
  if(errNum == WLZ_ERR_NONE)
  {
    if((vStr1 = WlzMake3DViewStruct(WLZ_3D_VIEW_STRUCT, &errNum)) != NULL)
    {
      vStr1->fixed = vStr->fixed;
      vStr1->theta = vStr->theta;
      vStr1->phi = vStr->phi;
      vStr1->zeta = vStr->zeta;
      vStr1->dist = vStr->dist;
      vStr1->scale = vStr->scale;
      vStr1->voxelSize[0] = 1.0;
      vStr1->voxelSize[1] = 1.0;
      vStr1->voxelSize[2] = 1.0;
      vStr1->voxelRescaleFlg = 0;
      vStr1->interp = vStr->interp;
      vStr1->view_mode = vStr->view_mode;
      vStr1->up = vStr->up;
      vStr1->initialised = WLZ_3DVIEWSTRUCT_INIT_NONE;
      vMat = vStr1->trans->mat;
      errNum = WlzInit3DViewStructAffineTransform(vStr1);
      if(errNum == WLZ_ERR_NONE)
      {
        errNum = Wlz3DViewStructTransformBB(obj, vStr1);
      }
      if(errNum != WLZ_ERR_NONE)
      {
	WlzFree3DViewStruct(vStr1);
	vStr1 = NULL;
      }
    }
  }
  /* Compute bounding box of the projection. */
  if(errNum == WLZ_ERR_NONE)
  {
    prjBox.xMin = WLZ_NINT(vStr1->minvals.vtX);
    prjBox.yMin = WLZ_NINT(vStr1->minvals.vtY);
    prjBox.xMax = WLZ_NINT(vStr1->maxvals.vtX);
    prjBox.yMax = WLZ_NINT(vStr1->maxvals.vtY);
    prjSz.vtX = prjBox.xMax - prjBox.xMin + 1;
    prjSz.vtY = prjBox.yMax - prjBox.yMin + 1;
  }
  /* Compute post projection scaling. */
  if((errNum == WLZ_ERR_NONE) && (vStr->voxelRescaleFlg != 0))
  {
    WlzIBox2	sBox;
    WlzIVertex2 sSz;
    WlzThreeDViewStruct *vStr2;

    vStr2 = WlzMake3DViewStruct(WLZ_3D_VIEW_STRUCT, &errNum);
    if(errNum == WLZ_ERR_NONE)
    {
      vStr2->fixed = vStr->fixed;
      vStr2->theta = vStr->theta;
      vStr2->phi = vStr->phi;
      vStr2->zeta = vStr->zeta;
      vStr2->dist = vStr->dist;
      vStr2->scale = vStr->scale;
      vStr2->voxelSize[0] = vStr->voxelSize[0];
      vStr2->voxelSize[1] = vStr->voxelSize[1];
      vStr2->voxelSize[2] = vStr->voxelSize[2];
      vStr2->voxelRescaleFlg = vStr->voxelRescaleFlg;
      vStr2->interp = vStr->interp;
      vStr2->view_mode = vStr->view_mode;
      vStr2->up = vStr->up;
      vStr2->initialised = WLZ_3DVIEWSTRUCT_INIT_NONE;
      errNum = WlzInit3DViewStructAffineTransform(vStr2);
      if(errNum == WLZ_ERR_NONE)
      {
        errNum = Wlz3DViewStructTransformBB(obj, vStr2);
      }
      if(errNum == WLZ_ERR_NONE)
      {
	sBox.xMin = WLZ_NINT(vStr2->minvals.vtX);
	sBox.yMin = WLZ_NINT(vStr2->minvals.vtY);
	sBox.xMax = WLZ_NINT(vStr2->maxvals.vtX);
	sBox.yMax = WLZ_NINT(vStr2->maxvals.vtY);
	sSz.vtX = sBox.xMax - sBox.xMin + 1;
	sSz.vtY = sBox.yMax - sBox.yMin + 1;
        rescaleTr = WlzMakeAffineTransform(WLZ_TRANSFORM_2D_AFFINE, &errNum);
      }
      if(errNum == WLZ_ERR_NONE)
      {
        double	**m;

	m = rescaleTr->mat;
	m[0][0] = (sSz.vtX * eps) / (prjSz.vtX * eps);
	m[1][1] = (sSz.vtY * eps) / (prjSz.vtY * eps);
	m[0][2] = sBox.xMin - WLZ_NINT(m[0][0] * prjBox.xMin);
	m[1][2] = sBox.yMin - WLZ_NINT(m[1][1] * prjBox.yMin);
      }
      (void )WlzFree3DViewStruct(vStr2);
    }
  }
  /* Compute plane equation, used to clip intervals if depth was given. */
  if((errNum == WLZ_ERR_NONE) && (depth > eps))
  {
    Wlz3DViewGetPlaneEqn(vStr1, pln + 0, pln + 1, pln + 2, pln + 3);
  }
  /* Create rectangular projection array buffers, one for each thread,
   * also if integrating values create a grey value workspace per thread. */
  if(errNum == WLZ_ERR_NONE)
  {
    int		idB;

#ifdef _OPENMP
#pragma omp parallel
    {
#pragma omp master
      {
        nThr = omp_get_num_threads();
      }
    }
#endif
    if((prjAry = (void ***)AlcCalloc(nThr, sizeof(void **))) == NULL)
    {
      errNum = WLZ_ERR_MEM_ALLOC;
    }
    else
    {
      if(intMod == WLZ_PROJECT_INT_MODE_NONE)
      {
	for(idB = 0; idB < nThr; ++idB)
	{
	  if(AlcUnchar2Calloc((WlzUByte ***)&(prjAry[idB]),
			      prjSz.vtY, prjSz.vtX) != ALC_ER_NONE)
	  {
	    errNum = WLZ_ERR_MEM_ALLOC;
	    break;
	  }
	}
      }
      else
      {
	for(idB = 0; idB < nThr; ++idB)
	{
	  if(AlcInt2Calloc((int ***)&(prjAry[idB]),
			   prjSz.vtY, prjSz.vtX) != ALC_ER_NONE)
	  {
	    errNum = WLZ_ERR_MEM_ALLOC;
	    break;
	  }
	}
      }
    }
    if((errNum == WLZ_ERR_NONE) &&
       (intMod == WLZ_PROJECT_INT_MODE_VALUES))
    {
      itvVal = (WlzGreyTableIsTiled(obj->values.core->type) == 0);
      if(itvVal == 0)
      {
	if((gVWSp = AlcCalloc(nThr, sizeof(WlzGreyValueWSpace *))) == NULL)
	{
	  errNum = WLZ_ERR_MEM_ALLOC;
	}
	else
	{
	  for(idB = 0; idB < nThr; ++idB) 
	  {
	    gVWSp[idB] = WlzGreyValueMakeWSp(obj, &errNum);
	    if(gVWSp[idB]->gType != WLZ_GREY_UBYTE)
	    {
	      errNum = WLZ_ERR_GREY_TYPE;
	      break;
	    }
	  }
	}
      }
    }
  }
  /* Scan through the 3D domain setting value in the projection array. */
  if(errNum == WLZ_ERR_NONE)
  {
    int		pIdx,
    		pCnt;
    WlzDomain	*doms;
    WlzValues	*vals = NULL;

    doms = obj->domain.p->domains;
    if(itvVal)
    {
      vals = obj->values.vox->values;
    }
    pCnt = obj->domain.p->lastpl - obj->domain.p->plane1 + 1;
#ifdef _OPENMP
#pragma omp parallel for
#endif
    for(pIdx =  0; pIdx < pCnt; ++pIdx)
    {
      int	thrId = 0;

      if((errNum == WLZ_ERR_NONE) && (doms[pIdx].core != NULL))
      {
	WlzObject   *obj2;
	WlzGreyWSpace gWSp;
	WlzIntervalWSpace iWSp;
	WlzErrorNum errNum2 = WLZ_ERR_NONE;

#ifdef _OPENMP
	thrId = omp_get_thread_num();
#endif
        obj2 = WlzMakeMain(WLZ_2D_DOMAINOBJ, doms[pIdx],
	                   (vals)? vals[pIdx]: nullVal,
	                   NULL, NULL, &errNum2);
        if(errNum2 == WLZ_ERR_NONE)
	{
	  if(itvVal)
	  {
	    errNum2 = WlzInitGreyScan(obj2, &iWSp, &gWSp);
	  }
	  else
	  {
	    errNum2 = WlzInitRasterScan(obj2, &iWSp, WLZ_RASTERDIR_ILIC);
	  }
	}
        if(errNum2 == WLZ_ERR_NONE)
	{
	  double      plnZ,
	  	      vMZX,
	  	      vMZY;
	  WlzIVertex3 p0,
	    	      p1;

	  p0.vtZ = p1.vtZ = obj->domain.p->plane1 + pIdx;
	  vMZX = (vMat[0][2] * p0.vtZ) + vMat[0][3] - prjBox.xMin;
	  vMZY = (vMat[1][2] * p0.vtZ) + vMat[1][3] - prjBox.yMin;
	  plnZ = (pln[2] * p0.vtZ) + pln[3];
	  while(((itvVal == 0) &&
	        ((errNum2 = WlzNextInterval(&iWSp)) == WLZ_ERR_NONE)) ||
	        ((itvVal != 0) &&
	        ((errNum2 = WlzNextGreyInterval(&iWSp)) == WLZ_ERR_NONE)))
	  {
	    int		skip = 0;
	    WlzDVertex2 q0,
	    		q1;

            p0.vtX = iWSp.lftpos;
	    p1.vtX = iWSp.rgtpos;
	    p0.vtY = p1.vtY = iWSp.linpos;
	    if(depth > eps)
	    {
	      int	c;
	      double	d0,
	      		d1,
			plnYZ;

	      /* Clip the 3D line segment p0,p1 using the plane equation. */
	      plnYZ = (pln[1] * p0.vtY) + plnZ;
	      d0 = (pln[0] * p0.vtX) + plnYZ;
	      d1 = (pln[0] * p1.vtX) + plnYZ;
	      c = ((d1 >  depth) << 3) | ((d0 >  depth) << 2) |
		  ((d1 < -depth) << 1) |  (d0 < -depth);
	      if(c)
	      {
		if((c == 3) || (c == 12)) /* 00-- or ++00 */
		{
		  /* Both out of range, so don't render. */
		  skip = 1;
		}
		else
		{
		  if(fabs(pln[0]) > eps)
		  {
		    double	plnX;

		    plnX = -1.0 / pln[0];
		    if((c &  1) != 0)      /* x0x- */
		    {
		      p0.vtX = plnX * (plnYZ + depth);
		    }
		    else if((c &  4) != 0) /* x+x0 */
		    {
		      p0.vtX = plnX * (plnYZ - depth);
		    }
		    if((c &  2) != 0)      /* 0x-x */
		    {
		      p1.vtX = plnX * (plnYZ + depth);
		    }
		    else if((c &  8) != 0) /* +x0x */
		    {
		      p1.vtX = plnX * (plnYZ - depth);
		    }
		  }
		}
	      }
	    }
	    if(skip == 0)
	    {
	      q0.vtX = (vMat[0][0] * p0.vtX) + (vMat[0][1] * p0.vtY) + vMZX;
	      q0.vtY = (vMat[1][0] * p0.vtX) + (vMat[1][1] * p0.vtY) + vMZY;
	      q1.vtX = (vMat[0][0] * p1.vtX) + (vMat[0][1] * p1.vtY) + vMZX;
	      q1.vtY = (vMat[1][0] * p1.vtX) + (vMat[1][1] * p1.vtY) + vMZY;
	      switch(intMod)
	      {
		case WLZ_PROJECT_INT_MODE_NONE:
		  {
		    WlzIVertex2 u0,
				u1;

		    WLZ_VTX_2_NINT(u0, q0);
		    WLZ_VTX_2_NINT(u1, q1);
		    WlzProjectObjLine((WlzUByte **)(prjAry[thrId]), u0, u1);
		  }
		  break;
		case WLZ_PROJECT_INT_MODE_DOMAIN:
		  {
		    int	        np,
				nq;
		    WlzDVertex3 dq;
		    WlzIVertex2 u0,
				u1;

		    WLZ_VTX_2_NINT(u0, q0);
		    WLZ_VTX_2_NINT(u1, q1);
		    WLZ_VTX_2_SUB(dq, q0, q1);
		    np = denDom * (iWSp.rgtpos - iWSp.lftpos + 1);
		    nq = (int )ceil(WLZ_VTX_2_LENGTH(dq) + eps);
		    WlzProjectObjLineDom((int **)(prjAry[thrId]), np / nq,
					 u0, u1);
		  }
		  break;
		case WLZ_PROJECT_INT_MODE_VALUES:
		  if(itvVal)
		  {
		    WlzProjectObjLineVal((int **)(prjAry[thrId]), denVal,
					 gWSp.u_grintptr.ubp, NULL,
					 vMat, vMZX, vMZY, p0, p1);
		  }
		  else
		  {
		    WlzProjectObjLineVal((int **)(prjAry[thrId]), denVal,
					 NULL, gVWSp[thrId],
					 vMat, vMZX, vMZY, p0, p1);
		  }
		  break;
	      }
	    }
	  }
	  (void )WlzEndGreyScan(&iWSp, &gWSp);
	  if(errNum2 == WLZ_ERR_EOO)
	  {
	    errNum2 = WLZ_ERR_NONE;
	  }
	}
	(void )WlzFreeObj(obj2);
	if(errNum2 != WLZ_ERR_NONE)
	{
#ifdef _OPENMP
#pragma omp critical
	  {
#endif
	    if(errNum == WLZ_ERR_NONE)
	    {
	      errNum = errNum2;
	    }
#ifdef _OPENMP
	  }
#endif
	}
      }
    }
  }
  /* Free grey value workspaces if they were created. */
  if(gVWSp)
  {
    int		idB;

    for(idB = 0; idB < nThr; ++idB) 
    {
      WlzGreyValueFreeWSp(gVWSp[idB]);
    }
    AlcFree(gVWSp);
  }
  if(errNum == WLZ_ERR_NONE)
  {
    int		idB;
    size_t	idC,
    		bufSz;
    WlzGreyP	buf0,
      		buf1;
    WlzIVertex2	prjOrg;

    prjOrg.vtX = prjBox.xMin;
    prjOrg.vtY = prjBox.yMin;
    bufSz = prjSz.vtX * prjSz.vtY;
    for(idB = 1; idB < nThr; ++idB)
    {
      if(intMod == WLZ_PROJECT_INT_MODE_NONE)
      {
        buf0.ubp = ((WlzUByte ***)(prjAry))[0][0],
	buf1.ubp = ((WlzUByte ***)(prjAry))[idB][0];
	for(idC = 0; idC < bufSz; ++idC)
	{
	  buf0.ubp[idC] += buf1.ubp[idC];
	}
      }
      else
      {
        buf0.inp = ((int ***)(prjAry))[0][0],
	buf1.inp = ((int ***)(prjAry))[idB][0];
	for(idC = 0; idC < bufSz; ++idC)
	{
	  buf0.inp[idC] += buf1.inp[idC];
	}
      }
    }
    switch(intMod != WLZ_PROJECT_INT_MODE_NONE)
    {
      buf0.inp = ((int ***)(prjAry))[0][0];
      for(idC = 0; idC < bufSz; ++idC)
      {
	buf0.inp[idC] /= 256;
      }
    }
    if(intMod == WLZ_PROJECT_INT_MODE_NONE)
    {
      bufObj = WlzAssignObject(
	       WlzFromArray2D((void **)(prjAry[0]), prjSz, prjOrg,
			      WLZ_GREY_UBYTE, WLZ_GREY_UBYTE,
			      0.0, 1.0, 1, 0, &errNum), NULL);
    }
    else
    {
      bufObj = WlzAssignObject(
	       WlzFromArray2D((void **)(prjAry[0]), prjSz, prjOrg,
			      WLZ_GREY_INT, WLZ_GREY_INT,
			      0.0, 1.0, 1, 0, &errNum), NULL);
    }
  }
  /* Free the projection array(s). */
  if(prjAry)
  {
    int		idB;

    for(idB = 0; idB < nThr; ++idB)
    {
      (void )Alc2Free((prjAry[idB]));
    }
    AlcFree(prjAry);
  }
  /* Make return object using threshold. */
  if(errNum == WLZ_ERR_NONE)
  {
    WlzPixelV	tV;
    WlzObject	*tObj = NULL;

    tV.type = WLZ_GREY_UBYTE;
    tV.v.ubv = 1;
    tObj = WlzAssignObject(
	   WlzThreshold(bufObj, tV, WLZ_THRESH_HIGH, &errNum), NULL);
    if(tObj)
    {
      if(intMod == WLZ_PROJECT_INT_MODE_NONE)
      {
	prjObj = WlzMakeMain(tObj->type, tObj->domain, nullVal,
			     NULL, NULL, &errNum);
      }
      else
      {
	prjObj = WlzMakeMain(tObj->type, tObj->domain, tObj->values,
			     NULL, NULL, &errNum);
      }
    }
    (void )WlzFreeObj(tObj);
  }
  (void )WlzFreeObj(bufObj);
  (void )WlzFree3DViewStruct(vStr1);
  /* Scale image. */
  if(rescaleTr != NULL)
  {
    if(errNum == WLZ_ERR_NONE)
    {
      WlzObject	*tObj = NULL;

      tObj = WlzAffineTransformObj(prjObj, rescaleTr, 
				   WLZ_INTERPOLATION_NEAREST, &errNum);
      
      (void )WlzFreeObj(prjObj);
      prjObj = tObj;
    }
    (void )WlzFreeAffineTransform(rescaleTr);
  }
#ifdef WLZ_DEBUG_PROJECT3D_TIME
  gettimeofday(times + 1, NULL);
  ALC_TIMERSUB(times + 1, times + 0, times + 2);
  (void )fprintf(stderr, "WlzGetProjectionFromObject: Elapsed time = %g\n",
                 times[2].tv_sec + (0.000001 * times[2].tv_usec));
#endif /* WLZ_DEBUG_PROJECT3D_TIME */
  if(dstErr)
  {
    *dstErr = errNum;
  }
  return(prjObj);
}