/*!
* \return	Woolz error code.
* \ingroup	WlzTransform
* \brief	Checks whether the given 2D affine transform is near
*		to an identity transform. If it's near it is set to
*		an identity transform.
* \param	tr			Given 2D affine transform.
* \param	tol			Tolerance value.
*/
WlzErrorNum	WlzTransformNearIdentity2D(WlzAffineTransform *tr, double tol)
{
  WlzAffineTransformPrim prim;
  WlzErrorNum	errNum = WLZ_ERR_NONE;

  if(tr)
  {
    errNum = WLZ_ERR_DOMAIN_NULL;
  }
  if(errNum == WLZ_ERR_NONE)
  {
    errNum = WlzAffineTransformPrimGet(tr, &prim);
  }
  if(errNum == WLZ_ERR_NONE)
  {
    if(!finite(prim.tx) || (fabs(prim.tx) <= tol))
    {
      prim.tx = 0.0;
    }
    if(!finite(prim.ty) || (fabs(prim.ty) <= tol))
    {
      prim.ty = 0.0;
    }
    if(!finite(prim.tz) || (fabs(prim.tz) <= tol))
    {
      prim.tz = 0.0;
    }
    if(!finite(prim.scale) || (fabs(prim.scale - 1.0) <= tol))
    {
      prim.scale = 1.0;
    }
    if(!finite(prim.theta) || (fabs(prim.theta) <= tol))
    {
      prim.theta = 0.0;
    }
    if(!finite(prim.phi) || (fabs(prim.phi) <= tol))
    {
      prim.phi = 0.0;
    }
    if(!finite(prim.alpha) || (fabs(prim.alpha) <= tol))
    {
      prim.alpha = 0.0;
    }
    if(!finite(prim.psi) || (fabs(prim.psi) <= tol))
    {
      prim.psi = 0.0;
    }
    if(!finite(prim.xsi) || (fabs(prim.xsi) <= tol))
    {
      prim.xsi = 0.0;
    }
    errNum = WlzAffineTransformPrimSet(tr, prim);
  }
  return(errNum);
}
Beispiel #2
0
void checkTrans(
  WlzAffineTransform *trans)
{
  WlzAffineTransformPrim prim;
  double	tol = 1.0e-6;
  WlzErrorNum	errNum = WLZ_ERR_NONE;

  if( trans == NULL ){
    return;
  }
  errNum = WlzAffineTransformPrimGet(trans, &prim);

  if(errNum  == WLZ_ERR_NONE)
  {
    if( !finite(prim.tx) || (fabs(prim.tx) <= tol) ){
      prim.tx = 0.0;
    }
    if( !finite(prim.ty) || (fabs(prim.ty) <= tol) ){
      prim.ty = 0.0;
    }
    if( !finite(prim.tz) || (fabs(prim.tz) <= tol) ){
      prim.tz = 0.0;
    }
    if( !finite(prim.scale) || (fabs(prim.scale - 1.0) <= tol) ){
      prim.scale = 1.0;
    }
    if( !finite(prim.theta) || (fabs(prim.theta) <= tol) ){
      prim.theta = 0.0;
    }
    if( !finite(prim.phi) || (fabs(prim.phi) <= tol) ){
      prim.phi = 0.0;
    }
    if( !finite(prim.alpha) || (fabs(prim.alpha) <= tol) ){
      prim.alpha = 0.0;
    }
    if( !finite(prim.psi) || (fabs(prim.psi) <= tol) ){
      prim.psi = 0.0;
    }
    if( !finite(prim.xsi) || (fabs(prim.xsi) <= tol) ){
      prim.xsi = 0.0;
    }

    errNum = WlzAffineTransformPrimSet(trans, prim);
  }
  return;
}
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);
}