def directionalOpen(imIn, imOut, d, size, grid=mamba.DEFAULT_GRID, edge=mamba.FILLED): """ Directional opening of image 'imIn' defined by combining an erosion in direction 'd' followed by a dilation in the transposed direction (which depends on the grid in use). Result is put in 'imOut'. """ directionalErode(imIn, imOut, d, size, grid=grid, edge=edge) j = (d + mamba.gridNeighbors(grid=grid) - 1) % (mamba.gridNeighbors(grid=grid) * 2) + 1 directionalDilate(imOut, imOut, j, size, grid=grid)
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 __init__(self, directions, grid): """ Structuring element constructor. A structuring element is defined by the couple 'directions' (given in an ordered list) and 'grid'. You cannot defines a structuring element that holds a direction more than once. You can look at the predefined structuring elements to get examples of how to make yours. """ self.grid = grid xx = {} for d in directions: xx[d] = 1 self.directions = list(xx.keys()) # This is actually a trick to # remove duplicate from the direction # list self.directions.sort() self.directions_w0 = self.directions[:] self.has_zero = False if self.directions_w0.count(0) > 0: self.directions_w0.remove(0) self.has_zero = True self.enc_dirs = functools.reduce(lambda x, y: x | (1 << y), self.directions, 0) self.enc_dirs_w0 = functools.reduce(lambda x, y: x | (1 << y), self.directions_w0, 0) # neighbors defines the max number of neighbor points according to the grid # in use self.neighbors = mamba.gridNeighbors(grid)
def directionalClose(imIn, imOut, d, size, grid=mamba.DEFAULT_GRID, edge=mamba.FILLED): """ Directional closing of image 'imIn' defined by combining a dilation in direction 'd' followed by an erosion in the transposed direction (which depends on the grid in use). Result is put in 'imOut'. If 'edge' is set to 'EMPTY', the operation must be modified to remain extensive. """ imWrk = mamba.imageMb(imIn) if edge==mamba.EMPTY: mamba.copy(imIn, imWrk) directionalDilate(imIn, imOut, d, size, grid=grid, edge=edge) j = (d + mamba.gridNeighbors(grid=grid) - 1) % (mamba.gridNeighbors(grid=grid) * 2) + 1 directionalErode(imOut, imOut, j, size, grid=grid) if edge==mamba.EMPTY: mamba.logic(imOut, imWrk, imOut, "sup")
def computeDiameter(imIn, dir, scale=(1.0, 1.0), grid=mamba.DEFAULT_GRID): """ Computes the diameter (diametral variation) of binary image 'imIn' in direction 'dir'. 'scale' is a tuple defining the horizontal and vertical scale factors (default is 1.0). Beware, if the input image 'imIn' is not a binary image, the function raises an error. """ if imIn.getDepth() != 1: mamba.raiseExceptionOnError(core.MB_ERR_BAD_DEPTH) if dir == 0: return 0.0 dir = ((dir - 1)%(mamba.gridNeighbors(grid)//2)) +1 imWrk = mamba.imageMb(imIn) mamba.copy(imIn, imWrk) mamba.diffNeighbor(imIn, imWrk, 1<<dir, grid=grid) if grid == mamba.HEXAGONAL: l = scale[1] if dir != 2: l = 2*l*scale[0]/math.sqrt(scale[0]*scale[0] + 4*scale[1]*scale[1]) else: if dir == 1: l = scale[0] elif dir == 3: l = scale[1] else: l = scale[0]*scale[1]/math.sqrt(scale[0]*scale[0] + scale[1]*scale[1]) l = l*mamba.computeVolume(imWrk) return l
def computeDiameter(imIn, dir, scale=(1.0, 1.0), grid=mamba.DEFAULT_GRID): """ Computes the diameter (diametral variation) of binary image 'imIn' in direction 'dir'. 'scale' is a tuple defining the horizontal and vertical scale factors (default is 1.0). Beware, if the input image 'imIn' is not a binary image, the function raises an error. """ if imIn.getDepth() != 1: mamba.raiseExceptionOnError(core.MB_ERR_BAD_DEPTH) if dir == 0: return 0.0 dir = ((dir - 1) % (mamba.gridNeighbors(grid) // 2)) + 1 imWrk = mamba.imageMb(imIn) mamba.copy(imIn, imWrk) mamba.diffNeighbor(imIn, imWrk, 1 << dir, grid=grid) if grid == mamba.HEXAGONAL: l = scale[1] if dir != 2: l = 2 * l * scale[0] / math.sqrt(scale[0] * scale[0] + 4 * scale[1] * scale[1]) else: if dir == 1: l = scale[0] elif dir == 3: l = scale[1] else: l = scale[0] * scale[1] / math.sqrt(scale[0] * scale[0] + scale[1] * scale[1]) l = l * mamba.computeVolume(imWrk) return l
def computePerimeter(imIn, scale=(1.0, 1.0), grid=mamba.DEFAULT_GRID): """ Computes the perimeter of all particles in binary image 'imIn' according to the Cauchy-Crofton formula. 'scale' is a tuple defining the horizontal and vertical scale factors (default is 1.0). The edge of the image is always set to 'EMPTY'. Beware, if the input image 'imIn' is not a binary image, the function raises an error. """ if imIn.getDepth() != 1: mamba.raiseExceptionOnError(core.MB_ERR_BAD_DEPTH) p = 0. for i in range(1, mamba.gridNeighbors(grid)//2 + 1): p += computeDiameter(imIn, i, scale=scale, grid=grid) p = 2*math.pi*p/mamba.gridNeighbors(grid) return p
def computePerimeter(imIn, scale=(1.0, 1.0), grid=mamba.DEFAULT_GRID): """ Computes the perimeter of all particles in binary image 'imIn' according to the Cauchy-Crofton formula. 'scale' is a tuple defining the horizontal and vertical scale factors (default is 1.0). The edge of the image is always set to 'EMPTY'. Beware, if the input image 'imIn' is not a binary image, the function raises an error. """ if imIn.getDepth() != 1: mamba.raiseExceptionOnError(core.MB_ERR_BAD_DEPTH) p = 0. for i in range(1, mamba.gridNeighbors(grid) // 2 + 1): p += computeDiameter(imIn, i, scale=scale, grid=grid) p = 2 * math.pi * p / mamba.gridNeighbors(grid) return p
def linearUltimateOpen(imIn, imOut, d, grid=mamba.DEFAULT_GRID): """ This operator performs the ultimate directional opening of the binary image 'imIn' in direction 'd' and puts the result in the 32-bit image 'imOut'. The value of 'imOut' at each point of 'imIn' corresponds to the length of the intercept passing through this point. """ imWrk1 = mamba.imageMb(imIn) imWrk2 = mamba.imageMb(imIn) size = 0 imOut.reset() mamba.copy(imIn, imWrk1) while mamba.computeVolume(imWrk1) != 0: size += 1 directionalErode(imWrk1, imWrk1, d, 1, grid=grid, edge=mamba.EMPTY) td = (d + mamba.gridNeighbors(grid) - 1) % (mamba.gridNeighbors(grid) * 2) + 1 directionalDilate(imWrk1, imWrk2, td, size, grid=grid) mamba.add(imOut, imWrk2, imOut)
def directionalClose(imIn, imOut, d, size, grid=mamba.DEFAULT_GRID, edge=mamba.FILLED): """ Directional closing of image 'imIn' defined by combining a dilation in direction 'd' followed by an erosion in the transposed direction (which depends on the grid in use). Result is put in 'imOut'. If 'edge' is set to 'EMPTY', the operation must be modified to remain extensive. """ imWrk = mamba.imageMb(imIn) if edge == mamba.EMPTY: mamba.copy(imIn, imWrk) directionalDilate(imIn, imOut, d, size, grid=grid, edge=edge) j = (d + mamba.gridNeighbors(grid=grid) - 1) % (mamba.gridNeighbors(grid=grid) * 2) + 1 directionalErode(imOut, imOut, j, size, grid=grid) if edge == mamba.EMPTY: mamba.logic(imOut, imWrk, imOut, "sup")
def rotatingGeodesicThick(imIn, imMask, imOut, dse): """ Performs successive geodesic thickenings 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 thickenings are concatenated. 'imIn', 'imMask' and 'imOut' are binary images. """ imWrk = mamba.imageMb(imIn) mamba.copy(imIn, imOut) for i in range(mamba.gridNeighbors(dse.getGrid())): hitOrMiss(imOut, imWrk, dse) mamba.logic(imWrk, imOut, imOut, "sup") mamba.logic(imMask, imOut, imOut, "inf") dse = dse.rotate()
def cellsFullThin(imIn, imOut, dse, edge=mamba.EMPTY): """ A full thinning transform is performed on each cell of the partition 'imIn' until idempotence. 'dse' is a double structuring element. The result is put in 'imOut'. 'edge' is set to EMPTY by default. """ 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())): cellsThin(imOut, imOut, dse, edge=edge) dse = dse.rotate() v1 = mamba.computeVolume(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 supThick(imIn, imOut, dse): """ Performs a sup of thickenings, each thickening being made with the successive rotations of 'dse'. The initial image 'imIn' is used at each step of thickening (union of thickenings). 'imIn' and 'imOut' are binary images. The edge is always set to EMPTY. """ imWrk1 = mamba.imageMb(imIn) imWrk2 = mamba.imageMb(imIn) mamba.copy(imIn, imWrk1) mamba.copy(imIn, imOut) for i in range(mamba.gridNeighbors(dse.getGrid())): hitOrMiss(imWrk1, imWrk2, dse) mamba.logic(imWrk2, imOut, imOut, "sup") dse = dse.rotate()
def directionalCoding(imIn, imOut, grid=mamba.DEFAULT_GRID): """ Coding of the direction which, at each point of 'imIn', corresponds to the direction of the maximal intercept through this point. The result of this coding is put in the grey scale image 'imOut'. 6 directions are coded on the hexagonal grid, 8 on the square one. """ imWrk1 = mamba.imageMb(imIn, 32) imWrk2 = mamba.imageMb(imIn, 32) imWrk3 = mamba.imageMb(imIn) imWrk4 = mamba.imageMb(imIn, 8) imOut.reset() imWrk1.reset() for i in range(mamba.gridNeighbors(grid=grid)): d = i + 1 linearUltimateOpen(imIn, imWrk2, d, grid=grid) mamba.generateSupMask(imWrk2, imWrk1, imWrk3, True) mamba.logic(imWrk2, imWrk1, imWrk1, "sup") mamba.convertByMask(imWrk3, imWrk4, 0, d) mamba.logic(imWrk4, imOut, imOut, "sup")
def diameterLabelling(imIn, imOut, dir, grid=mamba.DEFAULT_GRID): """ Labels each connected component of the binary image 'imIn' with its diameter in direction 'dir'. The labelled image is stored in the 32-bit image 'imOut'. If 'imIn' is a 8-bit or 32-bit image, this function works too. However, the 0-valued connected components are labelled with 0. This procedure works on hexagonal or square grid. 'dir' can be any strictly positive integer value. """ imWrk1 = mamba.imageMb(imIn, 1) imWrk2 = mamba.imageMb(imIn) ed = 1 << ((dir - 1)%(mamba.gridNeighbors(grid)//2)) +1 if imIn.getDepth() == 1: mamba.copy(imIn, imWrk1) mamba.diffNeighbor(imIn, imWrk1, ed, grid=grid) else: mamba.nonEqualNeighbors(imIn, imWrk2, ed, grid=grid, edge= mamba.EMPTY) mamba.threshold(imWrk2, imWrk1, 1, mamba.computeMaxRange(imWrk2)[1]) # They are used for the labelling. measureLabelling(imIn, imWrk1, imOut)
def diameterLabelling(imIn, imOut, dir, grid=mamba.DEFAULT_GRID): """ Labels each connected component of the binary image 'imIn' with its diameter in direction 'dir'. The labelled image is stored in the 32-bit image 'imOut'. If 'imIn' is a 8-bit or 32-bit image, this function works too. However, the 0-valued connected components are labelled with 0. This procedure works on hexagonal or square grid. 'dir' can be any strictly positive integer value. """ imWrk1 = mamba.imageMb(imIn, 1) imWrk2 = mamba.imageMb(imIn) ed = 1 << ((dir - 1) % (mamba.gridNeighbors(grid) // 2)) + 1 if imIn.getDepth() == 1: mamba.copy(imIn, imWrk1) mamba.diffNeighbor(imIn, imWrk1, ed, grid=grid) else: mamba.nonEqualNeighbors(imIn, imWrk2, ed, grid=grid, edge=mamba.EMPTY) mamba.threshold(imWrk2, imWrk1, 1, mamba.computeMaxRange(imWrk2)[1]) # They are used for the labelling. measureLabelling(imIn, imWrk1, imOut)