Пример #1
0
/*!
* \return	Integer array with values and coordinates from 3D object.
* \ingroup	WlzValueUtils
* \brief	Allocates a new array (4 ints per value: 0 = value,
* 		1 = x coordinate, 2 = y coordinate and 3 = z coordinate.
* \param	obj			Given object which must be a valid
* 					3D domain object with integer values.
* \param	dstNAry			Destination pointer for the number of
* 					values, must not be NULL.
* \param	dstErr			Destination error pointer, may be NULL.
*/
static int	*WlzCompDispMakeValAry3D(WlzObject *obj, int *dstNAry,
				         WlzErrorNum *dstErr)
{
  int		idO,
  		idP,
		nAry;
  int		*ary,
  		*array = NULL;
  WlzObject	*obj2D;
  WlzPlaneDomain *pDom;
  WlzErrorNum	errNum = WLZ_ERR_NONE;

  if((nAry = WlzVolume(obj, &errNum)) <= 0)
  {
    errNum = WLZ_ERR_DOMAIN_DATA;
  }
  if(errNum == WLZ_ERR_NONE)
  {
    if((array = AlcMalloc(nAry * 4 * sizeof(int))) == NULL)
    {
      errNum = WLZ_ERR_MEM_ALLOC;
    }
  }
  if(errNum == WLZ_ERR_NONE)
  {
    ary = array;
    pDom = obj->domain.p;
    for(idP = pDom->plane1; (errNum == WLZ_ERR_NONE) && (idP <= pDom->lastpl);
        ++idP)
    {
      idO = idP - pDom->plane1;
      obj2D = WlzMakeMain(WLZ_2D_DOMAINOBJ,
			  *(obj->domain.p->domains + idO),
			  *(obj->values.vox->values + idO),
			  NULL, NULL, &errNum);
      if(errNum == WLZ_ERR_NONE)
      {
	errNum = WlzCompDispSetAry(&ary, obj2D, idP, 3);
	WlzFreeObj(obj2D);
      }
    }
  }
  if(errNum != WLZ_ERR_NONE)
  {
    AlcFree(ary);
    ary = NULL;
  }
  else
  {
    *dstNAry = nAry;
    if(dstErr != NULL)
    {
      *dstErr = errNum;
    }
  }
  return(array);
}
Пример #2
0
static int WlzSize(
  WlzObject	*obj,
  WlzErrorNum	*dstErr)
{
  int	size;
  WlzErrorNum	errNum=WLZ_ERR_NONE;

  if( obj ){
    switch(obj->type){
    case WLZ_2D_DOMAINOBJ:
      size = WlzArea(obj, &errNum);
      break;

    case WLZ_3D_DOMAINOBJ:
      size = WlzVolume(obj, &errNum);
      break;

    case WLZ_EMPTY_OBJ:
      size = 0;
      break;

    default:
      size = -1;
      errNum = WLZ_ERR_OBJECT_TYPE;
      break;
    }
  }
  else {
    size = -1;
    errNum = WLZ_ERR_OBJECT_NULL;
  }

  if( dstErr ){
    *dstErr = errNum;
  }
  return size;
}
Пример #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);
}
Пример #4
0
/*!
* \return	RCC classification of the given objects, ie object 0 is a
*               returned classification of object 1.
* \ingroup	WlzBinaryOps
* \brief	The given pair of spatial domain objects are classified
*		using a RCC with optional enclosure and offset classifications.
*
*		For an explanation of RCC8 classifications
*		see the type definition ::WlzRCCClass and the paper:
*		D.A. Randell, etal,
*		"Discrete Mereotopology for Spatial Reasoning in
*		Automated Histological Image Analysis", PAMI 35(3) 2013.
*		The RCC8 has been extended to include both tangential and
*		non-tangential surrounds.
*
* 		The RCC classification is performed using simple combinations
* 		of the Woolz union, intersection, exclusive or, dilation,
* 		fill and convex hull operators on an ordered pair of
* 		spatial domains(\f$\Omega_0\f$ and \f$\Omega_1\f$):
*               \f{eqnarray*}{
                  C_0 &\leftarrow&
		    \Omega_0   \cap \Omega_1
		    \neq \emptyset \\
                  C_1 &\leftarrow&
		    \Omega_0^+ \cap \Omega_1
		    \neq \emptyset \\
                  C_2 &\leftarrow&
		    (\Omega_0   \oplus \Omega_1)
		    \neq \emptyset \\
                  C_3 &\leftarrow&
		    (\Omega_0   \cup \Omega_1)   \oplus
		    \Omega_1 
		    \neq \emptyset \\
                  C_4 &\leftarrow&
		    (\Omega_0^+ \cup \Omega_1)   \oplus
		    \Omega_1 
		    \neq \emptyset \\
                  C_5 &\leftarrow&
		    (\Omega_0   \cup \Omega_1)   \oplus
		    \Omega_0 
		    \neq \emptyset \\
                  C_6 &\leftarrow&
		    (\Omega_0   \cup \Omega_1^+) \oplus
		    \Omega_0 
		    \neq \emptyset \\
		  C_7 &\leftarrow&
		    (\Omega_0^{\bullet} \cup \Omega_1) \oplus
		    \Omega_0^{\bullet}
		    \neq \emptyset \\
		  C_8 &\leftarrow&
		    (\Omega_0 \cup \Omega_1^{\bullet}) \oplus
		    \Omega_1^{\bullet}
		    \neq \emptyset \\
		  C_9 &\leftarrow&
		    2|\Omega_0 \cap \Omega_1^{\circ}|   \ge |\Omega_0| \\
		  C_{10} &\leftarrow&
		    2|\Omega_0^{\circ}   \cap \Omega_1| \ge |\Omega_1|
  		\f}
*		where
*		  are the \f$\cup\f$, \f$\cap\f$ and \f$\oplus\f$
*		  are the set union (logical or), intersection (logical and)
*		  and xor (logical exclusive or) operators;
*		  \f$\Omega^+\f$ indicates the dilation of \f$\Omega\f$,
*		  \f$\Omega^{\circ}\f$ the convex hull of \f$\Omega\f$,
*		  \f$\Omega^{\bullet}\f$ indicates \f$\Omega\f$ filled and
*		  \f$|\Omega|\f$ the cardinality (area or volume) of
*		  \f$\Omega\f$.
* 		The decision tree for the classification excluding
* 		enclosure and offset is:
* 		\f[
C_0
\left\{
\begin{array}{ll}
0 & C_1
    \left\{
    \begin{array}{ll}
    0 & C_7
	\left\{
	\begin{array}{ll}
	0 & NTSURI \\
	  & \\
	  & \\
	1 & C_8
	    \left\{
	    \begin{array}{ll}
	    0 & NTSUR \\
	      & \\
	    1 & DC
	    \end{array}
	    \right. \\
	\end{array}
	\right. \\
      & \\
      & \\
    1 & C_7
	\left\{
	\begin{array}{ll}
	0 & TSURI \\
	  & \\
	  & \\
	1 & C_8
	    \left\{
	    \begin{array}{ll}
	    0 & TSUR \\
	      & \\
	    1 & EC
	    \end{array}
	    \right. \\
	\end{array}
	\right. \\
    \end{array}
    \right. \\
  & \\
  & \\
1 & C_2
    \left\{
    \begin{array}{ll}
    0 & EQ \\
      & \\
      & \\
    1 & C_3
	\left\{
	\begin{array}{ll}
	0 & C_4
	    \left\{
	    \begin{array}{ll}
	    0 & NTPP \\
	      & \\
	    1 & TPP
	    \end{array}
	    \right. \\
	  & \\
	  & \\
	1 & C_5
	    \left\{
	    \begin{array}{ll}
	    0 & C_6
		\left\{
		\begin{array}{ll}
		0 & NTPPI \\
		 & \\
		1 & TPPI
		\end{array}
		\right. \\
	      & \\
	    1 & PO
	    \end{array}
	    \right. \\
	\end{array}
	\right. \\
    \end{array}
    \right. \\
\end{array}
\right.
		\f]
*		The statistics are computed for each classification
*		as below:
* 		<table width="500" border="0">
		<caption>
		  Basic Morphological Operations for the RCC Spatial
		  Relationships
		</caption>
                <tr>
                  <td>RCC</td>
		  <td>Normalised Volume</td>
                </tr>
		<tr>
		  <td>\f$EMPTY(\Omega_0,\Omega_1)\f$</td>
		  <td>0.0</td>
		</tr>
		<tr>
		  <td>\f$DC(\Omega_0,\Omega_1)\f$</td>
		  <td>0.0</td>
		</tr>
		<tr>
		  <td>\f$EC(\Omega_0,\Omega_1)\f$</td>
		  <td>0.0</td>
		</tr>
		<tr>
		  <td>\f$EQ(\Omega_0,\Omega_1)\f$</td>
		  <td>1.0</td>
		</tr>
		<tr>
		  <td>\f$PO(\Omega_0,\Omega_1)\f$</td>
		  <td>\f$|\Omega_0 \cap \Omega_1|/
		         |\Omega_0 \cup \Omega_1|\f$</td>
		</tr>
		<tr>
		  <td>\f$TPP(\Omega_0,\Omega_1)\f$</td>
		  <td>\f$|\Omega_0|/|\Omega_0 \cup \Omega_1|\f$</td>
		</tr>
		<tr>
		  <td>\f$NTPP(\Omega_0,\Omega_1)\f$</td>
		  <td>\f$|\Omega_0|/|\Omega_0 \cup \Omega_1|\f$</td>
		</tr>
		<tr>
		  <td>\f$TPPI(\Omega_0,\Omega_1)\f$</td>
		  <td>\f$|\Omega_1|/|\Omega_0 \cup \Omega_1|\f$</td>
		</tr>
		<tr>
		  <td>\f$NTPPI(\Omega_0,\Omega_1)\f$</td>
		  <td>\f$|\Omega_1|/|\Omega_0 \cup \Omega_1|\f$</td>
		</tr>
		<tr>
		  <td>\f$TSUR(\Omega_0,\Omega_1)\f$</td>
		  <td>\f$|\Omega_0|/|\Omega_0 \cup \Omega_1|\f$</td>
		</tr>
		<tr>
		  <td>\f$TSURI(\Omega_0,\Omega_1)\f$</td>
		  <td>\f$|\Omega_1|/|\Omega_0 \cup \Omega_1|\f$</td>
		</tr>
		<tr>
		  <td>\f$NTSUR(\Omega_0,\Omega_1)\f$</td>
		  <td>\f$|\Omega_0|/|\Omega_0 \cup \Omega_1|\f$</td>
		</tr>
		<tr>
		  <td>\f$NTSURI(\Omega_0,\Omega_1)\f$</td>
		  <td>\f$|\Omega_1|/|\Omega_0 \cup \Omega_1|\f$</td>
		</tr>
		<tr>
		  <td>\f$ENC(\Omega_0,\Omega_1)\f$</td>
		  <td>\f$|\Omega_0 \cap \Omega_1^{\circ}|/|\Omega_0|\f$</td>
		</tr>
		<tr>
		  <td>\f$ENCI(\Omega_0,\Omega_1)\f$</td>
		  <td>\f$|\Omega_0^{\circ} \cap \Omega_1|/|\Omega_1|\f$</td>
		</tr>
		<tr>
		  <td>\f$OST(\Omega_0,\Omega_1)\f$</td>
		  <td>\f$q_1/(q_1 + q_2 - q_0)\f$</td>
		</tr>
		</table>
*
*		Many of the objects that are computed during the
*		classification are done so using a lazy evaluation with the
*		functions WlzRCCMakeC() and WlzRCCMakeT().
*
*		Enclosure and offset are somwhat more expensive to compute
*		than the other classifications, for this reason and because
*		they are not strictly part of a RCC they can be avoided by
*		setting the noEnc or noOst flags.
*
*		Enclosure will be computed if the noEnc has not been set and
*		the classification is not one of WLZ_RCC_EQ, WLZ_RCC_TSUR,
*		WLZ_RCC_TSURI, WLZ_RCC_NTSUR or WLZ_RCC_NTSURI.
*		Enclosure is computed using:
*		\f[
		  |\Omega_0 \cap \Omega_1^{\circ}|/|\Omega_0|
		\f]
*		for \f$\Omega_0\f$ to be encloded by \f$\Omega_1\f$ then
*		at least half of \f$\Omega_0\f$ must intersect the convex
*		hull of \f$\Omega_1\f$.
*
*		Offset will be computed if the noOst parameter has not
*		been set and the classification is not WLZ_RCC_EQ.
*		Offset is computed within a restricted domain in which
*		all pixels/voxels are equidistant for the domains of the
*		given objects:
		\f[
		\Omega_e = (\Omega_0 \cup \Omega_1)^\circ \cap
		           \Omega_0^{+d_{max}} \cap
			   \Omega_1^{+d_{max}} \cap
			   \Omega(D(\Omega_0) = D(\Omega_1))
		\f]
*		where \f$D(\Omega)\f$ is the distance transform of the domain
*		\f$\Omega\f$. Within \f$\Omega_e\f$ the first, second and
*		third quantiles (\f$q_0\f$, \f$q_1\f$ and \f$q_2\f$) of
*		the distances \f$D(\Omega_0)\f$ (or equivalently
*		\f$D(\Omega_1)\f$) are computed. The ratio of the median
*		to the median plus interquartile range is then computed
*		and the domains are classified as offset if this ratio
*		is greater than or equal to one half:
*		\f[
		\frac{q_1}{q_1 + q_2 - q_0} \geq 0.5
		\f]
* \param	obj0			First given spatial domain object.
* \param	obj1			Second given spatial domain object.
* \param	noEnc			Don't include enclosure if non-zero.
* \param	noOst			Don't include offset if non-zero.
* \param	maxOstDist		Maximum distance for offset, not
* 					used if noOst is non-zero.
* \param	dstStatCnt		Destination pointer for the number
* 					of elements returned in the array of
* 					statistics (see above), may be NULL.
* 					Ignored if dstStatAry is NULL.
* \param	dstStatAry		Destination pointer for an array of
* 					statistics (see above), may be NULL.
* 					If an array is returned it should be
* 					freed using AlcFree().
* \param	dstErr			Destination error pointer, may be NULL.
*/
WlzRCCClass 			WlzRegConCalcRCC(
				  WlzObject *obj0,
				  WlzObject *obj1,
				 int noEnc,
				 int noOst,
				 int maxOstDist,
				 int *dstStatCnt,
				 double **dstStatAry,
				 WlzErrorNum *dstErr)
{
  int 		i;
  WlzLong	i01 = 0, 		/* |\Omega_0 \cap \Omega_1| */
  		u01 = 0; 		/* |\Omega_0 \cup \Omega_1| */
  WlzLong	u[2] = {0},		/* |\Omega_i|, i \in 0 \cdots 1 */
  		v[2] = {0};		/* |c_9|, |c_{10}| */
  WlzObject	*c[11] = {NULL},		/* c_i, i \in 0 \cdots 10 */
		*o[2] = {NULL},		/* \Omega_i, i \in 0 \cdots 1 */
		*t[WLZ_RCCTOIDX_CNT] = {NULL}; /* Temporary object as
					in the enum WlzRCCTOIdx. */
  double	stats[WLZ_RCCIDX_CNT] = {0.0}; /* Classification statistics. */
  WlzValues	nullValues;
  WlzRCCClass	cls = WLZ_RCC_EMPTY; /* Classification mask. */
  WlzErrorNum	errNum = WLZ_ERR_NONE;

  nullValues.core = NULL;
  /* Compute classification using the decision tree. */
  if((obj0 == NULL) || (obj1 == NULL) ||
     (WlzIsEmpty(obj0, NULL) != 0) ||
     (WlzIsEmpty(obj1, NULL) != 0))
  {
    cls = WLZ_RCC_EMPTY;
  }
  else if((obj0->domain.core == NULL) || (obj1->domain.core == NULL))
  {
    errNum = WLZ_ERR_DOMAIN_NULL;
  }
  else if((obj0->type != obj1->type) ||
          ((obj0->type != WLZ_2D_DOMAINOBJ) &&
	   (obj0->type != WLZ_3D_DOMAINOBJ)))
  {
    errNum = WLZ_ERR_OBJECT_TYPE;
  }
  else if(((o[0] = WlzAssignObject(
                   WlzMakeMain(obj0->type, obj0->domain, nullValues,
  			       NULL, NULL, &errNum), NULL)) != NULL) &&
          ((o[1] = WlzAssignObject(
		   WlzMakeMain(obj1->type, obj1->domain, nullValues,
	  		       NULL, NULL, &errNum), NULL)) != NULL))
  {
    errNum = WlzRCCMakeC(o, c, t, 0);
  }
  if(errNum == WLZ_ERR_NONE)
  {
    if(WlzIsEmpty(c[0], NULL))
    {
      errNum = WlzRCCMakeC(o, c, t, 1);
      if(errNum == WLZ_ERR_NONE)
      {
	if(WlzIsEmpty(c[1], NULL))
	{
	  errNum = WlzRCCMakeC(o, c, t, 7);
	  if(errNum == WLZ_ERR_NONE)
	  {
	    if(WlzIsEmpty(c[7], NULL))
	    {
	      cls = WLZ_RCC_NTSURI;
	    }
	    else
	    {
	      errNum = WlzRCCMakeC(o, c, t, 8);
	      if(errNum == WLZ_ERR_NONE)
	      {
		if(WlzIsEmpty(c[8], NULL))
		{
		  cls = WLZ_RCC_NTSUR;
		}
		else
		{
		  cls = WLZ_RCC_DC;
		}
	      }
	    }
	  }
	}
	else
	{
	  errNum = WlzRCCMakeC(o, c, t, 7);
	  if(errNum == WLZ_ERR_NONE)
	  {
	    if(WlzIsEmpty(c[7], NULL))
	    {
	      cls = WLZ_RCC_TSURI;
	    }
	    else
	    {
	      errNum = WlzRCCMakeC(o, c, t, 8);
	      if(errNum == WLZ_ERR_NONE)
	      {
		if(WlzIsEmpty(c[8], NULL))
		{
		  cls = WLZ_RCC_TSUR;
		}
		else
		{
		  cls = WLZ_RCC_EC;
		}
	      }
	    }
	  }
	}
      }
    }
    else
    {
      errNum = WlzRCCMakeC(o, c, t, 2);
      if(errNum == WLZ_ERR_NONE)
      {
        if(WlzIsEmpty(c[2], NULL))
	{
	  cls = WLZ_RCC_EQ;
	}
	else
	{
	  errNum = WlzRCCMakeC(o, c, t, 3);
	  if(errNum == WLZ_ERR_NONE)
	  {
	    if(WlzIsEmpty(c[3], NULL))
	    {
	      errNum = WlzRCCMakeC(o, c, t, 4);
	      if(errNum == WLZ_ERR_NONE)
	      {
		if(WlzIsEmpty(c[4], NULL))
		{
		  cls = WLZ_RCC_NTPP;
		}
		else
		{
		  cls = WLZ_RCC_TPP;
		}
	      }
	    }
	    else
	    {
	      errNum = WlzRCCMakeC(o, c, t, 5);
	      if(errNum == WLZ_ERR_NONE)
	      {
		if(WlzIsEmpty(c[5], NULL))
		{
		  errNum = WlzRCCMakeC(o, c, t, 6);
		  if(errNum == WLZ_ERR_NONE)
		  {
		    if(WlzIsEmpty(c[6], NULL))
		    {
		      cls = WLZ_RCC_NTPPI;
		    }
		    else
		    {
		      cls = WLZ_RCC_TPPI;
		    }
		  }
		}
		else
		{
		  cls = WLZ_RCC_PO;
		}
	      }
	    }
	  }
	}
      }
    }
  }
  /* If enclosure is required check for it and add to classification mask. */
  if((errNum == WLZ_ERR_NONE) && (noEnc == 0) &&
     ((cls &
       (WLZ_RCC_EQ |
        WLZ_RCC_TSUR | WLZ_RCC_TSURI |
	WLZ_RCC_NTSUR | WLZ_RCC_NTSURI)) == 0))
  {
    for(i = 0; i <= 1; ++i)
    {
      errNum = WlzRCCMakeT(o, t,
                           (i == 0)? WLZ_RCCTOIDX_O0O1CI: WLZ_RCCTOIDX_O0CO1I);
      if(errNum == WLZ_ERR_NONE)
      {
        u[i] = WlzVolume(o[i], &errNum);
      }
      if(errNum == WLZ_ERR_NONE)
      {
        v[i] = WlzVolume(t[(i == 0)?
	                 WLZ_RCCTOIDX_O0O1CI: WLZ_RCCTOIDX_O0CO1I],
			 &errNum);
	if((errNum == WLZ_ERR_NONE) && (v[i] < 0))
	{
	  errNum = WLZ_ERR_DOMAIN_DATA;
	}
      }
      if(errNum != WLZ_ERR_NONE)
      {
        break;
      }
    }
    if(errNum == WLZ_ERR_NONE)
    {
      if((2 * v[0]) >= u[0])
      {
        cls |= WLZ_RCC_ENC;
      }
      if((2 * v[1]) >= u[1])
      {
        cls |= WLZ_RCC_ENCI;
      }
    }
  }
  /* Compute the maximum normalized volume for the classification(s) in the
   * classification mask. */
  if((errNum == WLZ_ERR_NONE) && (dstStatAry != NULL))
  {
    int 	i,
    		m;

    for(i = 0; i < WLZ_RCCIDX_CNT; ++i)
    {
      m = 1<<i;
      if(m & cls)
      {
	double	s = 0.0;

        switch(m)
	{
	  case WLZ_RCC_EQ:
	    s = 1.0;
	    break;
	  case WLZ_RCC_PO:
	    /* |\Omega_0 \cap \Omega_1| / |\Omega_0 \cup \Omega_1|  =
	     * u_0 / u_01 */
	    if(i01 <= 0)
	    {
	      errNum = WlzRCCMakeT(o, t, WLZ_RCCTOIDX_O0O1I);
	      if(errNum == WLZ_ERR_NONE)
	      {
	        i01 = WlzVolume(t[WLZ_RCCTOIDX_O0O1I], &errNum);
	      }
	    }
	    if((errNum == WLZ_ERR_NONE) && (u01 <= 0))
	    {
	      errNum = WlzRCCMakeT(o, t, WLZ_RCCTOIDX_O0O1U);
	      if(errNum == WLZ_ERR_NONE)
	      {
	        u01 = WlzVolume(t[WLZ_RCCTOIDX_O0O1U], &errNum);
	      }
	    }
	    if(errNum == WLZ_ERR_NONE)
	    {
	      s = (double )(i01) / (double )u01;
            }
	    break;
	  case WLZ_RCC_TSUR: /* FALLTHROUGH */
	  case WLZ_RCC_NTSUR: /* FALLTHROUGH */
	  case WLZ_RCC_TPP: /* FALLTHROUGH */
	  case WLZ_RCC_NTPP:
	    /* |\Omega_0| / |\Omega_0 \cup \Omega_1|  =
	     * u_0 / u_01 */
	    if(u[0] <= 0)
	    {
	      u[0] = WlzVolume(o[0], &errNum);
	    }
	    if(errNum == WLZ_ERR_NONE)
	    {
	      if(u01 <= 0)
	      {
	        errNum = WlzRCCMakeT(o, t, WLZ_RCCTOIDX_O0O1U);
		if(errNum == WLZ_ERR_NONE)
		{
		  u01 = WlzVolume(t[WLZ_RCCTOIDX_O0O1U], &errNum);
		}
	      }
	    }
	    if(errNum == WLZ_ERR_NONE)
	    {
	      s = (double )(u[0]) / (double )u01;
	    }
	    break;
	  case WLZ_RCC_TSURI: /* FALLTHROUGH */
	  case WLZ_RCC_NTSURI: /* FALLTHROUGH */
	  case WLZ_RCC_TPPI: /* FALLTHROUGH */
	  case WLZ_RCC_NTPPI:
	    /* |\Omega_1| / |\Omega_0 \cup \Omega_1|  =
	     * u_1 / u_01 */
	    if(u[1] <= 0)
	    {
	      u[1] = WlzVolume(o[1], &errNum);
	    }
	    if(errNum == WLZ_ERR_NONE)
	    {
	      if(u01 <= 0)
	      {
		errNum = WlzRCCMakeT(o, t, WLZ_RCCTOIDX_O0O1U);
		if(errNum == WLZ_ERR_NONE)
		{
		  u01 = WlzVolume(t[WLZ_RCCTOIDX_O0O1U], &errNum);
		}
	      }
	    }
	    if(errNum == WLZ_ERR_NONE)
	    {
	      s = (double )(u[1]) / (double )u01;
	    }
	    break;
	  case WLZ_RCC_ENC:
	    /* |\Omega_0 \cup \Omega_1^{\circ}|/|\Omega_0| =
	     * v_0 / u_0 */
	    if(u[1] >= 0)
	    {
	      s = (double )(v[0]) / (double )(u[0]);
	    }
	    break;
	  case WLZ_RCC_ENCI:
	    /* |\Omega_0^{\circ} \cup \Omega_1|/|\Omega_1| =
	     * v_1 / u_1 */
	    if(v[1] >= 0)
	    {
	      s = (double )(v[1]) / (double )(u[1]);
	    }
	    break;
	  default:
	    break;
	}
        if(errNum == WLZ_ERR_NONE)
	{
	  stats[i] = s;
	}
      }
    }
  }
  /* If offset is required check for it and add to both the classification
   * mask and statistics. */
  if((errNum == WLZ_ERR_NONE) && (noOst == 0) &&
     ((cls & WLZ_RCC_EQ) == 0))
  {
    int		ostQ[3];

    errNum = WlzRCCOffset(o, t,
                          maxOstDist, &(ostQ[0]), &(ostQ[1]), &(ostQ[2]));
    if(errNum == WLZ_ERR_NONE)
    {
#ifdef WLZ_RCC_DEBUG_OST
      (void )fprintf(stderr,
		     "WLZ_RCC_DEBUG_OST %d %d %d\n",
		     ostQ[0], ostQ[1], ostQ[2]);
#endif
      if((ostQ[1] > 0) && (ostQ[1] < maxOstDist) && (ostQ[2] >= ostQ[0]))
      {
	const double eps = 1.0e-06;

	if(ostQ[2] > ostQ[0])
	{
	  stats[WLZ_RCCIDX_OST] = (double )ostQ[1] /
		                  (double )(ostQ[2] + ostQ[1] - ostQ[0]);
	}
	else
	{
	  stats[WLZ_RCCIDX_OST] = 1.0;
	}
	if(stats[WLZ_RCCIDX_OST] > (0.5 - eps))
	{
	  cls |= WLZ_RCC_OST;
	}
      }
    }
  }
  /* Free objects. */
  for(i = 0; i < WLZ_RCCTOIDX_CNT; ++i)
  {
    (void )WlzFreeObj(t[i]);
  }
  for(i = 0; i <= 8; ++i)
  {
    (void )WlzFreeObj(c[i]);
  }
  for(i = 0; i < 2; ++i)
  {
    (void )WlzFreeObj(o[i]);
  }
  if((errNum == WLZ_ERR_NONE) && (dstStatAry != NULL))
  {
    if((*dstStatAry = (double *)
                        AlcMalloc(sizeof(double) * WLZ_RCCIDX_CNT)) == NULL)
    {
      errNum = WLZ_ERR_MEM_ALLOC;
    }
    else
    {
      (void )memcpy(*dstStatAry, stats, sizeof(double) * WLZ_RCCIDX_CNT);
      if(dstStatCnt)
      {
        *dstStatCnt = WLZ_RCCIDX_CNT;
      }
    }
  }
  if(dstErr)
  {
    *dstErr = errNum;
  }
  return(cls);
}
Пример #5
0
/*!
* \return	Woolz error code.
* \ingroup	WlzBinaryOps
* \brief	Computes a metric which quantifies the extent to which
* 		the domain of one of the given objects is offset from 
* 		the domain of the second. This is a symetric metric, ie
* 		\f$OST(\Omega_0, \Omega_1) \equiv OST(\Omega_0, \Omega_1)\f$.
*		A domain is computed which is equidistant from the domains
*		of the two given objects, is within maxDist of each object's
*		domain and is within the convex hull of the union of the
*		domains of the two given objects. Within this domain the
*		1st, 2nd and 3rd quantiles of the distance
*		(\f$q_0\f$, \f$q_1\f$ and \f$q_2\f$) are found.
*		The object's domains are classified as offset if
*		\f[
		frac{q_1}{q_1 + (q_1  - q_0) + (q_2 - q_1)} \geq 0.5
		\f]
*		ie
*		\f[
		frac{q_1}{q_1 + q_2 - q_0} \geq 0.5
		\f]
*		Small equi-distant domains with a volume less than half
*		the maximum distance do not classify the relationship as
*		an overlap.
* \param	o			Array with the two given spatial
* 					domain objects, must not be NULL
* 					and nor must the objects.
* \param	t			Array of temporary objects as in
* 					WlzRCCTOIdx.
* \param	maxDist			Maximum distance for offset. This
* 					is used to compute a distance object,
* 					large distances will significantly
* 					increase the processing time.
* \param	dQ0			Destination pointer for 1st quantile
* 					offset distance, must not be NULL.
* \param	dQ1			Destination pointer for 2nd quantile
* 					(ie median) offset distance,
* 					must not be NULL.
* \param	dQ2			Destination pointer for 3rd quantile
* 					offset distance, must not be NULL.
*/
static WlzErrorNum		WlzRCCOffset(
				  WlzObject **o,
				  WlzObject **t,
				  int maxDist,
				  int *dQ0,
				  int *dQ1,
				  int *dQ2)
{
  int		empty = 0;
  int		q[3] = {0};
  int		*dHist = NULL;
  WlzObject	*eObj = NULL;
  WlzErrorNum	errNum = WLZ_ERR_NONE;

  if(((o[0]->type == WLZ_2D_DOMAINOBJ) &&
      (o[1]->type == WLZ_2D_DOMAINOBJ)) ||
     ((o[0]->type == WLZ_3D_DOMAINOBJ) &&
      (o[1]->type == WLZ_3D_DOMAINOBJ)))
  {
    if((o[0]->domain.core == NULL) || (o[1]->domain.core == NULL))
    {
      errNum = WLZ_ERR_DOMAIN_NULL;
    }
  }
  else
  {
    errNum = WLZ_ERR_OBJECT_TYPE;
  }
  /* Compute distance transforms of the two given objects out to a given
   * maximum distance and then using these distances the equi-distant
   * domain between these two objects. The values of the eqi-distant object
   * are those of the distance between the objects.*/
  if(errNum == WLZ_ERR_NONE)
  {
    int		i;
    WlzObject	*sObj = NULL,
    		*cObj = NULL;
    WlzObject	*dObj[2];

    dObj[0] = dObj[1] = NULL;
    /* Create structuring element with which to dilate the given object
     * domains(by maxDist). */
    sObj = WlzAssignObject(
	   WlzMakeSphereObject(o[0]->type, maxDist, 0, 0, 0,
	                       &errNum), NULL);
    /* Create domain or convex hull of the union of the two given object
     * domains. */
    if(errNum == WLZ_ERR_NONE)
    {
      WlzObject	*uObj = NULL,
      		*xObj = NULL;

      errNum = WlzRCCMakeT(o, t, WLZ_RCCTOIDX_O0O1U);
      if(errNum == WLZ_ERR_NONE)
      {
	uObj = WlzAssignObject(t[WLZ_RCCTOIDX_O0O1U], NULL);
      }
      if(errNum == WLZ_ERR_NONE)
      {
        xObj = WlzAssignObject(
	       WlzObjToConvexHull(uObj, &errNum), NULL);
      }
      if(errNum == WLZ_ERR_NONE)
      {
        cObj = WlzAssignObject(
	       WlzConvexHullToObj(xObj, o[0]->type, &errNum), NULL);
      }
      (void )WlzFreeObj(xObj);
      (void )WlzFreeObj(uObj);
    }
    /* Dilate the two given objects and find the ntersection of the
     * dilated domains with each other and the convex hull computed
     * above. Within his domain compute the distances. */
    if(errNum == WLZ_ERR_NONE)
    {
      for(i = 0; i < 2; ++i)
      {
	WlzObject *tObj = NULL,
		  *rObj = NULL;

	tObj = WlzAssignObject(
	       WlzStructDilation(o[i], sObj, &errNum), NULL);
        if(errNum == WLZ_ERR_NONE)
	{
	  rObj = WlzAssignObject(
	         WlzIntersect2(tObj, cObj, &errNum), NULL);
	}
        (void )WlzFreeObj(tObj);
        if(errNum == WLZ_ERR_NONE)
	{
	  dObj[i] = WlzAssignObject(
		    WlzDistanceTransform(rObj, o[!i],
					 WLZ_OCTAGONAL_DISTANCE,
					 0.0, maxDist, &errNum), NULL);
	}
        (void )WlzFreeObj(rObj);
        if(errNum == WLZ_ERR_NONE)
	{
	  WlzPixelV bgdV;

	  bgdV.type = WLZ_GREY_INT;
	  bgdV.v.inv = maxDist;
	  errNum = WlzSetBackground(dObj[i], bgdV);
	}
        if(errNum != WLZ_ERR_NONE)
	{
	  break;
	}
      }
    }
    /* Find the domain which is equi-distant from the two given objects,
     * within the xDist range and within the convex hull of the union of
     * the two given object's domains. */
    (void )WlzFreeObj(sObj); sObj = NULL;
    if(errNum == WLZ_ERR_NONE)
    {
      WlzLong	vol = 0;
      WlzObject *qObj = NULL,
      		*tObj = NULL;

      qObj = WlzAssignObject(
             WlzImageArithmetic(dObj[0], dObj[1], WLZ_BO_EQ, 0, &errNum), NULL);
      if(errNum == WLZ_ERR_NONE)
      {
	WlzPixelV thrV;

	thrV.type = WLZ_GREY_INT;
	thrV.v.inv = 1;
        tObj = WlzAssignObject(
	       WlzThreshold(qObj, thrV, WLZ_THRESH_HIGH, &errNum), NULL);
      }
      /* Check that the eqi-distant domain is of a reasonable size ie has
       * a area or volume greater than half the maximum distance. */
      if(errNum == WLZ_ERR_NONE)
      {
        vol = WlzVolume(tObj, &errNum);
	if((maxDist / 2) >= vol)
	{
	  empty = 1;
	}
      }
      if((errNum == WLZ_ERR_NONE) && !empty)
      {
	WlzObject *mObj;
	WlzPixelV tmpV;

	tmpV.type = WLZ_GREY_INT;
	tmpV.v.inv = 0;
        mObj = WlzAssignObject(
	       WlzGreyTemplate(dObj[0], tObj, tmpV, &errNum), NULL);
        if(errNum == WLZ_ERR_NONE)
	{
	  tmpV.v.inv = 1;
	  eObj = WlzAssignObject(
	  	WlzThreshold(mObj, tmpV, WLZ_THRESH_HIGH, &errNum), NULL);
	}
	(void )WlzFreeObj(mObj);
      }
      (void )WlzFreeObj(tObj);
      (void )WlzFreeObj(qObj);
      if((errNum == WLZ_ERR_NONE) && !empty)
      {
	WlzLong vol;

	vol = WlzVolume(eObj, &errNum);
	if((maxDist / 2) >= vol)
	{
	  empty = 1;
	}
      }
    }
    (void )WlzFreeObj(cObj);
    (void )WlzFreeObj(sObj);
    (void )WlzFreeObj(dObj[0]);
    (void )WlzFreeObj(dObj[1]);
  }
  /* Compute a quantised distance histogram in which equi-distant distances
   * are quantized to integer values. */
  if((errNum == WLZ_ERR_NONE) && !empty)
  {
    if((dHist = (int *)AlcCalloc(maxDist + 1, sizeof(int))) == NULL)
    {
      errNum = WLZ_ERR_MEM_ALLOC;
    }
  }
  if((errNum == WLZ_ERR_NONE) && !empty)
  {
    if(eObj->type == WLZ_2D_DOMAINOBJ)
    {
      errNum = WlzRCCCompDistHist2D(maxDist, dHist, eObj);
    }
    else
    {
      errNum = WlzRCCCompDistHist3D(maxDist, dHist, eObj);
    }
#ifdef WLZ_RCC_DEBUG_OST
    {
      FILE *fP;

      fP = fopen("WLZ_RCC_DEBUG_OST.wlz", "w");
      (void )WlzWriteObj(fP, eObj);
      (void )fclose(fP);
    }
#endif
  }
  WlzFreeObj(eObj);
  if((errNum == WLZ_ERR_NONE) && !empty)
  {
    int		i,
    		n,
		s,
		nq;
    /* Compute the median, first and third quantile offset distances,
     * the ratio of median to the median plus inner inter-quantile range. */
    n = 0;
    for(i = 0; i < maxDist; ++i)
    {
      n += dHist[i];
    }
    i = 0;
    s = 0;
    nq = n / 4;
    while(s < nq)
    {
      s += dHist[i++];
    }
    q[0] = i;
    nq = n / 2;
    while(s < nq)
    {
      s += dHist[i++];
    }
    q[1] = i;
    nq = (3 * n) / 4;
    while(s < nq)
    {
      s += dHist[i++];
    }
    q[2] = i;
  }
  AlcFree(dHist);
  *dQ0 = q[0];
  *dQ1 = q[1];
  *dQ2 = q[2];
  return(errNum);
}
Пример #6
0
void install_paint_reference_object(
  WlzObject	*obj)
{
  Widget		toggle;
  WlzPixelV		bckgrnd;
  WlzPixelV		min, max, Min, Max;
  WlzErrorNum		errNum=WLZ_ERR_NONE;

  if( globals.orig_obj != NULL ){
    WlzFreeObj( globals.orig_obj );
  }
  globals.orig_obj = WlzAssignObject(obj, &errNum);
  obj = NULL;

  /* check input object for grey-value type
     convert if necessary */
  if( errNum == WLZ_ERR_NONE ){
    bckgrnd = WlzGetBackground(globals.orig_obj, &errNum);
    Min.type = WLZ_GREY_INT;
    Max.type = WLZ_GREY_INT;
    Min.v.inv = 0;
    Max.v.inv = 255;
  }
  
  if( errNum == WLZ_ERR_NONE ){
    switch( bckgrnd.type ){
    case WLZ_GREY_LONG:
      WlzGreyRange(globals.orig_obj, &min, &max);
      if( (min.v.lnv < 0) || (max.v.lnv > 255) ){
	if((obj = WlzConvertPix(globals.orig_obj, WLZ_GREY_INT, &errNum))){
	  WlzGreySetRange(obj, min, max, Min, Max, 0);
	  bckgrnd = WlzGetBackground(obj, &errNum);
	}
      }
      break;
    case WLZ_GREY_INT:
      WlzGreyRange(globals.orig_obj, &min, &max);
      if( (min.v.inv < 0) || (max.v.inv > 255) ){
	obj = WlzConvertPix(globals.orig_obj, WLZ_GREY_INT, &errNum);
	WlzGreySetRange(obj, min, max, Min, Max, 0);
	bckgrnd = WlzGetBackground(obj, &errNum);
      }
      break;
    case WLZ_GREY_SHORT:
      WlzGreyRange(globals.orig_obj, &min, &max);
      if( (min.v.shv < 0) || (max.v.shv > 255) ){
	if((obj = WlzConvertPix(globals.orig_obj, WLZ_GREY_SHORT, &errNum))){
	  WlzGreySetRange(obj, min, max, Min, Max, 0);
	  bckgrnd = WlzGetBackground(obj, &errNum);
	}
      }
      break;
    case WLZ_GREY_FLOAT:
      WlzGreyRange(globals.orig_obj, &min, &max);
      if( (min.v.flv < 0) || (max.v.flv > 255) ){
	if((obj = WlzConvertPix(globals.orig_obj, WLZ_GREY_INT, &errNum))){
	  WlzGreySetRange(obj, min, max, Min, Max, 0);
	  bckgrnd = WlzGetBackground(obj, &errNum);
	}
      }
      break;
    case WLZ_GREY_DOUBLE:
      WlzGreyRange(globals.orig_obj, &min, &max);
      if( (min.v.dbv < 0) || (max.v.dbv > 255) ){
	if((obj = WlzConvertPix(globals.orig_obj, WLZ_GREY_INT, &errNum))){
	  WlzGreySetRange(obj, min, max, Min, Max, 0);
	  bckgrnd = WlzGetBackground(obj, &errNum);
	}
      }
      break;

    default:
    case WLZ_GREY_UBYTE:
      break;
    }
  }

  /* copy for later transform for display purposes,
     the original is kept for IP purposes note pixconvert loses
     the background value */
  if( errNum == WLZ_ERR_NONE ){
    if( globals.obj != NULL ){
      WlzFreeObj( globals.obj );
    }
    if( obj ){
      globals.obj =
	WlzAssignObject(WlzConvertPix(obj, WLZ_GREY_UBYTE, &errNum), NULL);
      WlzFreeObj(globals.orig_obj);
      globals.orig_obj = WlzAssignObject(obj, NULL);
    }
    else {
      globals.obj =
	WlzAssignObject(WlzConvertPix(globals.orig_obj,
				      WLZ_GREY_UBYTE, &errNum), NULL);
    }
  }

  if( errNum == WLZ_ERR_NONE ){
    min.type = WLZ_GREY_UBYTE;
    max.type = WLZ_GREY_UBYTE;
    Min.type = WLZ_GREY_UBYTE;
    Max.type = WLZ_GREY_UBYTE;
    min.v.ubv = 0;
    max.v.ubv = 255;
    Min.v.ubv = globals.cmapstruct->gmin;
    Max.v.ubv = globals.cmapstruct->gmax;
    errNum = WlzGreySetRange(globals.obj, min, max, Min, Max, 0);
  }

  /* also convert the background values */
  if( errNum == WLZ_ERR_NONE ){
/*  min = WlzGetBackground(globals.orig_obj, NULL);*/
    WlzValueConvertPixel(&bckgrnd, bckgrnd, WLZ_GREY_INT);
    max.type = WLZ_GREY_INT;
    max.v.inv = ((bckgrnd.v.inv * (Max.v.ubv - Min.v.ubv)) / 255) + Min.v.ubv;
    WlzSetBackground(globals.obj, max);

    /* fill blank planes here - should be a resource option */
    if( (toggle = XtNameToWidget(read_obj_dialog, "*.fill_blanks")) ){
      Boolean	fill_blanks, min_domain;
      XtVaGetValues(toggle, XmNset, &fill_blanks, NULL);
      if( fill_blanks ){
	if( (toggle = XtNameToWidget(read_obj_dialog, "*.min_domain")) ){
	  XtVaGetValues(toggle, XmNset, &min_domain, NULL);
	  if( min_domain ){
	    WlzFillBlankPlanes(globals.obj, 1);
	  } else {
	    WlzFillBlankPlanes(globals.obj, 0);
	  }
	}
	else {
	  WlzFillBlankPlanes(globals.obj, 1);
	}
      }
    }

    /* setup the display list and properties dialog */
    setup_ref_display_list_cb(read_obj_dialog, NULL, NULL);
    setup_obj_props_cb(read_obj_dialog, NULL, NULL);
  }

  /* log the object size */
  if( (errNum == WLZ_ERR_NONE) && globals.logfileFp ){
    char strBuf[64];
    sprintf(strBuf, "(%d, %d, %d, %d, %d, %d)",
	    globals.obj->domain.p->kol1, globals.obj->domain.p->line1,
	    globals.obj->domain.p->plane1, globals.obj->domain.p->lastkl,
	    globals.obj->domain.p->lastln, globals.obj->domain.p->lastpl);
    MAPaintLogData("BoundingBox", strBuf, 0, NULL);
    sprintf(strBuf, "%d", (int) WlzVolume(globals.obj, NULL));
    MAPaintLogData("Volume", strBuf, 0, NULL);
  }

  if( errNum != WLZ_ERR_NONE ){
    MAPaintReportWlzError(globals.topl,
			  "install_paint_reference_object", errNum);
  }
  return;
}
Пример #7
0
void warpSetSignalDomain(
  WlzIVertex2	*selVtx)
{
  WlzErrorNum	errNum=WLZ_ERR_NONE;
  WlzPixelV	threshV, threshV1;
  WlzObject	*obj, *obj1;
  WlzUInt	combineMode;

  /* image processing sequence */
  if( warpGlobals.sgnlThreshObj == NULL ){
    warpSetSignalThreshObj();
  }
  if( warpGlobals.sgnlThreshObj ){
    obj1 = WlzAssignObject(warpGlobals.sgnlThreshObj, &errNum);
  }
  else {
    return;
  }

  /* threshold the resultant image */
  if( errNum == WLZ_ERR_NONE ){
    switch( warpGlobals.thresholdType ){
    case WLZ_RGBA_THRESH_NONE:
      break;

    case WLZ_RGBA_THRESH_SINGLE:
      threshV.type = WLZ_GREY_INT;
      threshV.v.inv = warpGlobals.threshRangeLow;
      if( obj1 ){
	/* clear signal object */
	if( warpGlobals.sgnlObj ){
	  WlzFreeObj(warpGlobals.sgnlObj);
	}
	  
	if((obj = WlzThreshold(obj1, threshV, WLZ_THRESH_HIGH, &errNum)) && (WlzVolume(obj, &errNum) > 0)){
	  obj = WlzAssignObject(obj, &errNum);
	  WlzFreeObj(obj1);
	  threshV.v.inv = warpGlobals.threshRangeHigh + 1;
	  if((obj1 = WlzThreshold(obj, threshV, WLZ_THRESH_LOW, &errNum)) && (WlzVolume(obj1, &errNum) > 0)){
	    warpGlobals.sgnlObj = WlzAssignObject(obj1, &errNum);
	  }
	  else {
	    if( obj1 ){
	      WlzFreeObj(obj1);
	    }
	    warpGlobals.sgnlObj = NULL;
	  }
	  WlzFreeObj(obj);
	}
	else {
	  if( obj ) {
	    WlzFreeObj(obj);
	  }
	  WlzFreeObj(obj1);
	  warpGlobals.sgnlObj = NULL;
	}
      }
      break;

    case WLZ_RGBA_THRESH_MULTI:
      /* clear signal object */
      if( warpGlobals.sgnlObj ){
	WlzFreeObj(warpGlobals.sgnlObj);
      }

      /* set the thresholds and combine mode */
      threshV.type = WLZ_GREY_RGBA;
      WLZ_RGBA_RGBA_SET(threshV.v.rgbv,
			warpGlobals.threshRangeRGBLow[0],
			warpGlobals.threshRangeRGBLow[1],
			warpGlobals.threshRangeRGBLow[2],
			255);
      threshV1.type = WLZ_GREY_RGBA;
      WLZ_RGBA_RGBA_SET(threshV1.v.rgbv,
			warpGlobals.threshRangeRGBHigh[0],
			warpGlobals.threshRangeRGBHigh[1],
			warpGlobals.threshRangeRGBHigh[2],
			255);
      WLZ_RGBA_RGBA_SET(combineMode,
			WLZ_BO_AND, WLZ_BO_AND, WLZ_BO_AND, 255);

      /* use multi-threshold */
      if((obj = WlzRGBAMultiThreshold(obj1, threshV, threshV1,
				      combineMode, &errNum))){
	if( WlzIsEmpty(obj, &errNum) ){
	    WlzFreeObj(obj);
	    warpGlobals.sgnlObj = NULL;
	} else {
	    warpGlobals.sgnlObj = WlzAssignObject(obj, &errNum);
	}
      }
      else {
	warpGlobals.sgnlObj = NULL;
      }
      WlzFreeObj(obj1);
      break;

    case WLZ_RGBA_THRESH_BOX:
      /* clear signal object */
      if( warpGlobals.sgnlObj ){
	WlzFreeObj(warpGlobals.sgnlObj);
      }

      /* use box-threshold */
      if((obj = WlzRGBABoxThreshold(obj1,
				    warpGlobals.lowRGBPoint,
				    warpGlobals.highRGBPoint,
				    &errNum))){
	if( WlzIsEmpty(obj, &errNum) ){
	    WlzFreeObj(obj);
	    warpGlobals.sgnlObj = NULL;
	} else {
	    warpGlobals.sgnlObj = WlzAssignObject(obj, &errNum);
	}
      }
      else {
	warpGlobals.sgnlObj = NULL;
      }
      WlzFreeObj(obj1);	  
      break;

    case WLZ_RGBA_THRESH_SLICE:
      /* clear signal object */
      if( warpGlobals.sgnlObj ){
	WlzFreeObj(warpGlobals.sgnlObj);
      }

      /* use slice-threshold */
      if((obj = WlzRGBASliceThreshold(obj1,
				      warpGlobals.lowRGBPoint,
				      warpGlobals.highRGBPoint,
				      &errNum))){
	if( WlzIsEmpty(obj, &errNum) ){
	    WlzFreeObj(obj);
	    warpGlobals.sgnlObj = NULL;
	} else {
	    warpGlobals.sgnlObj = WlzAssignObject(obj, &errNum);
	}
      }
      else {
	warpGlobals.sgnlObj = NULL;
      }
      WlzFreeObj(obj1);	  
      break;

    case WLZ_RGBA_THRESH_SPHERE:
      /* clear signal object */
      if( warpGlobals.sgnlObj ){
	WlzFreeObj(warpGlobals.sgnlObj);
      }

      /* use Ellipsoid-threshold */
      if((obj = WlzRGBAEllipsoidThreshold(obj1,
					  warpGlobals.lowRGBPoint,
					  warpGlobals.highRGBPoint,
					  warpGlobals.colorEllipseEcc,
					  &errNum))){
	if( WlzIsEmpty(obj, &errNum) ){
	    WlzFreeObj(obj);
	    warpGlobals.sgnlObj = NULL;
	} else {
	    warpGlobals.sgnlObj = WlzAssignObject(obj, &errNum);
	}
      }
      else {
	warpGlobals.sgnlObj = NULL;
      }
      WlzFreeObj(obj1);	  
      break;

    default:
      errNum = WLZ_ERR_PARAM_DATA;
      if( obj1 ){
	WlzFreeObj(obj1);
      }
      if( warpGlobals.sgnlObj ){
	WlzFreeObj(warpGlobals.sgnlObj);
	warpGlobals.sgnlObj = NULL;
      }
      break;
    }
  }

  /* check for local mode */
  if( warpGlobals.sgnlObj && !warpGlobals.globalThreshFlg ){
    if( selVtx != NULL ){
      warpGlobals.globalThreshVtx = *selVtx;
    }
    /* extract a local domain if the vertex is sensible */
    if( warpGlobals.globalThreshVtx.vtX != -10000 ){
      WlzObject	**objs=NULL;
      int	i, numObjs;
      double	x, y;
      obj1 = NULL;
      x = warpGlobals.globalThreshVtx.vtX;
      y = warpGlobals.globalThreshVtx.vtY;
      errNum = WlzLabel(warpGlobals.sgnlObj, &numObjs, &objs, 8192, 0,
			WLZ_4_CONNECTED);
      if( (errNum == WLZ_ERR_INT_DATA) && (numObjs == 8192) ){
	WlzObject	*tmpObj1, *tmpObj2;
	WlzDomain	domain;
	WlzValues	values;

	/* try again, smaller domain */
	for(i=0; i < numObjs; i++){
	  WlzFreeObj( objs[i] );
	}
	AlcFree((void *) objs);
	objs = NULL;
	numObjs = 0;
	domain.i = WlzMakeIntervalDomain(WLZ_INTERVALDOMAIN_RECT,
					 y - 80, y + 80,
					 x - 80, x + 80, &errNum);
	values.core = NULL;
	if((tmpObj1 = WlzMakeMain(warpGlobals.sgnlObj->type, domain, values,
				  NULL, NULL, &errNum))){
	  if((tmpObj2 = WlzIntersect2(warpGlobals.sgnlObj, tmpObj1, &errNum))){
	    tmpObj2->values = WlzAssignValues(warpGlobals.sgnlObj->values, NULL);
	    errNum = WlzLabel(warpGlobals.sgnlObj, &numObjs, &objs, 8192, 0,
			      WLZ_4_CONNECTED);
	    WlzFreeObj(tmpObj2);
	    if((errNum == WLZ_ERR_INT_DATA) && (numObjs == 8192) ){
	      errNum = WLZ_ERR_NONE;
	    }
	  }
	  WlzFreeObj(tmpObj1);
	}
	
      }
      if( errNum == WLZ_ERR_NONE ){

	for(i=0; i < numObjs; i++){
	  if( WlzInsideDomain( objs[i], 0.0, y, x, NULL ) ){
	    obj1 = WlzMakeMain(objs[i]->type,
			       objs[i]->domain,
			       objs[i]->values,
			       NULL, NULL, NULL);
	    obj1 = WlzAssignObject(obj1, NULL);
	  }
	  WlzFreeObj( objs[i] );
	}
	AlcFree((void *) objs);
      }
      if( obj1 ){
	WlzFreeObj(warpGlobals.sgnlObj);
	warpGlobals.sgnlObj = obj1;
      }
    }
    else {
      WlzFreeObj(warpGlobals.sgnlObj);
      warpGlobals.sgnlObj = NULL;
    }
  }

  /* check for increment mode */
  if( warpGlobals.incrThreshFlg && sgnlIncrObj() ){
    if( warpGlobals.sgnlObj ){
      if((obj1 = WlzUnion2(warpGlobals.sgnlObj, sgnlIncrObj(),
			   &errNum))){
	WlzFreeObj(warpGlobals.sgnlObj);
	warpGlobals.sgnlObj = WlzAssignObject(obj1, &errNum);
      }
    }
    else {
      warpGlobals.sgnlObj = WlzAssignObject(sgnlIncrObj(),
					    &errNum);
    }
  }

  if( errNum != WLZ_ERR_NONE ){
    MAPaintReportWlzError(globals.topl, "warpSetSignalDomain", errNum);
  }
  return;
}
Пример #8
0
int main(
  int   argc,
  char  **argv)
{
  WlzObject     	*obj1, *obj2;
  int			verboseFlg=0;
  char			*nameStr;
  double		xsize=1.0, ysize=1.0, zsize=1.0;
  int			i, j;
  double		dist, minDist;
  double		dist1, dist2, dist3, dist4;
  int			numVtxs1, numVtxs2;
  WlzDVertex3		*edgeVtxs1, *edgeVtxs2;
  WlzDVertex3		cmVtx1, cmVtx2;
  double		vol1, vol2;
  double		mass1, mass2;
  WlzErrorNum	errNum=WLZ_ERR_NONE;

  /* check input arguments */
  nameStr = argv[0];
  argv++;
  argc--;
  while( argc > 0 )
  {
    switch( argv[0][1] )
    {
    case 'h':
      usage(nameStr);
      return 0;

    case 'v':
      verboseFlg = 1;
      break;

    default:
      usage(nameStr);
      return 1;
    }
    argc--;
    argv++;
  }

  /* read the objects */
  if((obj1 = WlzReadObj(stdin, NULL)) &&
     (obj2 = WlzReadObj(stdin, NULL)) ){

    if( obj1->type != obj2->type ){
      return 1;
    }
    if((obj1->type != WLZ_2D_DOMAINOBJ) &&
       (obj1->type != WLZ_3D_DOMAINOBJ)){
      usage(nameStr);
      return 1;
    }

    /* get edge vertices */
    numVtxs1 = WlzEdgeVertices(obj1, &edgeVtxs1, NULL);
    numVtxs2 = WlzEdgeVertices(obj2, &edgeVtxs2, NULL);

    /* renormalise with pixel sizes */
    if( obj1->type == WLZ_3D_DOMAINOBJ ){
      xsize = obj1->domain.p->voxel_size[0];
      ysize = obj1->domain.p->voxel_size[1];
      zsize = obj1->domain.p->voxel_size[2];

      for(i=0; i < numVtxs1; i++){
	edgeVtxs1[i].vtX *= xsize;
	edgeVtxs1[i].vtY *= ysize;
	edgeVtxs1[i].vtZ *= zsize;
      }
      for(i=0; i < numVtxs2; i++){
	edgeVtxs2[i].vtX *= xsize;
	edgeVtxs2[i].vtY *= ysize;
	edgeVtxs2[i].vtZ *= zsize;
      }
    }

    /* get centre of mass vertices */
    if( obj1->type == WLZ_2D_DOMAINOBJ ){
      WlzDVertex2	vtx2;

      vtx2 = WlzCentreOfMass2D(obj1, 0, &mass1, &errNum);
      cmVtx1.vtX = vtx2.vtX;
      cmVtx1.vtY = vtx2.vtY;
      cmVtx1.vtZ = 0.0;

      vtx2 = WlzCentreOfMass2D(obj2, 0, &mass2, &errNum);
      cmVtx2.vtX = vtx2.vtX;
      cmVtx2.vtY = vtx2.vtY;
      cmVtx2.vtZ = 0.0;
    }
    else {
      cmVtx1 = WlzCentreOfMass3D(obj1, 0, &mass1, &errNum);
      cmVtx2 = WlzCentreOfMass3D(obj2, 0, &mass2, &errNum);
      cmVtx1.vtX *= xsize;
      cmVtx1.vtY *= ysize;
      cmVtx1.vtZ *= zsize;
      cmVtx2.vtX *= xsize;
      cmVtx2.vtY *= ysize;
      cmVtx2.vtZ *= zsize;
    }

    /* find distance between centres of mass */
    dist1 = vtxDist(cmVtx1, cmVtx2);

    /* find cm 1 to surface 2 dist */
    if( numVtxs2 > 0 ){
      minDist = vtxDist(cmVtx1, edgeVtxs2[0]);
      for(j=0; j < numVtxs2; j++){
	dist = vtxDist(cmVtx1, edgeVtxs2[j]);
	if( dist < minDist ){
	  minDist = dist;
	}
      }
      dist2 = minDist;
      if( !WlzInsideDomain(obj2, cmVtx1.vtZ/zsize,
			   cmVtx1.vtY/ysize, cmVtx1.vtX/xsize,
			   &errNum) ){
	dist2 *= -1.0;
      }
    }
    else {
      dist2 = 0.0;
    }

    /* find surface 1 to cm 2 dist */
    if( numVtxs1 > 0 ){
      minDist = vtxDist(cmVtx2, edgeVtxs1[0]);
      for(j=0; j < numVtxs1; j++){
	dist = vtxDist(cmVtx2, edgeVtxs1[j]);
	if( dist < minDist ){
	  minDist = dist;
	}
      }
      dist3 = minDist;
      if( !WlzInsideDomain(obj1, cmVtx2.vtZ/zsize,
			   cmVtx2.vtY/ysize, cmVtx2.vtX/xsize,
			   &errNum) ){
	dist3 *= -1.0;
      }
    }
    else {
      dist3 = 0.0;
    }

    /* find min distance between surfaces */
    if( (numVtxs1 > 0) && (numVtxs2 > 0) ){
      minDist = vtxDist(edgeVtxs1[0], edgeVtxs2[0]);
      for(i=0; i < numVtxs1; i++){
	for(j=0; j < numVtxs2; j++){
	  dist = vtxDist(edgeVtxs1[i], edgeVtxs2[j]);
	  if( dist < minDist ){
	    minDist = dist;
	  }
	}
      }
      dist4 = minDist;
    }
    else {
      dist4 = 0.0;
    }

    /* get the volumes */
    if( obj1->type == WLZ_2D_DOMAINOBJ ){
      vol1 = WlzArea(obj1, &errNum);
      vol2 = WlzArea(obj2, &errNum);
    }
    else {
      vol1 = WlzVolume(obj1, &errNum);
      vol2 = WlzVolume(obj2, &errNum);
      vol1 *= xsize*ysize*zsize;
      vol2 *= xsize*ysize*zsize;
    }

    /* print it */
    fprintf(stdout, "%.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f\n",
	    vol1, vol2, mass1, mass2, dist1, dist2, dist3, dist4);
  }

  return 0;
}
Пример #9
0
/*!
* \return	Woolz error code.
* \ingroup	WlzBinaryOps
* \brief	Splits the reference object into component objects cliped
*		from the reference object, with the bounding box of each
*		of the component objects determined using the pre-processed
*		object. The component objects are returned in size order.
* \param	refObj			Reference object.
* \param	ppObj			Pre-processed object which is
*					normalised to values in the range
*					0 - 255 as WlzUByte greys.
* \param	bWidth			Border width.
* \param	bgdFrac			Minimum fraction of values which are
* 					background values, with range
*					[0.0+ - 1.0-].
* \param	sigma			Histogram smoothing parameter used
*					by WlzHistogramCnvGauss().
* \param	compThrMethod		Method for computing threshold, used
*					in call to WlzCompThresholdVT().
* \param	nReqComp		Number of required components.
* \param	dstNComp		Destination pointer for the number of
*					components extracted, must not be NULL.
* \param	dstComp			Destination pointer for the extracted
*					components, must not be NULL.
*/
WlzErrorNum	WlzSplitObj(WlzObject *refObj, WlzObject *ppObj,
			      int bWidth, double bgdFrac, double sigma,
			      WlzCompThreshType compThrMethod,
			      int nReqComp, int *dstNComp,
			      WlzObject ***dstComp)
{
  int		dim,
  		idC;
  WlzObject	*hObj = NULL,
  		*tObj = NULL;
  WlzObject 	**comp = NULL;
  WlzBox	box;
  WlzPixelV	tV;
  WlzSplitObjData split;
  WlzThresholdType tType;
  WlzConnectType lCon;
  WlzErrorNum	errNum = WLZ_ERR_NONE;
  const int	maxComp = 1024;

  split.nLComp = 0;
  split.compI = NULL;
  split.lCompSz = NULL;
  split.lComp = NULL;
  if((refObj == NULL) || (ppObj == NULL))
  {
    errNum = WLZ_ERR_OBJECT_NULL;
  }
  else if((refObj->domain.core == NULL) || (ppObj->domain.core == NULL))
  {
    errNum = WLZ_ERR_DOMAIN_NULL;
  }
  else if((refObj->values.core == NULL) || (ppObj->values.core == NULL))
  {
    errNum = WLZ_ERR_VALUES_NULL;
  }
  else if(refObj->type != ppObj->type)
  {
    errNum = WLZ_ERR_OBJECT_TYPE;
  }
  else if((dstNComp == NULL) || (dstComp == NULL))
  {
    errNum = WLZ_ERR_PARAM_NULL;
  }
  else if((bgdFrac < DBL_EPSILON) || (bgdFrac > (1.0 - DBL_EPSILON)))
  {
    errNum = WLZ_ERR_PARAM_DATA;
  }
  else
  {
    switch(refObj->type)
    {
      case WLZ_2D_DOMAINOBJ:
	dim = 2;
        lCon = WLZ_8_CONNECTED;
	break;
      case WLZ_3D_DOMAINOBJ:
	dim = 3;
        lCon = WLZ_26_CONNECTED;
	break;
      default:
        errNum = WLZ_ERR_OBJECT_TYPE;
	break;
    }
  }
  /* Compute threshold value and type from histogram. */
  if(errNum == WLZ_ERR_NONE)
  {
    hObj = WlzAssignObject(
    	   WlzHistogramObj(ppObj, 256, 0.0, 1.0, &errNum), NULL);
  }
  if(errNum == WLZ_ERR_NONE)
  {
    errNum = WlzHistogramCnvGauss(hObj, sigma, 0);
  }
  if(errNum == WLZ_ERR_NONE)
  {
    errNum = WlzCompThresholdVT(hObj, compThrMethod, bgdFrac, 0.0,
    			        0.0, &tV, &tType);
  }
  (void )WlzFreeObj(hObj); hObj = NULL;
  /* Threshold object. */
  if(errNum == WLZ_ERR_NONE)
  {
    tObj = WlzAssignObject(
     	   WlzThreshold(ppObj, tV, tType, &errNum), NULL);
  }
  /* Label to get connected components. */
  if(errNum == WLZ_ERR_NONE)
  {
    errNum = WlzLabel(tObj, &(split.nLComp), &(split.lComp), maxComp, 0, lCon);
  }
  /* Sort connected components by size. */
  if(errNum == WLZ_ERR_NONE)
  {
    if(split.nLComp < nReqComp)
    {
      nReqComp = split.nLComp;
    }
    if(((split.compI = (int *)AlcMalloc(sizeof(int) *
                                        split.nLComp)) == NULL) ||
       ((split.lCompSz = (int *)AlcMalloc(sizeof(int) *
                                          split.nLComp)) == NULL))
    {
      errNum = WLZ_ERR_MEM_ALLOC;
    }
  }
  if(errNum == WLZ_ERR_NONE)
  {
    idC = 0; 
    while((errNum == WLZ_ERR_NONE) && (idC < split.nLComp))
    {
      split.compI[idC] = idC;
      split.lCompSz[idC] = (dim == 2)? WlzArea(split.lComp[idC], &errNum):
      				       WlzVolume(split.lComp[idC], &errNum);
      ++idC;
    }
  }
  if(errNum == WLZ_ERR_NONE)
  {
    /* Sort component indices by component size. */
    AlgQSort(split.compI, split.nLComp, sizeof(int), &split,
    	     WlzSplitObjSortSzFn);
    /* Allocate array for cliped component objects. */
    if((comp = (WlzObject **)AlcCalloc(sizeof(WlzObject *),
    				       split.nLComp)) == NULL)
    {
      errNum = WLZ_ERR_MEM_ALLOC;
    }
  }
  /* Compute bounding box and clip objects from the reference object. */
  if(errNum == WLZ_ERR_NONE)
  {
    idC = 0;
    while((errNum == WLZ_ERR_NONE) && (idC < nReqComp))
    {
      if(dim == 2)
      {
        box.i2 = WlzBoundingBox2I(split.lComp[split.compI[idC]], &errNum);
	if(errNum == WLZ_ERR_NONE)
	{
	  box.i2.xMin -= bWidth;
	  box.i2.yMin -= bWidth;
	  box.i2.xMax += bWidth;
	  box.i2.yMax += bWidth;
	  comp[idC] = WlzClipObjToBox2D(refObj, box.i2, &errNum);
	}
      }
      else /* dim == 3 */
      {
        box.i3 = WlzBoundingBox3I(split.lComp[split.compI[idC]], &errNum);
	if(errNum == WLZ_ERR_NONE)
	{
	  box.i3.xMin -= bWidth;
	  box.i3.yMin -= bWidth;
	  box.i3.zMin -= bWidth;
	  box.i3.xMax += bWidth;
	  box.i3.yMax += bWidth;
	  box.i3.zMax += bWidth;
	  comp[idC] = WlzClipObjToBox3D(refObj, box.i3, &errNum);
	}
      }
      ++idC;
    }
  }
  if(errNum == WLZ_ERR_NONE)
  {
    *dstNComp = nReqComp;
    *dstComp = comp;
  }
  /* Free temporary storage. */
  if(split.lComp)
  {
    for(idC = 0; idC < split.nLComp; ++idC)
    {
      (void )WlzFreeObj(split.lComp[idC]);
    }
    AlcFree(split.lComp);
  }
  AlcFree(split.compI);
  AlcFree(split.lCompSz);
  (void )WlzFreeObj(tObj);
  return(errNum);
}
Пример #10
0
/*!
* \return	Woolz error code.
* \ingroup	WlzBinaryOps
* \brief	Splits the given montage object into component objects
*		clipped from the montage object.  The montage object
*		must be composed of component images embedded in a
*		background, with little variation in the background
*		values.
* \param	mObj			Montage object, which must be either
*					a WLZ_2D_DOMAINOBJ or a
*					WLZ_3D_DOMAINOBJ with values.
* \param	gapV			Value for the uniform background.
*					Must be either WLZ_GREY_INT or
*					WLZ_GREY_RGBA.
* \param	tol			Tolerance (fraction) for the
*					variation in background values.
* \param	bWidth			Additional boundary width added
*					to detected images before they are
*					clipped.
* \param	minArea			Minimum area for a valid component
*					image, must be greater than zero.
* \param	maxComp			Maximum number of components.
* \param	dstNComp		Destination pointer for the number of
*					components extracted, must not be NULL.
* \param	dstComp			Destination pointer for the extracted
*					components, must not be NULL.
*/
WlzErrorNum 	WlzSplitMontageObj(WlzObject *mObj, WlzPixelV gapV,
				      double tol, int bWidth, int minArea,
				      int maxComp,
				      int *dstNComp, WlzObject ***dstComp)
{
  int		id0,
  		id1,
		area,
		nLComp = 0;
  WlzObject	*gObj = NULL,
  		*tObj = NULL;
  WlzObject	**lComp;
  WlzGreyType	objG;
  WlzBox	box;
  WlzPixelV	gapLV,
  		gapHV;
  WlzConnectType lCon;
  int		tI[8];
  WlzErrorNum	errNum = WLZ_ERR_NONE;

  tol = WLZ_CLAMP(tol, 0.0, 1.0);
  if(mObj == NULL)
  {
    errNum = WLZ_ERR_OBJECT_NULL;
  }
  else if(minArea < 1)
  {
    errNum = WLZ_ERR_PARAM_DATA;
  }
  else
  {
    switch(mObj->type)
    {
      case WLZ_2D_DOMAINOBJ:
	lCon = WLZ_4_CONNECTED;
        break;
      case WLZ_3D_DOMAINOBJ:
	lCon = WLZ_6_CONNECTED;
        break;
      default:
        errNum = WLZ_ERR_OBJECT_TYPE;
	break;
    }
  }
  if(errNum == WLZ_ERR_NONE)
  {
    objG = WlzGreyTypeFromObj(mObj, &errNum);
  }
  if(errNum == WLZ_ERR_NONE)
  {
    switch(gapV.type)
    {
      case WLZ_GREY_INT: /* FALLTHROUGH */
      case WLZ_GREY_RGBA:
        break;
      default:
	errNum = WLZ_ERR_GREY_TYPE;
	break;
    }
  }
  if(errNum == WLZ_ERR_NONE)
  {
    if(objG == WLZ_GREY_RGBA)
    {
      if(gapV.type != WLZ_GREY_RGBA)
      {
        (void )WlzValueConvertPixel(&gapV, gapV, WLZ_GREY_RGBA);
      }
    }
    else
    {
      if(gapV.type != WLZ_GREY_INT)
      {
        (void )WlzValueConvertPixel(&gapV, gapV, WLZ_GREY_INT);
      }
    }
    gapLV.type = gapHV.type = gapV.type;
    if(gapV.type == WLZ_GREY_INT)
    {
      tI[0] = gapV.v.inv * tol;
      gapLV.v.inv = gapV.v.inv - tI[0];
      gapHV.v.inv = gapV.v.inv + tI[0];
      tObj = WlzThreshold(mObj, gapLV, WLZ_THRESH_HIGH, &errNum);
      if((errNum == WLZ_ERR_NONE) && (tObj != NULL))
      {
	gObj = WlzThreshold(tObj, gapHV, WLZ_THRESH_LOW, &errNum);
      }
      (void )WlzFreeObj(tObj);
      tObj = NULL;
    }
    else /* gapV.type == WLZ_GREY_RGBA */
    {
	tI[0] = WLZ_RGBA_RED_GET(gapV.v.rgbv);
	tI[1] = (int )floor((double )(tI[0]) * tol);
	tI[2] = tI[0] - tI[1];
	tI[5] = tI[0] + tI[1];
	tI[0] = WLZ_RGBA_GREEN_GET(gapV.v.rgbv);
	tI[1] = (int )floor((double )(tI[0]) * tol);
	tI[3] = tI[0] - tI[1];
	tI[6] = tI[0] + tI[1];
	tI[0] = WLZ_RGBA_BLUE_GET(gapV.v.rgbv);
	tI[1] = (int )floor((double )(tI[0]) * tol);
	tI[4] = tI[0] - tI[1];
	tI[7] = tI[0] + tI[1];
	tI[2] = WLZ_CLAMP(tI[2], 0, 255);
	tI[3] = WLZ_CLAMP(tI[3], 0, 255);
	tI[4] = WLZ_CLAMP(tI[4], 0, 255);
	WLZ_RGBA_RGBA_SET(gapLV.v.rgbv, tI[2], tI[3], tI[4], 255);
	tI[5] = WLZ_CLAMP(tI[5], 0, 255);
	tI[6] = WLZ_CLAMP(tI[6], 0, 255);
	tI[7] = WLZ_CLAMP(tI[7], 0, 255);
	WLZ_RGBA_RGBA_SET(gapHV.v.rgbv, tI[5], tI[6], tI[7], 255);
        gObj = WlzRGBABoxThreshold(mObj, gapLV, gapHV, &errNum);
    }
  }
  if(errNum == WLZ_ERR_NONE)
  {
    tObj = WlzDiffDomain(mObj, gObj, &errNum);
  }
  (void )WlzFreeObj(gObj);
  if(errNum == WLZ_ERR_NONE)
  {
    errNum = WlzLabel(tObj, &nLComp, &lComp, maxComp, 0, lCon);
  }
  (void )WlzFreeObj(tObj);
  if(errNum == WLZ_ERR_NONE)
  {
    /* Get rid of small objects using minArea as the threshold. */
    id0 = 0;
    id1 = 0;
    while(id0 < nLComp)
    {
      switch((*(lComp + id0))->type)
      {
        case WLZ_2D_DOMAINOBJ:
	  area = WlzArea(*(lComp + id0), NULL);
	  break;
        case WLZ_3D_DOMAINOBJ:
	  area = WlzVolume(*(lComp + id0), NULL);
	  break;
        default:
          area = 0;
	  break;
      }
      if(area >= minArea)
      {
        *(lComp + id1) = *(lComp + id0);
        ++id1;
      }
      else
      {
        (void )WlzFreeObj(*(lComp + id0));
	*(lComp + id0) = NULL;
      }
      ++id0;
    }
    nLComp = id1;
  }
  if(errNum == WLZ_ERR_NONE)
  {
    /* Clip rectangular objects from the montage object. */
    id0 = 0;
    while((errNum == WLZ_ERR_NONE) && (id0 < nLComp))
    {
      if(tObj->type == WLZ_2D_DOMAINOBJ)
      {
        box.i2 = WlzBoundingBox2I(*(lComp + id0), &errNum);
	box.i2.xMin -= bWidth;
	box.i2.yMin -= bWidth;
	box.i2.xMax += bWidth;
	box.i2.yMax += bWidth;
	(void )WlzFreeObj(*(lComp + id0));
	*(lComp + id0) = WlzClipObjToBox2D(mObj, box.i2, &errNum);
      }
      else /* tObj->type == WLZ_3D_DOMAINOBJ */
      {
        box.i3 = WlzBoundingBox3I(*(lComp + id0), &errNum);
	box.i3.xMin -= bWidth;
	box.i3.yMin -= bWidth;
	box.i3.zMin -= bWidth;
	box.i3.xMax += bWidth;
	box.i3.yMax += bWidth;
	box.i3.zMax += bWidth;
	(void )WlzFreeObj(*(lComp + id0));
	*(lComp + id0) = WlzClipObjToBox3D(mObj, box.i3, &errNum);
      }
      ++id0;
    }
  }
  *dstNComp = nLComp;
  *dstComp = lComp;
  return(errNum);
}
Пример #11
0
int main(int	argc,
	 char	**argv)
{

  WlzObject	*obj;
  FILE		*inFile;
  char 		optList[] = "nh";
  int		option;
  int		count, vol;
  int		numericFlg=0;
  const char    *errMsg;
  WlzErrorNum   errNum = WLZ_ERR_NONE;
    
  /* read the argument list and check for an input file */
  opterr = 0;
  while( (option = getopt(argc, argv, optList)) != EOF ){
    switch( option ){

    case '~': /* dummy to avoid compiler warning */
      break;

    case 'n':
      numericFlg = 1;
      break;

    case 'h':
    default:
      usage(argv[0]);
      return( 1 );

    }
  }

  inFile = stdin;
  if( optind < argc ){
    if( (inFile = fopen(*(argv+optind), "r")) == NULL ){
      if( numericFlg ){
	fprintf(stdout, "0\n");
      }
      else {
	fprintf(stderr, "%s: can't open file %s\n", argv[0], *(argv+optind));
	usage(argv[0]);
      }
	return( 1 );
    }
  }

  count = 0;
  while( (obj = WlzReadObj(inFile, NULL)) != NULL )
  {
    count++;

    switch( obj->type )
    {
     case WLZ_3D_DOMAINOBJ:
       if(((vol = WlzVolume(obj , &errNum)) < 0) ||
          (errNum != WLZ_ERR_NONE)){
	 if( numericFlg ){
	   fprintf(stdout, "0\n");
	 }
	 else {
	   (void )WlzStringFromErrorNum(errNum, &errMsg);
	   fprintf(stderr,
		   "%s: Object %d: error in calculating the volume (%s).\n",
		   *argv, count, errMsg);
	 }
         return 1 ;
       }
       else {
	 if( numericFlg ){
	   fprintf(stdout, "%d\n", vol);
	 }
	 else {
	   fprintf(stderr, "Object %d: number of voxels = %d\n", count, vol);
	 }
       }
       break;

    case WLZ_EMPTY_OBJ:
      if( numericFlg ){
	fprintf(stdout, "0\n");
      }
      else {
	fprintf(stderr, "Object %d: number of voxels = %d\n", count, 0);
      }
      break;

     default:
       fprintf(stderr, "Object %d: not 3D object type\n", count);
       break;

    }

    WlzFreeObj( obj );
  }

  return( 0 );
}