/*! * \return Woolz error code. * \ingroup WlzExtFF * \brief Writes the given Woolz 3D domain object with values to * the given file using the Amira lattice (.am file) format. * \param fP Output file pointer * \param obj Object to be written. */ static WlzErrorNum WlzEffAmWrite3DDomObj(FILE *fP, WlzObject *obj) { size_t dataCnt, dataSz; WlzDBox3 bbD; WlzIBox3 bbI; WlzIVertex3 sz, org; WlzDVertex3 vSz; char *dateS, *dataTypeS; time_t tTime; WlzGreyType gType; void ***data = NULL; WlzErrorNum errNum = WLZ_ERR_UNIMPLEMENTED; if(obj->domain.core->type != WLZ_PLANEDOMAIN_DOMAIN) { errNum = WLZ_ERR_DOMAIN_TYPE; } else if(obj->values.core == NULL) { errNum = WLZ_ERR_VALUES_NULL; } else if(obj->values.core->type != WLZ_VOXELVALUETABLE_GREY) { errNum = WLZ_ERR_VALUES_TYPE; } else { bbI = WlzBoundingBox3I(obj, &errNum); } if(errNum == WLZ_ERR_NONE) { tTime = time(NULL); dateS = ctime(&tTime); *(dateS + strlen(dateS) - 1) = '\0'; } if(errNum == WLZ_ERR_NONE) { vSz = WlzVozelSz(obj, &errNum); } if(errNum == WLZ_ERR_NONE) { bbD.xMin = bbI.xMin * vSz.vtX; bbD.xMax = bbI.xMax * vSz.vtX; bbD.yMin = bbI.yMin * vSz.vtY; bbD.yMax = bbI.yMax * vSz.vtY; bbD.zMin = bbI.zMin * vSz.vtZ; bbD.zMax = bbI.zMax * vSz.vtZ; sz.vtX = bbI.xMax - bbI.xMin + 1; sz.vtY = bbI.yMax - bbI.yMin + 1; sz.vtZ = bbI.zMax - bbI.zMin + 1; org.vtX = bbI.xMin; org.vtY = bbI.yMin; org.vtZ = bbI.zMin; gType = WlzGreyTypeFromObj(obj, &errNum); } if(errNum == WLZ_ERR_NONE) { switch(gType) { case WLZ_GREY_SHORT: dataTypeS = "short"; break; case WLZ_GREY_UBYTE: dataTypeS = "byte"; break; default: errNum = WLZ_ERR_GREY_TYPE; break; } } if(errNum == WLZ_ERR_NONE) { if(fprintf(fP, "# AmiraMesh 3D BINARY 2.0\n" "# WlzExtFF\n" "# CreationDate: %s\n" "\n" "define Lattice %d %d %d\n" "\n" "Parameters {\n" " Content \"%dx%dx%d %s, uniform coordinates\",\n" " BoundingBox %g %g %g %g %g %g,\n" " CoordType \"uniform\"\n" "}\n" "\n" "Lattice { %s Data } @1\n" "\n" "# Data section follows\n" "@1\n", dateS, sz.vtX, sz.vtY, sz.vtZ, sz.vtX, sz.vtY, sz.vtZ, dataTypeS, bbD.xMin, bbD.xMax, bbD.yMin, bbD.yMax, bbD.zMin, bbD.zMax, dataTypeS) <= 0) { errNum = WLZ_ERR_WRITE_INCOMPLETE; } } if(errNum == WLZ_ERR_NONE) { errNum = WlzToArray3D(&data, obj, sz, org, 0, gType); } if(errNum == WLZ_ERR_NONE) { dataCnt = sz.vtX * sz.vtY * sz.vtZ; switch(gType) { case WLZ_GREY_SHORT: dataSz = 2; break; case WLZ_GREY_UBYTE: dataSz = 1; break; default: break; } if(fwrite(**data, dataSz, dataCnt, fP) != dataSz * dataCnt) { errNum = WLZ_ERR_WRITE_INCOMPLETE; } } (void )Alc3Free(data); return(errNum); }
/*! * \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 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, ok = 1, usage = 0, dim = 0, transform = 0; WlzIBox3 bBox[2]; WlzEffFormat outFmt = WLZEFF_FORMAT_NONE; WlzEffFormat inFmt[2] = {WLZEFF_FORMAT_NONE}; WlzObject *outObj = NULL; WlzObject *inObj[2] = {NULL}; char *outFileStr; char *inFileStr[2] = {NULL}; WlzAffineTransform *tr = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; const char *errMsg; static char optList[] = "s:t:o:u:hT", fileStrDef[] = "-"; opterr = 0; inFileStr[0] = fileStrDef; inFileStr[1] = fileStrDef; outFileStr = fileStrDef; while((usage == 0) && ((option = getopt(argc, argv, optList)) != -1)) { switch(option) { case 's': if((inFmt[0] = WlzEffStringExtToFormat(optarg)) == 0) { usage = 1; ok = 0; } break; case 't': if((inFmt[1] = WlzEffStringExtToFormat(optarg)) == 0) { usage = 1; ok = 0; } break; case 'u': if((outFmt = WlzEffStringExtToFormat(optarg)) == 0) { usage = 1; ok = 0; } break; case 'o': outFileStr = optarg; break; case 'T': transform = 1; break; case 'h': /* FALLTHROUGH */ default: usage = 1; break; } } if(usage == 0) { usage = (optind + 2 != argc); } if(usage == 0) { inFileStr[0] = *(argv + optind); inFileStr[1] = *(argv + optind + 1); } if(usage == 0) { if(inFmt[0] == WLZEFF_FORMAT_NONE) { inFmt[0] = WlzEffStringFormatFromFileName(inFileStr[0]); } if(inFmt[1] == WLZEFF_FORMAT_NONE) { inFmt[1] = WlzEffStringFormatFromFileName(inFileStr[1]); } if(outFmt == WLZEFF_FORMAT_NONE) { outFmt = WlzEffStringFormatFromFileName(outFileStr); } if((inFmt[0] == WLZEFF_FORMAT_NONE) || (inFmt[1] == WLZEFF_FORMAT_NONE) || (outFmt == WLZEFF_FORMAT_NONE)) { usage = 1; } } ok = (usage == 0); /* Read source and target objects. */ if(ok) { int idx; for(idx = 0; ok && (idx < 2); ++idx) { char *fStr; FILE *fP; if(strcmp(inFileStr[idx], "-") == 0) { fP = stdin; fStr = NULL; } else { fP = NULL; fStr = inFileStr[idx]; } if((inObj[idx] = WlzAssignObject(WlzEffReadObj(fP, fStr, inFmt[idx], 0, 0, 0, &errNum), NULL)) == NULL) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: Failed to read object from file(s) %s (%s)\n", *argv, inFileStr[idx], errMsg); } } } /* Check object dimension */ if(ok) { int idx; int d[2]; d[0] = 0; for(idx = 0; ok && (idx < 2); ++idx) { d[idx] = WlzObjectDimension(inObj[idx], &errNum); ok = (errNum == WLZ_ERR_NONE); } if(errNum == WLZ_ERR_NONE) { if((d[0] == 0) || (d[0] != d[1])) { ok = 0; errNum = WLZ_ERR_OBJECT_TYPE; } } dim = d[0]; if(errNum != WLZ_ERR_NONE) { (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: Failed to establish object dimensions (%s)\n", *argv, errMsg); } } /* Compute the bounding box of the source and target objects. */ if(ok) { int idx; for(idx = 0; ok && (idx < 2); ++idx) { bBox[idx] = WlzBoundingBox3I(inObj[idx], &errNum); if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: Failed to compute bounding box of object read from file\n" "%s (%s).\n", *argv, inFileStr[idx], errMsg); } } } /* Compute affine transform which will register the source to target * bounding box. */ if(ok) { int idx, nVtx; WlzVertexType vType; WlzTransformType tType; WlzVertexP vP[2]; WlzDVertex3 wSp[48]; vP[0].d3 = wSp; vP[1].d3 = wSp + 24; if(dim == 2) { nVtx = 4; vType = WLZ_VERTEX_D2; tType = WLZ_TRANSFORM_2D_AFFINE; for(idx = 0; idx < 2; ++idx) { (vP[idx].d2)[0].vtX = bBox[idx].xMin; (vP[idx].d2)[0].vtY = bBox[idx].yMin; (vP[idx].d2)[1].vtX = bBox[idx].xMax; (vP[idx].d2)[1].vtY = bBox[idx].yMin; (vP[idx].d2)[2].vtX = bBox[idx].xMax; (vP[idx].d2)[2].vtY = bBox[idx].yMax; (vP[idx].d2)[3].vtX = bBox[idx].xMin; (vP[idx].d2)[3].vtY = bBox[idx].yMax; } } else /* dim == 3 */ { nVtx = 8; vType = WLZ_VERTEX_D3; tType = WLZ_TRANSFORM_3D_AFFINE; for(idx = 0; idx < 2; ++idx) { (vP[idx].d3)[0].vtX = bBox[idx].xMin; (vP[idx].d3)[0].vtY = bBox[idx].yMin; (vP[idx].d3)[0].vtZ = bBox[idx].zMin; (vP[idx].d3)[1].vtX = bBox[idx].xMax; (vP[idx].d3)[1].vtY = bBox[idx].yMin; (vP[idx].d3)[1].vtZ = bBox[idx].zMin; (vP[idx].d3)[2].vtX = bBox[idx].xMax; (vP[idx].d3)[2].vtY = bBox[idx].yMax; (vP[idx].d3)[2].vtZ = bBox[idx].zMin; (vP[idx].d3)[3].vtX = bBox[idx].xMin; (vP[idx].d3)[3].vtY = bBox[idx].yMax; (vP[idx].d3)[3].vtZ = bBox[idx].zMin; (vP[idx].d3)[4].vtX = bBox[idx].xMin; (vP[idx].d3)[4].vtY = bBox[idx].yMin; (vP[idx].d3)[4].vtZ = bBox[idx].zMax; (vP[idx].d3)[5].vtX = bBox[idx].xMax; (vP[idx].d3)[5].vtY = bBox[idx].yMin; (vP[idx].d3)[5].vtZ = bBox[idx].zMax; (vP[idx].d3)[6].vtX = bBox[idx].xMax; (vP[idx].d3)[6].vtY = bBox[idx].yMax; (vP[idx].d3)[6].vtZ = bBox[idx].zMax; (vP[idx].d3)[7].vtX = bBox[idx].xMin; (vP[idx].d3)[7].vtY = bBox[idx].yMax; (vP[idx].d3)[7].vtZ = bBox[idx].zMax; } } tr = WlzAffineTransformLSq(vType, nVtx, vP[1], nVtx, vP[0], 0, NULL, tType, &errNum); if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: Failed to compute affine transform (%s).\n", *argv, errMsg); } } /* Create affine transform object or affine transform the source object * as required. */ if(ok) { int idx, idy; const double eps = 1.0e-12; /* Tidy up the transform, |t_{ij}| < eps => t_{ij} = 0.0. */ for(idy = 0; idy < 4; ++idy) { for(idx = 0; idx < 4; ++idx) { if(fabs(tr->mat[idy][idx]) < eps) { tr->mat[idy][idx] = 0.0; } } } if(transform) { outObj = WlzAffineTransformObj(inObj[0], tr, WLZ_INTERPOLATION_NEAREST, &errNum); if(outObj) { tr = NULL; } } else { WlzDomain dom; WlzValues val; dom.t = tr; val.core = NULL; outObj = WlzMakeMain(WLZ_AFFINE_TRANS, dom, val, NULL, NULL, &errNum); } if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: Failed to %saffine transform object (%s).\n", *argv, (transform)? "create ": "", errMsg); } } /* Write the output object. */ if(ok) { char *fStr; FILE *fP; if(strcmp(outFileStr, "-") == 0) { fP = stdout; fStr = NULL; } else { fP = NULL; fStr = outFileStr; } errNum = WlzEffWriteObj(fP, fStr, outObj, outFmt); if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsg); (void )fprintf(stderr, "%s: Failed to write object to file %s (%s)\n", *argv, outFileStr, errMsg); } } (void )WlzFreeObj(inObj[0]); (void )WlzFreeObj(inObj[1]); (void )WlzFreeObj(outObj); if(usage) { char *fmtStr = NULL; fmtStr = WlzEffFormatTable(2, 50, 10, NULL); (void )fprintf( stderr, "Usage: %s%s%s%s%s%s%s%s\n", *argv, " [-h] [-T]\n" "\t\t[-o<out file>] [-s<src fmt>] [-t<tgt fmt>] [-u<out fmt>]\n" "\t\t<source object> <target object>\n" "Computes the Woolz affine transform which makes the bounding box of\n" "the source object equal to that of the target object.\n" "Version: ", WlzVersion(), "\n" " -h Help, prints this usage information.\n" " -o Output file.\n" " -s Source file format.\n" " -t Target file format.\n" " -u Output file format.\n" " -T Transform the source object and write it to the output file\n" " rather than the affine transform.\n" "The known file formats are:\n" " Description Extension\n" " *********** *********\n", fmtStr, "Simple example:\n ", *argv, "-o out.wlz small.vtk big.stl\n" "Reads the source object from the file small.vtk and the target\n" "object from big.stl then computes the affine transform which scales\n" "the source objects bounding box to fit that of the target object.\n" "The output transform is written to the file out.wlz.\n"); } return(!ok); }
/*! * \return New filtered object with new values or NULL on error. * \ingroup WlzValuesFilters * \brief Applies a seperable filter to the given object using the given * convolution kernels. * \param inObj Input 2 or 3D spatial domain object * to be filtered which must have scalar * values. * \param cBufSz Convolution kernel sizes (sz), each * kernel buffer is sized (2 * sz) + 1 * with the centre indexed sz into the * buffer. * \param cBuf Convolution kernel buffers. * \param direc Set to non-zero in directions for which * the filter is to be applied. * \param gType Required return object grey type. * Passing in WLZ_GREY_ERROR will * request the given input object's grey * type. * \param pad Type of padding. * \param padVal Padding value, only used when * pad == ALG_PAD_VALUE. * \param dstErr Destination error pointer may be NULL. */ WlzObject *WlzSepFilter(WlzObject *inObj, WlzIVertex3 cBufSz, double *cBuf[], WlzIVertex3 direc, WlzGreyType gType, AlgPadType pad, double padVal, WlzErrorNum *dstErr) { int dim = 0, vSz = 0, nThr = 1; double **iBuf = NULL, **rBuf = NULL; double *vBuf = NULL; WlzObject *rnObj = NULL; WlzIVertex3 vBufSz = {0}; WlzIBox3 bBox = {0}; WlzErrorNum errNum = WLZ_ERR_NONE; #ifdef _OPENMP #pragma omp parallel { #pragma omp master { nThr = omp_get_num_threads(); } } #endif if(inObj == NULL) { errNum = WLZ_ERR_OBJECT_NULL; } else if(inObj->domain.core == NULL) { errNum = WLZ_ERR_DOMAIN_NULL; } else if(inObj->values.core == NULL) { errNum = WLZ_ERR_VALUES_NULL; } else { switch(inObj->type) { case WLZ_2D_DOMAINOBJ: dim = 2; break; case WLZ_3D_DOMAINOBJ: dim = 3; break; default: errNum = WLZ_ERR_OBJECT_TYPE; break; } } if((errNum == WLZ_ERR_NONE) && (gType == WLZ_GREY_ERROR)) { gType = WlzGreyTypeFromObj(inObj, &errNum); } if(errNum == WLZ_ERR_NONE) { if(errNum == WLZ_ERR_NONE) { switch(gType) { case WLZ_GREY_INT: /* FALLTHROUGH */ case WLZ_GREY_SHORT: /* FALLTHROUGH */ case WLZ_GREY_UBYTE: /* FALLTHROUGH */ case WLZ_GREY_FLOAT: /* FALLTHROUGH */ case WLZ_GREY_DOUBLE: break; default: errNum = WLZ_ERR_GREY_TYPE; break; } } } if(errNum == WLZ_ERR_NONE) { bBox = WlzBoundingBox3I(inObj, &errNum); if(errNum == WLZ_ERR_NONE) { vBufSz.vtX = bBox.xMax - bBox.xMin + 1; vBufSz.vtY = bBox.yMax - bBox.yMin + 1; if(dim == 3) { vBufSz.vtZ = bBox.zMax - bBox.zMin + 1; } } } if(errNum == WLZ_ERR_NONE) { vSz = ALG_MAX3(vBufSz.vtX, vBufSz.vtY, vBufSz.vtZ); if(((iBuf = (double **)AlcMalloc(sizeof(double *) * 2 * nThr)) == NULL) || ((vBuf = (double *)AlcMalloc(sizeof(double) * 2 * nThr * vSz)) == NULL)) { errNum = WLZ_ERR_MEM_ALLOC; } else { int idt; rBuf = iBuf + nThr; for(idt = 0; idt < nThr; ++idt) { iBuf[idt] = vBuf + (idt * vSz); rBuf[idt] = vBuf + ((nThr + idt) * vSz); } } } if(errNum == WLZ_ERR_NONE) { /* Convolve the object values. */ if(direc.vtX) { rnObj = WlzSepFilterX(inObj, dim, nThr, iBuf, rBuf, cBufSz.vtX, cBuf[0], pad, padVal, &errNum); } if((errNum == WLZ_ERR_NONE) && direc.vtY) { WlzObject *tObj; tObj = WlzSepFilterY((rnObj)? rnObj: inObj, dim, nThr, iBuf, rBuf, cBufSz.vtY, cBuf[1], pad, padVal, &errNum); (void )WlzFreeObj(rnObj); rnObj = tObj; } if((errNum == WLZ_ERR_NONE) && (dim == 3) && direc.vtZ) { WlzObject *tObj; tObj = WlzSepFilterZ((rnObj)? rnObj: inObj, bBox, nThr, iBuf, rBuf, cBufSz.vtZ, cBuf[2], pad, padVal, &errNum); (void )WlzFreeObj(rnObj); rnObj = tObj; } } if((errNum == WLZ_ERR_NONE) && (rnObj != NULL) && (gType != WLZ_GREY_DOUBLE)) { WlzObject *tObj; /* Convert object values to the required grey type. */ tObj = WlzConvertPix((rnObj)? rnObj: inObj, gType, &errNum); (void )WlzFreeObj(rnObj); rnObj = tObj; } if(errNum != WLZ_ERR_NONE) { (void )WlzFreeObj(rnObj); rnObj = NULL; } AlcFree(iBuf); AlcFree(vBuf); return(rnObj); }
/*! * \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 *WlzCompoundToRGBA3D(WlzCompoundArray *cObj, WlzRGBAColorSpace cSpc, WlzErrorNum *dstErr) { WlzIBox3 bBox; WlzDomain dom; WlzValues val; WlzPixelV bgd; WlzObject *rObj = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; dom.core = NULL; val.core = NULL; bgd.v.rgbv = 0; bgd.type = WLZ_GREY_RGBA; bBox = WlzBoundingBox3I((WlzObject *)cObj, &errNum); if(errNum == WLZ_ERR_NONE) { dom.p = WlzMakePlaneDomain(WLZ_PLANEDOMAIN_DOMAIN, bBox.zMin, bBox.zMax, bBox.yMin, bBox.yMax, bBox.xMin, bBox.xMax, &errNum); } if(errNum == WLZ_ERR_NONE) { val.vox = WlzMakeVoxelValueTb(WLZ_VOXELVALUETABLE_GREY, bBox.zMin, bBox.zMax, bgd, NULL, &errNum); } if(errNum == WLZ_ERR_NONE) { rObj = WlzMakeMain(WLZ_3D_DOMAINOBJ, dom, val, NULL, NULL, &errNum); } if(errNum == WLZ_ERR_NONE) { int idP, nPl; nPl = bBox.zMax - bBox.zMin + 1; for(idP = 0; idP < nPl; ++idP) { if(errNum == WLZ_ERR_NONE) { int idC; WlzObject *rObj2 = NULL; WlzCompoundArray *cObj2 = NULL; WlzErrorNum errNum2 = WLZ_ERR_NONE; cObj2 = WlzMakeCompoundArray(WLZ_COMPOUND_ARR_2, 1, cObj->n, NULL, WLZ_2D_DOMAINOBJ, &errNum2); idC = 0; while((errNum2 == WLZ_ERR_NONE) && (idC < cObj->n)) { int idP2, nPl2; WlzDomain dom2; WlzValues val2; dom2.core = NULL; val2.core = NULL; if((cObj->o[idC] != NULL) && (cObj->o[idC]->type == WLZ_3D_DOMAINOBJ)) { idP2 = bBox.zMin + idP - cObj->o[idC]->domain.p->plane1; nPl2 = cObj->o[idC]->domain.p->lastpl - cObj->o[idC]->domain.p->plane1 + 1; if((idP2 >= 0) && (idP2 <= nPl2)) { dom2 = *(cObj->o[idC]->domain.p->domains + idP2); val2 = *(cObj->o[idC]->values.vox->values + idP2); } } cObj2->o[idC] = (dom2.core == NULL)? WlzMakeEmpty(&errNum2): WlzMakeMain(WLZ_2D_DOMAINOBJ, dom2, val2, NULL, NULL, &errNum2); ++idC; } if(errNum2 == WLZ_ERR_NONE) { rObj2 = WlzCompoundToRGBA2D(cObj2, cSpc, &errNum2); } if(errNum2 == WLZ_ERR_NONE) { dom.p->domains[idP] = WlzAssignDomain(rObj2->domain, NULL); val.vox->values[idP] = WlzAssignValues(rObj2->values, NULL); } (void )WlzFreeObj(rObj2); (void )WlzFreeObj((WlzObject *)cObj2); if(errNum2 != WLZ_ERR_NONE) { errNum = errNum2; } } } } if(errNum != WLZ_ERR_NONE) { if(rObj != NULL) { (void )WlzFreeObj(rObj); rObj = NULL; } else { (void )WlzFreePlaneDomain(dom.p); (void )WlzFreeVoxelValueTb(val.vox); } } if(dstErr) { *dstErr = errNum; } return(rObj); }