def blackClip(imIn, imOut, step=0, grid=mamba.DEFAULT_GRID): """ Performs a black skeleton clipping (clipping of a black skeleton image). If 'step' is not defined (or equal to 0), the clipping is performed until idempotence. If 'step' is defined, 'step' black points (if possible) will be removed from each branch of the black skeleton. 'edge' is always set to FILLED. """ imWrk = mamba.imageMb(imIn) mamba.negate(imIn, imOut) if step == 0: v1 = mamba.computeVolume(imOut) v2 = 0 while v1 != v2: v2 = v1 endPoints(imOut, imWrk, grid=grid, edge=mamba.FILLED) mamba.diff(imOut, imWrk, imOut) v1 = mamba.computeVolume(imOut) else: for i in range(step): endPoints(imOut, imWrk, grid=grid, edge=mamba.FILLED) mamba.diff(imOut, imWrk, imOut) mamba.negate(imOut, imOut)
def hierarchicalLevel(imIn, imOut, grid=mamba.DEFAULT_GRID): """ Computes the next hierarchical level of image 'imIn' in the waterfalls transformation and puts the result in 'imOut'. This operation makes sure that the next hierarchical level is embedded in the previous one. 'imIn' must be a valued watershed image. """ imWrk0 = mamba.imageMb(imIn) imWrk1 = mamba.imageMb(imIn, 1) imWrk2 = mamba.imageMb(imIn, 1) imWrk3 = mamba.imageMb(imIn, 1) imWrk4 = mamba.imageMb(imIn, 32) mamba.threshold(imIn, imWrk1, 0, 0) mamba.negate(imWrk1, imWrk2) hierarchy(imIn, imWrk2, imWrk0, grid=grid) mamba.minima(imWrk0, imWrk2, grid=grid) mamba.label(imWrk2, imWrk4, grid=grid) mamba.watershedSegment(imWrk0, imWrk4, grid=grid) mamba.copyBytePlane(imWrk4, 3, imWrk0) mamba.threshold(imWrk0, imWrk2, 0, 0) mamba.diff(imWrk1, imWrk2, imWrk3) mamba.build(imWrk1, imWrk3) se = mamba.structuringElement(mamba.getDirections(grid), grid) mamba.dilate(imWrk3, imWrk1, 1, se) mamba.diff(imWrk2, imWrk1, imWrk1) mamba.logic(imWrk1, imWrk3, imWrk1, "sup") mamba.convertByMask(imWrk1, imWrk0, 255, 0) mamba.logic(imIn, imWrk0, imOut, "inf")
def rotatingThin(imIn, imOut, dse, edge=mamba.FILLED): """ Performs a complete rotation of thinnings , the initial 'dse' double structuring element being turned one step clockwise after each thinning. At each rotation step, the previous result is used as input for the next thinning (chained thinnings). Depending on the grid where 'dse' is defined, 6 or 8 rotations are performed. 'imIn' and 'imOut' are binary images. 'edge' is set to FILLED by default (default value is EMPTY in simple thin). """ imWrk = mamba.imageMb(imIn) if edge == mamba.FILLED: mamba.negate(imIn, imOut) for d in mamba.getDirections(dse.getGrid(), True): hitOrMiss(imOut, imWrk, dse.flip(), edge=mamba.EMPTY) mamba.logic(imWrk, imOut, imOut, "sup") dse = dse.rotate() mamba.negate(imOut, imOut) else: mamba.copy(imIn, imOut) for d in mamba.getDirections(dse.getGrid(), True): hitOrMiss(imOut, imWrk, dse, edge=mamba.EMPTY) mamba.diff(imOut, imWrk, imOut) dse = dse.rotate()
def fullThin(imIn, imOut, dse, edge=mamba.EMPTY): """ Performs a complete thinning of 'imIn' with the successive rotations of 'dse' (until idempotence) and puts the result in 'imOut'. 'imIn' and 'imOut' are binary images. 'edge' is set to EMPTY by default. """ if edge == mamba.EMPTY: imWrk = mamba.imageMb(imIn) mamba.copy(imIn, imOut) v1 = mamba.computeVolume(imOut) v2 = 0 while v1 != v2: v2 = v1 for i in range(mamba.gridNeighbors(dse.getGrid())): hitOrMiss(imOut, imWrk, dse) mamba.diff(imOut, imWrk, imOut) dse = dse.rotate() v1 = mamba.computeVolume(imOut) else: mamba.negate(imIn, imOut) v1 = mamba.computeVolume(imOut) v2 = 0 while v1 != v2: v2 = v1 rotatingThick(imOut, imOut, dse.flip()) v1 = mamba.computeVolume(imOut) mamba.negate(imOut, imOut)
def partitionLabel(imIn, imOut): """ This procedure labels each cell of image 'imIn' and puts the result in 'imOut'. The number of cells is returned. 'imIn' can be a 1-bit, 8-bit or a 32-bit image. 'imOut' is a 32-bit image. When 'imIn' is a binary image, all the connected components of the background are also labelled. When 'imIn' is a grey image, the 0-valued cells are also labelled (which is not the case with the label operator. Warning! The label values of adjacent cells are not necessarily consecutive. """ imWrk1 = mamba.imageMb(imIn, 1) imWrk2 = mamba.imageMb(imIn, 32) if imIn.getDepth() == 1: mamba.negate(imIn, imWrk1) else: mamba.threshold(imIn, imWrk1, 0, 0) nb1 = mamba.label(imWrk1, imWrk2) mamba.convertByMask(imWrk1, imOut, mamba.computeMaxRange(imOut)[1], 0) mamba.logic(imOut, imWrk2, imOut, "sup") nb2 = mamba.label(imIn, imWrk2) mamba.addConst(imWrk2, nb1, imWrk2) mamba.logic(imOut, imWrk2, imOut, "inf") return nb1 + nb2
def mosaicGradient(imIn, imOut, grid=mamba.DEFAULT_GRID): """ Builds the mosaic-gradient 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 = mamba.imageMb(imIn) imWrk2 = mamba.imageMb(imIn) imWrk3 = mamba.imageMb(imIn) imWrk4 = mamba.imageMb(imIn) imWrk5 = mamba.imageMb(imIn) imWrk6 = mamba.imageMb(imIn, 1) mosaic(imIn, imWrk2, imWrk3, grid=grid) mamba.sub(imWrk2, imWrk3, imWrk1) mamba.logic(imWrk2, imWrk3, imWrk2, "sup") mamba.negate(imWrk2, imWrk2) mamba.threshold(imWrk3, imWrk6, 1, 255) mamba.multiplePoints(imWrk6, imWrk6, grid=grid) mamba.convertByMask(imWrk6, imWrk3, 0, 255) se = mamba.structuringElement(mamba.getDirections(grid), grid) mamba.dilate(imWrk1, imWrk4, se=se) mamba.dilate(imWrk2, imWrk5, se=se) while mamba.computeVolume(imWrk3) != 0: mamba.dilate(imWrk1, imWrk1, 2, se=se) mamba.dilate(imWrk2, imWrk2, 2, se=se) mamba.logic(imWrk1, imWrk3, imWrk1, "inf") mamba.logic(imWrk2, imWrk3, imWrk2, "inf") mamba.logic(imWrk1, imWrk4, imWrk4, "sup") mamba.logic(imWrk2, imWrk5, imWrk5, "sup") mamba.erode(imWrk3, imWrk3, 2, se=se) mamba.negate(imWrk5, imWrk5) mamba.sub(imWrk4, imWrk5, imOut)
def hierarchicalLevel(imIn, imOut, grid=mamba.DEFAULT_GRID): """ Computes the next hierarchical level of image 'imIn' in the waterfalls transformation and puts the result in 'imOut'. This operation makes sure that the next hierarchical level is embedded in the previous one. 'imIn' must be a valued watershed image. """ imWrk0 = mamba.imageMb(imIn) imWrk1 = mamba.imageMb(imIn, 1) imWrk2 = mamba.imageMb(imIn, 1) imWrk3 = mamba.imageMb(imIn, 1) imWrk4 = mamba.imageMb(imIn, 32) mamba.threshold(imIn,imWrk1, 0, 0) mamba.negate(imWrk1, imWrk2) hierarchy(imIn, imWrk2, imWrk0, grid=grid) mamba.minima(imWrk0, imWrk2, grid=grid) mamba.label(imWrk2, imWrk4, grid=grid) mamba.watershedSegment(imWrk0, imWrk4, grid=grid) mamba.copyBytePlane(imWrk4, 3, imWrk0) mamba.threshold(imWrk0, imWrk2, 0, 0) mamba.diff(imWrk1, imWrk2, imWrk3) mamba.build(imWrk1, imWrk3) se = mamba.structuringElement(mamba.getDirections(grid), grid) mamba.dilate(imWrk3, imWrk1, 1, se) mamba.diff(imWrk2, imWrk1, imWrk1) mamba.logic(imWrk1, imWrk3, imWrk1, "sup") mamba.convertByMask(imWrk1, imWrk0, 255, 0) mamba.logic(imIn, imWrk0, imOut, "inf")
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 generalSegment(imIn, imOut, gain=2.0, offset=1, grid=mamba.DEFAULT_GRID): """ General segmentation algorithm. This algorithm is controlled by two parameters: the 'gain' (identical to the gain used in standard and P segmentation) and a new one, the 'offset'. The 'offset' indicates which level of hierarchy is compared to the current hierarchical image. The 'offset' is relative to the current hierarchical level. If 'offset' is equal to 1, this operator corresponds to the standard segmentation, if 'offset' is equal to 255 (this value stands for the infinity), the operator is equivalent to P algorithm. Image 'imOut' contains all these hierarchies which are embedded. 'imIn' and 'imOut' must be greyscale images. 'imIn' and 'imOut' must be different. This transformation returns the number of hierarchical levels. """ imWrk0 = mamba.imageMb(imIn) imWrk1 = mamba.imageMb(imIn) imWrk2 = mamba.imageMb(imIn) imWrk3 = mamba.imageMb(imIn) imWrk4 = mamba.imageMb(imIn, 1) imWrk5 = mamba.imageMb(imIn, 1) imWrk6 = mamba.imageMb(imIn, 32) mamba.copy(imIn, imWrk1) mamba.mulRealConst(imIn, gain, imWrk6) mamba.floorSubConst(imWrk6, 1, imWrk6) mamba.threshold(imWrk6, imWrk4, 255, mamba.computeMaxRange(imWrk6)[1]) mamba.copyBytePlane(imWrk6, 0, imWrk0) mamba.convert(imWrk4, imWrk2) mamba.logic(imWrk0, imWrk2, imWrk0, "sup") mamba.logic(imWrk0, imWrk1, imWrk0, "sup") imOut.reset() nbLevels = 0 mamba.threshold(imWrk1, imWrk4, 1, 255) flag = not(mamba.checkEmptiness(imWrk4)) while flag: nbLevels += 1 hierarchy(imWrk1, imWrk4, imWrk2, grid=grid) mamba.add(imOut, imWrk4, imOut) v = max(nbLevels - offset, 0) + 1 mamba.threshold(imOut, imWrk4, v, 255) mamba.valuedWatershed(imWrk2, imWrk3, grid=grid) mamba.threshold(imWrk3, imWrk5, 1, 255) flag = not(mamba.checkEmptiness(imWrk5)) hierarchy(imWrk3, imWrk5, imWrk2, grid=grid) mamba.generateSupMask(imWrk0, imWrk2, imWrk5, strict=False) mamba.logic(imWrk4, imWrk5, imWrk4, "inf") mamba.convertByMask(imWrk4, imWrk3, 0, 255) mamba.logic(imWrk1, imWrk3, imWrk3, "inf") mamba.negate(imWrk4, imWrk4) mamba.label(imWrk4, imWrk6, grid=grid) mamba.watershedSegment(imWrk3, imWrk6, grid=grid) mamba.copyBytePlane(imWrk6, 3, imWrk3) mamba.logic(imWrk1, imWrk2, imWrk1, "sup") mamba.logic(imWrk1, imWrk3, imWrk1, "inf") mamba.threshold(imWrk1, imWrk4, 1, 255) return nbLevels
def generalSegment(imIn, imOut, gain=2.0, offset=1, grid=mamba.DEFAULT_GRID): """ General segmentation algorithm. This algorithm is controlled by two parameters: the 'gain' (identical to the gain used in standard and P segmentation) and a new one, the 'offset'. The 'offset' indicates which level of hierarchy is compared to the current hierarchical image. The 'offset' is relative to the current hierarchical level. If 'offset' is equal to 1, this operator corresponds to the standard segmentation, if 'offset' is equal to 255 (this value stands for the infinity), the operator is equivalent to P algorithm. Image 'imOut' contains all these hierarchies which are embedded. 'imIn' and 'imOut' must be greyscale images. 'imIn' and 'imOut' must be different. This transformation returns the number of hierarchical levels. """ imWrk0 = mamba.imageMb(imIn) imWrk1 = mamba.imageMb(imIn) imWrk2 = mamba.imageMb(imIn) imWrk3 = mamba.imageMb(imIn) imWrk4 = mamba.imageMb(imIn, 1) imWrk5 = mamba.imageMb(imIn, 1) imWrk6 = mamba.imageMb(imIn, 32) mamba.copy(imIn, imWrk1) mamba.mulRealConst(imIn, gain, imWrk6) mamba.floorSubConst(imWrk6, 1, imWrk6) mamba.threshold(imWrk6, imWrk4, 255, mamba.computeMaxRange(imWrk6)[1]) mamba.copyBytePlane(imWrk6, 0, imWrk0) mamba.convert(imWrk4, imWrk2) mamba.logic(imWrk0, imWrk2, imWrk0, "sup") mamba.logic(imWrk0, imWrk1, imWrk0, "sup") imOut.reset() nbLevels = 0 mamba.threshold(imWrk1, imWrk4, 1, 255) flag = not (mamba.checkEmptiness(imWrk4)) while flag: nbLevels += 1 hierarchy(imWrk1, imWrk4, imWrk2, grid=grid) mamba.add(imOut, imWrk4, imOut) v = max(nbLevels - offset, 0) + 1 mamba.threshold(imOut, imWrk4, v, 255) mamba.valuedWatershed(imWrk2, imWrk3, grid=grid) mamba.threshold(imWrk3, imWrk5, 1, 255) flag = not (mamba.checkEmptiness(imWrk5)) hierarchy(imWrk3, imWrk5, imWrk2, grid=grid) mamba.generateSupMask(imWrk0, imWrk2, imWrk5, strict=False) mamba.logic(imWrk4, imWrk5, imWrk4, "inf") mamba.convertByMask(imWrk4, imWrk3, 0, 255) mamba.logic(imWrk1, imWrk3, imWrk3, "inf") mamba.negate(imWrk4, imWrk4) mamba.label(imWrk4, imWrk6, grid=grid) mamba.watershedSegment(imWrk3, imWrk6, grid=grid) mamba.copyBytePlane(imWrk6, 3, imWrk3) mamba.logic(imWrk1, imWrk2, imWrk1, "sup") mamba.logic(imWrk1, imWrk3, imWrk1, "inf") mamba.threshold(imWrk1, imWrk4, 1, 255) return nbLevels
def standardSegment(imIn, imOut, gain=2.0, grid=mamba.DEFAULT_GRID): """ General standard segmentation. This algorithm keeps the contours of the watershed transform which are above or equal to the hierarchical image associated to the next level of hierarchy when the altitude of the contour is multiplied by a 'gain' factor (default is 2.0). This transform also ends by idempotence. All the hierarchical levels of image 'imIn'(which is a valued watershed) are computed. 'imOut' contains all these hierarchies which are embedded, so that hierarchy i is simply obtained by a threshold [i+1, 255] of image 'imOut'. 'imIn' and 'imOut' must be greyscale images. 'imIn' and 'imOut' must be different. This transformation returns the number of hierarchical levels. """ imWrk0 = mamba.imageMb(imIn) imWrk1 = mamba.imageMb(imIn) imWrk2 = mamba.imageMb(imIn) imWrk3 = mamba.imageMb(imIn) imWrk4 = mamba.imageMb(imIn, 1) imWrk5 = mamba.imageMb(imIn, 1) imWrk6 = mamba.imageMb(imIn, 32) mamba.copy(imIn, imWrk1) mamba.mulRealConst(imIn, gain, imWrk6) mamba.floorSubConst(imWrk6, 1, imWrk6) mamba.threshold(imWrk6, imWrk4, 255, mamba.computeMaxRange(imWrk6)[1]) mamba.copyBytePlane(imWrk6, 0, imWrk0) mamba.convert(imWrk4, imWrk2) mamba.logic(imWrk0, imWrk2, imWrk0, "sup") mamba.logic(imWrk0, imWrk1, imWrk0, "sup") imOut.reset() nbLevels = 0 mamba.threshold(imWrk1, imWrk4, 1, 255) flag = not(mamba.checkEmptiness(imWrk4)) while flag: hierarchy(imWrk1, imWrk4, imWrk2, grid=grid) mamba.add(imOut, imWrk4, imOut) mamba.valuedWatershed(imWrk2, imWrk3, grid=grid) mamba.threshold(imWrk3, imWrk5, 1, 255) flag = not(mamba.checkEmptiness(imWrk5)) hierarchy(imWrk3, imWrk5, imWrk2, grid=grid) mamba.generateSupMask(imWrk0, imWrk2, imWrk5, strict=False) mamba.logic(imWrk4, imWrk5, imWrk4, "inf") mamba.convertByMask(imWrk4, imWrk3, 0, 255) mamba.logic(imWrk1, imWrk3, imWrk3, "inf") mamba.negate(imWrk4, imWrk4) mamba.label(imWrk4, imWrk6, grid=grid) mamba.watershedSegment(imWrk3, imWrk6, grid=grid) mamba.copyBytePlane(imWrk6, 3, imWrk3) mamba.logic(imWrk1, imWrk2, imWrk1, "sup") mamba.logic(imWrk1, imWrk3, imWrk1, "inf") mamba.threshold(imWrk1, imWrk4, 1, 255) nbLevels += 1 return nbLevels
def standardSegment(imIn, imOut, gain=2.0, grid=mamba.DEFAULT_GRID): """ General standard segmentation. This algorithm keeps the contours of the watershed transform which are above or equal to the hierarchical image associated to the next level of hierarchy when the altitude of the contour is multiplied by a 'gain' factor (default is 2.0). This transform also ends by idempotence. All the hierarchical levels of image 'imIn'(which is a valued watershed) are computed. 'imOut' contains all these hierarchies which are embedded, so that hierarchy i is simply obtained by a threshold [i+1, 255] of image 'imOut'. 'imIn' and 'imOut' must be greyscale images. 'imIn' and 'imOut' must be different. This transformation returns the number of hierarchical levels. """ imWrk0 = mamba.imageMb(imIn) imWrk1 = mamba.imageMb(imIn) imWrk2 = mamba.imageMb(imIn) imWrk3 = mamba.imageMb(imIn) imWrk4 = mamba.imageMb(imIn, 1) imWrk5 = mamba.imageMb(imIn, 1) imWrk6 = mamba.imageMb(imIn, 32) mamba.copy(imIn, imWrk1) mamba.mulRealConst(imIn, gain, imWrk6) mamba.floorSubConst(imWrk6, 1, imWrk6) mamba.threshold(imWrk6, imWrk4, 255, mamba.computeMaxRange(imWrk6)[1]) mamba.copyBytePlane(imWrk6, 0, imWrk0) mamba.convert(imWrk4, imWrk2) mamba.logic(imWrk0, imWrk2, imWrk0, "sup") mamba.logic(imWrk0, imWrk1, imWrk0, "sup") imOut.reset() nbLevels = 0 mamba.threshold(imWrk1, imWrk4, 1, 255) flag = not (mamba.checkEmptiness(imWrk4)) while flag: hierarchy(imWrk1, imWrk4, imWrk2, grid=grid) mamba.add(imOut, imWrk4, imOut) mamba.valuedWatershed(imWrk2, imWrk3, grid=grid) mamba.threshold(imWrk3, imWrk5, 1, 255) flag = not (mamba.checkEmptiness(imWrk5)) hierarchy(imWrk3, imWrk5, imWrk2, grid=grid) mamba.generateSupMask(imWrk0, imWrk2, imWrk5, strict=False) mamba.logic(imWrk4, imWrk5, imWrk4, "inf") mamba.convertByMask(imWrk4, imWrk3, 0, 255) mamba.logic(imWrk1, imWrk3, imWrk3, "inf") mamba.negate(imWrk4, imWrk4) mamba.label(imWrk4, imWrk6, grid=grid) mamba.watershedSegment(imWrk3, imWrk6, grid=grid) mamba.copyBytePlane(imWrk6, 3, imWrk3) mamba.logic(imWrk1, imWrk2, imWrk1, "sup") mamba.logic(imWrk1, imWrk3, imWrk1, "inf") mamba.threshold(imWrk1, imWrk4, 1, 255) nbLevels += 1 return nbLevels
def _generateMask_(imIn1, imIn2, imOut): #This procedure is used internally by the residues operators. It computes #a mask indicating the points in the image where 'imIn1' is greater or equal #to 'imIn2' with 'imIn1' strictly positive. #Depth of 'imOut' is 1. imWrk = mamba.imageMb(imOut) mamba.generateSupMask(imIn1, imIn2, imOut, False) if imIn1.getDepth()==1: mamba.negate(imIn1, imWrk) else: mamba.threshold(imIn1, imWrk, 0, 0) mamba.diff(imOut, imWrk, imOut)
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 negate3D(imIn, imOut): """ Negates the 3D image 'imIn' and puts the result in 'imOut'. The operation is a binary complement for binary images and a negation for greyscale and 32-bit images. """ outl = len(imOut) inl = len(imIn) if inl!=outl: mamba.raiseExceptionOnError(core.MB_ERR_BAD_SIZE) for i in range(outl): mamba.negate(imIn[i], imOut[i])
def negate3D(imIn, imOut): """ Negates the 3D image 'imIn' and puts the result in 'imOut'. The operation is a binary complement for binary images and a negation for greyscale and 32-bit images. """ outl = len(imOut) inl = len(imIn) if inl != outl: mamba.raiseExceptionOnError(core.MB_ERR_BAD_SIZE) for i in range(outl): mamba.negate(imIn[i], imOut[i])
def partitionErode(imIn, imOut, n=1, grid=mamba.DEFAULT_GRID): """ Graph erosion of the corresponding partition image 'imIn'. The size is given by 'n'. The corresponding partition image of the resulting eroded graph is put in 'imOut'. 'grid' can be set to HEXAGONAL or SQUARE. """ imWrk = mamba.imageMb(imIn) mamba.negate(imIn, imWrk) mamba.copy(imWrk, imOut) se = mamba.structuringElement(mamba.getDirections(grid), grid) for i in range(n): mamba.dilate(imOut, imOut, se=se) cellsBuild(imWrk, imOut, grid=grid) mamba.negate(imOut, imOut)
def extendedSegment(imIn, imTest, imOut, offset=255, grid=mamba.DEFAULT_GRID): """ Extended (experimental) segmentation algorithm. This algorithm is controlled by image 'imTest'. The current hierarchical image is compared to image 'imTest'. This image must be a greyscale image. The 'offset' indicates which level of hierarchy is compared to the current hierarchical image. The 'offset' is relative to the current hierarchical level (by default, 'offset' is equal to 255, so that the initial segmentation is used). Image 'imOut' contains all these hierarchies which are embedded. 'imIn', 'imTest' and 'imOut' must be greyscale images. 'imIn', 'imTest' and 'imOut' must be different. This transformation returns the number of hierarchical levels. """ imWrk1 = mamba.imageMb(imIn) imWrk2 = mamba.imageMb(imIn) imWrk3 = mamba.imageMb(imIn) imWrk4 = mamba.imageMb(imIn, 1) imWrk5 = mamba.imageMb(imIn, 1) imWrk6 = mamba.imageMb(imIn, 32) mamba.copy(imIn, imWrk1) imOut.reset() nbLevels = 0 mamba.threshold(imWrk1, imWrk4, 1, 255) flag = not(mamba.checkEmptiness(imWrk4)) while flag: nbLevels += 1 hierarchy(imWrk1, imWrk4, imWrk2, grid=grid) mamba.add(imOut, imWrk4, imOut) v = max(nbLevels - offset, 0) + 1 mamba.threshold(imOut, imWrk4, v, 255) mamba.valuedWatershed(imWrk2, imWrk3, grid=grid) mamba.threshold(imWrk3, imWrk5, 1, 255) flag = not(mamba.checkEmptiness(imWrk5)) hierarchy(imWrk3, imWrk5, imWrk2, grid=grid) mamba.generateSupMask(imTest, imWrk2, imWrk5, strict=False) mamba.logic(imWrk4, imWrk5, imWrk4, "inf") mamba.convertByMask(imWrk4, imWrk3, 0, 255) mamba.logic(imWrk1, imWrk3, imWrk3, "inf") mamba.negate(imWrk4, imWrk4) mamba.label(imWrk4, imWrk6, grid=grid) mamba.watershedSegment(imWrk3, imWrk6, grid=grid) mamba.copyBytePlane(imWrk6, 3, imWrk3) mamba.logic(imWrk1, imWrk2, imWrk1, "sup") mamba.logic(imWrk1, imWrk3, imWrk1, "inf") mamba.threshold(imWrk1, imWrk4, 1, 255) return nbLevels
def extendedSegment(imIn, imTest, imOut, offset=255, grid=mamba.DEFAULT_GRID): """ Extended (experimental) segmentation algorithm. This algorithm is controlled by image 'imTest'. The current hierarchical image is compared to image 'imTest'. This image must be a greyscale image. The 'offset' indicates which level of hierarchy is compared to the current hierarchical image. The 'offset' is relative to the current hierarchical level (by default, 'offset' is equal to 255, so that the initial segmentation is used). Image 'imOut' contains all these hierarchies which are embedded. 'imIn', 'imTest' and 'imOut' must be greyscale images. 'imIn', 'imTest' and 'imOut' must be different. This transformation returns the number of hierarchical levels. """ imWrk1 = mamba.imageMb(imIn) imWrk2 = mamba.imageMb(imIn) imWrk3 = mamba.imageMb(imIn) imWrk4 = mamba.imageMb(imIn, 1) imWrk5 = mamba.imageMb(imIn, 1) imWrk6 = mamba.imageMb(imIn, 32) mamba.copy(imIn, imWrk1) imOut.reset() nbLevels = 0 mamba.threshold(imWrk1, imWrk4, 1, 255) flag = not (mamba.checkEmptiness(imWrk4)) while flag: nbLevels += 1 hierarchy(imWrk1, imWrk4, imWrk2, grid=grid) mamba.add(imOut, imWrk4, imOut) v = max(nbLevels - offset, 0) + 1 mamba.threshold(imOut, imWrk4, v, 255) mamba.valuedWatershed(imWrk2, imWrk3, grid=grid) mamba.threshold(imWrk3, imWrk5, 1, 255) flag = not (mamba.checkEmptiness(imWrk5)) hierarchy(imWrk3, imWrk5, imWrk2, grid=grid) mamba.generateSupMask(imTest, imWrk2, imWrk5, strict=False) mamba.logic(imWrk4, imWrk5, imWrk4, "inf") mamba.convertByMask(imWrk4, imWrk3, 0, 255) mamba.logic(imWrk1, imWrk3, imWrk3, "inf") mamba.negate(imWrk4, imWrk4) mamba.label(imWrk4, imWrk6, grid=grid) mamba.watershedSegment(imWrk3, imWrk6, grid=grid) mamba.copyBytePlane(imWrk6, 3, imWrk3) mamba.logic(imWrk1, imWrk2, imWrk1, "sup") mamba.logic(imWrk1, imWrk3, imWrk1, "inf") mamba.threshold(imWrk1, imWrk4, 1, 255) return nbLevels
def simpleLevelling(imIn, imMask, imOut, grid=mamba.DEFAULT_GRID): """ Performs a simple levelling of 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 = mamba.imageMb(imIn) imWrk2 = mamba.imageMb(imIn) mask_im = mamba.imageMb(imIn, 1) mamba.logic(imIn, imMask, imWrk1, "inf") mamba.build(imIn, imWrk1, grid=grid) mamba.logic(imIn, imMask, imWrk2, "sup") mamba.dualBuild(imIn, imWrk2, grid=grid) mamba.generateSupMask(imIn, imMask, mask_im, False) mamba.convertByMask(mask_im, imOut, 0, mamba.computeMaxRange(imIn)[1]) mamba.logic(imOut, imWrk1, imWrk1, "inf") mamba.negate(imOut, imOut) mamba.logic(imOut, imWrk2, imOut, "inf") mamba.logic(imWrk1, imOut, imOut, "sup")
def tlaferm((name, thresh, outpath)): # =============== Load numpy file and create contours then save png ========= # threshold=25 # print 'name= ',name zr = np.load(name) im = Image.fromarray(zr.T) # xpixels, ypixels = im.size[1], im.size[0] dpi, scalefactor = 72, 1 xinch = xpixels * scalefactor / dpi yinch = ypixels * scalefactor / dpi # fig = plt.figure(figsize=(xinch, yinch)) ax = plt.axes([0, 0, 1, 1], frame_on=False, xticks=[], yticks=[]) imc = plt.contourf(zr, [0, thresh], colors='black', origin='image') ## imfile = 'image_' + os.path.basename(name) + '.png' impath = os.path.dirname(name) plt.savefig(os.path.join(outpath, imfile), dpi=dpi) plt.close('all') # # =================== load png and use mamba then save ====================================== im = mamba.imageMb(os.path.join(outpath, imfile)) im1, im2, im3, im4 = mamba.imageMb(im), mamba.imageMb(im), mamba.imageMb( im), mamba.imageMb(im) se = mamba.structuringElement([0, 1, 2, 3, 4, 5, 6], grid=mamba.HEXAGONAL) mamba.closeHoles(im, im) mamba.negate(im, im) mamba.closeHoles(im, im) mamba.negate(im, im) # #mamba.opening(im, im1, n=2, se=se) ma_file = 'mamba_' + imfile + '.png' im.save(os.path.join(outpath, ma_file))
def tlaferm((name,thresh,outpath)): # =============== Load numpy file and create contours then save png ========= # threshold=25 # print 'name= ',name zr=np.load(name) im=Image.fromarray(zr.T) # xpixels,ypixels = im.size[1],im.size[0] dpi,scalefactor = 72,1 xinch = xpixels * scalefactor / dpi yinch = ypixels * scalefactor / dpi # fig = plt.figure(figsize=(xinch,yinch)) ax = plt.axes([0, 0, 1, 1], frame_on=False, xticks=[], yticks=[]) imc=plt.contourf(zr, [0,thresh], colors='black', origin='image') ## imfile='image_'+os.path.basename(name)+'.png' impath=os.path.dirname(name) plt.savefig(os.path.join(outpath,imfile), dpi=dpi) plt.close('all') # # =================== load png and use mamba then save ====================================== im = mamba.imageMb(os.path.join(outpath,imfile)) im1,im2,im3,im4=mamba.imageMb(im),mamba.imageMb(im),mamba.imageMb(im),mamba.imageMb(im) se = mamba.structuringElement([0,1,2,3,4,5,6],grid=mamba.HEXAGONAL) mamba.closeHoles(im, im) mamba.negate(im, im) mamba.closeHoles(im, im) mamba.negate(im, im) # #mamba.opening(im, im1, n=2, se=se) ma_file='mamba_'+imfile+'.png' im.save(os.path.join(outpath,ma_file))
def closeHoles(imIn, imOut, grid=mamba.DEFAULT_GRID): """ Close holes in 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 = mamba.imageMb(imIn) mamba.negate(imIn, imIn) mamba.drawEdge(imWrk) mamba.logic(imIn, imWrk, imWrk, "inf") build(imIn, imWrk, grid=grid) mamba.negate(imIn, imIn) mamba.negate(imWrk, imOut)
def feretDiameterLabelling(imIn, imOut, direc): """ The Feret diameter of each connected component of the binary image or the partition image 'imIn' is computed and its value labels the corresponding component. The labelled image is stored in the 32-bit image 'imOut'. If 'direc' is "vertical", the vertical Feret diameter is computed. If it is set to "horizontal", the corresponding diameter is used. """ imWrk1 = mamba.imageMb(imIn, 1) imWrk2 = mamba.imageMb(imIn, 32) imWrk3 = mamba.imageMb(imIn, 32) imWrk4 = mamba.imageMb(imIn, 32) imWrk1.fill(1) if direc == "horizontal": dir = 7 elif direc == "vertical": dir = 1 else: mamba.raiseExceptionOnError(core.MB_ERR_BAD_DIRECTION) # The above statement generates an error ('direc' is not horizontal or # vertical. # An horizontal or vertical distance function is generated. mamba.linearErode(imWrk1, imWrk1, dir, grid=mamba.SQUARE, edge=mamba.EMPTY) mamba.computeDistance(imWrk1, imOut, grid=mamba.SQUARE, edge=mamba.FILLED) mamba.addConst(imOut, 1, imOut) if imIn.getDepth() == 1: # Each particle is valued with the distance. mamba.convertByMask(imIn, imWrk2, 0, mamba.computeMaxRange(imWrk3)[1]) mamba.logic(imOut, imWrk2, imWrk3, "inf") # The valued image is preserved. mamba.copy(imWrk3, imWrk4) # Each component is labelled by the maximal coordinate. mamba.build(imWrk2, imWrk3) # Using the dual reconstruction, we label the particles with the # minimal ccordinate. mamba.negate(imWrk2, imWrk2) mamba.logic(imWrk2, imWrk4, imWrk4, "sup") mamba.dualBuild(imWrk2, imWrk4) # We subtract 1 because the selected coordinate must be outside the particle. mamba.subConst(imWrk4, 1, imWrk4) mamba.negate(imWrk2, imWrk2) mamba.logic(imWrk2, imWrk4, imWrk4, "inf") # Then, the subtraction gives the Feret diameter. mamba.sub(imWrk3, imWrk4, imOut) else: mamba.copy(imOut, imWrk3) if imIn.getDepth() == 32: mamba.copy(imIn, imWrk2) else: mamba.convert(imIn, imWrk2) # Using the cells builds (direct and dual to label the cells with the maximum # and minimum distance. mamba.cellsBuild(imWrk2, imWrk3) mamba.cellsBuild(imWrk2, imWrk3) mamba.negate(imOut, imOut) mamba.cellsBuild(imWrk2, imOut) mamba.negate(imOut, imOut) # Subtracting 1... mamba.subConst(imOut, 1, imOut) # ... and getting the final result. mamba.sub(imWrk3, imOut, imOut)
# a problem of detection and counting of the teeth of a notched wheel when it is # associated to a preliminary selection of the zone where these teeth should be. ## SCRIPT ###################################################################### # Importing mamba import mamba import mambaDisplay im = mamba.imageMb("wheel.png", 1) im1 = mamba.imageMb(im, 1) im2 = mamba.imageMb(im, 1) # Opening of image mamba.opening(im, im1, 3) # Selection of the outside region mamba.negate(im1, im2) mamba.removeEdgeParticles(im2, im1) mamba.diff(im2, im1, im2) # Extracting the wheel teeth mamba.logic(im, im2, im2, "inf") # Cleaning the image mamba.opening(im2, im2) # Counting and marking each tooth mamba.thinD(im2, im1) nb_teeth = mamba.computeVolume(im1) print("Number of teeth: %d" % (nb_teeth)) mamba.dilate(im1, im1, 3, mamba.SQUARE3X3) im1.convert(8) im8 = mamba.imageMb(im, 8) mamba.convert(im, im8) mamba.subConst(im8, 1, im8)
def _watershed_using_quasi_distance(self): """ 疑似ユークリッド距離(Quasi Distance) に基づく Watershed 領域分割 Returns ------- numpy.ndarray 領域分割線の画像 """ from mamba import ( imageMb, gradient, add, negate, quasiDistance, copyBytePlane, subConst, build, maxima, label, watershedSegment, logic, mix, ) from utils.convert import mamba2np, np2mamba # Channel Split if self.src_img.ndim == 3: b, g, r = [np2mamba(self.src_img[:, :, i]) for i in range(3)] elif self.src_img.ndim == 2: b, g, r = [np2mamba(self.src_img)] * 3 # We will perform a thick gradient on each color channel (contours in original # picture are more or less fuzzy) and we add all these gradients gradient = imageMb(r) tmp_1 = imageMb(r) gradient.reset() gradient(r, tmp_1, 2) add(tmp_1, gradient, gradient) gradient(g, tmp_1, 2) add(tmp_1, gradient, gradient) gradient(b, tmp_1, 2) add(tmp_1, gradient, gradient) # Then we invert the gradient image and we compute its quasi-distance quasi_dist = imageMb(gradient, 32) negate(gradient, gradient) quasiDistance(gradient, tmp_1, quasi_dist) if self.is_logging: self.logger.logging_img(tmp_1, "quasi_dist_gradient") self.logger.logging_img(quasi_dist, "quasi_dist") # The maxima of the quasi-distance are extracted and filtered (too close maxima, # less than 6 pixels apart, are merged) tmp_2 = imageMb(r) marker = imageMb(gradient, 1) copyBytePlane(quasi_dist, 0, tmp_1) subConst(tmp_1, 3, tmp_2) build(tmp_1, tmp_2) maxima(tmp_2, marker) # The marker-controlled watershed of the gradient is performed watershed = imageMb(gradient) label(marker, quasi_dist) negate(gradient, gradient) watershedSegment(gradient, quasi_dist) copyBytePlane(quasi_dist, 3, watershed) # The segmented binary and color image are stored logic(r, watershed, r, "sup") logic(g, watershed, g, "sup") logic(b, watershed, b, "sup") segmented_image = mix(r, g, b) if self.is_logging: self.logger.logging_img(segmented_image, "segmented_image") watershed = mamba2np(watershed) return watershed