Beispiel #1
0
/*!
* \return	Sampled object.
* \ingroup	WlzMorphologyOps
* \brief	Samples the scaled distance object, interpolating the distance
* 		values.
* \param	obj			Given object to be sampled.
* \param	dim			Dimension either 2D or 3D.
* \param	scale			Scale factor.
* \param	dstErr			Destination error pointer, may be NULL.
*/
static WlzObject *WlzDistSample(WlzObject *obj, int dim, double scale,
    			        WlzErrorNum *dstErr)
{
  WlzObject	*sObj = NULL;
  WlzAffineTransform *tr = NULL;
  WlzErrorNum	errNum = WLZ_ERR_NONE;

  if(scale < 1.0)
  {
    errNum = WLZ_ERR_PARAM_DATA;
  }
  else
  {
    scale = 1.0 / scale;
    tr = (dim == 2)?
	 WlzAffineTransformFromScale(WLZ_TRANSFORM_2D_AFFINE,
				     scale, scale, 0.0, &errNum):
	 WlzAffineTransformFromScale(WLZ_TRANSFORM_3D_AFFINE,
				     scale, scale, scale, &errNum);
  }
  if(errNum == WLZ_ERR_NONE)
  {
    sObj = WlzAffineTransformObj(obj, tr, WLZ_INTERPOLATION_LINEAR,
				 &errNum);
  }
  (void )WlzFreeAffineTransform(tr);
  if(dstErr)
  {
    *dstErr = errNum;
  }
  return(sObj);
}
/*!
* \return	2D reference section cut from the reference object.
* \brief	Cuts a 2D reference section from the given reference object
*		which may either be a 2D or 3D object. If the given object
*		is 2D then it i shifted to respect the given 3D view structures
*		fixed point.
* \param	gRefObj			Given reference object.
* \param	view			Given 3D view data structure.
* \param	interp			Required interpolation.
* \param	dstErr			Destination error pointer, may be NULL.
*/
static WlzObject *WlzMatchICPPlaneGetSection(WlzObject *gRefObj,
				WlzThreeDViewStruct *view,
				WlzInterpolationType interp,
				WlzErrorNum *dstErr)
{
  WlzObject	*refObj2D = NULL;
  WlzAffineTransform *tr = NULL;
  WlzErrorNum	errNum = WLZ_ERR_NONE;
  
  if(gRefObj == NULL)
  {
    errNum = WLZ_ERR_OBJECT_NULL;
  }
  else
  {
    switch(gRefObj->type)
    {
      case WLZ_2D_DOMAINOBJ:
	tr = WlzAffineTransformFromTranslation(WLZ_TRANSFORM_2D_AFFINE,
				-(view->fixed.vtX), -(view->fixed.vtY), 0.0,
				&errNum);
	if(errNum == WLZ_ERR_NONE)
	{
          refObj2D = WlzAffineTransformObj(gRefObj, tr, interp, &errNum);
	}
	(void )WlzFreeAffineTransform(tr);
	break;
      case WLZ_3D_DOMAINOBJ:
        refObj2D = WlzGetSectionFromObject(gRefObj, view, interp, &errNum);
	break;
      default:
        errNum = WLZ_ERR_OBJECT_TYPE;
	break;
    }
  }
  if(dstErr)
  {
    *dstErr = errNum;
  }
  return(refObj2D);
}
Beispiel #3
0
int             main(int argc, char **argv)
{
  int		tI,
		idN,
  		option,
		con = WLZ_0_CONNECTED,
		nLo = 0,
		nHi = 0,
		maxSep = 1024,
		nObj = 0,
  		ok = 1,
		usage = 0;
  char		tC;
  double  	tD,
		mrkMass = 1.0,
  		rad = 0.0;
  int		tR[4];
  WlzPixelV	gV,
  		bV;
  WlzBlobMark	mrk = WLZ_BLOBMARK_CIRCLE;
  WlzObject     *inObj = NULL,
  		*outObj = NULL,
		*mrkObj = NULL;
  WlzObject	**lObj = NULL;
  FILE		*fP = NULL;
  char 		*inObjFileStr,
  		*outObjFileStr;
  WlzErrorNum	errNum = WLZ_ERR_NONE;
  const char	*errMsg;
  static char	optList[] = "c:g:G:hm:n:N:o:r:x:",
  		fileStrDef[] = "-";

  opterr = 0;
  memset(&gV, 0, sizeof(WlzPixelV));
  bV.type = WLZ_GREY_UBYTE;
  bV.v.ubv = 0;
  gV.type = WLZ_GREY_ERROR;
  inObjFileStr = fileStrDef;
  outObjFileStr = fileStrDef;
  while((usage == 0) && ((option = getopt(argc, argv, optList)) != -1))
  {
    switch(option)
    {
      case 'c':
        if(sscanf(optarg, "%d", &tI) != 1)
	{
	  usage = 1;
	}
	else
	{
	  switch(tI)
	  {
	    case  4:
	      con = WLZ_4_CONNECTED;
	      break;
	    case  6:
	      con = WLZ_6_CONNECTED;
	      break;
	    case  8:
	      con = WLZ_8_CONNECTED;
	      break;
	    case 18:
	      con = WLZ_18_CONNECTED;
	      break;
	    case 26:
	      con = WLZ_26_CONNECTED;
	      break;
	    default:
	      usage = 1;
	      break;
	  }
	}
	break;
      case 'g':
        switch(gV.type)
	{
	  case WLZ_GREY_UBYTE:
	    if((sscanf(optarg, "%d", &tI) != 1) ||
	       (tI < 0) || (tI > 255))
	    {
	      usage = 1;
	    }
	    else
	    {
	      gV.v.ubv = tI;
	    }
	    break;
	  case WLZ_GREY_SHORT:
	    if((sscanf(optarg, "%d", &tI) != 1) ||
	       (tI < SHRT_MIN) || (tI > SHRT_MAX))
	    {
	      usage = 1;
	    }
	    else
	    {
	      gV.v.shv = tI;
	    }
	    break;
	  case WLZ_GREY_INT:
	    if(sscanf(optarg, "%d", &tI) != 1)
	    {
	      usage = 1;
	    }
	    else
	    {
	      gV.v.inv = tI;
	    }
	    break;
	  case WLZ_GREY_FLOAT:
	    if((sscanf(optarg, "%lg", &tD) != 1) ||
	       (tD < -(FLT_MAX)) || (tD > FLT_MAX))
	    {
	      usage = 1;
	    }
	    else
	    {
	      gV.v.flv = tD;
	    }
	    break;
	  case WLZ_GREY_DOUBLE:
	    if(sscanf(optarg, "%lg", &tD) != 1)
	    {
	      usage = 1;
	    }
	    else
	    {
	      gV.v.dbv = tD;
	    }
	    break;
	  case WLZ_GREY_RGBA:
	    tR[3] = 255;
	    tR[0] = tR[1] = tR[2] = 0;
	    if((sscanf(optarg, "%d,%d,%d,%d",
	               &(tR[0]), &(tR[1]), &(tR[2]), &(tR[3])) == 0) ||
	       (tR[0] < 0) || (tR[0] > 255) ||
	       (tR[1] < 0) || (tR[1] > 255) ||
	       (tR[2] < 0) || (tR[2] > 255) ||
	       (tR[3] < 0) || (tR[3] > 255))
	    {
	      usage = 1;
	    }
	    else
	    {
	      WLZ_RGBA_RGBA_SET(gV.v.rgbv, tR[0], tR[1], tR[2], tR[3]);
	    }
	    break;
	  default:
	    usage = 1;
	    break;
	}
	break;
      case 'G':
        if(sscanf(optarg, "%c", &tC) != 1)
	{
	  usage = 1;
	}
	switch(tC)
	{
	  case 'v':
	    gV.type = WLZ_GREY_ERROR;
	    break;
	  case 'u':
	    gV.type = WLZ_GREY_UBYTE;
	    break;
	  case 's':
	    gV.type = WLZ_GREY_SHORT;
	    break;
	  case 'i':
	    gV.type = WLZ_GREY_INT;
	    break;
	  case 'f':
	    gV.type = WLZ_GREY_FLOAT;
	    break;
	  case 'd':
	    gV.type = WLZ_GREY_DOUBLE;
	    break;
	  case 'r':
	    gV.type = WLZ_GREY_RGBA;
	    break;
	  default:
	    usage = 1;
	    break;
	}
	break;
      case 'm':
        if((sscanf(optarg, "%d", &tI) != 1) ||
	   ((tI != WLZ_BLOBMARK_CIRCLE) && (tI != WLZ_BLOBMARK_SQUARE)))
	{
	  usage = 1;
	}
	else
	{
	  mrk = (WlzBlobMark )tI;
	}
	break;
      case 'n':
        if((sscanf(optarg, "%d", &nLo) != 1) || (nLo < 0))
	{
	  usage = 1;
	}
	break;
      case 'N':
        if((sscanf(optarg, "%d", &nHi) != 1) || (nHi < 0))
	{
	  usage = 1;
	}
	break;
      case 'o':
        outObjFileStr = optarg;
	break;
      case 'r':
        if((sscanf(optarg, "%lg", &rad) != 1) || (rad < 0.0))
	{
	  usage = 1;
	}
	break;
      case 'x':
        if((sscanf(optarg, "%d", &maxSep) != 1) || (maxSep < 1))
	{
	  usage = 1;
	}
      case 'h': /* FALLTHROUGH */
      default:
        usage = 1;
	break;
    }
  }
  if((usage == 0) && (nLo > nHi) && (nHi != 0))
  {
    usage = 1;
  }
  if((usage == 0) && (optind < argc))
  {
    if((optind + 1) != argc)
    {
      usage = 1;
    }
    else
    {
      inObjFileStr = *(argv + optind);
    }
  }
  ok = (usage == 0);
  /* Read input domain object. */
  if(ok)
  {
    if((inObjFileStr == NULL) ||
	(*inObjFileStr == '\0') ||
	((fP = (strcmp(inObjFileStr, "-")?
	       fopen(inObjFileStr, "r"): stdin)) == NULL) ||
	((inObj = WlzAssignObject(WlzReadObj(fP, &errNum), NULL)) == NULL) ||
	(errNum != WLZ_ERR_NONE))
    {
      ok = 0;
    }
    if(fP)
    {
      if(strcmp(inObjFileStr, "-"))
      {
	(void )fclose(fP);
      }
      fP = NULL;
    }
  }
  /* Check object type and connectivity. */
  if(ok)
  {
    switch(inObj->type)
    {
      case WLZ_2D_DOMAINOBJ:
	switch(con)
	{
	  case WLZ_0_CONNECTED:
	    con = WLZ_8_CONNECTED;
	    break;
	  case WLZ_4_CONNECTED: /* FALLTHROUGH */
	  case WLZ_8_CONNECTED:
	    break;
	  default:
	    ok = 0;
	    errNum = WLZ_ERR_PARAM_DATA;
	    (void )WlzStringFromErrorNum(errNum, &errMsg);
	    (void )fprintf(stderr,
	           "%s: Connectivity for 2D must be 4 or 8 (%s).\n",
		   *argv, errMsg);
	    break;
	}
	break;
      case WLZ_3D_DOMAINOBJ:
	switch(con)
	{
	  case WLZ_0_CONNECTED:
	    con = WLZ_26_CONNECTED;
	    break;
	  case  WLZ_6_CONNECTED: /* FALLTHROUGH */
	  case WLZ_18_CONNECTED: /* FALLTHROUGH */
	  case WLZ_26_CONNECTED:
	    break;
	  default:
	    ok = 0;
	    errNum = WLZ_ERR_PARAM_DATA;
	    (void )WlzStringFromErrorNum(errNum, &errMsg);
	    (void )fprintf(stderr,
	           "%s: Connectivity for 3D must be 6, 18 or 26 (%s).\n",
		   *argv, errMsg);
	    break;
	}
	break;
      default:
	ok = 0;
	errNum = WLZ_ERR_OBJECT_TYPE;
	(void )WlzStringFromErrorNum(errNum, &errMsg);
	(void )fprintf(stderr,
	       "%s: Input object must either a 2 or 3D domain object (%s).\n",
	       *argv, errMsg);
	break;
    }
  }
  /* Make basic marker with centre at the origin. */
  if(ok)
  {
    double	mrkRad;

    if(rad > 0.5)
    {
      mrkRad = rad;
    }
    else
    {
      mrkRad = 127;
    }
    if(mrk == WLZ_BLOBMARK_SQUARE)
    {
      mrkObj = WlzMakeCuboidObject(inObj->type, mrkRad, mrkRad, mrkRad,
                                   0, 0, 0, &errNum);
    }
    else /* mrk = WLZ_BLOBMARK_CIRCLE */
    {
      mrkObj = WlzMakeSphereObject(inObj->type, mrkRad, 0, 0, 0, &errNum);
    }
    if(mrkObj == NULL)
    {
      ok = 0;
      (void )WlzStringFromErrorNum(errNum, &errMsg);
      (void )fprintf(stderr,
             "%s: Failed to create basic marker object (%s).\n",
	     *argv, errMsg);
    }
    else
    {
      mrkMass = WlzVolume(mrkObj, NULL);
    }
  }
  /* Label the given domain. */
  if(ok)
  {
    errNum = WlzLabel(inObj, &nObj, &lObj, maxSep, 1, con);
    if((errNum != WLZ_ERR_NONE) || (nObj == 0))
    {
      ok = 0;
      if(errNum == WLZ_ERR_NONE)
      {
        errNum = WLZ_ERR_DOMAIN_DATA;
      }
      (void )WlzStringFromErrorNum(errNum, &errMsg);
      (void )fprintf(stderr,
      "%s: Failed to split the given object into separate regions (%s)\n",
      *argv, errMsg);
    }
  }
  /* Work through the separate object list removing small/large objects
   * according to the low and high thresholds. */
  if(ok)
  {
    int		idM;

    for(idN = 0, idM = 0; idN < nObj; ++idN)
    {
      int	vol;

      vol = WlzVolume(lObj[idN], &errNum);
      if(errNum == WLZ_ERR_NONE)
      {
        if(((nLo > 0) && (vol < nLo)) || ((nHi > 0) && (vol > nHi)))
	{
	  (void )WlzFreeObj(lObj[idN]);
	}
	else
	{
	  lObj[idM] = lObj[idN];
	  ++idM;
	}
      }
    }
    nObj = idM;
    if(nObj == 0)
    {
      ok = 0;
      errNum = WLZ_ERR_DOMAIN_DATA;
      (void )WlzStringFromErrorNum(errNum, &errMsg);
      (void )fprintf(stderr,
                     "%s: Failed to find and separate regions (%s)\n",
		     *argv, errMsg);

    }
  }
  /* Build a marker object by adding a mark at the centre of mass of each
   * separate fragment. */
  if(ok)
  {
    WlzObject	*obj0 = NULL;

    idN = 0;
    obj0 = WlzMakeEmpty(&errNum);
    while((errNum == WLZ_ERR_NONE) && (idN < nObj))
    {
      double	  mass;
      WlzDVertex3 com;
      WlzObject	  *obj1 = NULL,
      		  *obj2 = NULL;
      WlzAffineTransform *tr = NULL;

      com = WlzCentreOfMass3D(lObj[idN], 1, &mass, &errNum);
      if(errNum == WLZ_ERR_NONE)
      {
        double	s;

	if(rad < 0.5)
	{
	  double t;

	  t = mass / mrkMass;
	  if(inObj->type == WLZ_2D_DOMAINOBJ)
	  {
	    s = sqrt(t);
	  }
	  else /* inObj->type == WLZ_3D_DOMAINOBJ */
	  {
	    s = cbrt(t);
	  }
	}
	else
	{
	  s = 1.0;
	}
        tr = (inObj->type == WLZ_2D_DOMAINOBJ)?
             WlzAffineTransformFromPrimVal(
	       WLZ_TRANSFORM_2D_AFFINE, com.vtX, com.vtY, 0.0,
	       s, 0.0, 0.0, 0.0, 0.0, 0.0, 0, &errNum):
             WlzAffineTransformFromPrimVal(
	       WLZ_TRANSFORM_3D_AFFINE, com.vtX, com.vtY, com.vtZ,
	       s, 0.0, 0.0, 0.0, 0.0, 0.0, 0, &errNum);
      }
      if(errNum == WLZ_ERR_NONE)
      {
	obj1 = WlzAffineTransformObj(mrkObj, tr, WLZ_INTERPOLATION_NEAREST,
				     &errNum);
      }
      if(errNum == WLZ_ERR_NONE)
      {
	obj2 = WlzUnion2(obj0, obj1, &errNum);
      }
      if(errNum == WLZ_ERR_NONE)
      {
        (void )WlzFreeObj(obj0);
	obj0 = obj2;
	obj2 = NULL;
      }
      (void )WlzFreeObj(obj1);
      (void )WlzFreeObj(obj2);
      (void )WlzFreeAffineTransform(tr);
      ++idN;
    }
    if(errNum == WLZ_ERR_NONE)
    {
      WlzValues	val;
      WlzObjectType vTT;

      val.core = NULL;
      if(gV.type != WLZ_GREY_ERROR)
      {
	vTT = WlzGreyTableType(WLZ_GREY_TAB_RAGR, gV.type, NULL);
	if(inObj->type == WLZ_2D_DOMAINOBJ)
	{
	  val.v = WlzNewValueTb(obj0, vTT, bV, &errNum);
	}
	else /* inObj->type == WLZ_3D_DOMAINOBJ */
	{
	  val.vox = WlzNewValuesVox(obj0, vTT, bV, &errNum);
	}
      }
      if(errNum == WLZ_ERR_NONE)
      {
        outObj = WlzMakeMain(inObj->type, obj0->domain, val, NULL, NULL,
	                     &errNum);
      }
      if((errNum == WLZ_ERR_NONE) && (gV.type != WLZ_GREY_ERROR))
      {
        errNum = WlzGreySetValue(outObj, gV);
      }
    }
  }
  if(ok)
  {
    errNum = WLZ_ERR_WRITE_EOF;
    if(((fP = (strcmp(outObjFileStr, "-")?
              fopen(outObjFileStr, "w"): stdout)) == NULL) ||
       ((errNum = WlzWriteObj(fP, outObj)) != WLZ_ERR_NONE))
    {
      ok = 0;
      (void )WlzStringFromErrorNum(errNum, &errMsg);
      (void )fprintf(stderr,
                     "%s: Failed to write output object (%s).\n",
		     *argv, errMsg);
    }
    if(fP && strcmp(outObjFileStr, "-"))
    {
      (void )fclose(fP);
    }
  }
  (void )WlzFreeObj(inObj);
  if(lObj != NULL)
  {
    for(idN = 0; idN < nObj; ++idN)
    {
      (void )WlzFreeObj(lObj[idN]);
    }
    AlcFree(lObj);
  }
  (void )WlzFreeObj(outObj);
  if(usage)
  {
    (void )fprintf(stderr,
    "Usage: %s%sExample: %s%s",
    *argv,
    " [-c#] [-g#] [-G#] [-h] [-m#] [-n#] [-N#]\n"
    "       [-o<output object>] [-r#]] [-x#] [<input object>]\n"
    "Options:\n"
    "  -c  Connectivity: 4, 6, 8, 18 or 26 connected (default 8 for 2D\n"
    "      domains and 26 for 3D domains).\n"
    "  -g  Grey value for marker. This is a single number for all except\n"
    "      RGBA (colour) grey values. RGBA components must be separated by\n"
    "      by a comma.\n"
    "  -G  Grey value type for marker specified by letter:\n"
    "        v  no grey values (default).\n"
    "        u  unsigned byte grey values.\n"
    "        s  short grey values.\n"
    "        i  int grey values.\n"
    "        f  int grey values.\n"
    "        d  int grey values.\n"
    "        r  red, green, blue, alpha grey values.\n"
    "  -h  Help, prints usage message.\n"
    "  -m  Marker type specified by a number:\n"
    "        1  circle/sphere (default)\n"
    "        2  square/cube\n"
    "  -n  Threshold minimum area/volume of blob for a marker (default\n"
    "      >= 1).\n"
    "  -N  Threshold maximum area/volume of blob for a marker. If zero\n"
    "      there is no upper limit. (default  0).\n"
    "  -o  Output object file.\n"
    "  -r  Marker radius. Attempts to keep the same area/volume if zero.\n"
    "      (default 0).\n"
    "  -x  Maximum number of separate regions in the object (default 1024).\n"
    "Reads a spatial domain object and replaces each spatialy separate\n"
    "region with a marker placed at the centre of mass of the region.\n"
    "All files are read from the standard input and written to the standard\n"
    "output unless filenames are given.\n"
    "If grey values are required then the grey value type must be set before\n"
    "the actual grey value.\n",
    *argv,
    " -o out.wlz -n 4 -r 10 -G r -g 200,100,0,255 in.wlz\n"
    "A spatial domain object is read from the file in.wlz and each\n"
    "spatialy separate region of the domain is replaced by a circle or\n"
    "sphere of radius 10 (pixels). All small regions with less than four\n"
    "(pixels voxels) is ignored. The output object (with grey values set\n"
    "to orange) is written to the file out.wlz.\n");
  }
  return(!ok);
}
Beispiel #4
0
/*!
* \return	Distance object which shares the given foreground object's
*		domain and has integer distance values, null on error.
* \ingroup	WlzMorphologyOps
* \brief	Computes the distance of every pixel/voxel in the foreground
* 		object from the reference object.
*
*		A distance transform maps all position within a  forground
*		domain to their distances from a reference domain.
*		The distance transforms implemented within this function
*		use efficient morphological primitives.
*		
*		Given two domains,
*		\f$\Omega_r\f$ the reference domain and \f$\Omega_f\f$
*		the domain specifying the region of interest,
*		a domain with a thin shell \f$\Omega_i\f$
*		is iteratively expanded from it's initial domain
*		corresponding to the reference domain \f$\Omega_r\f$.
*		At each iteration
*		\f$\Omega_i\f$ is dilated and clipped
*		by it's intersection with \f$\Omega_f\f$ until \f$\Omega_i\f$
*		becomes the null domain \f$\emptyset\f$.
*		At each iteration the current distance is recorded in a value
*		table which
*		covers the domain \f$\Omega_f\f$.
*
*		An octagonal distance scheme may be used in which
*		the distance metric is alternated between 4 and 8
*		connected for 2D and 6 and 26 connectivities in 3D.
*		See: G. Borgefors. "Distance Transformations in Arbitrary
*		Dimensions" CVGIP 27:321-345, 1984.
*
* 		An approximate Euclidean distance transform may be computed
* 		by: Scaling the given foreground and reference objects using
* 		the given approximation scale parameter, dilating the
* 		reference domain using a sphere with a radius having the same
* 		value as the scale parameter and then finaly sampling the
* 		scaled distances.
* \param	forObj			Foreground object.
* \param	refObj			Reference object.
* \param	dFn			Distance function which must be
*					appropriate to the dimension of
*					the foreground and reference objects.
* \param	dParam			Parameter required for distance
* 					function. Currently only
* 					WLZ_APX_EUCLIDEAN_DISTANCE requires a
* 					parameter. In this case the parameter
* 					is the approximation scale.
* \param	dstErr			Destination error pointer, may be NULL.
*/
WlzObject 	*WlzDistanceTransform(WlzObject *forObj, WlzObject *refObj,
				   WlzDistanceType dFn, double dParam,
				   WlzErrorNum *dstErr)
{
  int 		idP,
		lastP,
		dim,
  		notDone = 1;
  double	scale;
  WlzObject	*tmpObj,
		*sObj = NULL,
		*sForObj = NULL,
		*sRefObj = NULL,
		*dilObj = NULL,
  		*dstObj = NULL,
		*difObj = NULL,
  		*curItrObj = NULL;
  WlzObject 	*bothObj[2];
  WlzDomain	*difDoms;
  WlzPixelV 	dstV,
  		bgdV;
  WlzValues 	*difVals;
  WlzAffineTransform *tr = NULL;
  WlzConnectType con;
  WlzObjectType dstGType;
  WlzErrorNum	errNum = WLZ_ERR_NONE;
  WlzValues 	difVal,
  		dstVal,
		nullVal;
  /* By defining WLZ_DIST_TRANSFORM_ENV these normalization parameters
   * are read from the environment. This is useful for optimization. */
#ifndef WLZ_DIST_TRANSFORM_ENV
  const
#endif /* ! WLZ_DIST_TRANSFORM_ENV */
  /* These normalizarion factors have been choosen to minimize the sum of
   * squares of the deviation of the distance values from Euclidean values
   * over a radius 100 circle or sphere, where the distances are computed
   * from the circumference of the sphere towards it's centre. The values
   * were established by experiment. */
  double	nrmDist4 =  0.97,
		nrmDist6 =  0.91,
		nrmDist8 =  1.36,
		nrmDist18 = 1.34,
		nrmDist26 = 1.60;

#ifdef WLZ_DIST_TRANSFORM_ENV
  double	val;
  char		*envStr;

  if(((envStr = getenv("WLZ_DIST_TRANSFORM_NRMDIST4")) != NULL) &&
     (sscanf(envStr, "%lg", &val) == 1))
  {
    nrmDist4 = val;
  }
  if(((envStr = getenv("WLZ_DIST_TRANSFORM_NRMDIST6")) != NULL) &&
     (sscanf(envStr, "%lg", &val) == 1))
  {
    nrmDist6 = val;
  }
  if(((envStr = getenv("WLZ_DIST_TRANSFORM_NRMDIST8")) != NULL) &&
     (sscanf(envStr, "%lg", &val) == 1))
  {
    nrmDist8 = val;
  }
  if(((envStr = getenv("WLZ_DIST_TRANSFORM_NRMDIST18")) != NULL) &&
     (sscanf(envStr, "%lg", &val) == 1))
  {
    nrmDist18 = val;
  }
  if(((envStr = getenv("WLZ_DIST_TRANSFORM_NRMDIST26")) != NULL) &&
     (sscanf(envStr, "%lg", &val) == 1))
  {
    nrmDist26 = val;
  }
#endif /* WLZ_DIST_TRANSFORM_ENV */
  scale = dParam;
  nullVal.core = NULL;
  /* Check parameters. */
  if((forObj == NULL) || (refObj == NULL))
  {
    errNum = WLZ_ERR_OBJECT_NULL;
  }
  else if(((forObj->type != WLZ_2D_DOMAINOBJ) &&
           (forObj->type != WLZ_3D_DOMAINOBJ)) ||
          ((refObj->type != WLZ_POINTS) &&
	   (refObj->type != forObj->type)))
  {
    errNum = WLZ_ERR_OBJECT_TYPE;
  }
  else if((forObj->domain.core == NULL) || (refObj->domain.core == NULL))
  {
    errNum = WLZ_ERR_DOMAIN_NULL;
  }
  if(errNum == WLZ_ERR_NONE)
  {
    bgdV.type = WLZ_GREY_INT;
    bgdV.v.inv = 0;
    dstV.type = WLZ_GREY_DOUBLE;
    dstV.v.dbv = 0.0;
    switch(forObj->type)
    {
      case WLZ_2D_DOMAINOBJ:
	switch(dFn)
	{
	  case WLZ_4_DISTANCE: /* FALLTHROUGH */
	  case WLZ_8_DISTANCE: /* FALLTHROUGH */
	  case WLZ_OCTAGONAL_DISTANCE: /* FALLTHROUGH */
	  case WLZ_APX_EUCLIDEAN_DISTANCE:
	    dim = 2;
	    break;
	  default:
	    errNum = WLZ_ERR_PARAM_DATA;
	    break;
	}
        break;
      case WLZ_3D_DOMAINOBJ:
	switch(dFn)
	{
	  case WLZ_6_DISTANCE:  /* FALLTHROUGH */
	  case WLZ_18_DISTANCE: /* FALLTHROUGH */
	  case WLZ_26_DISTANCE: /* FALLTHROUGH */
	  case WLZ_OCTAGONAL_DISTANCE: /* FALLTHROUGH */
	  case WLZ_APX_EUCLIDEAN_DISTANCE:
	    dim = 3;
	    break;
	  default:
	    errNum = WLZ_ERR_PARAM_DATA;
	    break;
	}
        break;
      default:
        errNum = WLZ_ERR_OBJECT_TYPE;
	break;
    }
  }
  if(errNum == WLZ_ERR_NONE)
  {
    switch(dFn)
    {
      case WLZ_4_DISTANCE:
        con = WLZ_4_CONNECTED;
	break;
      case WLZ_6_DISTANCE:
        con = WLZ_6_CONNECTED;
	break;
      case WLZ_8_DISTANCE:
        con = WLZ_8_CONNECTED;
	break;
      case WLZ_18_DISTANCE:
        con = WLZ_18_CONNECTED;
	break;
      case WLZ_26_DISTANCE:
        con = WLZ_26_CONNECTED;
	break;
      case WLZ_OCTAGONAL_DISTANCE:
        con = (dim == 2)? WLZ_8_CONNECTED: WLZ_26_CONNECTED;
	break;
      case WLZ_APX_EUCLIDEAN_DISTANCE:
        con = (dim == 2)? WLZ_8_CONNECTED: WLZ_26_CONNECTED;
	if(scale < 1.0)
	{
	  errNum = WLZ_ERR_PARAM_DATA;
	}
	break;
      case WLZ_EUCLIDEAN_DISTANCE:
	errNum = WLZ_ERR_UNIMPLEMENTED;
	break;
      default:
        errNum = WLZ_ERR_PARAM_DATA;
	break;
    }
  }
  /* Create scaled domains and a sphere domain for structual erosion if the
   * distance function is approximate Euclidean. */
  if(errNum == WLZ_ERR_NONE)
  {
    if(dFn == WLZ_APX_EUCLIDEAN_DISTANCE)
    {
      tr = (dim == 2)?
	   WlzAffineTransformFromScale(WLZ_TRANSFORM_2D_AFFINE,
	                               scale, scale, 0.0, &errNum):
	   WlzAffineTransformFromScale(WLZ_TRANSFORM_3D_AFFINE,
	                               scale, scale, scale, &errNum);
      if(errNum == WLZ_ERR_NONE)
      {
	tmpObj = WlzMakeMain(forObj->type, forObj->domain, nullVal,
			     NULL, NULL, &errNum);
	if(tmpObj)
	{
	  sForObj = WlzAssignObject(
	            WlzAffineTransformObj(tmpObj, tr,
		                          WLZ_INTERPOLATION_NEAREST,
		    			  &errNum), NULL);
	  (void )WlzFreeObj(tmpObj);
	}
      }
      if(errNum == WLZ_ERR_NONE)
      {
	if(refObj->type == WLZ_POINTS)
	{
	  sRefObj = WlzPointsToDomObj(refObj->domain.pts, scale, &errNum);
	}
	else /* type == WLZ_2D_DOMAINOBJ || type == WLZ_3D_DOMAINOBJ */
	{
	  tmpObj = WlzMakeMain(refObj->type, refObj->domain, nullVal,
			       NULL, NULL, &errNum);
	  if(errNum == WLZ_ERR_NONE)
	  {
	    sRefObj = WlzAssignObject(
		      WlzAffineTransformObj(tmpObj, tr,
					    WLZ_INTERPOLATION_NEAREST,
					    &errNum), NULL);
	  }
	}
	(void )WlzFreeObj(tmpObj);
      }
      if(errNum == WLZ_ERR_NONE)
      {
	sObj = WlzAssignObject(
	       WlzMakeSphereObject(forObj->type, scale,
	                           0.0, 0.0, 0.0, &errNum), NULL);
      }
      (void )WlzFreeAffineTransform(tr);
    }
    else
    {
      sForObj = WlzAssignObject(
	        WlzMakeMain(forObj->type, forObj->domain, nullVal,
	  		    NULL, NULL, &errNum), NULL);
      if(errNum == WLZ_ERR_NONE)
      {
	if(refObj->type == WLZ_POINTS)
	{
	  sRefObj = WlzPointsToDomObj(refObj->domain.pts, 1.0, &errNum);
	}
	else
	{
	  sRefObj = WlzAssignObject(
		    WlzMakeMain(refObj->type, refObj->domain, nullVal,
				NULL, NULL, &errNum), NULL);
	}
      }
    }
  }
  /* Create new values for the computed distances. */
  if(errNum == WLZ_ERR_NONE)
  {
    dstGType = WlzGreyTableType(WLZ_GREY_TAB_RAGR, WLZ_GREY_INT, NULL);
    if(dim == 2)
    {
      dstVal.v = WlzNewValueTb(sForObj, dstGType, bgdV, &errNum);
    }
    else
    {
      dstVal.vox = WlzNewValuesVox(sForObj, dstGType, bgdV, &errNum);
    }
  }
  /* Create a distance object using the foreground object's domain and
   * the new distance values. */
  if(errNum == WLZ_ERR_NONE)
  {
    dstObj = WlzMakeMain(sForObj->type, sForObj->domain, dstVal,
			 NULL, NULL, &errNum);
  }
  if(errNum == WLZ_ERR_NONE)
  {
    bothObj[0] = sForObj;
    errNum = WlzGreySetValue(dstObj, dstV);
  }
  /* Dilate the reference object while setting the distances in each
   * dilated shell. */
  while((errNum == WLZ_ERR_NONE) && notDone)
  {
    if(dFn == WLZ_APX_EUCLIDEAN_DISTANCE)
    {
      dstV.v.dbv += 1.0;
    }
    else
    {
      switch(con)
      {
	case WLZ_4_CONNECTED:
	  dstV.v.dbv += nrmDist4;
	  break;
	case WLZ_6_CONNECTED:
	  dstV.v.dbv += nrmDist6;
	  break;
	case WLZ_8_CONNECTED:
	  dstV.v.dbv += nrmDist8;
	  break;
	case WLZ_18_CONNECTED:
	  dstV.v.dbv += nrmDist18;
	  break;
	case WLZ_26_CONNECTED:
	  dstV.v.dbv += nrmDist26;
	  break;
        default:
	  errNum = WLZ_ERR_CONNECTIVITY_TYPE;
	  break;
      }
    }
    if(dFn == WLZ_APX_EUCLIDEAN_DISTANCE)
    {
      dilObj = WlzStructDilation(sRefObj, sObj, &errNum);
    }
    else
    {
      dilObj = WlzDilation(sRefObj, con, &errNum);
    }
    if(errNum == WLZ_ERR_NONE)
    {
      switch(sForObj->type)
      {
        case WLZ_2D_DOMAINOBJ:
	  curItrObj = WlzAssignObject(
	              WlzIntersect2(dilObj, sForObj, &errNum), NULL);
	  break;
        case WLZ_3D_DOMAINOBJ:
	  bothObj[1] = dilObj;
	  curItrObj = WlzAssignObject(
	              WlzIntersectN(2, bothObj, 1, &errNum), NULL);
	  break;
        default:
	  errNum = WLZ_ERR_OBJECT_TYPE;
	  break;
      }
    }
    (void)WlzFreeObj(dilObj);
    /* Create difference object for the expanding shell. */
    if(errNum == WLZ_ERR_NONE)
    {
      difObj = WlzDiffDomain(curItrObj, sRefObj, &errNum);
    }
    if((difObj == NULL) || WlzIsEmpty(difObj, &errNum))
    {
      notDone = 0;
    }
    else
    {
      /* Assign the distance object's values to the difference object
       * and set all it's values to the current distance. */
      if(errNum == WLZ_ERR_NONE)
      {
	switch(sForObj->type)
	{
	  case WLZ_2D_DOMAINOBJ:
	    difObj->values = WlzAssignValues(dstObj->values, NULL);
	    errNum = WlzGreySetValue(difObj, dstV);
	    break;
	  case WLZ_3D_DOMAINOBJ:
	    /* 3D is more complex than 2D: Need to create a temporary
	     * voxel valuetable and assign the individual 2D values. */
	    difVal.vox = WlzMakeVoxelValueTb(WLZ_VOXELVALUETABLE_GREY,
					     difObj->domain.p->plane1,
					     difObj->domain.p->lastpl,
					     bgdV, NULL, &errNum);
	    if(errNum == WLZ_ERR_NONE)
	    {
	      difObj->values = WlzAssignValues(difVal, NULL);
	      difDoms = difObj->domain.p->domains;
	      difVals = difObj->values.vox->values;
	      idP = difObj->domain.p->plane1;
	      lastP = difObj->domain.p->lastpl;
	      while(idP <= lastP)
	      {
		if((*difDoms).core)
		{
		  dstVal = dstObj->values.vox->values[idP - 
						      dstObj->domain.p->plane1];
		  *difVals = WlzAssignValues(dstVal, NULL);
		}
		++idP;
		++difDoms;
		++difVals;
	      }
	      if(difObj->domain.p->lastpl > difObj->domain.p->plane1)
	      {
		errNum = WlzGreySetValue(difObj, dstV);
	      }
	    }
	    break;
	  default:
	    errNum = WLZ_ERR_OBJECT_TYPE;
	    break;
	}
      }
      (void )WlzFreeObj(sRefObj);
      sRefObj = WlzAssignObject(curItrObj, NULL);
      (void )WlzFreeObj(curItrObj);
    }
    (void )WlzFreeObj(difObj); difObj = NULL;
    if(dFn == WLZ_OCTAGONAL_DISTANCE)
    {
      /* Alternate connectivities for octagonal distance. */
      if(dim == 2)
      {
	con = (con == WLZ_4_CONNECTED)? WLZ_8_CONNECTED: WLZ_4_CONNECTED;
      }
      else /* dim == 3 */
      {
	con = (con == WLZ_6_CONNECTED)? WLZ_26_CONNECTED: WLZ_6_CONNECTED;
      }
    }
  }
  (void )WlzFreeObj(sObj);
  (void )WlzFreeObj(sForObj);
  (void )WlzFreeObj(sRefObj);
  (void )WlzFreeObj(curItrObj);
  if((errNum == WLZ_ERR_NONE) && (dFn == WLZ_APX_EUCLIDEAN_DISTANCE))
  {
    tmpObj = WlzDistSample(dstObj, dim, scale, &errNum);
    (void )WlzFreeObj(dstObj);
    dstObj = tmpObj;
  }
  if(errNum != WLZ_ERR_NONE)
  {
    (void )WlzFreeObj(dstObj); dstObj = NULL;
  }
  if(dstErr)
  {
    *dstErr = errNum;
  }
  return(dstObj);
}
Beispiel #5
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 #6
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 #7
0
int             main(int argc, char **argv)
{
  int           option,
                ok = 1,
                usage = 0,
		dim = 0,
		transform = 0;
  WlzIBox3	bBox[2];
  WlzEffFormat  outFmt = WLZEFF_FORMAT_NONE;
  WlzEffFormat  inFmt[2] = {WLZEFF_FORMAT_NONE};
  WlzObject     *outObj = NULL;
  WlzObject	*inObj[2] = {NULL};
  char          *outFileStr;
  char		*inFileStr[2] = {NULL};
  WlzAffineTransform *tr = NULL;
  WlzErrorNum   errNum = WLZ_ERR_NONE;
  const char    *errMsg;
  static char   optList[] = "s:t:o:u:hT",
                fileStrDef[] = "-";

  opterr = 0;
  inFileStr[0] = fileStrDef;
  inFileStr[1] = fileStrDef;
  outFileStr = fileStrDef;
  while((usage == 0) && ((option = getopt(argc, argv, optList)) != -1))
  {
    switch(option)
    {
      case 's':
	if((inFmt[0] = WlzEffStringExtToFormat(optarg)) == 0)
	{
	  usage = 1;
	  ok = 0;
	}
	break;
      case 't':
	if((inFmt[1] = WlzEffStringExtToFormat(optarg)) == 0)
	{
	  usage = 1;
	  ok = 0;
	}
	break;
      case 'u':
	if((outFmt = WlzEffStringExtToFormat(optarg)) == 0)
	{
	  usage = 1;
	  ok = 0;
	}
	break;
      case 'o':
	outFileStr = optarg;
	break;
      case 'T':
	transform = 1;
	break;
      case 'h': /* FALLTHROUGH */
      default:
	usage = 1;
	break;
    }
  }
  if(usage == 0)
  {
    usage = (optind + 2 != argc);
  }
  if(usage == 0)
  {
    inFileStr[0] = *(argv + optind);
    inFileStr[1] = *(argv + optind + 1);
  }
  if(usage == 0)
  {
    if(inFmt[0] == WLZEFF_FORMAT_NONE)
    {
      inFmt[0] = WlzEffStringFormatFromFileName(inFileStr[0]);
    }
    if(inFmt[1] == WLZEFF_FORMAT_NONE)
    {
      inFmt[1] = WlzEffStringFormatFromFileName(inFileStr[1]);
    }
    if(outFmt == WLZEFF_FORMAT_NONE)
    {
      outFmt = WlzEffStringFormatFromFileName(outFileStr);
    }
    if((inFmt[0] == WLZEFF_FORMAT_NONE) || (inFmt[1] == WLZEFF_FORMAT_NONE) ||
       (outFmt == WLZEFF_FORMAT_NONE))
    {
      usage = 1;
    }
  }
  ok = (usage == 0);
  /* Read source and target objects. */
  if(ok)
  {
    int		idx;

    for(idx = 0; ok && (idx < 2); ++idx)
    {
      char *fStr;
      FILE *fP;

      if(strcmp(inFileStr[idx], "-") == 0)
      {
        fP = stdin;
	fStr = NULL;
      }
      else
      {
        fP = NULL;
	fStr = inFileStr[idx];
      }
      if((inObj[idx] = WlzAssignObject(WlzEffReadObj(fP, fStr, inFmt[idx],
                                       0, 0, 0, &errNum), NULL)) == NULL)
      {
	ok = 0;
	(void )WlzStringFromErrorNum(errNum, &errMsg);
	(void )fprintf(stderr,
	    "%s: Failed to read object from file(s) %s (%s)\n",
	    *argv, inFileStr[idx], errMsg);
      }
    }
  }
  /* Check object dimension */
  if(ok)
  {
    int		idx;
    int		d[2];

    d[0] = 0;
    for(idx = 0; ok && (idx < 2); ++idx)
    {
      d[idx] = WlzObjectDimension(inObj[idx], &errNum);
      ok = (errNum == WLZ_ERR_NONE);
    }
    if(errNum == WLZ_ERR_NONE)
    {
      if((d[0] == 0) || (d[0] != d[1]))
      {
	ok = 0;
        errNum = WLZ_ERR_OBJECT_TYPE;
      }
    }
    dim = d[0];
    if(errNum != WLZ_ERR_NONE)
    {
      (void )WlzStringFromErrorNum(errNum, &errMsg);
      (void )fprintf(stderr,
          "%s: Failed to establish object dimensions (%s)\n",
	  *argv, errMsg);
    }
  }
  /* Compute the bounding box of the source and target objects. */
  if(ok)
  {
    int		idx;

    for(idx = 0; ok && (idx < 2); ++idx)
    {
      bBox[idx] = WlzBoundingBox3I(inObj[idx], &errNum);
      if(errNum != WLZ_ERR_NONE)
      {
        ok = 0;
	(void )WlzStringFromErrorNum(errNum, &errMsg);
	(void )fprintf(stderr,
	    "%s: Failed to compute bounding box of object read from file\n"
	    "%s (%s).\n",
	    *argv, inFileStr[idx], errMsg);
      }
    }
  }
  /* Compute affine transform which will register the source to target
   * bounding box. */
  if(ok)
  {
    int		idx,
    		nVtx;
    WlzVertexType vType;
    WlzTransformType tType;
    WlzVertexP vP[2];
    WlzDVertex3	wSp[48];

    vP[0].d3 = wSp;
    vP[1].d3 = wSp + 24;
    if(dim == 2)
    {
      nVtx = 4;
      vType = WLZ_VERTEX_D2;
      tType = WLZ_TRANSFORM_2D_AFFINE;
      for(idx = 0; idx < 2; ++idx)
      {
        (vP[idx].d2)[0].vtX = bBox[idx].xMin;
	(vP[idx].d2)[0].vtY = bBox[idx].yMin;
        (vP[idx].d2)[1].vtX = bBox[idx].xMax;
	(vP[idx].d2)[1].vtY = bBox[idx].yMin;
        (vP[idx].d2)[2].vtX = bBox[idx].xMax;
	(vP[idx].d2)[2].vtY = bBox[idx].yMax;
        (vP[idx].d2)[3].vtX = bBox[idx].xMin;
	(vP[idx].d2)[3].vtY = bBox[idx].yMax;
      }
    }
    else /* dim == 3 */
    {
      nVtx = 8;
      vType = WLZ_VERTEX_D3;
      tType = WLZ_TRANSFORM_3D_AFFINE;
      for(idx = 0; idx < 2; ++idx)
      {
        (vP[idx].d3)[0].vtX = bBox[idx].xMin;
	(vP[idx].d3)[0].vtY = bBox[idx].yMin;
	(vP[idx].d3)[0].vtZ = bBox[idx].zMin;
        (vP[idx].d3)[1].vtX = bBox[idx].xMax;
	(vP[idx].d3)[1].vtY = bBox[idx].yMin;
	(vP[idx].d3)[1].vtZ = bBox[idx].zMin;
        (vP[idx].d3)[2].vtX = bBox[idx].xMax;
	(vP[idx].d3)[2].vtY = bBox[idx].yMax;
	(vP[idx].d3)[2].vtZ = bBox[idx].zMin;
        (vP[idx].d3)[3].vtX = bBox[idx].xMin;
	(vP[idx].d3)[3].vtY = bBox[idx].yMax;
	(vP[idx].d3)[3].vtZ = bBox[idx].zMin;
        (vP[idx].d3)[4].vtX = bBox[idx].xMin;
	(vP[idx].d3)[4].vtY = bBox[idx].yMin;
	(vP[idx].d3)[4].vtZ = bBox[idx].zMax;
        (vP[idx].d3)[5].vtX = bBox[idx].xMax;
	(vP[idx].d3)[5].vtY = bBox[idx].yMin;
	(vP[idx].d3)[5].vtZ = bBox[idx].zMax;
        (vP[idx].d3)[6].vtX = bBox[idx].xMax;
	(vP[idx].d3)[6].vtY = bBox[idx].yMax;
	(vP[idx].d3)[6].vtZ = bBox[idx].zMax;
        (vP[idx].d3)[7].vtX = bBox[idx].xMin;
	(vP[idx].d3)[7].vtY = bBox[idx].yMax;
	(vP[idx].d3)[7].vtZ = bBox[idx].zMax;
      }
    }
    tr = WlzAffineTransformLSq(vType, nVtx, vP[1], nVtx, vP[0], 0, NULL,
                               tType, &errNum);
    if(errNum != WLZ_ERR_NONE)
    {
      ok = 0;
      (void )WlzStringFromErrorNum(errNum, &errMsg);
      (void )fprintf(stderr,
	  "%s: Failed to compute affine transform (%s).\n",
	  *argv, errMsg);
    }
  }
  /* Create affine transform object or affine transform the source object
   * as required. */
  if(ok)
  {
    int		idx,
    		idy;
    const double eps = 1.0e-12;

    /* Tidy up the transform, |t_{ij}| < eps => t_{ij} = 0.0. */
    for(idy = 0; idy < 4; ++idy)
    {
      for(idx = 0; idx < 4; ++idx)
      {
        if(fabs(tr->mat[idy][idx]) < eps)
	{
	  tr->mat[idy][idx] = 0.0;
	}
      }
    }
    if(transform)
    {
      outObj = WlzAffineTransformObj(inObj[0], tr, WLZ_INTERPOLATION_NEAREST,
                                     &errNum);
      if(outObj)
      {
        tr = NULL;
      }
    }
    else
    {
      WlzDomain dom;
      WlzValues val;

      dom.t = tr;
      val.core = NULL;
      outObj = WlzMakeMain(WLZ_AFFINE_TRANS, dom, val, NULL, NULL, &errNum);
    }
    if(errNum != WLZ_ERR_NONE)
    {
      ok = 0;
      (void )WlzStringFromErrorNum(errNum, &errMsg);
      (void )fprintf(stderr,
                     "%s: Failed to %saffine transform object (%s).\n",
		     *argv, (transform)? "create ": "", errMsg);
    }
  }
  /* Write the output object. */
  if(ok)
  {
    char *fStr;
    FILE *fP;

    if(strcmp(outFileStr, "-") == 0)
    {
      fP = stdout;
      fStr = NULL;
    }
    else
    {
      fP = NULL;
      fStr = outFileStr;
    }
    errNum = WlzEffWriteObj(fP, fStr, outObj, outFmt);
    if(errNum != WLZ_ERR_NONE)
    {
      ok = 0;
      (void )WlzStringFromErrorNum(errNum, &errMsg);
      (void )fprintf(stderr,
	  "%s: Failed to write object to file %s (%s)\n",
	  *argv, outFileStr, errMsg);
    }
  }
  (void )WlzFreeObj(inObj[0]);
  (void )WlzFreeObj(inObj[1]);
  (void )WlzFreeObj(outObj);
  if(usage)
  {
    char *fmtStr = NULL;

    fmtStr = WlzEffFormatTable(2, 50, 10, NULL);
    (void )fprintf(
        stderr,
	"Usage: %s%s%s%s%s%s%s%s\n",
	*argv,
	" [-h] [-T]\n"
	"\t\t[-o<out file>] [-s<src fmt>] [-t<tgt fmt>] [-u<out fmt>]\n"
	"\t\t<source object> <target object>\n"
        "Computes the Woolz affine transform which makes the bounding box of\n"
	"the source object equal to that of the target object.\n"
	"Version: ",
	WlzVersion(),
	"\n"
	"  -h Help, prints this usage information.\n"
	"  -o Output file.\n"
	"  -s Source file format.\n"
	"  -t Target file format.\n"
	"  -u Output file format.\n"
	"  -T Transform the source object and write it to the output file\n"
	"     rather than the affine transform.\n"
        "The known file formats are:\n"
	"  Description                                       Extension\n"
	"  ***********                                       *********\n",
	fmtStr,
	"Simple example:\n  ",
	*argv,
	"-o out.wlz small.vtk big.stl\n"
	"Reads the source object from the file small.vtk and the target\n"
	"object from big.stl then computes the affine transform which scales\n"
	"the source objects bounding box to fit that of the target object.\n"
	"The output transform is written to the file out.wlz.\n");
  }
  return(!ok);
}
/*!
* - Function:   outputTheWarpedWlz
* - Returns:    WlzVetex 
* - Purpose:    Track vertex through neighbour layer.	
* - Parameters:	
*
*     -#   vsp:              input   WlzVertex.
*     -#   vtp:              input   WlzVertex.
*     -#   wlzViewStri:      WlzThreeDViewStruct for layer i;
*     -#   wlzViewStrip1:    WlzThreeDViewStruct for the next layer i+-1;
*     -#   UpOrDown:         > 0 Up;  <= 0 down; 
* - Author:       J. Rao, R. Baldock
*/
static WlzErrorNum outputTheWarpedWlz( WlzVertex  vsp, 
                                       WlzVertex  vtp,
				       WlzVertex  vfp,
                                       WlzDVertex2 *vtxVec1,
                                       char *souce2DWlzStr,
				       char *out2DWlzStr
				     )
{
    WlzAffineTransform  *trans=NULL;

    WlzTransformType     tType = WLZ_TRANSFORM_2D_AFFINE;
    WlzDVertex2         *vtxVec0;
    WlzDVertex2          sv, tv;
    WlzObject           *sObj=NULL, *tObj=NULL;
    FILE                *inFile;
    WlzErrorNum	         errNum = WLZ_ERR_NONE;
    WlzInterpolationType interp = WLZ_INTERPOLATION_LINEAR;  /* Use the nearest neighbour */

   /*------- allocate memory --------*/
   if (
        ((vtxVec0 = (WlzDVertex2 *)AlcCalloc(sizeof(WlzDVertex2), 3)) == NULL) 
      )
   {
      	errNum = WLZ_ERR_MEM_ALLOC;
   }

   if( errNum == WLZ_ERR_NONE )
   {
    	(vtxVec0)->vtX     = vsp.d3.vtX;
    	(vtxVec0)->vtY     = vsp.d3.vtY;
    	(vtxVec0 + 1)->vtX = vtp.d3.vtX;
    	(vtxVec0 + 1)->vtY = vtp.d3.vtY;
    	(vtxVec0 + 2)->vtX = vfp.d3.vtX;
    	(vtxVec0 + 2)->vtY = vfp.d3.vtY;
    	trans = WlzAffineTransformLSq2D(3, vtxVec1, 3, vtxVec0, 0, NULL, tType, &errNum); 
        /* trans = WlzAffineTransformLSq2D(3, vtxVec0, 3, vtxVec1, 0, NULL, tType, &errNum); */
   }
   
   if( errNum == WLZ_ERR_NONE )
   {
      /* check whether it is right or not */
      sv.vtX = (vtxVec0)->vtX;
      sv.vtY = (vtxVec0)->vtY;
      printf("compare\n");
      
      printf("first one\n");
      printf("source: %lg   %lg\n", sv.vtX, sv.vtY );
      tv = WlzAffineTransformVertexD2(trans, sv, &errNum);
      printf("target: %lg   %lg\n", (vtxVec1)->vtX, (vtxVec1)->vtY );
      printf("source trans to target : %lg   %lg\n", tv.vtX, tv.vtY);
      
      sv.vtX = (vtxVec0 + 1)->vtX;
      sv.vtY = (vtxVec0 + 1)->vtY;
      printf("second one\n");
      printf("source: %lg   %lg\n", sv.vtX, sv.vtY );
      tv = WlzAffineTransformVertexD2(trans, sv, &errNum);
      printf("target: %lg   %lg\n", (vtxVec1 + 1)->vtX, (vtxVec1 + 1)->vtY );
      printf("source trans to target : %lg   %lg\n", tv.vtX, tv.vtY);

      sv.vtX = (vtxVec0 + 2)->vtX;
      sv.vtY = (vtxVec0 + 2)->vtY;
      printf("third one\n");
      printf("source: %lg   %lg\n", sv.vtX, sv.vtY );
      tv = WlzAffineTransformVertexD2(trans, sv, &errNum);
      printf("target: %lg   %lg\n", (vtxVec1 + 2)->vtX, (vtxVec1 + 2)->vtY );
      printf("source trans to target : %lg   %lg\n", tv.vtX, tv.vtY);
   
   }
	   
	   
   if( errNum == WLZ_ERR_NONE )
   {
    	/* read Woolz object */
        if((inFile = fopen(souce2DWlzStr, "r")) == NULL )
        {
        	printf("cannot open the input woolz file.\n");
        	exit(1);
      	}
      	if( !( sObj = WlzReadObj(inFile, &errNum) ) )
      	{
        	printf("input Woolz Object Error.\n");
        	fclose(inFile); 
        	exit(1);
      	}
      	fclose(inFile); 
      	inFile = NULL;
   }
    
   if( errNum == WLZ_ERR_NONE )
   {
   	tObj =  WlzAffineTransformObj(sObj, trans, interp, &errNum  );
   }

   if( errNum == WLZ_ERR_NONE )
   {
   	/* write Woolz object */
      	if((inFile = fopen(out2DWlzStr, "w")) == NULL )
      	{
        	printf("cannot open the output woolz file.\n");
        	exit(1);
      	}
      	errNum = WlzWriteObj(inFile, tObj); 

      	if(  errNum != WLZ_ERR_NONE )
      	{
        	printf("input Woolz Object Error.\n");
        	fclose(inFile); 
        	exit(1);
      	}
      	fclose(inFile); 
      	inFile = NULL;
	
   }
  
      WlzFreeObj(sObj);
      WlzFreeObj(tObj);
      AlcFree(vtxVec0 );
      AlcFree(trans);


   return errNum;
}
/*! 
* \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);
}
int             main(int argc, char **argv)
{
  int		option,
  		ok = 1,
		debug = 0,
		usage = 0,
		verbose = 0,
		verboseDat = 0,
		idx0,
		index,
		binFlg = 0,
  		nMatch = 0,
		useCOfM = 0,
		maxItr = 200,
		minSpx = 15,
		minSegSpx = 10,
		matchImpNN = 7,
		noMatching = 0,
		decompLimit = INT_MAX,
		removeRefOrg = 1,
		refMedianSz = 0,
		srcMedianSz = 0,
		multipleFiles = 0;
  double	tD0,
  		tD1,
		delta = 0.01,	
  		maxAng = 30 * (ALG_M_PI / 180.0),
  		maxDeform = 0.5,
  		maxDisp = 20.0,
		refCThr = 20.0,
		srcCThr = 20.0,
		refThr = 254.0,
		srcThr = 254.0,
		matchImpThr = 2.5,
		refSmooth = 3.0,
		srcSmooth = 3.0;
  FILE		*vFP,
  		*lFP = NULL,
  		*fP = NULL;
  char		*inFileStr = NULL,
		*inTrFileStr = NULL,
  		*outFileBaseStr = NULL,
		*ctrFileBaseStr = NULL;
  char		secParFile[256];
  char		*refObjFileStr = NULL,
  		*srcObjFileStr = NULL;
  WlzThresholdType thrType = WLZ_THRESH_LOW;
  WlzEffFormat	refObjFileType = WLZEFF_FORMAT_WLZ,
  		srcObjFileType = WLZEFF_FORMAT_WLZ;
  WlzObject	*tObj0 = NULL,
  		*refObj3D = NULL,
		*refObj2D = NULL,
  		*srcObj2D = NULL,
		*refCObj2D = NULL,
  		*srcCObj2D = NULL;
  WlzDVertex2	tDV0,
  		refCOfM,
  		srcCOfM,
		refObj2DOrg;
  WlzAffineTransform *inTr = NULL,
		*inPTr = NULL;
  WlzThreeDViewStruct *view = NULL;
  WlzVertexP	matchRP,
  		matchSP;
  WlzInterpolationType interp = WLZ_INTERPOLATION_NEAREST;
  WlzMatchICPWeightCbData cbData;
  WlzAffineTransformPrim inTrPrim;
  WlzErrorNum	errNum = WLZ_ERR_NONE;
  char		fileNameBuf[FILENAME_MAX];
  const char	*errMsg;
  static char	optList[] =
                "dhvVo:r:Yt:x:y:a:s:efg:k:u:b:B:i:p:A:E:S:F:P:m:n:c:NL";
  const int	nScatter = 5;
  const char	nullStr[] = "<NULL>",
  		inFileStrDef[] = "-",
  	        outFileStrDef[] = "-";

  matchRP.v = matchSP.v = NULL;
  srcCOfM.vtX = srcCOfM.vtY = 0.0;
  inFileStr = (char *)inFileStrDef;
  outFileBaseStr = (char *)outFileStrDef;
  (void )memset(&inTrPrim, 0, sizeof(WlzAffineTransformPrim));
  inTrPrim.scale = 1.0;
  while((usage == 0) && ok &&
        ((option = getopt(argc, argv, optList)) != -1))
  {
    switch(option)
    {
      case 'd':
        debug = 1;
	break;
      case 'h':
        usage = 1;
	break;
      case 'v':
        verbose = 1;
	break;
      case 'V':
        verboseDat = 1;
	break;
      case 'o':
        outFileBaseStr = optarg;
	break;
      case 'r':
        if((refObjFileStr = AlcStrDup(optarg)) == NULL)
	{
	  ok = 0;
	  (void )fprintf(stderr, "%s Failed to allocate enough memory.\n",
	     	         *argv);
	}
        break;
      case 'Y':
        multipleFiles = 1;
	break;
      case 't':
        inTrFileStr = optarg;
	break;
      case 'x':
        if(sscanf(optarg, "%lg", &(inTrPrim.tx)) != 1)
	{
	  usage = 1;
	}
	break;
      case 'y':
        if(sscanf(optarg, "%lg", &(inTrPrim.ty)) != 1)
	{
	  usage = 1;
	}
	break;
      case 'a':
        if(sscanf(optarg, "%lg", &(inTrPrim.theta)) != 1)
	{
	  usage = 1;
	}
	else
	{
	  inTrPrim.theta *= ALG_M_PI / 180.0;
	}
	break;
      case 's':
        if(sscanf(optarg, "%lg", &(inTrPrim.scale)) != 1)
	{
	  usage = 1;
	}
	break;
      case 'e':
        useCOfM = 1;
	break;
      case 'f':
        removeRefOrg = 0;
	break;
      case 'b':
	binFlg = 1;
        thrType = WLZ_THRESH_LOW;
        usage = WlzMatchICPPlaneParseDPair(optarg, &refThr, &srcThr) != 3;
	break;
      case 'B':
	binFlg = 1;
        thrType = WLZ_THRESH_HIGH;
        usage = WlzMatchICPPlaneParseDPair(optarg, &refThr, &srcThr) != 3;
	break;
      case 'g':
        usage = WlzMatchICPPlaneParseDPair(optarg, &refCThr, &srcCThr) != 3;
	break;
      case 'k':
        usage = WlzMatchICPPlaneParseDPair(optarg, &tD0, &tD1) != 3;
	refMedianSz = WLZ_NINT(tD0);
	srcMedianSz = WLZ_NINT(tD1);
	break;
      case 'u':
        usage = WlzMatchICPPlaneParseDPair(optarg,
					   &refSmooth, &srcSmooth) != 3;
	break;
      case 'i':
        if((sscanf(optarg, "%d", &maxItr) != 1) || (maxItr <= 0))
	{
	  usage = 1;
	}
	break;
      case 'p':
        if((sscanf(optarg, "%d", &minSpx) != 1) || (minSpx <= 10))
	{
	  usage = 1;
	}
	break;
      case 'P':
        if(sscanf(optarg, "%d", &minSegSpx) != 1)
	{
	  usage = 1;
	}
	break;
      case 'A':
        if((sscanf(optarg, "%lg", &maxAng) != 1) ||
	   (maxAng < 0.0) || (maxAng > 180.0))
	{
	  usage = 1;
	}
	else
	{
	  maxAng *= ALG_M_PI / 180;
	}
	break;
      case 'E':
        if((sscanf(optarg, "%lg", &delta) != 1) || (delta < 0.0))
	{
	  usage = 1;
	}
      case 'F':
        if((sscanf(optarg, "%lg", &maxDeform) != 1) || (maxDeform < 0.0))
	{
	  usage = 1;
	}
	break;
      case 'S':
        if((sscanf(optarg, "%lg", &maxDisp) != 1) || (maxDisp <= 0.0))
	{
	  usage = 1;
	}
	break;
      case 'm':
        if((sscanf(optarg, "%lg", &matchImpThr) != 1) || (matchImpThr < 0.0))
	{
	  usage = 1;
	}
	break;
      case 'n':
        if((sscanf(optarg, "%d", &matchImpNN) != 1) || (matchImpNN < 1))
	{
	  usage = 1;
	}
	break;
      case 'c':
        ctrFileBaseStr = optarg;
        if(strlen(ctrFileBaseStr) > (FILENAME_MAX - 16))
	{
	  usage = 1;
	}
	break;
      case 'N':
        noMatching = 1;
	break;
      case 'L':
        interp = WLZ_INTERPOLATION_LINEAR;
	break;
      default:
        usage = 1;
	break;
    }
  }
  if(usage)
  {
    ok = 0;
  }
  if(ok)
  {
    if((inFileStr == NULL) || (*inFileStr == '\0') ||
       (outFileBaseStr == NULL) || (*outFileBaseStr == '\0'))
    {
      ok = 0;
      usage = 1;
    }
    if(ok && (optind < argc))
    {
      if((optind + 1) != argc)
      {
        usage = 1;
	ok = 0;
      }
      else
      {
        inFileStr = *(argv + optind);
      }
    }
  }
  if(verbose)
  {
    (void )fprintf(stderr, "Parameter and other internal variable values:\n");
    (void )fprintf(stderr, "  ok = %d\n", ok);
    (void )fprintf(stderr, "  debug = %d\n", debug);
    (void )fprintf(stderr, "  verbose = %d\n", verbose);
    (void )fprintf(stderr, "  verboseDat = %d\n", verboseDat);
    (void )fprintf(stderr, "  outFileBaseStr = %s\n",
		   outFileBaseStr? outFileBaseStr: nullStr);
    (void )fprintf(stderr, "  refObjFileStr = %s\n",
		   refObjFileStr? refObjFileStr: nullStr);
    (void )fprintf(stderr, "  multipleFiles = %d\n", multipleFiles);
    (void )fprintf(stderr, "  inTrFileStr = %s\n",
                   inTrFileStr? inTrFileStr: nullStr);
    (void )fprintf(stderr, "  inTrPrim.tx = %g\n", inTrPrim.tx);
    (void )fprintf(stderr, "  inTrPrim.ty = %g\n", inTrPrim.ty);
    (void )fprintf(stderr, "  inTrPrim.theta = %g (Radians)\n",
    		   inTrPrim.theta);
    (void )fprintf(stderr, "  inTrPrim.scale = %g\n", inTrPrim.scale);
    (void )fprintf(stderr, "  useCOfM = %d\n", useCOfM);
    (void )fprintf(stderr, "  removeRefOrg = %d\n", removeRefOrg);
    (void )fprintf(stderr, "  binFlg = %d\n", binFlg);
    (void )fprintf(stderr, "  thrType = %s\n",
	    (thrType == WLZ_THRESH_LOW)? "WLZ_THRESH_LOW": "WLZ_THRESH_HIGH");
    (void )fprintf(stderr, "  refThr = %g\n", refThr);
    (void )fprintf(stderr, "  srcThr = %g\n", srcThr);
    (void )fprintf(stderr, "  refCThr = %g\n", refCThr);
    (void )fprintf(stderr, "  srcCThr = %g\n", srcCThr);
    (void )fprintf(stderr, "  refMedianSz = %d\n", refMedianSz);
    (void )fprintf(stderr, "  srcMedianSz = %d\n", srcMedianSz);
    (void )fprintf(stderr, "  refSmooth = %g\n", refSmooth);
    (void )fprintf(stderr, "  srcSmooth = %g\n", srcSmooth);
    (void )fprintf(stderr, "  delta = %g\n", delta);
    (void )fprintf(stderr, "  maxItr = %d\n", maxItr);
    (void )fprintf(stderr, "  minSpx = %d\n", minSpx);
    (void )fprintf(stderr, "  maxAng = %g (Radians)\n", maxAng);
    (void )fprintf(stderr, "  maxDeform = %g\n", maxDeform);
    (void )fprintf(stderr, "  maxDisp = %g\n", maxDisp);
    (void )fprintf(stderr, "  matchImpThr = %g\n", matchImpThr);
    (void )fprintf(stderr, "  matchImpNN = %d\n", matchImpNN);
    (void )fprintf(stderr, "  ctrFileBaseStr = %s\n",
		   ctrFileBaseStr? ctrFileBaseStr: nullStr);
    (void )fprintf(stderr, "  noMatching = %d\n", noMatching);
    (void )fprintf(stderr, "  usage = %d\n", usage);
  }
  /* Create the initial affine transform and it's inverse. */
  if(ok)
  {
    if(inTrFileStr)
    {
      tObj0 = NULL;
      if(((fP = fopen(inTrFileStr, "r")) == NULL) ||
         ((tObj0 = WlzReadObj(fP, &errNum)) == NULL) ||
	 (tObj0->type != WLZ_AFFINE_TRANS) ||
	 (tObj0->domain.core == NULL))
      {
        errNum = WLZ_ERR_READ_INCOMPLETE;
      }
      else
      {
        inTr = WlzAffineTransformCopy(tObj0->domain.t, &errNum);
      }
      if(fP)
      {
        (void )fclose(fP);
	fP = NULL;
      }
      if(tObj0)
      {
        WlzFreeObj(tObj0);
	tObj0 = NULL;
      }
    }
    else
    {
      inTr = WlzMakeAffineTransform(WLZ_TRANSFORM_2D_AFFINE, &errNum);
      if(errNum == WLZ_ERR_NONE)
      {
	errNum = WlzAffineTransformPrimSet(inTr, inTrPrim);
      }
    }
    if(errNum == WLZ_ERR_NONE)
    {
      if(verbose)
      {
        (void )fprintf(stderr, "Affine transform inTr = \n");
	(void )AlcDouble2WriteAsci(stderr, inTr->mat, 3, 3);
      }
    }
    if(errNum != WLZ_ERR_NONE)
    {
      ok = 0;
      (void )WlzStringFromErrorNum(errNum, &errMsg);
      (void )fprintf(stderr,
      		     "%s: Failed to set initial affine transform (%s).\n",
		     *argv, errMsg);
    }
  }
  /* If the reference file name was given on the command line then it use it
   * in place of the reference file name in the section parameters bibfile. */
  if(ok && refObjFileStr)
  {
    if(((refObj3D = WlzAssignObject(
		    WlzEffReadObj(NULL, refObjFileStr, refObjFileType,
				  0, 0, 0, &errNum), NULL)) == NULL) ||
       (errNum != WLZ_ERR_NONE))
    {
      ok = 0;
      (void )WlzStringFromErrorNum(errNum, &errMsg);
      (void )fprintf(stderr,
      "%s Failed to read the reference object from file %s (%s).\n",
      *argv, refObjFileStr, errMsg);
    }
  }
  if(ok)
  {
    if((lFP = (strcmp(inFileStr, "-")?
             fopen(inFileStr, "r"): stdin)) == NULL)
    {
      ok = 0;
      (void )fprintf(stderr,
		   "%s Failed to open the input section parameters file %s.\n",
		    *argv, inFileStr);
    }
  }
  /* Get each input section parameters file and process it. */
  index = 0;
  while(ok &&
        ((fP = WlzMatchICPPlaneSecParFile(lFP, multipleFiles, index,
					  secParFile)) != NULL))
  {
    errNum = WlzMatchICPPlaneReadSecParam(fP, &view,
	refObjFileStr? NULL: &refObjFileStr, &refObjFileType,
	refObj3D? NULL: &refObj3D,
	&srcObjFileStr, &srcObjFileType, &srcObj2D);
    /* Note: srcObj2D assigned in WlzMatchICPPlaneReadSecParam(). */
    if(errNum == WLZ_ERR_NONE)
    {
      if(verboseDat)
      {
	if(verbose)
	{
	  (void )fprintf(stderr,
			 "Writing srcObj2D to dbg-srcObj2D.wlz.\n");
	}
	if((vFP = fopen("dbg-srcObj2D.wlz", "w")) != NULL)
	{
	  (void )WlzWriteObj(vFP, srcObj2D);
	  (void )fclose(vFP);
	}
      }
      errNum = WlzInit3DViewStruct(view, refObj3D);
    }
    if(fP)
    {
      fclose(fP);
      fP = NULL;
    }
    if(errNum != WLZ_ERR_NONE)
    {
      ok = 0;
      (void )WlzStringFromErrorNum(errNum, &errMsg);
      (void )fprintf(stderr,
       "%s Failed to read input section parameters from file %s (%s).\n",
       argv[0], secParFile, errMsg);
    }
    /* Create the section object. */
    if(ok)
    {
      refObj2D = WlzAssignObject(
      		 WlzMatchICPPlaneGetSection(refObj3D, view, interp,
		 		            &errNum), NULL);
      if((errNum == WLZ_ERR_NONE) && (refObj2D != NULL) &&
	  (refObj2D->type = WLZ_2D_DOMAINOBJ) && (refObj2D->domain.core))
      {
	refObj2DOrg.vtX = refObj2D->domain.i->kol1;
	refObj2DOrg.vtY = refObj2D->domain.i->line1;
	if(verboseDat)
	{
	  if(verbose)
	  {
	    (void )fprintf(stderr,
			   "Writing refObj2D to dbg-refObj2D.wlz.\n");
	  }
	  if((vFP = fopen("dbg-refObj2D.wlz", "w")) != NULL)
	  {
	    (void )WlzWriteObj(vFP, refObj2D);
	    (void )fclose(vFP);
	  }
	}
      }
      else
      {
	ok = 0;
        (void )WlzStringFromErrorNum(errNum, &errMsg);
	(void )fprintf(stderr,
	"%s Failed to get section from reference object (%s).\n",
	argv[0], errMsg);
      }
    }
    /* Create the contour objects. */
    if(ok)
    {
      refCObj2D = WlzMatchICPPlaneCreateContourObj(refObj2D,
				binFlg, thrType, refThr,
      				refMedianSz, refSmooth,
				refCThr, minSpx, debug,
				verboseDat? "dbg-refObj2D.wlz": NULL,
				&errNum);
      if(errNum == WLZ_ERR_NONE)
      {
	srcCObj2D = WlzMatchICPPlaneCreateContourObj(srcObj2D,
				binFlg, thrType, srcThr,
				srcMedianSz, srcSmooth,
				srcCThr, minSpx, debug,
				verboseDat? "dbg-srcObj2D.wlz": NULL,
				&errNum);
      }
      if(errNum != WLZ_ERR_NONE)
      {
	ok = 0;
        (void )WlzStringFromErrorNum(errNum, &errMsg);
	(void )fprintf(stderr, "%s Failed to compute contours (%s).\n",
		       argv[0], errMsg);
      }
    }
    /* Compute translation using centre of mass if required. Then create
     * modified initial and inverse transforms for this plane. */
    if(ok)
    {
      if(useCOfM)
      {
	if(verbose)
	{
	  (void )fprintf(stderr,
	  "Using centre of mass to refine initial affine transform.\n");
	}
	refCOfM = WlzCentreOfMass2D(refCObj2D, 0, NULL, &errNum);
	tObj0 = NULL;
	if(errNum == WLZ_ERR_NONE)
	{
	  tObj0 = WlzAssignObject(
		  WlzAffineTransformObj(srcCObj2D, inTr,
					WLZ_INTERPOLATION_NEAREST,
					&errNum), NULL);
	}
	if(errNum == WLZ_ERR_NONE)
	{
	  srcCOfM = WlzCentreOfMass2D(tObj0, 0, NULL, &errNum);
	}
	(void )WlzFreeObj(tObj0);
	if(errNum == WLZ_ERR_NONE)
	{
	  if(verbose)
	  {
	    (void )fprintf(stderr,
	    "centres of mass are: refCOfM = {%g,%g}, srcCOfM = {%g,%g}.\n",
	    refCOfM.vtX, refCOfM.vtY, srcCOfM.vtX, srcCOfM.vtY);
	  }
	  inPTr = WlzAffineTransformCopy(inTr, &errNum);
	}
	if(errNum == WLZ_ERR_NONE)
	{
	  WLZ_VTX_2_SUB(tDV0, refCOfM, srcCOfM);
	  inPTr->mat[0][2] += tDV0.vtX;
	  inPTr->mat[1][2] += tDV0.vtY;
	}
	if(errNum != WLZ_ERR_NONE)
	{
	  ok = 0;
	  (void )WlzStringFromErrorNum(errNum, &errMsg);
	  (void )fprintf(stderr,
	  "%s Failed to modify transform using centre of mass (%s).\n",
			 argv[0], errMsg);
	}
      }
      if(errNum == WLZ_ERR_NONE)
      {
	if(verbose)
	{
	  (void )fprintf(stderr, "Affine transform inPTr = ");
	  if(inPTr)
	  {
	    (void )fprintf(stderr, "\n");
	    (void )AlcDouble2WriteAsci(stderr, inPTr->mat, 3, 3);
	  }
	  else
	  {
	    (void )fprintf(stderr, "Identity\n");
	  }
	}
      }
    }
    if(ok && ctrFileBaseStr)
    {
      if(multipleFiles)
      {
        sprintf(fileNameBuf, "%s_%06d_ctr_ref.wlz", ctrFileBaseStr, index);
      }
      else
      {
        sprintf(fileNameBuf, "%s_ctr_ref.wlz", ctrFileBaseStr);
      }
      errNum = WLZ_ERR_FILE_OPEN;
      ok = (fP = fopen(fileNameBuf, "w")) != NULL;
      if(ok)
      {
	ok = (errNum = WlzWriteObj(fP, refCObj2D)) == WLZ_ERR_NONE;
      }
      if(fP)
      {
	(void )fclose(fP);
	fP = NULL;
      }
      if(ok)
      {
	if(multipleFiles)
	{
	  sprintf(fileNameBuf, "%s_%06d_ctr_src.wlz", ctrFileBaseStr, index);
	}
	else
	{
	  sprintf(fileNameBuf, "%s_ctr_src.wlz", ctrFileBaseStr);
	}
	ok = (fP = fopen(fileNameBuf, "w")) != NULL;
      }
      if(ok)
      {
	ok = (errNum = WlzWriteObj(fP, srcCObj2D)) == WLZ_ERR_NONE;
      }
      if(fP)
      {
	(void )fclose(fP);
	fP = NULL;
      }
      tObj0 = NULL;
      if(ok)
      {
        tObj0 = WlzAffineTransformObj(srcCObj2D, inPTr,
				      WLZ_INTERPOLATION_NEAREST, &errNum);
        ok = errNum == WLZ_ERR_NONE;
      }
      if(ok)
      {
        if(multipleFiles)
	{
	  sprintf(fileNameBuf, "%s_%06d_ctr_itrsrc.wlz", ctrFileBaseStr,
	  	  index);
	}
	else
	{
	  sprintf(fileNameBuf, "%s_ctr_itrsrc.wlz", ctrFileBaseStr);
	}
	ok = (fP = fopen(fileNameBuf, "w")) != NULL;
      }
      if(ok)
      {
	ok = (errNum = WlzWriteObj(fP, tObj0)) == WLZ_ERR_NONE;
      }
      (void )WlzFreeObj(tObj0);
      if(fP)
      {
        (void )fclose(fP);
	fP = NULL;
      }
      if(!ok)
      {
	(void )WlzStringFromErrorNum(errNum, &errMsg);
	(void )fprintf(stderr,
		       "%s Failed to write contour to file %s (%s)\n",
		       argv[0], fileNameBuf, errMsg);
      }
    }
    if(ok && (noMatching == 0))
    {
      /* Set up weighting function callback data. */
      cbData.tGM = refCObj2D->domain.ctr->model;
      cbData.sGM = srcCObj2D->domain.ctr->model;
      cbData.maxDisp = maxDisp;
      cbData.nScatter = nScatter;
      errNum = WlzMatchICPCtr(refCObj2D->domain.ctr, srcCObj2D->domain.ctr,
			      inPTr, maxItr, minSpx, minSegSpx,
			      &nMatch, &matchRP, &matchSP, decompLimit,
			      maxDisp, maxAng, maxDeform,
			      matchImpNN, matchImpThr,
			      WlzMatchICPWeightMatches, &cbData,
			      delta);
      if(errNum != WLZ_ERR_NONE)
      {
	ok = 0;
	(void )WlzStringFromErrorNum(errNum, &errMsg);
	(void )fprintf(stderr,
		       "%s Failed to compute tie-points from contours (%s).\n",
		       argv[0], errMsg);
      }
    }
    if(ok && verboseDat)
    {
	if(verbose)
	{
	  (void )fprintf(stderr,
			 "Writing tie points to dbg-tiepoints.num.\n");
	}
	if((vFP = fopen("dbg-tiepoints.num", "w")) != NULL)
	{
	  for(idx0 = 0; idx0 < nMatch; ++ idx0)
	  {
	    (void )fprintf(vFP, "%g %g %g %g\n",
		       (matchSP.d2 + idx0)->vtX,
		       (matchSP.d2 + idx0)->vtY,
		       (matchRP.d2 + idx0)->vtX - (matchSP.d2 + idx0)->vtX,
		       (matchRP.d2 + idx0)->vtY - (matchSP.d2 + idx0)->vtY);
	  }
	  (void )fclose(vFP);
	}
    }
    if(ok && ctrFileBaseStr)
    {
      if(multipleFiles)
      {
	sprintf(fileNameBuf, "%s_%06d_ctr_dcp.wlz", ctrFileBaseStr, index);
      }
      else
      {
	sprintf(fileNameBuf, "%s_ctr_dcp.wlz", ctrFileBaseStr);
      }
      if((fP = fopen(fileNameBuf, "w")) == NULL)
      {
	ok = 0;
	(void )fprintf(stderr, "%s Failed to open contour file %s\n",
		       argv[0], fileNameBuf);
      }
      else
      {
	if((errNum = WlzWriteObj(fP, srcCObj2D)) != WLZ_ERR_NONE)
	{
	  ok = 0;
	  (void )WlzStringFromErrorNum(errNum, &errMsg);
	  (void )fprintf(stderr,
	  		 "%s Failed to write contour to file %s (%s)\n",
			 argv[0], fileNameBuf, errMsg);
	}
	(void )fclose(fP);
	fP = NULL;
      }
    }
    /* Write the new section parameters file together with the computed
     * tie-points. */
    if(ok)
    {
      if(multipleFiles)
      {
        sprintf(fileNameBuf, "%s_%06d.bib", outFileBaseStr, index);
      }
      else
      {
        sprintf(fileNameBuf, "%s.bib", outFileBaseStr);
      }
      if((fP = fopen(fileNameBuf, "w")) == NULL)
      {
	ok = 0;
	(void )fprintf(stderr,
		 "%s Failed to open the output section parameters file %s.\n",
		 *argv, fileNameBuf);
      }
    }
    if(ok)
    {
      if(verbose)
      {
	(void )fprintf(stderr, "Writing section parameters.\n");
      }
      errNum = WlzMatchICPPlaneWriteSecParam(fP, view,
	  refObjFileStr, refObjFileType,
	  srcObjFileStr, srcObjFileType,
	  nMatch, matchRP.d2, matchSP.d2,
	  removeRefOrg? &refObj2DOrg: NULL);
      if(fP && strcmp(outFileBaseStr, "-"))
      {
	fclose(fP);
	fP = NULL;
      }
      if(errNum != WLZ_ERR_NONE)
      {
	ok = 0;
	(void )WlzStringFromErrorNum(errNum, &errMsg);
	(void )fprintf(stderr,
	"%s Failed to write output section parameters to file %s (%s).\n",
	argv[0], fileNameBuf, errMsg);
      }
    }
    WlzFreeAffineTransform(inPTr); inPTr = NULL;
    ++index;
  }
  (void )WlzFree3DViewStruct(view);
  AlcFree(refObjFileStr);
  AlcFree(srcObjFileStr);
  AlcFree(matchRP.v);
  AlcFree(matchSP.v);
  (void )WlzFreeAffineTransform(inTr);
  (void )WlzFreeObj(refObj3D);
  (void )WlzFreeObj(refObj2D);
  (void )WlzFreeObj(srcObj2D);
  (void )WlzFreeObj(refCObj2D);
  (void )WlzFreeObj(srcCObj2D);
  if(usage)
  {
      (void )fprintf(stderr,
      "Usage: %s%s%s%s",
      *argv,
      " [-h] [-d] [-v] [-V]\n"
      "                        [-o<output file base>] [-r <reference file>]\n"
      "                        [-Y] [-t#] [-x#] [-y#] [-a#] [-s#] [-e]\n"
      "                        [-g#,#] [-k#,#] [-u#,#] [-b #] [-B #]\n"
      "                        [-f] [-i#] [-s#] [-A#] [-S#] [-F#] [-m#] [-n#]\n"
      "                        [-c<contour file base>] [-N] [-L]\n"
      "                        [<section parameters file>]\n"
      "Version: ",
      WlzVersion(),
      "\n"
      "Options:\n"
      "  -h  Prints this usage information.\n"
      "  -d  Perform extra tests to aid debuging.\n"
      "  -E  Tolerance in mean registration metric value.\n"
      "  -v  Be verbose, lots of text output to the standard error output!\n"
      "  -V  Be verbose with Woolz objects and data, with file name names\n"
      "      prefixed by dbg-.\n"
      "  -o  Output file base.\n"
      "  -r  Reference file.\n"
      "  -Y  The section parameters file is a list of section parameters\n"
      "      files, one per line.\n"
      "  -e  Use centres of mass of geometric models to compute translation,\n"
      "      with this translaton being applied after the optional initial\n"
      "      affine transform.\n"
      "  -t  Initial affine transform (if given all initial affine\n"
      "      transform primitives are ignored).\n"
      "  -x  Initial horizontal translation.\n"
      "  -y  Initial vertical translation.\n"
      "  -a  Initial angle of rotation (degrees).\n"
      "  -s  Initial scale factor.\n"
      "  -g  Maximal gradient contour thresholds, with the format:\n"
      "      <ref threshold>,<src threshold>, either may be omitted.\n"
      "  -k  Median filter size, with the format:\n"
      "      <ref size>,<src size>, either may be omitted.\n"
      "  -u  Gaussian smoothing factors, with the format:\n"
      "      <ref smooth>,<src smooth>, either may be omitted.\n"
      "  -b  Low threshold values used to produce binary mask images for\n"
      "      matching instead of the grey valued images, with objects\n"
      "      having values at or below the given thresholds.\n"
      "  -B  High threshold values used to produce binary mask images for\n"
      "      matching instead of the grey valued images, with objects\n"
      "      having values above the given thresholds.\n"
      "  -f  Keep the reference offset in the reference tie-points (MAPaint\n"
      "      doesn't do this).\n"
      "  -i  Maximum number of iterations.\n"
      "  -p  Minimum number of simplices per shell.\n"
      "  -P  Minimum number of simplices per matched shell segment, with a\n"
      "      pair of correspondence points possibly being generated per\n"
      "      matched shell segment.\n"
      "  -A  Maximum angle (degrees) from a global transformation.\n"
      "  -S  Maximum displacement from a global transformed position.\n"
      "  -F  Maximum deformation from a global transformation.\n"
      "  -m  Implausibility threshold for rejecting implausible\n"
      "      correspondence points which should be greater than zero,\n"
      "      although the useful range is probably [0.5-5.0]. Higher\n"
      "      values allow more implausible matches to be returned.\n"
      "  -n  Number of match points in neighbourhood when checking the\n"
      "	     plausibility of the correspondence points.\n"
      "  -c  Outputs the computed and decomposed geometric models using\n"
      "	     the given file base.\n"
      "  -N  Don't compute the tie-points.\n"
      "  -L  Use linear interpolation (instead of nearest neighbour) when\n"
      "      cuting sections.\n"
      "  Reads either a 2D or 3D reference object and an MAPaint section\n"
      "parameters file. Computes tie-points and then writes a new MAPaint\n"
      "section parameters file which includes the tie-points.\n"
      "  An initial affine transform is computed from the (optional) initial\n"
      "translation, rotation and scale parameters. A centre of mass can be\n"
      "computed to improve the initial translation estimates. If a centre of\n"
      "mass computation is used then the images must have background with\n"
      "high values and foreground with low values. This initial affine\n"
      "transform is appiled to the source image before computing the\n"
      "tie-points. Once computed, the tie-points are transformed using the\n"
      "inverse of the intial transform, before output.\n"
      "  The tie-points are computed using an ICP based matching algorithm\n"
      "in which geometric models built from the maximal gradient edges\n"
      "extracted from the computed section of the reference object and\n"
      "the source object (refered to in the section parameters file).\n"
      "  To aid rejection of poor tie-points, the tie-points are ranked by\n"
      "plausibility, with the most plausible first.\n");
  }
  return(!ok);
}