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 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 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 multiplePoints(imIn, imOut, grid=mamba.DEFAULT_GRID): """ Extracts multiple points in 'imIn', supposed to be a "skeleton" image (connected components without thickness), and puts the result in 'imOut'. Note that, on a square grid, the resulting skeleton is supposed to be defined on a 4-connectivity grid. if it is not the case, some multiple points are likely to be missed. """ imWrk1 = mamba.imageMb(imIn) imWrk2 = mamba.imageMb(imIn) endPoints(imIn, imWrk2) if grid == mamba.HEXAGONAL: dse_list = [hexagonalS1, hexagonalS2] step = 1 nb = 6 else: dse_list = [squareS1, squareS2] step = 2 nb = 4 for dse in dse_list: for i in range(nb): hitOrMiss(imIn, imWrk1, dse) mamba.logic(imWrk1, imWrk2, imWrk2, "sup") dse = dse.rotate(step) mamba.diff(imIn, imWrk2, imOut)
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 endPoints(imIn, imOut, grid=mamba.DEFAULT_GRID, edge=mamba.FILLED): """ Extracts end points in 'imIn', supposed to be a "skeleton" image (connected components without thickness), and puts them in 'imOut'. 'edge' is FILLED by default and it can be modified to take into account extremities touching the edge. """ imWrk1 = mamba.imageMb(imIn) imWrk2 = mamba.imageMb(imIn) if grid == mamba.HEXAGONAL: dse1 = hexagonalE dse2 = hexagonalL nb = 6 step = 1 else: dse1 = squareE dse2 = squareL nb = 4 step = 2 rotatingThin(imIn, imWrk1, dse2, edge=edge) # added to avoid blocking of the process in clipping mamba.diff(imIn, imWrk1, imOut) for i in range(nb): hitOrMiss(imWrk1, imWrk2, dse1, edge=edge) mamba.logic(imOut, imWrk2, imOut, "sup") dse1 = dse1.rotate(step)
def lowerGeodesicErode(imIn, imMask, imOut, n=1, se=mamba.DEFAULT_SE): """ Performs a lower geodesic erosion of 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 (DEFAULT_SE 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: mamba.diff(imMask, imIn, imOut) lowerGeodesicDilate(imOut, imMask, imOut, n, se=se) mamba.diff(imMask, imOut, imOut) else: imWrk1 = mamba.imageMb(imIn) imWrk2 = mamba.imageMb(imIn, 1) mamba.logic(imIn, imMask, imOut, "inf") for i in range(n): mamba.generateSupMask(imOut, imMask, imWrk2, False) mamba.convertByMask(imWrk2, imWrk1, 0, mamba.computeMaxRange(imWrk1)[1]) mamba.logic(imOut, imWrk1, imOut, "sup") mamba.erode(imOut, imOut, se=se) mamba.logic(imOut, imMask, imOut, "inf")
def upperGeodesicDilate(imIn, imMask, imOut, n=1, se=mamba.DEFAULT_SE): """ Performs an upper geodesic dilation of 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 (DEFAULT_SE by default). Warning! 'imMask' and 'imOut' must be different. """ mamba.logic(imIn, imMask, imOut, "sup") if imIn.getDepth() == 1: for i in range(n): mamba.diff(imOut, imMask, imOut) mamba.dilate(imOut, imOut, se=se) mamba.logic(imMask, imOut, imOut, "sup") else: imWrk1 = mamba.imageMb(imIn) imWrk2 = mamba.imageMb(imIn, 1) for i in range(n): mamba.generateSupMask(imOut, imMask, imWrk2, True) mamba.convertByMask(imWrk2, imWrk1, 0, mamba.computeMaxRange(imWrk1)[1]) mamba.logic(imOut, imWrk1, imOut, "inf") mamba.dilate(imOut, imOut, se=se) mamba.logic(imOut, imMask, imOut, "sup")
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 fullGeodesicThin(imIn, imMask, imOut, dse): """ Performs a complete geodesic thinning (until idempotence) of image 'imIn' inside mask 'imMask' with all the rotations of the double structuring element 'dse'. The result is put in 'imOut'. """ mamba.diff(imMask, imIn, imOut) fullGeodesicThick(imOut, imMask, imOut, dse.flip()) mamba.diff(imMask, imOut, imOut)
def geodesicThin(imIn, imMask, imOut, dse): """ Geodesic thinning of image 'imIn' inside 'imMask' by the double structuring element 'dse'. The result is stored in 'imOut'. 'imIn', 'imMask' and 'imOut' are binary images. """ mamba.diff(imMask, imIn, imOut) thick(imOut, imOut, dse.flip()) mamba.diff(imMask, imOut, imOut)
def thin(imIn, imOut, dse, edge=mamba.EMPTY): """ Elementary thinning operator with 'dse' double structuring element. 'imIn' and 'imOut' are binary images. 'edge' is set to EMPTY by default. """ imWrk = mamba.imageMb(imIn) hitOrMiss(imIn, imWrk, dse, edge=edge) mamba.diff(imIn, imWrk, imOut)
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 rotatingGeodesicThin(imIn, imMask, imOut, dse): """ Performs successive geodesic thinnings of 'imIn' inside 'imMask' with clockwise rotations of the double structuring element 'dse'. The number of rotations is either 6 or 8 according to the grid where 'dse' is defined. All the thinnings are concatenated. 'imIn', 'imMask' and 'imOut' are binary images. """ mamba.diff(imMask, imIn, imOut) rotatingGeodesicThick(imOut, imMask, imOut, dse.flip()) mamba.diff(imMask, imOut, imOut)
def firstParticle(imIn, imOut, grid=mamba.DEFAULT_GRID): """ Extraction of the first particle (in scanning order) of binary image imIn. The particle is put into image imOut and removed from imIn. If imIn is empty, imOut is also empty. imIn and imOut must be different. """ imWrk = mamba.imageMb(imIn) mamba.compare(imIn, imWrk, imWrk) mamba.build(imIn, imWrk, grid=grid) mamba.diff(imIn, imWrk, imIn) mamba.copy(imWrk, imOut)
def removeEdgeParticles(imIn, imOut, grid=mamba.DEFAULT_GRID): """ Removes particles (connected components) touching the edge in 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 = mamba.imageMb(imIn) se = mamba.structuringElement(mamba.getDirections(grid), grid) mamba.dilate(imWrk, imWrk, se=se, edge=mamba.FILLED) mamba.logic(imIn, imWrk, imWrk, "inf") build(imIn, imWrk, grid=grid) mamba.diff(imIn, imWrk, imOut)
def infThin(imIn, imOut, dse, edge=mamba.EMPTY): """ Performs an inf of thinnings, each thinning being made with the successive rotations of 'dse'. The initial image 'imIn' is used at each step of thinning (intersection of thinnings). 'imIn' and 'imOut' are binary images. 'edge' is set to EMPTY by default. """ imWrk1 = mamba.imageMb(imIn) imWrk2 = mamba.imageMb(imIn) mamba.copy(imIn, imOut) mamba.copy(imIn, imWrk1) for i in range(mamba.gridNeighbors(dse.getGrid())): hitOrMiss(imWrk1, imWrk2, dse, edge=edge) mamba.diff(imOut, imWrk2, imOut) dse = dse.rotate()
def diff3D(imIn1, imIn2, imOut): """ Performs a set difference between 'imIn1' and 'imIn2' and puts the result in 'imOut'. The set difference will copy 'imIn1' pixels in 'imOut' if the corresponding pixel in 'imIn2' is lower and will write 0 otherwise: imOut = imIn1 if imIn1 > imin2 imOut = 0 otherwise. 'imIn1', imIn2' and 'imOut' can be 1-bit, 8-bit or 32-bit images of same size and depth. """ outl = len(imOut) in1l = len(imIn1) in2l = len(imIn2) if in1l != outl or in2l != outl: mamba.raiseExceptionOnError(core.MB_ERR_BAD_SIZE) for i in range(outl): mamba.diff(imIn1[i], imIn2[i], imOut[i])
def diff3D(imIn1, imIn2, imOut): """ Performs a set difference between 'imIn1' and 'imIn2' and puts the result in 'imOut'. The set difference will copy 'imIn1' pixels in 'imOut' if the corresponding pixel in 'imIn2' is lower and will write 0 otherwise: imOut = imIn1 if imIn1 > imin2 imOut = 0 otherwise. 'imIn1', imIn2' and 'imOut' can be 1-bit, 8-bit or 32-bit images of same size and depth. """ outl = len(imOut) in1l = len(imIn1) in2l = len(imIn2) if in1l!=outl or in2l!=outl: mamba.raiseExceptionOnError(core.MB_ERR_BAD_SIZE) for i in range(outl): mamba.diff(imIn1[i], imIn2[i], imOut[i])
def whiteClip(imIn, imOut, step=0, grid=mamba.DEFAULT_GRID, edge=mamba.FILLED): """ Performs a skeleton clipping of 'imIn' (supposed to contain a skeleton image) and puts the result in 'imOut'. If 'step' is not defined (or equal to 0), the clipping is performed until idempotence. If 'step' is defined, 'step' points (if possible) will be removed from each branch of the skeleton. 'edge' is set to FILLED by default. """ imWrk = mamba.imageMb(imIn) mamba.copy(imIn, imOut) if step == 0: v1 = mamba.computeVolume(imOut) v2 = 0 while v1 != v2: v2 = v1 endPoints(imOut, imWrk, grid=grid, edge=edge) mamba.diff(imOut, imWrk, imOut) v1 = mamba.computeVolume(imOut) else: for i in range(step): endPoints(imOut, imWrk, grid=grid, edge=edge) mamba.diff(imOut, imWrk, imOut)
## 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) mamba.logic(im8, im1, im8, "sup") name = mambaDisplay.tagOneColorPalette(255, (0, 0, 255))
## 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) mamba.logic(im8, im1, im8, "sup") name = mambaDisplay.tagOneColorPalette(255, (0,0,255))