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); }
/*! * \return Object containing the point (x,y). * \ingroup WlzBinaryOps * \brief Takes a WLZ_2D_DOMAINOBJ, calls WlzLabel to split the domain * and returns the one containing point(x,y). * \param obj Given WLZ_2D_DOMAINOBJ object. * \param x Column coordinate. * \param y Line coordinate. * \param dstErr Destination error code pointer, may be NULL. */ WlzObject *Wlz2DContains(WlzObject *obj, double x, double y, WlzErrorNum *dstErr) { WlzObject *retObj = NULL; WlzObject **objArray; int i, nobjs; /* int maxNobjs=1024; */ int maxNobjs=2048; int found = 0; WlzErrorNum errNum=WLZ_ERR_NONE; WlzConnectType connectivity = WLZ_8_CONNECTED; /* fprintf(stderr, "entering Wlz2DContains %f,%f\n", x,y); fflush(stderr); */ if(obj->type != WLZ_2D_DOMAINOBJ) return (NULL); /* get array of domains */ if(obj != NULL) { errNum = WlzLabel(obj, &nobjs, &objArray, maxNobjs, 0, connectivity); } /* fprintf(stderr, "got array of %d objects\n", nobjs); fflush(stderr); */ /* select the required domain */ /* ie the one which contains clicked point */ if((errNum == WLZ_ERR_NONE) && (nobjs > 0)) { for(i=0; i<nobjs; i++) { /* fprintf(stderr, "checking domain # %d\n", i); fflush(stderr); */ if(WlzInsideDomain(objArray[i], 0.0, y, x, &errNum)) { if(errNum == WLZ_ERR_NONE) { found = 1; /* fprintf(stderr, "domain # %d contains point\n", i); fflush(stderr); */ break; } else { (void )fprintf(stderr, "WlzInsideDomain, Wlz error: %d\n", (int )errNum); fflush(stderr); } } else { WlzFreeObj(objArray[i]); } } if((found == 1) && (errNum == WLZ_ERR_NONE)) { /* retObj = objArray[i]; */ retObj = WlzCopyObject(objArray[i], &errNum); WlzFreeObj(objArray[i]); if(errNum != WLZ_ERR_NONE) { (void )fprintf(stderr, "WlzCopyObject, Wlz error: %d\n", (int )errNum); fflush(stderr); } } } *dstErr = errNum; /* fprintf(stderr, "leaving Wlz2DContains\n"); fflush(stderr); */ return retObj; }
/*! * \return New Woolz object without holes or NULL on error. * \ingroup WlzDomainOps * \brief Fills the holes in the given object's domain (which are by * definition not connected to the outside). When the given * object's domain has more than one component part, the * object should first be labeled, this function should then be * called for each of the labeled parts and then the union of * the filled domains should be formed. * \param srcObj Given 3D domain object. * \param dstErr Destination error pointer, may be NULL. */ WlzObject *WlzDomainFill3D( WlzObject *srcObj, WlzErrorNum *dstErr) { int nPln = 0; WlzObject *bndObj = NULL, *filObj = NULL, *gvnObj = NULL, *sedObj = NULL, *shlObj = NULL; WlzPixelV zeroV; WlzValues nullVal; WlzErrorNum errNum = WLZ_ERR_NONE; nullVal.core = NULL; zeroV.type = WLZ_GREY_UBYTE; zeroV.v.ubv = 0; if(srcObj == NULL) { errNum = WLZ_ERR_OBJECT_NULL; } else if(srcObj->type != WLZ_3D_DOMAINOBJ) { errNum = WLZ_ERR_OBJECT_TYPE; } else if(srcObj->domain.core == NULL) { errNum = WLZ_ERR_DOMAIN_NULL; } else { gvnObj = WlzMakeMain(srcObj->type, srcObj->domain, nullVal, NULL, NULL, &errNum); } /* Create a then shell 1 voxel thick just inside the given objects's * domain. */ if(errNum == WLZ_ERR_NONE) { WlzObject *difObj = NULL; difObj = WlzAssignObject( WlzBoundaryDomain(gvnObj, &errNum), NULL); if(errNum == WLZ_ERR_NONE) { WlzIBox3 clipBox; /* Clip the dilated shell domain to make sure it stays within the * bounding box of the given object then all planes will align. */ clipBox.xMin = gvnObj->domain.p->kol1; clipBox.yMin = gvnObj->domain.p->line1; clipBox.zMin = gvnObj->domain.p->plane1; clipBox.xMax = gvnObj->domain.p->lastkl; clipBox.yMax = gvnObj->domain.p->lastln; clipBox.zMax = gvnObj->domain.p->lastpl; shlObj = WlzAssignObject( WlzClipObjToBox3D(difObj, clipBox, &errNum), NULL); } (void )WlzFreeObj(difObj); } /* Make sure that the bounding box of the thin shell domain fits it and * that it's first and last planes have interrvals. */ if(errNum == WLZ_ERR_NONE) { errNum = WlzStandardPlaneDomain(shlObj->domain.p, NULL); } /* Create a value table for the shell object with values set to zero. */ if(errNum == WLZ_ERR_NONE) { WlzValues val; WlzObjectType tType; tType = WlzGreyTableType(WLZ_GREY_TAB_INTL, WLZ_GREY_UBYTE, NULL); val.vox = WlzMakeVoxelValueTb(WLZ_VOXELVALUETABLE_GREY, shlObj->domain.p->plane1, shlObj->domain.p->lastpl, zeroV, NULL, &errNum); if(errNum == WLZ_ERR_NONE) { int p; nPln = shlObj->domain.p->lastpl - shlObj->domain.p->plane1 + 1; shlObj->values = WlzAssignValues(val, NULL); #ifdef _OPENMP #pragma omp parallel for shared(shlObj) #endif for(p = 0; p < nPln; ++p) { if(errNum == WLZ_ERR_NONE) { WlzDomain dom2; WlzErrorNum errNum2; dom2 = shlObj->domain.p->domains[p]; if(dom2.core) { WlzValues val2; WlzObject *shlObj2; shlObj2 = WlzMakeMain(WLZ_2D_DOMAINOBJ, dom2, nullVal, NULL, NULL, &errNum2); if(errNum2 == WLZ_ERR_NONE) { val2.i = WlzMakeIntervalValues(tType, shlObj2, zeroV, &errNum2); /* WlzMakeIntervalValues() sets all values to zero. */ } if(errNum2 == WLZ_ERR_NONE) { shlObj->values.vox->values[p] = WlzAssignValues(val2, NULL); } (void )WlzFreeObj(shlObj2); if(errNum2 == WLZ_ERR_NONE) { #ifdef _OPENMP #pragma omp critical { #endif if((errNum == WLZ_ERR_NONE) && (errNum2 != WLZ_ERR_NONE)) { errNum = errNum2; } #ifdef _OPENMP } #endif } } } } } } /* Compute the (plane-wise) boundary list for the given object. */ if(errNum == WLZ_ERR_NONE) { bndObj = WlzObjToBoundary(gvnObj, 0, &errNum); } /* Sweep down through the boundary object setting the values of * those voxels in the shell object to a non zero value when they * correspond to top level boundaries. */ if(errNum == WLZ_ERR_NONE) { int p; #ifdef _OPENMP #pragma omp parallel for shared(bndObj,shlObj) #endif for(p = 0; p < nPln; ++p) { if(errNum == WLZ_ERR_NONE) { WlzDomain bDom2; bDom2 = bndObj->domain.p->domains[p]; if(bDom2.core) { WlzDomain iDom2; WlzValues iVal2; WlzObject *iObj2 = NULL; WlzGreyValueWSpace *gVWSp = NULL; WlzErrorNum errNum2 = WLZ_ERR_NONE; iDom2 = shlObj->domain.p->domains[p]; iVal2 = shlObj->values.vox->values[p]; iObj2 = WlzMakeMain(WLZ_2D_DOMAINOBJ, iDom2, iVal2, NULL, NULL, &errNum2); if(errNum == WLZ_ERR_NONE) { gVWSp = WlzGreyValueMakeWSp(iObj2, &errNum2); } if(errNum2 == WLZ_ERR_NONE) { WlzBoundList *bnd, *bnd2; bnd2 = bDom2.b; for(bnd = bnd2; bnd != NULL; bnd = bnd->next) { if(bnd->poly != NULL) { WlzPolygonDomain *ply; ply = bnd->poly; if(ply) { int i; WlzIVertex2 *vtx; vtx = ply->vtx; for(i = 0; i < ply->nvertices; ++i) { WlzGreyValueGet(gVWSp, 0, vtx[i].vtY, vtx[i].vtX); *(gVWSp->gPtr[0].ubp) = 255; } } } } } else { #ifdef _OPENMP #pragma omp critical { #endif if(errNum == WLZ_ERR_NONE) { errNum = errNum2; } #ifdef _OPENMP } #endif } (void )WlzFreeObj(iObj2); WlzGreyValueFreeWSp(gVWSp); } } } } /* Threshold the shell object, throwing away all but where the voxels * are set to create a seed domain. Then remove the value table from * the shell object and free it as it's no longer needed. */ if(errNum == WLZ_ERR_NONE) { WlzObject *tObj = NULL; WlzPixelV tV; tV.type = WLZ_GREY_UBYTE; tV.v.ubv = 1; tObj = WlzAssignObject( WlzThreshold(shlObj, tV, WLZ_THRESH_HIGH, &errNum), NULL); if(errNum == WLZ_ERR_NONE) { sedObj = WlzAssignObject( WlzMakeMain(tObj->type, tObj->domain, nullVal, NULL, NULL, &errNum), NULL); } (void )WlzFreeObj(tObj); tObj = NULL; if(errNum == WLZ_ERR_NONE) { tObj = WlzAssignObject( WlzMakeMain(shlObj->type, shlObj->domain, nullVal, NULL, NULL, &errNum), NULL); } (void )WlzFreeObj(shlObj); shlObj = NULL; if(errNum == WLZ_ERR_NONE) { shlObj = tObj; tObj = NULL; } (void )WlzFreeObj(tObj); #ifdef WLZ_DOMOMAINFILL3D_DEBUG { FILE *fP; fP = fopen("debug-shlObj-00.wlz", "w"); (void )WlzWriteObj(fP, shlObj); (void )fclose(fP); } #endif } /* Label the shell domain using 26-connectivity in 3D and then * keep only those component objects which intersect the seed domain. * Then free the shell and seed domains replacing the shell domain * with the union of the intersecting labeled component objects. * Finaly free the intersecting component objects, keeping only the * new shell domain. */ if(errNum == WLZ_ERR_NONE) { int i, j, nCSObj = 0; WlzIBox3 bBox; WlzObject **csObj = NULL; bBox = WlzBoundingBox3I(shlObj, &errNum); if(errNum == WLZ_ERR_NONE) { int maxCSObj; maxCSObj = ((bBox.xMax - bBox.xMin + 1) * (bBox.yMax - bBox.yMin + 1) * (bBox.zMax - bBox.zMin + 1)) / 8; if(maxCSObj < 8) { maxCSObj = 8; } errNum = WlzLabel(shlObj, &nCSObj, &csObj, maxCSObj, 0, WLZ_26_CONNECTED); } if(errNum == WLZ_ERR_NONE) { for(i = 0; i < nCSObj; ++i) { if(!WlzHasIntersection(csObj[i], sedObj, &errNum)) { (void )WlzFreeObj(csObj[i]); csObj[i] = NULL; } } } if(errNum == WLZ_ERR_NONE) { /* Squeeze out any NULL objects reseting their number.*/ for(i = 0, j = 0; i < nCSObj; ++i) { if(csObj[i]) { csObj[j++] = csObj[i]; } } nCSObj = j; } if(errNum == WLZ_ERR_NONE) { WlzObject *iObj = NULL, *uObj = NULL; uObj = WlzAssignObject( WlzUnionN(nCSObj, csObj, 0, &errNum), NULL); iObj = WlzAssignObject( WlzIntersect2(uObj, shlObj, &errNum), NULL); (void )WlzFreeObj(uObj); (void )WlzFreeObj(shlObj); shlObj = iObj; #ifdef WLZ_DOMOMAINFILL3D_DEBUG { FILE *fP; fP = fopen("debug-shlObj-01.wlz", "w"); (void )WlzWriteObj(fP, shlObj); (void )fclose(fP); } #endif } if(csObj) { for(i = 0; i < nCSObj; ++i) { (void )WlzFreeObj(csObj[i]); } (void )AlcFree(csObj); } } /* Sweep down through the boundary lists again creating new boundary lists * which do not have boundaries that do not intersect the new shell domain. * Then create a new filled object from these boundary lists. */ if(errNum == WLZ_ERR_NONE) { int p, nPlnFil; WlzDomain filDom; nPlnFil = shlObj->domain.p->lastpl - shlObj->domain.p->plane1 + 1; filDom.p = WlzMakePlaneDomain(WLZ_PLANEDOMAIN_DOMAIN, shlObj->domain.p->plane1, shlObj->domain.p->lastpl, shlObj->domain.p->line1, shlObj->domain.p->lastln, shlObj->domain.p->kol1, shlObj->domain.p->lastkl, &errNum); #ifdef _OPENMP #pragma omp parallel for shared(bndObj,shlObj) #endif for(p = 0; p < nPlnFil; ++p) { if(errNum == WLZ_ERR_NONE) { WlzDomain bDom2; bDom2 = bndObj->domain.p->domains[p]; if(bDom2.core) { WlzDomain sDom2; WlzObject *fObj2 = NULL; WlzBoundList *newBnd = NULL; WlzErrorNum errNum2 = WLZ_ERR_NONE; sDom2 = shlObj->domain.p->domains[p]; if(sDom2.core) { newBnd = WlzDomFill3DDoBound2D(bDom2.b, sDom2, &errNum2); if(newBnd != NULL) { fObj2 = WlzBoundToObj(newBnd, WLZ_SIMPLE_FILL, &errNum2); (void )WlzFreeBoundList(newBnd); } if(errNum2 == WLZ_ERR_NONE) { if(fObj2) { filDom.p->domains[p] = WlzAssignDomain(fObj2->domain, NULL); } } else { #ifdef _OPENMP #pragma omp critical { #endif if(errNum == WLZ_ERR_NONE) { errNum = errNum2; } #ifdef _OPENMP } #endif } (void )WlzFreeObj(fObj2); } } } } if(errNum == WLZ_ERR_NONE) { errNum = WlzStandardPlaneDomain(filDom.p, NULL); } if(errNum == WLZ_ERR_NONE) { WlzObject *tObj0 = NULL, *tObj1 = NULL; /* Put back any isolated voxels this function has removed. */ tObj0 = WlzAssignObject( WlzMakeMain(srcObj->type, filDom, nullVal, NULL, NULL, &errNum), NULL); if(errNum == WLZ_ERR_NONE) { tObj1 = WlzUnion2(gvnObj, tObj0, &errNum); } if(errNum == WLZ_ERR_NONE) { filObj = WlzMakeMain(tObj1->type, tObj1->domain, nullVal, NULL, NULL, &errNum); } (void )WlzFreeObj(tObj0); (void )WlzFreeObj(tObj1); } } (void )WlzFreeObj(bndObj); (void )WlzFreeObj(gvnObj); (void )WlzFreeObj(shlObj); (void )WlzFreeObj(sedObj); if((errNum != WLZ_ERR_NONE) && (filObj != NULL)) { (void )WlzFreeObj(filObj); filObj = NULL; } if(dstErr) { *dstErr = errNum; } return(filObj); }
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; }
/*! * \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); }
/*! * \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); }
int main(int argc, char *argv[]) { int option, nReg = 0, tNReg = 0, ok = 1, usage = 0, verbose = 0, threshSet = 0, centreSet = 0; double minArea = 2; char *inExt, *dbgExt, *inDir, *dbgDir, *inFile, *dbgFile, *inPath = NULL, *dbgPath = NULL, *outFile = NULL; WlzRadDistVal distSort = WLZ_RADDISTVAL_AREA; WlzRadDistRec *distData = NULL; WlzPixelV thrVal; WlzDVertex2 centre; WlzCompThreshType thrMtd = WLZ_COMPTHRESH_OTSU; WlzThresholdType thrMod = WLZ_THRESH_HIGH; WlzEffFormat inFmt = WLZEFF_FORMAT_NONE, dbgFmt = WLZEFF_FORMAT_NONE; WlzObject *inObj = NULL, *disObj = NULL, *segObj = NULL; WlzGreyValueWSpace *disGVWSp = NULL; WlzObject **regObjs = NULL; FILE *fP = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; const int maxObj = 1000000; char pathBuf[FILENAME_MAX]; const double eps = 1.0e-06; const char *errMsg; static char optList[] = "hvAGDHELR:c:d:n:o:t:", defFile[] = "-"; thrVal.type = WLZ_GREY_DOUBLE; thrVal.v.dbv = 0.0; outFile = defFile; while((usage == 0) && ok && ((option = getopt(argc, argv, optList)) != -1)) { switch(option) { case 'A': distSort = WLZ_RADDISTVAL_AREA; break; case 'D': distSort = WLZ_RADDISTVAL_DIST; break; case 'G': distSort = WLZ_RADDISTVAL_ANGLE; break; case 'H': thrMod = WLZ_THRESH_HIGH; break; case 'E': thrMod = WLZ_THRESH_EQUAL; break; case 'L': thrMod = WLZ_THRESH_LOW; break; case 'R': distSort = WLZ_RADDISTVAL_RADIUS; break; case 'h': usage = 1; break; case 'v': verbose = 1; break; case 'c': centreSet = 1; if(sscanf(optarg, "%lg,%lg", &(centre.vtX), &(centre.vtY)) != 2) { usage = 1; } break; case 'd': dbgPath = optarg; break; case 'o': outFile = optarg; break; case 'n': if(sscanf(optarg, "%lg", &minArea) != 1) { usage = 1; } break; case 't': threshSet = 1; if(sscanf(optarg, "%lg", &(thrVal.v.dbv)) != 1) { usage = 1; } break; default: usage = 1; break; } } ok = !usage; if(ok) { if((optind + 1) != argc) { usage = 1; ok = 0; } else { inPath = *(argv + optind); } } if(ok && verbose) { (void )fprintf(stderr, "inPath = %s\n", inPath); } /* Parse input file path into path + name + ext. */ if(ok) { ok = (usage = WlzRadDistParsePath(inPath, &inDir, &inFile, &inExt, &inFmt)) == 0; } if(ok && verbose) { (void )fprintf(stderr, "inDir = %s\n", inDir); (void )fprintf(stderr, "inFile = %s\n", inFile); (void )fprintf(stderr, "inExt = %s\n", (inExt)? inExt: "(null)"); (void )fprintf(stderr, "inFmt = %s\n", WlzEffStringFromFormat(inFmt, NULL)); } /* Read image. */ if(ok) { errNum = WLZ_ERR_READ_EOF; if(inExt) { (void )sprintf(pathBuf, "%s/%s.%s", inDir, inFile, inExt); } else { (void )sprintf(pathBuf, "%s/%s", inDir, inFile); } if(((inObj = WlzAssignObject(WlzEffReadObj(NULL, pathBuf, inFmt, 0, 0, 0, &errNum), NULL)) == NULL) || (inObj->type != WLZ_2D_DOMAINOBJ)) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: Failed to read 2D image object from file %s (%s)\n", *argv, pathBuf, errMsg); } } if(ok && verbose) { (void )fprintf(stderr, "read input image ok.\n"); } /* Convert to grey if needed, normalise 0 - 255 if needed and compute * threshold value unless already known. */ if(ok) { if(WlzGreyTypeFromObj(inObj, NULL) == WLZ_GREY_RGBA) { WlzObject *ppObj; ppObj = WlzAssignObject( WlzRGBAToModulus(inObj, &errNum), NULL); if(errNum == WLZ_ERR_NONE) { (void )WlzFreeObj(inObj); inObj = ppObj; } } if(threshSet == 0) { WlzObject *hObj = NULL; errNum = WlzGreyNormalise(inObj, 1); if(errNum == WLZ_ERR_NONE) { hObj = WlzHistogramObj(inObj, 256, 0.0, 1.0, &errNum); } if(errNum == WLZ_ERR_NONE) { threshSet = 1; errNum = WlzCompThreshold(&thrVal.v.dbv, hObj, thrMtd, 0); } (void )WlzFreeObj(hObj); } if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: failed to normalise object (%s)\n", *argv, errMsg); } } /* Segment the object. */ if(ok) { if(inObj->values.core == NULL) { segObj = WlzAssignObject(inObj, NULL); } else { segObj = WlzAssignObject( WlzThreshold(inObj, thrVal, thrMod, &errNum), NULL); if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: failed to segment image (%s)\n", *argv, errMsg); } } } /* Compute object with the same domain as the input object but in which * the values are the minimum distance from an edge. */ if(ok) { WlzObject *bObj = NULL; bObj = WlzBoundaryDomain(inObj, &errNum); if(errNum == WLZ_ERR_NONE) { disObj = WlzAssignObject( WlzDistanceTransform(inObj, bObj, WLZ_OCTAGONAL_DISTANCE, 0.0, 0.0, &errNum), NULL); } if(errNum == WLZ_ERR_NONE) { disGVWSp = WlzGreyValueMakeWSp(disObj, &errNum); } if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: failed to compute distance object (%s)\n", *argv, errMsg); } (void )WlzFreeObj(bObj); } /* Output the debug image if required. */ if(ok && dbgPath) { WlzObject *dbgObj; dbgObj = WlzAssignObject(WlzCopyObject(inObj, &errNum), NULL); if(errNum == WLZ_ERR_NONE) { WlzPixelV iMin, iMax, oMin, oMax; if(dbgObj->values.core == NULL) { WlzValues tmpVal; oMax.type = WLZ_GREY_UBYTE; oMax.v.ubv = 255; tmpVal.v = WlzNewValueTb(dbgObj, WlzGreyTableType(WLZ_GREY_TAB_RAGR, WLZ_GREY_UBYTE, NULL), oMax, &errNum); if(errNum == WLZ_ERR_NONE) { dbgObj->values = WlzAssignValues(tmpVal, NULL); } } else { WlzObject *tmpObj = NULL; oMin.type = WLZ_GREY_UBYTE; oMin.v.ubv = 0; oMax.type = WLZ_GREY_UBYTE; oMax.v.ubv = 200; errNum = WlzGreyRange(dbgObj, &iMin, &iMax); if(errNum == WLZ_ERR_NONE) { errNum = WlzGreySetRange(dbgObj, iMin, iMax, oMin, oMax, 0); } if(errNum == WLZ_ERR_NONE) { tmpObj = WlzMakeMain(inObj->type, segObj->domain, dbgObj->values, NULL, NULL, &errNum); } if(errNum == WLZ_ERR_NONE) { oMax.v.ubv = 255; errNum = WlzGreySetValue(tmpObj, oMax); } (void )WlzFreeObj(tmpObj); if(errNum == WLZ_ERR_NONE) { tmpObj = WlzConvertPix(dbgObj, WLZ_GREY_UBYTE, &errNum); (void )WlzFreeObj(dbgObj); dbgObj = WlzAssignObject(tmpObj, NULL); } } } if(errNum == WLZ_ERR_NONE) { (void )WlzRadDistParsePath(dbgPath, &dbgDir, &dbgFile, &dbgExt, &dbgFmt); if(dbgExt) { (void )sprintf(pathBuf, "%s/%s.%s", dbgDir, dbgFile, dbgExt); } else { (void )sprintf(pathBuf, "%s/%s", dbgDir, dbgFile); } errNum = WlzEffWriteObj(NULL, pathBuf, dbgObj, dbgFmt); } (void )WlzFreeObj(dbgObj); if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: failed to output the debug image (%s)\n", *argv, errMsg); } } /* Label the segmented object. */ if(ok) { errNum = WlzLabel(segObj, &nReg, ®Objs, maxObj, 0, WLZ_8_CONNECTED); if(errNum != WLZ_ERR_NONE) { ok = 0; errNum = WLZ_ERR_MEM_ALLOC; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: failed to split into components (%s)\n", *argv, errMsg); } if(ok && verbose) { (void )fprintf(stderr, "nReg = %d\n", nReg); } } /* Compute centre of mass if not known. */ if(ok) { if(centreSet == 0) { centre = WlzCentreOfMass2D(inObj, 1, NULL, &errNum); if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: failed to compute centre of mass (%s)\n", *argv, errMsg); } } if(ok && verbose) { (void )fprintf(stderr, "centre = %lg,%lg\n", centre.vtX, centre.vtY); } } /* Allocate a radial distribution table. */ if(ok) { if((distData = (WlzRadDistRec *) AlcCalloc(nReg, sizeof(WlzRadDistRec))) == NULL) { ok = 0; errNum = WLZ_ERR_MEM_ALLOC; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: failed to allocate result lable (%s)\n", *argv, errMsg); } } /* Compute the redial distribution data. */ if(ok) { int idR = 0, idS = 0; while((errNum == WLZ_ERR_NONE) && (idR < nReg)) { double mass; WlzDVertex2 com; com = WlzCentreOfMass2D(regObjs[idR], 1, &mass, NULL); if(mass > minArea - eps) { WlzGreyValueGet(disGVWSp, 0.0, com.vtY, com.vtX); distData[idS].pos = com; distData[idS].area = mass; WLZ_VTX_2_SUB(com, centre, com); distData[idS].radius = WLZ_VTX_2_LENGTH(com); distData[idS].angle = ALG_M_PI + atan2(com.vtY, com.vtX); switch(disGVWSp->gType) { case WLZ_GREY_LONG: distData[idS].dist = *(disGVWSp->gPtr[0].lnp); break; case WLZ_GREY_INT: distData[idS].dist = *(disGVWSp->gPtr[0].inp); break; case WLZ_GREY_SHORT: distData[idS].dist = *(disGVWSp->gPtr[0].shp); break; case WLZ_GREY_UBYTE: distData[idS].dist = *(disGVWSp->gPtr[0].ubp); break; case WLZ_GREY_FLOAT: distData[idS].dist = *(disGVWSp->gPtr[0].flp); break; case WLZ_GREY_DOUBLE: distData[idS].dist = *(disGVWSp->gPtr[0].dbp); break; default: distData[idS].dist = 0.0; break; } ++idS; } ++idR; } tNReg = idS; switch(distSort) { case WLZ_RADDISTVAL_AREA: (void )qsort(distData, tNReg, sizeof(WlzRadDistRec), WlzRadDistRecSortArea); break; case WLZ_RADDISTVAL_ANGLE: (void )qsort(distData, tNReg, sizeof(WlzRadDistRec), WlzRadDistRecSortAngle); break; case WLZ_RADDISTVAL_RADIUS: (void )qsort(distData, tNReg, sizeof(WlzRadDistRec), WlzRadDistRecSortRadius); break; case WLZ_RADDISTVAL_DIST: (void )qsort(distData, tNReg, sizeof(WlzRadDistRec), WlzRadDistRecSortDist); break; } } /* Output the sorted radial distribution table. */ if(ok) { if(((fP = strcmp(outFile, "-")? fopen(outFile, "w"): stdout)) == NULL) { ok = 0; (void )fprintf(stderr, "%s: failed to open output file %s\n", *argv, outFile); } } if(ok) { int idR; for(idR = 0; idR < tNReg; ++idR) { double a; a = (distData[idR].angle > 0.0)? 0 + (180 * distData[idR].angle / ALG_M_PI): 360 + (180 * distData[idR].angle / ALG_M_PI); (void )fprintf(fP, "%g %g %g %g,%g %g\n", a, distData[idR].radius, distData[idR].area, distData[idR].pos.vtX, distData[idR].pos.vtY, distData[idR].dist); } } if(strcmp(outFile, "-")) { (void )fclose(fP); } /* Tidy up. */ AlcFree(distData); WlzGreyValueFreeWSp(disGVWSp); (void )WlzFreeObj(inObj); (void )WlzFreeObj(disObj); (void )WlzFreeObj(segObj); if(regObjs) { int idR; for(idR = 0; idR < nReg; ++idR) { (void )WlzFreeObj(regObjs[idR]); } AlcFree(regObjs); } if(usage) { (void )fprintf(stderr, "Usage: %s [-h] [-v] [-A] [-D] [-G] [-H] [-E] [-L] [-R]\n" "\t\t[-c #,#] [-d <debug image>] [-n #] [-o <out file>]\n" "\t\t[-t #] [<input image>]\n" "Segments the given object using a threshold value and outputs the \n" "radial distribution of the thresholded components.\n" "Version: %s\n" "Options:\n" " -h Help - prints this usage masseage.\n" " -v Verbose output.\n" " -A Sort output by area (default).\n" " -D Sort output by distance from boundary.\n" " -G Sort output by angle.\n" " -H Threshold high, use pixels at or above threshold (default).\n" " -E Threshold equal, use pixels at threshold.\n" " -L Threshold low, use pixels below threshold.\n" " -R Sort output by radial distance from centre.\n" " -c Centre (default is image centre).\n" " -d Debug image.\n" " -n Minimum area (default %g).\n" " -t Threshold value (default is to compute using Otsu's method).\n" "By default the input image object is read from the standard input and\n" "the radial distribution is written to the standard output.\n" "The image formats understood include wlz, jpg and tif.\n" "The output format is:\n" " <angle> <dist from centre> <area> <x pos>,<y pos> <dist form boundary>\n" "Example:\n" " %s -o out.txt -d debug.jpg in.tif\n" "The input image is read from in.tif, a debug image showing the\n" "segmented regions is written to debug.jpg and the radial distribution\n" "statistics are written to the file out.txt. With the output in\n" "out.txt, the following R code would plot the data as a set of circles\n" "with radius proportional to the square root of the component area:\n" " data <- read.table(\"out.txt\")\n" " attach(data)\n" " symbols(x=data$V1, y=data$V2, circles=sqrt(data$V3))\n", argv[0], WlzVersion(), minArea, argv[0]); } return(!ok); }
int main(int argc, char **argv) { WlzObject *obj; WlzObject **objlist = NULL; FILE *inFile; char optList[] = "i:M:vh"; int option; int count, numobj, maxobj = MAXOBJS, i, verbose = 0; int ignw = -1; 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 'i': ignw = atoi(optarg); if( ignw < 0 ){ fprintf(stderr, "%s: ignw = %d is invalid\n", argv[0], ignw); usage(argv[0]); return( 1 ); } break; case 'M': maxobj = atoi(optarg); if( maxobj <= 0 ){ fprintf(stderr, "%s: maxobj = %d is invalid\n", argv[0], maxobj); usage(argv[0]); return( 1 ); } break; case 'v': verbose = 1; break; case 'h': default: usage(argv[0]); return( 1 ); } } inFile = stdin; if( optind < argc ){ if( (inFile = fopen(*(argv+optind), "rb")) == NULL ){ (void )fprintf(stderr, "%s: can't open file %s\n", argv[0], *(argv+optind)); usage(argv[0]); return( 1 ); } } /* read objects and segment if possible */ ignw = (ignw < 0) ? 1 : ignw; count = 0; while((obj = WlzReadObj(inFile, NULL)) != NULL) { count++; switch( obj->type ){ case WLZ_2D_DOMAINOBJ: case WLZ_3D_DOMAINOBJ: errNum = WlzLabel(obj, &numobj, &objlist, maxobj, ignw, WLZ_8_CONNECTED); if(errNum == WLZ_ERR_DOMAIN_TYPE) { errNum = WlzWriteObj(stdout, obj); } else { if(verbose) { fprintf(stderr,"%s: writing %d objects from input object %d\n", argv[0], numobj, count); } for(i=0; (i < numobj) && (errNum == WLZ_ERR_NONE); i++){ errNum = WlzWriteObj(stdout, *(objlist + i)); WlzFreeObj(*(objlist + i)); } } break; default: errNum = WlzWriteObj(stdout, obj); break; } WlzFreeObj(obj); } if(errNum != WLZ_ERR_NONE) { (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr,"%s: failed to label object (%s).\n", argv[0], errMsg); return( 1 ); } return( 0 ); }