def mosaicGradient3D(imIn, imOut, grid=m3D.DEFAULT_GRID3D): """ Builds the mosaic-gradient 3D image of 'imIn' and puts the result in 'imOut'. The mosaic-gradient image is built by computing the differences of two mosaic images generated from 'imIn', the first one having its watershed lines valued by the suprema of the adjacent catchment basins values, the second one been valued by the infima. """ imWrk1 = m3D.image3DMb(imIn) imWrk2 = m3D.image3DMb(imIn) imWrk3 = m3D.image3DMb(imIn) imWrk4 = m3D.image3DMb(imIn) imWrk5 = m3D.image3DMb(imIn) mosaic3D(imIn, imWrk2, imWrk3, grid=grid) m3D.sub3D(imWrk2, imWrk3, imWrk1) m3D.logic3D(imWrk2, imWrk3, imWrk2, "sup") m3D.negate3D(imWrk2, imWrk2) se = m3D.structuringElement3D(m3D.getDirections3D(grid), grid) while m3D.computeVolume3D(imWrk3) != 0: m3D.dilate3D(imWrk1, imWrk4, 2, se=se) m3D.dilate3D(imWrk2, imWrk5, 2, se=se) m3D.logic3D(imWrk4, imWrk3, imWrk4, "inf") m3D.logic3D(imWrk5, imWrk3, imWrk5, "inf") m3D.logic3D(imWrk1, imWrk4, imWrk1, "sup") m3D.logic3D(imWrk2, imWrk5, imWrk2, "sup") m3D.erode3D(imWrk3, imWrk3, 2, se=se) m3D.negate3D(imWrk2, imWrk2) m3D.sub3D(imWrk1, imWrk2, imOut)
def linearClose3D(imIn, imOut, dir, n, grid=m3D.DEFAULT_GRID3D, edge=mamba.FILLED): """ Performs a closing by a segment of size 'n' in direction 'dir'. If 'edge' is set to 'EMPTY', the operation must be modified to remain extensive. """ imWrk = m3D.image3DMb(imIn) if edge == mamba.EMPTY: m3D.copy3D(imIn, imWrk) m3D.linearDilate3D(imIn, imOut, dir, n, grid=grid) m3D.linearErode3D(imOut, imOut, m3D.transposeDirection3D(dir, grid=grid), n, edge=edge, grid=grid) if edge == mamba.EMPTY: m3D.logic3D(imOut, imWrk, imOut, "sup")
def thick3D(imIn, imOut, dse): """ Elementary thickening operator with 'dse' double structuring element. 'imIn' and 'imOut' are binary 3D images. The edge is always EMPTY (as for mamba.hitOrMiss). """ imWrk = m3D.image3DMb(imIn) hitOrMiss3D(imIn, imWrk, dse) m3D.logic3D(imIn, imWrk, imOut, "sup")
def markerControlledWatershed3D(imIn, imMarkers, imOut, grid=m3D.DEFAULT_GRID3D): """ Marker-controlled watershed transform of greyscale or 32-bit 3D image 'imIn'. The binary 3D image 'imMarkers' contains the markers which control the flooding process. 'imOut' contains the valued watershed. """ im_mark = m3D.image3DMb(imIn, 32) imWrk = m3D.image3DMb(imIn) label3D(imMarkers, im_mark, grid=grid) watershedSegment3D(imIn, im_mark, grid=grid) m3D.copyBytePlane3D(im_mark, 3, imWrk) m3D.logic3D(imWrk, imIn, imOut, 'inf')
def minPartialBuild3D(imIn, imMask, imOut, grid=m3D.DEFAULT_GRID3D): """ Performs the partial reconstruction of 'imIn' with its minima which are contained in the binary mask 'imMask'. The result is put in 'imOut'. 'imIn' and 'imOut' must be different and greyscale images. """ imWrk = m3D.image3DMb(imIn, 1) minima3D(imIn, imWrk, 1, grid=grid) m3D.logic3D(imMask, imWrk, imWrk, "inf") m3D.convertByMask3D(imWrk, imOut, mamba.computeMaxRange(imIn[0])[1], 0) m3D.logic3D(imIn, imOut, imOut, "sup") m3D.dualBuild3D(imIn, imOut)
def upperGeodesicErode3D(imIn, imMask, imOut, n=1, se=m3D.CUBOCTAHEDRON1): """ Performs a upper geodesic erosion of 3D image 'imIn' above '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). Warning! 'imMask' and 'imOut' must be different. """ m3D.logic3D(imIn, imMask, imOut, "sup") for i in range(n): m3D.erode3D(imOut, imOut, se=se) m3D.logic3D(imOut, imMask, imOut, "sup")
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 closeHoles3D(imIn, imOut, grid=m3D.DEFAULT_GRID3D): """ Close holes in 3D image 'imIn' and puts the result in 'imOut'. This operator works on binary and greytone images. In this case, however, it should be used cautiously. """ imWrk = m3D.image3DMb(imIn) m3D.negate3D(imIn, imIn) m3D.drawEdge3D(imWrk) m3D.logic3D(imIn, imWrk, imWrk, "inf") build3D(imIn, imWrk, grid=grid) m3D.negate3D(imIn, imIn) m3D.negate3D(imWrk, imOut)
def lowerGeodesicDilate3D(imIn, imMask, imOut, n=1, se=m3D.CUBOCTAHEDRON1): """ Performs a lower geodesic dilation of 3D image 'imIn' below '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, "inf") for i in range(n): m3D.dilate3D(imOut, imOut, se=se) m3D.logic3D(imMask, imOut, imOut, "inf")
def removeEdgeParticles3D(imIn, imOut, grid=m3D.DEFAULT_GRID3D): """ Removes particles (connected components) touching the edge in 3D image 'imIn'. The resulting image is put in image 'imOut'. Although this operator may be used with greytone images, it should be considered with caution. """ imWrk = m3D.image3DMb(imIn) se = m3D.structuringElement3D(m3D.getDirections3D(grid), grid) m3D.dilate3D(imWrk, imWrk, se=se, edge=mamba.FILLED) m3D.logic3D(imIn, imWrk, imWrk, "inf") build3D(imIn, imWrk, grid=grid) m3D.diff3D(imIn, imWrk, imOut)
def closing3D(imIn, imOut, n=1, se=m3D.CUBOCTAHEDRON1, edge=mamba.FILLED): """ Performs a closing operation on 3D image 'imIn' and puts the result in 'imOut'. 'n' controls the size of the closing and 'se' the structuring element used. The default edge is set to 'FILLED'. If 'edge' is set to 'EMPTY', the operation is slightly modified to avoid errors (non extensivity). """ imWrk = m3D.image3DMb(imIn) if edge == mamba.EMPTY: m3D.copy3D(imIn, imWrk) m3D.dilate3D(imIn, imOut, n, se=se) m3D.erode3D(imOut, imOut, n, se=se.transpose(), edge=edge) if edge == mamba.EMPTY: m3D.logic3D(imOut, imWrk, 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 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 autoMedian3D(imIn, imOut, n, se=m3D.CUBOCTAHEDRON1): """ Morphological automedian filter performed with alternate sequential filters. """ oc_im = m3D.image3DMb(imIn) co_im = m3D.image3DMb(imIn) imWrk = m3D.image3DMb(imIn) alternateFilter3D(imIn, oc_im, n, True, se=se) alternateFilter3D(imIn, co_im, n, False, se=se) m3D.copy3D(imIn, imOut) m3D.copy3D(oc_im, imWrk) m3D.logic3D(co_im, imWrk, imWrk, "sup") m3D.logic3D(imWrk, imOut, imOut, "inf") m3D.copy3D(oc_im, imWrk) m3D.logic3D(co_im, imWrk, imWrk, "inf") m3D.logic3D(imWrk, imOut, imOut, "sup")
def supOpen3D(imIn, imOut, n, grid=m3D.DEFAULT_GRID3D): """ Performs the supremum of directional openings. A white particle is preserved (but not entirely) if its length is larger than 'n' in at least one direction. This operator is an opening. The image edge is set to 'EMPTY' in order to take into account particles touching the edge (they are considered as entirely included in 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.reset() # 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]: linearOpen3D(imIn, imWrk2, d, size, grid, edge=mamba.EMPTY) m3D.logic3D(imWrk1, imWrk2, imWrk1, "sup") # 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]: linearOpen3D(imIn, imWrk2, d, size, grid, edge=mamba.EMPTY) m3D.logic3D(imWrk1, imWrk2, imWrk1, "sup") # Finally neighbors located at a 1 distance from the center size = n for d in [1, 3, 9]: linearOpen3D(imIn, imWrk2, d, size, grid, edge=mamba.EMPTY) m3D.logic3D(imWrk1, imWrk2, imWrk1, "sup") elif grid == m3D.FACE_CENTER_CUBIC: for d in [1, 3, 5, 7, 8, 9]: linearOpen3D(imIn, imWrk2, d, n, grid, edge=mamba.EMPTY) m3D.logic3D(imWrk1, imWrk2, imWrk1, "sup") else: mamba.raiseExceptionOnError(core.MB_ERR_BAD_PARAMETER) m3D.copy3D(imWrk1, imOut)
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)