/*! * - Function: WlzEffWriteMeshTransform3DWithoutDisplacementVTK * - Returns: none * - Purpose: output the orginal mesh. * - Parameters: * -# *fp: pointer pointing to a specific file. * -# wmt3D: mesh transform. * - Author: J. Rao, R. Baldock and B. Hill */ static void OutPutSectionsForTest( WlzObject *WObjS, WlzObject *WObj2D, /* FILE *outFile; */ char *outFileStr, int iloop, WlzErrorNum *errNum){ WlzGreyValueWSpace *gVWSp; WlzErrorNum wErrN = WLZ_ERR_NONE; WlzGMModel *gM; WlzGMShell *cS, *fS; WlzGMLoopT *cLT, *fLT; WlzGMEdgeT *cET, *fET; WlzDVertex2 *v1, *v2; WlzIBox2 bBoxT; WlzObject *tObj; int ix, jy, kt, intensity; if( ( (WObjS->type) != WLZ_CONTOUR ) ) { wErrN = WLZ_ERR_DOMAIN_TYPE; } if( ( WObj2D->type != WLZ_2D_DOMAINOBJ ) ) { wErrN = WLZ_ERR_DOMAIN_TYPE; } if( ( WObj2D->domain.core == NULL ) ) { wErrN = WLZ_ERR_DOMAIN_NULL; } if(wErrN == WLZ_ERR_NONE) { /* copy it */ tObj = WlzCopyObject(WObj2D, &wErrN); if( wErrN != WLZ_ERR_NONE) { printf("Something wrong with when copy obj "); exit(1); } /* get the bounding box */ bBoxT = WlzBoundingBox2I(tObj, &wErrN); if( wErrN != WLZ_ERR_NONE) { printf("Something wrong with getting bounding box of obj "); exit(1); } /* revise the grey value of it */ gVWSp = WlzGreyValueMakeWSp(tObj, &wErrN); if( wErrN != WLZ_ERR_NONE) { printf("Something wrong with when make warped obj Wsp"); exit(1); } for(ix = bBoxT.xMin; ix <bBoxT.xMax; ix++) { kt = 0; for(jy= bBoxT.yMin; jy< bBoxT.yMax; jy++) { /* GetGreyValue(gVWSp, 0, jy, ix, &intensity, &errNum); */ intensity = 255; FillGreyValue(gVWSp, 0, jy, ix, intensity, &wErrN); } } } /* visualize the data: */ if( wErrN == WLZ_ERR_NONE) { iloop = 1; kt = 0; gM = WObjS->domain.ctr->model; /* For each shell of the model. */ cS = fS = (WlzGMShell *) gM->child; do { printf("Shell %d Shell index %d\n", kt, cS->idx); /* For each loop topology element of the model. */ cLT = fLT = (WlzGMLoopT *) cS->child; kt++; do { printf("Loop index %d\n", cLT->idx); /* For each edge topology element of the model. */ cET = fET = cLT->edgeT; do { if(cET == cET->edge->edgeT) /* Print edge end points */ { v1 = (WlzDVertex2 *) cET->vertexT->diskT->vertex->geo.vg2D; v2 = (WlzDVertex2 *) cET->opp->vertexT->diskT->vertex->geo.vg2D; /* printf("%lg %lg\n",cET->vertexT->diskT->vertex->geo.vg2D->vtx.vtX, \ cET->vertexT->diskT->vertex->geo.vg2D->vtx.vtY ); printf("%lg %lg\n",cET->opp->vertexT->diskT->vertex->geo.vg2D->vtx.vtX, \ cET->opp->vertexT->diskT->vertex->geo.vg2D->vtx.vtY ); */ /* if(kt%3 == iloop) */ { ix = (int) cET->vertexT->diskT->vertex->geo.vg2D->vtx.vtX; jy = (int) cET->vertexT->diskT->vertex->geo.vg2D->vtx.vtY; printf("idx %d ix %d iy %d\n", cET->vertexT->diskT->vertex->idx, ix, jy); intensity = kt * 40; FillGreyValue(gVWSp, 0, jy, ix, intensity, &wErrN); ix = (int) cET->opp->vertexT->diskT->vertex->geo.vg2D->vtx.vtX; jy = (int) cET->opp->vertexT->diskT->vertex->geo.vg2D->vtx.vtY; printf("idx %d ix %d iy %d\n", cET->opp->vertexT->diskT->vertex->idx, ix, jy); FillGreyValue(gVWSp, 0, jy, ix, intensity, &wErrN); } } cET = cET->next; } while (cET != fET); cLT = cLT->next; } while(cLT != fLT); cS = cS->next; } while(cS != fS ); } if((outFile = fopen(outFileStr, "w")) == NULL ) { printf("cannot open the output woolz file.\n"); exit(1); } if( ( wErrN = WlzWriteObj(outFile, tObj) ) != WLZ_ERR_NONE ) { printf("output Woolz Object Error.\n"); fclose(outFile); exit(1); } fclose(outFile); outFile = NULL; WlzGreyValueFreeWSp(gVWSp); }
/*! * \return Affine transform which brings the two objects into register. * \ingroup WlzRegistration * \brief Registers the two given 2D domain objects using a * frequency domain cross correlation. An affine transform * is computed, which when applied to the source object * takes it into register with the target object. * A resolution pyramid is built from the given objects * and used to register the objects, progressing from * a low resolution towards the full resolution objects. * \param tObj The target object. Must have * been assigned. * \param sObj The source object to be * registered with target object. * \param initTr Initial affine transform * to be applied to the source * object prior to registration. * Only translations in x and y * and rotation about the z axis * are used. May be NULL which is * equivalent to an identity transform. * \param trType Required transform type. * \param maxTran Maximum translation. * \param maxRot Maximum rotation. * \param maxItr Maximum number of iterations, * if \f$leq\f$ 0 then infinite iterations * are allowed. * \param winFn Window function. * \param noise Use Gaussian noise if non-zero. * \param dstConv Destination ptr for the * convergence flag (non zero * on convergence), may be NULL. * \param dstCCor Destination ptr for the cross * correlation value, may be NULL. * \param dstErr Destination error pointer, * may be NULL. */ static WlzAffineTransform *WlzRegCCorObjs2D(WlzObject *tObj, WlzObject *sObj, WlzAffineTransform *initTr, WlzTransformType trType, WlzDVertex2 maxTran, double maxRot, int maxItr, WlzWindowFnType winFn, int noise, int *dstConv, double *dstCCor, WlzErrorNum *dstErr) { int tI1, samIdx, nSam, conv; double cCor, rot0, rot1, sMaxRot; WlzPixelV gV[4]; WlzIVertex2 tIV0, tIV1; WlzIVertex3 samFacV; WlzDVertex2 tran0, tran1, sMaxTran; WlzIBox2 sBox, tBox; int *samFac = NULL; WlzObject **sTObj = NULL, **sSObj = NULL; WlzAffineTransform *samRegTr0 = NULL, *samRegTr1 = NULL, *regTr = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; WlzAffineTransformPrim trPrim; WlzPixelV zeroBgd; const int samFacStep = 4, maxSam = 16, minSamSz = 100; zeroBgd.type = WLZ_GREY_INT; zeroBgd.v.inv = 0; gV[0].type = gV[1].type = gV[2].type = gV[3].type = WLZ_GREY_DOUBLE; /* Compute the number of x4 subsampling operations to use. */ sBox = WlzBoundingBox2I(sObj, &errNum); if(errNum == WLZ_ERR_NONE) { tBox = WlzBoundingBox2I(tObj, &errNum); } if(errNum == WLZ_ERR_NONE) { tIV0.vtX = sBox.xMax - sBox.xMin + 1; tIV0.vtY = sBox.yMax - sBox.yMin + 1; tIV1.vtX = tBox.xMax - tBox.xMin + 1; tIV1.vtY = tBox.yMax - tBox.yMin + 1; tIV0.vtX = WLZ_MIN(tIV0.vtX, tIV1.vtX); tIV0.vtY = WLZ_MIN(tIV0.vtY, tIV1.vtY); nSam = 1; tI1 = WLZ_MIN(tIV0.vtX, tIV0.vtY); while((nSam < maxSam) && (tI1 > minSamSz)) { ++nSam; tI1 /= samFacStep; } } /* Allocate space for subsampled objects. */ if(errNum == WLZ_ERR_NONE) { if(((samFac = (int *)AlcMalloc(nSam * sizeof(int))) == NULL) || ((sTObj = (WlzObject **)AlcCalloc(nSam, sizeof(WlzObject *))) == NULL) || ((sSObj = (WlzObject **)AlcCalloc(nSam, sizeof(WlzObject *))) == NULL)) { errNum = WLZ_ERR_MEM_ALLOC; } } /* Compute subsampled objects and make sure the background value is zero. */ if(errNum == WLZ_ERR_NONE) { samIdx = 0; *samFac = 1; samFacV.vtX = samFacV.vtY = samFacStep; *(sTObj + 0) = WlzAssignObject(tObj, NULL); *(sSObj + 0) = WlzAssignObject(sObj, NULL); while((errNum == WLZ_ERR_NONE) && (++samIdx < nSam)) { *(samFac + samIdx) = *(samFac + samIdx - 1) * samFacStep; *(sTObj + samIdx) = WlzAssignObject( WlzSampleObj(*(sTObj + samIdx - 1), samFacV, WLZ_SAMPLEFN_GAUSS, &errNum), NULL); if(errNum == WLZ_ERR_NONE) { (void )WlzSetBackground(*(sTObj + samIdx), zeroBgd); *(sSObj + samIdx) = WlzAssignObject( WlzSampleObj(*(sSObj + samIdx - 1), samFacV, WLZ_SAMPLEFN_GAUSS, &errNum), NULL); } if(errNum == WLZ_ERR_NONE) { (void )WlzSetBackground(*(sSObj + samIdx), zeroBgd); } } } /* Register the subsampled objects starting with the lowest resolution * (highest subsampling) and progressing to the unsampled objects. */ if(errNum == WLZ_ERR_NONE) { if(initTr == NULL) { rot0 = 0.0; tran0.vtX = 0.0; tran0.vtY = 0.0; } else { errNum = WlzAffineTransformPrimGet(initTr, &trPrim); rot0 = trPrim.theta; tran0.vtX = trPrim.tx; tran0.vtY = trPrim.ty; } conv = 1; samIdx = nSam - 1; sMaxRot = maxRot; sMaxTran.vtX = maxTran.vtX / *(samFac + nSam - 1); sMaxTran.vtY = maxTran.vtY / *(samFac + nSam - 1); while((errNum == WLZ_ERR_NONE) && conv && (samIdx >= 0)) { /* Compute initial transform. */ rot1 = rot0; tran1.vtX = tran0.vtX / *(samFac + samIdx); tran1.vtY = tran0.vtY / *(samFac + samIdx); samRegTr0 = WlzAffineTransformFromPrimVal(WLZ_TRANSFORM_2D_AFFINE, tran1.vtX, tran1.vtY, 0.0, 1.0, rot1, 0.0, 0.0, 0.0, 0.0, 0, &errNum); /* Compute registration transform. */ if(errNum == WLZ_ERR_NONE) { samRegTr1 = WlzRegCCorObjs2D1(*(sTObj + samIdx), *(sSObj + samIdx), samRegTr0, trType, sMaxTran, sMaxRot, maxItr, winFn, noise, &conv, &cCor, &errNum); } if(samRegTr0) { (void )WlzFreeAffineTransform(samRegTr0); samRegTr0 = NULL; } if(samRegTr1) { samRegTr0 = samRegTr1; samRegTr1 = NULL; } if(errNum == WLZ_ERR_NONE) { errNum = WlzAffineTransformPrimGet(samRegTr0, &trPrim); } if(errNum == WLZ_ERR_NONE) { rot0 = trPrim.theta; tran0.vtX = trPrim.tx * *(samFac + samIdx); tran0.vtY = trPrim.ty * *(samFac + samIdx); } /* Set registration limits. */ sMaxRot = WLZ_M_PI / 24.0; sMaxTran.vtX = samFacStep * 3; sMaxTran.vtY = samFacStep * 3; --samIdx; } } if(errNum == WLZ_ERR_NONE) { if(dstConv) { *dstConv = conv; } if(dstCCor) { *dstCCor = cCor; } regTr = samRegTr0; } AlcFree(samFac); /* Free subsampled objects. */ if(sTObj) { for(samIdx = 0; samIdx < nSam; ++samIdx) { (void )WlzFreeObj(*(sTObj + samIdx)); } AlcFree(sTObj); } if(sSObj) { for(samIdx = 0; samIdx < nSam; ++samIdx) { (void )WlzFreeObj(*(sSObj + samIdx)); } AlcFree(sSObj); } if(dstErr) { *dstErr = errNum; } return(regTr); }
/*! * \return Translation. * \ingroup WlzRegistration * \brief Registers the given 2D domain objects using a * frequency domain cross correlation, to find * the translation which has the highest cross * correlation value. * \param tObj The target object. Must have * been assigned. * \param sObj The source object to be * registered with target object. * Must have been assigned. * \param initTr Initial affine transform * to be applied to the source * object prior to registration. * \param maxTran Maximum translation. * \param maxTran Maximum translation. * \param winFn Window function. * \param noise Use Gaussian noise if non-zero. * \param dstCCor Destination ptr for the cross * correlation value, may be NULL. * \param dstErr Destination error pointer, * may be NULL. */ static WlzDVertex2 WlzRegCCorObjs2DTran(WlzObject *tObj, WlzObject *sObj, WlzAffineTransform *initTr, WlzDVertex2 maxTran, WlzWindowFnType winFn, int noise, double *dstCCor, WlzErrorNum *dstErr) { int oIdx; double cCor = 0.0; double sSq[2]; double **oAr[2]; WlzIBox2 aBox; WlzIBox2 oBox[2], pBox[2]; WlzIVertex2 aSz, aOrg, centre, radius, tran; WlzDVertex2 dstTran; WlzObject *oObj[2], *pObj[2]; WlzErrorNum errNum = WLZ_ERR_NONE; dstTran.vtX = 0.0; dstTran.vtY = 0.0; oAr[0] = oAr[1] = NULL; oObj[0] = oObj[1] = NULL; pObj[0] = pObj[1] = NULL; oObj[0] = WlzAssignObject(tObj, NULL); /* Transform source object. */ if((initTr == NULL) || WlzAffineTransformIsIdentity(initTr, NULL)) { oObj[1] = WlzAssignObject(sObj, NULL); } else { oObj[1] = WlzAssignObject( WlzAffineTransformObj(sObj, initTr, WLZ_INTERPOLATION_NEAREST, &errNum), NULL); } /* Preprocess the objects. */ if(errNum == WLZ_ERR_NONE) { oIdx = 0; while((errNum == WLZ_ERR_NONE) && (oIdx < 2)) { oBox[oIdx] = WlzBoundingBox2I(oObj[oIdx], &errNum); ++oIdx; } } if(errNum == WLZ_ERR_NONE) { oIdx = 0; while((errNum == WLZ_ERR_NONE) && (oIdx < 2)) { centre.vtX = (oBox[oIdx].xMin + oBox[oIdx].xMax) / 2; centre.vtY = (oBox[oIdx].yMin + oBox[oIdx].yMax) / 2; radius.vtX = (oBox[oIdx].xMax - oBox[oIdx].xMin) / 2; radius.vtY = (oBox[oIdx].yMax - oBox[oIdx].yMin) / 2; pObj[oIdx] = WlzAssignObject( WlzRegCCorPProcessObj2D(oObj[oIdx], winFn, centre, radius, &errNum), NULL); ++oIdx; } } /* Create double arrays. */ if(errNum == WLZ_ERR_NONE) { oIdx = 0; while((errNum == WLZ_ERR_NONE) && (oIdx < 2)) { pBox[oIdx] = WlzBoundingBox2I(pObj[oIdx], &errNum); ++oIdx; } } if(errNum == WLZ_ERR_NONE) { aBox.xMin = WLZ_MIN(pBox[0].xMin, pBox[1].xMin) - (int )(maxTran.vtX) + 1; aBox.yMin = WLZ_MIN(pBox[0].yMin, pBox[1].yMin) - (int )(maxTran.vtY) + 1; aBox.xMax = WLZ_MAX(pBox[0].xMax, pBox[1].xMax) + (int )(maxTran.vtX) + 1; aBox.yMax = WLZ_MAX(pBox[0].yMax, pBox[1].yMax) + (int )(maxTran.vtY) + 1; aOrg.vtX = aBox.xMin; aOrg.vtY = aBox.yMin; aSz.vtX = aBox.xMax - aBox.xMin + 1; aSz.vtY = aBox.yMax - aBox.yMin + 1; (void )AlgBitNextPowerOfTwo((unsigned int *)&(aSz.vtX), aSz.vtX); (void )AlgBitNextPowerOfTwo((unsigned int *)&(aSz.vtY), aSz.vtY); oIdx = 0; while((errNum == WLZ_ERR_NONE) && (oIdx < 2)) { errNum = WlzToArray2D((void ***)&(oAr[oIdx]), pObj[oIdx], aSz, aOrg, noise, WLZ_GREY_DOUBLE); ++oIdx; } } if((dstCCor != NULL) && (errNum == WLZ_ERR_NONE)) { oIdx = 0; while((errNum == WLZ_ERR_NONE) && (oIdx < 2)) { WlzArrayStats2D((void **)(oAr[oIdx]), aSz, WLZ_GREY_DOUBLE, NULL, NULL, NULL, &(sSq[oIdx]), NULL, NULL); ++oIdx; } } #ifdef WLZ_REGCCOR_DEBUG if(errNum == WLZ_ERR_NONE) { FILE *fP = NULL; WlzObject *cCObjT = NULL; cCObjT = WlzFromArray2D((void **)(oAr[0]), aSz, aOrg, WLZ_GREY_DOUBLE, WLZ_GREY_DOUBLE, 0.0, 1.0, 0, 0, &errNum); if(cCObjT) { if((fP = fopen("oObjT0.wlz", "w")) != NULL) { (void )WlzWriteObj(fP, cCObjT); (void )fclose(fP); } (void )WlzFreeObj(cCObjT); } } if(errNum == WLZ_ERR_NONE) { FILE *fP = NULL; WlzObject *cCObjT = NULL; cCObjT = WlzFromArray2D((void **)(oAr[1]), aSz, aOrg, WLZ_GREY_DOUBLE, WLZ_GREY_DOUBLE, 0.0, 1.0, 0, 0, &errNum); if(cCObjT) { if((fP = fopen("oObjT1.wlz", "w")) != NULL) { (void )WlzWriteObj(fP, cCObjT); (void )fclose(fP); } (void )WlzFreeObj(cCObjT); } } #endif /* WLZ_REGCCOR_DEBUG */ /* Cross correlate. */ if(errNum == WLZ_ERR_NONE) { (void )AlgCrossCorrelate2D(oAr[0], oAr[1], aSz.vtX, aSz.vtY); AlgCrossCorrPeakXY(&(tran.vtX), &(tran.vtY), &cCor, oAr[0], aSz.vtX, aSz.vtY, maxTran.vtX, maxTran.vtY); } #ifdef WLZ_REGCCOR_DEBUG if(errNum == WLZ_ERR_NONE) { FILE *fP = NULL; WlzObject *cCObjT = NULL; oIdx = 0; while((errNum == WLZ_ERR_NONE) && (oIdx < 2)) { (void )WlzGreyStats(pObj[oIdx], NULL, NULL, NULL, NULL, &(sSq[oIdx]), NULL, NULL, &errNum); ++oIdx; } if(errNum == WLZ_ERR_NONE) { cCObjT = WlzFromArray2D((void **)(oAr[0]), aSz, aOrg, WLZ_GREY_DOUBLE, WLZ_GREY_DOUBLE, 0.0, 255.0 / (1.0 + (sqrt(sSq[0] * sSq[1]) * aSz.vtX * aSz.vtY)), 0, 0, &errNum); } if(cCObjT) { if((fP = fopen("cCObjT.wlz", "w")) != NULL) { (void )WlzWriteObj(fP, cCObjT); (void )fclose(fP); } (void )WlzFreeObj(cCObjT); } } #endif /* WLZ_REGCCOR_DEBUG */ for(oIdx = 0; oIdx < 2; ++oIdx) { (void )WlzFreeObj(oObj[oIdx]); (void )WlzFreeObj(pObj[oIdx]); AlcDouble2Free(oAr[oIdx]); } if(errNum == WLZ_ERR_NONE) { dstTran.vtX = tran.vtX; dstTran.vtY = tran.vtY; if(dstCCor) { cCor = cCor / (1.0 + (sqrt(sSq[0] * sSq[1]) * aSz.vtX * aSz.vtY)); *dstCCor = cCor; } } if(dstErr) { *dstErr = errNum; } return(dstTran); }
/*! * \return Angle of roatation. * \ingroup WlzRegistration * \brief Polar samples then registers the given 2D domain objects * using a frequency domain cross correlation, to find * the angle of rotation about the given centre of rotation * which has the highest cross correlation value. * The rotation is always about the objects cente of mass. * \param tObj The target object. Must have * been assigned. * \param sObj The source object to be * registered with target object. * Must have been assigned. * \param initTr Initial affine transform * to be applied to the source * object prior to registration. * \param maxRot Maximum rotation. * \param winFn Window function. * \param noise Use Gaussian noise if non-zero. * \param dstErr Destination error pointer, * may be NULL. */ static double WlzRegCCorObjs2DRot(WlzObject *tObj, WlzObject *sObj, WlzAffineTransform *initTr, double maxRot, WlzWindowFnType winFn, int noise, WlzErrorNum *dstErr) { int oIdx, angCnt; double angInc, dstRot = 0.0; WlzIBox2 aBox; WlzIBox2 oBox[2]; WlzIVertex2 rot, aSz, aOrg, winRad, winOrg, rotPad, rotCentreI; double **oAr[2]; WlzObject *oObj[2], *pObj[2], *wObj[2]; WlzErrorNum errNum = WLZ_ERR_NONE; const int rotCnt = 500; const double distInc = 1.0; /* Assign the target and transform source objects. */ oAr[0] = oAr[1] = NULL; oAr[0] = oAr[1] = NULL; oObj[0] = oObj[1] = NULL; pObj[0] = pObj[1] = NULL; wObj[0] = wObj[1] = NULL; oObj[0] = WlzAssignObject(tObj, NULL); if((initTr == NULL) || WlzAffineTransformIsIdentity(initTr, NULL)) { oObj[1] = WlzAssignObject(sObj, NULL); } else { oObj[1] = WlzAssignObject( WlzAffineTransformObj(sObj, initTr, WLZ_INTERPOLATION_NEAREST, &errNum), NULL); } if(errNum == WLZ_ERR_NONE) { oIdx = 0; while((errNum == WLZ_ERR_NONE) && (oIdx < 2)) { tObj = WlzAutoCor(oObj[oIdx], &errNum); (void )WlzFreeObj(oObj[oIdx]); oObj[oIdx] = WlzAssignObject(tObj, NULL); ++oIdx; } } /* Compute rectangular to polar transformation. */ if(errNum == WLZ_ERR_NONE) { angInc = (2.0 * (maxRot + WLZ_M_PI)) / rotCnt; angCnt = (2.0 * WLZ_M_PI) / angInc; oIdx = 0; while((errNum == WLZ_ERR_NONE) && (oIdx < 2)) { rotCentreI.vtX = 0; rotCentreI.vtY = 0; if(errNum == WLZ_ERR_NONE) { pObj[oIdx] = WlzAssignObject( WlzPolarSample(oObj[oIdx], rotCentreI, angInc, distInc, angCnt, 0, &errNum), NULL); } ++oIdx; } } /* Preproccess the objects using windows or noise. */ if(errNum == WLZ_ERR_NONE) { oIdx = 0; while((errNum == WLZ_ERR_NONE) && (oIdx < 2)) { oBox[oIdx] = WlzBoundingBox2I(pObj[oIdx], &errNum); if(errNum == WLZ_ERR_NONE) { winOrg.vtX = (oBox[oIdx].xMax + oBox[oIdx].xMin) / 2; winOrg.vtY = (oBox[oIdx].yMax + oBox[oIdx].yMin) / 2; winRad.vtX = (oBox[oIdx].xMax - oBox[oIdx].xMin) / 2; winRad.vtY = (oBox[oIdx].yMax - oBox[oIdx].yMin) / 2; wObj[oIdx] = WlzAssignObject( WlzRegCCorPProcessObj2D(pObj[oIdx], winFn, winOrg, winRad, &errNum), NULL); } ++oIdx; } } /* Create 2D double arrays from the polar sampled objects. */ if(errNum == WLZ_ERR_NONE) { oIdx = 0; while((errNum == WLZ_ERR_NONE) && (oIdx < 2)) { oBox[oIdx] = WlzBoundingBox2I(wObj[oIdx], &errNum); ++oIdx; } } if(errNum == WLZ_ERR_NONE) { aBox.xMin = WLZ_MIN(oBox[0].xMin, oBox[1].xMin); aBox.yMin = WLZ_MIN(oBox[0].yMin, oBox[1].yMin); aBox.xMax = WLZ_MAX(oBox[0].xMax, oBox[1].xMax); aBox.yMax = WLZ_MAX(oBox[0].yMax, oBox[1].yMax); rotPad.vtX = (aBox.xMax - aBox.xMin) / 2; rotPad.vtY = 1 + WLZ_NINT(maxRot / angInc); aBox.yMin -= rotPad.vtY; aBox.yMax += rotPad.vtY; aOrg.vtX = aBox.xMin; aOrg.vtY = aBox.yMin; aSz.vtX = aBox.xMax - aBox.xMin + 1; aSz.vtY = aBox.yMax - aBox.yMin + 1; (void )AlgBitNextPowerOfTwo((unsigned int *)&(aSz.vtX), aSz.vtX); (void )AlgBitNextPowerOfTwo((unsigned int *)&(aSz.vtY), aSz.vtY); oIdx = 0; while((errNum == WLZ_ERR_NONE) && (oIdx < 2)) { errNum = WlzToArray2D((void ***)&(oAr[oIdx]), wObj[oIdx], aSz, aOrg, noise, WLZ_GREY_DOUBLE); ++oIdx; } } #ifdef WLZ_REGCCOR_DEBUG if(errNum == WLZ_ERR_NONE) { FILE *fP = NULL; WlzObject *aObj = NULL; aObj = WlzFromArray2D((void **)(oAr[0]), aSz, aOrg, WLZ_GREY_DOUBLE, WLZ_GREY_DOUBLE, 0.0, 1.0, 0, 0, &errNum); if((fP = fopen("cObjR0.wlz", "w")) != NULL) { (void )WlzWriteObj(fP, aObj); (void )fclose(fP); } WlzFreeObj(aObj); aObj = WlzFromArray2D((void **)(oAr[1]), aSz, aOrg, WLZ_GREY_DOUBLE, WLZ_GREY_DOUBLE, 0.0, 1.0, 0, 0, &errNum); if((fP = fopen("cObjR1.wlz", "w")) != NULL) { (void )WlzWriteObj(fP, aObj); (void )fclose(fP); } WlzFreeObj(aObj); } #endif /* WLZ_REGCCOR_DEBUG */ /* Cross correlate. */ if(errNum == WLZ_ERR_NONE) { (void )AlgCrossCorrelate2D(oAr[0], oAr[1], aSz.vtX, aSz.vtY); AlgCrossCorrPeakXY(&(rot.vtX), &(rot.vtY), NULL, oAr[0], aSz.vtX, aSz.vtY, rotPad.vtX, rotPad.vtY); dstRot = rot.vtY * angInc; /* dstRot = -(rot.vtY) * angInc; */ } #ifdef WLZ_REGCCOR_DEBUG if(errNum == WLZ_ERR_NONE) { double sSq[2]; FILE *fP = NULL; WlzObject *cCObjR = NULL; oIdx = 0; while((errNum == WLZ_ERR_NONE) && (oIdx < 2)) { (void )WlzGreyStats(wObj[oIdx], NULL, NULL, NULL, NULL, &(sSq[oIdx]), NULL, NULL, &errNum); ++oIdx; } if(errNum == WLZ_ERR_NONE) { cCObjR = WlzFromArray2D((void **)(oAr[0]), aSz, aOrg, WLZ_GREY_DOUBLE, WLZ_GREY_DOUBLE, 0.0, 255.0 / (1.0 + (sqrt(sSq[0] * sSq[1]) * aSz.vtX * aSz.vtY)), 0, 0, &errNum); } if(cCObjR) { if((fP = fopen("cCObjR.wlz", "w")) != NULL) { (void )WlzWriteObj(fP, cCObjR); (void )fclose(fP); } (void )WlzFreeObj(cCObjR); } } #endif /* WLZ_REGCCOR_DEBUG */ for(oIdx = 0; oIdx < 2; ++oIdx) { (void )WlzFreeObj(oObj[oIdx]); (void )WlzFreeObj(pObj[oIdx]); (void )WlzFreeObj(wObj[oIdx]); AlcDouble2Free(oAr[oIdx]); } if(dstErr) { *dstErr = errNum; } return(dstRot); }
/*! * \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); }
/*! * \return Woolz error code. * \ingroup WlzBinaryOps * \brief Splits the reference object into component objects cliped * from the reference object, with the bounding box of each * of the component objects determined using the pre-processed * object. The component objects are returned in size order. * \param refObj Reference object. * \param ppObj Pre-processed object which is * normalised to values in the range * 0 - 255 as WlzUByte greys. * \param bWidth Border width. * \param bgdFrac Minimum fraction of values which are * background values, with range * [0.0+ - 1.0-]. * \param sigma Histogram smoothing parameter used * by WlzHistogramCnvGauss(). * \param compThrMethod Method for computing threshold, used * in call to WlzCompThresholdVT(). * \param nReqComp Number of required components. * \param dstNComp Destination pointer for the number of * components extracted, must not be NULL. * \param dstComp Destination pointer for the extracted * components, must not be NULL. */ WlzErrorNum WlzSplitObj(WlzObject *refObj, WlzObject *ppObj, int bWidth, double bgdFrac, double sigma, WlzCompThreshType compThrMethod, int nReqComp, int *dstNComp, WlzObject ***dstComp) { int dim, idC; WlzObject *hObj = NULL, *tObj = NULL; WlzObject **comp = NULL; WlzBox box; WlzPixelV tV; WlzSplitObjData split; WlzThresholdType tType; WlzConnectType lCon; WlzErrorNum errNum = WLZ_ERR_NONE; const int maxComp = 1024; split.nLComp = 0; split.compI = NULL; split.lCompSz = NULL; split.lComp = NULL; if((refObj == NULL) || (ppObj == NULL)) { errNum = WLZ_ERR_OBJECT_NULL; } else if((refObj->domain.core == NULL) || (ppObj->domain.core == NULL)) { errNum = WLZ_ERR_DOMAIN_NULL; } else if((refObj->values.core == NULL) || (ppObj->values.core == NULL)) { errNum = WLZ_ERR_VALUES_NULL; } else if(refObj->type != ppObj->type) { errNum = WLZ_ERR_OBJECT_TYPE; } else if((dstNComp == NULL) || (dstComp == NULL)) { errNum = WLZ_ERR_PARAM_NULL; } else if((bgdFrac < DBL_EPSILON) || (bgdFrac > (1.0 - DBL_EPSILON))) { errNum = WLZ_ERR_PARAM_DATA; } else { switch(refObj->type) { case WLZ_2D_DOMAINOBJ: dim = 2; lCon = WLZ_8_CONNECTED; break; case WLZ_3D_DOMAINOBJ: dim = 3; lCon = WLZ_26_CONNECTED; break; default: errNum = WLZ_ERR_OBJECT_TYPE; break; } } /* Compute threshold value and type from histogram. */ if(errNum == WLZ_ERR_NONE) { hObj = WlzAssignObject( WlzHistogramObj(ppObj, 256, 0.0, 1.0, &errNum), NULL); } if(errNum == WLZ_ERR_NONE) { errNum = WlzHistogramCnvGauss(hObj, sigma, 0); } if(errNum == WLZ_ERR_NONE) { errNum = WlzCompThresholdVT(hObj, compThrMethod, bgdFrac, 0.0, 0.0, &tV, &tType); } (void )WlzFreeObj(hObj); hObj = NULL; /* Threshold object. */ if(errNum == WLZ_ERR_NONE) { tObj = WlzAssignObject( WlzThreshold(ppObj, tV, tType, &errNum), NULL); } /* Label to get connected components. */ if(errNum == WLZ_ERR_NONE) { errNum = WlzLabel(tObj, &(split.nLComp), &(split.lComp), maxComp, 0, lCon); } /* Sort connected components by size. */ if(errNum == WLZ_ERR_NONE) { if(split.nLComp < nReqComp) { nReqComp = split.nLComp; } if(((split.compI = (int *)AlcMalloc(sizeof(int) * split.nLComp)) == NULL) || ((split.lCompSz = (int *)AlcMalloc(sizeof(int) * split.nLComp)) == NULL)) { errNum = WLZ_ERR_MEM_ALLOC; } } if(errNum == WLZ_ERR_NONE) { idC = 0; while((errNum == WLZ_ERR_NONE) && (idC < split.nLComp)) { split.compI[idC] = idC; split.lCompSz[idC] = (dim == 2)? WlzArea(split.lComp[idC], &errNum): WlzVolume(split.lComp[idC], &errNum); ++idC; } } if(errNum == WLZ_ERR_NONE) { /* Sort component indices by component size. */ AlgQSort(split.compI, split.nLComp, sizeof(int), &split, WlzSplitObjSortSzFn); /* Allocate array for cliped component objects. */ if((comp = (WlzObject **)AlcCalloc(sizeof(WlzObject *), split.nLComp)) == NULL) { errNum = WLZ_ERR_MEM_ALLOC; } } /* Compute bounding box and clip objects from the reference object. */ if(errNum == WLZ_ERR_NONE) { idC = 0; while((errNum == WLZ_ERR_NONE) && (idC < nReqComp)) { if(dim == 2) { box.i2 = WlzBoundingBox2I(split.lComp[split.compI[idC]], &errNum); if(errNum == WLZ_ERR_NONE) { box.i2.xMin -= bWidth; box.i2.yMin -= bWidth; box.i2.xMax += bWidth; box.i2.yMax += bWidth; comp[idC] = WlzClipObjToBox2D(refObj, box.i2, &errNum); } } else /* dim == 3 */ { box.i3 = WlzBoundingBox3I(split.lComp[split.compI[idC]], &errNum); if(errNum == WLZ_ERR_NONE) { box.i3.xMin -= bWidth; box.i3.yMin -= bWidth; box.i3.zMin -= bWidth; box.i3.xMax += bWidth; box.i3.yMax += bWidth; box.i3.zMax += bWidth; comp[idC] = WlzClipObjToBox3D(refObj, box.i3, &errNum); } } ++idC; } } if(errNum == WLZ_ERR_NONE) { *dstNComp = nReqComp; *dstComp = comp; } /* Free temporary storage. */ if(split.lComp) { for(idC = 0; idC < split.nLComp; ++idC) { (void )WlzFreeObj(split.lComp[idC]); } AlcFree(split.lComp); } AlcFree(split.compI); AlcFree(split.lCompSz); (void )WlzFreeObj(tObj); return(errNum); }
/*! * \return Woolz error code. * \ingroup WlzBinaryOps * \brief Splits the given montage object into component objects * clipped from the montage object. The montage object * must be composed of component images embedded in a * background, with little variation in the background * values. * \param mObj Montage object, which must be either * a WLZ_2D_DOMAINOBJ or a * WLZ_3D_DOMAINOBJ with values. * \param gapV Value for the uniform background. * Must be either WLZ_GREY_INT or * WLZ_GREY_RGBA. * \param tol Tolerance (fraction) for the * variation in background values. * \param bWidth Additional boundary width added * to detected images before they are * clipped. * \param minArea Minimum area for a valid component * image, must be greater than zero. * \param maxComp Maximum number of components. * \param dstNComp Destination pointer for the number of * components extracted, must not be NULL. * \param dstComp Destination pointer for the extracted * components, must not be NULL. */ WlzErrorNum WlzSplitMontageObj(WlzObject *mObj, WlzPixelV gapV, double tol, int bWidth, int minArea, int maxComp, int *dstNComp, WlzObject ***dstComp) { int id0, id1, area, nLComp = 0; WlzObject *gObj = NULL, *tObj = NULL; WlzObject **lComp; WlzGreyType objG; WlzBox box; WlzPixelV gapLV, gapHV; WlzConnectType lCon; int tI[8]; WlzErrorNum errNum = WLZ_ERR_NONE; tol = WLZ_CLAMP(tol, 0.0, 1.0); if(mObj == NULL) { errNum = WLZ_ERR_OBJECT_NULL; } else if(minArea < 1) { errNum = WLZ_ERR_PARAM_DATA; } else { switch(mObj->type) { case WLZ_2D_DOMAINOBJ: lCon = WLZ_4_CONNECTED; break; case WLZ_3D_DOMAINOBJ: lCon = WLZ_6_CONNECTED; break; default: errNum = WLZ_ERR_OBJECT_TYPE; break; } } if(errNum == WLZ_ERR_NONE) { objG = WlzGreyTypeFromObj(mObj, &errNum); } if(errNum == WLZ_ERR_NONE) { switch(gapV.type) { case WLZ_GREY_INT: /* FALLTHROUGH */ case WLZ_GREY_RGBA: break; default: errNum = WLZ_ERR_GREY_TYPE; break; } } if(errNum == WLZ_ERR_NONE) { if(objG == WLZ_GREY_RGBA) { if(gapV.type != WLZ_GREY_RGBA) { (void )WlzValueConvertPixel(&gapV, gapV, WLZ_GREY_RGBA); } } else { if(gapV.type != WLZ_GREY_INT) { (void )WlzValueConvertPixel(&gapV, gapV, WLZ_GREY_INT); } } gapLV.type = gapHV.type = gapV.type; if(gapV.type == WLZ_GREY_INT) { tI[0] = gapV.v.inv * tol; gapLV.v.inv = gapV.v.inv - tI[0]; gapHV.v.inv = gapV.v.inv + tI[0]; tObj = WlzThreshold(mObj, gapLV, WLZ_THRESH_HIGH, &errNum); if((errNum == WLZ_ERR_NONE) && (tObj != NULL)) { gObj = WlzThreshold(tObj, gapHV, WLZ_THRESH_LOW, &errNum); } (void )WlzFreeObj(tObj); tObj = NULL; } else /* gapV.type == WLZ_GREY_RGBA */ { tI[0] = WLZ_RGBA_RED_GET(gapV.v.rgbv); tI[1] = (int )floor((double )(tI[0]) * tol); tI[2] = tI[0] - tI[1]; tI[5] = tI[0] + tI[1]; tI[0] = WLZ_RGBA_GREEN_GET(gapV.v.rgbv); tI[1] = (int )floor((double )(tI[0]) * tol); tI[3] = tI[0] - tI[1]; tI[6] = tI[0] + tI[1]; tI[0] = WLZ_RGBA_BLUE_GET(gapV.v.rgbv); tI[1] = (int )floor((double )(tI[0]) * tol); tI[4] = tI[0] - tI[1]; tI[7] = tI[0] + tI[1]; tI[2] = WLZ_CLAMP(tI[2], 0, 255); tI[3] = WLZ_CLAMP(tI[3], 0, 255); tI[4] = WLZ_CLAMP(tI[4], 0, 255); WLZ_RGBA_RGBA_SET(gapLV.v.rgbv, tI[2], tI[3], tI[4], 255); tI[5] = WLZ_CLAMP(tI[5], 0, 255); tI[6] = WLZ_CLAMP(tI[6], 0, 255); tI[7] = WLZ_CLAMP(tI[7], 0, 255); WLZ_RGBA_RGBA_SET(gapHV.v.rgbv, tI[5], tI[6], tI[7], 255); gObj = WlzRGBABoxThreshold(mObj, gapLV, gapHV, &errNum); } } if(errNum == WLZ_ERR_NONE) { tObj = WlzDiffDomain(mObj, gObj, &errNum); } (void )WlzFreeObj(gObj); if(errNum == WLZ_ERR_NONE) { errNum = WlzLabel(tObj, &nLComp, &lComp, maxComp, 0, lCon); } (void )WlzFreeObj(tObj); if(errNum == WLZ_ERR_NONE) { /* Get rid of small objects using minArea as the threshold. */ id0 = 0; id1 = 0; while(id0 < nLComp) { switch((*(lComp + id0))->type) { case WLZ_2D_DOMAINOBJ: area = WlzArea(*(lComp + id0), NULL); break; case WLZ_3D_DOMAINOBJ: area = WlzVolume(*(lComp + id0), NULL); break; default: area = 0; break; } if(area >= minArea) { *(lComp + id1) = *(lComp + id0); ++id1; } else { (void )WlzFreeObj(*(lComp + id0)); *(lComp + id0) = NULL; } ++id0; } nLComp = id1; } if(errNum == WLZ_ERR_NONE) { /* Clip rectangular objects from the montage object. */ id0 = 0; while((errNum == WLZ_ERR_NONE) && (id0 < nLComp)) { if(tObj->type == WLZ_2D_DOMAINOBJ) { box.i2 = WlzBoundingBox2I(*(lComp + id0), &errNum); box.i2.xMin -= bWidth; box.i2.yMin -= bWidth; box.i2.xMax += bWidth; box.i2.yMax += bWidth; (void )WlzFreeObj(*(lComp + id0)); *(lComp + id0) = WlzClipObjToBox2D(mObj, box.i2, &errNum); } else /* tObj->type == WLZ_3D_DOMAINOBJ */ { box.i3 = WlzBoundingBox3I(*(lComp + id0), &errNum); box.i3.xMin -= bWidth; box.i3.yMin -= bWidth; box.i3.zMin -= bWidth; box.i3.xMax += bWidth; box.i3.yMax += bWidth; box.i3.zMax += bWidth; (void )WlzFreeObj(*(lComp + id0)); *(lComp + id0) = WlzClipObjToBox3D(mObj, box.i3, &errNum); } ++id0; } } *dstNComp = nLComp; *dstComp = lComp; return(errNum); }
/*! * \return New filtered object with new values or NULL on error. * \ingroup WlzValuesFilters * \brief Applies a seperable filter along the y axis (lines) * to the given object using the given convolution kernel. * \param inObj Input 2 or 3D spatial domain object * to be filtered which must have scalar * values. * \param dim Object's dimension. * \param maxThr Maximum number of threads to use. * \param iBuf Working buffers large enough for any * column in the image with one for each * thread. * \param rBuf Buffers as for iBuf. * \param cBufSz Convolution kernel size. * \param cBuf Convolution kernel buffer. * \param pad Type of padding. * \param padVal Padding value. * \param dstErr Destination error pointer may be NULL. */ static WlzObject *WlzSepFilterY(WlzObject *inObj, int dim, int maxThr, double **iBuf, double **rBuf, int cBufSz, double *cBuf, AlgPadType pad, double padVal, WlzErrorNum *dstErr) { int idp, poff, nPln; WlzObjectType rGTT; WlzDomain *domains; WlzValues *iVal, *rVal; WlzPixelV zV; WlzObject *rnObj = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; zV.v.dbv = 0.0; zV.type = WLZ_GREY_DOUBLE; rGTT = WlzGreyTableType(WLZ_GREY_TAB_RAGR, WLZ_GREY_DOUBLE, NULL); if(dim == 3) { WlzPlaneDomain *pDom; WlzVoxelValues *vVal; pDom = inObj->domain.p; vVal = inObj->values.vox; nPln = pDom->lastpl - pDom->plane1 + 1; domains = pDom->domains; iVal = vVal->values; poff = pDom->plane1 - vVal->plane1; rnObj = WlzNewObjectValues(inObj, rGTT, zV, 1, zV, &errNum); if(errNum == WLZ_ERR_NONE) { rVal = rnObj->values.vox->values; } } else { nPln = 1; poff = 0; domains = &(inObj->domain); iVal = &(inObj->values); rnObj = WlzNewObjectValues(inObj, rGTT, zV, 1, zV, &errNum); if(errNum == WLZ_ERR_NONE) { rVal = &(rnObj->values); } } if(errNum == WLZ_ERR_NONE) { #ifdef _OPENMP #pragma omp parallel for num_threads(maxThr) #endif for(idp = 0; idp < nPln; ++idp) { if(errNum == WLZ_ERR_NONE) { if(domains[idp].core != NULL) { int thrId = 0; WlzIBox2 bBox; WlzObject *iObj = NULL, *rObj = NULL; WlzGreyValueWSpace *iVWSp = NULL, *rVWSp = NULL; WlzErrorNum errNum2 = WLZ_ERR_NONE; #ifdef _OPENMP thrId = omp_get_thread_num(); #endif iObj = WlzMakeMain(WLZ_2D_DOMAINOBJ, domains[idp], iVal[idp + poff], NULL, NULL, &errNum); if(errNum == WLZ_ERR_NONE) { rObj = WlzMakeMain(WLZ_2D_DOMAINOBJ, domains[idp], rVal[idp], NULL, NULL, &errNum); } if(errNum == WLZ_ERR_NONE) { bBox = WlzBoundingBox2I(iObj, &errNum); } if((errNum == WLZ_ERR_NONE) && ((iVWSp = WlzGreyValueMakeWSp(iObj, &errNum)) != NULL) && ((rVWSp = WlzGreyValueMakeWSp(rObj, &errNum)) != NULL)) { int idx; for(idx = bBox.xMin; idx <= bBox.xMax; ++idx) { int y0, y1, idy; WlzUByte in = 0; for(idy = bBox.yMin; idy <= bBox.yMax + 1; ++idy) { WlzGreyValueGet(iVWSp, 0, idy, idx); if(iVWSp->bkdFlag == 0) { double *ibp; if(in == 0) { y0 = idy; in = 1; } y1 = idy; ibp = iBuf[thrId] + (y1 - y0); switch(iVWSp->gType) { case WLZ_GREY_INT: *ibp = iVWSp->gVal[0].inv; break; case WLZ_GREY_SHORT: *ibp = iVWSp->gVal[0].shv; break; case WLZ_GREY_UBYTE: *ibp = iVWSp->gVal[0].ubv; break; case WLZ_GREY_FLOAT: *ibp = iVWSp->gVal[0].flv; break; case WLZ_GREY_DOUBLE: *ibp = iVWSp->gVal[0].dbv; break; default: break; } } else if(in) { int idr, len; len = y1 - y0 + 1; AlgConvolveD(len, rBuf[thrId], cBufSz * 2 + 1, cBuf, len, iBuf[thrId], pad, padVal); for(idr = y0; idr <= y1; ++idr) { WlzGreyValueGet(rVWSp, 0, idr, idx); *(rVWSp->gPtr[0].dbp) = rBuf[thrId][idr - y0]; } } } } } WlzGreyValueFreeWSp(iVWSp); WlzGreyValueFreeWSp(rVWSp); (void )WlzFreeObj(iObj); (void )WlzFreeObj(rObj); if(errNum2 != WLZ_ERR_NONE) { #ifdef _OPENMP #pragma omp critical { #endif if(errNum == WLZ_ERR_NONE) { errNum = errNum2; } #ifdef _OPENMP } #endif } } } } } if(errNum != WLZ_ERR_NONE) { (void )WlzFreeObj(rnObj); rnObj = NULL; } if(dstErr) { *dstErr = errNum; } return(rnObj); }