def mosaic(imIn, imOut, imWts, grid=mamba.DEFAULT_GRID): """ Builds the mosaic image of 'imIn' and puts the results into 'imOut'. The watershed line (pixel values set to 255) is stored in the greytone 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 = mamba.imageMb(imIn, 1) imWrk2 = mamba.imageMb(imIn) mamba.copy(imIn, imWrk2) im_mark = mamba.imageMb(imIn, 32) se = mamba.structuringElement(mamba.getDirections(grid), grid) mamba.gradient(imIn, imOut, se=se) mamba.minima(imOut, imWrk1, grid=grid) mamba.add(im_mark, imWrk1, im_mark) imWrk1.convert(8) mamba.build(imWrk1, imWrk2, grid=grid) mamba.add(im_mark, imWrk2, im_mark) watershedSegment(imOut, im_mark, grid=grid) mamba.copyBytePlane(im_mark, 3, imWts) mamba.subConst(im_mark, 1, im_mark) mamba.copyBytePlane(im_mark, 0, imOut)
def quasiDistance(imIn, imOut1, imOut2, grid=mamba.DEFAULT_GRID): """ Quasi-distance of image 'imIn'. 'imOut1' contains the residues image and 'imOut2' contains the quasi-distance (associated function). The quasi-distance of a greytone image is made of a patch of distance functions of some almost flat regions in the image. When the image is a simple indicator function of a set, the quasi-distance and the distance function are identical. Depth of 'imOut1' is the same as 'imIn', depth of 'imOut2' is 32. """ imWrk1 = mamba.imageMb(imIn, 32) imWrk2 = mamba.imageMb(imIn, 32) imWrk3 = mamba.imageMb(imIn, 32) maskIm = mamba.imageMb(imIn, 1) se = mamba.structuringElement(mamba.getDirections(grid), grid) _initialQuasiDist_(imIn, imOut1, imOut2, grid=grid) mamba.copy(imOut2, imWrk1) v1 = mamba.computeVolume(imOut2) v2 = v1 + 1 while v2 > v1: v2 = v1 mamba.erode(imWrk1, imWrk2, se=se) mamba.sub(imWrk1, imWrk2, imWrk2) mamba.threshold(imWrk2, maskIm, 2, mamba.computeMaxRange(imWrk2)[1]) mamba.convertByMask(maskIm, imWrk3, 0, mamba.computeMaxRange(imWrk3)[1]) mamba.logic(imWrk2, imWrk3, imWrk2, "inf") mamba.subConst(imWrk2, 1, imWrk3) mamba.logic(imWrk2, imWrk3, imWrk2, "inf") # Patch non saturated subtraction mamba.sub(imWrk1, imWrk2, imWrk1) v1 = mamba.computeVolume(imWrk1) mamba.copy(imWrk1, imOut2)
def multiSuperpose(imInout, *imIns): """ Superposes multiple binary images ('imIns') to the greyscale image 'imInout'. The binary images are put above the greyscale. The result is meant to be seen with an appropriate color palette. """ imWrk = mamba.imageMb(imInout) mamba.subConst(imInout, len(imIns), imInout) for i,im in enumerate(imIns): mamba.convertByMask(im, imWrk, 0, 256-len(imIns)+i) mamba.logic(imInout, imWrk, imInout, "sup")
def floorSubConst(imIn, v, imOut): """ Subtracts a constant value 'v' to image 'imIn' and puts the result in 'imOut'. If imIn - v is negative, the result is truncated and limited to 0. Note that this operator is mainly useful for 32-bit images, as the result of the subtraction is always truncated for 8-bit images. """ imMask = mamba.imageMb(imIn, 1) imWrk = mamba.imageMb(imIn) mamba.subConst(imIn, v, imWrk) mamba.generateSupMask(imIn, imWrk, imMask, False) mamba.convertByMask(imMask, imOut, 0, mamba.computeMaxRange(imOut)[1]) mamba.logic(imOut, imWrk, imOut, "inf")
def highMaxima(imIn, imOut, h, grid=mamba.DEFAULT_GRID): """ Computes the maxima of the reconstruction of image 'imIn' by imIn - h and puts the result in 'imOut'. Grid used by the build operation can be specified by 'grid'. Only works with 8-bit or 32-bit images as input. 'imOut' must be binary. """ imWrk = mamba.imageMb(imIn) if imIn.getDepth() == 8: mamba.subConst(imIn, h, imWrk) mamba.hierarBuild(imIn, imWrk, grid=grid) else: mamba.floorSubConst(imIn, h, imWrk) mamba.build(imIn, imWrk, grid=grid) maxima(imWrk, imOut, 1, grid=grid)
def subConst3D(imIn, v, imOut): """ Subtracts 'v' value to 'imIn' pixel values and puts the result in 'imOut'. The operation can be sum up in the following formula: imOut = imIn - v 'imIn' and imOut' can be 8-bit or 32-bit images of same size and depth. The operation is saturated (lower limit is 0) for greyscale images. """ outl = len(imOut) inl = len(imIn) if inl!=outl: mamba.raiseExceptionOnError(core.MB_ERR_BAD_SIZE) for i in range(outl): mamba.subConst(imIn[i], v, imOut[i])
def subConst3D(imIn, v, imOut): """ Subtracts 'v' value to 'imIn' pixel values and puts the result in 'imOut'. The operation can be sum up in the following formula: imOut = imIn - v 'imIn' and imOut' can be 8-bit or 32-bit images of same size and depth. The operation is saturated (lower limit is 0) for greyscale images. """ outl = len(imOut) inl = len(imIn) if inl != outl: mamba.raiseExceptionOnError(core.MB_ERR_BAD_SIZE) for i in range(outl): mamba.subConst(imIn[i], v, imOut[i])
def maxDynamics(imIn, imOut, h, grid=mamba.DEFAULT_GRID): """ Extracts the maxima of 'imIn' with a dynamics higher or equal to 'h' and puts the result in 'imOut'. Grid used by the dual build operation can be specified by 'grid'. Only works with 8-bit or 32-bit images as input. 'imOut' must be binary. """ imWrk = mamba.imageMb(imIn) if imIn.getDepth() == 8: mamba.subConst(imIn, h, imWrk) mamba.hierarBuild(imIn, imWrk, grid=grid) mamba.sub(imIn, imWrk, imWrk) else: mamba.floorSubConst(imIn, h, imWrk) mamba.build(imIn, imWrk, grid=grid) mamba.floorSub(imIn, imWrk, imWrk) mamba.threshold(imWrk, imOut, h, mamba.computeMaxRange(imIn)[1])
def maxima(imIn, imOut, h=1, grid=mamba.DEFAULT_GRID): """ Computes the maxima of 'imIn' using a build operation and puts the result in 'imOut'. When 'h' is equal to 1 (default value), the operator provides the maxima of 'imIn'. Grid used by the build operation can be specified by 'grid'. Only works with with 8-bit or 32-bit images as input. 'imOut' must be binary. """ imWrk = mamba.imageMb(imIn) if imIn.getDepth() == 8: mamba.subConst(imIn, h, imWrk) mamba.hierarBuild(imIn, imWrk, grid=grid) mamba.sub(imIn, imWrk, imWrk) else: mamba.floorSubConst(imIn, h, imWrk) mamba.build(imIn, imWrk, grid=grid) mamba.floorSub(imIn, imWrk, imWrk) mamba.threshold(imWrk, imOut, 1, mamba.computeMaxRange(imIn)[1])
# 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)) im8.save('wheel_teeth.png', palette=mambaDisplay.getPalette(name))
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)
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
# 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)) im8.save('wheel_teeth.png', palette=mambaDisplay.getPalette(name))