/*! * \return New grey value domain object with incrementing values. * \ingroup WlzValuesUtils * \brief Creates a new 3D domain object with integer values that * increment throughout the object in scan order. Object * values are set by incrementing the given value in place. * \param in Input domain object. * \param val Pointer to current value, this is * incremented in place. * \param dstErr Destination error pointer, may be NULL. */ static WlzObject *WlzGreyNewIncValues3D(WlzObject *in, int *val, WlzErrorNum *dstErr) { WlzObjectType gTT; WlzObject *out = NULL; WlzPixelV bgd; WlzValues values; WlzErrorNum errNum = WLZ_ERR_NONE; bgd.type = WLZ_GREY_INT; bgd.v.inv = 0; values.core = NULL; gTT = WlzGreyTableType(WLZ_GREY_TAB_RAGR, WLZ_GREY_INT, &errNum); values.vox = WlzNewValuesVox(in, gTT, bgd, &errNum); if(errNum == WLZ_ERR_NONE) { out = WlzMakeMain(in->type, in->domain, values, in->plist, NULL, &errNum); } if(errNum == WLZ_ERR_NONE) { errNum = WlzGreySetIncValuesItr(out, val); } if(errNum != WLZ_ERR_NONE) { if(out) { (void )WlzFreeObj(out); out = NULL; } else if(values.core != NULL) { (void )WlzFreeVoxelValueTb(values.vox); } } if(dstErr != NULL) { *dstErr = errNum; } return(out); }
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 Distance object which shares the given foreground object's * domain and has integer distance values, null on error. * \ingroup WlzMorphologyOps * \brief Computes the distance of every pixel/voxel in the foreground * object from the reference object. * * A distance transform maps all position within a forground * domain to their distances from a reference domain. * The distance transforms implemented within this function * use efficient morphological primitives. * * Given two domains, * \f$\Omega_r\f$ the reference domain and \f$\Omega_f\f$ * the domain specifying the region of interest, * a domain with a thin shell \f$\Omega_i\f$ * is iteratively expanded from it's initial domain * corresponding to the reference domain \f$\Omega_r\f$. * At each iteration * \f$\Omega_i\f$ is dilated and clipped * by it's intersection with \f$\Omega_f\f$ until \f$\Omega_i\f$ * becomes the null domain \f$\emptyset\f$. * At each iteration the current distance is recorded in a value * table which * covers the domain \f$\Omega_f\f$. * * An octagonal distance scheme may be used in which * the distance metric is alternated between 4 and 8 * connected for 2D and 6 and 26 connectivities in 3D. * See: G. Borgefors. "Distance Transformations in Arbitrary * Dimensions" CVGIP 27:321-345, 1984. * * An approximate Euclidean distance transform may be computed * by: Scaling the given foreground and reference objects using * the given approximation scale parameter, dilating the * reference domain using a sphere with a radius having the same * value as the scale parameter and then finaly sampling the * scaled distances. * \param forObj Foreground object. * \param refObj Reference object. * \param dFn Distance function which must be * appropriate to the dimension of * the foreground and reference objects. * \param dParam Parameter required for distance * function. Currently only * WLZ_APX_EUCLIDEAN_DISTANCE requires a * parameter. In this case the parameter * is the approximation scale. * \param dstErr Destination error pointer, may be NULL. */ WlzObject *WlzDistanceTransform(WlzObject *forObj, WlzObject *refObj, WlzDistanceType dFn, double dParam, WlzErrorNum *dstErr) { int idP, lastP, dim, notDone = 1; double scale; WlzObject *tmpObj, *sObj = NULL, *sForObj = NULL, *sRefObj = NULL, *dilObj = NULL, *dstObj = NULL, *difObj = NULL, *curItrObj = NULL; WlzObject *bothObj[2]; WlzDomain *difDoms; WlzPixelV dstV, bgdV; WlzValues *difVals; WlzAffineTransform *tr = NULL; WlzConnectType con; WlzObjectType dstGType; WlzErrorNum errNum = WLZ_ERR_NONE; WlzValues difVal, dstVal, nullVal; /* By defining WLZ_DIST_TRANSFORM_ENV these normalization parameters * are read from the environment. This is useful for optimization. */ #ifndef WLZ_DIST_TRANSFORM_ENV const #endif /* ! WLZ_DIST_TRANSFORM_ENV */ /* These normalizarion factors have been choosen to minimize the sum of * squares of the deviation of the distance values from Euclidean values * over a radius 100 circle or sphere, where the distances are computed * from the circumference of the sphere towards it's centre. The values * were established by experiment. */ double nrmDist4 = 0.97, nrmDist6 = 0.91, nrmDist8 = 1.36, nrmDist18 = 1.34, nrmDist26 = 1.60; #ifdef WLZ_DIST_TRANSFORM_ENV double val; char *envStr; if(((envStr = getenv("WLZ_DIST_TRANSFORM_NRMDIST4")) != NULL) && (sscanf(envStr, "%lg", &val) == 1)) { nrmDist4 = val; } if(((envStr = getenv("WLZ_DIST_TRANSFORM_NRMDIST6")) != NULL) && (sscanf(envStr, "%lg", &val) == 1)) { nrmDist6 = val; } if(((envStr = getenv("WLZ_DIST_TRANSFORM_NRMDIST8")) != NULL) && (sscanf(envStr, "%lg", &val) == 1)) { nrmDist8 = val; } if(((envStr = getenv("WLZ_DIST_TRANSFORM_NRMDIST18")) != NULL) && (sscanf(envStr, "%lg", &val) == 1)) { nrmDist18 = val; } if(((envStr = getenv("WLZ_DIST_TRANSFORM_NRMDIST26")) != NULL) && (sscanf(envStr, "%lg", &val) == 1)) { nrmDist26 = val; } #endif /* WLZ_DIST_TRANSFORM_ENV */ scale = dParam; nullVal.core = NULL; /* Check parameters. */ if((forObj == NULL) || (refObj == NULL)) { errNum = WLZ_ERR_OBJECT_NULL; } else if(((forObj->type != WLZ_2D_DOMAINOBJ) && (forObj->type != WLZ_3D_DOMAINOBJ)) || ((refObj->type != WLZ_POINTS) && (refObj->type != forObj->type))) { errNum = WLZ_ERR_OBJECT_TYPE; } else if((forObj->domain.core == NULL) || (refObj->domain.core == NULL)) { errNum = WLZ_ERR_DOMAIN_NULL; } if(errNum == WLZ_ERR_NONE) { bgdV.type = WLZ_GREY_INT; bgdV.v.inv = 0; dstV.type = WLZ_GREY_DOUBLE; dstV.v.dbv = 0.0; switch(forObj->type) { case WLZ_2D_DOMAINOBJ: switch(dFn) { case WLZ_4_DISTANCE: /* FALLTHROUGH */ case WLZ_8_DISTANCE: /* FALLTHROUGH */ case WLZ_OCTAGONAL_DISTANCE: /* FALLTHROUGH */ case WLZ_APX_EUCLIDEAN_DISTANCE: dim = 2; break; default: errNum = WLZ_ERR_PARAM_DATA; break; } break; case WLZ_3D_DOMAINOBJ: switch(dFn) { case WLZ_6_DISTANCE: /* FALLTHROUGH */ case WLZ_18_DISTANCE: /* FALLTHROUGH */ case WLZ_26_DISTANCE: /* FALLTHROUGH */ case WLZ_OCTAGONAL_DISTANCE: /* FALLTHROUGH */ case WLZ_APX_EUCLIDEAN_DISTANCE: dim = 3; break; default: errNum = WLZ_ERR_PARAM_DATA; break; } break; default: errNum = WLZ_ERR_OBJECT_TYPE; break; } } if(errNum == WLZ_ERR_NONE) { switch(dFn) { case WLZ_4_DISTANCE: con = WLZ_4_CONNECTED; break; case WLZ_6_DISTANCE: con = WLZ_6_CONNECTED; break; case WLZ_8_DISTANCE: con = WLZ_8_CONNECTED; break; case WLZ_18_DISTANCE: con = WLZ_18_CONNECTED; break; case WLZ_26_DISTANCE: con = WLZ_26_CONNECTED; break; case WLZ_OCTAGONAL_DISTANCE: con = (dim == 2)? WLZ_8_CONNECTED: WLZ_26_CONNECTED; break; case WLZ_APX_EUCLIDEAN_DISTANCE: con = (dim == 2)? WLZ_8_CONNECTED: WLZ_26_CONNECTED; if(scale < 1.0) { errNum = WLZ_ERR_PARAM_DATA; } break; case WLZ_EUCLIDEAN_DISTANCE: errNum = WLZ_ERR_UNIMPLEMENTED; break; default: errNum = WLZ_ERR_PARAM_DATA; break; } } /* Create scaled domains and a sphere domain for structual erosion if the * distance function is approximate Euclidean. */ if(errNum == WLZ_ERR_NONE) { if(dFn == WLZ_APX_EUCLIDEAN_DISTANCE) { tr = (dim == 2)? WlzAffineTransformFromScale(WLZ_TRANSFORM_2D_AFFINE, scale, scale, 0.0, &errNum): WlzAffineTransformFromScale(WLZ_TRANSFORM_3D_AFFINE, scale, scale, scale, &errNum); if(errNum == WLZ_ERR_NONE) { tmpObj = WlzMakeMain(forObj->type, forObj->domain, nullVal, NULL, NULL, &errNum); if(tmpObj) { sForObj = WlzAssignObject( WlzAffineTransformObj(tmpObj, tr, WLZ_INTERPOLATION_NEAREST, &errNum), NULL); (void )WlzFreeObj(tmpObj); } } if(errNum == WLZ_ERR_NONE) { if(refObj->type == WLZ_POINTS) { sRefObj = WlzPointsToDomObj(refObj->domain.pts, scale, &errNum); } else /* type == WLZ_2D_DOMAINOBJ || type == WLZ_3D_DOMAINOBJ */ { tmpObj = WlzMakeMain(refObj->type, refObj->domain, nullVal, NULL, NULL, &errNum); if(errNum == WLZ_ERR_NONE) { sRefObj = WlzAssignObject( WlzAffineTransformObj(tmpObj, tr, WLZ_INTERPOLATION_NEAREST, &errNum), NULL); } } (void )WlzFreeObj(tmpObj); } if(errNum == WLZ_ERR_NONE) { sObj = WlzAssignObject( WlzMakeSphereObject(forObj->type, scale, 0.0, 0.0, 0.0, &errNum), NULL); } (void )WlzFreeAffineTransform(tr); } else { sForObj = WlzAssignObject( WlzMakeMain(forObj->type, forObj->domain, nullVal, NULL, NULL, &errNum), NULL); if(errNum == WLZ_ERR_NONE) { if(refObj->type == WLZ_POINTS) { sRefObj = WlzPointsToDomObj(refObj->domain.pts, 1.0, &errNum); } else { sRefObj = WlzAssignObject( WlzMakeMain(refObj->type, refObj->domain, nullVal, NULL, NULL, &errNum), NULL); } } } } /* Create new values for the computed distances. */ if(errNum == WLZ_ERR_NONE) { dstGType = WlzGreyTableType(WLZ_GREY_TAB_RAGR, WLZ_GREY_INT, NULL); if(dim == 2) { dstVal.v = WlzNewValueTb(sForObj, dstGType, bgdV, &errNum); } else { dstVal.vox = WlzNewValuesVox(sForObj, dstGType, bgdV, &errNum); } } /* Create a distance object using the foreground object's domain and * the new distance values. */ if(errNum == WLZ_ERR_NONE) { dstObj = WlzMakeMain(sForObj->type, sForObj->domain, dstVal, NULL, NULL, &errNum); } if(errNum == WLZ_ERR_NONE) { bothObj[0] = sForObj; errNum = WlzGreySetValue(dstObj, dstV); } /* Dilate the reference object while setting the distances in each * dilated shell. */ while((errNum == WLZ_ERR_NONE) && notDone) { if(dFn == WLZ_APX_EUCLIDEAN_DISTANCE) { dstV.v.dbv += 1.0; } else { switch(con) { case WLZ_4_CONNECTED: dstV.v.dbv += nrmDist4; break; case WLZ_6_CONNECTED: dstV.v.dbv += nrmDist6; break; case WLZ_8_CONNECTED: dstV.v.dbv += nrmDist8; break; case WLZ_18_CONNECTED: dstV.v.dbv += nrmDist18; break; case WLZ_26_CONNECTED: dstV.v.dbv += nrmDist26; break; default: errNum = WLZ_ERR_CONNECTIVITY_TYPE; break; } } if(dFn == WLZ_APX_EUCLIDEAN_DISTANCE) { dilObj = WlzStructDilation(sRefObj, sObj, &errNum); } else { dilObj = WlzDilation(sRefObj, con, &errNum); } if(errNum == WLZ_ERR_NONE) { switch(sForObj->type) { case WLZ_2D_DOMAINOBJ: curItrObj = WlzAssignObject( WlzIntersect2(dilObj, sForObj, &errNum), NULL); break; case WLZ_3D_DOMAINOBJ: bothObj[1] = dilObj; curItrObj = WlzAssignObject( WlzIntersectN(2, bothObj, 1, &errNum), NULL); break; default: errNum = WLZ_ERR_OBJECT_TYPE; break; } } (void)WlzFreeObj(dilObj); /* Create difference object for the expanding shell. */ if(errNum == WLZ_ERR_NONE) { difObj = WlzDiffDomain(curItrObj, sRefObj, &errNum); } if((difObj == NULL) || WlzIsEmpty(difObj, &errNum)) { notDone = 0; } else { /* Assign the distance object's values to the difference object * and set all it's values to the current distance. */ if(errNum == WLZ_ERR_NONE) { switch(sForObj->type) { case WLZ_2D_DOMAINOBJ: difObj->values = WlzAssignValues(dstObj->values, NULL); errNum = WlzGreySetValue(difObj, dstV); break; case WLZ_3D_DOMAINOBJ: /* 3D is more complex than 2D: Need to create a temporary * voxel valuetable and assign the individual 2D values. */ difVal.vox = WlzMakeVoxelValueTb(WLZ_VOXELVALUETABLE_GREY, difObj->domain.p->plane1, difObj->domain.p->lastpl, bgdV, NULL, &errNum); if(errNum == WLZ_ERR_NONE) { difObj->values = WlzAssignValues(difVal, NULL); difDoms = difObj->domain.p->domains; difVals = difObj->values.vox->values; idP = difObj->domain.p->plane1; lastP = difObj->domain.p->lastpl; while(idP <= lastP) { if((*difDoms).core) { dstVal = dstObj->values.vox->values[idP - dstObj->domain.p->plane1]; *difVals = WlzAssignValues(dstVal, NULL); } ++idP; ++difDoms; ++difVals; } if(difObj->domain.p->lastpl > difObj->domain.p->plane1) { errNum = WlzGreySetValue(difObj, dstV); } } break; default: errNum = WLZ_ERR_OBJECT_TYPE; break; } } (void )WlzFreeObj(sRefObj); sRefObj = WlzAssignObject(curItrObj, NULL); (void )WlzFreeObj(curItrObj); } (void )WlzFreeObj(difObj); difObj = NULL; if(dFn == WLZ_OCTAGONAL_DISTANCE) { /* Alternate connectivities for octagonal distance. */ if(dim == 2) { con = (con == WLZ_4_CONNECTED)? WLZ_8_CONNECTED: WLZ_4_CONNECTED; } else /* dim == 3 */ { con = (con == WLZ_6_CONNECTED)? WLZ_26_CONNECTED: WLZ_6_CONNECTED; } } } (void )WlzFreeObj(sObj); (void )WlzFreeObj(sForObj); (void )WlzFreeObj(sRefObj); (void )WlzFreeObj(curItrObj); if((errNum == WLZ_ERR_NONE) && (dFn == WLZ_APX_EUCLIDEAN_DISTANCE)) { tmpObj = WlzDistSample(dstObj, dim, scale, &errNum); (void )WlzFreeObj(dstObj); dstObj = tmpObj; } if(errNum != WLZ_ERR_NONE) { (void )WlzFreeObj(dstObj); dstObj = NULL; } if(dstErr) { *dstErr = errNum; } return(dstObj); }
/*! * \return New object or NULL on error. * \ingroup WlzValuesUtils * \brief Transfers grey values from the source object to the * destination object within the intersection of the source * and destination. Grey values within the destination * object outside of the source object are unchanged. * It is an error if either object has a different dimension * or grey value type, except for when either is an empty * object. * \param dObj Destination object which may be * empty, but otherwise should be of the * same dimension as the source object * with valid values.. * \param sObj Source object which if not empty must * have both a valid domain and valid * values. * \param inplace Overwrite the destination object's * values if non zero. * \param dstErr Destination error pointer, may be NULL. */ WlzObject *WlzGreyTransfer( WlzObject *dObj, WlzObject *sObj, int inplace, WlzErrorNum *dstErr) { WlzObject *rObj = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; if((dObj == NULL) || (sObj == NULL)) { errNum = WLZ_ERR_OBJECT_NULL; } else if(WlzIsEmpty(dObj, NULL)) { rObj = WlzMakeEmpty(&errNum); } else if(WlzIsEmpty(sObj, NULL)) { rObj = WlzMakeMain(dObj->type, dObj->domain, dObj->values, dObj->plist, NULL, &errNum); } else if(dObj->type != sObj->type) { errNum = WLZ_ERR_OBJECT_TYPE; } else if((dObj->domain.core == NULL) || (sObj->domain.core == NULL)) { errNum = WLZ_ERR_DOMAIN_NULL; } else if(sObj->values.core == NULL) { errNum = WLZ_ERR_VALUES_NULL; } else { switch(sObj->type) { case WLZ_2D_DOMAINOBJ: case WLZ_3D_DOMAINOBJ: /* FALLTHROUGH */ { WlzObject *rIObj = NULL; rIObj = WlzIntersect2(dObj, sObj, &errNum); if((errNum == WLZ_ERR_NONE) && (WlzIsEmpty(rIObj, NULL) == 0)) { rObj = (inplace)? WlzMakeMain(dObj->type, dObj->domain, dObj->values, dObj->plist, NULL, &errNum): WlzCopyObject(dObj, &errNum); if(errNum == WLZ_ERR_NONE) { /* If the destination object does not have values then * create them to match the domain of the destination * object. */ if((sObj->values.core != NULL) && (rObj->values.core == NULL)) { WlzPixelV bgdV; WlzGreyType gType; WlzObjectType gTT; WlzValues newVal; newVal.core = NULL; bgdV = WlzGetBackground(sObj, &errNum); if(errNum == WLZ_ERR_NONE) { gType = WlzGreyTypeFromObj(sObj, &errNum); } if(errNum == WLZ_ERR_NONE) { gTT = WlzGreyTableType(WLZ_GREY_TAB_RAGR, gType, NULL); if(rObj->type == WLZ_2D_DOMAINOBJ) { newVal.v = WlzNewValueTb(rObj, gTT, bgdV, &errNum); } else /* rObj->type == WLZ_3D_DOMAINOBJ */ { newVal.vox = WlzNewValuesVox(rObj, gTT, bgdV, &errNum); } } if(errNum == WLZ_ERR_NONE) { rObj->values = WlzAssignValues(newVal, NULL); } if(errNum == WLZ_ERR_NONE) { errNum = WlzGreySetValue(rObj, bgdV); } } } if(errNum == WLZ_ERR_NONE) { if(sObj->type == WLZ_2D_DOMAINOBJ) { WlzObject *sIObj; rIObj->values = WlzAssignValues(rObj->values, NULL); sIObj = WlzMakeMain(WLZ_2D_DOMAINOBJ, rIObj->domain, sObj->values, NULL, NULL, &errNum); if(errNum == WLZ_ERR_NONE) { errNum = WlzGreyTransfer2D(rIObj, sIObj); } (void )WlzFreeObj(sIObj); } else /* sObj->type == WLZ_3D_DOMAINOBJ */ { int p, rTiled, sTiled, nPlanes; rTiled = WlzGreyTableIsTiled(rObj->values.core->type); sTiled = WlzGreyTableIsTiled(sObj->values.core->type); nPlanes = rIObj->domain.p->lastpl - rIObj->domain.p->plane1 + 1; #ifdef _OPENMP #pragma omp parallel for #endif for(p = 0; p < nPlanes; ++p) { if(errNum == WLZ_ERR_NONE) { int pln; WlzDomain dom; WlzValues val; WlzObject *rIObj2D = NULL, *sIObj2D = NULL; WlzErrorNum errNum2D = WLZ_ERR_NONE; pln = p + rIObj->domain.p->plane1; dom = rIObj->domain.p->domains[p]; val = (rTiled)? rObj->values: rObj->values.vox->values[pln - rObj->values.vox->plane1]; rIObj2D = WlzMakeMain(WLZ_2D_DOMAINOBJ, dom, val, NULL, NULL, &errNum2D); if(errNum2D == WLZ_ERR_NONE) { val = (sTiled)? sObj->values: sObj->values.vox->values[pln - sObj->values.vox->plane1]; sIObj2D = WlzMakeMain(WLZ_2D_DOMAINOBJ, dom, val, NULL, NULL, &errNum2D); } if(errNum2D == WLZ_ERR_NONE) { errNum2D = WlzGreyTransfer2D(rIObj2D, sIObj2D); } (void )WlzFreeObj(rIObj2D); (void )WlzFreeObj(sIObj2D); #ifdef _OPENMP #pragma omp critical { #endif if((errNum == WLZ_ERR_NONE) && (errNum2D != WLZ_ERR_NONE)) { errNum = errNum2D; } #ifdef _OPENMP } #endif } } } } } (void )WlzFreeObj(rIObj); } break; default: errNum = WLZ_ERR_OBJECT_TYPE; break; } } if(errNum != WLZ_ERR_NONE) { WlzFreeObj(rObj); rObj = NULL; } if(dstErr) { *dstErr = errNum; } return(rObj); }
/*! * \return New woolz object or NULL on error. * \ingroup WlzArithmetic * \brief Scales the values of the given 3D Woolz object so that * \f$v_{new} = m v_{given} + a.\f$ The input object is known * to be a valid 3D domain object with grey values. * \param iObj Given object. * \param m Value to multiply object values by. * \param a Value to add to product. * \param rGType Required grey type for returned object. * \param dstErr Destination error pointer, may be NULL. */ static WlzObject *WlzScalarMulAdd3D(WlzObject *iObj, WlzPixelV m, WlzPixelV a, WlzGreyType rGType, WlzErrorNum *dstErr) { WlzValues rValues; WlzObjectType rVType; WlzPixelV bgdV; WlzObject *rObj = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; rValues.core = NULL; bgdV = WlzGetBackground(iObj, &errNum); if(errNum == WLZ_ERR_NONE) { rVType = WlzGreyTableType(WLZ_GREY_TAB_RAGR, rGType, &errNum); } if(errNum == WLZ_ERR_NONE) { rValues.vox = WlzNewValuesVox(iObj, rVType, bgdV, &errNum); } if(errNum == WLZ_ERR_NONE) { int idP, plMin, plMax; WlzPlaneDomain *iPDom; WlzVoxelValues *iVox, *rVox; iPDom = iObj->domain.p; iVox = iObj->values.vox; rVox = rValues.vox; plMin = iPDom->plane1; plMax = iPDom->lastpl; #ifdef _OPENMP #pragma omp parallel for #endif for(idP = plMin; idP <= plMax; ++idP) { WlzErrorNum errNum2D = WLZ_ERR_NONE; if(errNum == WLZ_ERR_NONE) { int idO; WlzDomain *iDom2D; WlzValues *iVal2D, *rVal2D; idO = idP - iPDom->plane1; iDom2D = iPDom->domains + idO; iVal2D = iVox->values + idO; rVal2D = rVox->values + idO; if(((*iDom2D).core != NULL) && ((*iVal2D).core != NULL) && ((*rVal2D).core != NULL)) { WlzObject *iObj2D = NULL, *rObj2D = NULL; if((iObj2D = WlzMakeMain(WLZ_2D_DOMAINOBJ, *iDom2D, *iVal2D, NULL, NULL, &errNum2D)) != NULL) { rObj2D = WlzScalarMulAdd2D(iObj2D, m, a, rGType, &errNum2D); } if(errNum2D == WLZ_ERR_NONE) { *rVal2D = WlzAssignValues(rObj2D->values, NULL); } (void )WlzFreeObj(iObj2D); (void )WlzFreeObj(rObj2D); } } if(errNum2D != WLZ_ERR_NONE) { #ifdef _OPENMP #pragma omp critical { #endif if(errNum == WLZ_ERR_NONE) { errNum = errNum2D; } #ifdef _OPENMP } #endif } } } if(errNum == WLZ_ERR_NONE) { rObj = WlzMakeMain(WLZ_3D_DOMAINOBJ, iObj->domain, rValues, iObj->plist, iObj->assoc, &errNum); } if(errNum != WLZ_ERR_NONE) { if(rObj == NULL) { (void )WlzFreeVoxelValueTb(rValues.vox); } else { (void )WlzFreeObj(rObj); rObj = NULL; } } if(dstErr != NULL) { *dstErr = errNum; } return(rObj); }
/*! * \return Compound array with vector displacements. * \ingroup WlzValueUtils * \brief Computes the 3D displacement field from the first to the * second object, where both are 3D domain objects that are * derived from the same 3D domain object with integer * incremental grey values. * \param obj0 First object. * \param obj1 Second object. * \param dstErr Destination error pointer, may be NULL. */ static WlzCompoundArray *WlzCompDispIncGrey3D(WlzObject *obj0, WlzObject *obj1, WlzErrorNum *dstErr) { int idF, idN, idO, idP, nAry, iWidth; int *ary, *valF; int *val[4]; WlzObject *objs[3], *objs2D[4]; WlzObjectType gTT; WlzPlaneDomain *domP; WlzCompoundArray *dsp = NULL; WlzIntervalWSpace iWSp[4]; WlzGreyWSpace gWSp[4]; WlzPixelV bgd; WlzValues values; WlzErrorNum errNum = WLZ_ERR_NONE; /* Create compound object for displacements from the first object. */ objs[0] = objs[1] = objs[2] = NULL; bgd.type = WLZ_GREY_INT; bgd.v.inv = 0; gTT = WlzGreyTableType(WLZ_GREY_TAB_RAGR, WLZ_GREY_INT, &errNum); for(idN = 0; idN < 3; ++idN) { values.vox = WlzNewValuesVox(obj0, gTT, bgd, &errNum); if(errNum == WLZ_ERR_NONE) { objs[idN] = WlzMakeMain(obj0->type, obj0->domain, values, NULL, NULL, &errNum); } if(errNum != WLZ_ERR_NONE) { break; } } if(errNum == WLZ_ERR_NONE) { } /* Create sorted (by grey value) array of values and positions for the * second object. */ if(errNum == WLZ_ERR_NONE) { ary = WlzCompDispMakeValAry3D(obj1, &nAry, &errNum); } if(errNum == WLZ_ERR_NONE) { qsort(ary, nAry, 4 * sizeof(int), WlzCompDispArySortFn3D); } /* Scan through the first object computing displacements. */ if(errNum == WLZ_ERR_NONE) { idF = 0; domP = obj0->domain.p; for(idP = domP->plane1; idP <= domP->lastpl; ++idP) { idO = idP - domP->plane1; objs2D[0] = objs2D[1] = objs2D[2]; objs2D[3] = WlzMakeMain(WLZ_2D_DOMAINOBJ, *(domP->domains + idO), *(obj0->values.vox->values + idO), NULL, NULL, &errNum); for(idN = 0; (errNum == WLZ_ERR_NONE) && (idN < 3); ++idN) { objs2D[idN] = WlzMakeMain(WLZ_2D_DOMAINOBJ, *(objs[idN]->domain.p->domains + idO), *(objs[idN]->values.vox->values + idO), NULL, NULL, &errNum); } for(idN = 0; (errNum == WLZ_ERR_NONE) && (idN < 4); ++idN) { errNum = WlzInitGreyScan(objs2D[idN], iWSp + idN, gWSp + idN); } while(((errNum = WlzNextGreyInterval(iWSp + 0)) == WLZ_ERR_NONE) && ((errNum = WlzNextGreyInterval(iWSp + 1)) == WLZ_ERR_NONE) && ((errNum = WlzNextGreyInterval(iWSp + 2)) == WLZ_ERR_NONE) && ((errNum = WlzNextGreyInterval(iWSp + 3)) == WLZ_ERR_NONE)) { iWidth = iWSp[0].rgtpos - iWSp[0].lftpos + 1; val[0] = gWSp[0].u_grintptr.inp; val[1] = gWSp[1].u_grintptr.inp; val[2] = gWSp[2].u_grintptr.inp; val[3] = gWSp[3].u_grintptr.inp; for(idN = 0; idN < iWidth; ++idN) { /* Find value in array. */ idF = WlzCompDispFindDsp(nAry, ary, *(val[3]), 4); if(idF >= 0) { valF = ary + (idF * 4); *(val[0]) = valF[1] - (iWSp[0].lftpos + idN); /* x */ *(val[1]) = valF[2] - iWSp[0].linpos; /* y */ *(val[2]) = valF[3] - idP; /* z */ } else { *(val[0]) = INT_MAX; *(val[1]) = INT_MAX; *(val[2]) = INT_MAX; } ++(val[0]); ++(val[1]); ++(val[2]); ++(val[3]); } } if(errNum == WLZ_ERR_EOO) { errNum = WLZ_ERR_NONE; } (void )WlzFreeObj(objs2D[0]); (void )WlzFreeObj(objs2D[1]); (void )WlzFreeObj(objs2D[2]); (void )WlzFreeObj(objs2D[3]); } } AlcFree(ary); if(errNum == WLZ_ERR_NONE) { dsp = WlzMakeCompoundArray(WLZ_COMPOUND_ARR_1, 3, 3, objs, WLZ_3D_DOMAINOBJ, &errNum); } if(errNum != WLZ_ERR_NONE) { if(dsp != NULL) { (void )WlzFreeObj((WlzObject *)dsp); } else { for(idN = 0; idN < 3; ++idN) { (void )WlzFreeObj(objs[idN]); } } } if(dstErr != NULL) { *dstErr = errNum; } return(dsp); }