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]
# 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 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(UnsignedShortType(), UnsignedByteType()) # 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) """ for i, level in enumerate(pyramid): imp_level = IL.wrap(level, str(i+1))
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 imp2 = IL.wrap(blurred, "integral image radius 5 blur") imp2.getProcessor().setLut(imp.getProcessor().getLut()) imp2.show() # Compare with Gaussian blur from ij import ImagePlus from ij.plugin.filter import GaussianBlur from ij.gui import Line, ProfilePlot
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]
imgE = Views.extendZero(target) # Integrate every dimension, cummulatively by writing into # a target image that is also the input for d in xrange(img.numDimensions()): coord = [0] * img.numDimensions() # array of zeros coord[d] = -1 # Cummulative sum along the current dimension # Note that instead of the ImgMath offset op, # we could have used Views.translate(Views.extendZero(target), [1, 0])) # (Notice though the sign change in the translation) integral = add(target, offset(imgE, coord)) compute(integral).into(target) # The target is the integral image integralImg = target # 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)) # divide by total number of pixels in the block blurred = ArrayImgs.floats(Intervals.dimensionsAsLongArray(img)) compute(op).into(blurred, FloatType()) # Show the blurred image with the same LUT as the original imp2 = IL.wrap(blurred, "integral image radius 5 blur") imp2.getProcessor().setLut(imp.getProcessor().getLut()) imp2.show()
from net.imglib2.algorithm.math.ImgMath import computeIntoFloat, sub, div from net.imglib2.img.display.imagej import ImageJFunctions as IL from net.imglib2.type.numeric.real import FloatType from ij import IJ # Simple example: normalize an image imp = IJ.getImage() ip = imp.getProcessor() minV, maxV = ip.getMin(), ip.getMax() img = IL.wrap(imp) op = div(sub(img, minV), maxV - minV + 1) result = computeIntoFloat(op) IL.wrap(result, "normalized").show() # As a view IL.wrap(op.view(FloatType()), "normalized").show()
def test(red, green, blue, easy=True): saturation = let( "red", red, "green", green, "blue", blue, "max", maximum("red", "green", "blue"), "min", minimum("red", "green", "blue"), IF(EQ(0, "max"), THEN(0), ELSE(div(sub("max", "min"), "max")))) brightness = div(maximum(red, green, blue), 255.0) hue = IF( EQ(0, saturation), THEN(0), ELSE( let( "red", red, "green", green, "blue", blue, "max", maximum("red", "green", "blue"), "min", minimum("red", "green", "blue"), "range", sub("max", "min"), "redc", div(sub("max", "red"), "range"), "greenc", div(sub("max", "green"), "range"), "bluec", div(sub("max", "blue"), "range"), "hue", div( IF( EQ("red", "max"), THEN(sub("bluec", "greenc")), ELSE( IF(EQ("green", "max"), THEN(sub(add(2, "redc"), "bluec")), ELSE(sub(add(4, "greenc"), "redc"))))), 6), IF(LT("hue", 0), THEN(add("hue", 1)), ELSE("hue"))))) #print hierarchy(hue) #print "hue view:", hue.view( FloatType() ).iterationOrder() if easy: # About 26 ms """ hsb = Views.stack( hue.view( FloatType() ), saturation.view( FloatType() ), brightness.view( FloatType() ) ) """ # About 13 ms: half! Still much worse than plain ImageJ, # but the source images are iterated 4 times, rather than just once, # and the saturation is computed twice, # and the min, max is computed 3 and 4 times, respectively. hsb = Views.stack(hue.viewDouble(FloatType()), saturation.viewDouble(FloatType()), brightness.viewDouble(FloatType())) """ # Even worse: ~37 ms width, height = rgb.dimension(0), rgb.dimension(1) h = compute(hue).into(ArrayImgs.floats([width, height])) s = compute(saturation).into(ArrayImgs.floats([width, height])) b = compute(brightness).into(ArrayImgs.floats([width, height])) hsb = Views.stack( h, s, b ) """ imp = IL.wrap(hsb, "HSB view") else: # Tested it: takes more time (~40 ms vs 26 ms above) width, height = rgb.dimension(0), rgb.dimension(1) hb = zeros(width * height, 'f') sb = zeros(width * height, 'f') bb = zeros(width * height, 'f') h = ArrayImgs.floats(hb, [width, height]) s = ArrayImgs.floats(sb, [width, height]) b = ArrayImgs.floats(bb, [width, height]) #print "ArrayImg:", b.iterationOrder() ImgUtil.copy(ImgView.wrap(hue.view(FloatType()), None), h) ImgUtil.copy(ImgView.wrap(saturation.view(FloatType()), None), s) ImgUtil.copy(ImgView.wrap(brightness.view(FloatType()), None), b) stack = ImageStack(width, height) stack.addSlice(FloatProcessor(width, height, hb, None)) stack.addSlice(FloatProcessor(width, height, sb, None)) stack.addSlice(FloatProcessor(width, height, bb, None)) imp = ImagePlus("hsb", stack) return imp