# HG changeset patch # User imgteam # Date 1710021859 0 # Node ID 33b2ca53a56615a076a49cf66809d1d5640e5b3f planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tree/master/tools/image_math commit b356d76025941b691c156f8ff931cd759d35b107 diff -r 000000000000 -r 33b2ca53a566 image_math.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/image_math.py Sat Mar 09 22:04:19 2024 +0000 @@ -0,0 +1,90 @@ +import argparse +import ast +import operator + +import numpy as np +import skimage.io + + +supported_operators = { + ast.Add: operator.add, + ast.Sub: operator.sub, + ast.Mult: operator.mul, + ast.Div: operator.truediv, + ast.FloorDiv: operator.floordiv, + ast.Pow: operator.pow, + ast.USub: operator.neg, +} + + +supported_functions = { + 'sqrt': np.sqrt, + 'abs': abs, +} + + +def eval_ast_node(node, inputs): + """ + Evaluates a node of the syntax tree. + """ + + # Numeric constants evaluate to numeric values. + if isinstance(node, ast.Constant): + assert type(node.value) in (int, float) + return node.value + + # Variables are looked up from the inputs and resolved. + if isinstance(node, ast.Name): + assert node.id in inputs.keys() + return inputs[node.id] + + # Binary operators are evaluated based on the `supported_operators` dictionary. + if isinstance(node, ast.BinOp): + assert type(node.op) in supported_operators.keys(), node.op + op = supported_operators[type(node.op)] + return op(eval_ast_node(node.left, inputs), eval_ast_node(node.right, inputs)) + + # Unary operators are evaluated based on the `supported_operators` dictionary. + if isinstance(node, ast.UnaryOp): + assert type(node.op) in supported_operators.keys(), node.op + op = supported_operators[type(node.op)] + return op(eval_ast_node(node.operand, inputs)) + + # Function calls are evaluated based on the `supported_functions` dictionary. + if isinstance(node, ast.Call): + assert len(node.args) == 1 and len(node.keywords) == 0 + assert node.func.id in supported_functions.keys(), node.func.id + func = supported_functions[node.func.id] + return func(eval_ast_node(node.args[0], inputs)) + + # The node is unsupported and could not be evaluated. + raise TypeError(f'Unsupported node type: "{node}"') + + +def eval_expression(expr, inputs): + return eval_ast_node(ast.parse(expr, mode='eval').body, inputs) + + +if __name__ == '__main__': + + parser = argparse.ArgumentParser() + parser.add_argument('--expression', type=str, required=True) + parser.add_argument('--output', type=str, required=True) + parser.add_argument('--input', default=list(), action='append', required=True) + args = parser.parse_args() + + inputs = dict() + im_shape = None + for input in args.input: + name, filepath = input.split(':') + im = skimage.io.imread(filepath) + assert name not in inputs, 'Input name "{name}" is ambiguous.' + inputs[name] = im + if im_shape is None: + im_shape = im.shape + else: + assert im.shape == im_shape, 'Input images differ in size and/or number of channels.' + + result = eval_expression(args.expression, inputs) + + skimage.io.imsave(args.output, result) diff -r 000000000000 -r 33b2ca53a566 image_math.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/image_math.xml Sat Mar 09 22:04:19 2024 +0000 @@ -0,0 +1,131 @@ + + with NumPy + + 1.26.4 + 0 + + + operation_3443 + + + numpy + scikit-image + + + + + + ^[a-zA-Z0-9-_\*\+ \(\)/]+$ + + + + + ^[a-zA-Z_][a-zA-Z0-9_]*$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This tool processes images according to pixel-wise arithmetic expressions. + + The supported pixel-wise expressions are: + + - Addition, subtraction, multiplication, and division (``+``, ``-``, ``*``, ``/``) + - Integer division (e.g., ``input // 2``) + - Power (e.g., ``input ** 2``) + - Negation (e.g., ``-input``) + - Absolute values (e.g., ``abs(input)``) + - Square root (e.g., ``sqrt(input)``) + - Combinations of the above (also using parentheses) + + Examples: + + - **Negate an image.** + Expression: ``-image`` + where ``image`` is an arbitrary input image. + + - **Mean of two images.** + Expression: ``(image1 + image2) / 2`` + where ``image1`` and `image2` are two arbitrary input images. + + - **Perform division avoiding division-by-zero.** + Expression: ``image1 / (abs(image2) + 1e-8)`` + where ``image1`` and `image2` are two arbitrary input images. + + + + 10.1038/s41586-020-2649-2 + + \ No newline at end of file diff -r 000000000000 -r 33b2ca53a566 test-data/half_of_input1_plus_one.tiff Binary file test-data/half_of_input1_plus_one.tiff has changed diff -r 000000000000 -r 33b2ca53a566 test-data/input1.tiff Binary file test-data/input1.tiff has changed diff -r 000000000000 -r 33b2ca53a566 test-data/input1_abs.tiff Binary file test-data/input1_abs.tiff has changed diff -r 000000000000 -r 33b2ca53a566 test-data/input1_times_2.tiff Binary file test-data/input1_times_2.tiff has changed diff -r 000000000000 -r 33b2ca53a566 test-data/minus_input1.tiff Binary file test-data/minus_input1.tiff has changed diff -r 000000000000 -r 33b2ca53a566 test-data/ones.tiff Binary file test-data/ones.tiff has changed