def pyramidAreaAveraging(img, top_level, min_width=32, sumType=UnsignedLongType, mathType=UnsignedLongType, converter=Util.genericIntegerTypeConverter()): """ Return a list of image views, one per scale level of the image pyramid, except for level zero (the first image) which is the provided img. All images are of the same type as the source img. Based on an integral image for fast computation. """ img_type = img.randomAccess().get().createVariable() # Create an integral image in longs alg = IntegralImg(img, sumType(), converter) alg.process() integralImg = alg.getResult() # Create an image pyramid as views, with ImgMath and imglib2, # which amounts to scale area averaging sped up by the integral image # and generated on demand whenever each pyramid level is read. width = img.dimension(0) imgE = Views.extendBorder(integralImg) blockSide = 1 level_index = 1 # Corners for level 1: a box of 2x2 corners = [[0, 0], [1, 0], [0, 1], [1, 1]] pyramid = [img] while width > min_width and level_index <= top_level: blockSide *= 2 width /= 2 # Scale the corner coordinates to make the block larger cs = [[c * blockSide for c in corner] for corner in corners] blockRead = div(block(imgE, cs), pow(blockSide, 2)) # the op # a RandomAccessibleInterval view of the op, computed with shorts but seen as bytes view = blockRead.view(mathType(), img_type.createVariable()) # Views.subsample by 2 will turn a 512-pixel width to a 257 width, # so crop to proper interval 256 level = Views.interval(Views.subsample(view, blockSide), [0] * img.numDimensions(), # min [img.dimension(d) / blockSide -1 for d in xrange(img.numDimensions())]) # max pyramid.append(level) level_index += 1 # for next iteration return pyramid
def filterBankBlockStatistics(img, block_width=5, block_height=5, integralImgE=None, sumType=UnsignedLongType(), converter=Util.genericRealTypeConverter()): # corners of a block centered at the pixel block_width = int(block_width) block_height = int(block_height) w0 = -block_width/2 # e.g. -2 when block_width == 4 and also when block_width == 5 h0 = -block_height/2 decX = 1 if 0 == block_width % 2 else 0 decY = 1 if 0 == block_height % 2 else 0 w1 = block_width/2 - decX # e.g. 2 when block_width == 5 but 1 when block_width == 4 h1 = block_height/2 - decY corners = [[w0, h0], [w1, h0], [w0, h1], [w1, h1]] # Create the integral image, stored as 64-bit if not integralImgE: alg = IntegralImg(img, sumType, converter) alg.process() integralImgE = Views.extendBorder(alg.getResult()) # Create the integral image of squares, stored as 64-bit sqimg = compute(power(img, 2)).view(sumType) algSq = IntegralImg(sqimg, sumType, converter) algSq.process() integralImgSqE = Views.extendBorder(algSq.getResult()) # block mean: creates holes in blurred membranes opMean = div(block(integralImgSqE, corners), block_width * block_height) # block variance: sum of squares minus square of sum opVariance = sub(block(integralImgSqE, corners), power(block(integralImgE, corners), 2)) opVarianceNonZero = let("var", opVariance, IF(LT("var", 0), THEN(0), ELSE("var"))) return [opMean, opVarianceNonZero]
LoopBuilder.setImages(red, green, img_sub).forEachPixel(Consumer(threshold)) IL.wrap(img_sub, "LoopBuilder").show() # Takes 10 seconds!! An eternity compared to the other two fast methods # Example 3: with Imglib2 ImgMath from net.imglib2.algorithm.math.ImgMath import compute, IF, THEN, ELSE, greaterThan from net.imglib2.algorithm.math.abstractions import Util from net.imglib2.converter import Converters from net.imglib2.img.array import ArrayImgs from net.imglib2.util import Intervals from net.imglib2.img.display.imagej import ImageJFunctions as IL img = IL.wrap(imp_rgb) # an ARGBType Img red = Converters.argbChannel(img, 1) # a view of the ARGB red channel green = Converters.argbChannel(img, 2) # a view of the ARGB green channel operation = IF(greaterThan(red, threshold -1), THEN(green), ELSE(0)) print Util.hierarchy(operation) IL.wrap(operation.view(), "ImgMath view").show() img_sub = compute(operation).into(ArrayImgs.unsignedBytes(Intervals.dimensionsAsLongArray(img))) IL.wrap(img_sub, "ImgMath img").show()
from net.imglib2.algorithm.math.ImgMath import compute, block, div, offset, add from net.imglib2.algorithm.math.abstractions import Util from net.imglib2.algorithm.integral import IntegralImg from net.imglib2.img.display.imagej import ImageJFunctions as IL from net.imglib2.type.numeric.integer import UnsignedByteType, \ UnsignedShortType, UnsignedLongType from net.imglib2.view import Views from ij import IJ imp = IJ.getImage() # e.g. EM of Drosophila neurons 180-220-sub512x512-30.tif #imp = IJ.openImage("/home/albert/lab/TEM/abd/microvolumes/Seg/180-220-sub/180-220-sub512x512-30.tif") img = compute(IL.wrap(imp)).intoImg(UnsignedShortType()) # in 16-bit # Create an integral image in longs alg = IntegralImg(img, UnsignedLongType(), Util.genericIntegerTypeConverter()) alg.process() integralImg = alg.getResult() # Create an image pyramid as views, with ImgMath and imglib2, # which amounts to scale area averaging sped up by the integral image # and generated on demand whenever each pyramid level is read. width = img.dimension(0) min_width = 32 imgE = Views.extendBorder(integralImg) blockSide = 1 # Corners for level 1: a box of 2x2 corners = [[0, 0], [1, 0], [0, 1], [1, 1]] pyramid = [] # level 0 is the image itself, not added while width > min_width: blockSide *= 2
from net.imglib2.algorithm.math.ImgMath import compute, block, div from net.imglib2.img.display.imagej import ImageJFunctions as IL from net.imglib2.algorithm.integral import IntegralImg from net.imglib2.type.numeric.integer import UnsignedLongType from net.imglib2.type.numeric.real import FloatType from net.imglib2.view import Views from net.imglib2.algorithm.math.abstractions import Util from ij import IJ imp = IJ.getImage() # an 8-bit image, e.g. blobs sample image img = IL.wrap(imp) # A converter from 8-bit (unsigned byte) to 64-bit (unsigned long) int_converter = Util.genericIntegerTypeConverter() # Create the integral image of an 8-bit input, stored as 64-bit alg = IntegralImg(img, UnsignedLongType(), int_converter) alg.process() integralImg = alg.getResult() # Read out blocks of radius 5 (i.e. 10x10 for a 2d image) # in a way that is entirely n-dimensional (applies to 1d, 2d, 3d, 4d ...) radius = 5 nd = img.numDimensions() op = div(block(Views.extendBorder(integralImg), [radius] * nd), pow(radius * 2, nd)) blurred = img.factory().create(img) # an 8-bit image # Compute in floats, store result into longs # using a default generic RealType converter via t.setReal(t.getRealDouble()) compute(op).into(blurred, None, FloatType(), None) # Show the blurred image with the same LUT as the original
def filterBank(img, bs=4, bl=8, sumType=UnsignedLongType(), converter=Util.genericRealTypeConverter()): """ Haar-like features from Viola and Jones using integral images tuned to identify neuron membranes in electron microscopy. bs: length of the short side of a block bl: length of the long side of a block sumType: the type with which to add up pixel values in the integral image converter: for the IntegralImg, to convert from input to sumType """ # Create the integral image, stored as 64-bit alg = IntegralImg(img, sumType, converter) alg.process() integralImg = alg.getResult() imgE = Views.extendBorder(integralImg) # corners of a 4x8 or 8x4 rectangular block where 0,0 is the top left cornersV = [[0, 0], [bs -1, 0], # Vertical [0, bl -1], [bs -1, bl - 1]] cornersH = [[0, 0], [bl -1, 0], # Horizontal [0, bs -1], [bl -1, bs - 1]] # Two adjacent vertical rectangles 4x8 - 4x8 centered on the pixel blockVL = block(imgE, shift(cornersV, -bs, -bl/2)) blockVR = block(imgE, shift(cornersV, 0, -bl/2)) op1 = let("VL", blockVL, "VR", blockVR, IF(GT("VL", "VR"), THEN(div("VR", "VL")), ELSE(div("VL", "VR")))) #op1 = sub(blockVL, blockVR) #op2 = sub(blockVR, blockVL) # Two adjacent horizontal rectangles 8x4 - 8x4 centered on the pixel blockHT = block(imgE, shift(cornersH, -bs, -bl/2)) blockHB = block(imgE, shift(cornersH, -bs, 0)) op3 = let("HT", blockHT, "HB", blockHB, IF(GT("HT", "HB"), THEN(div("HB", "HT")), # div works better than sub ELSE(div("HT", "HB")))) #op3 = sub(blockHT, blockHB) #op4 = sub(blockHB, blockHT) # Two bright-black-bright vertical features 4x8 - 4x8 - 4x8 block3VL = block(imgE, shift(cornersV, -bs -bs/2, -bl/2)) block3VC = block(imgE, shift(cornersV, -bs/2, -bl/2)) block3VR = block(imgE, shift(cornersV, bs/2, -bl/2)) op5 = let("3VL", block3VL, "3VC", block3VC, "3VR", block3VR, IF(AND(GT("3VL", "3VC"), GT("3VR", "3VC")), THEN(sub(add("3VL", "3VR"), "3VC")), # like Viola and Jones 2001 ELSE(div(add("3VL", "3VC", "3VR"), 3)))) # average block value # Purely like Viola and Jones 2001: work poorly for EM membranes #op5 = sub(block3VC, block3VL, block3VR) # center minus sides #op6 = sub(add(block3VL, block3VR), block3VC) # sides minus center # Two bright-black-bright horizontal features 8x4 / 8x4 / 8x4 block3HT = block(imgE, shift(cornersH, -bl/2, -bs -bs/2)) block3HC = block(imgE, shift(cornersH, -bl/2, -bs/2)) block3HB = block(imgE, shift(cornersH, -bl/2, bs/2)) op7 = let("3HT", block3HT, "3HC", block3HC, "3HB", block3HB, IF(AND(GT("3HT", "3HC"), GT("3HB", "3HC")), THEN(sub(add("3HT", "3HB"), "3HC")), # like Viola and Jones 2001 ELSE(div(add("3HT", "3HC", "3HB"), 3)))) # average block value # Purely like Viola and Jones 2001: work poorly for EM membranes #op7 = sub(block3HC, block3HT, block3HB) # center minus top and bottom #op8 = sub(add(block3HT, block3HB), block3HC) # top and bottom minus center # Combination of vertical and horizontal edge detection #op9 = maximum(op1, op3) #op10 = maximum(op6, op8) #op10 = maximum(op5, op7) """ # corners of a square block where 0,0 is at the top left cornersS = [[0, 0], [bs, 0], [0, bs], [bs, bs]] # 2x2 squares for oblique edge detection blockSTL = block(imgE, shift(cornersS, -bs, -bs)) # top left blockSTR = block(imgE, shift(cornersS, 0, -bs)) # top right blockSBL = block(imgE, shift(cornersS, -bs, 0)) # bottom left blockSBR = block(imgE, cornersS) # bottom right op11 = sub(add(blockSTL, blockSBR), blockSTR, blockSBL) op12 = sub(add(blockSTR, blockSBL), blockSTL, blockSBR) """ # Combination of vertical, horizontal and oblique edge detection #op13 = maximum(op1, op3, op6, op8, op11, op12) #op13 = maximum(op1, op3, op5, op7, op11, op12) # combinations are terrible for RandomForest # Edge detectors: sum of 3 adjacent pixels (not dividing by the other 6 # to avoid penalizing Y membrane configurations) """ op14 = maximum(add(offset(op13, [-1, -1]), op13, offset(op13, [ 1, 1])), add(offset(op13, [ 0, -1]), op13, offset(op13, [ 0, 1])), add(offset(op13, [ 1, -1]), op13, offset(op13, [-1, 1])), add(offset(op13, [-1, 0]), op13, offset(op13, [ 1, 0]))) """ #opMean, opVariance = filterBankBlockStatistics(img, integralImgE=imgE, sumType=sumType, converter=converter) # Return an ordered list of all ops #return [op1, op2, op3, op4, op5, op6, op7, op8, op9, op10, op11, op12, op13, op14] #return [op1, op3, op5, op7, opMean, opVariance] return [op1, op3, op5, op7]
def filterBankOrthogonalEdges(img, bs=4, bl=8, sumType=UnsignedLongType(), converter=Util.genericRealTypeConverter()): """ Haar-like features from Viola and Jones using integral images of a set of rotated images tuned to identify neuron membranes in electron microscopy. bs: length of the short side of a block bl: length of the long side of a block sumType: the type with which to add up pixel values in the integral image converter: for the IntegralImg, to convert from input to sumType """ # Create the integral image, stored as 64-bit alg = IntegralImg(img, sumType, converter) alg.process() integralImg = alg.getResult() imgE = Views.extendBorder(integralImg) # corners of a 4x8 or 8x4 rectangular block where 0,0 is the top left cornersV = [[0, 0], [bs -1, 0], # Vertical [0, bl -1], [bs -1, bl - 1]] cornersH = [[0, 0], [bl -1, 0], # Horizontal [0, bs -1], [bl -1, bs - 1]] # Two adjacent vertical rectangles 4x8 - 4x8 centered on the pixel blockVL = block(imgE, shift(cornersV, -bs, -bl/2)) blockVR = block(imgE, shift(cornersV, 0, -bl/2)) op1 = sub(blockVL, blockVR) op2 = sub(blockVR, blockVL) # Two adjacent horizontal rectangles 8x4 - 8x4 centered on the pixel blockHT = block(imgE, shift(cornersH, -bs, -bl/2)) blockHB = block(imgE, shift(cornersH, -bs, 0)) op3 = sub(blockHT, blockHB) op4 = sub(blockHB, blockHT) # Two bright-black-bright vertical features 4x8 - 4x8 - 4x8 block3VL = block(imgE, shift(cornersV, -bs -bs/2, -bl/2)) block3VC = block(imgE, shift(cornersV, -bs/2, -bl/2)) block3VR = block(imgE, shift(cornersV, bs/2, -bl/2)) op5 = let("3VL", block3VL, "3VC", block3VC, "3VR", block3VR, IF(AND(GT("3VL", "3VC"), GT("3VR", "3VC")), THEN(sub(add("3VL", "3VR"), "3VC")), # like Viola and Jones 2001 ELSE(div(add("3VL", "3VC", "3VR"), 3)))) # average block value # Purely like Viola and Jones 2001: work poorly for EM membranes #op5 = sub(block3VC, block3VL, block3VR) # center minus sides #op6 = sub(add(block3VL, block3VR), block3VC) # sides minus center # Two bright-black-bright horizontal features 8x4 / 8x4 / 8x4 block3HT = block(imgE, shift(cornersH, -bl/2, -bs -bs/2)) block3HC = block(imgE, shift(cornersH, -bl/2, -bs/2)) block3HB = block(imgE, shift(cornersH, -bl/2, bs/2)) op7 = let("3HT", block3HT, "3HC", block3HC, "3HB", block3HB, IF(AND(GT("3HT", "3HC"), GT("3HB", "3HC")), THEN(sub(add("3HT", "3HB"), "3HC")), ELSE(div(add("3HT", "3HC", "3HB"), 3)))) # average block value TODO make a single block #op7 = sub(block3HC, block3HT, block3HB) # center minus top and bottom #op8 = sub(add(block3HT, block3HB), block3HC) # top and bottom minus center # Two adjacent horizontal rectanges, 12x12 - 4x4 bll = bl + bl/2 cornersLarge = [[0, 0], [bll -1, 0], [0, bll -1], [bll -1, bll -1]] cornersSmall = [[0, 0], [bs -1, 0], [0, bs -1], [bs -1, bs -1]] # Subtract a large black rectangle from a small bright one - aiming at capturing synapses # Bright on the right blockLargeL = block(imgE, shift(cornersLarge, -bll, -bll/2)) blockSmallR = block(imgE, shift(cornersSmall, 0, -bs/2)) op9 = sub(blockSmallR, blockLargeL) # Bright on the left blockLargeR = block(imgE, shift(cornersLarge, 0, -bll/2)) blockSmallL = block(imgE, shift(cornersSmall, -bs, -bs/2)) op10 = sub(blockSmallL, blockLargeR) # Bright at the bottom blockLargeT = block(imgE, shift(cornersLarge, -bll/2, -bll)) blockSmallB = block(imgE, shift(cornersSmall, -bs/2, 0)) op11 = sub(blockSmallB, blockLargeT) # Bright at the top blockLargeB = block(imgE, shift(cornersLarge, -bll/2, 0)) blockSmallT = block(imgE, shift(cornersSmall, -bs/2, -bs)) op12 = sub(blockSmallT, blockLargeB) return [op1, op2, op3, op4, op5, op7, op9, op10, op11, op12]
def filterBank(img, sumType=UnsignedLongType(), converter=Util.genericRealTypeConverter()): """ Haar-like features from Viola and Jones tuned to identify neuron membranes in electron microscopy. """ # Create the integral image, stored as 64-bit alg = IntegralImg(img, sumType, converter) alg.process() integralImg = alg.getResult() imgE = Views.extendBorder(integralImg) # corners of a 4x8 or 8x4 rectangular block where 0,0 is the top left bs = 4 # short side bl = 8 # long side cornersV = [ [0, 0], [bs - 1, 0], # Vertical [0, bl - 1], [bs - 1, bl - 1] ] cornersH = [ [0, 0], [bl - 1, 0], # Horizontal [0, bs - 1], [bl - 1, bs - 1] ] # Two adjacent vertical rectangles 4x8 - 4x8 centered on the pixel blockVL = block(imgE, shift(cornersV, -bs, -bl / 2)) blockVR = block(imgE, shift(cornersV, 0, -bl / 2)) op1 = sub(blockVL, blockVR) op2 = sub(blockVR, blockVL) # Two adjacent horizontal rectangles 8x4 - 8x4 centered on the pixel blockHT = block(imgE, shift(cornersH, -bs, -bl / 2)) blockHB = block(imgE, shift(cornersH, -bs, 0)) op3 = sub(blockHT, blockHB) op4 = sub(blockHB, blockHT) # Two bright-black-bright vertical features 4x8 - 4x8 - 4x8 block3VL = block(imgE, shift(cornersV, -bs - bs / 2, -bl / 2)) block3VC = block(imgE, shift(cornersV, -bs / 2, -bl / 2)) block3VR = block(imgE, shift(cornersV, bs / 2, -bl / 2)) op5 = sub(block3VC, block3VL, block3VR) # center minus sides op6 = sub(add(block3VL, block3VR), block3VC) # sides minus center # Two bright-black-bright horizontal features 8x4 / 8x4 / 8x4 block3HT = block(imgE, shift(cornersH, -bl / 2, -bs - bs / 2)) block3HC = block(imgE, shift(cornersH, -bl / 2, -bs / 2)) block3HB = block(imgE, shift(cornersH, -bl / 2, bs / 2)) op7 = sub(block3HC, block3HT, block3HB) # center minus top and bottom op8 = sub(add(block3HT, block3HB), block3HC) # top and bottom minus center # Combination of vertical and horizontal edge detection op9 = maximum(op1, op3) op10 = maximum(op6, op8) # corners of a square block where 0,0 is at the top left cornersS = [[0, 0], [bs, 0], [0, bs], [bs, bs]] # 2x2 squares for oblique edge detection blockSTL = block(imgE, shift(cornersS, -bs, -bs)) # top left blockSTR = block(imgE, shift(cornersS, 0, -bs)) # top right blockSBL = block(imgE, shift(cornersS, -bs, 0)) # bottom left blockSBR = block(imgE, cornersS) # bottom right op11 = sub(add(blockSTL, blockSBR), blockSTR, blockSBL) op12 = sub(add(blockSTR, blockSBL), blockSTL, blockSBR) # Combination of vertical, horizontal and oblique edge detection op13 = maximum(op1, op3, op6, op8, op11, op12) # Edge detectors: sum of 3 adjacent pixels (not dividing by the other 6 # to avoid penalizing Y membrane configurations) op14 = maximum(add(offset(op13, [-1, -1]), op13, offset(op13, [1, 1])), add(offset(op13, [0, -1]), op13, offset(op13, [0, 1])), add(offset(op13, [1, -1]), op13, offset(op13, [-1, 1])), add(offset(op13, [-1, 0]), op13, offset(op13, [1, 0]))) # Return a list of all ops #return [ob for name, ob in vars().iteritems() if re.match(r"^op\d+$", name)] # Ordered return [ op1, op2, op3, op4, op5, op6, op7, op8, op9, op10, op11, op12, op13, op14 ]