/*! * \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); }
/*! * \return New 3D domain object with corresponding WLZ_GREY_RGBA values. * \ingroup WlzValuesUtils * \brief Creates a WLZ_GREY_RGBA valued object from the given compound * array. This is a static function which will always be called * with valid parameters so they aren't checked. * \param cObj Compound array object. * \param cSpc The colour space. * \param dstErr Destination error pointer may be NULL. */ static WlzObject *WlzCompoundToRGBA2D(WlzCompoundArray *cObj, WlzRGBAColorSpace cSpc, WlzErrorNum *dstErr) { int i, j; WlzObject *rtnObj=NULL; WlzPixelV bckgrnd; WlzObject *objs[4]; WlzObjectType vType; WlzUInt b[4]; WlzErrorNum errNum=WLZ_ERR_NONE; /* Make a copy of the object pointers because WlzUnionN() modifies the * array if it contains empty objects. */ for(i = 0; i < 3; ++i) { objs[i] = cObj->o[i]; } rtnObj = WlzUnionN(3, objs, 0, &errNum); if(errNum == WLZ_ERR_NONE) { /* Add an RGBA valuetable, extract background for each channel */ vType = WlzGreyTableType(WLZ_GREY_TAB_RAGR, WLZ_GREY_RGBA, &errNum); for(i=0; (errNum == WLZ_ERR_NONE) && (i < 3); i++) { bckgrnd = WlzGetBackground(cObj->o[i], &errNum); if(errNum == WLZ_ERR_NONE) { errNum = WlzValueConvertPixel(&bckgrnd, bckgrnd, WLZ_GREY_UBYTE); b[i] = bckgrnd.v.ubv; } } } if(errNum == WLZ_ERR_NONE) { WlzValues values; bckgrnd.type = WLZ_GREY_RGBA; WLZ_RGBA_RGBA_SET(bckgrnd.v.rgbv, b[0], b[1], b[2], 255); values.v = WlzNewValueTb(rtnObj, vType, bckgrnd, &errNum); if(values.v != NULL) { rtnObj->values = WlzAssignValues(values, &errNum); } else { (void )WlzFreeObj(rtnObj); rtnObj = NULL; } } /* Transfer values */ if( errNum == WLZ_ERR_NONE) { WlzGreyValueWSpace *gValWSpc[4]; WlzIntervalWSpace iwsp; WlzGreyWSpace gwsp; WlzGreyV gval; /* do it dumb fashion for now, rgb only */ gValWSpc[0] = gValWSpc[1] = gValWSpc[2] = gValWSpc[3] = NULL; for(i=0; i < 3; i++) { if((cObj->o[i] != NULL) && (cObj->o[i]->type != WLZ_EMPTY_OBJ)) { gValWSpc[i] = WlzGreyValueMakeWSp(cObj->o[i], &errNum); if(errNum != WLZ_ERR_NONE) { break; } } } if(errNum == WLZ_ERR_NONE) { errNum = WlzInitGreyScan(rtnObj, &iwsp, &gwsp); } while((errNum == WLZ_ERR_NONE) && ((errNum = WlzNextGreyInterval(&iwsp)) == WLZ_ERR_NONE)) { WlzPixelV pix; for(j = iwsp.lftpos; j <= iwsp.rgtpos; j++) { for(i = 0; i < 3; i++) { if(gValWSpc[i] == NULL) { pix.v.ubv = (i < 2)? 0: 255; } else { WlzGreyValueGet(gValWSpc[i], 0, iwsp.linpos, j); pix.type = gValWSpc[i]->gType; pix.v = gValWSpc[i]->gVal[0]; WlzValueConvertPixel(&pix, pix, WLZ_GREY_UBYTE); } b[i] = pix.v.ubv; } WLZ_RGBA_RGBA_SET(gval.rgbv, b[0], b[1], b[2], b[3]); *gwsp.u_grintptr.rgbp = gval.rgbv; gwsp.u_grintptr.rgbp++; } } if(errNum == WLZ_ERR_EOO) { errNum = WLZ_ERR_NONE; } for(i=0; i < 3; i++) { WlzGreyValueFreeWSp(gValWSpc[i]); } } if(dstErr != NULL) { *dstErr = errNum; } return(rtnObj); }
int main(int argc, char **argv) { WlzObject *obj1, *obj, **objlist; WlzObjectType type = (WlzObjectType) -1; int n, nmax; FILE *inFile; char optList[] = "n:h"; int option; const char *errMsg; WlzErrorNum errNum = WLZ_ERR_NONE; /* read the argument list and check for an input file */ opterr = 0; nmax = 100; while( (option = getopt(argc, argv, optList)) != EOF ) { switch( option ) { case 'n': nmax = atoi(optarg); if( nmax < 1 ) { fprintf(stderr, "%s: nmax = %d is invalid\n", argv[0], nmax); usage(argv[0]); return( 1 ); } break; case 'h': default: usage(argv[0]); return( 1 ); } } inFile = stdin; if( optind < argc ) { if( (inFile = fopen(*(argv+optind), "r")) == NULL ) { fprintf(stderr, "%s: can't open file %s\n", argv[0], *(argv+optind)); usage(argv[0]); return( 1 ); } } /* allocate space for the object pointers */ if( (objlist = (WlzObject **) AlcMalloc(sizeof(WlzObject *) * nmax)) == NULL ) { (void )fprintf(stderr, "%s: memory allocation failed.\n", argv[0]); return( 1 ); } /* read objects accumulating compatible types */ n = 0; while(((obj = WlzReadObj(inFile, NULL)) != NULL) && (n < nmax) ) { if( type == -1 && (obj->type == WLZ_2D_DOMAINOBJ || obj->type == WLZ_3D_DOMAINOBJ) ) { type = obj->type; } if( (obj->type == type) || (obj->type == WLZ_EMPTY_OBJ) ) { objlist[n++] = WlzAssignObject(obj, NULL); } else { WlzFreeObj( obj ); } } if((obj1 = WlzUnionN(n, objlist, 1, &errNum)) == NULL) { (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: failed to perform union (%s).\n", argv[0], errMsg); return(1); } else { if((errNum = WlzWriteObj(stdout, obj1)) != WLZ_ERR_NONE) { (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: failed to write union object (%s).\n", argv[0], errMsg); } } /* freespace so purify can check for leaks */ WlzFreeObj(obj1); while( n-- ) { WlzFreeObj(objlist[n]); } AlcFree((void *) objlist); return( 0 ); }
int main(int argc, char **argv) { WlzObject *obj1 = NULL, *obj = NULL, **objlist = NULL, *rtnObj = NULL; WlzObjectType type = (WlzObjectType) -1; int i, n, nmax, p; FILE *inFile; char optList[] = "d:mn:h"; int option; int meanFlg=0; WlzPixelV bckgrnd; WlzValues values; const char *errMsg; WlzErrorNum errNum = WLZ_ERR_NONE; /* setup the return object type and background */ bckgrnd.type = WLZ_GREY_INT; bckgrnd.v.inv = 0; /* read the argument list and check for an input file */ opterr = 0; nmax = 100; rtnObj = NULL; while( (option = getopt(argc, argv, optList)) != EOF ){ switch( option ){ /* read in a target domain over which to capture the occupancy, set grey type to WLZ_GREY_INT and value to zero */ case 'd': if((inFile = fopen(optarg, "rb")) != NULL){ if((obj = WlzReadObj(inFile, &errNum)) != NULL){ if( (rtnObj = WlzAddValuesTable(obj, WLZ_GREY_INT, bckgrnd, &errNum)) == NULL ){ (void )WlzStringFromErrorNum(errNum, &errMsg); fprintf(stderr, "%s: Failed to add values table to occupancy object: %s\n", argv[0], errMsg); return 1; } errNum = WlzGreySetValue(rtnObj, bckgrnd); WlzFreeObj(obj); } else { fprintf(stderr, "%s: Can't read occupancy domain file\n", argv[0]); usage(argv[0]); } fclose(inFile); } else { fprintf(stderr, "%s: Can't open occupancy domain file\n", argv[0]); usage(argv[0]); } break; case 'm': meanFlg = 1; break; case 'n': nmax = atoi(optarg); if( nmax < 1 ){ fprintf(stderr, "%s: nmax = %d is invalid\n", argv[0], nmax); usage(argv[0]); return( 1 ); } break; case 'h': default: usage(argv[0]); return( 1 ); } } inFile = stdin; if( optind < argc ){ if( (inFile = fopen(*(argv+optind), "rb")) == NULL ){ fprintf(stderr, "%s: can't open file %s\n", argv[0], *(argv+optind)); usage(argv[0]); return( 1 ); } } /* allocate space for the object pointers */ if( (objlist = (WlzObject **) AlcMalloc(sizeof(WlzObject *) * nmax)) == NULL ){ (void )fprintf(stderr, "%s: memory allocation failed.\n", argv[0]); return( 1 ); } /* read objects accumulating compatible types */ n = 0; while( ((obj = WlzAssignObject(WlzReadObj(inFile, NULL), NULL)) != NULL) && (n < nmax) ) { if( type == -1 && (obj->type == WLZ_2D_DOMAINOBJ || obj->type == WLZ_3D_DOMAINOBJ) ){ type = obj->type; } if( obj->type == type ){ objlist[n++] = WlzAssignObject(obj, NULL); } else { WlzFreeObj( obj ); } } if( type == WLZ_EMPTY_OBJ ){ return( 0 ); } /* check for occupancy object */ if( rtnObj ){ /* check type against return object - must be the same */ if( type != rtnObj->type ){ fprintf(stderr, "%s: Occupancy domain object type does not match\n" " inout domain type\n", argv[0]); usage(argv[0]); return 1; } } else { /* use the union of input domains as the occupancy object */ if((obj1 = WlzUnionN(n, objlist, 1, &errNum)) == NULL) { (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: failed to perform union (%s).\n", argv[0], errMsg); return(1); } rtnObj = WlzAddValuesTable(obj1, WLZ_GREY_INT, bckgrnd, &errNum); errNum = WlzGreySetValue(rtnObj, bckgrnd); WlzFreeObj(obj1); } /* now add 1 to each pixel for each domain */ bckgrnd.v.inv = 1; for(i=0; i < n; i++){ if((obj1 = WlzIntersect2(rtnObj, objlist[i], &errNum)) != NULL){ if( obj1->type != WLZ_EMPTY_OBJ ){ if( obj1->type == WLZ_2D_DOMAINOBJ ){ obj1->values = WlzAssignValues(rtnObj->values, &errNum); } else { values.vox = WlzMakeVoxelValueTb(WLZ_VOXELVALUETABLE_GREY, obj1->domain.p->plane1, obj1->domain.p->lastpl, bckgrnd, NULL, &errNum); obj1->values = WlzAssignValues(values, &errNum); for(p=obj1->domain.p->plane1; p <= obj1->domain.p->lastpl; p++){ values.vox->values[p-obj1->domain.p->plane1] = WlzAssignValues(rtnObj->values.vox->values[p-rtnObj->domain.p->plane1], &errNum); } } errNum = WlzGreyScalarAddValue(obj1, bckgrnd); } WlzFreeObj(obj1); } } /* output occupancy object */ if( rtnObj ){ if( meanFlg ){ bckgrnd.v.inv = 255; errNum = WlzGreyScalarMultValue(rtnObj, bckgrnd); bckgrnd.v.inv = n; errNum = WlzGreyScalarDivValue(rtnObj, bckgrnd); } WlzWriteObj(stdout, rtnObj); } /* freespace so purify can check for leaks */ while( n-- ){ WlzFreeObj(objlist[n]); } AlcFree((void *) objlist); return( 0 ); }