From c8bb77a393332ab35c5cea63dd8723799ffa0cf3 Mon Sep 17 00:00:00 2001 From: GenevieveBuckley <30920819+GenevieveBuckley@users.noreply.github.com> Date: Sun, 23 Sep 2018 21:11:01 +1000 Subject: [PATCH 1/8] Add convenience functions for volume rendering transfer functions. --- ipyvolume/transferfunction.py | 133 ++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/ipyvolume/transferfunction.py b/ipyvolume/transferfunction.py index 1229ce49..4ea65d13 100644 --- a/ipyvolume/transferfunction.py +++ b/ipyvolume/transferfunction.py @@ -4,6 +4,7 @@ from traittypes import Array import traitlets import numpy as np +import matplotlib.cm from . import serialize from .serialize import array_rgba_png_serialization, array_serialization N = 1024 @@ -164,3 +165,135 @@ def control(self, max_opacity=0.2): return ipywidgets.VBox( [ipywidgets.HBox([ipywidgets.Label(value="levels:"), l1, l2, l3]), ipywidgets.HBox([ipywidgets.Label(value="opacities:"), o1, o2, o3])] ) + + +def linear_transfer_function(rgb_values, + min_opacity=0, + max_opacity=0.05, + reverse_opacity=False): + """Transfer function from a single RGB value with a linear opacity. + + :param rgb_values: Tuple or list containing the RGB values + of red, green and blue, respectively. + :param min_opacity: Minimum opacity, default value is 0.0. + Lowest possible value is 0.0, optional. + :param max_opacity: Maximum opacity, default value is 0.05. + Highest possible value is 1.0, optional. + :param reverse_opacity: Linearly decrease opacity, optional. + :type rgb: listlike + :type min_opacity: float, int + :type max_opacity: float, int + :type reverse_opacity: bool + :return: transfer_function + :rtype: ipyvolume TransferFunction + + :Example: + >>> import ipyvolume as ipv + >>> rgb = (0, 255, 0) # RGB value for green + >>> green_tf = ipv.transfer_function.linear_transfer_function(rgb) + >>> ds = ipv.datasets.aquariusA2.fetch() + >>> ipv.volshow(ds.data[::4,::4,::4], tf=green_tf) + >>> ipv.show() + + .. seealso:: matplotlib_transfer_function() + """ + _num_elements = 256 # length of rgba transfer function array + r, g, b = [value/255. for value in rgb_values] # rescales 0-255 to float + opacity = np.linspace(min_opacity, max_opacity, num=_num_elements) + if reverse_opacity: opacity = np.flip(opacity, axis=0) + rgba = np.transpose(np.stack([[r] * _num_elements, + [g] * _num_elements, + [b] * _num_elements, + opacity])) + transfer_function = TransferFunction(rgba=rgba) + return transfer_function + + +def matplotlib_transfer_function(colormap_name, + min_opacity=0, + max_opacity=0.05, + reverse_colormap=False, + reverse_opacity=False): + """Transfer function from matplotlib colormaps. + + :param colormap_name: name of matplotlib colormap + :param min_opacity: Minimum opacity, default value is 0. + Lowest possible value is 0, optional. + :param max_opacity: Maximum opacity, default value is 0.05. + Highest possible value is 1.0, optional. + :param reverse_colormap: reversed matplotlib colormap, optional. + :param reverse_opacity: Linearly decrease opacity, optional. + :type colormap_name: str + :type min_opacity: float, int + :type max_opacity: float, int + :type reverse_colormap: bool + :type reverse_opacity: bool + :return: transfer_function + :rtype: ipyvolume TransferFunction + + :Example: + >>> import ipyvolume as ipv + >>> rgb = (0, 255, 0) # RGB value for green + >>> green_tf = ipv.transfer_function.matplotlib_transfer_function('viridis') + >>> ds = ipv.datasets.aquariusA2.fetch() + >>> ipv.volshow(ds.data[::4,::4,::4], tf=green_tf) + >>> ipv.show() + + .. seealso:: linear_transfer_function() + """ + _num_elements = 256 # length of rgba transfer function array + cmap = matplotlib.cm.get_cmap(name=colormap_name) + rgba = np.array([cmap(i) for i in np.linspace(0, 1, _num_elements)]) + if reverse_colormap: rgba = np.flip(rgba, axis=0) + # Create opacity values to overwrite default matplotlib opacity=1.0 + opacity = np.linspace(min_opacity, max_opacity, num=_num_elements) + if reverse_opacity: opacity = np.flip(opacity, axis=0) + rgba[:,-1] = opacity # replace opacity=1 with actual opacity + transfer_function = TransferFunction(rgba=rgba) + return transfer_function + + +def load_transfer_functions(include_rgb_linear=True, + include_matplotlib=True, + include_matplotlib_reversed=True): + """Load predefined transfer functions into a dictionary. + + :param include_rgb_linear: load transfer functions from individual + RGB values & linear opacity, optional. + :param include_matplotlib: load transfer functions from matplotlib + colormaps & linear opacity, optional. + :param include_matplotlib_reversed: load transfer functions from + REVERSED matplotlib colormaps, optional. + :type include_rgb_linear: bool + :type include_matplotlib: bool + :type include_matplotlib_reversed: bool + :return: dictionary of predefined transfer functions. + :rtype: dict of ipyvolume TransferFunction instances + """ + transfer_functions = {} + # RGB primary and secondary colors + if include_rgb_linear: + colors = {'grey': (0, 0, 0), + 'red': (255, 0, 0), + 'green': (0, 255, 0), + 'blue': (0, 0, 255), + 'yellow': (255, 255, 0), + 'magenta': (255, 0, 255), + 'cyan': (0, 255, 255)} + for color_key in colors: + rgb = colors[color_key] + tf = linear_transfer_function(rgb) + transfer_functions[color_key] = tf + reverse_tf = linear_transfer_function(rgb, reverse_opacity=True) + transfer_functions[color_key + '_r'] = tf_reversed + # matplotlib colormaps + if include_matplotlib: + matplotlib_colormaps = [m for m in matplotlib.cm.datad if not m.endswith("_r")] + for colormap in matplotlib_colormaps: + transfer_functions[colormap] = matplotlib_transfer_function(colormap) + # reversed matplotlib colormaps + if include_matplotlib_reversed: + reversed_matplotlib_colormaps = [m for m in matplotlib.cm.datad if m.endswith("_r")] + for colormap in reversed_matplotlib_colormaps: + transfer_functions[colormap] = matplotlib_transfer_function(colormap) + return transfer_functions From 47caedbf58a8383fe89133b85c78714d3c0b83dc Mon Sep 17 00:00:00 2001 From: GenevieveBuckley <30920819+GenevieveBuckley@users.noreply.github.com> Date: Sun, 23 Sep 2018 21:34:20 +1000 Subject: [PATCH 2/8] Fix typo --- ipyvolume/transferfunction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipyvolume/transferfunction.py b/ipyvolume/transferfunction.py index 4ea65d13..970d6205 100644 --- a/ipyvolume/transferfunction.py +++ b/ipyvolume/transferfunction.py @@ -284,7 +284,7 @@ def load_transfer_functions(include_rgb_linear=True, rgb = colors[color_key] tf = linear_transfer_function(rgb) transfer_functions[color_key] = tf - reverse_tf = linear_transfer_function(rgb, reverse_opacity=True) + tf_reversed = linear_transfer_function(rgb, reverse_opacity=True) transfer_functions[color_key + '_r'] = tf_reversed # matplotlib colormaps if include_matplotlib: From 8de9b8400d5ba7b6848fbdf50258f4ec30735638 Mon Sep 17 00:00:00 2001 From: GenevieveBuckley <30920819+GenevieveBuckley@users.noreply.github.com> Date: Sun, 23 Sep 2018 21:49:32 +1000 Subject: [PATCH 3/8] Better matplotlib colormap namespace coverage with dir(matplotlib.cm) --- ipyvolume/transferfunction.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/ipyvolume/transferfunction.py b/ipyvolume/transferfunction.py index 970d6205..5ea9481d 100644 --- a/ipyvolume/transferfunction.py +++ b/ipyvolume/transferfunction.py @@ -288,12 +288,18 @@ def load_transfer_functions(include_rgb_linear=True, transfer_functions[color_key + '_r'] = tf_reversed # matplotlib colormaps if include_matplotlib: - matplotlib_colormaps = [m for m in matplotlib.cm.datad if not m.endswith("_r")] + matplotlib_colormaps = [m for m in dir(matplotlib.cm) if not m.endswith("_r")] for colormap in matplotlib_colormaps: - transfer_functions[colormap] = matplotlib_transfer_function(colormap) + try: + transfer_functions[colormap] = matplotlib_transfer_function(colormap) + except ValueError: + continue # reversed matplotlib colormaps if include_matplotlib_reversed: - reversed_matplotlib_colormaps = [m for m in matplotlib.cm.datad if m.endswith("_r")] + reversed_matplotlib_colormaps = [m for m in dir(matplotlib.cm) if m.endswith("_r")] for colormap in reversed_matplotlib_colormaps: - transfer_functions[colormap] = matplotlib_transfer_function(colormap) + try: + transfer_functions[colormap] = matplotlib_transfer_function(colormap) + except ValueError: + continue return transfer_functions From 9bc0e9f2560d2c719de9c22c9452a19120bae071 Mon Sep 17 00:00:00 2001 From: GenevieveBuckley <30920819+GenevieveBuckley@users.noreply.github.com> Date: Mon, 24 Sep 2018 15:12:00 +1000 Subject: [PATCH 4/8] Travis CI, add matplotlib to test environment. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 25583745..8f2697f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,7 @@ before_install: - conda config --set always_yes yes --set changeps1 no - conda update -q conda - conda info -a - - conda create -q -n test-environment python=$PYTHON_VERSION numpy scipy runipy + - conda create -q -n test-environment python=$PYTHON_VERSION numpy scipy runipy matplotlib - source activate test-environment - conda install -c conda-forge pytest pytest-cov bokeh - pip install coveralls @@ -42,4 +42,4 @@ script: # - runipy examples/examples.ipynb after_success: - coveralls \ No newline at end of file + coveralls From 68b330efe01e1bcf2a1fd187a6964aafdbc7401d Mon Sep 17 00:00:00 2001 From: GenevieveBuckley <30920819+GenevieveBuckley@users.noreply.github.com> Date: Mon, 24 Sep 2018 20:39:10 +1000 Subject: [PATCH 5/8] No single line if statements. --- ipyvolume/transferfunction.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ipyvolume/transferfunction.py b/ipyvolume/transferfunction.py index 5ea9481d..cf649769 100644 --- a/ipyvolume/transferfunction.py +++ b/ipyvolume/transferfunction.py @@ -200,7 +200,8 @@ def linear_transfer_function(rgb_values, _num_elements = 256 # length of rgba transfer function array r, g, b = [value/255. for value in rgb_values] # rescales 0-255 to float opacity = np.linspace(min_opacity, max_opacity, num=_num_elements) - if reverse_opacity: opacity = np.flip(opacity, axis=0) + if reverse_opacity: + opacity = np.flip(opacity, axis=0) rgba = np.transpose(np.stack([[r] * _num_elements, [g] * _num_elements, [b] * _num_elements, @@ -234,7 +235,7 @@ def matplotlib_transfer_function(colormap_name, :Example: >>> import ipyvolume as ipv >>> rgb = (0, 255, 0) # RGB value for green - >>> green_tf = ipv.transfer_function.matplotlib_transfer_function('viridis') + >>> green_tf = ipv.transfer_function.matplotlib_transfer_function('bone') >>> ds = ipv.datasets.aquariusA2.fetch() >>> ipv.volshow(ds.data[::4,::4,::4], tf=green_tf) >>> ipv.show() @@ -244,10 +245,12 @@ def matplotlib_transfer_function(colormap_name, _num_elements = 256 # length of rgba transfer function array cmap = matplotlib.cm.get_cmap(name=colormap_name) rgba = np.array([cmap(i) for i in np.linspace(0, 1, _num_elements)]) - if reverse_colormap: rgba = np.flip(rgba, axis=0) + if reverse_colormap: + rgba = np.flip(rgba, axis=0) # Create opacity values to overwrite default matplotlib opacity=1.0 opacity = np.linspace(min_opacity, max_opacity, num=_num_elements) - if reverse_opacity: opacity = np.flip(opacity, axis=0) + if reverse_opacity: + opacity = np.flip(opacity, axis=0) rgba[:,-1] = opacity # replace opacity=1 with actual opacity transfer_function = TransferFunction(rgba=rgba) return transfer_function From 89a6d792581acd29a3086a86a58c876c09116453 Mon Sep 17 00:00:00 2001 From: GenevieveBuckley <30920819+GenevieveBuckley@users.noreply.github.com> Date: Mon, 24 Sep 2018 21:37:51 +1000 Subject: [PATCH 6/8] Transfer functions - allow length of rgba array to be passed as kwarg --- ipyvolume/transferfunction.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/ipyvolume/transferfunction.py b/ipyvolume/transferfunction.py index cf649769..e89b4dee 100644 --- a/ipyvolume/transferfunction.py +++ b/ipyvolume/transferfunction.py @@ -170,7 +170,8 @@ def control(self, max_opacity=0.2): def linear_transfer_function(rgb_values, min_opacity=0, max_opacity=0.05, - reverse_opacity=False): + reverse_opacity=False, + n_elements = 256): """Transfer function from a single RGB value with a linear opacity. :param rgb_values: Tuple or list containing the RGB values @@ -180,10 +181,12 @@ def linear_transfer_function(rgb_values, :param max_opacity: Maximum opacity, default value is 0.05. Highest possible value is 1.0, optional. :param reverse_opacity: Linearly decrease opacity, optional. + :param n_elements: Length of rgba array transfer function attribute. :type rgb: listlike :type min_opacity: float, int :type max_opacity: float, int :type reverse_opacity: bool + :type n_elements: int :return: transfer_function :rtype: ipyvolume TransferFunction @@ -197,14 +200,13 @@ def linear_transfer_function(rgb_values, .. seealso:: matplotlib_transfer_function() """ - _num_elements = 256 # length of rgba transfer function array r, g, b = [value/255. for value in rgb_values] # rescales 0-255 to float - opacity = np.linspace(min_opacity, max_opacity, num=_num_elements) + opacity = np.linspace(min_opacity, max_opacity, num=n_elements) if reverse_opacity: opacity = np.flip(opacity, axis=0) - rgba = np.transpose(np.stack([[r] * _num_elements, - [g] * _num_elements, - [b] * _num_elements, + rgba = np.transpose(np.stack([[r] * n_elements, + [g] * n_elements, + [b] * n_elements, opacity])) transfer_function = TransferFunction(rgba=rgba) return transfer_function @@ -214,7 +216,8 @@ def matplotlib_transfer_function(colormap_name, min_opacity=0, max_opacity=0.05, reverse_colormap=False, - reverse_opacity=False): + reverse_opacity=False, + n_elements=256): """Transfer function from matplotlib colormaps. :param colormap_name: name of matplotlib colormap @@ -224,11 +227,13 @@ def matplotlib_transfer_function(colormap_name, Highest possible value is 1.0, optional. :param reverse_colormap: reversed matplotlib colormap, optional. :param reverse_opacity: Linearly decrease opacity, optional. + :param n_elements: Length of rgba array transfer function attribute. :type colormap_name: str :type min_opacity: float, int :type max_opacity: float, int :type reverse_colormap: bool :type reverse_opacity: bool + :type n_elements: int :return: transfer_function :rtype: ipyvolume TransferFunction @@ -242,13 +247,12 @@ def matplotlib_transfer_function(colormap_name, .. seealso:: linear_transfer_function() """ - _num_elements = 256 # length of rgba transfer function array cmap = matplotlib.cm.get_cmap(name=colormap_name) - rgba = np.array([cmap(i) for i in np.linspace(0, 1, _num_elements)]) + rgba = np.array([cmap(i) for i in np.linspace(0, 1, n_elements)]) if reverse_colormap: rgba = np.flip(rgba, axis=0) # Create opacity values to overwrite default matplotlib opacity=1.0 - opacity = np.linspace(min_opacity, max_opacity, num=_num_elements) + opacity = np.linspace(min_opacity, max_opacity, num=n_elements) if reverse_opacity: opacity = np.flip(opacity, axis=0) rgba[:,-1] = opacity # replace opacity=1 with actual opacity From a4d809c4e53b98b742fb6cccb92f611d27467a80 Mon Sep 17 00:00:00 2001 From: GenevieveBuckley <30920819+GenevieveBuckley@users.noreply.github.com> Date: Mon, 24 Sep 2018 22:21:45 +1000 Subject: [PATCH 7/8] More flexible color input for transfer functions with matplotlib.colors.to_rgb() --- ipyvolume/transferfunction.py | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/ipyvolume/transferfunction.py b/ipyvolume/transferfunction.py index e89b4dee..f8d7273b 100644 --- a/ipyvolume/transferfunction.py +++ b/ipyvolume/transferfunction.py @@ -5,6 +5,7 @@ import traitlets import numpy as np import matplotlib.cm +import matplotlib.colors from . import serialize from .serialize import array_rgba_png_serialization, array_serialization N = 1024 @@ -167,22 +168,22 @@ def control(self, max_opacity=0.2): ) -def linear_transfer_function(rgb_values, +def linear_transfer_function(color, min_opacity=0, max_opacity=0.05, reverse_opacity=False, n_elements = 256): - """Transfer function from a single RGB value with a linear opacity. + """Transfer function of a single color and linear opacity. - :param rgb_values: Tuple or list containing the RGB values - of red, green and blue, respectively. + :param color: Listlike RGB, or string with hexidecimal or named color. + RGB values should be within 0-1 range. :param min_opacity: Minimum opacity, default value is 0.0. Lowest possible value is 0.0, optional. :param max_opacity: Maximum opacity, default value is 0.05. Highest possible value is 1.0, optional. :param reverse_opacity: Linearly decrease opacity, optional. :param n_elements: Length of rgba array transfer function attribute. - :type rgb: listlike + :type color: listlike or string :type min_opacity: float, int :type max_opacity: float, int :type reverse_opacity: bool @@ -192,15 +193,14 @@ def linear_transfer_function(rgb_values, :Example: >>> import ipyvolume as ipv - >>> rgb = (0, 255, 0) # RGB value for green - >>> green_tf = ipv.transfer_function.linear_transfer_function(rgb) + >>> green_tf = ipv.transfer_function.linear_transfer_function('green') >>> ds = ipv.datasets.aquariusA2.fetch() >>> ipv.volshow(ds.data[::4,::4,::4], tf=green_tf) >>> ipv.show() .. seealso:: matplotlib_transfer_function() """ - r, g, b = [value/255. for value in rgb_values] # rescales 0-255 to float + r, g, b = matplotlib.colors.to_rgb(color) opacity = np.linspace(min_opacity, max_opacity, num=n_elements) if reverse_opacity: opacity = np.flip(opacity, axis=0) @@ -280,17 +280,11 @@ def load_transfer_functions(include_rgb_linear=True, transfer_functions = {} # RGB primary and secondary colors if include_rgb_linear: - colors = {'grey': (0, 0, 0), - 'red': (255, 0, 0), - 'green': (0, 255, 0), - 'blue': (0, 0, 255), - 'yellow': (255, 255, 0), - 'magenta': (255, 0, 255), - 'cyan': (0, 255, 255)} - for color_key in colors: - rgb = colors[color_key] - tf = linear_transfer_function(rgb) - transfer_functions[color_key] = tf + colors = ['red', 'green', 'blue', 'yellow', 'magenta', 'cyan', + 'black', 'gray', 'white'] + for color in colors: + tf = linear_transfer_function(color) + transfer_functions[color] = tf tf_reversed = linear_transfer_function(rgb, reverse_opacity=True) transfer_functions[color_key + '_r'] = tf_reversed # matplotlib colormaps From 2321bf721a60a3fb872b58f9e711a649c22c0473 Mon Sep 17 00:00:00 2001 From: GenevieveBuckley <30920819+GenevieveBuckley@users.noreply.github.com> Date: Wed, 26 Sep 2018 11:22:31 +1000 Subject: [PATCH 8/8] Simplify predefined_transfer_functions() discovery of all the matplotlib colormaps. --- ipyvolume/transferfunction.py | 48 +++++++++-------------------------- 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/ipyvolume/transferfunction.py b/ipyvolume/transferfunction.py index f8d7273b..7e9c6d59 100644 --- a/ipyvolume/transferfunction.py +++ b/ipyvolume/transferfunction.py @@ -260,47 +260,23 @@ def matplotlib_transfer_function(colormap_name, return transfer_function -def load_transfer_functions(include_rgb_linear=True, - include_matplotlib=True, - include_matplotlib_reversed=True): +def predefined_transfer_functions(): """Load predefined transfer functions into a dictionary. - :param include_rgb_linear: load transfer functions from individual - RGB values & linear opacity, optional. - :param include_matplotlib: load transfer functions from matplotlib - colormaps & linear opacity, optional. - :param include_matplotlib_reversed: load transfer functions from - REVERSED matplotlib colormaps, optional. - :type include_rgb_linear: bool - :type include_matplotlib: bool - :type include_matplotlib_reversed: bool :return: dictionary of predefined transfer functions. :rtype: dict of ipyvolume TransferFunction instances """ transfer_functions = {} # RGB primary and secondary colors - if include_rgb_linear: - colors = ['red', 'green', 'blue', 'yellow', 'magenta', 'cyan', - 'black', 'gray', 'white'] - for color in colors: - tf = linear_transfer_function(color) - transfer_functions[color] = tf - tf_reversed = linear_transfer_function(rgb, reverse_opacity=True) - transfer_functions[color_key + '_r'] = tf_reversed - # matplotlib colormaps - if include_matplotlib: - matplotlib_colormaps = [m for m in dir(matplotlib.cm) if not m.endswith("_r")] - for colormap in matplotlib_colormaps: - try: - transfer_functions[colormap] = matplotlib_transfer_function(colormap) - except ValueError: - continue - # reversed matplotlib colormaps - if include_matplotlib_reversed: - reversed_matplotlib_colormaps = [m for m in dir(matplotlib.cm) if m.endswith("_r")] - for colormap in reversed_matplotlib_colormaps: - try: - transfer_functions[colormap] = matplotlib_transfer_function(colormap) - except ValueError: - continue + colors = ['red', 'green', 'blue', 'yellow', 'magenta', 'cyan', + 'black', 'gray', 'white'] + for color in colors: + tf = linear_transfer_function(color) + transfer_functions[color] = tf + tf_reversed = linear_transfer_function(rgb, reverse_opacity=True) + transfer_functions[color_key + '_r'] = tf_reversed + # All matplotlib colormaps + matplotlib_colormaps = matplotlib.cm.cmap_d.keys() + for colormap in matplotlib_colormaps: + transfer_functions[colormap] = matplotlib_transfer_function(colormap) return transfer_functions