def wrap(img, title="", n_channels=1): """ Like ImageJFunctions.wrap but, when n_channels=1 (the default), then a new dimension of size 1 is inserted at position 2 to prevent the Z axis from showing as the channels axis. To enable ImageJFunctions.wrap default behavior, set n_channels to a value other than 1. """ if 1 == n_channels: # Append a dimension of size 1 at the end # and permute it iteratively so that it becomes the channels dimension (d=2) img = Views.addDimension(img, 1, 1) d = img.numDimensions( ) - 1 # starts with the last: the new one of size 1 while d > 2: img = Views.permute(img, d, d - 1) d -= 1 # return IL.wrap(img, title)
def smooth_temporal_gradient(ops, img, sigma_xy, sigma_t, frame_start, frame_end, normalize_output): """ Smooth input image (imglib2 array) xyt with Gaussian and build temporal gradient from frame_start to frame_end """ n = img.numDimensions() assert n == 3, "Input data needs to be 3-dimensional, 2D + time" dims = [img.dimension(d) for d in range(n)] dim_x, dim_y, dim_t = dims if frame_end == -1: frame_end = dim_t if frame_end > dim_t: frame_end = dim_t assert frame_start < frame_end, "'Frame start' must be smaller than 'Frame end'" ## crop image temporally by using a View # img_crop = Views.interval(img, [0, 0, frame_start], [dim_x-1, dim_y-1, frame_end-1]) # or by cropping without view img_crop = ops.transform().crop( img, Intervals.createMinMax(0, 0, frame_start, dim_x - 1, dim_y - 1, frame_end - 1)) # create output for smoothing img_smooth = ArrayImgFactory(FloatType()).create(img_crop) # update dimensions (after cropping) dims = [img_crop.dimension(d) for d in range(n)] # smooth with Gaussian (use mirror as border treatment) Gauss3.gauss([sigma_xy, sigma_xy, sigma_t], Views.extendBorder(img_crop), img_smooth) # compute gradient along (time) axis 2 gradient = ArrayImgFactory(FloatType()).create(dims) PartialDerivative.gradientBackwardDifference( Views.extendBorder(img_smooth), gradient, 2) # separate response into growing and shrinking part by thresholding and multiplying that mask thresholded = ops.run("threshold.apply", gradient, FloatType(0.)) mask = ops.convert().float32(thresholded) grow = ops.run("math.multiply", gradient, mask) # same for shrinking, but negate befor to obtain negative part gradient_neg = ops.run("math.multiply", gradient, -1) thresholded = ops.run("threshold.apply", gradient_neg, FloatType(0.)) mask = ops.convert().float32(thresholded) shrink = ops.run("math.multiply", gradient, mask) # allign growth and shrink by offset of 1 in t dimesnion shrink = Views.interval(shrink, [0, 0, 0], [dims[0] - 1, dims[1] - 1, dims[2] - 2]) grow = Views.interval(grow, [0, 0, 1], [dims[0] - 1, dims[1] - 1, dims[2] - 1]) img_smooth = Views.interval(img_smooth, [0, 0, 0], [dims[0] - 1, dims[1] - 1, dims[2] - 2]) # concatenate output if normalize_output: shrink = rescale_uint8(ops, shrink) grow = rescale_uint8(ops, grow) img_smooth = rescale_uint8(ops, img_smooth) out = Views.stack([shrink, grow, img_smooth]) # permute channel with time to make time last dim (for back-conversion) out = Views.permute(out, 2, 3) # crop view 1st time point result (empty since using backwardDifferences) out = Views.interval(out, [0, 0, 0, 1], [out.dimension(d) - 1 for d in range(4)]) # get ImagePlus back and set correct dimensions out_imp = IJF.wrap(out, "Grow and shrink") # resize IMP now one frame less (see crop view) out_imp.setDimensions(3, 1, frame_end - frame_start - 2) out_imp.setDisplayMode(IJ.COMPOSITE) # IJ.save(out_imp, "H:/projects/032_loose_speckle/grow_and_shrink_ij.tif") return out_imp