/*! * \return The filtered object, or NULL on error. * \ingroup WlzValueFilters * \brief Applies a recursive filter to the given object. * \param srcObj Given object. * \param ftr Recursive filter. * \param actionMsk Action mask. * \param dstErr Destination error pointer, may * be null. */ WlzObject *WlzRsvFilterObj(WlzObject *srcObj, WlzRsvFilter *ftr, int actionMsk, WlzErrorNum *dstErr) { WlzValues tVal; WlzObject *xObj = NULL, *yObj = NULL, *xyObj = NULL, *zObj = NULL, *dstObj = NULL; WlzErrorNum errNum = WLZ_ERR_NONE; if((srcObj == NULL) || (ftr == NULL)) { errNum = WLZ_ERR_OBJECT_NULL; } else if(srcObj->domain.core == NULL) { errNum = WLZ_ERR_DOMAIN_NULL; } else if(srcObj->values.core == NULL) { errNum = WLZ_ERR_VALUES_NULL; } else if(WlzGreyTableIsTiled(srcObj->values.core->type)) { errNum = WLZ_ERR_VALUES_TYPE; } else { switch(srcObj->type) { case WLZ_EMPTY_OBJ: dstObj = WlzMakeEmpty(&errNum); break; case WLZ_2D_DOMAINOBJ: if((actionMsk & (WLZ_RSVFILTER_ACTION_X | WLZ_RSVFILTER_ACTION_Y)) != 0) { /* Filter in each required direction. */ if((actionMsk & WLZ_RSVFILTER_ACTION_X) != 0) { xObj = WlzRsvFilterObj2DX(srcObj, ftr, &errNum); } if((errNum == WLZ_ERR_NONE) && ((actionMsk & WLZ_RSVFILTER_ACTION_Y) != 0)) { yObj = WlzRsvFilterObj2DY((xObj)? xObj: srcObj, ftr, &errNum); } if(errNum == WLZ_ERR_NONE) { if(yObj) { dstObj = yObj; yObj = NULL; } else if(xObj) { dstObj = xObj; xObj = NULL; } } if(xObj) { WlzFreeObj(xObj); } if(yObj) { WlzFreeObj(yObj); } } else { /* No filtering required. */ dstObj = WlzNewGrey(srcObj, &errNum); } break; case WLZ_3D_DOMAINOBJ: if((actionMsk & (WLZ_RSVFILTER_ACTION_X | WLZ_RSVFILTER_ACTION_Y | WLZ_RSVFILTER_ACTION_Z)) != 0) { if((actionMsk & (WLZ_RSVFILTER_ACTION_X | WLZ_RSVFILTER_ACTION_Y)) != 0) { xyObj = WlzRsvFilterObj3DXY(srcObj, ftr, actionMsk, &errNum); } if((errNum == WLZ_ERR_NONE) && ((actionMsk & WLZ_RSVFILTER_ACTION_Z) != 0)) { zObj = WlzRsvFilterObj3DZ((xyObj)? xyObj: srcObj, ftr, &errNum); } if(errNum == WLZ_ERR_NONE) { if(zObj) { dstObj = zObj; zObj = NULL; } else if(xyObj) { dstObj = xyObj; xyObj = NULL; } } if(xyObj) { WlzFreeObj(xyObj); } if(zObj) { WlzFreeObj(zObj); } } else { /* No filtering required. */ tVal = WlzCopyValues(srcObj->type, srcObj->values, srcObj->domain, &errNum); if(errNum == WLZ_ERR_NONE) { dstObj= WlzMakeMain(srcObj->type, srcObj->domain, tVal, NULL, NULL, &errNum); } } break; default: errNum = WLZ_ERR_OBJECT_TYPE; break; } } if(dstErr) { *dstErr = errNum; } return(dstObj); }
/*! * \ingroup WlzValuesUtils * \brief Set the value maskVal within the domain given by the * mask object. The mask object can be a 2D, 3D, polygon * or boundary object. A 3D mask with a 2D object is an * error. A 2D mask with a 3D object will be applied to * each plane in turn. * * \return New object with the same domain as the input object but with values in the intersection with the mask domain set to the mask value. NULL on error. * \param obj Input object * \param mask Mask object. * \param maskVal mask value. * \param dstErr Error return. * \par Source: * WlzGreyMask.c */ WlzObject *WlzGreyMask( WlzObject *obj, WlzObject *mask, WlzPixelV maskVal, WlzErrorNum *dstErr) { WlzObject *rtnObj=NULL; WlzObject *tmpMask, *obj1; WlzValues values; WlzPixelV tmpMaskval; WlzIntervalWSpace iwsp; WlzGreyWSpace gwsp; WlzGreyP gptr; int i; WlzErrorNum errNum=WLZ_ERR_NONE; /* check obj */ if( obj == NULL ){ errNum = WLZ_ERR_OBJECT_NULL; } else { switch( obj->type ){ case WLZ_2D_DOMAINOBJ: if( obj->values.core == NULL ){ errNum = WLZ_ERR_VALUES_NULL; } break; case WLZ_3D_DOMAINOBJ: return WlzGreyMask3d(obj, mask, maskVal, dstErr); case WLZ_TRANS_OBJ: if((values.obj = WlzGreyMask(obj->values.obj, mask, maskVal, &errNum)) != NULL){ return WlzMakeMain(WLZ_TRANS_OBJ, obj->domain, values, NULL, NULL, dstErr); } break; case WLZ_EMPTY_OBJ: return WlzMakeEmpty(dstErr); default: errNum = WLZ_ERR_OBJECT_TYPE; break; } } /* check the mask */ if( errNum == WLZ_ERR_NONE ){ if( mask == NULL ){ errNum = WLZ_ERR_OBJECT_NULL; } else { values.core = NULL; switch( mask->type ){ case WLZ_2D_DOMAINOBJ: tmpMask = WlzMakeMain(WLZ_2D_DOMAINOBJ, mask->domain, values, NULL, NULL, &errNum); break; case WLZ_TRANS_OBJ: tmpMask = WlzMakeMain(WLZ_2D_DOMAINOBJ, mask->values.obj->domain, values, NULL, NULL, &errNum); break; case WLZ_EMPTY_OBJ: return WlzMakeMain(WLZ_2D_DOMAINOBJ, obj->domain, obj->values, NULL, NULL, dstErr); case WLZ_2D_POLYGON: tmpMask = WlzPolyToObj(mask->domain.poly, WLZ_SIMPLE_FILL, &errNum); break; case WLZ_BOUNDLIST: tmpMask = WlzBoundToObj(mask->domain.b, WLZ_SIMPLE_FILL, &errNum); break; default: errNum = WLZ_ERR_OBJECT_TYPE; break; } if( errNum == WLZ_ERR_NONE ){ tmpMask = WlzAssignObject(tmpMask, NULL); } } } /* copy input obj and setvalues in the intersection */ if(errNum == WLZ_ERR_NONE){ if((rtnObj = WlzNewGrey(obj, &errNum)) != NULL){ if((obj1 = WlzIntersect2(obj, tmpMask, &errNum)) != NULL){ obj1->values = WlzAssignValues(rtnObj->values, NULL); errNum = WlzInitGreyScan(obj1, &iwsp, &gwsp); WlzValueConvertPixel(&tmpMaskval, maskVal, gwsp.pixeltype); 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++){ *gptr.inp = tmpMaskval.v.inv; } break; case WLZ_GREY_SHORT: for(i=0; i<iwsp.colrmn; i++, gptr.shp++){ *gptr.shp = tmpMaskval.v.shv; } break; case WLZ_GREY_UBYTE: for(i=0; i<iwsp.colrmn; i++, gptr.ubp++){ *gptr.ubp = tmpMaskval.v.ubv; } break; case WLZ_GREY_FLOAT: for(i=0; i<iwsp.colrmn; i++, gptr.flp++){ *gptr.flp = tmpMaskval.v.flv; } break; case WLZ_GREY_DOUBLE: for(i=0; i<iwsp.colrmn; i++, gptr.dbp++){ *gptr.dbp = tmpMaskval.v.dbv; } break; case WLZ_GREY_RGBA: for(i=0; i<iwsp.colrmn; i++, gptr.rgbp++){ *gptr.rgbp = tmpMaskval.v.rgbv; } break; default: errNum = WLZ_ERR_GREY_TYPE; break; } } if( errNum == WLZ_ERR_EOO ){ errNum = WLZ_ERR_NONE; } WlzFreeObj(obj1); } else { WlzFreeObj(rtnObj); rtnObj = NULL; } } WlzFreeObj(tmpMask); } if( dstErr ){ *dstErr = errNum; } return rtnObj; }
/*! * \ingroup WlzValuesUtils * \brief Makes a dithered object from the given grey-level Woolz object. The destination bits are determined by the number of shades in the dithered image (usually 1 bit) and the bit planes to use (typically to match the bit-planes of a display mask). * * \return Object with dithered grey values. * \param o Input object. * \param destBits Destination bit planes for dithered values. * \param dstErr Error return. * \par Source: * WlzGreyDitherObj.c */ WlzObject *WlzGreyDitherObj( WlzObject *o, unsigned int destBits, WlzErrorNum *dstErr) { WlzObject *obj=NULL; WlzIntervalWSpace iwsp1, iwsp2; WlzGreyWSpace gwsp1, gwsp2; int i, j, m, g, G; int n_to; int bit_val[8], permute_val[256]; int factor_from, factor_to, mask_start; WlzErrorNum errNum=WLZ_ERR_NONE; /* check object type - 1 only */ if( o == NULL ){ errNum = WLZ_ERR_OBJECT_NULL; } else { switch( o->type ){ case WLZ_2D_DOMAINOBJ: /* check it has a valuetable */ if( o->values.core == NULL ){ errNum = WLZ_ERR_VALUES_NULL; } else if( WlzGreyTableIsTiled(o->values.core->type) ){ errNum = WLZ_ERR_VALUES_TYPE; } break; case WLZ_3D_DOMAINOBJ: case WLZ_TRANS_OBJ: errNum = WLZ_ERR_OBJECT_TYPE; break; case WLZ_EMPTY_OBJ: obj = WlzMakeEmpty(&errNum); break; default: errNum = WLZ_ERR_OBJECT_TYPE; break; } } if( !obj && (errNum == WLZ_ERR_NONE) ){ /* count the bits in the mask */ for(i=1, n_to=0; i <= destBits; i <<= 1 ) { if( i & destBits ) n_to++; } /* check numbers of bits */ if( n_to == 0 ){ errNum = WLZ_ERR_PARAM_DATA; } else { /* make a new object with the same domain */ if((obj = WlzNewGrey(o, &errNum)) != NULL){ if( n_to < 8 ){ /* set up some factors */ factor_from = 255; factor_to = (1<<n_to) - 1; mask_start = (1 << (8-n_to)) - 1; /* set up bit permute lut */ for(i=0, j=1, m=destBits; i < n_to; i++) { while( !(m&1) ) { m = m >> 1; j = j << 1; } m = m >> 1; bit_val[i] = j; } for(i=0; i < (1<<n_to); i++) { permute_val[i] = 0; for(j=0, m=1; j < n_to; j++, m = m<<1 ){ permute_val[i] += (i&m) * bit_val[j]; } } for(;i < 256; i++){ permute_val[i] = 0; } /* now scan the objects */ WlzInitGreyScan(o,&iwsp1,&gwsp1); WlzInitGreyScan(obj,&iwsp2,&gwsp2); while((WlzNextGreyInterval(&iwsp1) == WLZ_ERR_NONE) && (WlzNextGreyInterval(&iwsp2) == WLZ_ERR_NONE ) ) { j = iwsp1.linpos; G = rand() & mask_start; switch( gwsp1.pixeltype ) { case WLZ_GREY_INT: for(i=iwsp1.lftpos; i<=iwsp1.rgtpos; i++, gwsp1.u_grintptr.inp++, gwsp2.u_grintptr.inp++ ) { G += (*gwsp1.u_grintptr.inp) & 255; g = (G * factor_to) / factor_from; (*gwsp2.u_grintptr.inp) = permute_val[g]; G -= (g * factor_from) / factor_to; } break; case WLZ_GREY_SHORT: for(i=iwsp1.lftpos; i<=iwsp1.rgtpos; i++, gwsp1.u_grintptr.shp++, gwsp2.u_grintptr.shp++ ) { G += (*gwsp1.u_grintptr.shp) & 255; g = (G * factor_to) / factor_from; (*gwsp2.u_grintptr.shp) = (short )(permute_val[g]); G -= (g * factor_from) / factor_to; } break; case WLZ_GREY_UBYTE: for(i=iwsp1.lftpos; i<=iwsp1.rgtpos; i++, gwsp1.u_grintptr.ubp++, gwsp2.u_grintptr.ubp++ ) { G += (*gwsp1.u_grintptr.ubp); g = (G * factor_to) / factor_from; (*gwsp2.u_grintptr.ubp) = (WlzUByte )(permute_val[g]); G -= (g * factor_from) / factor_to; } break; case WLZ_GREY_RGBA: /* RGBA to be done RAB */ default: errNum = WLZ_ERR_GREY_TYPE; break; } } } } } }
/*! * \return The filtered object, maybe NULL on error. * \ingroup WlzValueFilters * \brief WlzSeqPar performs a sequential or parallel local * transform. A distance transform is an example of a * sequential transform and a laplacian is an example of * a parallel transform. * Only WLZ_2D_DOMAINOBJ objects with values may be passed * to WlzSeqPar(). * \note If the point to be transformed is at line l and col k, * there is an array of pointers spWSpace->adrptr[-7:7], * whose i'th entry gives the address of the point * (l + (i * spWSpace->ldelta), k), but which is only * meaningful for lines within bdrSz of the point. * For example: spWSpace->adrptr[-3] and * spWSpace->adrptr[3] are undefined if bdrSz < 3. * \param srcObj The given WLZ_2D_DOMAINOBJ object. * \param newObjFlag If zero then the given object is * overwritten otherwise a new object is * created. * \param sequentialFlag If non zero the transform is * seqential and transformed * values are used in calculating * the neighbouring values. * If zero the transform always * works on the original grey * values. * \param rasterDir Direction of raster scan. * \param bdrSz Local transform kernel half-size, * must be in range 0 - 7. * The usual 8 immediate neighbors * correspond to bdrSz == 1. * \param bkgVal Background grey value. * \param transformData Data supplied to the transform * function. * \param transformFn Supplied transform function. * \param dstErr Destination error pointer, may * be NULL. */ WlzObject *WlzSeqPar(WlzObject *srcObj, int newObjFlag, int sequentialFlag, WlzRasterDir rasterDir, int bdrSz, int bkgVal, void *transformData, int (*transformFn)(WlzSeqParWSpace *, void *), WlzErrorNum *dstErr) { int tI0, curLine, howManyLn, bdrP1, numBufs, kol, kol1, lineSz, /* lineSz is the maximally sized line, including borders */ lineOffset, /* lineOffset is difference in line numbers between that being transformed and that needed in the input buffer for use in the transform */ nextLine, inLine, needed, idx, itop, minLine; WlzErrorNum errNum = WLZ_ERR_NONE; int *lineBufSpace = NULL, /* lineBufSpace is the current line buffer */ *firstColP, *lastColP, *dstBuf, *lastLnP, *tIP0; WlzIntervalDomain *jdp; WlzObject *dstObj = NULL; WlzSeqParWSpace spWSpace; /* Work space which is passed on to the transform function. */ WlzIntervalWSpace srcIWsp, dstIWsp; WlzGreyWSpace srcGWsp, dstGWsp; int *lineBufs[15], /* lineBufs is the array of line buffers needed to obtain neighbouring points. */ *adrbase[15]; WLZ_DBG((WLZ_DBG_LVL_FN|WLZ_DBG_LVL_1), ("WlzSeqPar FE %p %d %d %d %d %d %p %p %p\n", srcObj, newObjFlag, sequentialFlag, (int )rasterDir, bdrSz, bkgVal, transformData, transformFn, dstErr)); if(srcObj == NULL) { errNum = WLZ_ERR_OBJECT_NULL; if(dstErr) { *dstErr = errNum; } return(NULL); } if((srcObj->type != WLZ_2D_DOMAINOBJ) || (srcObj->values.core == NULL) || (srcObj->domain.core == NULL)) { errNum = WLZ_ERR_OBJECT_TYPE; if(dstErr) { *dstErr = errNum; } return(NULL); } /* * Make an array spWSpace.adrptr of pointers with range [-7,7] */ spWSpace.adrptr = adrbase + 7; spWSpace.brdrsz = bdrSz; jdp = srcObj->domain.i; bdrP1 = -bdrSz - 1; /* * The buffer must contain both the current line and bdrSz lines each side. */ numBufs = (bdrSz * 2) + 1; kol1 = kol = jdp->kol1; lineSz = jdp->lastkl - kol + numBufs; /* * Some pointers to first/last line and column positions * to accomodate different rasters */ firstColP = &dstIWsp.lftpos; lastColP = &dstIWsp.rgtpos; lastLnP = &jdp->lastln; /* * spWSpace.kdelta is increment to adrs when processing points * if positive, procede left to right in lines * if negative procede right to left * spWSpace.ldelta is increment to line number * if positive, the raster is proceeding in increasing line numbers * if negative the raster is procceding with decreasing line numbers */ spWSpace.kdelta = 1; spWSpace.ldelta = 1; /* * Alter parameters for right to left rasters */ if(!sequentialFlag) { rasterDir = WLZ_RASTERDIR_ILIC; } if((rasterDir == WLZ_RASTERDIR_ILDC) || (rasterDir == WLZ_RASTERDIR_DLDC)) { tIP0 = firstColP; firstColP = lastColP; lastColP = tIP0; spWSpace.kdelta = -1; } /* * Alter parameters for bottom to top rasters */ if((rasterDir == WLZ_RASTERDIR_DLIC) || (rasterDir == WLZ_RASTERDIR_DLDC)) { spWSpace.ldelta = -1; lastLnP = &jdp->line1; } lineOffset = bdrSz * spWSpace.ldelta; /* * Allocate space to buffers */ lineBufSpace = (int *)AlcCalloc(lineSz * (numBufs + 1), sizeof(int)); for(idx = 0; idx < numBufs; ++idx) { lineBufs[idx] = lineBufSpace + (idx + 1) * lineSz; } /* * Either use existing object or create a new grey table object * according to the newObjFlag. * If a new object is required then it is created with the SAME interval * list and property list and a DIFFERENT but IDENTICAL grey table. */ if(newObjFlag) { dstObj = WlzNewGrey(srcObj, &errNum); } else { dstObj = srcObj; } /* * Use nxxiv in transplant mode, placing and taking grey values * from the line buffers * srcIWsp is the workspace for entering values into the line buffers * dstIWsp is the workspace for processing the intervals and replacing * the transformed values into the grey table */ srcGWsp.gvio = 1; /* output (since we are specifying tranpl) */ dstGWsp.gvio = 0; /* input */ errNum = WlzInitGreyRasterScan(srcObj, &srcIWsp, &srcGWsp, rasterDir, 1); if(errNum == WLZ_ERR_NONE) { errNum = WlzInitGreyRasterScan(dstObj, &dstIWsp, &dstGWsp, rasterDir, 1); } if(errNum != WLZ_ERR_NONE) { if(dstErr) { *dstErr = errNum; } return(NULL); } /* * curLine is the line currently being processed * inLine is the last line entered into the line buffers * initialize it to an imaginary line just before the first border line */ (void )WlzNextInterval(&srcIWsp); nextLine = srcIWsp.linpos; inLine = srcIWsp.linpos - (spWSpace.ldelta * (bdrSz + 1)); /* * Process the next interval */ while((errNum == WLZ_ERR_NONE) && ((errNum = WlzNextGreyInterval(&dstIWsp)) == WLZ_ERR_NONE)) { /* * When processing curLine, the line buffer must contain * lines upto needed=curLine+border width */ curLine = dstIWsp.linpos; needed = curLine + lineOffset; /* * Next action depends which lines are aleady in the line buffers */ howManyLn = (needed - inLine) * spWSpace.ldelta; WLZ_DBG((WLZ_DBG_LVL_3), ("WlzSeqPar 01 %d %d\n", inLine, curLine)); if(howManyLn < 0) { /* * This is an error it is impossible to have input a line before needed */ errNum = WLZ_ERR_DOMAIN_DATA; if(dstErr) { *dstErr = errNum; } return(NULL); } else if(howManyLn > 0) { /* * If howManyLn > 0 we need to input some lines to input buffer * routine to fill the input buffers used to determine the value * of the transform needed = maximal(minimal)line needed to be filled * into the buffer * if backwards raster, needed = curLine - bdrSz * if forwards raster, needed = curLine + bdrSz * nextLine = line number of the next interval to be filed by nxxiv */ while(1) { if((needed - nextLine) * spWSpace.ldelta < 0) { /* * Next line needed less far away than next line available * from nxxint fill required buffer lines with bkgVal and return * minLine = WlzSeqParPosMod(inLine, numBufs), i.e. the remainder * of inLine when divided by numBufs. * It thus can be used as the offset into the array of * logical line addresses to determine which line in the * circular buffer inLine is being stored */ while(inLine != needed) { inLine += spWSpace.ldelta; minLine = WlzSeqParPosMod(inLine, numBufs); WlzValueSetInt(lineBufs[minLine], bkgVal, lineSz); } break; } /* * Line(s) needed include some real lines, i.e. obtained from nxxiv * first fill buffer with bkgVal */ while(inLine != nextLine) { inLine += spWSpace.ldelta; minLine = WlzSeqParPosMod(inLine, numBufs); WlzValueSetInt(lineBufs[minLine], bkgVal, lineSz); } /* * nxxint interval to be put into buffer calculate adrs in * buffer by adding leftcol to logical adrs stored in * lineBufs[minLine]. then insert with nxxiv */ do { srcGWsp.u_grintptr.inp = lineBufs[minLine] + srcIWsp.lftpos + bdrSz - kol1; WlzGreyInterval(&srcIWsp); /* * Call nxxint to get next interval if in same line store in buffer */ if(WlzNextInterval(&srcIWsp) != 0) { /* * When nxxint has finished all interval set a high value into * nextLine, so all future lines input will be dummy lines */ nextLine = (*lastLnP) - (bdrP1 * spWSpace.ldelta); goto filledLABEL; } } while(srcIWsp.nwlpos == 0); /* * If next interval in new line,store new line number and * jump to start of routine to see what sort of line buffer * processing is required next */ nextLine = srcIWsp.linpos; } } /* * Line buffers filled for processing of this line either from above call * or buffer filled for previous intervals processed on this line * kol is the first point in the interval to be processed * itop is the last point to be processed */ filledLABEL: /* LABEL! see goto above */ kol = *firstColP; itop = *lastColP; /* * First load spWSpace.adrptr with addresses of required neighbors */ idx = bdrP1; while(idx++ < bdrSz) { tI0 = idx * spWSpace.ldelta; spWSpace.adrptr[idx] = lineBufs[WlzSeqParPosMod(curLine + tI0, numBufs)] + kol + bdrSz - kol1; } /* * Then set the pointer where the transformed value will be placed * if parallel mode, adrs is in current line buffer * if sequential mode, adrs is in input buffer * dstGWsp.grintptr is the location in the nxxiv workspace * giving the location from which nxxiv will transplant * values into the grey table */ if(sequentialFlag) { dstBuf = spWSpace.adrptr[0]; } else { dstBuf = lineBufSpace + bdrSz; } dstGWsp.u_grintptr.inp = dstBuf + dstIWsp.lftpos - kol; /* * Procede thru interval,calling the transforming function * at each point and storing the result where required */ kol -= spWSpace.kdelta; while(kol != itop) { kol += spWSpace.kdelta; *dstBuf = (*transformFn)(&spWSpace, transformData); /* * after each point, update the neighbor adrs and the output adrs */ idx = bdrP1; while(idx++ < bdrSz) { spWSpace.adrptr[idx] += spWSpace.kdelta; } dstBuf += spWSpace.kdelta; } /* * Having finished this interval proceed to the next */ } if(errNum == WLZ_ERR_EOO) { errNum = WLZ_ERR_NONE; } if(lineBufSpace) { AlcFree((void *)lineBufSpace); } if(dstErr) { *dstErr = errNum; } WLZ_DBG((WLZ_DBG_LVL_FN|WLZ_DBG_LVL_1), ("WlzSeqPar FX %p\n", dstObj)); return(dstObj); }