diff --git a/.gitignore b/.gitignore index 643044a..7d3311d 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,6 @@ docs/_build/ # coverage cover/ + +# generated by various tools +html/ diff --git a/doc/resource/user-guide/image.rst b/doc/resource/user-guide/image.rst new file mode 100644 index 0000000..d24b19a --- /dev/null +++ b/doc/resource/user-guide/image.rst @@ -0,0 +1,48 @@ +Image Operations in VisTrails +----------------------------- + +"arithmetic" VisTrails module +============================= +The VisTrails module `arithmetic` enables basic arithmetic for image processing +and data analysis. The function is capable of applying the basic arithmetic +operations (addition, subtraction, multiplication and division) to two data set +arrays, two constants, or an array and a constant. + +**Addition.** The addition of EITHER two images or volume data sets, OR an +image/data set and a value. This function is typically used for offset purposes, +or basic recombination of several isolated materials or phases into a single +segmented volume. + +**Subtraction.** Enables the subtraction of EITHER one image or volume data set +from another, OR reduction of all values in an image/data set by a set value. +This function is typically used for offset purposes, or basic isolation of +objects or materials/phases in a data set. + +**Multiplication.** Enables the multiplication of input 1 (x1) by input 2 (x2). +The inputs can be of any valid numpy data type (e.g. an image or volume data a +fixed, constant, value). This function is typically used for offset purposes +(rescaling), or for assigning values in the generation of a labelfield identifying +objects or materials/phases in a data set. + +**Division.** Enables the division of input 1 (x1, numerator) by input 2 (x2, +denominator). The inputs can be of any valid numpy data type (e.g. an image or +volume data a fixed, constant, value). Basic tests are included in the division +function which test for, and ensure that division by zero does not occur. This +function is typically used for offset purposes (rescaling, normalization). + +"arithmetic_expression" VisTrails module +======================================== +The VisTrails module `arithmetic_expression` enables the use of custom +expressions to evaluate up to 8 input variables, named A-H. This function +enables more complex arithmetic to be carried out on 2 or more (current limit +is 8) arrays or constants. The arithmetic expression is defined by the user, as +a string, and after assignment of inputs A through H the string is parsed into +the appropriate python expression and executed. Note that inputs C through H +are optional and need only be defined when desired or required. + +"logical" VisTrails module +========================== +The VisTrails module `logical` enables the computation of the basic logical +operations oft used in image processing of two image or volume data sets. This +function can be used for data comparison, material isolation, noise removal, +or mask application/generation. diff --git a/vt_config/NSLS-II/init.py b/vt_config/NSLS-II/init.py index 8e19ff3..b1efcd2 100644 --- a/vt_config/NSLS-II/init.py +++ b/vt_config/NSLS-II/init.py @@ -52,6 +52,7 @@ # get modules to import import_dict = load_config() + _black_list = ['who', 'mafromtxt', 'ndfromtxt', 'source', 'info', 'add_newdoc_ufunc', 'frombuffer', 'fromiter', 'frompyfunc', 'getbuffer', @@ -103,6 +104,7 @@ def get_modules(): 'scipy.special', 'scipy.stats', 'skxray.calibration', + 'skxray.image_processing.arithmetic', 'skxray.correlation', 'skxray.core', 'skxray.recip', @@ -111,6 +113,7 @@ def get_modules(): 'skxray.io.save_powder_output', 'skxray.io.gsas_file_reader', 'skxray.api.diffraction', + 'vttools.to_wrap.image_processing.arithmetic', 'vttools.to_wrap.fitting', ] diff --git a/vttools/tests/__init__.py b/vttools/tests/__init__.py index 8b13789..06e0246 100644 --- a/vttools/tests/__init__.py +++ b/vttools/tests/__init__.py @@ -1 +1,36 @@ - +# ###################################################################### +# Copyright (c) 2014, Brookhaven Science Associates, Brookhaven # +# National Laboratory. All rights reserved. # +# # +# Redistribution and use in source and binary forms, with or without # +# modification, are permitted provided that the following conditions # +# are met: # +# # +# * Redistributions of source code must retain the above copyright # +# notice, this list of conditions and the following disclaimer. # +# # +# * Redistributions in binary form must reproduce the above copyright # +# notice this list of conditions and the following disclaimer in # +# the documentation and/or other materials provided with the # +# distribution. # +# # +# * Neither the name of the Brookhaven Science Associates, Brookhaven # +# National Laboratory nor the names of its contributors may be used # +# to endorse or promote products derived from this software without # +# specific prior written permission. # +# # +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, # +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OTHERWISE) ARISING # +# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # +# POSSIBILITY OF SUCH DAMAGE. # +######################################################################## +import logging +logger = logging.getLogger(__name__) diff --git a/vttools/tests/test_img_proc.py b/vttools/tests/test_img_proc.py new file mode 100644 index 0000000..e54944b --- /dev/null +++ b/vttools/tests/test_img_proc.py @@ -0,0 +1,274 @@ +# Module for the BNL image processing project +# Developed at the NSLS-II, Brookhaven National Laboratory +# Developed by Gabriel Iltis, Sept. 2014 +""" +This module contains test functions for the file-IO functions +for reading and writing data sets using the netCDF file format. + +The files read and written using this function are assumed to +conform to the format specified for x-ray computed microtomorgraphy +data collected at Argonne National Laboratory, Sector 13, GSECars. +""" + +import numpy as np +from numpy.testing import assert_equal, assert_raises, raises + +import vttools.to_wrap.image_processing.arithmetic as img + + +@raises(AttributeError) +def arithmetic_helper_fails(op, x1, x2): + img.arithmetic(op, x1, x2) + + +def test_arithmetic_fails(): + ops = ['addition', 'subtraction', 'division', 'multiplication'] + x1s = [0] * len(ops) + x2s = [0] * len(ops) + for op, x1, x2 in zip(ops, x1s, x2s): + yield arithmetic_helper_fails, op, x1, x2 + + +def test_arithmetic_basic(): + """ + Test function for the image processing function: arithmetic_basic + + """ + test_array_1 = np.zeros((30, 30, 30), dtype=np.int) + test_array_1[0:15, 0:15, 0:15] = 1 + test_array_2 = np.zeros((30, 30, 30), dtype=np.int) + test_array_2[15:29, 15:29, 15:29] = 87 + test_array_3 = np.ones((40, 30, 30), dtype='float') + test_array_3[10:20, 10:20, 10:20] = 87.4 + test_array_4 = np.zeros((30, 30), dtype=np.int) + test_array_4[24:29, 24:29] = 254 + + test_1D_array_1 = np.zeros(100, dtype=np.int) + test_1D_array_1[0:30] = 50 + test_1D_array_2 = np.zeros(50, dtype=np.int) + test_1D_array_2[20:49] = 10 + test_1D_array_3 = np.ones(10, dtype='float') + + test_constant_int = 5 + test_constant_flt = 2.0 + + # Int array and int constant + add_check = test_array_1 + test_constant_int + sub_check = np.subtract(test_array_1, test_constant_int) + mult_check = np.multiply(test_array_1, test_constant_int) + div_check = np.divide(test_array_1, test_constant_int) + + assert_equal(img.arithmetic('add', test_array_1, test_constant_int), + add_check) + assert_equal(img.arithmetic('subtract', test_array_1, + test_constant_int), sub_check) + assert_equal(img.arithmetic('multiply', test_array_1, + test_constant_int), mult_check) + assert_equal(img.arithmetic('divide', test_array_1, test_constant_int), + div_check) + assert_raises(FloatingPointError, img.arithmetic, 'divide', test_array_1, + test_array_1) + assert_raises(FloatingPointError, img.arithmetic, 'divide', test_array_1, 0) + + # Int array and int array + add_check = test_array_1 + test_array_2 + sub_check = np.subtract(test_array_1, test_array_2) + mult_check = np.multiply(test_array_1, test_array_2) + + assert_equal(img.arithmetic('add', test_array_1, test_array_2), + add_check) + assert_equal(img.arithmetic('subtract', test_array_1, test_array_2), + sub_check) + assert_equal(img.arithmetic('multiply', test_array_1, test_array_2,), + mult_check) + assert_raises(FloatingPointError, img.arithmetic, 'divide', test_array_2, + test_array_1) + + # Float array and float constant + add_check = test_array_3 + test_constant_flt + sub_check = np.subtract(test_array_3, test_constant_flt) + mult_check = np.multiply(test_array_3, test_constant_flt) + div_check = np.divide(test_array_3, test_constant_flt) + + assert_equal(img.arithmetic('add', test_array_3, test_constant_flt), + add_check) + assert_equal(img.arithmetic('subtract', test_array_3, + test_constant_flt), sub_check) + assert_equal(img.arithmetic('multiply', test_array_3, + test_constant_flt), mult_check) + assert_equal(img.arithmetic('divide', test_array_3, test_constant_flt), + div_check) + + # Float array and float array + add_check = test_array_3 + test_array_3 + sub_check = np.subtract(test_array_3, test_array_3) + mult_check = np.multiply(test_array_3, test_array_3) + div_check = np.divide(test_array_3, test_array_3) + + assert_equal(img.arithmetic('add', test_array_3, test_array_3), + add_check) + assert_equal(img.arithmetic('subtract', test_array_3, test_array_3,), + sub_check) + assert_equal(img.arithmetic('multiply', test_array_3, test_array_3), + mult_check) + assert_equal(img.arithmetic('divide', test_array_3, test_array_3), + div_check) + # Mixed dtypes: Int array and float array + assert_equal(img.arithmetic('add', test_array_1, + test_array_1.astype('float')).dtype, float) + # Float array and int constant + assert_equal(img.arithmetic('add', test_array_3, + test_constant_int).dtype, float) + # Int array and float constant + assert_equal(img.arithmetic('add', test_array_1, + test_constant_flt).dtype, float) + # Mismatched array sizes + assert_raises(ValueError, img.arithmetic, 'add', test_array_1, + test_array_3) + + +def test_arithmetic_custom(): + """ + Test function for vttools.to_wrap.image_proc.arithmetic_expression, + a function that allows the inclusion of up to 8 inputs (arrays or + constants) and application of a custom expression, to simplify image + arithmetic including 2 or more objects or parameters. + """ + # TEST DATA + test_array_1 = np.zeros((90, 90, 90), dtype=np.int) + test_array_1[10:19, 10:19, 10:19] = 1 + test_array_2 = np.zeros((90, 90, 90), dtype=np.int) + test_array_2[20:29, 20:29, 20:29] = 2 + test_array_3 = np.zeros((90, 90, 90), dtype=np.int) + test_array_3[30:39, 30:39, 30:39] = 3 + test_array_4 = np.zeros((90, 90, 90), dtype=np.int) + test_array_4[40:49, 40:49, 40:49] = 4 + test_array_5 = np.zeros((90, 90, 90), dtype=np.int) + test_array_5[50:59, 50:59, 50:59] = 5 + test_array_6 = np.zeros((90, 90, 90), dtype=np.int) + test_array_6[60:69, 60:69, 60:69] = 6 + test_array_7 = np.zeros((90, 90, 90), dtype=np.int) + test_array_7[70:79, 70:79, 70:79] = 7 + test_array_8 = np.zeros((90, 90, 90), dtype=np.int) + test_array_8[80:89, 80:89, 80:89] = 8 + + # Array manipulation + # -int only + result = (test_array_1 + test_array_2 + test_array_3 + test_array_4 + + test_array_5 + test_array_6 + test_array_7 + test_array_8) + + assert_equal(img.arithmetic_expression('A+B+C+D+E+F+G+H', test_array_1, + test_array_2, test_array_3, + test_array_4, test_array_5, + test_array_6, test_array_7, + test_array_8), result) + + # -float only + result = ((test_array_1.astype('float') + 3.5) + + (test_array_3.astype('float') / 2.0) - + test_array_4.astype('float')) + + assert_equal(img.arithmetic_expression('(A+B)+(C/D)-E', + test_array_1.astype('float'), 3.5, + test_array_3.astype('float'), 2.0, + test_array_4.astype('float')), + result) + + # -mixed int and float + result = ((test_array_1 + 3.5) + (test_array_3.astype('float') / 2) - + test_array_4) + + assert_equal(img.arithmetic_expression('(A+B)+(C/D)-E', test_array_1, 3.5, + test_array_3.astype('float'), 2, + test_array_4.astype('float')), + result) + + assert_equal(img.arithmetic_expression('(A+B)+(C/D)-E', test_array_1, 3.5, + test_array_3.astype('float'), 2, + test_array_4.astype('float')).dtype, + float) + + +def test_logical(): + """ + Test function for mathops.logic_basic, a function that allows for + logical operations to be performed on one or two arrays or constants + depending on the type of operation. + For example: + logical not only takes one object, and returns the inverse, while the + other operations provide a comparison of two objects). + """ + # TEST DATA + test_array_1 = np.zeros((90, 90, 90), dtype=np.int) + test_array_1[0:39, 0:39, 0:39] = 1 + test_array_2 = np.zeros((90, 90, 90), dtype=np.int) + test_array_2[20:79, 20:79, 20:79] = 2 + test_array_3 = np.zeros((90, 90, 90), dtype=np.int) + test_array_3[40:89, 40:89, 40:89] = 3 + + # and + assert_equal(img.logical('and', test_array_1, test_array_1), test_array_1) + + test_result = img.logical('and', test_array_1, test_array_2) + assert_equal(test_result[20:39, 20:39, 20:39], True) + assert_equal(test_result.sum(), ((39-20)**3)) + + assert_equal(img.logical('and', test_array_1, test_array_3), False) + + # or + assert_equal(img.logical('or', test_array_1, test_array_1), test_array_1) + + assert_equal(img.logical('or', test_array_1, test_array_2).sum(), + (test_array_1.sum() + test_array_2.sum() / 2 - + np.logical_and(test_array_1, test_array_2).sum())) + + test_result = img.logical('or', test_array_1, test_array_3) + assert_equal(test_result.sum(), (test_array_1.sum() + test_array_3.sum() / + test_array_3.max())) + + # not + assert_equal(img.logical('not', test_array_1).sum(), + (90**3-test_array_1.sum())) + + assert_equal(img.logical('not', test_array_3).sum(), + (90**3-(test_array_3.sum()/test_array_3.max()))) + + # xor + assert_equal(img.logical('xor', test_array_1, test_array_1), + np.zeros((90, 90, 90), dtype=np.int)) + + assert_equal(img.logical('xor', test_array_1, test_array_2).sum(), + ((test_array_1.sum() + test_array_2.sum() / 2) - + (2 * np.logical_and(test_array_1, test_array_2).sum()))) + + # nand + assert_equal(img.logical('nand', test_array_1, test_array_1), + np.logical_not(test_array_1)) + + test_result = img.logical('nand', test_array_1, test_array_2) + assert_equal(test_result[20:39, 20:39, 20:39], False) + + # nor + assert_equal(img.logical('nor', test_array_1, test_array_1), + np.logical_not(test_array_1)) + assert_equal(img.logical('nor', test_array_1, test_array_2).sum(), + (np.ones((90, 90, 90), dtype=np.int).sum() - + (np.logical_or(test_array_1, test_array_2).sum()))) + + # subtract + assert_equal(img.logical('sub', test_array_1, test_array_1), False) + + test_result = img.logical('sub', test_array_1, test_array_2) + assert_equal(test_result[20:39, 20:39, 20:39], False) + + assert_equal(test_result.sum(), (test_array_1.sum() - + np.logical_and(test_array_1, + test_array_2).sum())) + + test_result = img.logical('sub', test_array_1, test_array_3) + assert_equal(test_result, test_array_1) + + +if __name__ == '__main__': + import nose + nose.runmodule(argv=['-s', '--with-doctest'], exit=False) diff --git a/vttools/to_wrap/image_processing/__init__.py b/vttools/to_wrap/image_processing/__init__.py new file mode 100644 index 0000000..0ee4e8c --- /dev/null +++ b/vttools/to_wrap/image_processing/__init__.py @@ -0,0 +1,36 @@ +# ###################################################################### +# Copyright (c) 2014, Brookhaven Science Associates, Brookhaven # +# National Laboratory. All rights reserved. # +# # +# Redistribution and use in source and binary forms, with or without # +# modification, are permitted provided that the following conditions # +# are met: # +# # +# * Redistributions of source code must retain the above copyright # +# notice, this list of conditions and the following disclaimer. # +# # +# * Redistributions in binary form must reproduce the above copyright # +# notice this list of conditions and the following disclaimer in # +# the documentation and/or other materials provided with the # +# distribution. # +# # +# * Neither the name of the Brookhaven Science Associates, Brookhaven # +# National Laboratory nor the names of its contributors may be used # +# to endorse or promote products derived from this software without # +# specific prior written permission. # +# # +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, # +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OTHERWISE) ARISING # +# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # +# POSSIBILITY OF SUCH DAMAGE. # +######################################################################## +import logging +logger = logging.getLogger(__name__) \ No newline at end of file diff --git a/vttools/to_wrap/image_processing/arithmetic.py b/vttools/to_wrap/image_processing/arithmetic.py new file mode 100644 index 0000000..42eacb2 --- /dev/null +++ b/vttools/to_wrap/image_processing/arithmetic.py @@ -0,0 +1,273 @@ +# Module for the BNL image processing project +# Developed at the NSLS-II, Brookhaven National Laboratory +# Developed by Gabriel Iltis, Oct. 2013 +""" +This module is designed to facilitate image arithmetic and logical operations +on image data sets. +""" +from __future__ import (absolute_import, division, print_function, + unicode_literals) +import numpy as np +import parser +from skxray.image_processing.arithmetic import (logical_and, logical_nand, + logical_or, logical_nor, + logical_not, logical_xor, + logical_sub, add, subtract, + multiply, divide) +import logging + +logger = logging.getLogger(__name__) + + +__all__ = ["arithmetic", "logical", "arithmetic_expression"] + + +def arithmetic(operation, x1, x2, div_by_zero='raise', out=None): + """Arithmetic for inputs x1 and x2. result = x1 {+ - / *} x2 + + Wrapper around numpy functions 'np.add', 'np.subtract', 'np.divide', + 'np.multiply'. As such, much of this docstring is copied from numpydocs to + preserve the information + + Parameters + ---------- + operation : {"add", "subtract", "multiply", "divide"} + add: + The sum of 'x1' and 'x2', element-wise. Returns a scalar if + both 'x1' and 'x2' are scalars. + Note: Equivalent to 'x1' + 'x2' in terms of array broadcasting. + subtract: + The difference of 'x1' and 'x2', element-wise. Returns a scalar if + both 'x1' and 'x2' are scalars. + Note: Equivalent to ''x1 - x2'' in terms of array broadcasting. + divide: + The quotient 'x1/x2', element-wise. Returns a scalar if + both 'x1' and 'x2' are scalars. + Notes: + - Equivalent to 'x1' / 'x2' in terms of array-broadcasting. + - Behavior on division by zero can be changed using 'seterr'. + - When both 'x1' and 'x2' are of an integer type, 'divide' will + return integers and throw away the fractional part. Moreover, + division by zero always yields zero in integer arithmetic. + multiply: + The product of 'x1' and 'x2', element-wise. Returns a scalar if + both 'x1' and 'x2' are scalars. + Note: Equivalent to 'x1' * 'x2' in terms of array broadcasting. + + x1, x2 : array + Can be floats or arrays + + div_by_zero : {'ignore', 'warn', 'raise'}, optional + Treatment for division by zero. + + out : array, optional + Array into which the output is placed. Its type is preserved and it + must be of the right shape to hold the output. See numpy doc.ufuncs. + + Returns + ------- + output : array # use underscores for variable names, hyphens in prose + Returns the resulting array or constant to the designated variable + + Example + ------- + >>> x1 = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]]) + >>> x2 = np.array([[2, 0, 2], [0, 2, 0], [2, 0, 2]]) + >>> arithmetic('add', x1, x2) + array([[2, 1, 2], + [1, 3, 1], + [2, 1, 2]]) + >>> arithmetic('subtract', x1, x2) + array([[-2, 1, -2], + [ 1, -1, 1], + [-2, 1, -2]]) + >>> arithmetic('multiply', x1, x2) + array([[0, 0, 0], + [0, 2, 0], + [0, 0, 0]]) + >>> arithmetic('divide', x1, x2, div_by_zero='raise') + Traceback (most recent call last): + File "", line 1, in + File "/home/edill/dev/python/VTTools/vttools/to_wrap/arithmetic.py", + line 109, in arithmetic + return op(x1, x2, out) + FloatingPointError: divide by zero encountered in divide + >>> arithmetic('divide', x1, x2, div_by_zero='warn') + /home/edill/dev/python/VTTools/vttools/to_wrap/arithmetic.py:109: + RuntimeWarning: divide by zero encountered in divide + return op(x1, x2, out) + array([[0, 0, 0], + [0, 0, 0], + [0, 0, 0]]) + >>> arithmetic('divide', x1, x2, div_by_zero='ignore') + array([[0, 0, 0], + [0, 0, 0], + [0, 0, 0]]) + """ + # ensure that inputs are numpy arrays + x1 = np.asarray(x1) + x2 = np.asarray(x2) + # use numpy built-in functionality to handle divide by zero problems + np.seterr(divide=div_by_zero) + # can use this one-liner instead of the mapping dictionary + op = getattr(np, operation) + return op(x1, x2, out) + + +def arithmetic_expression(expression, A, B, + C=None, D=None, E=None, F=None, G=None, H=None): + """Custom expression evaluator for up to 8 inputs A-H + + TODO: Note that using the 'Interpreter' object in lmfit.asteval may be a + cleaner and more effective parsing tool for evaluating the input + expression. + + Parameters + ---------- + expression : str + Note that the syntax of the mathematical expression must conform to + python syntax, + eg.: + using * for multiplication instead of x + using ** for exponents instead of ^ + + Arithmetic operators: + + : addition (adds values on either side of the operator + - : subtraction (subtracts values on either side of the operator + * : multiplication (multiplies values on either side of the + operator + / : division (divides the left operand (numerator) by the right + hand operand (denominator)) + % : modulus (divides the left operand (numerator) by the right + hand operand (denominator) and returns the remainder) + ** : exponent (left operand (base) is raised to the power of the + right operand (exponent)) + // : floor division (divides the left operand (numerator) by the + right hand operand (denominator), but returns the quotient + with any digits after the decimal point removed, + e.g. 9.0/2.0 = 4.0) + + Logical operations are also included and available so long as the: + > : greater than + < : less than + == : exactly equals + != : not equal + >= : greater than or equal + <= : less than or equal + + In the event that bitwise operations are required the operators &, + |, ^, ~ may also be used, though I'm struggling to come up with a + scenario where this will be used. + + Order of operations and parenthesis are taken into account when + evaluating the expression. + + A, B : {ndarray, int, float} + Data set or constant to be offset or manipulated + + C, D, E, F, G, H : {ndarray, int, float}, optional + Optional input ports for data sets or constants to be offset or + manipulated using complex, custom, expressions + + + Returns + ------- + output : {ndarray, int, float} + Returns the resulting array or value to the designated variable + + Example + ------- + >>> A = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]]) + >>> B = np.array([[2, 0, 2], [0, 2, 0], [2, 0, 2]]) + >>> C = 4 + >>> D = 1.3 + >>> arithmetic_expression('(A+C)/(B+D)', A, B, C, D) + array([[ 1.21212121, 3.84615385, 1.21212121], + [ 3.84615385, 1.51515152, 3.84615385], + [ 1.21212121, 3.84615385, 1.21212121]]) + """ + return eval(parser.expr(expression).compile()) + + +def logical(operation, x1, x2=None, out=None): + """Boolean logic for inputs x1 and x2 + + Parameters + ---------- + operation : {'and', 'or', 'not', 'xor', 'nor', 'nand', 'sub'} + Binary operations: + and: Compute the truth value of x1 AND x2 element-wise. + or: Compute the truth value of x1 OR x2 element-wise. + xor: Compute the truth value of x1 XOR x2, element-wise. + nor: Compute truth value of NOT (x1 OR x2)) element wise. + nand: Computes the truth value of NOT (x1 AND x2) element wise. + sub: Compute truth value of x1 AND (NOT (x1 AND x2)) element + wise. + Unary operations: + not: Compute the truth value of NOT x element-wise. + + x1, x2 : array + Input arrays. 'x1' and 'x2' must be of the same shape. + Note that x2 is optional for Unary operations + + out : array + An array to store the output. Must be the same shape as input arrays + + Returns + ------- + output : array + Boolean result with the same shape as 'x1' and 'x2' of the logical + operation on corresponding elements of 'x1' and 'x2'. + + See Also + -------- + - User guide section on "Image Operations" + ('/doc/resource/user-guide/image.rst') + ** TODO: This needs to be updated when there are sphinx docs for + these tools.** + - numpy functions: 'np.logical_and', 'np.logical_or', 'np.logical_not', + and 'np.logical_xor' + - skxray functions: 'skxray.image_processing.arithmetic.logical_nand', + 'skxray.image_processing.arithmetic.logical_nor', + and 'skxray.image_processing.arithmetic.logical_sub' + + Example + ------- + >>> x1 = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]]) + >>> x2 = np.array([[2, 0, 2], [0, 2, 0], [2, 0, 2]]) + >>> logical('and', x1, x2) + array([[False, False, False], + [False, True, False], + [False, False, False]], dtype=bool) + >>> logical('or', x1, x2) + array([[ True, True, True], + [ True, True, True], + [ True, True, True]], dtype=bool) + >>> logical('not', x1, x2) # note that 'not' will ignore x2 + array([[1, 0, 1], + [0, 0, 0], + [1, 0, 1]]) + >>> logical('xor', x1, x2) + array([[ True, True, True], + [ True, True, True], + [ True, True, True]], dtype=bool) + >>> logical('xor', x1, x2) + array([[ True, True, True], + [ True, False, True], + [ True, True, True]], dtype=bool) + >>> logical('nand', x1, x2) + array([[ True, True, True], + [ True, True, True], + [ True, True, True]], dtype=bool) + >>> logical('sub', x1, x2) + array([[False, True, False], + [ True, True, True], + [False, True, False]], dtype=bool) + """ + + # can use this one-liner instead of the mapping dictionary + op = globals()["logical_" + operation] + # special case the unary operations + if operation in {'not'}: + return op(x1, out) + return op(x1, x2, out)