/*! * \return * \brief * \param obj * \param dstErr */ static WlzObject *WlzObjToConvexPolygon3d( WlzObject *obj, WlzErrorNum *dstErr) { WlzObject *polygon=NULL, *obj1, *obj2; WlzDomain domain, *domains, *new_domains; WlzValues values; int p; WlzErrorNum errNum=WLZ_ERR_NONE; /* the object and domain have been checked therefore can create the new straight away and fill each plane appropriately */ if((domain.p = WlzMakePlaneDomain(WLZ_PLANEDOMAIN_POLYGON, obj->domain.p->plane1, obj->domain.p->lastpl, obj->domain.p->line1, obj->domain.p->lastln, obj->domain.p->kol1, obj->domain.p->lastkl, &errNum)) != NULL){ domain.p->voxel_size[0] = obj->domain.p->voxel_size[0]; domain.p->voxel_size[1] = obj->domain.p->voxel_size[1]; domain.p->voxel_size[2] = obj->domain.p->voxel_size[2]; values.core = NULL; polygon = WlzMakeMain(WLZ_3D_DOMAINOBJ, domain, values, NULL, NULL, &errNum); } if( errNum == WLZ_ERR_NONE ){ domains = obj->domain.p->domains; new_domains = domain.p->domains; values.core = NULL; for(p=obj->domain.p->plane1; p <= obj->domain.p->lastpl; p++, domains++, new_domains++){ if( (*domains).core ){ obj1 = WlzMakeMain(WLZ_2D_DOMAINOBJ, *domains, values, NULL, NULL, NULL); if((obj2 = WlzObjToConvexPolygon(obj1, &errNum)) != NULL){ *new_domains = WlzAssignDomain(obj2->domain, NULL); WlzFreeObj(obj2); } else { WlzFreeObj(polygon); polygon = NULL; break; } WlzFreeObj(obj1); } else { (*new_domains).core = NULL; } } } if( dstErr ){ *dstErr = errNum; } return polygon; }
/*! * \return New convex hull values. * \ingroup WlzConvexHull * \brief Fill in parameters of the convex hull into the values table. * \param cvh Given convex hull. * \param obj Given object. * \param dstErr Destination error pointer, may be NULL. */ static WlzConvHullValues *WlzMakeConvexHullValues3d( WlzObject *cvh, WlzObject *obj, WlzErrorNum *dstErr) { WlzValues rtnvalues, *valuess, values; WlzObject *obj1, *obj2; WlzDomain *domains1, *domains2; WlzPixelV bckgrnd; int p; WlzErrorNum errNum=WLZ_ERR_NONE; /* this is only called after a successful call to WlzObjToConvexPolygon therefore the given convex hull object and object have been checked and match */ rtnvalues.c = NULL; bckgrnd.type = WLZ_GREY_UBYTE; bckgrnd.v.ubv = 0; /* make a voxeltable and calculate the convex hull values for each plane */ if((rtnvalues.vox = WlzMakeVoxelValueTb(WLZ_VOXELVALUETABLE_CONV_HULL, cvh->domain.p->plane1, cvh->domain.p->lastpl, bckgrnd, NULL, &errNum)) != NULL){ domains1 = cvh->domain.p->domains; domains2 = obj->domain.p->domains; valuess = rtnvalues.vox->values; for(p=cvh->domain.p->plane1; p <= cvh->domain.p->lastpl; p++, domains1++, domains2++, valuess++){ if( (*domains1).core != NULL ){ values.core = NULL; obj1 = WlzMakeMain(WLZ_2D_POLYGON, *domains1, values, NULL, NULL, NULL); obj2 = WlzMakeMain(WLZ_2D_DOMAINOBJ, *domains2, values, NULL, NULL, NULL); if((values.c = WlzMakeConvexHullValues(obj1, obj2, &errNum)) != NULL){ *valuess = WlzAssignValues(values, NULL); } else { WlzFreeObj(obj2); WlzFreeObj(obj1); WlzFreeVoxelValueTb(rtnvalues.vox); rtnvalues.vox = NULL; break; } WlzFreeObj(obj2); WlzFreeObj(obj1); } } } if( dstErr ){ *dstErr = errNum; } return rtnvalues.c; }
void write_reference_object_cb( Widget w, XtPointer client_data, XtPointer call_data) { XmFileSelectionBoxCallbackStruct *cbs = (XmFileSelectionBoxCallbackStruct *) call_data; WlzEffFormat image_type = (WlzEffFormat) client_data; /* set hour glass cursor */ HGU_XmSetHourGlassCursor(globals.topl); /* write the reference object */ if( globals.origObjType == WLZ_2D_DOMAINOBJ ){ WlzObject *tmpObj; tmpObj = WlzMakeMain(WLZ_2D_DOMAINOBJ, globals.orig_obj->domain.p->domains[0], globals.orig_obj->values.vox->values[0], NULL, NULL, NULL); HGU_XmWriteExtFFObject(tmpObj, w, cbs, &image_type); WlzFreeObj(tmpObj); } else { HGU_XmWriteExtFFObject(globals.orig_obj, w, cbs, &image_type); } /* set hour glass cursor */ HGU_XmUnsetHourGlassCursor(globals.topl); return; }
/*! * \return Woolz error code. * \ingroup WlzBinaryOps * \brief For each value in the given 3D distance object the count in * the given distance histogram (array) is incremented. This * is done by forming 2D objects for each plane and calling * WlzRCCCompDistHist2D(). * \param maxDist Maximum distance value. * \param dHist Distance histogram (array). * \param dObj Given 3D distance object. */ static WlzErrorNum WlzRCCCompDistHist3D( int maxDist, int *dHist, WlzObject *dObj) { int n, p; WlzVoxelValues *pVal; WlzPlaneDomain *pDom; WlzErrorNum errNum = WLZ_ERR_NONE; pVal = dObj->values.vox; pDom = dObj->domain.p; n = pDom->lastpl - pDom->plane1 + 1; for(p = 0; p < n; ++p) { if(pDom->domains[p].core != NULL) { WlzObject *dObj2; dObj2 = WlzMakeMain(WLZ_2D_DOMAINOBJ, pDom->domains[p], pVal->values[p], NULL, NULL, &errNum); errNum = WlzRCCCompDistHist2D(maxDist, dHist, dObj2); (void )WlzFreeObj(dObj2); if(errNum != WLZ_ERR_NONE) { break; } } } return(errNum); }
/*! * \return Integer array with values and coordinates from 3D object. * \ingroup WlzValueUtils * \brief Allocates a new array (4 ints per value: 0 = value, * 1 = x coordinate, 2 = y coordinate and 3 = z coordinate. * \param obj Given object which must be a valid * 3D domain object with integer values. * \param dstNAry Destination pointer for the number of * values, must not be NULL. * \param dstErr Destination error pointer, may be NULL. */ static int *WlzCompDispMakeValAry3D(WlzObject *obj, int *dstNAry, WlzErrorNum *dstErr) { int idO, idP, nAry; int *ary, *array = NULL; WlzObject *obj2D; WlzPlaneDomain *pDom; WlzErrorNum errNum = WLZ_ERR_NONE; if((nAry = WlzVolume(obj, &errNum)) <= 0) { errNum = WLZ_ERR_DOMAIN_DATA; } if(errNum == WLZ_ERR_NONE) { if((array = AlcMalloc(nAry * 4 * sizeof(int))) == NULL) { errNum = WLZ_ERR_MEM_ALLOC; } } if(errNum == WLZ_ERR_NONE) { ary = array; pDom = obj->domain.p; for(idP = pDom->plane1; (errNum == WLZ_ERR_NONE) && (idP <= pDom->lastpl); ++idP) { idO = idP - pDom->plane1; obj2D = WlzMakeMain(WLZ_2D_DOMAINOBJ, *(obj->domain.p->domains + idO), *(obj->values.vox->values + idO), NULL, NULL, &errNum); if(errNum == WLZ_ERR_NONE) { errNum = WlzCompDispSetAry(&ary, obj2D, idP, 3); WlzFreeObj(obj2D); } } } if(errNum != WLZ_ERR_NONE) { AlcFree(ary); ary = NULL; } else { *dstNAry = nAry; if(dstErr != NULL) { *dstErr = errNum; } } return(array); }
/*! * \return Woolz error code. * \ingroup WlzBoundary * \brief decomposes a boundary into it's component polygons. * \param bndObj Given boundary. * \param dstNumObjs Destination pointer for the number of polygons. * \param dstObjArray Destination pointer for the array of polygons. */ WlzErrorNum WlzBoundaryToPolyObjArray( WlzObject *bndObj, int *dstNumObjs, WlzObject ***dstObjArray) { WlzErrorNum errNum=WLZ_ERR_NONE; WlzDomain domain; WlzValues values; WlzObject *obj, **objs; WlzPolygonDomain **polyArray; int i, numPolys; /* check inputs */ if( bndObj == NULL ){ errNum = WLZ_ERR_OBJECT_NULL; } else if((dstNumObjs == NULL) || (dstObjArray == NULL)){ errNum = WLZ_ERR_PARAM_NULL; } else { /* generate array of poly domains */ errNum = WlzBoundObjToPolyDomArray(bndObj, &numPolys, &polyArray); } /* convert to polygon objects */ if( errNum == WLZ_ERR_NONE ){ if((objs = (WlzObject **) AlcMalloc(sizeof(WlzObject *)*numPolys)) == NULL){ errNum = WLZ_ERR_MEM_ALLOC; for(i=0; i < numPolys; i++){ WlzFreePolyDmn(polyArray[i]); } AlcFree(polyArray); numPolys = 0; } else { for(i=0; i < numPolys; i++){ domain.poly = polyArray[i]; values.core = NULL; obj = WlzMakeMain(WLZ_2D_POLYGON, domain, values, NULL, NULL, &errNum); objs[i] = WlzAssignObject(obj, NULL); WlzFreePolyDmn(polyArray[i]); } AlcFree(polyArray); } } *dstNumObjs = numPolys; *dstObjArray = objs; return errNum; }
/*! * \return Woolz error code. * \ingroup WlzArithmetic * \brief Increments all values of the firstobject which are within * the domain of the second object. The domain of the first * object must cover that of the second. * Because this is a static object it is assumed that the * two 2D objects are known to be valid. * \param gObj First object. * \param dObj Second object. */ static WlzErrorNum WlzGreyIncValuesInDomain2D(WlzObject *gObj, WlzObject *dObj) { WlzObject *tObj; WlzErrorNum errNum = WLZ_ERR_NONE; tObj = WlzAssignObject( WlzMakeMain(WLZ_2D_DOMAINOBJ, dObj->domain, gObj->values, NULL, NULL, &errNum), NULL); if(errNum == WLZ_ERR_NONE) { errNum = WlzGreyIncValues2D(tObj); (void )WlzFreeObj(tObj); } return(errNum); }
/*! * \return New grey value domain object with incrementing values. * \ingroup WlzValuesUtils * \brief Creates a new 2D 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 *WlzGreyNewIncValues2D(WlzObject *in, int *val, WlzErrorNum *dstErr) { WlzObject *out = NULL; WlzObjectType gTT; 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); if(errNum == WLZ_ERR_NONE) { values.v = WlzNewValueTb(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 != NULL) { (void )WlzFreeObj(out); out = NULL; } else if(values.core != NULL) { (void )WlzFreeValues(values); } } if(dstErr != NULL) { *dstErr = errNum; } return(out); }
void read_reference_object_cb( Widget w, XtPointer client_data, XtPointer call_data) { XmFileSelectionBoxCallbackStruct *cbs = (XmFileSelectionBoxCallbackStruct *) call_data; WlzEffFormat image_type; WlzObject *obj; String icsfile; WlzDomain domain; WlzValues values; WlzObject *newObj; WlzErrorNum errNum=WLZ_ERR_NONE; /* set hour glass cursor */ HGU_XmSetHourGlassCursor(globals.topl); /* read the new reference object note the switch is to allow direct read given the image type to include the model input options */ if( client_data ){ image_type = (WlzEffFormat) client_data; if((icsfile = HGU_XmGetFileStr(globals.topl, cbs->value, cbs->dir))){ obj = WlzEffReadObj(NULL, icsfile, image_type, 0, 0, 0, &errNum); AlcFree(icsfile); } else { obj = NULL; } } else { obj = HGU_XmReadExtFFObject(read_obj_dialog, cbs, &image_type, &errNum); } if( obj == NULL){ HGU_XmUserError(globals.topl, "Read Reference Object:\n" " No reference object read - either the\n" " selected file is empty or it is not the\n" " correct object type - please check the\n" " file or make a new selection", XmDIALOG_FULL_APPLICATION_MODAL); /* set hour glass cursor */ HGU_XmUnsetHourGlassCursor(globals.topl); return; } if( obj->values.core == NULL ){ HGU_XmUserError(globals.topl, "Read Reference Object:\n" " The reference object must have a grey-\n" " value table. Please select an alternate\n" " object", XmDIALOG_FULL_APPLICATION_MODAL); WlzFreeObj( obj ); /* set hour glass cursor */ HGU_XmUnsetHourGlassCursor(globals.topl); return; } /* install the new reference object */ switch( obj->type ){ case WLZ_2D_DOMAINOBJ: if((domain.p = WlzMakePlaneDomain(WLZ_PLANEDOMAIN_DOMAIN, 0, 0, obj->domain.i->line1, obj->domain.i->lastln, obj->domain.i->kol1, obj->domain.i->lastkl, &errNum))){ domain.p->domains[0] = WlzAssignDomain(obj->domain, NULL); if((values.vox = WlzMakeVoxelValueTb(WLZ_VOXELVALUETABLE_GREY, 0, 0, WlzGetBackground(obj, NULL), NULL, &errNum))){ values.vox->values[0] = WlzAssignValues(obj->values, NULL); newObj = WlzMakeMain(WLZ_3D_DOMAINOBJ, domain, values, obj->plist, NULL, &errNum); WlzFreeObj(obj); obj = newObj; } } globals.origObjType = WLZ_2D_DOMAINOBJ; break; case WLZ_3D_DOMAINOBJ: globals.origObjType = WLZ_3D_DOMAINOBJ; break; default: HGU_XmUserError(globals.topl, "Read Reference Object:\n" " The reference object must be a 2- or 3-D\n" " grey-level image. Please select an alternate\n" " object", XmDIALOG_FULL_APPLICATION_MODAL); WlzFreeObj( obj ); /* set hour glass cursor */ HGU_XmUnsetHourGlassCursor(globals.topl); return; } globals.origObjExtType = image_type; /* set title and reference file list */ if((icsfile = HGU_XmGetFileStr(globals.topl, cbs->value, cbs->dir))) { Widget cascade; HGU_XmFileListAddFile(globals.fileList, icsfile, image_type); HGU_XmFileListWriteResourceFile(globals.fileList, globals.resourceFile); if((cascade = XtNameToWidget(globals.topl, "*file_menu*_pulldown*Recent"))){ HGU_XmFileListResetMenu(globals.fileList, cascade, referenceFileListCb); } if( XmStringGetLtoR(cbs->value, XmSTRING_DEFAULT_CHARSET, &icsfile) ) { set_topl_title(icsfile); globals.file = icsfile; } } MAPaintLogData("ReferenceFile", globals.file, 0, NULL); /* clear feedback object and install new reference object */ if( errNum == WLZ_ERR_NONE ){ if( globals.fb_obj ){ WlzFreeObj(globals.fb_obj); globals.fb_obj = NULL; } install_paint_reference_object( obj ); } /* set hour glass cursor */ HGU_XmUnsetHourGlassCursor(globals.topl); if( errNum != WLZ_ERR_NONE ){ MAPaintReportWlzError(globals.topl, "read_reference_object_cb", errNum); } return; }
int main(int argc, char **argv) { WlzObject *inObj, *outObj; WlzDomain domain; WlzValues values; FILE *inFP, *outFP, *bibFP; char *outFile, *bibFile; char optList[] = "b:f:o:hv"; int option; WlzErrorNum errNum=WLZ_ERR_NONE; BibFileRecord *bibfileRecord; BibFileError bibFileErr; int verboseFlg=0; WlzMeshTransform *meshTr = NULL; WlzFnType wlzFnType; WlzMeshGenMethod meshMthd; int basisFnPolyOrder = 3; int meshMinDst; int meshMaxDst; WlzTransformType affineType; WlzDVertex2 *srcVtxs, *dstVtxs; int relFlg=0; int numVtxs, maxNumVtxs; char *errMsg; /* additional defaults */ outFile = "-"; bibFile = NULL; numVtxs = 0; maxNumVtxs = 0; srcVtxs = NULL; dstVtxs = NULL; /* read the argument list and check for an input file */ opterr = 0; while( (option = getopt(argc, argv, optList)) != EOF ){ switch( option ){ case 'b': bibFile = optarg; break; case 'o': outFile = optarg; break; case 'h': default: usage(argv[0]); return 0; case 'v': verboseFlg = 1; break; } } /* check input file/stream */ inFP = stdin; if( optind < argc ){ if( (inFP = fopen(*(argv+optind), "rb")) == NULL ){ fprintf(stderr, "%s: can't open file %s\n", argv[0], *(argv+optind)); usage(argv[0]); return 1; } } /* check output file/stream */ if(strcmp(outFile, "-")) { if((outFP = fopen(outFile, "w")) == NULL) { errNum = WLZ_ERR_WRITE_EOF; } } else { outFP = stdout; } /* check bibfile - get warp function parameters and tie-points */ if((errNum == WLZ_ERR_NONE) && (bibFile != NULL)){ if((bibFP = fopen(bibFile, "r")) != NULL){ bibFileErr = BibFileRecordRead(&bibfileRecord, &errMsg, bibFP); while( bibFileErr == BIBFILE_ER_NONE ){ /* test for warp function and mesh parameters */ if( !strncmp(bibfileRecord->name, "WlzWarpTransformParams", 22) ){ WlzEffBibParseWarpTransformParamsRecord(bibfileRecord, &wlzFnType, &affineType, &meshMthd, &meshMinDst, &meshMaxDst); } /* test for tie points */ if( !strncmp(bibfileRecord->name, "WlzTiePointVtxs", 15) ){ int index; WlzDVertex3 dstVtx; WlzDVertex3 srcVtx; WlzEffBibParseTiePointVtxsRecord(bibfileRecord, &index, &dstVtx, &srcVtx, &relFlg); if( relFlg ){ fprintf(stderr, "%s: warning, relative vertex positions, probably an error\n", argv[0]); } if( numVtxs >= maxNumVtxs ){ maxNumVtxs += 512; srcVtxs = (WlzDVertex2 *) AlcRealloc(srcVtxs, sizeof(WlzDVertex2)*maxNumVtxs); dstVtxs = (WlzDVertex2 *) AlcRealloc(dstVtxs, sizeof(WlzDVertex2)*maxNumVtxs); } srcVtxs[numVtxs].vtX = srcVtx.vtX; srcVtxs[numVtxs].vtY = srcVtx.vtY; dstVtxs[numVtxs].vtX = dstVtx.vtX; dstVtxs[numVtxs].vtY = dstVtx.vtY; numVtxs++; } BibFileRecordFree(&bibfileRecord); bibFileErr = BibFileRecordRead(&bibfileRecord, &errMsg, bibFP); } fclose( bibFP ); if( bibFileErr == BIBFILE_ER_EOF ){ bibFileErr = BIBFILE_ER_NONE; } if( bibFileErr != BIBFILE_ER_NONE ){ fprintf(stderr, "%s: error reading bibfile\n", argv[0]); return 1; } } else { fprintf(stderr, "%s: can't open parameter bibfile %s\n", argv[0], bibFile); return 1; } } else { fprintf(stderr, "%s: warp parameters and tie-points bibfile required\n", argv[0]); usage(argv[0]); return 1; } /* read objects and section if possible */ while((errNum == WLZ_ERR_NONE) && ((inObj = WlzReadObj(inFP, &errNum)) != NULL)) { switch( inObj->type ) { case WLZ_2D_DOMAINOBJ: /* now create the mesh transform */ if((meshTr = WlzMeshTransformFromCPts(inObj, wlzFnType, basisFnPolyOrder, numVtxs, srcVtxs, numVtxs, dstVtxs, meshMthd, meshMinDst, meshMaxDst, &errNum)) != NULL){ /* write out transform and free */ domain.mt = meshTr; values.core = NULL; outObj = WlzMakeMain(WLZ_MESH_TRANS, domain, values, NULL, NULL, &errNum); WlzWriteObj(outFP, outObj); WlzFreeObj(outObj); } else { /* something wrong */ fprintf(stderr, "%s: failed to generate a mesh transform, error code: %d\n", argv[0], errNum); } break; default: fprintf(stderr, "%s: 2D input object required\n", argv[0]); usage(argv[0]); return 1; } WlzFreeObj(inObj); } if(errNum == WLZ_ERR_READ_EOF) { errNum = WLZ_ERR_NONE; } return errNum; }
/*! * \return New 3D object. * \ingroup WlzAllocation * \brief Constructs a 3D domain object from 2D domain objects read * from the given files. Each file is read in turn and added * to the 3D object. An empty plane can be specified by * setting the file string to NULL. Either all or none of * the 2D objects must have values. When the 2D objects * have values then the background value of the first 2D * object is set to be the background value of the 3D object. * \param nFileStr Number of file strings. * \param fileStr File strings. * \param plane1 The plane coordinate of the first * 2D object. * \param xSz Column voxel size. * \param ySz Line voxel size. * \param zSz Plane voxel size. * \param dstErr Destination error pointer, may be NULL. */ WlzObject *WlzConstruct3DObjFromFile(int nFileStr, char **fileStr, int plane1, float xSz, float ySz, float zSz, WlzErrorNum *dstErr) { int idx, lastpl; WlzDomain dom3D; WlzValues val3D; WlzObject *obj2D = NULL, *obj3D = NULL; WlzPixelV bgd; FILE *fP = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; dom3D.core = NULL; val3D.core = NULL; lastpl = plane1 + nFileStr - 1; if((nFileStr <= 0) || (fileStr == NULL) || (*fileStr == NULL)) { errNum = WLZ_ERR_PARAM_NULL; } else { if((fP = fopen(*fileStr, "r")) == NULL) { errNum = WLZ_ERR_READ_EOF; } else { obj2D = WlzReadObj(fP, &errNum); (void )fclose(fP); fP = NULL; } } if(errNum == WLZ_ERR_NONE) { switch(obj2D->type) { case WLZ_EMPTY_OBJ: break; case WLZ_2D_DOMAINOBJ: if(obj2D->domain.core == NULL) { errNum = WLZ_ERR_DOMAIN_NULL; } break; default: errNum = WLZ_ERR_OBJECT_TYPE; break; } } /* Make a plane domain, set column and line bounds later. */ if(errNum == WLZ_ERR_NONE) { dom3D.p = WlzMakePlaneDomain(WLZ_PLANEDOMAIN_DOMAIN, plane1, lastpl, 0, 1, 0, 1, &errNum); } if(errNum == WLZ_ERR_NONE) { dom3D.p->voxel_size[0] = xSz; dom3D.p->voxel_size[1] = ySz; dom3D.p->voxel_size[2] = zSz; } /* Make a voxel value table. */ if(errNum == WLZ_ERR_NONE) { if(obj2D->values.core) { bgd = WlzGetBackground(obj2D, &errNum); if(errNum == WLZ_ERR_NONE) { val3D.vox = WlzMakeVoxelValueTb(WLZ_VOXELVALUETABLE_GREY, plane1, lastpl, bgd, NULL, &errNum); } } } idx = 0; while((errNum == WLZ_ERR_NONE) && (idx < nFileStr)) { if(obj2D) { switch(obj2D->type) { case WLZ_EMPTY_OBJ: break; case WLZ_2D_DOMAINOBJ: if(obj2D->domain.core == NULL) { errNum = WLZ_ERR_DOMAIN_NULL; } break; default: errNum = WLZ_ERR_OBJECT_TYPE; break; } if(errNum == WLZ_ERR_NONE) { *(dom3D.p->domains + idx) = WlzAssignDomain(obj2D->domain, NULL); if(val3D.core) { if((obj2D->domain.core != NULL) && (obj2D->values.core == NULL)) { errNum = WLZ_ERR_VALUES_NULL; } else { *(val3D.vox->values + idx) = WlzAssignValues(obj2D->values, NULL); } } } WlzFreeObj(obj2D); obj2D = NULL; } if(errNum == WLZ_ERR_NONE) { ++idx; if((idx < nFileStr) && *(fileStr + idx)) { if((fP = fopen(*(fileStr + idx), "r")) == NULL) { errNum = WLZ_ERR_READ_EOF; } else { obj2D = WlzReadObj(fP, &errNum); (void )fclose(fP); fP = NULL; } } } } if(errNum == WLZ_ERR_NONE) { errNum = WlzStandardPlaneDomain(dom3D.p, val3D.vox); } if(errNum == WLZ_ERR_NONE) { obj3D = WlzMakeMain(WLZ_3D_DOMAINOBJ, dom3D, val3D, NULL, NULL, &errNum); } if(errNum != WLZ_ERR_NONE) { if(dom3D.core) { (void )WlzFreeDomain(dom3D); } if(val3D.core) { (void )WlzFreeValues(val3D); } } if(dstErr) { *dstErr = errNum; } return(obj3D); }
/*! * \return New 3D object. * \ingroup WlzAllocation * \brief Constructs a 3D domain object from 2D domain objects. Each * 2D object is assigned in turn to the 3D object no domains * or values are copied. An empty can be specified by * setting the 2D object to NULL. Either all or none of * the 2D objects must have values. When the 2D objects * have values then the background value of the first 2D * object is set to be the background value of the 3D object. * \param nObjs Number of objects. * \param objs The 2D objects, the first of which * MUST not be NULL. * \param plane1 The plane coordinate of the first * 2D object. * \param xSz Column voxel size. * \param ySz Line voxel size. * \param zSz Plane voxel size. * \param dstErr Destination error pointer, may be NULL. */ WlzObject *WlzConstruct3DObjFromObj(int nObjs, WlzObject **objs, int plane1, float xSz, float ySz, float zSz, WlzErrorNum *dstErr) { int idx, lastpl; WlzDomain dom3D; WlzValues val3D; WlzObject *obj2D, *obj3D = NULL; WlzPixelV bgd; WlzErrorNum errNum = WLZ_ERR_NONE; dom3D.core = NULL; val3D.core = NULL; lastpl = plane1 + nObjs - 1; if((nObjs <= 0) || (objs == NULL)) { errNum = WLZ_ERR_PARAM_NULL; } else if((obj2D = *objs) == NULL) { errNum = WLZ_ERR_OBJECT_NULL; } else { switch(obj2D->type) { case WLZ_EMPTY_OBJ: break; case WLZ_2D_DOMAINOBJ: if(obj2D->domain.core == NULL) { errNum = WLZ_ERR_DOMAIN_NULL; } break; default: errNum = WLZ_ERR_OBJECT_TYPE; break; } } /* Make a plane domain, set column and line bounds later. */ if(errNum == WLZ_ERR_NONE) { dom3D.p = WlzMakePlaneDomain(WLZ_PLANEDOMAIN_DOMAIN, plane1, lastpl, 0, 1, 0, 1, &errNum); } if(errNum == WLZ_ERR_NONE) { dom3D.p->voxel_size[0] = xSz; dom3D.p->voxel_size[1] = ySz; dom3D.p->voxel_size[2] = zSz; } /* Make a voxel value table. */ if(errNum == WLZ_ERR_NONE) { if(obj2D->values.core) { bgd = WlzGetBackground(obj2D, &errNum); if(errNum == WLZ_ERR_NONE) { val3D.vox = WlzMakeVoxelValueTb(WLZ_VOXELVALUETABLE_GREY, plane1, lastpl, bgd, NULL, &errNum); } } } for(idx = 0; (errNum == WLZ_ERR_NONE) && (idx < nObjs); ++idx) { obj2D = *(objs + idx); if(obj2D) { switch(obj2D->type) { case WLZ_EMPTY_OBJ: break; case WLZ_2D_DOMAINOBJ: if(obj2D->domain.core == NULL) { errNum = WLZ_ERR_DOMAIN_NULL; } break; default: errNum = WLZ_ERR_OBJECT_TYPE; break; } if(errNum == WLZ_ERR_NONE) { *(dom3D.p->domains + idx) = WlzAssignDomain(obj2D->domain, NULL); if(val3D.core) { if((obj2D->domain.core != NULL) && (obj2D->values.core == NULL)) { errNum = WLZ_ERR_VALUES_NULL; } else { *(val3D.vox->values + idx) = WlzAssignValues(obj2D->values, NULL); } } } } } if(errNum == WLZ_ERR_NONE) { errNum = WlzStandardPlaneDomain(dom3D.p, val3D.vox); } if(errNum == WLZ_ERR_NONE) { obj3D = WlzMakeMain(WLZ_3D_DOMAINOBJ, dom3D, val3D, NULL, NULL, &errNum); } if(errNum != WLZ_ERR_NONE) { if(dom3D.core) { (void )WlzFreeDomain(dom3D); } if(val3D.core) { (void )WlzFreeValues(val3D); } } if(dstErr) { *dstErr = errNum; } return(obj3D); }
/*! * \ingroup WlzValuesFilters * \brief Set new grey-range by simple linear interpolation. It assumes that the min and max values enclose the true range of grey-values in the object. Failure to check this could result in a segmentation fault. The transform function is: \f[g' = \frac{(gMax - gMin)}{(gmax - gmin)} (g - gmin) + gMin + \delta \f] Here \f$\delta\f$ is the dither value. * * \return Woolz error number * \param obj Input grey-level object whose values are to be reset. * \param min Initial minimum value * \param max Initial maximum value * \param Min Final minimum value * \param Max Final maximum value * \param Dither values if destination range is greater than source range * and this flag is non-zero. * \par Source: * WlzGreySetRange.c */ WlzErrorNum WlzGreySetRange( WlzObject *obj, WlzPixelV min, WlzPixelV max, WlzPixelV Min, WlzPixelV Max, int dither) { double gMin = 0.0, gMax = 0.0, sigma = 0.0, factor, val; WlzIntervalWSpace iwsp; WlzGreyWSpace gwsp; WlzGreyP gptr; WlzObject *tempobj; WlzValues *values; WlzDomain *domains; int i, j, nplanes; WlzErrorNum errNum = WLZ_ERR_NONE; /* check object */ if( obj == NULL ){ errNum = WLZ_ERR_OBJECT_NULL; } if( errNum == WLZ_ERR_NONE ){ switch( obj->type ){ case WLZ_2D_DOMAINOBJ: if( obj->domain.i == NULL ){ return WLZ_ERR_DOMAIN_NULL; } if( obj->values.core == NULL ){ return WLZ_ERR_VALUES_NULL; } if( WlzGreyTableIsTiled(obj->values.core->type) ){ return WLZ_ERR_VALUES_TYPE; } break; case WLZ_3D_DOMAINOBJ: /* check planedomain and voxeltable */ if( obj->domain.p == NULL ){ return WLZ_ERR_DOMAIN_NULL; } if( obj->domain.p->type != WLZ_PLANEDOMAIN_DOMAIN ){ return WLZ_ERR_PLANEDOMAIN_TYPE; } if( obj->values.vox == NULL ){ return WLZ_ERR_VALUES_NULL; } if( obj->values.vox->type != WLZ_VOXELVALUETABLE_GREY ){ return WLZ_ERR_VOXELVALUES_TYPE; } /* set range of each plane if non-empty - indicated by NULL */ domains = obj->domain.p->domains; values = obj->values.vox->values; nplanes = obj->domain.p->lastpl - obj->domain.p->plane1 + 1; for(i=0; i < nplanes; i++, domains++, values++){ if( (*domains).core == NULL || (*values).core == NULL ){ continue; } tempobj = WlzMakeMain(WLZ_2D_DOMAINOBJ, *domains, *values, NULL, NULL, &errNum); if((tempobj == NULL) && (errNum == WLZ_ERR_NONE) ){ errNum = WLZ_ERR_UNSPECIFIED; break; } errNum = WlzGreySetRange(tempobj, min, max, Min, Max, dither); WlzFreeObj( tempobj ); if( errNum != WLZ_ERR_NONE ){ break; } } return errNum; case WLZ_TRANS_OBJ: return WlzGreySetRange(obj->values.obj, min, max, Min, Max, dither); case WLZ_EMPTY_OBJ: return errNum; default: errNum = WLZ_ERR_OBJECT_TYPE; break; } } if( errNum == WLZ_ERR_NONE ){ /* get conversion function - should use LUT use 4 LUTS for rgb type since bounded */ if( WlzGreyTypeFromObj(obj, &errNum) == WLZ_GREY_RGBA ){ WlzUByte rgbaLut[4][256]; WlzUInt rgbamin[4], rgbaMin[4]; double rgbaFactor[4], val1; WlzUInt red, green, blue, alpha; rgbamin[0] = WLZ_RGBA_RED_GET(min.v.rgbv); rgbaMin[0] = WLZ_RGBA_RED_GET(Min.v.rgbv); if(WLZ_RGBA_RED_GET(max.v.rgbv) > WLZ_RGBA_RED_GET(min.v.rgbv)){ rgbaFactor[0] = (((double) WLZ_RGBA_RED_GET(Max.v.rgbv) - WLZ_RGBA_RED_GET(Min.v.rgbv))/ (WLZ_RGBA_RED_GET(max.v.rgbv) - WLZ_RGBA_RED_GET(min.v.rgbv))); } else { rgbaFactor[0] = 0.0; } rgbamin[1] = WLZ_RGBA_GREEN_GET(min.v.rgbv); rgbaMin[1] = WLZ_RGBA_GREEN_GET(Min.v.rgbv); if(WLZ_RGBA_GREEN_GET(max.v.rgbv) > WLZ_RGBA_GREEN_GET(min.v.rgbv)){ rgbaFactor[1] = (((double) WLZ_RGBA_GREEN_GET(Max.v.rgbv) - WLZ_RGBA_GREEN_GET(Min.v.rgbv))/ (WLZ_RGBA_GREEN_GET(max.v.rgbv) - WLZ_RGBA_GREEN_GET(min.v.rgbv))); } else { rgbaFactor[1] = 0.0; } rgbamin[2] = WLZ_RGBA_BLUE_GET(min.v.rgbv); rgbaMin[2] = WLZ_RGBA_BLUE_GET(Min.v.rgbv); if(WLZ_RGBA_BLUE_GET(max.v.rgbv) > WLZ_RGBA_BLUE_GET(min.v.rgbv)){ rgbaFactor[2] = (((double) WLZ_RGBA_BLUE_GET(Max.v.rgbv) - WLZ_RGBA_BLUE_GET(Min.v.rgbv))/ (WLZ_RGBA_BLUE_GET(max.v.rgbv) - WLZ_RGBA_BLUE_GET(min.v.rgbv))); } else { rgbaFactor[2] = 0.0; } rgbamin[3] = WLZ_RGBA_ALPHA_GET(min.v.rgbv); rgbaMin[3] = WLZ_RGBA_ALPHA_GET(Min.v.rgbv); if(WLZ_RGBA_ALPHA_GET(max.v.rgbv) > WLZ_RGBA_ALPHA_GET(min.v.rgbv)){ rgbaFactor[3] = (((double) WLZ_RGBA_ALPHA_GET(Max.v.rgbv) - WLZ_RGBA_ALPHA_GET(Min.v.rgbv))/ (WLZ_RGBA_ALPHA_GET(max.v.rgbv) - WLZ_RGBA_ALPHA_GET(min.v.rgbv))); } else { rgbaFactor[3] = 0.0; } /* now set up the LUTS */ for(i=0; i < 4; i++){ for(j=0; j < 256; j++){ val1 = rgbaFactor[i] * (j - rgbamin[i]) + rgbaMin[i]; rgbaLut[i][j] = (WlzUByte )WLZ_CLAMP(val1, 0, 255); } } /* set values - can assume rgba grey-type */ errNum = WlzInitGreyScan(obj, &iwsp, &gwsp); if(errNum == WLZ_ERR_NONE) { while( WlzNextGreyInterval(&iwsp) == WLZ_ERR_NONE ){ gptr = gwsp.u_grintptr; for (i=0; i<iwsp.colrmn; i++, gptr.rgbp++){ red = rgbaLut[0][WLZ_RGBA_RED_GET(*gptr.rgbp)]; green = rgbaLut[0][WLZ_RGBA_GREEN_GET(*gptr.rgbp)]; blue = rgbaLut[0][WLZ_RGBA_BLUE_GET(*gptr.rgbp)]; alpha = rgbaLut[0][WLZ_RGBA_ALPHA_GET(*gptr.rgbp)]; WLZ_RGBA_RGBA_SET(*gptr.rgbp, red, green, blue, alpha); } } (void )WlzEndGreyScan(&iwsp, &gwsp); if(errNum == WLZ_ERR_EOO){ errNum = WLZ_ERR_NONE; } } } else { WlzValueConvertPixel(&min, min, WLZ_GREY_DOUBLE); WlzValueConvertPixel(&max, max, WLZ_GREY_DOUBLE); WlzValueConvertPixel(&Min, Min, WLZ_GREY_DOUBLE); WlzValueConvertPixel(&Max, Max, WLZ_GREY_DOUBLE); if( fabs(max.v.dbv - min.v.dbv) < DBL_EPSILON ){ return WLZ_ERR_FLOAT_DATA; } errNum = WlzInitGreyScan(obj, &iwsp, &gwsp); if(errNum == WLZ_ERR_NONE) { factor = (Max.v.dbv - Min.v.dbv) / (max.v.dbv - min.v.dbv); if(dither) { if(fabs(factor) < 1.0 + DBL_EPSILON) { dither = 0; } else { sigma = fabs(2.0 / factor); AlgRandSeed(101); switch(gwsp.pixeltype) { case WLZ_GREY_INT: gMin = ALG_MAX(Min.v.dbv, INT_MIN); gMax = ALG_MIN(Max.v.dbv, INT_MAX); break; case WLZ_GREY_SHORT: gMin = ALG_MAX(Min.v.dbv, SHRT_MIN); gMax = ALG_MIN(Max.v.dbv, SHRT_MAX); break; case WLZ_GREY_UBYTE: gMin = ALG_MAX(Min.v.dbv, 0); gMax = ALG_MIN(Max.v.dbv, 255); break; case WLZ_GREY_FLOAT: gMin = ALG_MAX(Min.v.dbv, FLT_MIN); gMax = ALG_MIN(Max.v.dbv, FLT_MAX); break; case WLZ_GREY_DOUBLE: gMin = Min.v.dbv; gMax = Max.v.dbv; break; default: break; } } } while((errNum == WLZ_ERR_NONE) && ((errNum = WlzNextGreyInterval(&iwsp)) == WLZ_ERR_NONE)){ gptr = gwsp.u_grintptr; switch (gwsp.pixeltype) { case WLZ_GREY_INT: for (i=0; i<iwsp.colrmn; i++, gptr.inp++){ if(dither){ val = factor * (*gptr.inp + AlgRandZigNormal(0.0, sigma) - min.v.dbv) + Min.v.dbv; val = WLZ_CLAMP(val, gMin, gMax); } else { val = factor * (*gptr.inp - min.v.dbv) + Min.v.dbv; } *gptr.inp = WLZ_NINT(val); } break; case WLZ_GREY_SHORT: for (i=0; i<iwsp.colrmn; i++, gptr.shp++){ if(dither){ val = factor * (*gptr.shp + AlgRandZigNormal(0.0, sigma) - min.v.dbv) + Min.v.dbv; val = WLZ_CLAMP(val, gMin, gMax); } else { val = factor * (*gptr.shp - min.v.dbv) + Min.v.dbv; } *gptr.shp = (short )WLZ_NINT(val); } break; case WLZ_GREY_UBYTE: for (i=0; i<iwsp.colrmn; i++, gptr.ubp++){ if(dither){ val = factor * (*gptr.ubp + AlgRandZigNormal(0.0, sigma) - min.v.dbv) + Min.v.dbv; val = WLZ_CLAMP(val, gMin, gMax); } else { val = factor * (*gptr.ubp - min.v.dbv) + Min.v.dbv; } *gptr.ubp = (WlzUByte )WLZ_NINT(val); } break; case WLZ_GREY_FLOAT: for (i=0; i<iwsp.colrmn; i++, gptr.flp++){ if(dither){ val = factor * (*gptr.flp + AlgRandZigNormal(0.0, sigma) - min.v.dbv) + Min.v.dbv; val = WLZ_CLAMP(val, gMin, gMax); } else { val = factor * (*gptr.flp - min.v.dbv) + Min.v.dbv; } *gptr.flp = (float )val; } break; case WLZ_GREY_DOUBLE: for (i=0; i<iwsp.colrmn; i++, gptr.dbp++){ if(dither){ val = factor * (*gptr.dbp + AlgRandZigNormal(0.0, sigma) - min.v.dbv) + Min.v.dbv; val = WLZ_CLAMP(val, gMin, gMax); } else { val = factor * (*gptr.dbp - min.v.dbv) + Min.v.dbv; } *gptr.dbp = val; } break; default: errNum = WLZ_ERR_GREY_TYPE; break; } } (void )WlzEndGreyScan(&iwsp, &gwsp); if(errNum == WLZ_ERR_EOO){ errNum = WLZ_ERR_NONE; } } } } return errNum; }
/*! * \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); }
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 RCC classification of the given objects, ie object 0 is a * returned classification of object 1. * \ingroup WlzBinaryOps * \brief The given pair of spatial domain objects are classified * using a RCC with optional enclosure and offset classifications. * * For an explanation of RCC8 classifications * see the type definition ::WlzRCCClass and the paper: * D.A. Randell, etal, * "Discrete Mereotopology for Spatial Reasoning in * Automated Histological Image Analysis", PAMI 35(3) 2013. * The RCC8 has been extended to include both tangential and * non-tangential surrounds. * * The RCC classification is performed using simple combinations * of the Woolz union, intersection, exclusive or, dilation, * fill and convex hull operators on an ordered pair of * spatial domains(\f$\Omega_0\f$ and \f$\Omega_1\f$): * \f{eqnarray*}{ C_0 &\leftarrow& \Omega_0 \cap \Omega_1 \neq \emptyset \\ C_1 &\leftarrow& \Omega_0^+ \cap \Omega_1 \neq \emptyset \\ C_2 &\leftarrow& (\Omega_0 \oplus \Omega_1) \neq \emptyset \\ C_3 &\leftarrow& (\Omega_0 \cup \Omega_1) \oplus \Omega_1 \neq \emptyset \\ C_4 &\leftarrow& (\Omega_0^+ \cup \Omega_1) \oplus \Omega_1 \neq \emptyset \\ C_5 &\leftarrow& (\Omega_0 \cup \Omega_1) \oplus \Omega_0 \neq \emptyset \\ C_6 &\leftarrow& (\Omega_0 \cup \Omega_1^+) \oplus \Omega_0 \neq \emptyset \\ C_7 &\leftarrow& (\Omega_0^{\bullet} \cup \Omega_1) \oplus \Omega_0^{\bullet} \neq \emptyset \\ C_8 &\leftarrow& (\Omega_0 \cup \Omega_1^{\bullet}) \oplus \Omega_1^{\bullet} \neq \emptyset \\ C_9 &\leftarrow& 2|\Omega_0 \cap \Omega_1^{\circ}| \ge |\Omega_0| \\ C_{10} &\leftarrow& 2|\Omega_0^{\circ} \cap \Omega_1| \ge |\Omega_1| \f} * where * are the \f$\cup\f$, \f$\cap\f$ and \f$\oplus\f$ * are the set union (logical or), intersection (logical and) * and xor (logical exclusive or) operators; * \f$\Omega^+\f$ indicates the dilation of \f$\Omega\f$, * \f$\Omega^{\circ}\f$ the convex hull of \f$\Omega\f$, * \f$\Omega^{\bullet}\f$ indicates \f$\Omega\f$ filled and * \f$|\Omega|\f$ the cardinality (area or volume) of * \f$\Omega\f$. * The decision tree for the classification excluding * enclosure and offset is: * \f[ C_0 \left\{ \begin{array}{ll} 0 & C_1 \left\{ \begin{array}{ll} 0 & C_7 \left\{ \begin{array}{ll} 0 & NTSURI \\ & \\ & \\ 1 & C_8 \left\{ \begin{array}{ll} 0 & NTSUR \\ & \\ 1 & DC \end{array} \right. \\ \end{array} \right. \\ & \\ & \\ 1 & C_7 \left\{ \begin{array}{ll} 0 & TSURI \\ & \\ & \\ 1 & C_8 \left\{ \begin{array}{ll} 0 & TSUR \\ & \\ 1 & EC \end{array} \right. \\ \end{array} \right. \\ \end{array} \right. \\ & \\ & \\ 1 & C_2 \left\{ \begin{array}{ll} 0 & EQ \\ & \\ & \\ 1 & C_3 \left\{ \begin{array}{ll} 0 & C_4 \left\{ \begin{array}{ll} 0 & NTPP \\ & \\ 1 & TPP \end{array} \right. \\ & \\ & \\ 1 & C_5 \left\{ \begin{array}{ll} 0 & C_6 \left\{ \begin{array}{ll} 0 & NTPPI \\ & \\ 1 & TPPI \end{array} \right. \\ & \\ 1 & PO \end{array} \right. \\ \end{array} \right. \\ \end{array} \right. \\ \end{array} \right. \f] * The statistics are computed for each classification * as below: * <table width="500" border="0"> <caption> Basic Morphological Operations for the RCC Spatial Relationships </caption> <tr> <td>RCC</td> <td>Normalised Volume</td> </tr> <tr> <td>\f$EMPTY(\Omega_0,\Omega_1)\f$</td> <td>0.0</td> </tr> <tr> <td>\f$DC(\Omega_0,\Omega_1)\f$</td> <td>0.0</td> </tr> <tr> <td>\f$EC(\Omega_0,\Omega_1)\f$</td> <td>0.0</td> </tr> <tr> <td>\f$EQ(\Omega_0,\Omega_1)\f$</td> <td>1.0</td> </tr> <tr> <td>\f$PO(\Omega_0,\Omega_1)\f$</td> <td>\f$|\Omega_0 \cap \Omega_1|/ |\Omega_0 \cup \Omega_1|\f$</td> </tr> <tr> <td>\f$TPP(\Omega_0,\Omega_1)\f$</td> <td>\f$|\Omega_0|/|\Omega_0 \cup \Omega_1|\f$</td> </tr> <tr> <td>\f$NTPP(\Omega_0,\Omega_1)\f$</td> <td>\f$|\Omega_0|/|\Omega_0 \cup \Omega_1|\f$</td> </tr> <tr> <td>\f$TPPI(\Omega_0,\Omega_1)\f$</td> <td>\f$|\Omega_1|/|\Omega_0 \cup \Omega_1|\f$</td> </tr> <tr> <td>\f$NTPPI(\Omega_0,\Omega_1)\f$</td> <td>\f$|\Omega_1|/|\Omega_0 \cup \Omega_1|\f$</td> </tr> <tr> <td>\f$TSUR(\Omega_0,\Omega_1)\f$</td> <td>\f$|\Omega_0|/|\Omega_0 \cup \Omega_1|\f$</td> </tr> <tr> <td>\f$TSURI(\Omega_0,\Omega_1)\f$</td> <td>\f$|\Omega_1|/|\Omega_0 \cup \Omega_1|\f$</td> </tr> <tr> <td>\f$NTSUR(\Omega_0,\Omega_1)\f$</td> <td>\f$|\Omega_0|/|\Omega_0 \cup \Omega_1|\f$</td> </tr> <tr> <td>\f$NTSURI(\Omega_0,\Omega_1)\f$</td> <td>\f$|\Omega_1|/|\Omega_0 \cup \Omega_1|\f$</td> </tr> <tr> <td>\f$ENC(\Omega_0,\Omega_1)\f$</td> <td>\f$|\Omega_0 \cap \Omega_1^{\circ}|/|\Omega_0|\f$</td> </tr> <tr> <td>\f$ENCI(\Omega_0,\Omega_1)\f$</td> <td>\f$|\Omega_0^{\circ} \cap \Omega_1|/|\Omega_1|\f$</td> </tr> <tr> <td>\f$OST(\Omega_0,\Omega_1)\f$</td> <td>\f$q_1/(q_1 + q_2 - q_0)\f$</td> </tr> </table> * * Many of the objects that are computed during the * classification are done so using a lazy evaluation with the * functions WlzRCCMakeC() and WlzRCCMakeT(). * * Enclosure and offset are somwhat more expensive to compute * than the other classifications, for this reason and because * they are not strictly part of a RCC they can be avoided by * setting the noEnc or noOst flags. * * Enclosure will be computed if the noEnc has not been set and * the classification is not one of WLZ_RCC_EQ, WLZ_RCC_TSUR, * WLZ_RCC_TSURI, WLZ_RCC_NTSUR or WLZ_RCC_NTSURI. * Enclosure is computed using: * \f[ |\Omega_0 \cap \Omega_1^{\circ}|/|\Omega_0| \f] * for \f$\Omega_0\f$ to be encloded by \f$\Omega_1\f$ then * at least half of \f$\Omega_0\f$ must intersect the convex * hull of \f$\Omega_1\f$. * * Offset will be computed if the noOst parameter has not * been set and the classification is not WLZ_RCC_EQ. * Offset is computed within a restricted domain in which * all pixels/voxels are equidistant for the domains of the * given objects: \f[ \Omega_e = (\Omega_0 \cup \Omega_1)^\circ \cap \Omega_0^{+d_{max}} \cap \Omega_1^{+d_{max}} \cap \Omega(D(\Omega_0) = D(\Omega_1)) \f] * where \f$D(\Omega)\f$ is the distance transform of the domain * \f$\Omega\f$. Within \f$\Omega_e\f$ the first, second and * third quantiles (\f$q_0\f$, \f$q_1\f$ and \f$q_2\f$) of * the distances \f$D(\Omega_0)\f$ (or equivalently * \f$D(\Omega_1)\f$) are computed. The ratio of the median * to the median plus interquartile range is then computed * and the domains are classified as offset if this ratio * is greater than or equal to one half: * \f[ \frac{q_1}{q_1 + q_2 - q_0} \geq 0.5 \f] * \param obj0 First given spatial domain object. * \param obj1 Second given spatial domain object. * \param noEnc Don't include enclosure if non-zero. * \param noOst Don't include offset if non-zero. * \param maxOstDist Maximum distance for offset, not * used if noOst is non-zero. * \param dstStatCnt Destination pointer for the number * of elements returned in the array of * statistics (see above), may be NULL. * Ignored if dstStatAry is NULL. * \param dstStatAry Destination pointer for an array of * statistics (see above), may be NULL. * If an array is returned it should be * freed using AlcFree(). * \param dstErr Destination error pointer, may be NULL. */ WlzRCCClass WlzRegConCalcRCC( WlzObject *obj0, WlzObject *obj1, int noEnc, int noOst, int maxOstDist, int *dstStatCnt, double **dstStatAry, WlzErrorNum *dstErr) { int i; WlzLong i01 = 0, /* |\Omega_0 \cap \Omega_1| */ u01 = 0; /* |\Omega_0 \cup \Omega_1| */ WlzLong u[2] = {0}, /* |\Omega_i|, i \in 0 \cdots 1 */ v[2] = {0}; /* |c_9|, |c_{10}| */ WlzObject *c[11] = {NULL}, /* c_i, i \in 0 \cdots 10 */ *o[2] = {NULL}, /* \Omega_i, i \in 0 \cdots 1 */ *t[WLZ_RCCTOIDX_CNT] = {NULL}; /* Temporary object as in the enum WlzRCCTOIdx. */ double stats[WLZ_RCCIDX_CNT] = {0.0}; /* Classification statistics. */ WlzValues nullValues; WlzRCCClass cls = WLZ_RCC_EMPTY; /* Classification mask. */ WlzErrorNum errNum = WLZ_ERR_NONE; nullValues.core = NULL; /* Compute classification using the decision tree. */ if((obj0 == NULL) || (obj1 == NULL) || (WlzIsEmpty(obj0, NULL) != 0) || (WlzIsEmpty(obj1, NULL) != 0)) { cls = WLZ_RCC_EMPTY; } else if((obj0->domain.core == NULL) || (obj1->domain.core == NULL)) { errNum = WLZ_ERR_DOMAIN_NULL; } else if((obj0->type != obj1->type) || ((obj0->type != WLZ_2D_DOMAINOBJ) && (obj0->type != WLZ_3D_DOMAINOBJ))) { errNum = WLZ_ERR_OBJECT_TYPE; } else if(((o[0] = WlzAssignObject( WlzMakeMain(obj0->type, obj0->domain, nullValues, NULL, NULL, &errNum), NULL)) != NULL) && ((o[1] = WlzAssignObject( WlzMakeMain(obj1->type, obj1->domain, nullValues, NULL, NULL, &errNum), NULL)) != NULL)) { errNum = WlzRCCMakeC(o, c, t, 0); } if(errNum == WLZ_ERR_NONE) { if(WlzIsEmpty(c[0], NULL)) { errNum = WlzRCCMakeC(o, c, t, 1); if(errNum == WLZ_ERR_NONE) { if(WlzIsEmpty(c[1], NULL)) { errNum = WlzRCCMakeC(o, c, t, 7); if(errNum == WLZ_ERR_NONE) { if(WlzIsEmpty(c[7], NULL)) { cls = WLZ_RCC_NTSURI; } else { errNum = WlzRCCMakeC(o, c, t, 8); if(errNum == WLZ_ERR_NONE) { if(WlzIsEmpty(c[8], NULL)) { cls = WLZ_RCC_NTSUR; } else { cls = WLZ_RCC_DC; } } } } } else { errNum = WlzRCCMakeC(o, c, t, 7); if(errNum == WLZ_ERR_NONE) { if(WlzIsEmpty(c[7], NULL)) { cls = WLZ_RCC_TSURI; } else { errNum = WlzRCCMakeC(o, c, t, 8); if(errNum == WLZ_ERR_NONE) { if(WlzIsEmpty(c[8], NULL)) { cls = WLZ_RCC_TSUR; } else { cls = WLZ_RCC_EC; } } } } } } } else { errNum = WlzRCCMakeC(o, c, t, 2); if(errNum == WLZ_ERR_NONE) { if(WlzIsEmpty(c[2], NULL)) { cls = WLZ_RCC_EQ; } else { errNum = WlzRCCMakeC(o, c, t, 3); if(errNum == WLZ_ERR_NONE) { if(WlzIsEmpty(c[3], NULL)) { errNum = WlzRCCMakeC(o, c, t, 4); if(errNum == WLZ_ERR_NONE) { if(WlzIsEmpty(c[4], NULL)) { cls = WLZ_RCC_NTPP; } else { cls = WLZ_RCC_TPP; } } } else { errNum = WlzRCCMakeC(o, c, t, 5); if(errNum == WLZ_ERR_NONE) { if(WlzIsEmpty(c[5], NULL)) { errNum = WlzRCCMakeC(o, c, t, 6); if(errNum == WLZ_ERR_NONE) { if(WlzIsEmpty(c[6], NULL)) { cls = WLZ_RCC_NTPPI; } else { cls = WLZ_RCC_TPPI; } } } else { cls = WLZ_RCC_PO; } } } } } } } } /* If enclosure is required check for it and add to classification mask. */ if((errNum == WLZ_ERR_NONE) && (noEnc == 0) && ((cls & (WLZ_RCC_EQ | WLZ_RCC_TSUR | WLZ_RCC_TSURI | WLZ_RCC_NTSUR | WLZ_RCC_NTSURI)) == 0)) { for(i = 0; i <= 1; ++i) { errNum = WlzRCCMakeT(o, t, (i == 0)? WLZ_RCCTOIDX_O0O1CI: WLZ_RCCTOIDX_O0CO1I); if(errNum == WLZ_ERR_NONE) { u[i] = WlzVolume(o[i], &errNum); } if(errNum == WLZ_ERR_NONE) { v[i] = WlzVolume(t[(i == 0)? WLZ_RCCTOIDX_O0O1CI: WLZ_RCCTOIDX_O0CO1I], &errNum); if((errNum == WLZ_ERR_NONE) && (v[i] < 0)) { errNum = WLZ_ERR_DOMAIN_DATA; } } if(errNum != WLZ_ERR_NONE) { break; } } if(errNum == WLZ_ERR_NONE) { if((2 * v[0]) >= u[0]) { cls |= WLZ_RCC_ENC; } if((2 * v[1]) >= u[1]) { cls |= WLZ_RCC_ENCI; } } } /* Compute the maximum normalized volume for the classification(s) in the * classification mask. */ if((errNum == WLZ_ERR_NONE) && (dstStatAry != NULL)) { int i, m; for(i = 0; i < WLZ_RCCIDX_CNT; ++i) { m = 1<<i; if(m & cls) { double s = 0.0; switch(m) { case WLZ_RCC_EQ: s = 1.0; break; case WLZ_RCC_PO: /* |\Omega_0 \cap \Omega_1| / |\Omega_0 \cup \Omega_1| = * u_0 / u_01 */ if(i01 <= 0) { errNum = WlzRCCMakeT(o, t, WLZ_RCCTOIDX_O0O1I); if(errNum == WLZ_ERR_NONE) { i01 = WlzVolume(t[WLZ_RCCTOIDX_O0O1I], &errNum); } } if((errNum == WLZ_ERR_NONE) && (u01 <= 0)) { errNum = WlzRCCMakeT(o, t, WLZ_RCCTOIDX_O0O1U); if(errNum == WLZ_ERR_NONE) { u01 = WlzVolume(t[WLZ_RCCTOIDX_O0O1U], &errNum); } } if(errNum == WLZ_ERR_NONE) { s = (double )(i01) / (double )u01; } break; case WLZ_RCC_TSUR: /* FALLTHROUGH */ case WLZ_RCC_NTSUR: /* FALLTHROUGH */ case WLZ_RCC_TPP: /* FALLTHROUGH */ case WLZ_RCC_NTPP: /* |\Omega_0| / |\Omega_0 \cup \Omega_1| = * u_0 / u_01 */ if(u[0] <= 0) { u[0] = WlzVolume(o[0], &errNum); } if(errNum == WLZ_ERR_NONE) { if(u01 <= 0) { errNum = WlzRCCMakeT(o, t, WLZ_RCCTOIDX_O0O1U); if(errNum == WLZ_ERR_NONE) { u01 = WlzVolume(t[WLZ_RCCTOIDX_O0O1U], &errNum); } } } if(errNum == WLZ_ERR_NONE) { s = (double )(u[0]) / (double )u01; } break; case WLZ_RCC_TSURI: /* FALLTHROUGH */ case WLZ_RCC_NTSURI: /* FALLTHROUGH */ case WLZ_RCC_TPPI: /* FALLTHROUGH */ case WLZ_RCC_NTPPI: /* |\Omega_1| / |\Omega_0 \cup \Omega_1| = * u_1 / u_01 */ if(u[1] <= 0) { u[1] = WlzVolume(o[1], &errNum); } if(errNum == WLZ_ERR_NONE) { if(u01 <= 0) { errNum = WlzRCCMakeT(o, t, WLZ_RCCTOIDX_O0O1U); if(errNum == WLZ_ERR_NONE) { u01 = WlzVolume(t[WLZ_RCCTOIDX_O0O1U], &errNum); } } } if(errNum == WLZ_ERR_NONE) { s = (double )(u[1]) / (double )u01; } break; case WLZ_RCC_ENC: /* |\Omega_0 \cup \Omega_1^{\circ}|/|\Omega_0| = * v_0 / u_0 */ if(u[1] >= 0) { s = (double )(v[0]) / (double )(u[0]); } break; case WLZ_RCC_ENCI: /* |\Omega_0^{\circ} \cup \Omega_1|/|\Omega_1| = * v_1 / u_1 */ if(v[1] >= 0) { s = (double )(v[1]) / (double )(u[1]); } break; default: break; } if(errNum == WLZ_ERR_NONE) { stats[i] = s; } } } } /* If offset is required check for it and add to both the classification * mask and statistics. */ if((errNum == WLZ_ERR_NONE) && (noOst == 0) && ((cls & WLZ_RCC_EQ) == 0)) { int ostQ[3]; errNum = WlzRCCOffset(o, t, maxOstDist, &(ostQ[0]), &(ostQ[1]), &(ostQ[2])); if(errNum == WLZ_ERR_NONE) { #ifdef WLZ_RCC_DEBUG_OST (void )fprintf(stderr, "WLZ_RCC_DEBUG_OST %d %d %d\n", ostQ[0], ostQ[1], ostQ[2]); #endif if((ostQ[1] > 0) && (ostQ[1] < maxOstDist) && (ostQ[2] >= ostQ[0])) { const double eps = 1.0e-06; if(ostQ[2] > ostQ[0]) { stats[WLZ_RCCIDX_OST] = (double )ostQ[1] / (double )(ostQ[2] + ostQ[1] - ostQ[0]); } else { stats[WLZ_RCCIDX_OST] = 1.0; } if(stats[WLZ_RCCIDX_OST] > (0.5 - eps)) { cls |= WLZ_RCC_OST; } } } } /* Free objects. */ for(i = 0; i < WLZ_RCCTOIDX_CNT; ++i) { (void )WlzFreeObj(t[i]); } for(i = 0; i <= 8; ++i) { (void )WlzFreeObj(c[i]); } for(i = 0; i < 2; ++i) { (void )WlzFreeObj(o[i]); } if((errNum == WLZ_ERR_NONE) && (dstStatAry != NULL)) { if((*dstStatAry = (double *) AlcMalloc(sizeof(double) * WLZ_RCCIDX_CNT)) == NULL) { errNum = WLZ_ERR_MEM_ALLOC; } else { (void )memcpy(*dstStatAry, stats, sizeof(double) * WLZ_RCCIDX_CNT); if(dstStatCnt) { *dstStatCnt = WLZ_RCCIDX_CNT; } } } if(dstErr) { *dstErr = errNum; } return(cls); }
/*! * \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 setup_ref_display_list_cb( Widget w, XtPointer client_data, XtPointer call_data) { WlzPlaneDomain *planedmn; WlzThreeDViewStruct *viewStr; WlzErrorNum errNum=WLZ_ERR_NONE; if( globals.obj == NULL || globals.obj->type != WLZ_3D_DOMAINOBJ || globals.ref_display_list == 0){ return; } /* create a new display list */ glDeleteLists(globals.ref_display_list, 1); globals.ref_display_list = glGenLists( (GLsizei) 1 ); planedmn = globals.obj->domain.p; /* create the reference object 3D display DisplayList */ glNewList( globals.ref_display_list, GL_COMPILE ); glBegin(GL_LINES); glColor3f(1.0, 0.0, 0.0); glIndexi( (int) HGU_XGetColorPixel(globals.dpy, globals.cmap, 1.0, 0.0, 0.0) ); glVertex3i(planedmn->kol1, planedmn->line1, planedmn->plane1); glVertex3i(planedmn->lastkl, planedmn->line1, planedmn->plane1); glVertex3i(planedmn->kol1, planedmn->line1, planedmn->lastpl); glVertex3i(planedmn->lastkl, planedmn->line1, planedmn->lastpl); glVertex3i(planedmn->kol1, planedmn->lastln, planedmn->plane1); glVertex3i(planedmn->lastkl, planedmn->lastln, planedmn->plane1); glVertex3i(planedmn->kol1, planedmn->lastln, planedmn->lastpl); glVertex3i(planedmn->lastkl, planedmn->lastln, planedmn->lastpl); glColor3f(0.0, 1.0, 0.0); glIndexi( (int) HGU_XGetColorPixel(globals.dpy, globals.cmap, 0.0, 1.0, 0.0) ); glVertex3i(planedmn->kol1, planedmn->line1, planedmn->plane1); glVertex3i(planedmn->kol1, planedmn->lastln, planedmn->plane1); glVertex3i(planedmn->kol1, planedmn->line1, planedmn->lastpl); glVertex3i(planedmn->kol1, planedmn->lastln, planedmn->lastpl); glVertex3i(planedmn->lastkl, planedmn->line1, planedmn->plane1); glVertex3i(planedmn->lastkl, planedmn->lastln, planedmn->plane1); glVertex3i(planedmn->lastkl, planedmn->line1, planedmn->lastpl); glVertex3i(planedmn->lastkl, planedmn->lastln, planedmn->lastpl); glColor3f(0.0, 0.0, 1.0); glIndexi( (int) HGU_XGetColorPixel(globals.dpy, globals.cmap, 0.0, 0.0, 1.0) ); glVertex3i(planedmn->kol1, planedmn->line1, planedmn->plane1); glVertex3i(planedmn->kol1, planedmn->line1, planedmn->lastpl); glVertex3i(planedmn->kol1, planedmn->lastln, planedmn->plane1); glVertex3i(planedmn->kol1, planedmn->lastln, planedmn->lastpl); glVertex3i(planedmn->lastkl, planedmn->line1, planedmn->plane1); glVertex3i(planedmn->lastkl, planedmn->line1, planedmn->lastpl); glVertex3i(planedmn->lastkl, planedmn->lastln, planedmn->plane1); glVertex3i(planedmn->lastkl, planedmn->lastln, planedmn->lastpl); glEnd(); if( globals.fb_obj && (globals.fb_obj->type == WLZ_3D_DOMAINOBJ) ){ WlzObject *obj1, *boundobj; WlzValues values; int z, step; /* establish the step size */ planedmn = globals.fb_obj->domain.p; step = (planedmn->lastkl - planedmn->kol1) / 6; z = (planedmn->lastln - planedmn->line1) / 6; step = WLZ_MIN(step, z); z = (planedmn->lastpl - planedmn->plane1) / 6; step = WLZ_MIN(step, z); /* set up const z boundaries */ glColor3f(1.0, 1.0, 1.0); glIndexi((int) HGU_XGetColorPixel(globals.dpy, globals.cmap, 1.0, 1.0, 1.0)); (void) glLineWidth( (GLfloat) 2.0 ); for(z=planedmn->plane1+step/2; z <= planedmn->lastpl; z += step) { values.core = NULL; obj1 = WlzMakeMain(WLZ_2D_DOMAINOBJ, planedmn->domains[z - planedmn->plane1], values, NULL, NULL, NULL); if( obj1->domain.core != NULL ) { boundobj = WlzObjToBoundary(obj1, 1, &errNum); if( boundobj != NULL ) { MAOpenGLDisplayBoundList(boundobj->domain.b, (float) z ); WlzFreeObj( boundobj ); } if( errNum != WLZ_ERR_NONE ){ break; } } WlzFreeObj( obj1 ); } (void) glLineWidth( (GLfloat) 1.0 ); /* set up const y boundaries */ if( errNum == WLZ_ERR_NONE ){ glColor3f(1.0, 1.0, 0.0); glIndexi((int) HGU_XGetColorPixel(globals.dpy, globals.cmap, 1.0, 1.0, 0.0)); if((viewStr = WlzMake3DViewStruct(WLZ_3D_VIEW_STRUCT, &errNum))){ viewStr->theta = WLZ_M_PI / 2.0; viewStr->phi = WLZ_M_PI / 2.0; viewStr->dist = planedmn->line1 - step/2; errNum = WlzInit3DViewStruct(viewStr, globals.fb_obj); for(z=viewStr->dist+step; (errNum == WLZ_ERR_NONE) && (z <= planedmn->lastln); z += step) { Wlz3DSectionIncrementDistance(viewStr, (double) step); if((obj1 = WlzGetSectionFromObject(globals.fb_obj, viewStr, WLZ_INTERPOLATION_NEAREST, &errNum))){ obj1 = WlzAssignObject(obj1, NULL); boundobj = WlzObjToBoundary(obj1, 1, &errNum); if( boundobj != NULL ) { /* convert boundary coordinates to voxel coordinates */ Wlz3DSectionTransformYBound(boundobj->domain.b, viewStr); MAOpenGLDisplayYBoundList(boundobj->domain.b, (float) z); WlzFreeObj( boundobj ); } WlzFreeObj( obj1 ); } } WlzFree3DViewStruct(viewStr); } } /* set up const x boundaries */ if( errNum == WLZ_ERR_NONE ){ glIndexi((int) HGU_XGetColorPixel(globals.dpy, globals.cmap, 1.0, 1.0, 0.0)); glColor3f(1.0, 1.0, 0.0); if((viewStr = WlzMake3DViewStruct(WLZ_3D_VIEW_STRUCT, &errNum))){ viewStr->theta = 0.0; viewStr->phi = WLZ_M_PI / 2.0; viewStr->dist = planedmn->kol1 - step/2; errNum = WlzInit3DViewStruct(viewStr, globals.fb_obj); for(z=viewStr->dist+step; (errNum == WLZ_ERR_NONE) && (z <= planedmn->lastkl); z += step) { Wlz3DSectionIncrementDistance(viewStr, (double) step); if((obj1 = WlzGetSectionFromObject(globals.fb_obj, viewStr, WLZ_INTERPOLATION_NEAREST, &errNum))){ obj1 = WlzAssignObject(obj1, NULL); boundobj = WlzObjToBoundary(obj1, 1, &errNum); if( boundobj != NULL ) { /* convert boundary coordinates to voxel coordinates */ Wlz3DSectionTransformXBound(boundobj->domain.b, viewStr); MAOpenGLDisplayXBoundList(boundobj->domain.b, (float) z); WlzFreeObj( boundobj ); } WlzFreeObj( obj1 ); } } WlzFree3DViewStruct(viewStr); } } } if( errNum == WLZ_ERR_NONE ){ glIndexi( (int) HGU_XGetColorPixel(globals.dpy, globals.cmap, 1.0, 1.0, 1.0) ); glColor3f(1.0, 1.0, 1.0); glEndList(); WLZ_VTX_3_SET(globals.bbox_vtx, planedmn->kol1 - 2.0, planedmn->line1 - 2.0, planedmn->plane1 - 2.0); WLZ_VTX_3_SET(globals.bbox_size, planedmn->lastkl - planedmn->kol1 + 4.0, planedmn->lastln - planedmn->line1 + 4.0, planedmn->lastpl - planedmn->plane1 + 4.0); glFogf(GL_FOG_DENSITY, 0.25/globals.bbox_size.vtX); MAOpenGLDrawScene( globals.canvas ); } else { MAPaintReportWlzError(globals.topl, "setup_ref_display_list_cb", errNum); } return; }
void referenceFileListCb( Widget w, XtPointer client_data, XtPointer call_data) { HGU_XmFileListCallbackStruct *cbs= (HGU_XmFileListCallbackStruct *) client_data; WlzObject *obj; Widget cascade; WlzErrorNum errNum=WLZ_ERR_NONE; HGU_XmSetHourGlassCursor(globals.topl); if( cbs == NULL ){ /* clear list selection */ HGU_XmFileListClearList(globals.fileList); HGU_XmFileListWriteResourceFile(globals.fileList, globals.resourceFile); if((cascade = XtNameToWidget(globals.topl, "*file_menu*_pulldown*Recent"))){ HGU_XmFileListResetMenu(globals.fileList, cascade, referenceFileListCb); } } else if((obj = HGU_XmFileListReadObject(w, cbs, &errNum))){ WlzDomain domain; WlzValues values; WlzObject *newObj; switch( obj->type ){ case WLZ_2D_DOMAINOBJ: if((domain.p = WlzMakePlaneDomain(WLZ_PLANEDOMAIN_DOMAIN, 0, 0, obj->domain.i->line1, obj->domain.i->lastln, obj->domain.i->kol1, obj->domain.i->lastkl, &errNum))){ domain.p->domains[0] = WlzAssignDomain(obj->domain, NULL); if((values.vox = WlzMakeVoxelValueTb(WLZ_VOXELVALUETABLE_GREY, 0, 0, WlzGetBackground(obj, NULL), NULL, &errNum))){ values.vox->values[0] = WlzAssignValues(obj->values, NULL); newObj = WlzMakeMain(WLZ_3D_DOMAINOBJ, domain, values, NULL, NULL, NULL); WlzFreeObj(obj); obj = newObj; } } globals.origObjType = WLZ_2D_DOMAINOBJ; break; case WLZ_3D_DOMAINOBJ: globals.origObjType = WLZ_3D_DOMAINOBJ; break; default: HGU_XmUserError(globals.topl, "Read Reference Object:\n" " The reference object must be a 2- or 3-D\n" " grey-level image. Please select an alternate\n" " object", XmDIALOG_FULL_APPLICATION_MODAL); WlzFreeObj( obj ); /* set hour glass cursor */ HGU_XmUnsetHourGlassCursor(globals.topl); return; } MAPaintLogData("ReferenceFile", refFileList[0], 0, NULL); install_paint_reference_object( obj ); /* set the title of the top-level window */ set_topl_title(cbs->file); globals.file = AlcStrDup(cbs->file); globals.origObjExtType = cbs->format; /* add to the file list and write file */ HGU_XmFileListAddFile(globals.fileList, cbs->file, cbs->format); HGU_XmFileListWriteResourceFile(globals.fileList, globals.resourceFile); if((cascade = XtNameToWidget(globals.topl, "*file_menu*_pulldown*Recent"))){ HGU_XmFileListResetMenu(globals.fileList, cascade, referenceFileListCb); } } HGU_XmUnsetHourGlassCursor(globals.topl); if( errNum != WLZ_ERR_NONE ){ MAPaintReportWlzError(globals.topl, "ReferenceFileListCb", errNum); } return; }
void file_menu_init( Widget topl) { Widget rc, form, toggle; Visual *visual; Arg arg[1]; char fileStr[128]; FILE *fp; WlzEffFormat image_type=WLZEFF_FORMAT_WLZ; /* set the top-level title */ set_topl_title(NULL); /* get the visual explicitly */ visual = HGU_XmWidgetToVisual(topl); XtSetArg(arg[0], XmNvisual, visual); /* create the read-model file selection dialog */ read_model_dialog = XmCreateFileSelectionDialog(topl, "read_model_dialog", arg, 1); XtAddCallback(read_model_dialog, XmNokCallback, read_reference_object_cb, (XtPointer) WLZEFF_FORMAT_WLZ); XtAddCallback(read_model_dialog, XmNokCallback, PopdownCallback, NULL); XtAddCallback( read_model_dialog, XmNcancelCallback, PopdownCallback, NULL); XtAddCallback(read_model_dialog, XmNmapCallback, FSBPopupCallback, NULL); XtManageChild( read_model_dialog ); /* create the read-obj file selection dialog */ read_obj_dialog = HGU_XmCreateExtFFObjectFSB(topl, "read_obj_dialog", read_reference_object_cb, NULL); if((rc = XtNameToWidget(read_obj_dialog, "*.formatFormRC"))){ /* add a form to include file type and fill-blanks toggle */ form = XtVaCreateManagedWidget("read_file_form", xmFormWidgetClass, rc, XmNborderWidth, 0, NULL); /* add a fill-blanks toggles */ toggle = XtVaCreateManagedWidget("fill_blanks", xmToggleButtonGadgetClass, form, XmNindicatorOn, True, XmNindicatorType, XmN_OF_MANY, XmNset, False, XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, NULL); toggle = XtVaCreateManagedWidget("min_domain", xmToggleButtonGadgetClass, form, XmNindicatorOn, True, XmNindicatorType, XmN_OF_MANY, XmNset, True, XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, toggle, NULL); } HGU_XmExtFFObjectFSBSetType(read_obj_dialog, WLZEFF_FORMAT_WLZ); XtManageChild( read_obj_dialog ); /* add to the save restore list */ HGU_XmSaveRestoreAddWidget( read_obj_dialog, HGU_XmFSD_SaveFunc, (XtPointer) XtName(topl), NULL, NULL ); /* create the write-obj file selection dialog */ write_obj_dialog = HGU_XmCreateExtFFObjectFSB(topl, "write_obj_dialog", write_reference_object_cb, NULL); HGU_XmExtFFObjectFSBSetType(write_obj_dialog, WLZEFF_FORMAT_WLZ); /* initialise the reference file list pulldown */ if( !globals.sectViewFlg ){ Widget cascade; if((cascade = XtNameToWidget(globals.topl, "*file_menu*_pulldown*Recent"))){ globals.resourceFile = (String) AlcMalloc(sizeof(char) * (strlen(getenv("HOME")) + 16)); sprintf(globals.resourceFile, "%s/%s", getenv("HOME"), ".maRecentFiles"); globals.fileList = HGU_XmFileListCreateList(globals.resourceFile, NULL); HGU_XmFileListResetMenu(globals.fileList, cascade, referenceFileListCb); } } /* add to the save restore list */ HGU_XmSaveRestoreAddWidget( write_obj_dialog, HGU_XmFSD_SaveFunc, (XtPointer) XtName(topl), NULL, NULL ); /* create the object properties dialog */ obj_props_dialog_init( topl ); globals.file = NULL; globals.obj = NULL; globals.orig_obj = NULL; globals.fb_obj = NULL; globals.origObjExtType = image_type; /* setup the theiler directory and menu item - check for stage */ XtGetApplicationResources(topl, &globals, set_att_res, XtNumber(set_att_res), NULL, 0); /* check the logfile */ if( globals.logfile ){ if( (globals.logfileFp = fopen(globals.logfile, "w")) == NULL ){ fprintf(stderr, "MAPaint: something wrong with the logfile %s\n" "Please check name and permissions\n" "Logging not enabled\n\007", globals.logfile); } else { char timeBuf[16]; struct hostent *hstnt; fprintf(stderr, "MAPaint: logging enabled to %s\n", globals.logfile); MAPaintLogData("Filename", globals.logfile, 0, NULL); MAPaintLogData("User", getenv("USER"), 0, NULL); #if defined (LINUX2) || defined (DARWIN) strcpy(timeBuf, "00/00/00"); #else tmpTime = time(NULL); cftime(timeBuf, "%d/%m/%Y", &tmpTime); #endif /* LINUX2 */ MAPaintLogData("Date", timeBuf, 0, NULL); #if defined (LINUX2) || defined (DARWIN) strcpy(timeBuf, "00.00"); #else cftime(timeBuf, "%H.%M", &tmpTime); #endif /* LINUX2 */ MAPaintLogData("Time", timeBuf, 0, NULL); hstnt = gethostbyname(getenv("HOST")); MAPaintLogData("Host", getenv("HOST"), 0, NULL); MAPaintLogData("Hostname", hstnt->h_name, 0, NULL); } } else { globals.logfileFp = NULL; } /* check base directory - if the string has come from the resources then we need to duplicate it to allow it to be freed possibly some memory leakage here */ /* note: only non-NULL if set by the user, if NULL then attempt to find the cdrom or copied data */ if( globals.base_theiler_dir ){ globals.base_theiler_dir = AlcStrDup( globals.base_theiler_dir ); } /* else { FILE *pp;*/ /* search for the Theiler mode directory as per the CDROM should search local disc first */ /*#if defined (LINUX2) if((pp = popen("find /mnt -maxdepth 4 -name Models", "r"))){ while( fscanf(pp, "%s", fileStr) != EOF ){ if( strstr(fileStr, "Models") ){ globals.base_theiler_dir = AlcStrDup(fileStr); break; } } pclose(pp); } #elif defined (DARWIN) if( (pp = popen("find /Volumes -maxdepth 4 -name Models", "r")) ){ while( fscanf(pp, "%s", fileStr) != EOF ){ if( strstr(fileStr, "Models") ){ globals.base_theiler_dir = AlcStrDup(fileStr); break; } } pclose(pp); } #elif defined (SUNOS4) || defined (SUNOS5) if( pp = popen("find /cdrom -maxdepth 4 -name Models", "r") ){ while( fscanf(pp, "%s", fileStr) != EOF ){ if( strstr(fileStr, "Models") ){ globals.base_theiler_dir = AlcStrDup(fileStr); break; } } pclose(pp); } #else globals.base_theiler_dir = NULL; #endif }*/ if( globals.theiler_stage ){ char *tStr; if((tStr = theilerString(globals.theiler_stage))){ globals.theiler_stage = AlcStrDup(tStr); } else { globals.theiler_stage = NULL; } } theiler_menu_init( topl ); /* check for an initial reference file else check Theiler stage */ if( initial_reference_file != NULL ){ WlzObject *obj; /* open the reference object file and install */ if( (fp = fopen(initial_reference_file, "r")) ){ HGU_XmSetHourGlassCursor(topl); if((obj = WlzReadObj( fp, NULL ))){ WlzDomain domain; WlzValues values; WlzObject *newObj; switch( obj->type ){ case WLZ_2D_DOMAINOBJ: domain.p = WlzMakePlaneDomain(WLZ_PLANEDOMAIN_DOMAIN, 0, 0, obj->domain.i->line1, obj->domain.i->lastln, obj->domain.i->kol1, obj->domain.i->lastkl, NULL); domain.p->domains[0] = WlzAssignDomain(obj->domain, NULL); values.vox = WlzMakeVoxelValueTb(WLZ_VOXELVALUETABLE_GREY, 0, 0, WlzGetBackground(obj, NULL), NULL, NULL); values.vox->values[0] = WlzAssignValues(obj->values, NULL); newObj = WlzMakeMain(WLZ_3D_DOMAINOBJ, domain, values, NULL, NULL, NULL); WlzFreeObj(obj); obj = newObj; globals.origObjType = WLZ_2D_DOMAINOBJ; break; case WLZ_3D_DOMAINOBJ: globals.origObjType = WLZ_3D_DOMAINOBJ; break; default: HGU_XmUserError(globals.topl, "Read Reference Object:\n" " The reference object must be a 2- or 3-D woolz\n" " grey-level image. Please select an alternate\n" " object", XmDIALOG_FULL_APPLICATION_MODAL); WlzFreeObj( obj ); /* set hour glass cursor */ HGU_XmUnsetHourGlassCursor(globals.topl); return; } HGU_XmFileListAddFile(globals.fileList, initial_reference_file, image_type); HGU_XmFileListWriteResourceFile(globals.fileList, globals.resourceFile); MAPaintLogData("ReferenceFile", initial_reference_file, 0, NULL); install_paint_reference_object( obj ); /* set the globals reference file */ globals.file = initial_reference_file; /* set the title of the top-level window */ set_topl_title(globals.file); } else { /* if it fails to read, check the file name or the file content for special options */ if( strstr(initial_reference_file, "MAPaint") ){ /* standard MAPaint startup */ globals.app_name = "MAPaint"; /* set the title of the top-level window */ set_topl_title(globals.file); } else if( strstr(initial_reference_file, "SectionView") ){ /* restricted section view startup */ globals.app_name = "SectionView"; globals.sectViewFlg = 1; /* set the title of the top-level window */ set_topl_title(globals.file); } else if( theilerString(initial_reference_file) ){ /* load in theiler stage anatomy etc. */ globals.app_name = "SectionView"; globals.sectViewFlg = 1; set_theiler_stage_cb(topl, theilerString(initial_reference_file), NULL); } else { char strBuf[33]; /* check the content */ rewind(fp); fscanf(fp, "%32s", strBuf); if( strstr(strBuf, "MAPaint") ){ /* standard MAPaint startup */ globals.app_name = "MAPaint"; /* set the title of the top-level window */ set_topl_title(globals.file); } else if( strstr(strBuf, "SectionView") ){ /* restricted section view startup */ globals.app_name = "SectionView"; globals.sectViewFlg = 1; /* set the title of the top-level window */ set_topl_title(globals.file); } else if( theilerString(strBuf) ){ /* load in theiler stage anatomy etc. */ globals.app_name = "SectionView"; globals.sectViewFlg = 1; set_theiler_stage_cb(topl, theilerString(strBuf), NULL); } } /* set the globals reference file */ globals.file = NULL; } (void) fclose( fp ); HGU_XmUnsetHourGlassCursor(topl); } } else if( globals.theiler_stage ){ globals.app_name = "MAPaint"; set_theiler_stage_cb(topl, theilerString(globals.theiler_stage), NULL); } /* reset the colormap */ if( globals.sectViewFlg == 1 ){ init_paint_cmapstruct(globals.topl); } /* check for an initial domain file */ if( initial_domain_file != NULL ){ WlzObject *obj; /* open the domain object file and put it in as a 3D feedback option */ if( (fp = fopen(initial_domain_file, "rb")) ){ HGU_XmSetHourGlassCursor(topl); if((obj = WlzReadObj( fp, NULL ))){ if( globals.fb_obj ){ WlzFreeObj(globals.fb_obj); } globals.fb_obj = WlzAssignObject(obj, NULL); setup_ref_display_list_cb(read_obj_dialog, NULL, NULL); } (void) fclose( fp ); HGU_XmUnsetHourGlassCursor(topl); } } return; }
/*! * \return Object read from file. * \ingroup WlzExtFF * \brief Reads a Woolz object from the given stream using the * GRUMMP vmesh tetrahedral mesh file format. * \param fP Input file stream. * \param dstErr Destination error number ptr, may be * NULL. */ WlzObject *WlzEffReadObjEMT(FILE *fP, WlzErrorNum *dstErr) { int nElm = 0, nNod = 0; char *str; WlzDVertex3 *vBuf = NULL; WlzCMesh3D *mesh = NULL; WlzObject *obj = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; char cBuf[256]; if(fP == NULL) { errNum = WLZ_ERR_PARAM_NULL; } else { int idC = 0; /* Optional records may be comments starting with a '#' but apart * from these the first line should have a single integer specifying * the number of nodes. */ do { if((str = WlzEffReadObjEMTRec(fP, cBuf, 256)) == NULL) { errNum = WLZ_ERR_READ_INCOMPLETE; } else if(*str != '#') { if((idC = sscanf(str, "%d", &nNod)) != 1) { errNum = WLZ_ERR_READ_INCOMPLETE; } } } while((errNum == WLZ_ERR_NONE) && (idC != 1)); } /* Check for reasonable number of nodes. */ if(errNum == WLZ_ERR_NONE) { if(nNod <= 0) { errNum = WLZ_ERR_READ_INCOMPLETE; } } /* Create a new 3D constrained mesh. */ if(errNum == WLZ_ERR_NONE) { mesh = WlzCMeshNew3D(&errNum); } /* Read in the node positions into a temporary buffer computing their * bounding box and then create the nodes. */ if(errNum == WLZ_ERR_NONE) { if((vBuf = AlcMalloc(sizeof(WlzDVertex3) * nNod)) == NULL) { errNum = WLZ_ERR_MEM_ALLOC; } } if(errNum == WLZ_ERR_NONE) { int idN = 0; while(idN < nNod) { if((str = WlzEffReadObjEMTRec(fP, cBuf, 256)) == NULL) { errNum = WLZ_ERR_READ_INCOMPLETE; break; } else if(*str != '#') { if(sscanf(str, "%lg %lg %lg", &(vBuf[idN].vtX), &(vBuf[idN].vtY), &(vBuf[idN].vtZ)) != 3) { errNum = WLZ_ERR_READ_INCOMPLETE; break; } ++idN; } } } if(errNum == WLZ_ERR_NONE) { mesh->bBox = WlzBoundingBoxVtx3D(nNod, vBuf, NULL); if(AlcVectorExtendAndGet(mesh->res.nod.vec, nNod) == NULL) { errNum = WLZ_ERR_MEM_ALLOC; } } if(errNum == WLZ_ERR_NONE) { errNum = WlzCMeshReassignGridCells3D(mesh, nNod); } if(errNum == WLZ_ERR_NONE) { int idN; for(idN = 0; idN < nNod; ++idN) { (void )WlzCMeshNewNod3D(mesh, vBuf[idN], NULL); } } AlcFree(vBuf); /* Read the number of elements in the mesh. */ if(errNum == WLZ_ERR_NONE) { int idC = 0; /* Optional records may be comments starting with a '#' but apart * from these the first line should have a single integer specifying * the number of nodes. */ do { if((str = WlzEffReadObjEMTRec(fP, cBuf, 256)) == NULL) { errNum = WLZ_ERR_READ_INCOMPLETE; } else if(*str != '#') { if((idC = sscanf(str, "%d", &nElm)) != 1) { errNum = WLZ_ERR_READ_INCOMPLETE; } } } while((errNum == WLZ_ERR_NONE) && (idC != 1)); } /* Check for reasonable number of elements. */ if(errNum == WLZ_ERR_NONE) { if(nElm <= 0) { errNum = WLZ_ERR_READ_INCOMPLETE; } } /* Allocate room for the elements in the mesh. */ if(errNum == WLZ_ERR_NONE) { if(AlcVectorExtendAndGet(mesh->res.elm.vec, nElm) == NULL) { errNum = WLZ_ERR_MEM_ALLOC; } } /* Read the elements adding them to the mesh. */ if(errNum == WLZ_ERR_NONE) { int idE = 0; while(idE < nElm) { if((str = WlzEffReadObjEMTRec(fP, cBuf, 256)) == NULL) { errNum = WLZ_ERR_READ_INCOMPLETE; break; } else if(*str != '#') { int dummyI; int nIdx[4]; WlzCMeshNod3D *nBuf[4]; if((sscanf(str, "%d %d %d %d %d", &dummyI, &(nIdx[0]), &(nIdx[1]), &(nIdx[2]), &(nIdx[3])) != 5) || (nIdx[0] <= 0) || (nIdx[0] > nNod) || (nIdx[1] <= 0) || (nIdx[1] > nNod) || (nIdx[2] <= 0) || (nIdx[2] > nNod) || (nIdx[3] <= 0) || (nIdx[3] > nNod)) { errNum = WLZ_ERR_READ_INCOMPLETE; break; } else { int idN; for(idN = 0; idN < 4; ++idN) { nBuf[idN] = (WlzCMeshNod3D *) AlcVectorItemGet(mesh->res.nod.vec, nIdx[idN] - 1); } (void )WlzCMeshNewElm3D(mesh, nBuf[0], nBuf[1], nBuf[3], nBuf[2], 1, &errNum); if(errNum != WLZ_ERR_NONE) { break; } ++idE; } } } } /* Ignore the boundary faces in the file, they're redundant information. */ if(errNum == WLZ_ERR_NONE) { WlzDomain dom; WlzValues val; val.core = NULL; dom.cm3 = mesh; WlzCMeshDelUnusedNodes3D(mesh); WlzCMeshUpdateMaxSqEdgLen3D(mesh); obj = WlzMakeMain(WLZ_CMESH_3D, dom, val, NULL, NULL, &errNum); } if(errNum != WLZ_ERR_NONE) { (void )WlzCMeshFree3D(mesh); } if(dstErr) { *dstErr = errNum; } return(obj); }
/*! * \ingroup WlzBinaryOps * \brief Segment a domain into connected parts. Connectivity is defined by the connect parameter and can be 4- or 8-connected for 2D objects and 6-, 18- or 26-connected for 3D objects. Note this version requires that there is sufficient space in the objects array defined by maxNumObjs and this is not extended. This should be changed in future so that the array is extended as required. * * \return Error number. * \param obj input object to be segmented * \param mm number of objects return * \param dstArrayObjs object array return, allocated in the procedure. * \param maxNumObjs maximum number of object to return (determines the size of the array) * \param ignlns ignore objects with num lines <= ignlns * \param connect connectivity to determine connected regions * \par Source: * WlzLabel.c */ WlzErrorNum WlzLabel( WlzObject *obj, int *mm, WlzObject ***dstArrayObjs, int maxNumObjs, int ignlns, WlzConnectType connect) { WlzIntervalDomain *jdp; WlzRagRValues *jvp; WlzIntervalWSpace iwsp; WlzInterval *itvl; WlzInterval *jtvl; WlzLAllocBuf *crntal, *altemp; WlzLAllocBuf *al, *crntst, *crlast; WlzLAllocBuf *precal, *precst, *prlast; int nints, mkl; int maxinline,nob,line,ended,lend,chainlistsize; WlzLLink *freechain, *alprec, *alloc, *link1, *link2; int lftcrn, lftprc, rtcrn, rtprec; int jrtcrn, mxkl, jl, jr; int oll, ofl, jjj; WlzDomain domain; WlzValues values; int jdqt; WlzErrorNum errNum=WLZ_ERR_NONE; WlzObject **objlist; /* see HISTORY for comments from the FORTRAN version */ /* now we allocate space for the objects */ if( (objlist = (WlzObject **)AlcMalloc(sizeof(WlzObject *) * maxNumObjs)) == NULL ){ errNum = WLZ_ERR_MEM_ALLOC; } else { *dstArrayObjs = objlist; } /* check object note *mm is always set to zero on error return because the "Too many objects" error can return nobj valid objects therefore if *mm != 0 there are valid objects in objlist which must be freed */ if( obj == NULL ){ *mm = 0; return WLZ_ERR_OBJECT_NULL; } /* check types */ switch( obj->type ){ case WLZ_2D_DOMAINOBJ: if( obj->domain.core == NULL ){ *mm = 0; return WLZ_ERR_DOMAIN_NULL; } switch( obj->domain.core->type ){ case WLZ_INTERVALDOMAIN_INTVL: break; case WLZ_INTERVALDOMAIN_RECT: if( (obj->domain.i->lastln - obj->domain.i->line1) < ignlns ){ *mm = 0; return( WLZ_ERR_NONE ); } if( maxNumObjs < 1 ){ *mm = 0; return( WLZ_ERR_INT_DATA ); } objlist[0] = WlzAssignObject( WlzMakeMain(obj->type, obj->domain, obj->values, NULL, NULL, &errNum), NULL); *mm = 1; return WLZ_ERR_NONE; default: *mm = 0; return WLZ_ERR_DOMAIN_TYPE; } break; case WLZ_3D_DOMAINOBJ: if( obj->domain.core == NULL ){ *mm = 0; return WLZ_ERR_DOMAIN_NULL; } return WlzLabel3d(obj, mm, objlist, maxNumObjs, ignlns, connect); case WLZ_EMPTY_OBJ: *mm = 0; return WLZ_ERR_NONE; case WLZ_TRANS_OBJ: /* should be able to do this quite easily */ default: *mm = 0; return WLZ_ERR_OBJECT_TYPE; } /* check connectivity parameter */ switch( connect ){ default: return WLZ_ERR_INT_DATA; case WLZ_8_CONNECTED: jdqt = 0; break; case WLZ_4_CONNECTED: jdqt = 1; break; } /* * Allocate and initialise the working spaces. * * The chain store size is not easily predictable. * Here we use ((twice the number of lines) plus (one third the * number of intervals)), very much smaller than the * previous allocation, but apparently adequate to * segment both metaphase spreads and single connected objects. * In any case, this version of label can if necessary allocate * more space when the original chain store fills up. */ chainlistsize = mintcount(obj->domain.i, &maxinline) / 3; chainlistsize += (1 + obj->domain.i->lastln - obj->domain.i->line1)*2; if( (freechain = chainalloc(0,chainlistsize)) == NULL ){ *mm = 0; return WLZ_ERR_MEM_ALLOC; } buckle(freechain, freechain, chainlistsize); /* * The allocation list size is determined as twice * (1 + maximum number of intervals in a line of input object). */ if( (al = (WlzLAllocBuf *) AlcCalloc(2 * (maxinline+1), sizeof(WlzLAllocBuf))) == NULL ){ chainFree(); *mm = 0; return WLZ_ERR_MEM_ALLOC; } jvp = obj->values.v; /* initialise interval scanning */ WlzInitRasterScan (obj, &iwsp, WLZ_RASTERDIR_ILIC); line = iwsp.linpos; ended = 0 ; lend = 0 ; WlzNextInterval(&iwsp); crntst = al + 1 + maxinline; nob = 0 ; prlast = al ; precst = al + 1 ; /* commence new line */ while (!ended) { crntal = crntst-1 ; line++; if (lend) { ended = 1 ; lend = 0 ; } else { /* read the intervals into the allocation buffer */ while (iwsp.linpos <= line) { crntal++; crntal->a_int.ileft = iwsp.lftpos; crntal->a_int.iright = iwsp.rgtpos; if (WlzNextInterval(&iwsp) != 0) { lend = 1 ; break ; } } } crlast = crntal ; crntal = crntst ; precal = precst ; alloc = NULL ; /* test whether last interval in current line dealt with */ while (crntal <= crlast) { lftcrn = crntal->a_int.ileft ; jrtcrn = crntal->a_int.iright ; rtcrn = jrtcrn+2 ; alprec = precal->a_link; lftprc = precal->a_int.ileft ; rtprec = precal->a_int.iright +2 ; /* test whether last interval in preceeding line dealt with */ if (precal > prlast || rtcrn <= lftprc + jdqt) { /* is interval in current line already allocated */ if (!alloc) { /* start a new object and allocate this interval to it */ /* interval list */ link1 = newchain2(freechain,lftcrn,jrtcrn); /* line-of-intervals list */ link2 = newchain1(freechain, (unsigned long) link1); /* "object" -- first line, last line, line-of-intervals pointer */ crntal->a_link = newchain1(freechain,line) ; crntal->a_link = addlink1 (freechain, crntal->a_link, (unsigned long)line); crntal->a_link = addlink1 (freechain, crntal->a_link, (unsigned long) link2); } /* move on to next interval in current line */ crntal++ ; alloc = NULL; } else { if (rtprec > lftcrn+ (int )jdqt) { /* case of overlapping intervals: */ /* is intvl in current line already allocated ? */ if (!alloc) { /* allocate this interval and add to object list */ alloc = alprec ; link1 = alloc->l_link ; crntal->a_link = alloc ; /* test whether this line has already been started for this object */ if (link1->l_u.line != line) { /* update last line */ link1->l_u.line = line; /* add a link to the line list for this line */ link1 = newchain2(freechain,lftcrn,jrtcrn); alloc->l_u.u_link = addlink1 (freechain, alloc->l_u.u_link, (unsigned long) link1); } else { /* add interval to interval list for last line */ link1 = alloc->l_u.u_link; link1->l_u.u_link = addlink2 (freechain, link1->l_u.u_link, lftcrn, jrtcrn); } } else { /* merge lists and reallocate intervals */ /* test whether both already allocated to same object */ if (alprec != alloc) { merge (&alprec, &alloc, freechain); /* reallocate intervals in preceding line */ for (altemp = precst; altemp <= prlast; altemp++) { if (altemp->a_link == alloc) { altemp->a_link = alprec ; } } /* reallocate intervals in current line */ for (altemp = crntst; altemp <= crntal; altemp++) { if (altemp->a_link == alloc) { altemp->a_link = alprec ; } } alloc = alprec ; } } if (rtcrn < rtprec) { /* move to next interval in this line */ crntal++; alloc = NULL ; continue; /* the outer while loop */ } } /* move on to next interval in preceding line */ precal++ ; } } /* all intervals in current line dealt with: */ /* find and construct any finished objects */ if (precst <= prlast) { for (precal = precst; precal <= prlast; precal++) { alprec = precal->a_link ; /* has the object to which this interval was allocated been dealt with */ if (alprec) { /* remove any later intervals allocated to the same object */ for (altemp = precal; altemp <= prlast; altemp++) { if (altemp->a_link == alprec) { altemp->a_link = NULL ; } } /* test if this object has intervals in the current line */ /* and if so skip to end of outer loop */ if (crntst <= crlast) { for (altemp = crntst; altemp <= crlast; altemp++) { if (altemp->a_link == alprec) goto loopend; } } /* construct object - first find line and column bounds */ link1 = alprec->l_link; oll = (int) link1->l_u.line; link1 = link1->l_link; ofl = (int) link1->l_u.line; link1 = alprec->l_u.u_link; mkl = 0; /* Just to keep lint happy. */ mxkl = 0; /* Just to keep lint happy. */ nints = 0; for (jjj=ofl; jjj<=oll; jjj++) { link1 = link1->l_link; link2 = link1->l_u.u_link; do { link2 = link2->l_link; jl = link2->l_u.intv.ileft; jr = link2->l_u.intv.iright; if (nints == 0 || jl < mkl) mkl = jl ; if (nints == 0 || jr > mxkl) mxkl = jr ; nints++; /* test for end of line */ } while (link2 != link1->l_u.u_link); } /* test whether object large enough, if not ignore it */ /* test for height or width less than threshold */ if (oll-ofl < ignlns || mxkl-mkl < ignlns) { for (jjj = ofl; jjj <= oll; jjj++) { link1 = link1->l_link; link2 = link1->l_u.u_link; /* recover chain space */ join (freechain, link2) ; } } else { /* test for object array overflow */ if (nob >= maxNumObjs) { AlcFree((void *) al); chainFree(); *mm = nob; return(WLZ_ERR_INT_DATA); } link1 = alprec->l_u.u_link; /* set up domain and object, and update counts need to test successful space allocation here */ jdp = WlzMakeIntervalDomain(WLZ_INTERVALDOMAIN_INTVL, ofl,oll,mkl,mxkl, &errNum); domain.i = jdp; values.v = jvp; *objlist = WlzAssignObject( WlzMakeMain(WLZ_2D_DOMAINOBJ, domain,values,NULL,obj,&errNum), NULL); objlist++; nob++; /* get the size correct here !!! */ itvl = (WlzInterval *) AlcMalloc (nints * sizeof(WlzInterval)); jdp->freeptr = AlcFreeStackPush(jdp->freeptr, (void *)itvl, NULL); /* write intervals and interval pointers lists */ for (jjj=ofl; jjj<=oll; jjj++) { jtvl = itvl; nints = 0; link1 = link1->l_link; link2 = link1->l_u.u_link; do { /* * the following increment since the interval * list is circular, monotone increasing, * but link2 originally points at the * rightmost interval (see comment at top). */ link2 = link2->l_link; itvl->ileft = link2->l_u.intv.ileft - mkl; itvl->iright = link2->l_u.intv.iright - mkl; itvl++; nints++; /* test for end of line */ } while (link2 != link1->l_u.u_link); WlzMakeInterval(jjj,jdp,nints,jtvl); join (freechain, link2) ; } } /* return line list etc to free-list store */ join (freechain, alprec->l_u.u_link); join (freechain, alprec) ; } loopend: ; } } /* update pointers - swap the two halves of the allocation list */ /* before getting the intervals in the next line */ altemp = crntst ; crntst = precst ; precst = altemp ; prlast = crlast ; } *mm = nob ; AlcFree((void *) al); chainFree(); return WLZ_ERR_NONE; }
/*! * \return Woolz compound array object. * \ingroup WlzExtFF * \brief Given an object in which distinct voxel values represent * different domains and each of these has a corresponding * material, this function creates a new compound array * object. Each of the materials has it's own domain and * the material properties are encoded in a simple ascii * property list of the domain. * The materials are known to have indices which increase * from 0. * \param gObj Given index object. * \param head Amira header data structure with the * list of materials. * \param dstErr Destination pointer for error number, * may be NULL. */ static WlzCompoundArray *WlzEffAmSplitLabelObj(WlzObject *gObj, WlzEffAmHead *head, WlzErrorNum *dstErr) { int idx, empty = 0; WlzPixelV thrV; WlzObject *tObj0 = NULL, *tObj1 = NULL, *tObj2, *tObj3; WlzEffAmMaterial *mat; WlzPropertyList *pList = NULL; WlzCompoundArray *aObj = NULL; WlzDomain nullDom; WlzValues dValues, nullVal; WlzErrorNum errNum = WLZ_ERR_NONE; const int allEmptyAfterFirst = 0; idx = 0; nullDom.core = NULL; nullVal.core = NULL; dValues.core = NULL; mat = head->materials; thrV.type = WLZ_GREY_INT; /* Create compound array object. */ aObj = WlzMakeCompoundArray(WLZ_COMPOUND_ARR_1, 1, head->matCount, NULL, WLZ_3D_DOMAINOBJ, &errNum); /* For each material create a new domain corresponding to the index. */ while((errNum == WLZ_ERR_NONE) && (idx < head->matCount)) { thrV.v.inv = mat->id + 1; pList = WlzEffAmMakeMaterialPropList(mat, &errNum); if(empty && allEmptyAfterFirst) { tObj3 = WlzMakeMain(WLZ_EMPTY_OBJ, nullDom, nullVal, pList, NULL, &errNum); } else { if(errNum == WLZ_ERR_NONE) { if(idx == 0) { tObj1 = WlzAssignObject( WlzThreshold(gObj, thrV, WLZ_THRESH_HIGH, &errNum), NULL); if(errNum == WLZ_ERR_NONE) { tObj2 = WlzDiffDomain(gObj, tObj1, &errNum); } } else { tObj1 = WlzAssignObject( WlzThreshold(tObj0, thrV, WLZ_THRESH_HIGH, &errNum), NULL); if(errNum == WLZ_ERR_NONE) { tObj2 = WlzDiffDomain(tObj0, tObj1, &errNum); } } } if(errNum == WLZ_ERR_NONE) { if(WlzIsEmpty(tObj1, &errNum) || WlzIsEmpty(tObj2, &errNum)) { empty = 1; tObj3 = WlzMakeMain(WLZ_EMPTY_OBJ, nullDom, nullVal, pList, NULL, &errNum); } else { tObj3 = WlzMakeMain(WLZ_3D_DOMAINOBJ, tObj2->domain, dValues, pList, NULL, &errNum); } } (void )WlzFreeObj(tObj2); tObj2 = NULL; (void )WlzFreeObj(tObj0); tObj0 = tObj1; tObj1 = NULL; } if(errNum == WLZ_ERR_NONE) { aObj->o[idx] = WlzAssignObject(tObj3, NULL); } ++idx; mat = mat->next; } (void )WlzFreeObj(tObj0); if(dstErr) { *dstErr = errNum; } return(aObj); }
int main(int argc, char **argv) { int option, ok = 1, usage = 0; WlzObject *inObj = NULL, *outObj = NULL, *grdObj = NULL, *edgObj = NULL; double alpha = 1.0, mult = 1.0; WlzPixelV pThrV, sThrV; WlzCannyDericheFilterAction action = WLZ_CANNYDERICHE_FLTACT_EDG; WlzErrorNum errNum = WLZ_ERR_NONE; FILE *fP = NULL; char *outFileStr, *inObjFileStr; static char optList[] = "ho:a:m:l:u:deg", outFileStrDef[] = "-", inObjFileStrDef[] = "-"; opterr = 0; outFileStr = outFileStrDef; inObjFileStr = inObjFileStrDef; pThrV.type = sThrV.type = WLZ_GREY_INT; pThrV.v.inv = sThrV.v.inv = 1.0; while(ok && ((option = getopt(argc, argv, optList)) != -1)) { switch(option) { case 'o': outFileStr = optarg; break; case 'h': usage = 1; ok = 0; break; case 'a': if((optarg == NULL) || (sscanf(optarg, "%lg", &alpha) != 1)) { usage = 1; ok = 0; } break; case 'd': action = WLZ_CANNYDERICHE_FLTACT_DIR; break; case 'e': action = WLZ_CANNYDERICHE_FLTACT_EDG; break; case 'g': action = WLZ_CANNYDERICHE_FLTACT_GRD; break; case 'm': if((optarg == NULL) || (sscanf(optarg, "%lg", &mult) != 1)) { usage = 1; ok = 0; } case 'l': if((optarg == NULL) || (sscanf(optarg, "%d", &(sThrV.v.inv)) != 1)) { usage = 1; ok = 0; } break; case 'u': if((optarg == NULL) || (sscanf(optarg, "%d", &(pThrV.v.inv)) != 1)) { usage = 1; ok = 0; } break; default: usage = 1; ok = 0; break; } } if(ok) { if((inObjFileStr == NULL) || (*inObjFileStr == '\0') || (outFileStr == NULL) || (*outFileStr == '\0')) { ok = 0; usage = 1; } } if(ok && (optind < argc)) { if((optind + 1) != argc) { usage = 1; ok = 0; } else { inObjFileStr = *(argv + optind); } } 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; (void )fprintf(stderr, "%s: failed to read object from file %s\n", *argv, inObjFileStr); } if(fP && strcmp(inObjFileStr, "-")) { fclose(fP); } } if(ok) { switch(action) { case WLZ_CANNYDERICHE_FLTACT_DIR: outObj = WlzCannyDeriche(NULL, inObj, alpha, mult, pThrV, sThrV, &errNum); break; case WLZ_CANNYDERICHE_FLTACT_EDG: edgObj = WlzCannyDeriche(&grdObj, inObj, alpha, mult, pThrV, sThrV, &errNum); if(errNum == WLZ_ERR_NONE) { outObj = WlzMakeMain(edgObj->type, edgObj->domain, grdObj->values, NULL, NULL, &errNum); } WlzFreeObj(edgObj); WlzFreeObj(grdObj); break; case WLZ_CANNYDERICHE_FLTACT_GRD: edgObj = WlzCannyDeriche(&outObj, inObj, alpha, mult, pThrV, sThrV, &errNum); WlzFreeObj(edgObj); break; } if(errNum != WLZ_ERR_NONE) { ok = 0; (void )fprintf(stderr, "%s Failed to filter object (%s)\n", *argv, WlzStringFromErrorNum(errNum, NULL)); } } if(ok) { if(((fP = (strcmp(outFileStr, "-")? fopen(outFileStr, "w"): stdout)) == NULL) || (WlzWriteObj(fP, outObj) != WLZ_ERR_NONE)) { ok = 0; (void )fprintf(stderr, "%s: failed to write output object\n", *argv); } if(fP && strcmp(outFileStr, "-")) { fclose(fP); } } if(inObj) { WlzFreeObj(inObj); } if(outObj) { WlzFreeObj(outObj); } if(usage) { (void )fprintf(stderr, "Usage: %s%s%s%sExample: %s%s", *argv, " [-o<output object>] [-h] [-o] [-a#] [-d] [-e] [-g]\n" " [-m#] [-l#] [-u#] [<input object>]\n" "Version: ", WlzVersion(), "\n" "Options:\n" " -h Prints this usage information\n" " -o Output object file name.\n" " -a Deriche's alpha parameter.\n" " -d Output object with edge direction encoding.\n" " -e Output object with edge gradients.\n" " -g Output object with all gradients.\n" " -m Filter multiplier parameter.\n" " -l Lower hysteresis threshold value.\n" " -u Upper hysteresis threshold value.\n" "Applies a Canny/Deriche edge detection filter to a Woolz domain object\n" "with grey values.\n" "The input object is read from stdin and the filtered object is\n" "written to stdout unless the filenames are given.\n", *argv, " -e -a 1.0 -l 15 -u 40 -m 10 -o edges.wlz in.wlz\n" "The input Woolz object is read from in.wlz, and filtered using a\n" "Deriche edge operator with alpha=1.0, gradients are multiplied by\n" "10.0 before non-maximal suppression or hysteresis thresholding. The\n" "hysteresis threshold values of 15 and 40 are used to threshold the\n" "edge image which is written to edge.wlz.\n"); } return(!ok); }
/*! * \ingroup WlzDomainOps * \brief Calculate the volume of the input 3D domain object. * * \return Volume of input 3D object, -1 on error. * \param obj Input object pointer. * \param wlzErr Error return. * \par Source: * WlzVolume.c */ WlzLong WlzVolume( WlzObject *obj, WlzErrorNum *wlzErr) { WlzObject *tmpobj; WlzPlaneDomain *pldom; WlzDomain domain; WlzValues values; WlzLong vol = -1, p; WlzErrorNum errNum = WLZ_ERR_NONE; /* check the object */ if( obj == NULL ) { if(*wlzErr) { *wlzErr = WLZ_ERR_OBJECT_NULL; } return( -1 ); } switch( obj->type ){ case WLZ_2D_DOMAINOBJ: return(WlzArea(obj, wlzErr)); case WLZ_3D_DOMAINOBJ: if( obj->domain.core == NULL ){ if(*wlzErr) { *wlzErr = WLZ_ERR_DOMAIN_NULL; } return -1; } if( obj->domain.core->type != WLZ_PLANEDOMAIN_DOMAIN ) { if(*wlzErr) { *wlzErr = WLZ_ERR_DOMAIN_TYPE; } return -1; } break; case WLZ_EMPTY_OBJ: if(*wlzErr) { *wlzErr = WLZ_ERR_NONE; } return 0; default: if(wlzErr) { *wlzErr = WLZ_ERR_OBJECT_TYPE; } return( -1 ); } /* scan through planes calculating the area */ vol = 0; values.core = NULL; pldom = obj->domain.p; for(p=0; (p <= (pldom->lastpl - pldom->plane1)) && (errNum == WLZ_ERR_NONE); p++){ domain = pldom->domains[p]; if( domain.core ){ tmpobj = WlzMakeMain(WLZ_2D_DOMAINOBJ, domain, values, NULL, NULL, &errNum); if(tmpobj) { vol += WlzArea(tmpobj , &errNum); WlzFreeObj( tmpobj ); } } } if(errNum != WLZ_ERR_NONE) { vol = -1; } if(wlzErr) { *wlzErr = errNum; } return( vol ); }
int main(int argc, char *argv[]) { int idE, nElm = 0, ok = 1, option, usage = 0, conform = 1, copyMesh = 0, features = 0, laplacianItr = 0, lowPassItr = 0, smoothBnd = 0, verify = 0; char outFmt = 'v'; double laplacianAlpha = 0.1, lowPassLambda = 0.33, lowPassMu = -0.34, minElmSz = 25.0, maxElmSz = 100.0; int *idx = NULL; double *vol = NULL, *minLen = NULL, *maxLen = NULL; FILE *fP = NULL; char *inObjFileStr, *outFileStr; const char *errMsgStr; WlzErrorNum errNum = WLZ_ERR_NONE; WlzDomain dom; WlzValues val; WlzObject *obj = NULL, *outObj = NULL; WlzCMeshP mesh, meshCopy; WlzObjectType cMType; static char optList[] = "a:BCFhl:L:m:M:o:u:vVwW:y"; const char inObjFileStrDef[] = "-", outFileStrDef[] = "-"; val.core = NULL; mesh.v = NULL; opterr = 0; inObjFileStr = (char *)inObjFileStrDef; outFileStr = (char *)outFileStrDef; while((usage == 0) && ((option = getopt(argc, argv, optList)) != EOF)) { switch(option) { case 'a': if(sscanf(optarg, "%lg", &laplacianAlpha) != 1) { usage = 1; } break; case 'B': smoothBnd = 1; break; case 'C': conform = 0; break; case 'F': features = 1; break; case 'l': if(sscanf(optarg, "%lg", &lowPassLambda) != 1) { usage = 1; } break; case 'L': if(sscanf(optarg, "%d", &laplacianItr) != 1) { usage = 1; } break; case 'm': if(sscanf(optarg, "%lg", &minElmSz) != 1) { usage = 1; } break; case 'M': if(sscanf(optarg, "%lg", &maxElmSz) != 1) { usage = 1; } break; case 'o': outFileStr = optarg; break; case 'u': if(sscanf(optarg, "%lg", &lowPassMu) != 1) { usage = 1; } break; case 'W': if(sscanf(optarg, "%d", &lowPassItr) != 1) { usage = 1; } break; case 'v': outFmt = 'v'; break; case 'V': verify = 1; break; case 'w': outFmt = 'w'; break; case 'y': copyMesh = 1; break; case 'h': default: usage = 1; break; } } ok = usage == 0; if(ok) { if((inObjFileStr == NULL) || (*inObjFileStr == '\0') || (outFileStr == NULL) || (*outFileStr == '\0')) { ok = 0; usage = 1; } if(ok && (optind < argc)) { if((optind + 1) != argc) { usage = 1; ok = 0; } else { inObjFileStr = *(argv + optind); } } } if(ok) { if((inObjFileStr == NULL) || (*inObjFileStr == '\0') || ((fP = (strcmp(inObjFileStr, "-")? fopen(inObjFileStr, "r"): stdin)) == NULL) || ((obj = WlzAssignObject(WlzReadObj(fP, &errNum), NULL)) == NULL) || (errNum != WLZ_ERR_NONE)) { ok = 0; (void )fprintf(stderr, "%s: failed to read object from file %s\n", *argv, inObjFileStr); } if(fP && strcmp(inObjFileStr, "-")) { (void )fclose(fP); fP = NULL; } } if(ok) { (void )WlzAssignObject(obj, NULL); mesh = WlzCMeshFromObj(obj, minElmSz, maxElmSz, NULL, conform, &errNum); if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsgStr); (void )fprintf(stderr, "%s Failed to create conforming mesh, %s.\n", argv[0], errMsgStr); } } if(ok) { WlzCMeshSetBoundNodFlags(mesh); if(verify) { errNum = WlzCMeshVerify(mesh, NULL, 1, stderr); if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsgStr); (void )fprintf(stderr, "%s Failed to verify mesh, %s.\n", argv[0], errMsgStr); } } } if(ok && (laplacianItr > 0)) { errNum = WlzCMeshLaplacianSmooth(mesh, laplacianItr, laplacianAlpha, smoothBnd, 1); if((errNum == WLZ_ERR_NONE) && verify) { errNum = WlzCMeshVerify(mesh, NULL, 1, stderr); } if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsgStr); (void )fprintf(stderr, "%s Failed to Laplacian smooth mesh, %s.\n", argv[0], errMsgStr); } } if(ok && (lowPassItr > 0)) { errNum = WlzCMeshLPFilterLM(mesh, lowPassLambda, lowPassMu, lowPassItr, smoothBnd, 1); if((errNum == WLZ_ERR_NONE) && verify) { errNum = WlzCMeshVerify(mesh, NULL, 1, stderr); } if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsgStr); (void )fprintf(stderr, "%s Failed to low pass filter mesh, %s.\n", argv[0], errMsgStr); } } if(ok && (copyMesh != 0)) { meshCopy = WlzCMeshCopy(mesh, 1, 0, NULL, NULL, &errNum); if(errNum != WLZ_ERR_NONE) { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsgStr); (void )fprintf(stderr, "%s Failed to copy mesh, %s.\n", argv[0], errMsgStr); } else { (void )WlzCMeshFree(mesh); mesh = meshCopy; meshCopy.v = NULL; } } if(ok) { if((fP = (strcmp(outFileStr, "-")? fopen(outFileStr, "w"): stdout)) == NULL) { ok = 0; (void )fprintf(stderr, "%s: Failed to open output file %s.\n", argv[0], outFileStr); } } if(ok && features) { errNum = WlzCMeshCmpElmFeat(mesh, &nElm, &idx, &vol, &minLen, &maxLen); if(errNum == WLZ_ERR_NONE) { for(idE = 0; idE < nElm; ++idE) { (void )printf("%d %lg %lg %lg\n", *(idx + idE), *(vol + idE), *(minLen + idE), *(maxLen + idE)); } } else { ok = 0; (void )WlzStringFromErrorNum(errNum, &errMsgStr); (void )fprintf(stderr, "%s Failed to compute mesh features, %s.\n", argv[0], errMsgStr); } AlcFree(idx); AlcFree(vol); AlcFree(minLen); AlcFree(maxLen); } if(ok) { switch(outFmt) { case 'v': WlzCMeshDbgOutVTK(fP, mesh); break; case 'w': switch(mesh.m2->type) { case WLZ_CMESH_2D: cMType = WLZ_CMESH_2D; dom.cm2 = mesh.m2; break; case WLZ_CMESH_3D: cMType = WLZ_CMESH_3D; dom.cm3 = mesh.m3; break; default: errNum = WLZ_ERR_DOMAIN_TYPE; break; } if(errNum == WLZ_ERR_NONE) { outObj = WlzMakeMain(cMType, dom, val, NULL, NULL, &errNum); } if(errNum == WLZ_ERR_NONE) { errNum = WlzWriteObj(fP, outObj); } (void )WlzFreeObj(outObj); break; } } if(fP && strcmp(outFileStr, "-")) { (void )fclose(fP); fP = NULL; } (void )WlzFreeObj(obj); if(usage) { fprintf(stderr, "Usage: %s [-h] [-o<output file>]\n" " [-L#] [-a#] [-W#] [-l#] [-u#] [-B] [-C]\n" " [-m#] [-M#] [-F] [-V] [-y] [<input object>]\n" "Computes a conforming mesh for the given input object.\n" "Options are:\n" " -h Help, prints this usage message.\n" " -o Output file.\n" " -a Laplacian alpha parameter.\n" " -W Number of low pass filter smoothing iterations.\n" " -l Low pass filter lambda value.\n" " -u Low pass filter mu value.\n" " -B Smooth boundary (requires a smoothing method to be\n" " selected).\n" " -C Don't make the mesh conform to the object's domain.\n" " -m Minimum mesh element size.\n" " -L Number of Laplacian smoothing iterations.\n" " -M Maximum mesh element size.\n" " -F Prints features of the mesh elements to the standard\n" " output.\n" " -V Verify mesh. This may take a long time and may give\n" " segmentation faults for buggy meshes.\n" " -v VTK file format for output.\n" " -w Woolz file format for output.\n" " -y Copy mesh to squeeze out all deleted entities.\n", argv[0]); } return(!ok); }
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 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); }
/*! * \ingroup WlzBoundary * \brief Return a domain object corresponding to the input boundary object. * * \return Domain object corresponding to the input boundary, NULL on error. * \param boundary Input boundary object. * \param fillMode Fill mode for the individual polyline boundaries. If the input object is a genuine boundary object then there will be no self-intersecting polylines and <tt> fillMode = WLZ_SIMPLE_FILL </tt> is appropriate. See WlzPolyToObj(). * \param dstErr Error return * \par Source: * WlzBoundToObj.c */ WlzObject *WlzBoundaryToObj( WlzObject *boundary, WlzPolyFillMode fillMode, WlzErrorNum *dstErr) { WlzObject *rtnObj=NULL, *tmpObj; WlzDomain domain, *domains, *bnddmns; WlzValues values; int p; WlzErrorNum errNum=WLZ_ERR_NONE; /* check object */ if( boundary == NULL ){ errNum = WLZ_ERR_OBJECT_NULL; } else { switch( boundary->type ){ case WLZ_3D_DOMAINOBJ: /* check plane domain */ if( boundary->domain.p == NULL ){ errNum = WLZ_ERR_DOMAIN_NULL; } else { switch( boundary->domain.p->type ){ case WLZ_PLANEDOMAIN_BOUNDLIST: if((domain.p = WlzMakePlaneDomain(WLZ_PLANEDOMAIN_DOMAIN, boundary->domain.p->plane1, boundary->domain.p->lastpl, boundary->domain.p->line1, boundary->domain.p->lastln, boundary->domain.p->kol1, boundary->domain.p->lastkl, &errNum)) != NULL){ domain.p->voxel_size[0] = boundary->domain.p->voxel_size[0]; domain.p->voxel_size[1] = boundary->domain.p->voxel_size[1]; domain.p->voxel_size[2] = boundary->domain.p->voxel_size[2]; domains = domain.p->domains; bnddmns = boundary->domain.p->domains; for(p=domain.p->plane1; p <= domain.p->lastpl; p++, domains++, bnddmns++){ if( (*bnddmns).poly ){ if((tmpObj = WlzBoundToObj((*bnddmns).b, fillMode, &errNum)) != NULL){ *domains = WlzAssignDomain(tmpObj->domain, NULL); WlzFreeObj(tmpObj); } else { WlzFreePlaneDomain(domain.p); domain.p = NULL; break; } } } if( domain.p ){ values.core = NULL; if( (rtnObj = WlzMakeMain(WLZ_3D_DOMAINOBJ, domain, values, NULL, NULL, &errNum)) == NULL ){ WlzFreePlaneDomain(domain.p); } } } break; case WLZ_EMPTY_DOMAIN: return WlzMakeEmpty(dstErr); default: errNum = WLZ_ERR_DOMAIN_TYPE; break; } } break; case WLZ_BOUNDLIST: return WlzBoundToObj(boundary->domain.b, fillMode, dstErr); case WLZ_TRANS_OBJ: if((values.obj = WlzBoundaryToObj(boundary->values.obj, fillMode, &errNum)) != NULL){ return WlzMakeMain(WLZ_TRANS_OBJ, boundary->domain, values, NULL, NULL, dstErr); } break; case WLZ_EMPTY_OBJ: return WlzMakeEmpty(dstErr); default: errNum = WLZ_ERR_OBJECT_TYPE; break; } } if( dstErr ){ *dstErr = errNum; } return rtnObj; }
/*! * \return Object read from file. * \ingroup WlzExtFF * \brief Reads a Woolz object from the given stream using the * stereolithography stl surface file format. This format * has surface definitions defined by a name and a collection * of facet normals and loops. Each facet loop has the * cooordinates of it's vertices. * Only triangulated surface models can be read and all solids * will be treated as one. * \param fP Input file stream. * \param dstErr Destination error number ptr, may be * NULL. */ WlzObject *WlzEffReadObjStl(FILE *fP, WlzErrorNum *dstErr) { int vCnt = 0, inSolid = 0, inFacet = 0, inLoop = 0; char *sav, *str, *tok; WlzDVertex3 vBuf[3]; WlzGMModel *model = NULL; WlzObject *obj = NULL; WlzDomain dom; WlzValues val; WlzErrorNum errNum = WLZ_ERR_NONE; char cBuf[256]; dom.core = NULL; val.core = NULL; if(fP == NULL) { errNum = WLZ_ERR_PARAM_NULL; } else { model = WlzGMModelNew(WLZ_GMMOD_3D, 0, 0, &errNum); } while((errNum == WLZ_ERR_NONE) && ((str = WlzEffReadObjStlRec(fP, cBuf, 256)) != NULL)) { if((tok = strtok_r(str, " \t", &sav)) != NULL) { if(strncmp(tok, "solid", 5) == 0) { if(inSolid == 0) { inSolid = 1; } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } else if(strncmp(tok, "facet", 5) == 0) { if((inSolid == 1) && (inFacet == 0)) { inFacet = 1; /* Normal vector is ignored. */ } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } else if(strncmp(tok, "outer", 5) == 0) { if(((tok = strtok_r(NULL, " \t", &sav)) == NULL) || (strncmp(tok, "loop", 4) != 0) || (inSolid == 0) || (inFacet == 0) || (inLoop != 0)) { errNum = WLZ_ERR_READ_INCOMPLETE; } else { vCnt = 0; inLoop = 1; } } else if(strncmp(tok, "vertex", 6) == 0) { char *pTok[3]; if((vCnt < 3) && ((pTok[0] = strtok_r(NULL, " \t", &sav)) != NULL) && ((pTok[1] = strtok_r(NULL, " \t", &sav)) != NULL) && ((pTok[2] = strtok_r(NULL, " \t", &sav)) != NULL) && (sscanf(pTok[0], "%lg", &(vBuf[vCnt].vtX)) == 1) && (sscanf(pTok[1], "%lg", &(vBuf[vCnt].vtY)) == 1) && (sscanf(pTok[2], "%lg", &(vBuf[vCnt].vtZ)) == 1)) { ++vCnt; } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } else if(strncmp(tok, "endloop", 7) == 0) { if(inLoop == 1) { inLoop = 0; if(vCnt == 3) { errNum = WlzGMModelConstructSimplex3D(model, vBuf); } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } else if(strncmp(tok, "endfacet", 8) == 0) { if(inFacet == 1) { inFacet = 0; } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } else if(strncmp(tok, "endsolid", 8) == 0) { if(inSolid == 1) { inSolid = 0; } else { errNum = WLZ_ERR_READ_INCOMPLETE; } } } } /* Create the Woolz object. */ if(errNum == WLZ_ERR_NONE) { if((dom.ctr = WlzMakeContour(&errNum)) != NULL) { dom.ctr->model = WlzAssignGMModel(model, NULL); obj = WlzMakeMain(WLZ_CONTOUR, dom, val, NULL, NULL, &errNum); } } if(errNum != WLZ_ERR_NONE) { if(obj != NULL) { (void )WlzFreeObj(obj); } else if(dom.ctr != NULL) { (void )WlzFreeContour(dom.ctr); } else { (void )WlzGMModelFree(model); } obj = NULL; } if(dstErr) { *dstErr = errNum; } return(obj); }