def lowerGeodesicErode3D(imIn, imMask, imOut, n=1, se=m3D.CUBOCTAHEDRON1): """ Performs a lower geodesic erosion of 3D image 'imIn' under 'imMask'. The result is put inside 'imOut', 'n' controls the size of the erosion. 'se' specifies the type of structuring element used to perform the computation (CUBOCTAHEDRON by default). The binary lower geodesic erosion is realised using the fact that the dilation is the dual operation of the erosion. Warning! 'imMask' and 'imOut' must be different. """ if imIn.getDepth() == 1: m3D.diff3D(imMask, imIn, imOut) lowerGeodesicDilate3D(imOut, imMask, imOut, n, se=se) m3D.diff3D(imMask, imOut, imOut) else: imWrk1 = m3D.image3DMb(imIn) imWrk2 = m3D.image3DMb(imIn, 1) m3D.logic3D(imIn, imMask, imOut, "inf") for i in range(n): m3D.generateSupMask3D(imOut, imMask, imWrk2, False) m3D.convertByMask3D(imWrk2, imWrk1, 0, m3D.computeMaxRange3D(imWrk1)[1]) m3D.logic3D(imOut, imWrk1, imOut, "sup") m3D.erode3D(imOut, imOut, se=se) m3D.logic3D(imOut, imMask, imOut, "inf")
def upperGeodesicDilate3D(imIn, imMask, imOut, n=1, se=m3D.CUBOCTAHEDRON1): """ Performs a upper geodesic dilation of 3D image 'imIn' above 'imMask'. The result is put inside 'imOut', 'n' controls the size of the dilation. 'se' specifies the type of structuring element used to perform the computation (CUBOCTAHEDRON by default). Warning! 'imMask' and 'imOut' must be different. """ m3D.logic3D(imIn, imMask, imOut, "sup") if imIn.getDepth() == 1: for i in range(n): m3D.diff3D(imOut, imMask, imOut) m3D.dilate3D(imOut, imOut, se=se) m3D.logic3D(imMask, imOut, imOut, "sup") else: imWrk1 = m3D.image3DMb(imIn) imWrk2 = m3D.image3DMb(imIn, 1) for i in range(n): m3D.generateSupMask3D(imOut, imMask, imWrk2, True) m3D.convertByMask3D(imWrk2, imWrk1, 0, m3D.computeMaxRange3D(imWrk1)[1]) m3D.logic3D(imOut, imWrk1, imOut, "inf") m3D.dilate3D(imOut, imOut, se=se) m3D.logic3D(imOut, imMask, imOut, "sup")
def simpleLevelling3D(imIn, imMask, imOut, grid=m3D.DEFAULT_GRID3D): """ Performs a simple levelling of 3D image 'imIn' controlled by image 'imMask' and puts the result in 'imOut'. This operation is composed of two geodesic reconstructions. This filter tends to level regions in the image of homogeneous grey values. """ imWrk1 = m3D.image3DMb(imIn) imWrk2 = m3D.image3DMb(imIn) mask_im = m3D.image3DMb(imIn, 1) m3D.logic3D(imIn, imMask, imWrk1, "inf") m3D.build3D(imIn, imWrk1, grid=grid) m3D.logic3D(imIn, imMask, imWrk2, "sup") m3D.dualBuild3D(imIn, imWrk2, grid=grid) m3D.generateSupMask3D(imIn, imMask, mask_im, False) m3D.convertByMask3D(mask_im, imOut, 0, m3D.computeMaxRange3D(imIn)[1]) m3D.logic3D(imOut, imWrk1, imWrk1, "inf") m3D.negate3D(imOut, imOut) m3D.logic3D(imOut, imWrk2, imOut, "inf") m3D.logic3D(imWrk1, imOut, imOut, "sup")
def infClose3D(imIn, imOut, n, grid=m3D.DEFAULT_GRID3D): """ Performs the infimum of directional closings. A black particle is preserved if its length is larger than 'n' in at least one direction. This operator is a closing. The image edge is set to 'FILLED' in order to take into account particles touching the edge (they are supposed not to extend outside the image window). When square grid is used, the size in oblique directions are reduced to be similar to the horizontal and vertical size. """ imWrk1 = m3D.image3DMb(imIn) imWrk2 = m3D.image3DMb(imIn) imWrk1.fill(m3D.computeMaxRange3D(imIn)[1]) # Default grid is a proxy for an actual grid if grid == m3D.CUBIC: # First neighbors located at a sqrt(2) distance from the center size = int((1.4142 * n + 1) / 2) for d in [2, 4, 10, 12, 14, 16]: linearClose3D(imIn, imWrk2, d, size, grid) m3D.logic3D(imWrk1, imWrk2, imWrk1, "inf") # First neighbors located at a sqrt(3) distance from the center size = int((1.7320 * n + 1) / 2) for d in [11, 13, 15, 17]: linearClose3D(imIn, imWrk2, d, size, grid) m3D.logic3D(imWrk1, imWrk2, imWrk1, "inf") # Finally neighbors located at a 1 distance from the center size = n for d in [1, 3, 9]: linearClose3D(imIn, imWrk2, d, size, grid) m3D.logic3D(imWrk1, imWrk2, imWrk1, "inf") elif grid == m3D.FACE_CENTER_CUBIC: for d in [1, 3, 5, 7, 8, 9]: linearClose3D(imIn, imWrk2, d, n, grid) m3D.logic3D(imWrk1, imWrk2, imWrk1, "inf") else: mamba.raiseExceptionOnError(core.MB_ERR_BAD_PARAMETER) m3D.copy3D(imWrk1, imOut)
def erode3D(imIn, imOut, n=1, se=CUBOCTAHEDRON1, edge=mamba.FILLED): """ This operator performs an erosion, using the structuring element 'se' (set by default as CUBOCTAHEDRON1), of 3D image 'imIn' and puts the result in 'imOut'. The operation is repeated 'n' times (default is 1). This operator assumes a 'FILLED' edge by default. This operator always considers that the origin of the structuring element in use is at position 0 even if this point does not belong to it. """ (width,height,length) = imIn.getSize() depth = imIn.getDepth() if length!=len(imOut): mamba.raiseExceptionOnError(core.MB_ERR_BAD_SIZE) zext = se.grid.getZExtension() imWrk = m3D.image3DMb(width, height, length+zext*2, depth) if edge==mamba.EMPTY: for i in range(zext): imWrk[i].reset() imWrk[length+zext*2-1-i].reset() else: value = mamba.computeMaxRange(imIn[0])[1] for i in range(zext): imWrk[i].fill(value) imWrk[length+zext*2-1-i].fill(value) m3D.copy3D(imIn, imOut) dirs = se.getDirections(withoutZero=True) for size in range(n): m3D.copy3D(imOut, imWrk, 0, 1) if not se.hasZero(): imOut.fill(m3D.computeMaxRange3D(imIn)[1]) for i in range(length): dirs_enc = se.grid.getEncodedDirs(dirs,i) mamba.infNeighbor(imWrk[i], imOut[i], dirs_enc[-1], grid=se.grid.get2DGrid(), edge=edge) mamba.infNeighbor(imWrk[i+1], imOut[i], dirs_enc[0], grid=se.grid.get2DGrid(), edge=edge) mamba.infNeighbor(imWrk[i+2], imOut[i], dirs_enc[1], grid=se.grid.get2DGrid(), edge=edge)