Files
dev/script/___Lib/ijutils.py
2020-01-20 08:23:23 +01:00

750 lines
30 KiB
Python

####################################################################################################
# Facade to ImageJ functionality
####################################################################################################
#More information on:
# Image: https://imagej.nih.gov/ij/docs/guide/146-28.html#toc-Section-28
# Process: https://imagej.nih.gov/ij/docs/guide/146-29.html#toc-Section-29
# Analyze: https://imagej.nih.gov/ij/docs/guide/146-30.html#toc-Section-30
import ch.psi.utils.Convert as Convert
import ch.psi.pshell.imaging.Utils as Utils
from startup import get_context
import java.awt.image.BufferedImage as BufferedImage
import jarray
import ij.IJ as IJ
import ij.ImageJ as ImageJ
import ij.WindowManager as WindowManager
import ij.ImagePlus as ImagePlus
import ij.Prefs as Prefs
import ij.io.FileSaver as FileSaver
import ij.io.Opener as Opener
import ij.process.ImageProcessor as ImageProcessor
import ij.process.ByteProcessor as ByteProcessor
import ij.process.ShortProcessor as ShortProcessor
import ij.process.ColorProcessor as ColorProcessor
import ij.process.FloatProcessor as FloatProcessor
import ij.process.ImageConverter as ImageConverter
import ij.process.AutoThresholder as AutoThresholder
import ij.process.LUT as LUT
import ij.measure.Measurements as Measurements
import ij.measure.ResultsTable as ResultsTable
import ij.plugin.filter.Analyzer as Analyzer
import ij.plugin.filter.GaussianBlur as GaussianBlur
import ij.plugin.filter.Filters as Filters
import ij.plugin.filter.FFTFilter as FFTFilter
import ij.plugin.filter.BackgroundSubtracter as BackgroundSubtracter
import ij.plugin.filter.EDM as EDM
import ij.plugin.filter.Shadows as Shadows
import ij.plugin.filter.UnsharpMask as UnsharpMask
import ij.plugin.filter.MaximumFinder as MaximumFinder
import ij.plugin.filter.EDM as EDM
import ij.plugin.filter.Shadows as Shadows
import ij.plugin.filter.UnsharpMask as UnsharpMask
import ij.plugin.filter.RankFilters as RankFilters
import ij.plugin.filter.Convolver as Convolver
import ij.plugin.filter.ParticleAnalyzer as ParticleAnalyzer
import ij.plugin.ContrastEnhancer as ContrastEnhancer
import ij.plugin.Thresholder as Thresholder
import ij.plugin.ImageCalculator as ImageCalculator
import ij.plugin.FFT as FFT
import ij.plugin.Concatenator as Concatenator
#ImageJ customizations
import ch.psi.pshell.imaging.ij.FFTMath as FFTMath
import ch.psi.pshell.imaging.ij.FFTFilter as FFTFilter
import ch.psi.pshell.imaging.ij.Binary as Binary
import ch.psi.pshell.imaging.ij.Slicer as Slicer
#This eliminates the error messages due to the bug on ij.gui.ImageWindow row 555 (ij is null)
if not "_image_j" in globals().keys():
_image_j = ImageJ(None, ImageJ.NO_SHOW)
###################################################################################################
#Image creation, copying & saving
###################################################################################################
def load_image(image, title = "img"):
"""
image: file name or BufferedImage
"""
if isinstance(image, str):
try:
file = get_context().setup.expandPath(image)
except:
pass
try:
image = Utils.newImage(file)
except:
#try loading from assembly
image = get_context().setup.getAssemblyImage(image)
return ImagePlus(title, image)
def load_array(array, width=None, height=None, title = "img"):
"""
array: 1d array if width and height defined , or else 2d array to be flattened.
"""
#2D
if (width==None) and (height==None):
if array.typecode == '[B': proc = ByteProcessor(len(array[0]), len(array), Convert.flatten(array))
elif array.typecode == '[S': proc = ShortProcessor(len(array[0]), len(array), Convert.flatten(array), None)
elif array.typecode in ['[I','[F', '[D']: proc = FloatProcessor(len(array[0]), len(array), Convert.flatten(array))
else: raise Exception("Invalid array type")
#1D
else:
if (len(array) > width*height):
array = array[:(width*height)]
if array.typecode == 'b': proc = ByteProcessor(width, height, array)
elif array.typecode == 'h': proc = ShortProcessor(width, height, array, None)
elif array.typecode in ['i','f','d']: proc = FloatProcessor(width, height, array)
else: raise Exception("Invalid array type")
return ImagePlus(title, proc)
def save_image(ip, path=None, format = None):
"""
Saves image or stack
If parameters omitted, saves image again in same location, with same format.
"""
fs = FileSaver(ip)
if path == None: fs.save()
else:
try:
path = get_context().setup.expandPath(path)
except:
pass
if format == "bmp": fs.saveAsBmp(path)
elif format == "fits": fs.saveAsFits(path)
elif format == "gif": fs.saveAsGif(path)
elif format == "jpeg": fs.saveAsJpeg(path)
elif format == "lut": fs.saveAsLut(path)
elif format == "pgm": fs.saveAsPgm(path)
elif format == "png": fs.saveAsPng(path)
elif format == "raw" and ip.getImageStackSize()>1: fs.saveAsRawStack(path)
elif format == "raw": fs.saveAsRaw(path)
elif format == "txt": fs.saveAsText(path)
elif format == "tiff" and ip.getImageStackSize()>1: fs.saveAsTiffStack(path)
elif format == "tiff": fs.saveAsTiff(path)
elif format == "zip": fs.saveAsZip(path)
def open_image(path):
"""
Open file using ij.io,Opener
"""
try:
path = get_context().setup.expandPath(path)
except:
pass
opener = Opener()
return opener.openImage(path)
def new_image(width, height, image_type="byte", title = "img", fill_color = None):
"""
type = "byte", "short", "color" or "float"
"""
if image_type == "byte": p=ByteProcessor(width, height)
elif image_type == "short": p=ShortProcessor(width, height)
elif image_type == "color": p=ColorProcessor(width, height)
elif image_type == "float": p=FloatProcessor(width, height)
else: raise Exception("Invalid image type " + str(image_type))
ret = ImagePlus(title, p)
if fill_color is not None:
p.setColor(fill_color)
p.resetRoi()
p.fill()
return ret
def get_ip_array(ip):
"""
Returns data array of ImagePlus
"""
if type(ip.getProcessor()) == FloatProcessor:
return ip.getProcessor().getFloatArray()
else:
return ip.getProcessor().getIntArray()
def sub_image(ip, x, y, width, height):
"""
Returns new ImagePlus
"""
ip.setRoi(x, y, width, height)
p=ip.getProcessor().crop()
return ImagePlus(ip.getTitle() + " subimage", p)
def copy_image(ip):
return ip.duplicate()
def copy_image_to(ip_source, ip_dest, x, y):
ip_source.deleteRoi()
ip_source.copy()
ip_dest.setRoi(x, y, ip_source.getWidth(), ip_source.getHeight())
ip_dest.paste()
ip_dest.changes = False
ip_dest.deleteRoi()
def pad_image(ip, left=0, right=0, top=0, bottom=0, fill_color = None):
p=ip.getProcessor()
width = p.getWidth() + left + right
height = p.getHeight() + top + bottom
image_type = get_image_type(ip)
ret = new_image(width, height, image_type, ip.getTitle() + " padded", fill_color)
ip.deleteRoi()
ip.copy()
ret.setRoi(left, top, p.getWidth(), p.getHeight())
ret.paste()
ret.changes = False
ret.deleteRoi()
return ret
def get_image_type(ip):
"""
Returns: "byte", "short", "color" or "float"
"""
p=ip.getProcessor()
if type(p) == ShortProcessor: return "short"
elif type(p) == ColorProcessor: return "color"
elif type(p) == FloatProcessor: return "float"
return "byte"
###################################################################################################
#Image type conversion
###################################################################################################
def grayscale(ip, do_scaling=None, in_place=True):
ip = ip if in_place else ip.duplicate()
ic = ImageConverter(ip)
if do_scaling is not None:
ic.setDoScaling(do_scaling)
ic.convertToGray8()
return ip
def get_channel(ip, channel):
"""
Return a channel from a color image as a new ImagePlus.
channel: "red", "green","blue", "alpha", "brightness",
"""
proc = ip.getProcessor()
if channel == "red": ret = proc.getChannel(1, None)
elif channel == "green": ret = proc.getChannel(2, None)
elif channel == "blue": ret = proc.getChannel(3, None)
elif channel == "alpha": ret = proc.getChannel(4, None)
elif channel == "brightness": ret = proc.getBrightness()
else: raise Exception("Invalid channel " + str(channel))
return ImagePlus(ip.getTitle() + " channel: " + channel, ret)
###################################################################################################
#Thresholder
###################################################################################################
def threshold(ip, min_threshold, max_threshold, in_place=True):
ip = ip if in_place else ip.duplicate()
ip.getProcessor().setThreshold(min_threshold, max_threshold, ImageProcessor.NO_LUT_UPDATE)
WindowManager.setTempCurrentImage(ip)
Thresholder().run("mask")
return ip
def auto_threshold(ip, dark_background = False, method = AutoThresholder.getMethods()[0], in_place=True):
ip = ip if in_place else ip.duplicate()
ip.getProcessor().setAutoThreshold(method, dark_background , ImageProcessor.NO_LUT_UPDATE)
WindowManager.setTempCurrentImage(ip)
thresholder=Thresholder().run("mask")
return ip
###################################################################################################
#Binary functions
###################################################################################################
def binary_op(ip, op, dark_background=False, iterations=1, count=1, in_place=True):
"""
op = "erode","dilate", "open","close", "outline", "fill holes", "skeletonize"
"""
ip = ip if in_place else ip.duplicate()
binary = Binary(count, iterations, dark_background )
binary.setup(op, ip)
binary.run(ip.getProcessor())
return ip
def binary_erode(ip, dark_background=False, iterations=1, count=1, in_place=True):
return binary_op(ip, "erode", dark_background, iterations, count, in_place)
def binary_dilate(ip, dark_background=False, iterations=1, count=1, in_place=True):
return binary_op(ip, "dilate", dark_background, iterations, count, in_place)
def binary_open(ip, dark_background=False, iterations=1, count=1, in_place=True):
return binary_op(ip, "open", dark_background, iterations, count, in_place)
def binary_close(ip, dark_background=False, iterations=1, count=1, in_place=True):
return binary_op(ip, "close", dark_background, iterations, count)
def binary_outline(ip, dark_background=False, in_place=True):
return binary_op(ip, "outline", dark_background, in_place=in_place)
def binary_fill_holes(ip, dark_background=False, in_place=True):
return binary_op(ip, "fill holes", dark_background, in_place=in_place)
def binary_skeletonize(ip, dark_background=False, in_place=True):
return binary_op(ip, "skeletonize", dark_background, in_place=in_place)
def analyse_particles(ip, min_size, max_size, fill_holes = True, exclude_edges = True, extra_measurements = 0, \
print_table = False, output_image = "outlines", minCirc = 0.0, maxCirc = 1.0):
"""
Returns: tuple (ResultsTable results_table, ImagePlus output_image)
output_image = "outlines", "overlay_outlines", "masks", "overlay_masks", "roi_masks" or None
extra_measurements = mask with Measurements.CENTROID, PERIMETER, RECT, MIN_MAX, ELLIPSE, CIRCULARITY, AREA_FRACTION, INTEGRATED_DENSITY, INVERT_Y, FERET, KURTOSIS, MEDIAN, MODE, SKEWNESS, STD_DEV
Measurements is a mask of flags: https://imagej.nih.gov/ij/developer/api/ij/measure/Measurements.html.
Returned ResultsTable hold public fields: https://imagej.nih.gov/ij/developer/api/ij/measure/ResultsTable.html
"""
rt = ResultsTable()
show_summary = False
options = ParticleAnalyzer.SHOW_RESULTS | ParticleAnalyzer.CLEAR_WORKSHEET
"""
ParticleAnalyzer.SHOW_ROI_MASKS | \
#ParticleAnalyzer.RECORD_STARTS | \
#ParticleAnalyzer.ADD_TO_MANAGER | \
#ParticleAnalyzer.FOUR_CONNECTED | \
#ParticleAnalyzer.IN_SITU_SHOW | \
#ParticleAnalyzer.SHOW_NONE | \
"""
if show_summary: options = options | ParticleAnalyzer.DISPLAY_SUMMARY
if output_image == "outlines": options = options | ParticleAnalyzer.SHOW_OUTLINES
elif output_image == "overlay_outlines": options = options | ParticleAnalyzer.SHOW_OVERLAY_OUTLINES
elif output_image == "masks": options = options | ParticleAnalyzer.SHOW_MASKS
elif output_image == "overlay_masks": options = options | ParticleAnalyzer.SHOW_OVERLAY_MASKS
elif output_image == "roi_masks": options = options | ParticleAnalyzer.SHOW_ROI_MASKS
#ParticleAnalyzer.SHOW_ROI_MASKS
if exclude_edges: options = options | ParticleAnalyzer.EXCLUDE_EDGE_PARTICLES
if fill_holes: options = options | ParticleAnalyzer.INCLUDE_HOLES
measurements = Measurements.AREA | Measurements.MEAN | Measurements.CENTER_OF_MASS | Measurements.RECT
pa = ParticleAnalyzer(options, measurements, rt, min_size, max_size, minCirc, maxCirc)
pa.setHideOutputImage(True)
pa.setResultsTable(rt)
if pa.analyze(ip):
if print_table:
print rt.getColumnHeadings()
for row in range (rt.counter):
print rt.getRowAsString(row)
return (rt, pa.getOutputImage())
###################################################################################################
#Image operators
###################################################################################################
def op_image(ip1, ip2, op, float_result=False, in_place=True):
"""
op = "add","subtract", "multiply","divide", "and", "or", "xor", "min", "max", "average", "difference" or "copy"
"""
ip1 = ip1 if in_place else ip1.duplicate()
ic = ImageCalculator()
pars = op
if float_result:
op = op + " float"
ic.run(pars, ip1, ip2)
return ip1
def op_const(ip, op, val, in_place=True):
"""
op = "add","subtract", "multiply","divide", "and", "or", "xor", "min", "max", "gamma", "set" or "log", "exp", "sqr", "sqrt","abs"
"""
ip = ip if in_place else ip.duplicate()
pr = ip.getProcessor()
if op == 'add': pr.add(val)
elif op == 'sub': pr.subtract(val)
elif op == 'multiply': pr.multiply(val)
elif op == 'divide' and val!=0: pr.multiply(1.0/val)
elif op == 'and': pr.and(val)
elif op == 'or': pr.or(val)
elif op == 'xor': pr.xor(val)
elif op == 'min': pr.min(val);pr.resetMinAndMax()
elif op == 'max': pr.max(val);pr.resetMinAndMax()
elif op == 'gamma' and 0.05 < val < 5.0: pr.gamma(val)
elif op == 'set': pr.set(val)
elif op == 'log': pr.log()
elif op == 'exp': pr.exp()
elif op == 'sqr': pr.sqr()
elif op == 'sqrt': pr.sqrt()
elif op == 'abs': pr.abs();pr.resetMinAndMax()
else: raise Exception("Invalid operation " + str(op))
return ip
def op_fft(ip1, ip2, op, do_inverse = True) :
"""
Images must have same sizes, and multiple of 2 height and width.
op = "correlate" (complex conjugate multiply), "convolve" (Fourier domain multiply), "deconvolve" (Fourier domain divide)
"""
if op == "correlate": op_index = 0
elif op == "convolve": op_index = 1
elif op == "deconvolve": op_index = 2
else: raise Exception("Invalid operation " + str(op))
return FFTMath().doMath(ip1, ip2, op_index, do_inverse)
def op_rank(ip, op, kernel_radius =1 , dark_outliers = False ,threshold = 50, in_place=True):
"""
op = "mean", "min", "max", "variance", "median", "close_maxima", "open_maxima", "remove_outliers", "remove_nan", "despeckle"
"""
if op == "mean": filter_type = RankFilters.MEAN
elif op == "min": filter_type = RankFilters.MIN
elif op == "max": filter_type = RankFilters.MAX
elif op == "variance": filter_type = RankFilters.VARIANCE
elif op == "median": filter_type = RankFilters.MEDIAN
elif op == "close_maxima": filter_type = RankFilters.CLOSE
elif op == "open_maxima": filter_type = RankFilters.OPEN
elif op == "remove_outliers": filter_type = RankFilters.OUTLIERS
elif op == "remove_nan": filter_type = RankFilters.REMOVE_NAN
elif op == "despeckle": filter_type, kernel_radius = RankFilters.MEDIAN, 1
else: raise Exception("Invalid operation " + str(op))
ip = ip if in_place else ip.duplicate()
RankFilters().rank(ip.getProcessor(), kernel_radius, filter_type, RankFilters.DARK_OUTLIERS if dark_outliers else RankFilters.BRIGHT_OUTLIERS ,threshold)
return ip
def op_edm(ip, op="edm", dark_background=False, in_place=True):
"""
Euclidian distance map & derived operations
op ="edm", "watershed","points", "voronoi"
"""
ip = ip if in_place else ip.duplicate()
pr = ip.getProcessor()
edm=EDM()
Prefs.blackBackground=dark_background
if op=="edm":
#pr.setPixels(0, edm.makeFloatEDM(pr, 0, False));
#pr.resetMinAndMax();
if dark_background:
pr.invert()
edm.toEDM(pr)
else:
edm.setup(op, ip)
edm.run(pr)
return ip
def watershed(ip, dark_background=False, in_place=True):
return op_edm(ip, "watershed", dark_background, in_place)
def ultimate_points(ip, dark_background=False, in_place=True):
return op_edm(ip, "points", dark_background, in_place)
def veronoi(ip, dark_background=False, in_place=True):
return op_edm(ip, "voronoi", dark_background, in_place)
def edm(ip, dark_background=False, in_place=True):
return op_edm(ip, "edm", dark_background, in_place)
def op_filter(ip, op, in_place=True):
"""
This is redundant as just calls processor methods.
op ="invert", "smooth", "sharpen", "edge", "add"
"""
ip = ip if in_place else ip.duplicate()
f = Filters()
f.setup(op, ip )
f.run(ip.getProcessor())
return ip
###################################################################################################
#Other operations
###################################################################################################
def gaussian_blur(ip, sigma_x=3.0, sigma_y=3.0, accuracy = 0.01, in_place=True):
ip = ip if in_place else ip.duplicate()
GaussianBlur().blurGaussian(ip.getProcessor(), sigma_x, sigma_y, accuracy)
return ip
def find_maxima(ip, tolerance=25, threshold = ImageProcessor.NO_THRESHOLD, output_type=MaximumFinder.IN_TOLERANCE, exclude_on_edges = False, is_edm = False):
"""
Returns new ImagePlus
tolerance: maxima are accepted only if protruding more than this value from the ridge to a higher maximum
threshhold: minimum height of a maximum (uncalibrated);
output_type = SINGLE_POINTS, IN_TOLERANCE or SEGMENTED. No output image is created for output types POINT_SELECTION, LIST and COUNT.
"""
byte_processor = MaximumFinder().findMaxima(ip.getProcessor(), tolerance, threshold, output_type, exclude_on_edges, is_edm)
return ImagePlus(ip.getTitle() + " maxima", byte_processor)
def get_maxima_points(ip, tolerance=25, exclude_on_edges = False):
polygon = MaximumFinder().getMaxima(ip.getProcessor(), tolerance, exclude_on_edges)
return (polygon.xpoints, polygon.ypoints)
def enhance_contrast(ip, equalize_histo = True, saturated_pixels = 0.5, normalize = False, stack_histo = False, in_place=True):
ip = ip if in_place else ip.duplicate()
ce = ContrastEnhancer()
if equalize_histo:
ce.equalize(ip.getProcessor());
else:
ce.stretchHistogram(ip.getProcessor(), saturated_pixels)
if normalize:
ip.getProcessor().setMinAndMax(0,1.0 if (ip.getProcessor().getBitDepth()==32) else ip.getProcessor().maxValue())
return ip
def shadows(ip, op, in_place=True):
"""
op ="north","northeast", "east", "southeast","south", "southwest", "west","northwest"
"""
ip = ip if in_place else ip.duplicate()
shadows= Shadows()
shadows.setup(op, ip)
shadows.run(ip.getProcessor())
return ip
def unsharp_mask(ip, sigma, weight, in_place=True):
"""
Float processor
"""
ip = ip if in_place else ip.duplicate()
ip.getProcessor().snapshot()
unsharp=UnsharpMask()
USmask.setup(" ", ip)
USmask.sharpenFloat( ip.getProcessor(),sigma, weight)
return ip
def subtract_background(ip, radius = 50, create_background=False, dark_background=False, use_paraboloid =True, do_presmooth = True, correctCorners = True, rgb_brightness=False, in_place=True):
ip = ip if in_place else ip.duplicate()
if rgb_brightness:
BackgroundSubtracter().rollingBallBrightnessBackground(ip.getProcessor(), radius, create_background,not dark_background, use_paraboloid, do_presmooth, correctCorners)
else:
BackgroundSubtracter().rollingBallBackground(ip.getProcessor(), radius, create_background, not dark_background, use_paraboloid, do_presmooth, correctCorners)
return ip
###################################################################################################
#FFT
###################################################################################################
def image_fft(ip, show = True):
WindowManager.setTempCurrentImage(ip)
fft = FFT()
fft.run("fft")
#TODO: how to avoid it to be created?
#ret = ImagePlus("FHT of " + ip.getTitle(), WindowManager.getCurrentImage().getProcessor())
ret = WindowManager.getCurrentImage()
if not show:
WindowManager.getCurrentImage().hide()
return ret
def image_ffti(ip, show = True):
WindowManager.setTempCurrentImage(ip)
fft = FFT()
fft.run("inverse")
#WindowManager.getCurrentImage().hide()
#TODO: how to avoid it to be created?
#ret = WindowManager.getCurrentImage()
#WindowManager.getCurrentImage().hide()
#ret = ImagePlus(ip.getTitle() + " ffti", WindowManager.getCurrentImage().getProcessor())
ret = WindowManager.getCurrentImage()
if not show:
WindowManager.getCurrentImage().hide()
return ret
def bandpass_filter(ip, small_dia_px, large_dia_px, suppress_stripes = 0, stripes_tolerance_direction = 5.0, autoscale_after_filtering = False, saturate_if_autoscale = False, display_filter = False, in_place=True):
"""
suppress_stripes = 0 for none, 1 for horizontal, 2 for vertical
"""
ip = ip if in_place else ip.duplicate()
filter= FFTFilter();
FFTFilter.filterLargeDia = large_dia_px
FFTFilter.filterSmallDia = small_dia_px
FFTFilter.choiceIndex = suppress_stripes
FFTFilter.toleranceDia = stripes_tolerance_direction
FFTFilter.doScalingDia = autoscale_after_filtering
FFTFilter.saturateDia = saturate_if_autoscale
FFTFilter.displayFilter =display_filter
filter.setup(None, ip);
filter.run(ip.getProcessor())
return ip
###################################################################################################
#Convolution
###################################################################################################
KERNEL_BLUR = [[0.1111, 0.1111, 0.1111], [0.1111, 0.1111, 0.1111], [0.1111, 0.1111, 0.1111]]
KERNEL_SHARPEN = [[0.0, -0.75, 0.0], [-0.75, 4.0, -0.75], [0.0, -0.75, 0.0]]
KERNEL_SHARPEN_2 = [[-1.0, -1.0, -1.0], [-1.0, 9.0, -1.0], [-1.0, -1.0, -1.0]]
KERNEL_LIGHT = [[0.1, 0.1, 0.1], [0.1, 1.0, 0.1],[0.1, 0.1, 0.1]]
KERNEL_DARK = [[0.01, 0.01, 0.01],[0.01, 0.5, 0.01],[0.01, 0.01, 0.01]]
KERNEL_EDGE_DETECT = [[0.0, -0.75, 0.0], [-0.75, 3.0, -0.75], [0.0, -0.75, 0.0]]
KERNEL_EDGE_DETECT_2 = [[-0.5, -0.5, -0.5], [-0.5, 4.0, -0.5], [-0.5, -0.5, -0.5]]
KERNEL_DIFFERENTIAL_EDGE_DETECT = [[-1.0, 0.0, 1.0], [0.0, 0.0, 0.0], [1.0, 0.0, -1.0]]
KERNEL_PREWITT = [[-2.0, -1.0, 0.0], [-1.0, 0.0, 1.0 ], [0.0, 1.0, 2.0]]
KERNEL_SOBEL = [[2.0, 2.0, 0.0], [2.0, 0.0, -2.0 ], [0.0, -2.0, -2.0]]
def convolve(ip, kernel, in_place=True):
"""
kernel: list of lists
"""
ip = ip if in_place else ip.duplicate()
kernel_width = len(kernel)
kernel_height= len(kernel[0])
kernel = [item for row in kernel for item in row]
#Convolver().convolve(ip.getProcessor(), kernel, kernel_width, kernel_height)
ip.getProcessor().convolve(kernel, kernel_width, kernel_height)
return ip
###################################################################################################
#Shortcut to ImageProcessor methods
###################################################################################################
def invert(ip, in_place=True):
ip = ip if in_place else ip.duplicate()
ip.getProcessor().invert()
return ip
def smooth(ip, in_place=True):
ip = ip if in_place else ip.duplicate()
ip.getProcessor().smooth()
return ip
def sharpen(ip, in_place=True):
ip = ip if in_place else ip.duplicate()
ip.getProcessor().sharpen()
return ip
def edges(ip, in_place=True): #Sobel
ip = ip if in_place else ip.duplicate()
ip.getProcessor().findEdges()
return ip
def noise(ip, sigma = 25.0, in_place=True):
ip = ip if in_place else ip.duplicate()
ip.getProcessor().noise(sigma)
return ip
def remap(ip, min=None, max=None, in_place=True):
ip = ip if in_place else ip.duplicate()
if min is None or max is None:
stats = get_statistics(ip, Measurements.MIN_MAX)
if min is None: min = stats.min
if max is None: max = stats.max
ip.getProcessor().setMinAndMax(min, max)
return ip
def set_lut(ip, r, g, b):
"""
r,g and b are lists of 256 integers
"""
r = [x if x<128 else x-256 for x in r]
g = [x if x<128 else x-256 for x in g]
b = [x if x<128 else x-256 for x in b]
ip.setLut(LUT(jarray.array(r,'b'),jarray.array(g,'b'),jarray.array(b,'b')))
def resize(ip, width, height):
"""
Returns new ImagePlus
"""
p = ip.getProcessor().resize(width, height)
return ImagePlus(ip.getTitle() + " resized", p)
def binning(ip, factor):
p=ip.getProcessor().bin(factor)
return ImagePlus(ip.getTitle() + " resized", p)
def get_histogram(ip, hist_min = 0, hist_max = 0, hist_bins = 256, roi=None):
"""
hist_min, hist_max, hist_bins used only for float images (otherwise fixed to 0,255,256)
roi is list [x,y,w,h]
"""
if roi == None: ip.deleteRoi()
else: ip.setRoi(roi[0],roi[1],roi[2],roi[3])
image_statistics = ip.getStatistics(0, hist_bins, hist_min, hist_max)
return image_statistics.getHistogram()
def get_array(ip):
return ip.getProcessor().getIntArray()
def get_line(ip, x1, y1, x2, y2):
return ip.getProcessor().getLine(x1, y1, x2, y2)
def get_pixel_range(ip):
return (ip.getProcessor().getMin(), ip.getProcessor().getMax())
def get_num_channels(ip):
return ip.getProcessor().getNChannels()
def is_binary(ip):
return ip.getProcessor().isBinary()
def get_pixel(ip, x, y):
return ip.getProcessor().getPixel(x,y)
def get_pixel_array(ip, x, y):
a = [0]*get_num_channels(ip)
return ip.getProcessor().getPixel(x,y,a)
def get_pixels(ip):
return ip.getProcessor().getPixels()
def get_width(ip):
return ip.getProcessor().getWidth()
def get_height(ip):
return ip.getProcessor().getHeight()
def get_row(ip, y):
a = [0]*get_width(ip)
array = jarray.array(a,'i')
ip.getProcessor().getRow(0, y, array, get_width(ip))
return array
def get_col(ip, x):
a = [0]*get_height(ip)
array = jarray.array(a,'i')
ip.getProcessor().getColumn(x, 0, array, get_height(ip))
return array
def get_statistics(ip, measurements = None):
"""
Measurements is a mask of flags: https://imagej.nih.gov/ij/developer/api/ij/measure/Measurements.html.
Statistics object hold public fields: https://imagej.nih.gov/ij/developer/api/ij/process/ImageStatistics.html
"""
if measurements is None:
return ip.getStatistics()
else:
return ip.getStatistics(measurements)
###################################################################################################
#Image stack functions
###################################################################################################
def create_stack(ip_list, keep=True, title = None):
stack = Concatenator().concatenate(ip_list, keep)
if title is not None:
stack.setTitle(title)
return stack
def reslice(stack, start_at = "Top", vertically = True, flip = True, output_pixel_spacing=1.0, avoid_interpolation = True, title = None):
ss = Slicer()
ss.rotate = vertically
ss.startAt = start_at
ss.flip = flip
ss.nointerpolate = avoid_interpolation
ss.outputZSpacing = output_pixel_spacing
stack = ss.reslice(stack)
if title is not None:
stack.setTitle(title)
return stack
###############################################################################
# ImagePlus list operations
###############################################################################
def integrate_ips(ips, as_float=True):
"""
Integrate list if ImagePlus with the same size.
"""
aux = None
for i in range(len(ips)):
if i==0:
img_type = "float" if as_float else "short"
aux = new_image(ips[i].width, ips[i].height, image_type=img_type, title = "sum", fill_color = None)
op_image(aux, ips[i], "add", float_result=as_float, in_place=True)
return aux
def average_ips (ips, roi=None, as_float=True):
"""
Average list if ImagePlus with the same size.
"""
aux = integrate_ips(ips, as_float)
op_const(aux, "divide", float(len(ips)), in_place=True)
return aux