def mosaic3D(imIn, imOut, imWts, grid=m3D.DEFAULT_GRID3D): """ Builds the mosaic 3D image of 'imIn' and puts the results into 'imOut'. The watershed line (pixel values set to 255) is stored in the greytone 3D image 'imWts'. A mosaic image is a simple image made of various tiles of uniform grey values. It is built using the watershed of 'imIn' gradient and original markers made of gradient minima which are labelled by the maximum value of 'imIn' pixels inside them. """ imWrk1 = m3D.image3DMb(imIn, 1) imWrk2 = m3D.image3DMb(imIn) m3D.copy3D(imIn, imWrk2) im_mark = m3D.image3DMb(imIn, 32) se = m3D.structuringElement3D(m3D.getDirections3D(grid), grid) m3D.gradient3D(imIn, imOut, se=se) m3D.minima3D(imOut, imWrk1, grid=grid) m3D.add3D(im_mark, imWrk1, im_mark) imWrk1.convert(8) m3D.build3D(imWrk1, imWrk2, grid=grid) m3D.add3D(im_mark, imWrk2, im_mark) m3D.watershedSegment3D(imOut, im_mark, grid=grid) m3D.copyBytePlane3D(im_mark, 3, imWts) m3D.subConst3D(im_mark, 1, im_mark) m3D.copyBytePlane3D(im_mark, 0, imOut)
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 drawEdge3D(imOut, thick=1): """ Draws a frame around the edge of 'imOut' whose value equals the maximum range value and whose thickness is given by 'thick' (default 1). """ imOut.reset() se=m3D.structuringElement3D(list(m3D.getDirections3D(m3D.CUBIC)), m3D.CUBIC) m3D.dilate3D(imOut, imOut, thick, se=se, edge=mamba.FILLED)
def hitOrMiss3D(imIn, imOut, dse, edge=mamba.EMPTY): """ Performs a binary Hit-or-miss operation on 3D image 'imIn' using the doubleStructuringElement3D 'dse'. Result is put in 'imOut'. WARNING! 'imIn' and 'imOut' must be different images. """ (width,height,length) = imIn.getSize() depth = imIn.getDepth() if depth!=1: mamba.raiseExceptionOnError(core.MB_ERR_BAD_DEPTH) if length!=len(imOut): mamba.raiseExceptionOnError(core.MB_ERR_BAD_SIZE) zext = dse.grid.getZExtension() imWrk = m3D.image3DMb(width, height, length+zext*2, depth) # Border handling imWrk.reset() m3D.copy3D(imIn, imWrk, firstPlaneOut=1) if edge==mamba.FILLED: m3D.negate3D(imWrk, imWrk) for i in range(zext): imWrk[i].reset() imWrk[length+zext*2-1-i].reset() dse = dse.flip() # Central point if dse.se1.hasZero(): m3D.copy3D(imWrk, imOut, firstPlaneIn=1) else: if dse.se0.hasZero(): for i in range(length): mamba.negate(imWrk[i+1], imOut[i]) else: imOut.fill(1) # Other directions dirs = m3D.getDirections3D(dse.getGrid(), True) dirs0 = dse.se0.getDirections() dirs1 = dse.se1.getDirections() grid2D = dse.getGrid().get2DGrid() for d in dirs: if d in dirs1: for i in range(length): (planeOffset, dc) = dse.getGrid().convertFromDir(d,i) mamba.infNeighbor(imWrk[i+1+planeOffset], imOut[i], 1<<dc, grid=grid2D, edge=edge) elif d in dirs0: for i in range(length): (planeOffset, dc) = dse.getGrid().convertFromDir(d,i) mamba.diffNeighbor(imWrk[i+1+planeOffset], imOut[i], 1<<dc, grid=grid2D, edge=edge)
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 regularisedGradient3D(imIn, imOut, n, grid=m3D.DEFAULT_GRID3D): """ Computes the regularized gradient of 3D image 'imIn' of size 'n'. The result is put in 'imOut'. A regularized gradient of size 'n' extracts in the 3D image contours thinner than 'n' while avoiding false detections. This operation is only valid for omnidirectional structuring elements. """ imWrk = m3D.image3DMb(imIn) se = m3D.structuringElement3D(m3D.getDirections3D(grid), grid) gradient3D(imIn, imWrk, n, se=se) whiteTopHat3D(imWrk, imWrk, n, se=se) m3D.erode3D(imWrk, imOut, n-1, se=se)
def strongLevelling3D(imIn, imOut, n, eroFirst, grid=m3D.DEFAULT_GRID3D): """ Strong levelling of 'imIn', result in 'imOut'. 'n' defines the size of the erosion and dilation of 'imIn' in the operation. If 'eroFirst' is true, the operation starts with an erosion, it starts with a dilation otherwise. This filter is stronger (more efficient) that simpleLevelling3D. However, the order of the initial operations (erosion and dilation) matters. """ imWrk = m3D.image3DMb(imIn) se = m3D.structuringElement3D(m3D.getDirections3D(grid), grid) if eroFirst: m3D.erode3D(imIn, imWrk, n, se=se) m3D.build3D(imIn, imWrk, grid=grid) m3D.dilate3D(imIn, imOut, n, se=se) m3D.dualBuild3D(imWrk, imOut, grid=grid) else: m3D.dilate3D(imIn, imWrk, n, se=se) m3D.dualBuild3D(imIn, imWrk, grid=grid) m3D.erode3D(imIn, imOut, n, se=se) m3D.build3D(imWrk, imOut, grid=grid)