int main( int argc, char **argv) { FILE *inFile; char optList[] = "d:m:t:hv"; int option; WlzErrorNum errNum=WLZ_ERR_NONE; int verboseFlg=0; int type=1; WlzObject *obj = NULL, *obj1, *obj2, *obj3; double matchVal=0.0; double s1, s2, s3, s4; double delta=0.01; double **mixing=NULL, **contrib=NULL; int k, l; int numCatRows=-1, numCatCols=-1; /* read the argument list and check for an input file */ opterr = 0; while( (option = getopt(argc, argv, optList)) != EOF ){ switch( option ){ case 'd': delta = atof(optarg); if((delta >= 1.0) || (delta <= 1.0e-10)){ delta = 0.01; fprintf(stderr, "%s: invalid delta, reset to 0.01", argv[0]); } break; case 'm': if((inFile = fopen(optarg, "r")) != NULL){ if( fscanf(inFile, "%d, %d", &numCatCols, &numCatRows) < 2 ){ fprintf(stderr, "%s: can't read mixing matrix dimensions\n", argv[0]); usage(argv[0]); return 1; } AlcDouble2Malloc(&mixing, numCatRows, numCatCols); AlcDouble2Malloc(&contrib, numCatRows, numCatCols); for(l=0; l < numCatRows; l++){ for(k=0; k < numCatCols; k++){ if( fscanf(inFile, "%lg,", &(mixing[l][k])) < 1 ){ fprintf(stderr, "%s: can't read mixing matrix\n", argv[0]); usage(argv[0]); return 1; } } } for(l=0; l < numCatRows; l++){ for(k=0; k < numCatCols; k++){ if( fscanf(inFile, "%lg,", &(contrib[l][k])) < 1 ){ fprintf(stderr, "%s: can't read contributing matrix\n", argv[0]); usage(argv[0]); return 1; } } } } else { fprintf(stderr, "%s: can't open matrix file\n", argv[0]); usage(argv[0]); return 1; } break; case 't': type = atoi(optarg); break; case 'v': verboseFlg = 1; break; case 'h': default: usage(argv[0]); return 1; } } /* verbose output */ if( verboseFlg ){ fprintf(stderr, "%s: parameter values:\n", argv[0]); fprintf(stderr, "\ttype = %d, delta = %f\n", type, delta); if( type == 6 ){ fprintf(stderr, "\t mixing matrix:\n"); for(l=0; l < numCatRows; l++){ for(k=0; k < numCatCols; k++){ fprintf(stderr, "%f, ", mixing[l][k]); } fprintf(stderr, "\n"); } fprintf(stderr, "\n"); fprintf(stderr, "\t contributing matrix:\n"); for(l=0; l < numCatRows; l++){ for(k=0; k < numCatCols; k++){ fprintf(stderr, "%f, ", contrib[l][k]); } fprintf(stderr, "\n"); } } } /* get objects from stdin */ inFile = stdin; if((obj1 = WlzReadObj(inFile, &errNum)) != NULL){ switch( obj1->type ){ case WLZ_2D_DOMAINOBJ: case WLZ_3D_DOMAINOBJ: case WLZ_EMPTY_OBJ: break; default: fprintf(stderr, "%s: invalid object type: %d\n", argv[0], obj->type); usage(argv[0]); return 1; } } else { fprintf(stderr, "%s: can't read first object\n", argv[0]); usage(argv[0]); return 1; } if((obj2 = WlzReadObj(inFile, &errNum)) != NULL){ if( (obj2->type != obj1->type) && (obj2->type != WLZ_EMPTY_OBJ) ){ fprintf(stderr, "%s: objects must be same type\n", argv[0]); usage(argv[0]); return 1; } } else { fprintf(stderr, "%s: can't read second object\n", argv[0]); usage(argv[0]); return 1; } /* this can fail silently but if there is an object it must be valid */ if((obj3 = WlzReadObj(inFile, &errNum)) != NULL){ if( (obj3->type != obj1->type) && (obj3->type != WLZ_EMPTY_OBJ) ){ fprintf(stderr, "%s: objects must be same type\n", argv[0]); usage(argv[0]); return 1; } } /* now calculate a match value */ switch( type ){ case 1: if((obj = WlzIntersect2(obj1, obj2, &errNum)) != NULL){ s1 = WlzSize(obj, &errNum); WlzFreeObj(obj); } else { s1 = 0; } if((obj = WlzUnion2(obj1, obj2, &errNum)) != NULL){ s2 = WlzSize(obj, &errNum); WlzFreeObj(obj); } else { s2 = 1; } matchVal = s1 / s2; break; case 2: if((obj = WlzIntersect2(obj1, obj2, &errNum)) != NULL){ s1 = WlzSize(obj, &errNum); WlzFreeObj(obj); } else { s1 = 0; } if((obj = WlzUnion2(obj1, obj2, &errNum)) != NULL){ s2 = WlzSize(obj, &errNum); WlzFreeObj(obj); } else { s2 = 1; } matchVal = s1 / s2; if( type == 2 ){ s1 = WlzSize(obj1, &errNum); s2 = WlzSize(obj2, &errNum); if( s2 > s1 ){ if( matchVal == 0.0 ){ matchVal = 10.0; } else { matchVal = 1.0 / matchVal; matchVal = WLZ_MIN(matchVal, 10.0); } } } break; case 3: if((obj = WlzIntersect2(obj1, obj2, &errNum)) != NULL){ s1 = WlzSize(obj, &errNum); WlzFreeObj(obj); } else { s1 = 0; } s2 = WlzSize(obj1, &errNum); matchVal = 0.0; if( s2 > 0 ){ matchVal = s1 / s2; } break; case 4: if((obj = WlzIntersect2(obj1, obj2, &errNum)) != NULL){ s1 = WlzSize(obj, &errNum); WlzFreeObj(obj); } else { s1 = 0; } s2 = WlzSize(obj2, &errNum); matchVal = 0.0; if( s2 > 0 ){ matchVal = s1 / s2; } break; case 5: /* this is a coparative measure designed to give a value of 1 to a random pattern and between zero and infinite for matches to one or the other. For analysis for clustering probably better to use the logarithm. Zero and infinite are delta and 1/delta. */ /* must be a third object */ if( obj3 == NULL ){ fprintf(stderr, "%s: for match option 5 3 input object required\n", argv[0]); return 1; } s1 = WlzSize(obj2, &errNum); if((obj = WlzIntersect2(obj1, obj2, &errNum)) != NULL){ s2 = WlzSize(obj, &errNum); WlzFreeObj(obj); } else { s2 = 0.0; } s3 = WlzSize(obj3, &errNum); if((obj = WlzIntersect2(obj1, obj3, &errNum)) != NULL){ s4 = WlzSize(obj, &errNum); WlzFreeObj(obj); } else { s4 = 0.0; } if((s1 < 0.0) || (s2 < 0.0) || (s3 < 0.0) || (s4 < 0.0)){ /* just fail */ fprintf(stderr, "%s: something gone wrong, negative size.\n", argv[0]); return 1; } /* calculating (s2/s1) * (s3/s4) */ /* if the denominator non-zero then simple formula */ if( s2 > 0.0 ){ if( s4 > 0.0 ){ matchVal = (s2/s1) * (s3/s4); } else { matchVal = s2 / s1; } } else { if( s4 > 0.0 ){ matchVal = s3/s4; } else { matchVal = 1.0; } } matchVal = WLZ_MAX(matchVal, delta); matchVal = WLZ_MIN(matchVal, 1.0/delta); break; case 6: /* this requires a mixing and contributing matrix and the images read in must have grey-values set to the right categories */ if((numCatRows == -1) || (numCatCols == -1) || (mixing == NULL) || (contrib == NULL)){ fprintf(stderr, "%s: bad matrix data\n", argv[0]); usage(argv[0]); return 1; } matchVal = WlzMixtureValue(obj1, obj2, numCatRows, numCatCols, mixing, contrib, &errNum); break; default: fprintf(stderr, "%s: invalid match type\n", argv[0]); usage(argv[0]); return 1; } /* print value */ fprintf(stdout, "%f\n", matchVal); return 0; }
/*! * \return Autocorrelated object or NULL on error. * \ingroup WlzRegistration * \brief Computes the autocorrelation of the given 2D object, see * WlzAutoCor(). * \param gObj Given object. * \param dstErr Destination error pointer, may be NULL. */ static WlzObject *WlzAutoCor2D(WlzObject *gObj, WlzErrorNum *dstErr) { WlzIVertex2 aSz, wSz, aOrg, wOrg; WlzIBox2 box; double **wAr = NULL, **aAr = NULL; WlzObject *aObj = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; if(gObj == NULL) { errNum = WLZ_ERR_OBJECT_NULL; } else if(gObj->domain.core == NULL) { errNum = WLZ_ERR_DOMAIN_NULL; } else if(gObj->values.core == NULL) { errNum = WLZ_ERR_VALUES_NULL; } if(errNum == WLZ_ERR_NONE) { box = WlzBoundingBox2I(gObj, &errNum); aSz.vtX = box.xMax - box.xMin + 1; aSz.vtY = box.yMax - box.yMin + 1; /* Make sure aSz is even in x and y. */ if((aSz.vtX & 1) != 0) { aSz.vtX += 1; } if((aSz.vtY & 1) != 0) { aSz.vtY += 1; } wOrg.vtX = box.xMin - (aSz.vtX / 2); wOrg.vtY = box.yMin - (aSz.vtY / 2); wSz.vtX = aSz.vtX * 2; wSz.vtY = aSz.vtY * 2; (void )AlgBitNextPowerOfTwo((unsigned int *)&(wSz.vtX), wSz.vtX); (void )AlgBitNextPowerOfTwo((unsigned int *)&(wSz.vtY), wSz.vtY); errNum = WlzToArray2D((void ***)&wAr, gObj, wSz, wOrg, 0, WLZ_GREY_DOUBLE); } if(errNum == WLZ_ERR_NONE) { (void )AlgAutoCorrelate2D(wAr, wSz.vtX, wSz.vtY); if(AlcDouble2Malloc(&aAr, aSz.vtY, aSz.vtX) != ALC_ER_NONE) { errNum = WLZ_ERR_MEM_ALLOC; } } if(errNum == WLZ_ERR_NONE) { WlzAutoCorRearrange2D(aAr, aSz, wAr, wSz); aOrg.vtX = -(aSz.vtX / 2); aOrg.vtY = -(aSz.vtY / 2); aObj = WlzFromArray2D((void **)aAr, aSz, aOrg, WLZ_GREY_DOUBLE, WLZ_GREY_DOUBLE, 0.0, 1.0, 0, 0, &errNum); } if(errNum != WLZ_ERR_NONE) { if(aObj != NULL) { (void )WlzFreeObj(aObj); } } (void )Alc2Free((void **)wAr); (void )Alc2Free((void **)aAr); if(dstErr) { *dstErr = errNum; } return(aObj); }
int main(int argc, char *argv[]) { int iX, iY, oX, oY, rep, option, ok = 1, usage = 0, nRep = 16, dataSz = 256, dataSz2, dataSz3, dataSz4; long seed; double sum; double **data0 = NULL, **data1 = NULL; AlgError errNum = ALG_ERR_NONE; static char optList[] = "hn:s:"; while(ok && ((option = getopt(argc, argv, optList)) != -1)) { switch(option) { case 'n': if(sscanf(optarg, "%d", &nRep) != 1) { usage = 1; } break; case 's': if(sscanf(optarg, "%ld", &seed) != 1) { usage = 1; } break; case 'h': /* FALLTHROUGH */ default: usage = 1; break; } } ok = (usage == 0); if(ok) { dataSz2 = (dataSz - 1) / 2; dataSz3 = (dataSz - 1) / 3; dataSz4 = (dataSz - 1) / 4; AlgRandSeed(seed); if((AlcDouble2Malloc(&data0, dataSz, dataSz) != ALC_ER_NONE) || (AlcDouble2Malloc(&data1, dataSz, dataSz) != ALC_ER_NONE)) { ok = 0; (void )fprintf(stderr, "%s: Failed to allocate data arrays.\n",* argv); } } if(ok) { for(rep = 0; rep < nRep; ++rep) { iX = (int )(dataSz4 * (0.5 - AlgRandUniform())); iY = (int )(dataSz4 * (0.5 - AlgRandUniform())); for(oY = 0; oY < dataSz; ++oY) { for(oX = 0; oX < dataSz; ++oX) { *(*(data0 + oY) + oX) = 0.0; *(*(data1 + oY) + oX) = 0.0; } } *(*(data0 + dataSz2 + iY) + dataSz2 + iX) = 1.0; *(*(data1 + dataSz2) + dataSz2) = 1.0; errNum = AlgCrossCorrelate2D(data0, data1, dataSz, dataSz); if(errNum == ALG_ERR_NONE) { sum = 0; for(oY = 0; oY < dataSz; ++oY) { for(oX = 0; oX < dataSz; ++oX) { sum += *(*(data0 + oY) + oX); } } if(sum < 1.0) { sum = 1.0; } AlgCrossCorrPeakXY(&oX, &oY, NULL, data0, dataSz, dataSz, dataSz3, dataSz3); (void )fprintf(stderr, "%d %d %d %d", iX, iY, oX, oY); if((iX != oX) || (iY != oY)) { (void )fprintf(stderr, " 0\n"); } else { (void )fprintf(stderr, " 1\n"); } } } } if(usage) { (void )fprintf(stderr, "Usage: %s%s", *argv, " [-h] [-n #] [-s #]\n" "Options:\n" " -h Prints this usage information.\n" " -n Number of repeats.\n" " -s Seed for random number generator.\n" "Tests AlgCrossCorrelate2D() and AlgCrossCorrPeakXY() by creating\n" "arrays with a single non-zero value, in one of which the non-zero\n" "value is offset. The output consists of the offset, the computed\n" "offset and finaly 1 if the two are equal or 0 if they are not.\n"); } return(!ok); }
/*! * \return New Woolz domain object with maximal domain and grey * values which encode the gradient's direction or NULL * on error. * \ingroup WlzFeatures * \brief Computes the maximal domain and gradient direction of * given Woolz 2D domain object. * \note All the objects domains are known to be the same. * \param grdM Gradient magnitude. * \param grdY Gradient (partial derivative) * through lines. * \param grdX Gradient (partial derivative) * through columns. * \param minThrV Minimum gradient value to * consider. * \param dstErr Destination error pointer, may * be null. */ static WlzObject *WlzNMSuppress2D(WlzObject *grdM, WlzObject *grdY, WlzObject *grdX, WlzPixelV minThrV, WlzErrorNum *dstErr) { int idN, inLen, outLen, inLnIdx = 0; WlzGreyType gType, bufType; WlzIVertex2 bufSz, inPos, outPos, orgPos; WlzValues tmpVal; WlzDomain dstDom, grdDom; WlzIntervalWSpace tmpIWSp = {0}, grdMIWSp = {0}, grdYIWSp = {0}, grdXIWSp = {0}; WlzGreyWSpace tmpGWSp, grdMGWSp, grdYGWSp, grdXGWSp; WlzPixelV zeroV; WlzGreyP grdMBufGP, grdYBufGP, grdXBufGP; WlzDynItvPool pool; WlzObject *dstObj = NULL, *tmpObj = NULL; void *grdYBuf = NULL, *grdXBuf = NULL; void **grdMBuf = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; tmpVal.core = NULL; pool.itvBlock = NULL; dstDom.core = NULL; if((grdM->type != WLZ_2D_DOMAINOBJ) || (grdY->type != WLZ_2D_DOMAINOBJ) || (grdX->type != WLZ_2D_DOMAINOBJ)) { errNum = WLZ_ERR_OBJECT_NULL; } else if((grdM->domain.core == NULL) || (grdY->domain.core == NULL) || (grdX->domain.core == NULL)) { errNum = WLZ_ERR_DOMAIN_NULL; } else if((grdM->values.core == NULL) || (grdY->values.core == NULL) || (grdX->values.core == NULL)) { errNum = WLZ_ERR_VALUES_NULL; } else { /* Find required buffer type (WLZ_GREY_DOUBLE or WLZ_GREY_INT). */ bufType = WLZ_GREY_INT; gType = WlzGreyTableTypeToGreyType(grdM->values.core->type, &errNum); if(errNum == WLZ_ERR_NONE) { if((gType == WLZ_GREY_FLOAT) || (gType == WLZ_GREY_DOUBLE)) { bufType = WLZ_GREY_DOUBLE; } else { gType = WlzGreyTableTypeToGreyType(grdY->values.core->type, &errNum); if(errNum == WLZ_ERR_NONE) { if((gType == WLZ_GREY_FLOAT) || (gType == WLZ_GREY_DOUBLE)) { bufType = WLZ_GREY_DOUBLE; } else { gType = WlzGreyTableTypeToGreyType(grdX->values.core->type, &errNum); if(errNum == WLZ_ERR_NONE) { if((gType == WLZ_GREY_FLOAT) || (gType == WLZ_GREY_DOUBLE)) { bufType = WLZ_GREY_DOUBLE; } } } } } } } /* Convert minimum gradient threshold value. */ if(errNum == WLZ_ERR_NONE) { if(bufType == WLZ_GREY_INT) { errNum = WlzValueConvertPixel(&minThrV, minThrV, WLZ_GREY_INT); } else /* bufType == WLZ_GREY_DOUBLE */ { errNum = WlzValueConvertPixel(&minThrV, minThrV, WLZ_GREY_DOUBLE); } } if(errNum == WLZ_ERR_NONE) { grdDom = grdM->domain; /* Make destination object with WLZ_GREY_UBYTE greys. */ zeroV.type = WLZ_GREY_UBYTE; zeroV.v.inv = 0; tmpVal.v = WlzNewValueTb(grdM, WlzGreyTableType(WLZ_GREY_TAB_RAGR, WLZ_GREY_UBYTE, NULL), zeroV, &errNum); if(errNum == WLZ_ERR_NONE) { /* Use the input domain while calculating the new maximal domain. */ tmpObj = WlzMakeMain(WLZ_2D_DOMAINOBJ, grdM->domain, tmpVal, NULL, NULL, &errNum); } } /* Initialize the memory pool with some size of block. Any +ve number * greater than the maximum number of intervals in any destination line * would work but the fewer allocations then the more efficient the code, * hence this attempt to guess the required number of intervals in the * destination domain. */ if(errNum == WLZ_ERR_NONE) { pool.itvsInBlock = (((grdDom.i->lastkl - grdDom.i->kol1 + 1) * (grdDom.i->lastln - grdDom.i->line1 + 1)) / 64) + grdDom.i->lastkl - grdDom.i->kol1 + 1024; } /* Make gradient buffers. */ if(errNum == WLZ_ERR_NONE) { bufSz.vtY = 3; bufSz.vtX = grdDom.i->lastkl - grdDom.i->kol1 + 1; if(bufType == WLZ_GREY_INT) { if((AlcInt2Malloc((int ***)&grdMBuf, bufSz.vtY, bufSz.vtX) != ALC_ER_NONE) || ((grdYBuf = AlcMalloc(sizeof(int) * bufSz.vtX)) == NULL) || ((grdXBuf = AlcMalloc(sizeof(int) * bufSz.vtX)) == NULL)) { errNum = WLZ_ERR_MEM_ALLOC; } else { grdYBufGP.inp = (int *)grdYBuf; grdXBufGP.inp = (int *)grdXBuf; } } else /* bufType == WLZ_GREY_DOUBLE */ { if((AlcDouble2Malloc((double ***)&grdMBuf, bufSz.vtY, bufSz.vtX) != ALC_ER_NONE) || ((grdYBuf = AlcMalloc(sizeof(double) * bufSz.vtX)) == NULL) || ((grdXBuf = AlcMalloc(sizeof(double) * bufSz.vtX)) == NULL)) { errNum = WLZ_ERR_MEM_ALLOC; } else { grdYBufGP.dbp = (double *)grdYBuf; grdXBufGP.dbp = (double *)grdXBuf; } } } /* Make destination interval domain with interval lines but not intervals. */ if(errNum == WLZ_ERR_NONE) { dstDom.i = WlzMakeIntervalDomain(WLZ_INTERVALDOMAIN_INTVL, grdDom.i->line1, grdDom.i->lastln, grdDom.i->kol1, grdDom.i->lastkl, &errNum); } if(errNum == WLZ_ERR_NONE) { /* Scan down through the gradient objects. */ if(((errNum = WlzInitGreyScan(tmpObj, &tmpIWSp, &tmpGWSp)) == WLZ_ERR_NONE) && ((errNum = WlzInitGreyScan(grdM, &grdMIWSp, &grdMGWSp)) == WLZ_ERR_NONE) && ((errNum = WlzInitGreyScan(grdY, &grdYIWSp, &grdYGWSp)) == WLZ_ERR_NONE) && ((errNum = WlzInitGreyScan(grdX, &grdXIWSp, &grdXGWSp)) == WLZ_ERR_NONE)) { orgPos.vtX = grdDom.i->kol1; orgPos.vtY = grdDom.i->line1; while((errNum == WLZ_ERR_NONE) && ((errNum = WlzNextGreyInterval(&grdMIWSp)) == WLZ_ERR_NONE)) { inLen = grdMIWSp.rgtpos - grdMIWSp.lftpos + 1; inPos.vtX = grdMIWSp.lftpos - orgPos.vtX; /* Process any lines between this and the last by clearing the * gradient magnitude buffer . */ if(grdMIWSp.nwlpos > 0) { idN = (grdMIWSp.nwlpos >= 3)? 3: grdMIWSp.nwlpos; while(--idN >= 0) { inPos.vtY = grdMIWSp.linpos - orgPos.vtY - idN; inLnIdx = (3 + inPos.vtY) % 3; if(bufType == WLZ_GREY_INT) { WlzValueSetInt(*((int **)grdMBuf + inLnIdx), 0, bufSz.vtX); } else /* bufType == WLZ_GREY_DOUBLE */ { WlzValueSetDouble(*((double **)grdMBuf + inLnIdx), 0, bufSz.vtX); } } } /* Copy intervals to values buffers. */ if(bufType == WLZ_GREY_INT) { grdMBufGP.inp = *((int **)grdMBuf + inLnIdx); } else /* bufType == WLZ_GREY_DOUBLE */ { grdMBufGP.dbp = *((double **)grdMBuf + inLnIdx); } WlzValueCopyGreyToGrey(grdMBufGP, inPos.vtX, bufType, grdMGWSp.u_grintptr, 0, grdMGWSp.pixeltype, inLen); if(grdMIWSp.intrmn == 0) { while((errNum == WLZ_ERR_NONE) && (tmpIWSp.linpos < grdMIWSp.linpos)) { outPos.vtY = tmpIWSp.linpos - orgPos.vtY; if(outPos.vtY >= 0) { outLen = tmpIWSp.rgtpos - tmpIWSp.lftpos + 1; outPos.vtX = tmpIWSp.lftpos - orgPos.vtX; WlzValueCopyGreyToGrey(grdYBufGP, 0, bufType, grdYGWSp.u_grintptr, 0, grdYGWSp.pixeltype, outLen); WlzValueCopyGreyToGrey(grdXBufGP, 0, bufType, grdXGWSp.u_grintptr, 0, grdXGWSp.pixeltype, outLen); if(bufType == WLZ_GREY_INT) { errNum = WlzNMSuppress2DBufI(dstDom.i, (int **)grdMBuf, (int *)grdYBuf, (int *)grdXBuf, &pool, tmpGWSp.u_grintptr.ubp, outLen, outPos, orgPos, minThrV.v.inv); } else /* bufType == WLZ_GREY_DOUBLE */ { errNum = WlzNMSuppress2DBufD(dstDom.i, (double **)grdMBuf, (double *)grdYBuf, (double *)grdXBuf, &pool, tmpGWSp.u_grintptr.ubp, outLen, outPos, orgPos, minThrV.v.dbv); } } if(errNum == WLZ_ERR_NONE) { errNum = WlzNextGreyInterval(&tmpIWSp); } if(errNum == WLZ_ERR_NONE) { errNum = WlzNextGreyInterval(&grdYIWSp); } if(errNum == WLZ_ERR_NONE) { errNum = WlzNextGreyInterval(&grdXIWSp); } } } } if(errNum == WLZ_ERR_EOO) { errNum = WLZ_ERR_NONE; } } if(tmpIWSp.gryptr == &tmpGWSp) { (void )WlzEndGreyScan(&tmpIWSp, &tmpGWSp); } if(grdMIWSp.gryptr == &grdMGWSp) { (void )WlzEndGreyScan(&grdMIWSp, &grdMGWSp); } if(grdYIWSp.gryptr == &grdYGWSp) { (void )WlzEndGreyScan(&grdYIWSp, &grdYGWSp); } if(grdXIWSp.gryptr == &grdXGWSp) { (void )WlzEndGreyScan(&grdXIWSp, &grdXGWSp); } } if(errNum == WLZ_ERR_NONE) { if((errNum = WlzStandardIntervalDomain(dstDom.i)) == WLZ_ERR_NONE) { dstObj = WlzMakeMain(WLZ_2D_DOMAINOBJ, dstDom, tmpVal, NULL, NULL, &errNum); } } if(tmpObj) { WlzFreeObj(tmpObj); } if(errNum != WLZ_ERR_NONE) { if(tmpObj == NULL) { if(dstDom.core) { (void )WlzFreeDomain(dstDom); } if(tmpVal.core) { (void )WlzFreeValues(tmpVal); } } } if(grdMBuf) { Alc2Free(grdMBuf); } if(grdYBuf) { AlcFree(grdYBuf); } if(grdXBuf) { AlcFree(grdXBuf); } if(dstErr) { *dstErr = errNum; } return(dstObj); }
int main( int argc, char **argv) { FILE *inFile; char optList[] = "d:m:t:hv"; int option; WlzErrorNum errNum=WLZ_ERR_NONE; int verboseFlg=0; int type=1; int numRows=0, numCols=0; WlzObject *obj, *obj1, *obj2, *obj3; WlzObject **rowDoms, **colDoms; WlzObjectType objType; double matchVal=0.0; double s1, s2, s3, s4; double delta=0.01; double **mixing=NULL, **contrib=NULL; int i, j, k, l; int numCatRows=-1, numCatCols=-1; WlzDBox3 box1, box2; /* read the argument list and check for an input file */ opterr = 0; while( (option = getopt(argc, argv, optList)) != EOF ){ switch( option ){ case 'd': delta = atof(optarg); if((delta >= 1.0) || (delta <= 1.0e-10)){ delta = 0.01; fprintf(stderr, "%s: invalid delta, reset to 0.01", argv[0]); } break; case 'm': if((inFile = fopen(optarg, "r")) != NULL){ if( fscanf(inFile, "%d, %d", &numCatCols, &numCatRows) < 2 ){ fprintf(stderr, "%s: can't read mixing matrix dimensions\n", argv[0]); usage(argv[0]); return 1; } AlcDouble2Malloc(&mixing, numCatRows, numCatCols); AlcDouble2Malloc(&contrib, numCatRows, numCatCols); for(l=0; l < numCatRows; l++){ for(k=0; k < numCatCols; k++){ if( fscanf(inFile, "%lg,", &(mixing[l][k])) < 1 ){ fprintf(stderr, "%s: can't read mixing matrix\n", argv[0]); usage(argv[0]); return 1; } } } for(l=0; l < numCatRows; l++){ for(k=0; k < numCatCols; k++){ if( fscanf(inFile, "%lg,", &(contrib[l][k])) < 1 ){ fprintf(stderr, "%s: can't read contributing matrix\n", argv[0]); usage(argv[0]); return 1; } } } } else { fprintf(stderr, "%s: can't open matrix file\n", argv[0]); usage(argv[0]); return 1; } break; case 't': type = atoi(optarg); break; case 'v': verboseFlg = 1; break; case 'h': default: usage(argv[0]); return 1; } } /* there must be two more arguments */ if( (argc - optind) < 2 ){ fprintf(stderr, "%s: not enough arguments\n", argv[0]); usage(argv[0]); return 1; } numRows = atoi(*(argv+optind)); numCols = atoi(*(argv+optind+1)); if((numRows <= 0) || (numCols <= 0)){ fprintf(stderr, "%s: both number of rows and columns must be > 0\n", argv[0]); usage(argv[0]); return 1; } /* verbose output */ if( verboseFlg ){ fprintf(stderr, "%s: parameter values:\n", argv[0]); fprintf(stderr, "\ttype = %d, delta = %f\n", type, delta); if( type == 6 ){ fprintf(stderr, "\t mixing matrix:\n"); for(l=0; l < numCatRows; l++){ for(k=0; k < numCatCols; k++){ fprintf(stderr, "%f, ", mixing[l][k]); } fprintf(stderr, "\n"); } fprintf(stderr, "\n"); fprintf(stderr, "\t contributing matrix:\n"); for(l=0; l < numCatRows; l++){ for(k=0; k < numCatCols; k++){ fprintf(stderr, "%f, ", contrib[l][k]); } fprintf(stderr, "\n"); } } } /* get objects from stdin */ inFile = stdin; rowDoms = (WlzObject **) AlcMalloc(sizeof(WlzObject *) * numRows); for(i=0; i < numRows; i++){ if((rowDoms[i] = WlzReadObj(inFile, &errNum)) != NULL){ if((i == 0) || (objType == WLZ_EMPTY_OBJ)){ objType = rowDoms[i]->type; } if((rowDoms[i]->type != objType) && (rowDoms[i]->type != WLZ_EMPTY_OBJ)){ fprintf(stderr, "%s: invalid object type: %d\n", argv[0], rowDoms[i]->type); usage(argv[0]); return 1; } } else { fprintf(stderr, "%s: not enough row objects\n", argv[0]); } } /* now the column domains */ if( type == 5 ){ colDoms = (WlzObject **) AlcMalloc(sizeof(WlzObject *) * numCols * 2); for(i=0; i < numCols*2; i++){ if((colDoms[i] = WlzReadObj(inFile, &errNum)) != NULL){ if((colDoms[i]->type != objType) && (colDoms[i]->type != WLZ_EMPTY_OBJ)){ fprintf(stderr, "%s: invalid object type: %d\n", argv[0], colDoms[i]->type); usage(argv[0]); return 1; } } else { fprintf(stderr, "%s: not enough row objects\n", argv[0]); } } } else { colDoms = (WlzObject **) AlcMalloc(sizeof(WlzObject *) * numCols); for(i=0; i < numCols; i++){ if((colDoms[i] = WlzReadObj(inFile, &errNum)) != NULL){ if((colDoms[i]->type != objType) && (colDoms[i]->type != WLZ_EMPTY_OBJ)){ fprintf(stderr, "%s: invalid object type: %d\n", argv[0], colDoms[i]->type); usage(argv[0]); return 1; } } else { fprintf(stderr, "%s: not enough row objects\n", argv[0]); } } } /* now calculate the match values */ for(i=0; i < numRows; i ++){ if( verboseFlg ){ fprintf(stderr, "%s: start row %d\n", argv[0], i+1); } obj1 = rowDoms[i]; for(j=0; j < numCols; j++){ if( type == 5 ){ obj2 = colDoms[j*2]; obj3 = colDoms[j*2 + 1]; } else { obj2 = colDoms[j]; } switch( type ){ case 1: if((obj = WlzIntersect2(obj1, obj2, &errNum)) != NULL){ s1 = WlzSize(obj, &errNum); WlzFreeObj(obj); } else { s1 = 0; } if((obj = WlzUnion2(obj1, obj2, &errNum)) != NULL){ s2 = WlzSize(obj, &errNum); WlzFreeObj(obj); } else { s2 = 1; } matchVal = s1 / s2; break; case 2: if((obj = WlzIntersect2(obj1, obj2, &errNum)) != NULL){ s1 = WlzSize(obj, &errNum); WlzFreeObj(obj); } else { s1 = 0; } if((obj = WlzUnion2(obj1, obj2, &errNum)) != NULL){ s2 = WlzSize(obj, &errNum); WlzFreeObj(obj); } else { s2 = 1; } matchVal = s1 / s2; if( type == 2 ){ s1 = WlzSize(obj1, &errNum); s2 = WlzSize(obj2, &errNum); if( s2 > s1 ){ if( matchVal == 0.0 ){ matchVal = 10.0; } else { matchVal = 1.0 / matchVal; matchVal = WLZ_MIN(matchVal, 10.0); } } } break; case 3: if((obj = WlzIntersect2(obj1, obj2, &errNum)) != NULL){ s1 = WlzSize(obj, &errNum); WlzFreeObj(obj); } else { s1 = 0; } s2 = WlzSize(obj1, &errNum); matchVal = 0.0; if( s2 > 0 ){ matchVal = s1 / s2; } break; case 4: if((obj = WlzIntersect2(obj1, obj2, &errNum)) != NULL){ s1 = WlzSize(obj, &errNum); WlzFreeObj(obj); } else { s1 = 0; } s2 = WlzSize(obj2, &errNum); matchVal = 0.0; if( s2 > 0 ){ matchVal = s1 / s2; } break; case 5: /* this is a coparative measure designed to give a value of 1 to a random pattern and between zero and infinite for matches to one or the other. For analysis for clustering probably better to use the logarithm. Zero and infinite are delta and 1/delta. */ /* must be a third object */ if( obj3 == NULL ){ fprintf(stderr, "%s: for match option 5 3 input object required\n", argv[0]); return 1; } s1 = WlzSize(obj2, &errNum); if((obj = WlzIntersect2(obj1, obj2, &errNum)) != NULL){ s2 = WlzSize(obj, &errNum); WlzFreeObj(obj); } else { s2 = 0.0; } s3 = WlzSize(obj3, &errNum); if((obj = WlzIntersect2(obj1, obj3, &errNum)) != NULL){ s4 = WlzSize(obj, &errNum); WlzFreeObj(obj); } else { s4 = 0.0; } if((s1 < 0.0) || (s2 < 0.0) || (s3 < 0.0) || (s4 < 0.0)){ /* just fail */ fprintf(stderr, "%s: something gone wrong, negative size.\n", argv[0]); return 1; } /* calculating (s2/s1) * (s3/s4) */ /* if the denominator non-zero then simple formula */ if( s2 > 0.0 ){ if( s4 > 0.0 ){ matchVal = (s2/s1) * (s3/s4); } else { matchVal = s2 / s1; } } else { if( s4 > 0.0 ){ matchVal = s3/s4; } else { matchVal = 1.0; } } matchVal = WLZ_MAX(matchVal, delta); matchVal = WLZ_MIN(matchVal, 1.0/delta); break; case 6: /* this requires a mixing and contributing matrix and the images read in must have grey-values set to the right categories */ if((numCatRows == -1) || (numCatCols == -1) || (mixing == NULL) || (contrib == NULL)){ fprintf(stderr, "%s: bad matrix data\n", argv[0]); usage(argv[0]); return 1; } matchVal = WlzMixtureValue(obj1, obj2, numCatRows, numCatCols, mixing, contrib, &errNum); break; case 7: box1 = WlzBoundingBox3D(obj1, &errNum); box2 = WlzBoundingBox3D(obj2, &errNum); s1 = (box1.xMax + box1.xMin)/2.0 - (box2.xMax + box2.xMin)/2.0; s2 = (box1.yMax + box1.yMin)/2.0 - (box2.yMax + box2.yMin)/2.0; matchVal = sqrt(s1*s1 + s2*s2); break; case 8: if((obj = WlzIntersect2(obj1, obj2, &errNum)) != NULL){ s1 = WlzSize(obj, &errNum); WlzFreeObj(obj); } else { s1 = -1; } matchVal = s1; break; default: fprintf(stderr, "%s: invalid match type\n", argv[0]); usage(argv[0]); return 1; } /* print value */ fprintf(stdout, "%f", matchVal); if( (numCols - j) > 1 ){ fprintf(stdout, "\t"); } if( verboseFlg ){ fprintf(stderr, "."); } } fprintf(stdout, "\n"); if( verboseFlg ){ fprintf(stderr, "\n%s: completed row %d\n", argv[0], i+1); } } return 0; }
/*! * \return The value held in the given Java object. * \ingroup JWlz * \brief Returns a 2D array built from the given 2D java array. * \param jEnv Given JNI environment ptr. * \param cObjName The Java woolz C object class string. * \param jObjName The Java woolz Java object class * string. * \param jniObjName The Java woolz JNI object class string. * \param idrCnt Indirection count (ie 1 for *, 2 for * **, ...). * \param pKey Parameter key. * \param jWArray The Java Woolz array. * \param wArraySz The number of elements in the Java * Woolz array. * \param isCpy Destination pointer for JNI copy flag. */ jlong WlzJavaArray2DGet(JNIEnv *jEnv, char *cObjName, char *jObjName, char *jniObjName, int idrCnt, int pKey, jarray jWArray, WlzIVertex2 wArraySz, jboolean *isCpy) { int idY, ok = 0; void *bufJ; jobject aryJ1D; void **aryW2D = NULL; jlong rtnVal = 0; if(jWArray && (wArraySz.vtX > 0) && (wArraySz.vtY > 0)) { switch(pKey) { case WLZ_JPM_KEY_BYTE_ARY2: ok = AlcChar2Malloc((char ***)&aryW2D, wArraySz.vtY, wArraySz.vtX) == ALC_ER_NONE; if(ok) { idY = 0; while(ok && (idY < wArraySz.vtY)) { ok = ((aryJ1D = (*jEnv)->GetObjectArrayElement(jEnv, (jobjectArray )jWArray, idY)) != NULL) && ((bufJ = (void *)(*jEnv)->GetByteArrayElements(jEnv, (jbyteArray )aryJ1D, isCpy)) == NULL); if(ok) { (void )memcpy(*((WlzUByte **)aryW2D + idY), bufJ, wArraySz.vtX * sizeof(jbyte)); if(*isCpy) { (*jEnv)->ReleaseByteArrayElements(jEnv, (jbyteArray )aryJ1D, (jbyte *)bufJ, 0); *isCpy = JNI_FALSE; } } ++idY; } } break; case WLZ_JPM_KEY_SHORT_ARY2: ok = AlcShort2Malloc((short ***)&aryW2D, wArraySz.vtY, wArraySz.vtX) == ALC_ER_NONE; if(ok) { idY = 0; while(ok && (idY < wArraySz.vtY)) { ok = ((aryJ1D = (*jEnv)->GetObjectArrayElement(jEnv, (jobjectArray )jWArray, idY)) != NULL) && ((bufJ = (void *)(*jEnv)->GetShortArrayElements(jEnv, (jshortArray )aryJ1D, isCpy)) != NULL); if(ok) { (void )memcpy(*((WlzUByte **)aryW2D + idY), bufJ, wArraySz.vtX * sizeof(short)); if(*isCpy) { (*jEnv)->ReleaseShortArrayElements(jEnv, (jshortArray )aryJ1D, (jshort *)bufJ, 0); *isCpy = JNI_FALSE; } } ++idY; } } break; case WLZ_JPM_KEY_INT_ARY2: ok = AlcInt2Malloc((int ***)&aryW2D, wArraySz.vtY, wArraySz.vtX) == ALC_ER_NONE; if(ok) { idY = 0; while(ok && (idY < wArraySz.vtY)) { ok = ((aryJ1D = (*jEnv)->GetObjectArrayElement(jEnv, (jobjectArray )jWArray, idY)) != NULL) && ((bufJ = (void *)(*jEnv)->GetIntArrayElements(jEnv, (jintArray )aryJ1D, isCpy)) != NULL); if(ok) { (void )memcpy(*((WlzUByte **)aryW2D + idY), bufJ, wArraySz.vtX * sizeof(int)); if(*isCpy) { (*jEnv)->ReleaseIntArrayElements(jEnv, (jintArray )aryJ1D, (jint *)bufJ, 0); *isCpy = JNI_FALSE; } } ++idY; } } break; case WLZ_JPM_KEY_FLOAT_ARY2: ok = AlcFloat2Malloc((float ***)&aryW2D, wArraySz.vtY, wArraySz.vtX) == ALC_ER_NONE; if(ok) { idY = 0; while(ok && (idY < wArraySz.vtY)) { ok = ((aryJ1D = (*jEnv)->GetObjectArrayElement(jEnv, (jobjectArray )jWArray, idY)) != NULL) && ((bufJ = (void *)(*jEnv)->GetFloatArrayElements(jEnv, (jfloatArray )aryJ1D, isCpy)) != NULL); if(ok) { (void )memcpy(*((WlzUByte **)aryW2D + idY), bufJ, wArraySz.vtX * sizeof(float)); if(*isCpy) { (*jEnv)->ReleaseFloatArrayElements(jEnv, (jfloatArray )aryJ1D, (jfloat *)bufJ, 0); *isCpy = JNI_FALSE; } } ++idY; } } break; case WLZ_JPM_KEY_DOUBLE_ARY2: ok = AlcDouble2Malloc((double ***)&aryW2D, wArraySz.vtY, wArraySz.vtX) == ALC_ER_NONE; if(ok) { idY = 0; while(ok && (idY < wArraySz.vtY)) { ok = ((aryJ1D = (*jEnv)->GetObjectArrayElement(jEnv, (jobjectArray )jWArray, idY)) != NULL) && ((bufJ = (void *)(*jEnv)->GetDoubleArrayElements(jEnv, (jdoubleArray )aryJ1D, isCpy)) != NULL); if(ok) { (void )memcpy(*((WlzUByte **)aryW2D + idY), bufJ, wArraySz.vtX * sizeof(double)); if(*isCpy) { (*jEnv)->ReleaseDoubleArrayElements(jEnv, (jdoubleArray )aryJ1D, (jdouble *)bufJ, 0); *isCpy = JNI_FALSE; } } ++idY; } } break; default: break; } if(ok) { rtnVal = (jlong )aryW2D; *isCpy = JNI_TRUE; } else if(aryW2D) { Alc2Free(aryW2D); } } return(rtnVal); }
/*! * \return New Woolz domain object with gradient grey values or NULL on * error. * \ingroup WlzValuesFilters * \brief Computes the magnitude of the gray values in the * 3 given Woolz 2D domain objects. * \param srcObj0 First object. * \param srcObj1 Second object. * \param srcObj2 Third object. * \param dstErr Destination error pointer, may be null. */ static WlzObject *WlzGreyMagnitude2D3(WlzObject *srcObj0, WlzObject *srcObj1, WlzObject *srcObj2, WlzErrorNum *dstErr) { int idN, itvLen, bufSz; double **iBufA = NULL; WlzObject *tObj0, *istObj = NULL, *dstObj = NULL; WlzObject *iObjA[3], *tObjA[3]; WlzGreyP tGP0; WlzGreyType dstGType = WLZ_GREY_ERROR; WlzGreyType gTypeA[3]; WlzPixelV dstBgd; WlzPixelV bgdA[3]; WlzValues dstVal; WlzIntervalWSpace dstIWSp; WlzGreyWSpace dstGWSp; WlzIntervalWSpace iIWSpA[3]; WlzGreyWSpace iGWSpA[3]; WlzErrorNum errNum = WLZ_ERR_NONE; *(iObjA + 0) = *(iObjA + 1) = *(iObjA + 2) = NULL; /* Check source objects. */ if((srcObj0 == NULL) || (srcObj1 == NULL) ||(srcObj2 == NULL)) { errNum = WLZ_ERR_OBJECT_NULL;; } else if((srcObj0->type != WLZ_2D_DOMAINOBJ) || (srcObj1->type != WLZ_2D_DOMAINOBJ) || (srcObj2->type != WLZ_2D_DOMAINOBJ)) { errNum = WLZ_ERR_OBJECT_TYPE; } else if((srcObj0->domain.core == NULL) || (srcObj1->domain.core == NULL) || (srcObj2->domain.core == NULL)) { errNum = WLZ_ERR_DOMAIN_NULL; } else if((srcObj0->values.core == NULL) || (srcObj1->values.core == NULL) || (srcObj2->values.core == NULL)) { errNum = WLZ_ERR_VALUES_NULL; } /* Compute the intersection of the source objects. */ if(errNum == WLZ_ERR_NONE) { *(tObjA + 0) = srcObj0; *(tObjA + 1) = srcObj1; *(tObjA + 2) = srcObj2; istObj = WlzIntersectN(3, tObjA, 0, &errNum); } if(errNum == WLZ_ERR_NONE) { *(iObjA + 0) = WlzMakeMain(WLZ_2D_DOMAINOBJ, istObj->domain, srcObj0->values, NULL, NULL, &errNum); } if(errNum == WLZ_ERR_NONE) { *(iObjA + 1) = WlzMakeMain(WLZ_2D_DOMAINOBJ, istObj->domain, srcObj1->values, NULL, NULL, &errNum); } if(errNum == WLZ_ERR_NONE) { *(iObjA + 2) = WlzMakeMain(WLZ_2D_DOMAINOBJ, istObj->domain, srcObj2->values, NULL, NULL, &errNum); } /* Get background value and grey types */ idN = 0; while((errNum == WLZ_ERR_NONE) && (idN < 3)) { tObj0 = *(iObjA + idN); *(gTypeA + idN) = WlzGreyTableTypeToGreyType(tObj0->values.core->type, &errNum); if(errNum == WLZ_ERR_NONE) { *(bgdA + idN) = WlzGetBackground(tObj0, &errNum); } ++idN; } /* Promote grey types. */ if(errNum == WLZ_ERR_NONE) { if((*(gTypeA + 0) == WLZ_GREY_DOUBLE) || (*(gTypeA + 1) == WLZ_GREY_DOUBLE) || (*(gTypeA + 2) == WLZ_GREY_DOUBLE)) { dstGType = WLZ_GREY_DOUBLE; } else if((*(gTypeA + 0) == WLZ_GREY_FLOAT) || (*(gTypeA + 1) == WLZ_GREY_FLOAT) || (*(gTypeA + 2) == WLZ_GREY_FLOAT)) { dstGType = WLZ_GREY_FLOAT; } else if((*(gTypeA + 0) == WLZ_GREY_INT) || (*(gTypeA + 1) == WLZ_GREY_INT) || (*(gTypeA + 2) == WLZ_GREY_INT)) { dstGType = WLZ_GREY_INT; } else if((*(gTypeA + 0) == WLZ_GREY_SHORT) || (*(gTypeA + 1) == WLZ_GREY_SHORT) || (*(gTypeA + 2) == WLZ_GREY_SHORT)) { dstGType = WLZ_GREY_SHORT; } else if((*(gTypeA + 0) == WLZ_GREY_UBYTE) || (*(gTypeA + 1) == WLZ_GREY_UBYTE) || (*(gTypeA + 2) == WLZ_GREY_UBYTE)) { dstGType = WLZ_GREY_SHORT; } else { /* RGBA to be done RAB */ errNum = WLZ_ERR_GREY_TYPE; } } /* Make destination object with intersection domain and new values. */ if(errNum == WLZ_ERR_NONE) { (void )WlzValueConvertPixel(&dstBgd, *(bgdA + 0), dstGType); dstVal.v = WlzNewValueTb(*(iObjA + 0), WlzGreyValueTableType(0, WLZ_GREY_TAB_RAGR, dstGType, NULL), dstBgd, &errNum); if(errNum == WLZ_ERR_NONE) { dstObj = WlzMakeMain(WLZ_2D_DOMAINOBJ, istObj->domain, dstVal, NULL, NULL, &errNum); } } if(istObj) { WlzFreeObj(istObj); } /* Make buffers. */ if(errNum == WLZ_ERR_NONE) { bufSz = dstObj->domain.i->lastkl - dstObj->domain.i->kol1 + 1; if(AlcDouble2Malloc(&iBufA, 3, bufSz) != ALC_ER_NONE) { errNum = WLZ_ERR_MEM_ALLOC; } } /* Scan through the objects computing the magnitude. */ if(errNum == WLZ_ERR_NONE) { idN = 0; while((errNum == WLZ_ERR_NONE) && (idN < 3)) { errNum = WlzInitGreyScan(*(iObjA + idN), iIWSpA + idN, iGWSpA + idN); ++idN; } if(errNum == WLZ_ERR_NONE) { errNum = WlzInitGreyScan(dstObj, &dstIWSp, &dstGWSp); } while((errNum == WLZ_ERR_NONE) && ((errNum = WlzNextGreyInterval(iIWSpA + 0)) == WLZ_ERR_NONE) && ((errNum = WlzNextGreyInterval(iIWSpA + 1)) == WLZ_ERR_NONE) && ((errNum = WlzNextGreyInterval(iIWSpA + 2)) == WLZ_ERR_NONE) && ((errNum = WlzNextGreyInterval(&dstIWSp)) == WLZ_ERR_NONE)) { itvLen = dstIWSp.rgtpos - dstIWSp.lftpos + 1; /* Copy intervals to double buffers. */ idN = 0; while(idN < 3) { tGP0.dbp = *(iBufA + idN); WlzValueCopyGreyToGrey(tGP0, 0, WLZ_GREY_DOUBLE, (iGWSpA + idN)->u_grintptr, 0, (iGWSpA + idN)->pixeltype, itvLen); ++idN; } /* Compute magnitude. */ WlzBufMagD3(*(iBufA + 0), *(iBufA + 1), *(iBufA + 2), itvLen); /* Clamp into destination interval. */ tGP0.dbp = *(iBufA + 0); WlzValueClampGreyIntoGrey(dstGWSp.u_grintptr, 0, dstGWSp.pixeltype, tGP0, 0, WLZ_GREY_DOUBLE, itvLen); } if(errNum == WLZ_ERR_EOO) { errNum = WLZ_ERR_NONE; } } /* Free intersection objects. */ idN = 0; while(idN < 3) { if(iObjA[idN]) { WlzFreeObj(iObjA[idN]); } ++idN; } /* Free buffers. */ if(iBufA) { Alc2Free((void **)iBufA); } /* Tidy up on error. */ if(dstObj && (errNum != WLZ_ERR_NONE)) { WlzFreeObj(dstObj); dstObj = NULL; } /* Pass back error status. */ if(dstErr) { *dstErr = errNum; } return(dstObj); }