From 35bbb9852524515db7fa6996065200e8d365c8dc Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Wed, 19 Jul 2023 18:54:51 -0300 Subject: [PATCH 01/41] Post processing functions file --- fury/postprocessing.py | 177 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 fury/postprocessing.py diff --git a/fury/postprocessing.py b/fury/postprocessing.py new file mode 100644 index 000000000..71e0ae420 --- /dev/null +++ b/fury/postprocessing.py @@ -0,0 +1,177 @@ +import numpy as np +from fury import window, actor +from vtk import vtkWindowToImageFilter +from fury.lib import Texture +from fury.io import load_image +from fury.utils import rgb_to_vtk + + +def window_to_texture( + window : window.RenderWindow, + texture_name : str, + target_actor : actor.Actor, + blending_mode : str = "None", + wrap_mode : str = "ClampToBorder", + border_color : tuple = ( + 0.0, + 0.0, + 0.0, + 1.0), + interpolate : bool = True): + """Captures a rendered window and pass it as a texture to the given actor. + Parameters + ---------- + window : window.RenderWindow + Window to be captured. + texture_name : str + Name of the texture to be passed to the actor. + target_actor : actor.Actor + Target actor to receive the texture. + blending_mode : str, optional + Texture blending mode. The options are: + 1. None + 2. Replace + 3. Modulate + 4. Add + 5. AddSigned + 6. Interpolate + 7. Subtract + wrap_mode : str, optional + Texture wrapping mode. The options are: + 1. ClampToEdge + 2. Repeat + 3. MirroredRepeat + 4. ClampToBorder + border_color : tuple (4, ), optional + Texture RGBA border color. + interpolate : bool, optional + Texture interpolation.""" + + wrap_mode_dic = {"ClampToEdge" : Texture.ClampToEdge, + "Repeat" : Texture.Repeat, + "MirroredRepeat" : Texture.MirroredRepeat, + "ClampToBorder" : Texture.ClampToBorder} + + blending_mode_dic = {"None" : 0, "Replace" : 1, + "Modulate" : 2, "Add" : 3, + "AddSigned" : 4, "Interpolate" : 5, + "Subtract" : 6} + + r, g, b, a = border_color + + windowToImageFilter = vtkWindowToImageFilter() + windowToImageFilter.SetInput(window) + + windowToImageFilter.Update() + + texture = Texture() + texture.SetInputConnection(windowToImageFilter.GetOutputPort()) + texture.SetBorderColor(r, g, b, a) + texture.SetWrap(wrap_mode_dic[wrap_mode]) + texture.SetInterpolate(interpolate) + texture.MipmapOn() + texture.SetBlendingMode(blending_mode_dic[blending_mode]) + + target_actor.GetProperty().SetTexture(texture_name, texture) + + +def texture_to_actor( + path_to_texture : str, + texture_name : str, + target_actor : actor.Actor, + blending_mode : str = "None", + wrap_mode : str = "ClampToBorder", + border_color : tuple = ( + 0.0, + 0.0, + 0.0, + 1.0), + interpolate : bool = True): + """Passes an imported texture to an actor. + Parameters + ---------- + path_to_texture : str + Texture image path. + texture_name : str + Name of the texture to be passed to the actor. + target_actor : actor.Actor + Target actor to receive the texture. + blending_mode : str + Texture blending mode. The options are: + 1. None + 2. Replace + 3. Modulate + 4. Add + 5. AddSigned + 6. Interpolate + 7. Subtract + wrap_mode : str + Texture wrapping mode. The options are: + 1. ClampToEdge + 2. Repeat + 3. MirroredRepeat + 4. ClampToBorder + border_color : tuple (4, ) + Texture RGBA border color. + interpolate : bool + Texture interpolation.""" + + wrap_mode_dic = {"ClampToEdge" : Texture.ClampToEdge, + "Repeat" : Texture.Repeat, + "MirroredRepeat" : Texture.MirroredRepeat, + "ClampToBorder" : Texture.ClampToBorder} + + blending_mode_dic = {"None" : 0, "Replace" : 1, + "Modulate" : 2, "Add" : 3, + "AddSigned" : 4, "Interpolate" : 5, + "Subtract" : 6} + + r, g, b, a = border_color + + texture = Texture() + + colormapArray = load_image(path_to_texture) + colormapData = rgb_to_vtk(colormapArray) + + texture.SetInputDataObject(colormapData) + texture.SetBorderColor(r, g, b, a) + texture.SetWrap(wrap_mode_dic[wrap_mode]) + texture.SetInterpolate(interpolate) + texture.MipmapOn() + texture.SetBlendingMode(blending_mode_dic[blending_mode]) + + target_actor.GetProperty().SetTexture(texture_name, texture) + + +def colormap_to_texture( + colormap : np.array, + texture_name : str, + target_actor : actor.Actor, + interpolate : bool = True): + """Converts a colormap to a texture and pass it to an actor. + Parameters + ---------- + colormap : np.array (N, 4) or (1, N, 4) + RGBA color map array. The array can be two dimensional, although a three dimensional one is preferred. + texture_name : str + Name of the color map texture to be passed to the actor. + target_actor : actor.Actor + Target actor to receive the color map texture. + interpolate : bool + Color map texture interpolation.""" + + if len(colormap.shape) == 2: + colormap = np.array([colormap]) + + texture = Texture() + + cmap = (255*colormap).astype(np.uint8) + cmap = rgb_to_vtk(cmap) + + texture.SetInputDataObject(cmap) + texture.SetWrap(Texture.ClampToEdge) + texture.SetInterpolate(interpolate) + texture.MipmapOn() + texture.SetBlendingMode(0) + + target_actor.GetProperty().SetTexture(texture_name, texture) \ No newline at end of file From ef06af3a5130a6bc2895e7a0ad554e0a2835bf86 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Wed, 19 Jul 2023 18:59:05 -0300 Subject: [PATCH 02/41] docs? --- docs/experimental/viz_kde_2d.py | 377 ++++++++++++++++++++++++++++ docs/experimental/viz_kde_3d_att.py | 340 +++++++++++++++++++++++++ 2 files changed, 717 insertions(+) create mode 100644 docs/experimental/viz_kde_2d.py create mode 100644 docs/experimental/viz_kde_3d_att.py diff --git a/docs/experimental/viz_kde_2d.py b/docs/experimental/viz_kde_2d.py new file mode 100644 index 000000000..d688b8f38 --- /dev/null +++ b/docs/experimental/viz_kde_2d.py @@ -0,0 +1,377 @@ +import numpy as np +from fury import window, actor +from fury.shaders import compose_shader, shader_apply_effects +from fury.lib import Texture, WindowToImageFilter +from fury.io import load_image +from fury.utils import rgb_to_vtk +from matplotlib import colormaps + + +def window_to_texture( + window : window.RenderWindow, + texture_name : str, + target_actor : actor.Actor, + blending_mode : str = "None", + wrap_mode : str = "ClampToBorder", + border_color : tuple = ( + 0.0, + 0.0, + 0.0, + 1.0), + interpolate : bool = True): + """Captures a rendered window and pass it as a texture to the given actor. + + Parameters + ---------- + window : window.RenderWindow + Window to be captured. + texture_name : str + Name of the texture to be passed to the actor. + target_actor : actor.Actor + Target actor to receive the texture. + blending_mode : str, optional + Texture blending mode. The options are: + 1. None + 2. Replace + 3. Modulate + 4. Add + 5. AddSigned + 6. Interpolate + 7. Subtract + + wrap_mode : str, optional + Texture wrapping mode. The options are: + 1. ClampToEdge + 2. Repeat + 3. MirroredRepeat + 4. ClampToBorder + + border_color : tuple (4, ), optional + Texture RGBA border color. + interpolate : bool, optional + Texture interpolation.""" + + wrap_mode_dic = {"ClampToEdge" : Texture.ClampToEdge, + "Repeat" : Texture.Repeat, + "MirroredRepeat" : Texture.MirroredRepeat, + "ClampToBorder" : Texture.ClampToBorder} + + blending_mode_dic = {"None" : 0, "Replace" : 1, + "Modulate" : 2, "Add" : 3, + "AddSigned" : 4, "Interpolate" : 5, + "Subtract" : 6} + + r, g, b, a = border_color + + windowToImageFilter = WindowToImageFilter() + windowToImageFilter.SetInput(window) + + windowToImageFilter.Update() + + texture = Texture() + texture.SetInputConnection(windowToImageFilter.GetOutputPort()) + texture.SetBorderColor(r, g, b, a) + texture.SetWrap(wrap_mode_dic[wrap_mode]) + texture.SetInterpolate(interpolate) + texture.MipmapOn() + texture.SetBlendingMode(blending_mode_dic[blending_mode]) + + target_actor.GetProperty().SetTexture(texture_name, texture) + + +def texture_to_actor( + path_to_texture : str, + texture_name : str, + target_actor : actor.Actor, + blending_mode : str = "None", + wrap_mode : str = "ClampToBorder", + border_color : tuple = ( + 0.0, + 0.0, + 0.0, + 1.0), + interpolate : bool = True): + """Passes an imported texture to an actor. + + Parameters + ---------- + path_to_texture : str + Texture image path. + texture_name : str + Name of the texture to be passed to the actor. + target_actor : actor.Actor + Target actor to receive the texture. + blending_mode : str + Texture blending mode. The options are: + 1. None + 2. Replace + 3. Modulate + 4. Add + 5. AddSigned + 6. Interpolate + 7. Subtract + + wrap_mode : str + Texture wrapping mode. The options are: + 1. ClampToEdge + 2. Repeat + 3. MirroredRepeat + 4. ClampToBorder + + border_color : tuple (4, ) + Texture RGBA border color. + interpolate : bool + Texture interpolation.""" + + wrap_mode_dic = {"ClampToEdge" : Texture.ClampToEdge, + "Repeat" : Texture.Repeat, + "MirroredRepeat" : Texture.MirroredRepeat, + "ClampToBorder" : Texture.ClampToBorder} + + blending_mode_dic = {"None" : 0, "Replace" : 1, + "Modulate" : 2, "Add" : 3, + "AddSigned" : 4, "Interpolate" : 5, + "Subtract" : 6} + + r, g, b, a = border_color + + texture = Texture() + + colormapArray = load_image(path_to_texture) + colormapData = rgb_to_vtk(colormapArray) + + texture.SetInputDataObject(colormapData) + texture.SetBorderColor(r, g, b, a) + texture.SetWrap(wrap_mode_dic[wrap_mode]) + texture.SetInterpolate(interpolate) + texture.MipmapOn() + texture.SetBlendingMode(blending_mode_dic[blending_mode]) + + target_actor.GetProperty().SetTexture(texture_name, texture) + + +def colormap_to_texture( + colormap : np.array, + texture_name : str, + target_actor : actor.Actor, + interpolate : bool = True): + """Converts a colormap to a texture and pass it to an actor. + + Parameters + ---------- + colormap : np.array (N, 4) or (1, N, 4) + RGBA color map array. The array can be two dimensional, although a three dimensional one is preferred. + texture_name : str + Name of the color map texture to be passed to the actor. + target_actor : actor.Actor + Target actor to receive the color map texture. + interpolate : bool + Color map texture interpolation.""" + + if len(colormap.shape) == 2: + colormap = np.array([colormap]) + + texture = Texture() + + cmap = (255*colormap).astype(np.uint8) + cmap = rgb_to_vtk(cmap) + + texture.SetInputDataObject(cmap) + texture.SetWrap(Texture.ClampToEdge) + texture.SetInterpolate(interpolate) + texture.MipmapOn() + texture.SetBlendingMode(0) + + target_actor.GetProperty().SetTexture(texture_name, texture) + + +def shader_custom_uniforms(actor : actor.Actor, shader_type : str): + """Eases the passing of uniform values to the shaders by returning ``actor.GetShaderProperty().GetVertexCustomUniforms()``, + that give access to the ``SetUniform`` methods. + Parameters + ---------- + actor : actor.Actor + Actor which the uniform values will be passed to. + shader_type : str + Shader type of the uniform values to be passed. It can be: + * "vertex" + * "fragment" + * "geometry" + """ + if shader_type == "vertex": + return actor.GetShaderProperty().GetVertexCustomUniforms() + elif shader_type == "fragment": + return actor.GetShaderProperty().GetFragmentCustomUniforms() + elif shader_type == "geometry": + return actor.GetShaderProperty().GetGeometryCustomUniforms() + else: + raise ValueError("Shader type unknown.") + + +def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int = 0): + """Converts an array to a given desired range. + + Parameters + ---------- + array : np.ndarray + Array to be normalized. + min : float + Bottom value of the interval of normalization. If no value is given, it is passed as 0.0. + max : float + Upper value of the interval of normalization. If no value is given, it is passed as 1.0. + + Returns + ------- + + array : np.array + Array converted to the given desired range. + """ + if np.max(array) != np.min(array): + return ((array - np.min(array))/(np.max(array) - np.min(array)))*(max - min) + min + else: + raise ValueError( + "Can't normalize an array which maximum and minimum value are the same.") + + +kde_dec = """ +float kde(vec3 point, float sigma){ + return exp(-1.0*pow(length(point), 2.0)/(2.0*sigma*sigma) ); +} +""" + +kde_impl = """ +float current_kde = kde(normalizedVertexMCVSOutput, sigma); +color = vec3(current_kde); +fragOutput0 = vec4(color, 1.0); +""" + +tex_dec = """ +const float normalizingFactor = 1.0; // Parameter to be better defined later + +vec3 color_mapping(float intensity, float normalizingFactor){ + float normalizedIntensity = intensity/normalizingFactor; + return texture(colormapTexture, vec2(normalizedIntensity,0)).rgb; +} +""" + +tex_impl = """ +vec2 renorm_tex = normalizedVertexMCVSOutput.xy*0.5 + 0.5; +float intensity = texture(screenTexture, renorm_tex).r; + +if(intensity<=0.0){ + discard; +}else{ + color = color_mapping(intensity, normalizingFactor).rgb; + fragOutput0 = vec4(color, 1.0); +} +""" + + +fs_dec = compose_shader([kde_dec]) + +fs_impl = compose_shader([kde_impl]) + + +# Windows and scenes setup +width, height = (1920, 1080) +offWidth, offHeight = (1080, 1080) + +offScene = window.Scene() +offScene.set_camera(position=(-6, 5, -10), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + +off_manager = window.ShowManager( + offScene, + "demo", + (offWidth, + offHeight), + reset_camera=True, + order_transparent=True) + +off_manager.window.SetOffScreenRendering(True) + +off_manager.initialize() + + +scene = window.Scene() +scene.set_camera(position=(-6, 5, -10), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + +manager = window.ShowManager( + scene, + "demo", + (width, + height), + reset_camera=True, + order_transparent=True) + + +manager.initialize() + +n_points = 1000 +points = np.random.rand(n_points, 3) +points = normalize(points, -5, 5) +sigma = 0.3 +scale = 0.5 + +billboard = actor.billboard( + points, + (0.0, + 0.0, + 1.0), + scales=scale, + fs_dec=fs_dec, + fs_impl=fs_impl) + +# Blending and uniforms setup +shader_apply_effects(off_manager.window, billboard, window.gl_disable_depth) +shader_apply_effects(off_manager.window, billboard, window.gl_set_additive_blending) +shader_custom_uniforms(billboard, "fragment").SetUniformf("sigma", sigma) + +off_manager.scene.add(billboard) + +off_manager.render() + +scale = np.array([width/height, 1.0, 0.0]) + +# Render to second billboard for color map post-processing. +textured_billboard = actor.billboard(np.array([[0.0, 0.0, 0.0]]), (1.0, 1.0, 1.0), + scales=20.0*scale, fs_dec=tex_dec, fs_impl=tex_impl) + +cmap = colormaps["inferno"] +cmap = np.array([cmap(i) for i in np.arange(0.0, 1.0, 1/256)]) + +colormap_to_texture(cmap, "colormapTexture", textured_billboard) + + +def event_callback(obj, event): + pos, focal, vu = manager.scene.get_camera() + off_manager.scene.set_camera(pos, focal, vu) + off_manager.scene.Modified() + off_manager.render() + + window_to_texture( + off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate") + + + +window_to_texture( + off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate") + +manager.scene.add(textured_billboard) + +manager.add_iren_callback(event_callback, "RenderEvent") + +manager.start() diff --git a/docs/experimental/viz_kde_3d_att.py b/docs/experimental/viz_kde_3d_att.py new file mode 100644 index 000000000..c0a2b9748 --- /dev/null +++ b/docs/experimental/viz_kde_3d_att.py @@ -0,0 +1,340 @@ +import numpy as np +from fury import window, actor +from fury.shaders import compose_shader, shader_apply_effects, attribute_to_actor +from fury.lib import Texture, WindowToImageFilter +from fury.io import load_image +from fury.utils import rgb_to_vtk +from matplotlib import colormaps + + +def window_to_texture( + window : window.RenderWindow, + texture_name : str, + target_actor : actor.Actor, + blending_mode : str = "None", + wrap_mode : str = "ClampToBorder", + border_color : tuple = ( + 0.0, + 0.0, + 0.0, + 1.0), + interpolate : bool = True): + """Captures a rendered window and pass it as a texture to the given actor. + + Parameters + ---------- + window : window.RenderWindow + Window to be captured. + texture_name : str + Name of the texture to be passed to the actor. + target_actor : actor.Actor + Target actor to receive the texture. + blending_mode : str, optional + Texture blending mode. The options are: + 1. None + 2. Replace + 3. Modulate + 4. Add + 5. AddSigned + 6. Interpolate + 7. Subtract + + wrap_mode : str, optional + Texture wrapping mode. The options are: + 1. ClampToEdge + 2. Repeat + 3. MirroredRepeat + 4. ClampToBorder + + border_color : tuple (4, ), optional + Texture RGBA border color. + interpolate : bool, optional + Texture interpolation.""" + + wrap_mode_dic = {"ClampToEdge" : Texture.ClampToEdge, + "Repeat" : Texture.Repeat, + "MirroredRepeat" : Texture.MirroredRepeat, + "ClampToBorder" : Texture.ClampToBorder} + + blending_mode_dic = {"None" : 0, "Replace" : 1, + "Modulate" : 2, "Add" : 3, + "AddSigned" : 4, "Interpolate" : 5, + "Subtract" : 6} + + r, g, b, a = border_color + + windowToImageFilter = WindowToImageFilter() + windowToImageFilter.SetInput(window) + + windowToImageFilter.Update() + + texture = Texture() + texture.SetInputConnection(windowToImageFilter.GetOutputPort()) + texture.SetBorderColor(r, g, b, a) + texture.SetWrap(wrap_mode_dic[wrap_mode]) + texture.SetInterpolate(interpolate) + texture.MipmapOn() + texture.SetBlendingMode(blending_mode_dic[blending_mode]) + + target_actor.GetProperty().SetTexture(texture_name, texture) + + +def texture_to_actor( + path_to_texture : str, + texture_name : str, + target_actor : actor.Actor, + blending_mode : str = "None", + wrap_mode : str = "ClampToBorder", + border_color : tuple = ( + 0.0, + 0.0, + 0.0, + 1.0), + interpolate : bool = True): + """Passes an imported texture to an actor. + + Parameters + ---------- + path_to_texture : str + Texture image path. + texture_name : str + Name of the texture to be passed to the actor. + target_actor : actor.Actor + Target actor to receive the texture. + blending_mode : str + Texture blending mode. The options are: + 1. None + 2. Replace + 3. Modulate + 4. Add + 5. AddSigned + 6. Interpolate + 7. Subtract + + wrap_mode : str + Texture wrapping mode. The options are: + 1. ClampToEdge + 2. Repeat + 3. MirroredRepeat + 4. ClampToBorder + + border_color : tuple (4, ) + Texture RGBA border color. + interpolate : bool + Texture interpolation.""" + + wrap_mode_dic = {"ClampToEdge" : Texture.ClampToEdge, + "Repeat" : Texture.Repeat, + "MirroredRepeat" : Texture.MirroredRepeat, + "ClampToBorder" : Texture.ClampToBorder} + + blending_mode_dic = {"None" : 0, "Replace" : 1, + "Modulate" : 2, "Add" : 3, + "AddSigned" : 4, "Interpolate" : 5, + "Subtract" : 6} + + r, g, b, a = border_color + + texture = Texture() + + colormapArray = load_image(path_to_texture) + colormapData = rgb_to_vtk(colormapArray) + + texture.SetInputDataObject(colormapData) + texture.SetBorderColor(r, g, b, a) + texture.SetWrap(wrap_mode_dic[wrap_mode]) + texture.SetInterpolate(interpolate) + texture.MipmapOn() + texture.SetBlendingMode(blending_mode_dic[blending_mode]) + + target_actor.GetProperty().SetTexture(texture_name, texture) + + +def colormap_to_texture( + colormap : np.array, + texture_name : str, + target_actor : actor.Actor, + interpolate : bool = True): + """Converts a colormap to a texture and pass it to an actor. + + Parameters + ---------- + colormap : np.array (N, 4) or (1, N, 4) + RGBA color map array. The array can be two dimensional, although a three dimensional one is preferred. + texture_name : str + Name of the color map texture to be passed to the actor. + target_actor : actor.Actor + Target actor to receive the color map texture. + interpolate : bool + Color map texture interpolation.""" + + if len(colormap.shape) == 2: + colormap = np.array([colormap]) + + texture = Texture() + + cmap = (255*colormap).astype(np.uint8) + cmap = rgb_to_vtk(cmap) + + texture.SetInputDataObject(cmap) + texture.SetWrap(Texture.ClampToEdge) + texture.SetInterpolate(interpolate) + texture.MipmapOn() + texture.SetBlendingMode(0) + + target_actor.GetProperty().SetTexture(texture_name, texture) + + +def shader_custom_uniforms(actor : actor.Actor, shader_type : str): + """Eases the passing of uniform values to the shaders by returning ``actor.GetShaderProperty().GetVertexCustomUniforms()``, + that give access to the ``SetUniform`` methods. + Parameters + ---------- + actor : actor.Actor + Actor which the uniform values will be passed to. + shader_type : str + Shader type of the uniform values to be passed. It can be: + * "vertex" + * "fragment" + * "geometry" + """ + if shader_type == "vertex": + return actor.GetShaderProperty().GetVertexCustomUniforms() + elif shader_type == "fragment": + return actor.GetShaderProperty().GetFragmentCustomUniforms() + elif shader_type == "geometry": + return actor.GetShaderProperty().GetGeometryCustomUniforms() + else: + raise ValueError("Shader type unknown.") + + +def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int = 0): + """Converts an array to a given desired range. + + Parameters + ---------- + array : np.ndarray + Array to be normalized. + min : float + Bottom value of the interval of normalization. If no value is given, it is passed as 0.0. + max : float + Upper value of the interval of normalization. If no value is given, it is passed as 1.0. + + Returns + ------- + + array : np.array + Array converted to the given desired range. + """ + if np.max(array) != np.min(array): + return ((array - np.min(array))/(np.max(array) - np.min(array)))*(max - min) + min + else: + raise ValueError( + "Can't normalize an array which maximum and minimum value are the same.") + + +kde_vs_dec = """ +in float in_sigma; +varying float out_sigma; + +in float in_scale; +varying float out_scale; +""" + + +kde_vs_impl = """ +out_sigma = in_sigma; +out_scale = in_scale; +""" + + +kde_dec = """ +varying float out_sigma; +varying float out_scale; +float kde(vec3 point, float sigma, float scale){ + return exp(-1.0*pow(length(point/scale), 2.0)/(2.0*sigma*sigma) ); +} + +vec3 color_mapping(float intensity, float normalizingFactor){ + float normalizedIntensity = intensity/normalizingFactor; + return texture(colormapTexture, vec2(normalizedIntensity,0)).rgb; +} +""" + +kde_impl = """ +float current_kde = kde(normalizedVertexMCVSOutput, out_sigma, out_scale); + +if(current_kde <= discard_value){ + discard; +}else{ + color = color_mapping(current_kde, 1.0).rgb;; + fragOutput0 = vec4(color, 1.0); +} +""" + + + +fs_dec = compose_shader([kde_dec]) + +fs_impl = compose_shader([kde_impl]) + + +# Windows and scenes setup +width, height = (1920, 1080) +offWidth, offHeight = (1080, 1080) + +offScene = window.Scene() + +off_manager = window.ShowManager( + offScene, + "demo", + (width, + height), + reset_camera=True, + order_transparent=True) + + +off_manager.initialize() + + + +n_points = 500 +points = np.random.rand(n_points, 3) +points = normalize(points, -5, 5) +scales = np.random.rand(n_points, 1) +sigmas = normalize(np.random.rand(n_points, 1), 0.3, 0.5) + +sigma = 0.25 +scale = 0.5 + +billboard = actor.billboard( + points, + (0.0, + 0.0, + 1.0), + scales=scales, + fs_dec=fs_dec, + fs_impl=fs_impl, + vs_dec=kde_vs_dec, + vs_impl=kde_vs_impl) + +# Blending and uniforms setup +shader_apply_effects(off_manager.window, billboard, window.gl_disable_depth) +shader_apply_effects(off_manager.window, billboard, window.gl_set_additive_blending) +shader_custom_uniforms(billboard, "fragment").SetUniformf("sigma", sigma) +shader_custom_uniforms(billboard, "fragment").SetUniformf("discard_value", 0.0001) +attribute_to_actor(billboard, np.repeat(scales, 4), "in_scale") +attribute_to_actor(billboard, np.repeat(sigmas, 4), "in_sigma") + +off_manager.scene.add(billboard) + + + +# Render to second billboard for color map post-processing. +cmap = colormaps["inferno"] +cmap = np.array([cmap(i) for i in np.arange(0.0, 1.0, 1/256)]) + + +colormap_to_texture(cmap, "colormapTexture", billboard) + +off_manager.start() From 110ad6bde018d032ed119d6d0228c6efdfb3d03d Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Thu, 20 Jul 2023 19:30:33 -0300 Subject: [PATCH 03/41] feat: Introduces EffectManager Class --- docs/experimental/viz_kde_2d.py | 239 ++------------------ docs/experimental/viz_kde_class.py | 61 +++++ fury/actors/effect_manager.py | 153 +++++++++++++ fury/postprocessing.py | 22 +- fury/shaders/__init__.py | 1 + fury/shaders/base.py | 21 ++ fury/shaders/effects/color_mapping.glsl | 3 + fury/shaders/utils/normal_distribution.glsl | 11 + 8 files changed, 274 insertions(+), 237 deletions(-) create mode 100644 docs/experimental/viz_kde_class.py create mode 100644 fury/actors/effect_manager.py create mode 100644 fury/shaders/effects/color_mapping.glsl create mode 100644 fury/shaders/utils/normal_distribution.glsl diff --git a/docs/experimental/viz_kde_2d.py b/docs/experimental/viz_kde_2d.py index d688b8f38..affb59000 100644 --- a/docs/experimental/viz_kde_2d.py +++ b/docs/experimental/viz_kde_2d.py @@ -1,213 +1,11 @@ import numpy as np from fury import window, actor -from fury.shaders import compose_shader, shader_apply_effects -from fury.lib import Texture, WindowToImageFilter -from fury.io import load_image -from fury.utils import rgb_to_vtk +from fury.shaders import compose_shader, shader_apply_effects, import_fury_shader, shader_custom_uniforms +from os.path import join +from fury.postprocessing import window_to_texture, colormap_to_texture from matplotlib import colormaps -def window_to_texture( - window : window.RenderWindow, - texture_name : str, - target_actor : actor.Actor, - blending_mode : str = "None", - wrap_mode : str = "ClampToBorder", - border_color : tuple = ( - 0.0, - 0.0, - 0.0, - 1.0), - interpolate : bool = True): - """Captures a rendered window and pass it as a texture to the given actor. - - Parameters - ---------- - window : window.RenderWindow - Window to be captured. - texture_name : str - Name of the texture to be passed to the actor. - target_actor : actor.Actor - Target actor to receive the texture. - blending_mode : str, optional - Texture blending mode. The options are: - 1. None - 2. Replace - 3. Modulate - 4. Add - 5. AddSigned - 6. Interpolate - 7. Subtract - - wrap_mode : str, optional - Texture wrapping mode. The options are: - 1. ClampToEdge - 2. Repeat - 3. MirroredRepeat - 4. ClampToBorder - - border_color : tuple (4, ), optional - Texture RGBA border color. - interpolate : bool, optional - Texture interpolation.""" - - wrap_mode_dic = {"ClampToEdge" : Texture.ClampToEdge, - "Repeat" : Texture.Repeat, - "MirroredRepeat" : Texture.MirroredRepeat, - "ClampToBorder" : Texture.ClampToBorder} - - blending_mode_dic = {"None" : 0, "Replace" : 1, - "Modulate" : 2, "Add" : 3, - "AddSigned" : 4, "Interpolate" : 5, - "Subtract" : 6} - - r, g, b, a = border_color - - windowToImageFilter = WindowToImageFilter() - windowToImageFilter.SetInput(window) - - windowToImageFilter.Update() - - texture = Texture() - texture.SetInputConnection(windowToImageFilter.GetOutputPort()) - texture.SetBorderColor(r, g, b, a) - texture.SetWrap(wrap_mode_dic[wrap_mode]) - texture.SetInterpolate(interpolate) - texture.MipmapOn() - texture.SetBlendingMode(blending_mode_dic[blending_mode]) - - target_actor.GetProperty().SetTexture(texture_name, texture) - - -def texture_to_actor( - path_to_texture : str, - texture_name : str, - target_actor : actor.Actor, - blending_mode : str = "None", - wrap_mode : str = "ClampToBorder", - border_color : tuple = ( - 0.0, - 0.0, - 0.0, - 1.0), - interpolate : bool = True): - """Passes an imported texture to an actor. - - Parameters - ---------- - path_to_texture : str - Texture image path. - texture_name : str - Name of the texture to be passed to the actor. - target_actor : actor.Actor - Target actor to receive the texture. - blending_mode : str - Texture blending mode. The options are: - 1. None - 2. Replace - 3. Modulate - 4. Add - 5. AddSigned - 6. Interpolate - 7. Subtract - - wrap_mode : str - Texture wrapping mode. The options are: - 1. ClampToEdge - 2. Repeat - 3. MirroredRepeat - 4. ClampToBorder - - border_color : tuple (4, ) - Texture RGBA border color. - interpolate : bool - Texture interpolation.""" - - wrap_mode_dic = {"ClampToEdge" : Texture.ClampToEdge, - "Repeat" : Texture.Repeat, - "MirroredRepeat" : Texture.MirroredRepeat, - "ClampToBorder" : Texture.ClampToBorder} - - blending_mode_dic = {"None" : 0, "Replace" : 1, - "Modulate" : 2, "Add" : 3, - "AddSigned" : 4, "Interpolate" : 5, - "Subtract" : 6} - - r, g, b, a = border_color - - texture = Texture() - - colormapArray = load_image(path_to_texture) - colormapData = rgb_to_vtk(colormapArray) - - texture.SetInputDataObject(colormapData) - texture.SetBorderColor(r, g, b, a) - texture.SetWrap(wrap_mode_dic[wrap_mode]) - texture.SetInterpolate(interpolate) - texture.MipmapOn() - texture.SetBlendingMode(blending_mode_dic[blending_mode]) - - target_actor.GetProperty().SetTexture(texture_name, texture) - - -def colormap_to_texture( - colormap : np.array, - texture_name : str, - target_actor : actor.Actor, - interpolate : bool = True): - """Converts a colormap to a texture and pass it to an actor. - - Parameters - ---------- - colormap : np.array (N, 4) or (1, N, 4) - RGBA color map array. The array can be two dimensional, although a three dimensional one is preferred. - texture_name : str - Name of the color map texture to be passed to the actor. - target_actor : actor.Actor - Target actor to receive the color map texture. - interpolate : bool - Color map texture interpolation.""" - - if len(colormap.shape) == 2: - colormap = np.array([colormap]) - - texture = Texture() - - cmap = (255*colormap).astype(np.uint8) - cmap = rgb_to_vtk(cmap) - - texture.SetInputDataObject(cmap) - texture.SetWrap(Texture.ClampToEdge) - texture.SetInterpolate(interpolate) - texture.MipmapOn() - texture.SetBlendingMode(0) - - target_actor.GetProperty().SetTexture(texture_name, texture) - - -def shader_custom_uniforms(actor : actor.Actor, shader_type : str): - """Eases the passing of uniform values to the shaders by returning ``actor.GetShaderProperty().GetVertexCustomUniforms()``, - that give access to the ``SetUniform`` methods. - Parameters - ---------- - actor : actor.Actor - Actor which the uniform values will be passed to. - shader_type : str - Shader type of the uniform values to be passed. It can be: - * "vertex" - * "fragment" - * "geometry" - """ - if shader_type == "vertex": - return actor.GetShaderProperty().GetVertexCustomUniforms() - elif shader_type == "fragment": - return actor.GetShaderProperty().GetFragmentCustomUniforms() - elif shader_type == "geometry": - return actor.GetShaderProperty().GetGeometryCustomUniforms() - else: - raise ValueError("Shader type unknown.") - - def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int = 0): """Converts an array to a given desired range. @@ -233,11 +31,7 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int "Can't normalize an array which maximum and minimum value are the same.") -kde_dec = """ -float kde(vec3 point, float sigma){ - return exp(-1.0*pow(length(point), 2.0)/(2.0*sigma*sigma) ); -} -""" +kde_dec = import_fury_shader(join("utils", "normal_distribution.glsl")) kde_impl = """ float current_kde = kde(normalizedVertexMCVSOutput, sigma); @@ -245,23 +39,17 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int fragOutput0 = vec4(color, 1.0); """ -tex_dec = """ -const float normalizingFactor = 1.0; // Parameter to be better defined later - -vec3 color_mapping(float intensity, float normalizingFactor){ - float normalizedIntensity = intensity/normalizingFactor; - return texture(colormapTexture, vec2(normalizedIntensity,0)).rgb; -} -""" +tex_dec = import_fury_shader(join("effects", "color_mapping.glsl")) tex_impl = """ +// Turning screen coordinates to texture coordinates vec2 renorm_tex = normalizedVertexMCVSOutput.xy*0.5 + 0.5; float intensity = texture(screenTexture, renorm_tex).r; if(intensity<=0.0){ discard; }else{ - color = color_mapping(intensity, normalizingFactor).rgb; + color = color_mapping(intensity, colormapTexture).rgb; fragOutput0 = vec4(color, 1.0); } """ @@ -287,9 +75,7 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int offScene, "demo", (offWidth, - offHeight), - reset_camera=True, - order_transparent=True) + offHeight)) off_manager.window.SetOffScreenRendering(True) @@ -307,9 +93,7 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int scene, "demo", (width, - height), - reset_camera=True, - order_transparent=True) + height)) manager.initialize() @@ -342,7 +126,10 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int # Render to second billboard for color map post-processing. textured_billboard = actor.billboard(np.array([[0.0, 0.0, 0.0]]), (1.0, 1.0, 1.0), - scales=20.0*scale, fs_dec=tex_dec, fs_impl=tex_impl) + scales=10.0, fs_dec=tex_dec, fs_impl=tex_impl) + +# Disables the texture warnings +textured_billboard.GetProperty().GlobalWarningDisplayOff() cmap = colormaps["inferno"] cmap = np.array([cmap(i) for i in np.arange(0.0, 1.0, 1/256)]) diff --git a/docs/experimental/viz_kde_class.py b/docs/experimental/viz_kde_class.py new file mode 100644 index 000000000..8c3134d88 --- /dev/null +++ b/docs/experimental/viz_kde_class.py @@ -0,0 +1,61 @@ +from fury.actors.effect_manager import EffectManager +from fury.window import Scene, ShowManager +import numpy as np + +def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int = 0): + """Converts an array to a given desired range. + + Parameters + ---------- + array : np.ndarray + Array to be normalized. + min : float + Bottom value of the interval of normalization. If no value is given, it is passed as 0.0. + max : float + Upper value of the interval of normalization. If no value is given, it is passed as 1.0. + + Returns + ------- + + array : np.array + Array converted to the given desired range. + """ + if np.max(array) != np.min(array): + return ((array - np.min(array))/(np.max(array) - np.min(array)))*(max - min) + min + else: + raise ValueError( + "Can't normalize an array which maximum and minimum value are the same.") + + +width, height = (1920, 1080) + +scene = Scene() +scene.set_camera(position=(-6, 5, -10), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + +manager = ShowManager( + scene, + "demo", + (width, + height)) + +manager.initialize() + + +n_points = 500 +points = np.random.rand(n_points, 3) +points = normalize(points, -5, 5) +sigmas = normalize(np.random.rand(n_points, 1), 0.1, 0.3) +print(sigmas.shape[0]) +print(points.shape[0]) + +effects = EffectManager(manager) + +kde_actor = effects.kde(np.array([[0.0, 0.0, 0.0]]), points, sigmas, scale = 20.0, colormap = "inferno") + +manager.scene.add(kde_actor) + +manager.start() \ No newline at end of file diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py new file mode 100644 index 000000000..45babc981 --- /dev/null +++ b/fury/actors/effect_manager.py @@ -0,0 +1,153 @@ +import os +import numpy as np +from fury.actor import billboard +from fury.postprocessing import (colormap_to_texture, + window_to_texture) +from fury.shaders import (import_fury_shader, + compose_shader, + attribute_to_actor, + shader_apply_effects, + shader_custom_uniforms) +from fury.window import (ShowManager, + Scene, + gl_disable_depth, + gl_set_additive_blending) +from matplotlib import colormaps + +class EffectManager(): + """Class that manages the application of post-processing effects on actors. + + Parameters + ---------- + manager : ShowManager + Target manager that wil render post processed actors.""" + def __init__(self, manager : ShowManager): + self.scene = Scene() + pos, focal, vu = manager.scene.get_camera() + self.scene.set_camera(pos, focal, vu) + self.scene.set_camera() + self.on_manager = manager + self.off_manager = ShowManager(self.scene, + size=manager.size) + self.off_manager.window.SetOffScreenRendering(True) + self.off_manager.initialize() + + + + def kde(self, center, points : np.ndarray, sigmas, scale = 1, colormap = "viridis", custom_colormap : np.array = None): + if not isinstance(sigmas, np.ndarray): + sigmas = np.array(sigmas) + if sigmas.shape[0] != 1 and sigmas.shape[0] != points.shape[0]: + raise IndexError("sigmas size must be one or points size.") + + varying_dec = """ + varying float out_sigma; + varying float out_scale; + """ + + kde_dec = import_fury_shader(os.path.join("utils", "normal_distribution.glsl")) + + kde_impl = """ + float current_kde = kde(normalizedVertexMCVSOutput/out_scale, out_sigma); + color = vec3(current_kde); + fragOutput0 = vec4(color, 1.0); + """ + + kde_vs_dec = """ + in float in_sigma; + varying float out_sigma; + + in float in_scale; + varying float out_scale; + """ + + + kde_vs_impl = """ + out_sigma = in_sigma; + out_scale = in_scale; + """ + + tex_dec = import_fury_shader(os.path.join("effects", "color_mapping.glsl")) + + tex_impl = """ + // Turning screen coordinates to texture coordinates + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + float intensity = texture(screenTexture, renorm_tex).r; + color = color_mapping(intensity, colormapTexture).rgb; + + if(intensity<=0.0){ + discard; + }else{ + fragOutput0 = vec4(color, 1.0); + } + """ + + fs_dec = compose_shader([varying_dec, kde_dec]) + + # Scales parameter will be defined by the empirical rule: + # 1*sima = 68.27% of data inside the curve + # 2*sigma = 95.45% of data inside the curve + # 3*sigma = 99.73% of data inside the curve + scales = 3.0*np.copy(sigmas) + + bill = billboard( + points, + (0.0, + 0.0, + 1.0), + scales=scales, + fs_dec=fs_dec, + fs_impl=kde_impl, + vs_dec=kde_vs_dec, + vs_impl=kde_vs_impl) + + # Blending and uniforms setup + window = self.off_manager.window + shader_apply_effects(window, bill, gl_disable_depth) + shader_apply_effects(window, bill, gl_set_additive_blending) + + attribute_to_actor(bill, np.repeat(sigmas, 4), "in_sigma") + attribute_to_actor(bill, np.repeat(scales, 4), "in_scale") + + self.off_manager.scene.add(bill) + + self.off_manager.render() + + # Render to second billboard for color map post-processing. + textured_billboard = billboard(center, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) + shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) + + # Disables the texture warnings + textured_billboard.GetProperty().GlobalWarningDisplayOff() + + if custom_colormap == None: + cmap = colormaps[colormap] + cmap = np.array([cmap(i) for i in np.arange(0.0, 1.0, 1/256)]) + else: + cmap = custom_colormap + + colormap_to_texture(cmap, "colormapTexture", textured_billboard) + + def event_callback(obj, event): + pos, focal, vu = self.on_manager.scene.get_camera() + self.off_manager.scene.set_camera(pos, focal, vu) + self.off_manager.scene.Modified() + self.off_manager.render() + + window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate") + + window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate") + + self.on_manager.add_iren_callback(event_callback, "RenderEvent") + + return textured_billboard + \ No newline at end of file diff --git a/fury/postprocessing.py b/fury/postprocessing.py index 71e0ae420..7d88fe8ab 100644 --- a/fury/postprocessing.py +++ b/fury/postprocessing.py @@ -1,15 +1,15 @@ import numpy as np -from fury import window, actor -from vtk import vtkWindowToImageFilter -from fury.lib import Texture +from fury.window import RenderWindow +from fury.actor import Actor +from fury.lib import Texture, WindowToImageFilter from fury.io import load_image from fury.utils import rgb_to_vtk def window_to_texture( - window : window.RenderWindow, + window : RenderWindow, texture_name : str, - target_actor : actor.Actor, + target_actor : Actor, blending_mode : str = "None", wrap_mode : str = "ClampToBorder", border_color : tuple = ( @@ -25,7 +25,7 @@ def window_to_texture( Window to be captured. texture_name : str Name of the texture to be passed to the actor. - target_actor : actor.Actor + target_actor : Actor Target actor to receive the texture. blending_mode : str, optional Texture blending mode. The options are: @@ -59,7 +59,7 @@ def window_to_texture( r, g, b, a = border_color - windowToImageFilter = vtkWindowToImageFilter() + windowToImageFilter = WindowToImageFilter() windowToImageFilter.SetInput(window) windowToImageFilter.Update() @@ -78,7 +78,7 @@ def window_to_texture( def texture_to_actor( path_to_texture : str, texture_name : str, - target_actor : actor.Actor, + target_actor : Actor, blending_mode : str = "None", wrap_mode : str = "ClampToBorder", border_color : tuple = ( @@ -94,7 +94,7 @@ def texture_to_actor( Texture image path. texture_name : str Name of the texture to be passed to the actor. - target_actor : actor.Actor + target_actor : Actor Target actor to receive the texture. blending_mode : str Texture blending mode. The options are: @@ -146,7 +146,7 @@ def texture_to_actor( def colormap_to_texture( colormap : np.array, texture_name : str, - target_actor : actor.Actor, + target_actor : Actor, interpolate : bool = True): """Converts a colormap to a texture and pass it to an actor. Parameters @@ -155,7 +155,7 @@ def colormap_to_texture( RGBA color map array. The array can be two dimensional, although a three dimensional one is preferred. texture_name : str Name of the color map texture to be passed to the actor. - target_actor : actor.Actor + target_actor : Actor Target actor to receive the color map texture. interpolate : bool Color map texture interpolation.""" diff --git a/fury/shaders/__init__.py b/fury/shaders/__init__.py index 8a8f11764..37cb94110 100644 --- a/fury/shaders/__init__.py +++ b/fury/shaders/__init__.py @@ -8,6 +8,7 @@ replace_shader_in_actor, shader_apply_effects, shader_to_actor, + shader_custom_uniforms ) __all__ = [ diff --git a/fury/shaders/base.py b/fury/shaders/base.py index 6caec9f5c..6d1c825b1 100644 --- a/fury/shaders/base.py +++ b/fury/shaders/base.py @@ -412,3 +412,24 @@ def attribute_to_actor(actor, arr, attr_name, deep=True): mapper.MapDataArrayToVertexAttribute( attr_name, attr_name, DataObject.FIELD_ASSOCIATION_POINTS, -1 ) + +def shader_custom_uniforms(actor, shader_type): + """Eases the passing of uniform values to the shaders by returning ``actor.GetShaderProperty().GetVertexCustomUniforms()``, + that give access to the ``SetUniform`` methods. + Parameters + ---------- + actor : actor.Actor + Actor which the uniform values will be passed to. + shader_type : str + Shader type of the uniform values to be passed. It can be: + * "vertex" + * "fragment" + * "geometry" + """ + SHADER_FUNCTIONS = {"vertex" : actor.GetShaderProperty().GetVertexCustomUniforms(), + "fragment" : actor.GetShaderProperty().GetFragmentCustomUniforms(), + "geometry" : actor.GetShaderProperty().GetGeometryCustomUniforms()} + + + + return SHADER_FUNCTIONS[shader_type] diff --git a/fury/shaders/effects/color_mapping.glsl b/fury/shaders/effects/color_mapping.glsl new file mode 100644 index 000000000..ee7215bdb --- /dev/null +++ b/fury/shaders/effects/color_mapping.glsl @@ -0,0 +1,3 @@ +vec3 color_mapping(float intensity, sampler2D colormapTexture){ + return texture(colormapTexture, vec2(intensity,0)).rgb; +} \ No newline at end of file diff --git a/fury/shaders/utils/normal_distribution.glsl b/fury/shaders/utils/normal_distribution.glsl new file mode 100644 index 000000000..f725b1b4b --- /dev/null +++ b/fury/shaders/utils/normal_distribution.glsl @@ -0,0 +1,11 @@ +// This assumes the center of the normal distribution is the center of the screen +#define PI 3.1415926 +float kde(vec3 point, float sigma){ + return (1/(sigma*sqrt(2.0*PI)))*exp(-1.0*pow(length(point), 2.0)/(2.0*sigma*sigma) ); +} + + +// This requires a center to be passed +// float kde(vec3 point, vec3 center, float sigma){ +// return (1/(sigma*sqrt(2.0*PI)))*exp(-1.0*pow(length(center - point), 2.0)/(2.0*sigma*sigma) ); +// } \ No newline at end of file From a09e26230bb43a2ea5a6f3394f808650a540f8ca Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Thu, 20 Jul 2023 19:56:37 -0300 Subject: [PATCH 04/41] feat: Opacity inclusion --- fury/actors/effect_manager.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index 45babc981..18606a9a1 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -34,7 +34,7 @@ def __init__(self, manager : ShowManager): - def kde(self, center, points : np.ndarray, sigmas, scale = 1, colormap = "viridis", custom_colormap : np.array = None): + def kde(self, center, points : np.ndarray, sigmas, scale = 1, opacity = 1.0, colormap = "viridis", custom_colormap : np.array = None): if not isinstance(sigmas, np.ndarray): sigmas = np.array(sigmas) if sigmas.shape[0] != 1 and sigmas.shape[0] != points.shape[0]: @@ -79,7 +79,7 @@ def kde(self, center, points : np.ndarray, sigmas, scale = 1, colormap = "viridi if(intensity<=0.0){ discard; }else{ - fragOutput0 = vec4(color, 1.0); + fragOutput0 = vec4(color, opacity); } """ @@ -117,6 +117,7 @@ def kde(self, center, points : np.ndarray, sigmas, scale = 1, colormap = "viridi # Render to second billboard for color map post-processing. textured_billboard = billboard(center, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("opacity", opacity) # Disables the texture warnings textured_billboard.GetProperty().GlobalWarningDisplayOff() From bec5f0ee2be2c14fa80dfa27aca48cdeb2eff1c9 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Thu, 20 Jul 2023 21:08:13 -0300 Subject: [PATCH 05/41] fix: Fixed sigma scale and spelling issues --- fury/actors/effect_manager.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index 18606a9a1..777879c0a 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -20,7 +20,7 @@ class EffectManager(): Parameters ---------- manager : ShowManager - Target manager that wil render post processed actors.""" + Target manager that will render post processed actors.""" def __init__(self, manager : ShowManager): self.scene = Scene() pos, focal, vu = manager.scene.get_camera() @@ -48,7 +48,7 @@ def kde(self, center, points : np.ndarray, sigmas, scale = 1, opacity = 1.0, col kde_dec = import_fury_shader(os.path.join("utils", "normal_distribution.glsl")) kde_impl = """ - float current_kde = kde(normalizedVertexMCVSOutput/out_scale, out_sigma); + float current_kde = kde(normalizedVertexMCVSOutput*out_scale, out_sigma); color = vec3(current_kde); fragOutput0 = vec4(color, 1.0); """ @@ -86,10 +86,10 @@ def kde(self, center, points : np.ndarray, sigmas, scale = 1, opacity = 1.0, col fs_dec = compose_shader([varying_dec, kde_dec]) # Scales parameter will be defined by the empirical rule: - # 1*sima = 68.27% of data inside the curve - # 2*sigma = 95.45% of data inside the curve - # 3*sigma = 99.73% of data inside the curve - scales = 3.0*np.copy(sigmas) + # 2*1*sima = 68.27% of data inside the curve + # 2*2*sigma = 95.45% of data inside the curve + # 2*3*sigma = 99.73% of data inside the curve + scales = 2*3.0*np.copy(sigmas) bill = billboard( points, From b572b4bbcd21c2bf3ac59231e4733b6c4f426851 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Fri, 21 Jul 2023 15:13:31 -0300 Subject: [PATCH 06/41] fix: Usage of native colormap function and cleanups --- docs/experimental/viz_kde_class.py | 29 ++++---- fury/actors/effect_manager.py | 5 +- fury/postprocessing.py | 102 +++++++++++++---------------- 3 files changed, 62 insertions(+), 74 deletions(-) diff --git a/docs/experimental/viz_kde_class.py b/docs/experimental/viz_kde_class.py index 8c3134d88..8ad0f19bf 100644 --- a/docs/experimental/viz_kde_class.py +++ b/docs/experimental/viz_kde_class.py @@ -1,24 +1,24 @@ -from fury.actors.effect_manager import EffectManager -from fury.window import Scene, ShowManager import numpy as np +from fury.actors.effect_manager import EffectManager +from fury.window import Scene, ShowManager, record + def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int = 0): - """Converts an array to a given desired range. + """Convert an array to a given desired range. Parameters ---------- array : np.ndarray - Array to be normalized. + Array to be normalized. min : float - Bottom value of the interval of normalization. If no value is given, it is passed as 0.0. + Bottom value of the interval of normalization. If no value is given, it is passed as 0.0. max : float - Upper value of the interval of normalization. If no value is given, it is passed as 1.0. + Upper value of the interval of normalization. If no value is given, it is passed as 1.0. Returns ------- - array : np.array - Array converted to the given desired range. + Array converted to the given desired range. """ if np.max(array) != np.min(array): return ((array - np.min(array))/(np.max(array) - np.min(array)))*(max - min) + min @@ -27,7 +27,7 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int "Can't normalize an array which maximum and minimum value are the same.") -width, height = (1920, 1080) +width, height = (800, 800) scene = Scene() scene.set_camera(position=(-6, 5, -10), @@ -49,13 +49,16 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int points = np.random.rand(n_points, 3) points = normalize(points, -5, 5) sigmas = normalize(np.random.rand(n_points, 1), 0.1, 0.3) -print(sigmas.shape[0]) -print(points.shape[0]) effects = EffectManager(manager) -kde_actor = effects.kde(np.array([[0.0, 0.0, 0.0]]), points, sigmas, scale = 20.0, colormap = "inferno") +kde_actor = effects.kde(np.array([[0.0, 0.0, 0.0]]), points, sigmas, scale = 20.0, colormap = "viridis") manager.scene.add(kde_actor) -manager.start() \ No newline at end of file +interactive = True + +if interactive: + manager.start() + +record(scene, out_path = "kde_points.png", size = (800, 800)) \ No newline at end of file diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index 777879c0a..4998a098f 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -1,6 +1,7 @@ import os import numpy as np from fury.actor import billboard +from fury.colormap import create_colormap from fury.postprocessing import (colormap_to_texture, window_to_texture) from fury.shaders import (import_fury_shader, @@ -12,7 +13,6 @@ Scene, gl_disable_depth, gl_set_additive_blending) -from matplotlib import colormaps class EffectManager(): """Class that manages the application of post-processing effects on actors. @@ -123,8 +123,7 @@ def kde(self, center, points : np.ndarray, sigmas, scale = 1, opacity = 1.0, col textured_billboard.GetProperty().GlobalWarningDisplayOff() if custom_colormap == None: - cmap = colormaps[colormap] - cmap = np.array([cmap(i) for i in np.arange(0.0, 1.0, 1/256)]) + cmap = create_colormap(np.arange(0.0, 1.0, 1/256), colormap) else: cmap = custom_colormap diff --git a/fury/postprocessing.py b/fury/postprocessing.py index 7d88fe8ab..ed9e55eb2 100644 --- a/fury/postprocessing.py +++ b/fury/postprocessing.py @@ -1,11 +1,21 @@ import numpy as np -from fury.window import RenderWindow from fury.actor import Actor -from fury.lib import Texture, WindowToImageFilter from fury.io import load_image +from fury.lib import Texture, WindowToImageFilter from fury.utils import rgb_to_vtk +from fury.window import RenderWindow +WRAP_MODE_DIC = {"clamptoedge" : Texture.ClampToEdge, + "repeat" : Texture.Repeat, + "mirroredrepeat" : Texture.MirroredRepeat, + "clamptoborder" : Texture.ClampToBorder} + +BLENDING_MODE_DIC = {"none" : 0, "replace" : 1, + "modulate" : 2, "add" : 3, + "addsigned" : 4, "interpolate" : 5, + "subtract" : 6} + def window_to_texture( window : RenderWindow, texture_name : str, @@ -18,7 +28,7 @@ def window_to_texture( 0.0, 1.0), interpolate : bool = True): - """Captures a rendered window and pass it as a texture to the given actor. + """Capture a rendered window and pass it as a texture to the given actor. Parameters ---------- window : window.RenderWindow @@ -29,36 +39,24 @@ def window_to_texture( Target actor to receive the texture. blending_mode : str, optional Texture blending mode. The options are: - 1. None - 2. Replace - 3. Modulate - 4. Add - 5. AddSigned - 6. Interpolate - 7. Subtract + 1. None + 2. Replace + 3. Modulate + 4. Add + 5. AddSigned + 6. Interpolate + 7. Subtract wrap_mode : str, optional Texture wrapping mode. The options are: - 1. ClampToEdge - 2. Repeat - 3. MirroredRepeat - 4. ClampToBorder + 1. ClampToEdge + 2. Repeat + 3. MirroredRepeat + 4. ClampToBorder border_color : tuple (4, ), optional Texture RGBA border color. interpolate : bool, optional Texture interpolation.""" - wrap_mode_dic = {"ClampToEdge" : Texture.ClampToEdge, - "Repeat" : Texture.Repeat, - "MirroredRepeat" : Texture.MirroredRepeat, - "ClampToBorder" : Texture.ClampToBorder} - - blending_mode_dic = {"None" : 0, "Replace" : 1, - "Modulate" : 2, "Add" : 3, - "AddSigned" : 4, "Interpolate" : 5, - "Subtract" : 6} - - r, g, b, a = border_color - windowToImageFilter = WindowToImageFilter() windowToImageFilter.SetInput(window) @@ -66,11 +64,11 @@ def window_to_texture( texture = Texture() texture.SetInputConnection(windowToImageFilter.GetOutputPort()) - texture.SetBorderColor(r, g, b, a) - texture.SetWrap(wrap_mode_dic[wrap_mode]) + texture.SetBorderColor(*border_color) + texture.SetWrap(WRAP_MODE_DIC[wrap_mode.lower()]) texture.SetInterpolate(interpolate) texture.MipmapOn() - texture.SetBlendingMode(blending_mode_dic[blending_mode]) + texture.SetBlendingMode(BLENDING_MODE_DIC[blending_mode.lower()]) target_actor.GetProperty().SetTexture(texture_name, texture) @@ -87,7 +85,7 @@ def texture_to_actor( 0.0, 1.0), interpolate : bool = True): - """Passes an imported texture to an actor. + """Pass an imported texture to an actor. Parameters ---------- path_to_texture : str @@ -98,47 +96,35 @@ def texture_to_actor( Target actor to receive the texture. blending_mode : str Texture blending mode. The options are: - 1. None - 2. Replace - 3. Modulate - 4. Add - 5. AddSigned - 6. Interpolate - 7. Subtract + 1. None + 2. Replace + 3. Modulate + 4. Add + 5. AddSigned + 6. Interpolate + 7. Subtract wrap_mode : str Texture wrapping mode. The options are: - 1. ClampToEdge - 2. Repeat - 3. MirroredRepeat - 4. ClampToBorder + 1. ClampToEdge + 2. Repeat + 3. MirroredRepeat + 4. ClampToBorder border_color : tuple (4, ) Texture RGBA border color. interpolate : bool Texture interpolation.""" - - wrap_mode_dic = {"ClampToEdge" : Texture.ClampToEdge, - "Repeat" : Texture.Repeat, - "MirroredRepeat" : Texture.MirroredRepeat, - "ClampToBorder" : Texture.ClampToBorder} - - blending_mode_dic = {"None" : 0, "Replace" : 1, - "Modulate" : 2, "Add" : 3, - "AddSigned" : 4, "Interpolate" : 5, - "Subtract" : 6} - - r, g, b, a = border_color - + texture = Texture() colormapArray = load_image(path_to_texture) colormapData = rgb_to_vtk(colormapArray) texture.SetInputDataObject(colormapData) - texture.SetBorderColor(r, g, b, a) - texture.SetWrap(wrap_mode_dic[wrap_mode]) + texture.SetBorderColor(*border_color) + texture.SetWrap(WRAP_MODE_DIC[wrap_mode.lower()]) texture.SetInterpolate(interpolate) texture.MipmapOn() - texture.SetBlendingMode(blending_mode_dic[blending_mode]) + texture.SetBlendingMode(BLENDING_MODE_DIC[blending_mode.lower()]) target_actor.GetProperty().SetTexture(texture_name, texture) @@ -148,7 +134,7 @@ def colormap_to_texture( texture_name : str, target_actor : Actor, interpolate : bool = True): - """Converts a colormap to a texture and pass it to an actor. + """Convert a colormap to a texture and pass it to an actor. Parameters ---------- colormap : np.array (N, 4) or (1, N, 4) From 269c4de1ad06b9509f6ac6f8a98c9f8d4ea9a1af Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Fri, 21 Jul 2023 16:59:43 -0300 Subject: [PATCH 07/41] feat: Added remove_effect feature --- docs/experimental/viz_kde_class.py | 9 +++++++-- fury/actors/effect_manager.py | 24 +++++++++++++++++++++--- fury/window.py | 6 ++++-- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/docs/experimental/viz_kde_class.py b/docs/experimental/viz_kde_class.py index 8ad0f19bf..bd773d0a8 100644 --- a/docs/experimental/viz_kde_class.py +++ b/docs/experimental/viz_kde_class.py @@ -56,9 +56,14 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int manager.scene.add(kde_actor) -interactive = True +interactive = False if interactive: manager.start() -record(scene, out_path = "kde_points.png", size = (800, 800)) \ No newline at end of file +effects.remove_effect(kde_actor) + +# record(scene, out_path = "kde_points.png", size = (800, 800)) + +if interactive: + manager.start() \ No newline at end of file diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index 4998a098f..2fc0ee57e 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -31,6 +31,8 @@ def __init__(self, manager : ShowManager): size=manager.size) self.off_manager.window.SetOffScreenRendering(True) self.off_manager.initialize() + self._n_active_effects = 0 + self._active_effects = {} @@ -104,9 +106,9 @@ def kde(self, center, points : np.ndarray, sigmas, scale = 1, opacity = 1.0, col # Blending and uniforms setup window = self.off_manager.window + shader_apply_effects(window, bill, gl_disable_depth) shader_apply_effects(window, bill, gl_set_additive_blending) - attribute_to_actor(bill, np.repeat(sigmas, 4), "in_sigma") attribute_to_actor(bill, np.repeat(scales, 4), "in_scale") @@ -129,10 +131,12 @@ def kde(self, center, points : np.ndarray, sigmas, scale = 1, opacity = 1.0, col colormap_to_texture(cmap, "colormapTexture", textured_billboard) - def event_callback(obj, event): + def kde_callback(obj, event): pos, focal, vu = self.on_manager.scene.get_camera() self.off_manager.scene.set_camera(pos, focal, vu) self.off_manager.scene.Modified() + shader_apply_effects(window, bill, gl_disable_depth) + shader_apply_effects(window, bill, gl_set_additive_blending) self.off_manager.render() window_to_texture( @@ -141,13 +145,27 @@ def event_callback(obj, event): textured_billboard, blending_mode="Interpolate") + # Initialization window_to_texture( self.off_manager.window, "screenTexture", textured_billboard, blending_mode="Interpolate") - self.on_manager.add_iren_callback(event_callback, "RenderEvent") + callback_id = self.on_manager.add_iren_callback(kde_callback, "RenderEvent") + + self._active_effects[textured_billboard] = callback_id + self._n_active_effects += 1 return textured_billboard + + def remove_effect(self, effect_actor): + if self._n_active_effects > 0: + self.on_manager.scene.RemoveObserver(self._active_effects[effect_actor]) + self.on_manager.scene.RemoveActor(effect_actor) + self.off_manager.scene.RemoveActor(effect_actor) + self._active_effects.pop(effect_actor) + self._n_active_effects -= 1 + else: + raise IndexError("Manager has no active effects.") \ No newline at end of file diff --git a/fury/window.py b/fury/window.py index 649a3bf07..b583bb690 100644 --- a/fury/window.py +++ b/fury/window.py @@ -740,8 +740,9 @@ def play_events_from_file(self, filename): def add_window_callback(self, win_callback, event=Command.ModifiedEvent): """Add window callbacks.""" - self.window.AddObserver(event, win_callback) + window_id = self.window.AddObserver(event, win_callback) self.window.Render() + return window_id def add_timer_callback(self, repeat, duration, timer_callback): if not self.iren.GetInitialized(): @@ -758,7 +759,8 @@ def add_timer_callback(self, repeat, duration, timer_callback): def add_iren_callback(self, iren_callback, event='MouseMoveEvent'): if not self.iren.GetInitialized(): self.initialize() - self.iren.AddObserver(event, iren_callback) + iren_id = self.iren.AddObserver(event, iren_callback) + return iren_id def destroy_timer(self, timer_id): self.iren.DestroyTimer(timer_id) From fae89e18b6ed5692921efe31ca3b385191ea5361 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Sat, 22 Jul 2023 01:56:52 -0300 Subject: [PATCH 08/41] feat: Added gaussian and laplacian filters --- docs/experimental/viz_laplacian.py | 70 ++++++++++++ fury/actors/effect_manager.py | 168 +++++++++++++++++++++++++++++ 2 files changed, 238 insertions(+) create mode 100644 docs/experimental/viz_laplacian.py diff --git a/docs/experimental/viz_laplacian.py b/docs/experimental/viz_laplacian.py new file mode 100644 index 000000000..aab55216e --- /dev/null +++ b/docs/experimental/viz_laplacian.py @@ -0,0 +1,70 @@ +import numpy as np + +from fury.actor import cube +from fury.actors.effect_manager import EffectManager +from fury.window import Scene, ShowManager, record + +def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int = 0): + """Convert an array to a given desired range. + + Parameters + ---------- + array : np.ndarray + Array to be normalized. + min : float + Bottom value of the interval of normalization. If no value is given, it is passed as 0.0. + max : float + Upper value of the interval of normalization. If no value is given, it is passed as 1.0. + + Returns + ------- + array : np.array + Array converted to the given desired range. + """ + if np.max(array) != np.min(array): + return ((array - np.min(array))/(np.max(array) - np.min(array)))*(max - min) + min + else: + raise ValueError( + "Can't normalize an array which maximum and minimum value are the same.") + + +width, height = (800, 800) + +scene = Scene() +scene.set_camera(position=(-6, 5, -10), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + +manager = ShowManager( + scene, + "demo", + (width, + height)) + +manager.initialize() + +cube_actor = cube(np.array([[0.0, 0.0, 0.0]]), colors = (1.0, 0.5, 0.0)) + +effects = EffectManager(manager) + +lapl_actor = effects.laplacian(np.array([[0.0, 0.0, 0.0]]), cube_actor, 4.0, 1.0) + +lapl2 = effects.laplacian(np.array([[0.0, 0.0, 0.0]]), lapl_actor, 4.0, 1.0) + +# manager.scene.add(cu) +manager.scene.add(lapl2) + +interactive = True + +if interactive: + manager.start() + +effects.remove_effect(lapl_actor) +effects.remove_effect(lapl2) + +# record(scene, out_path = "kde_points.png", size = (800, 800)) + +if interactive: + manager.start() \ No newline at end of file diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index 2fc0ee57e..99e3c75f8 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -158,7 +158,174 @@ def kde_callback(obj, event): self._n_active_effects += 1 return textured_billboard + + def laplacian(self, center, actor, scale, opacity): + + + laplacian_operator = """ + const float laplacian_mat[3*3] = {0.0, 1.0, 0.0, + 1.0,-4.0, 1.0, + 0.0, 1.0, 0.0}; + + const float x_offsets[3*3] = {-1.0, 0.0, 1.0, + -1.0, 0.0, 1.0, + -1.0, 0.0, 1.0}; + + const float y_offsets[3*3] = {-1.0, -1.0, -1.0, + 0.0, 0.0, 0.0, + 1.0, 1.0, 1.0}; + """ + + lapl_dec = """ + float laplacian_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ + float value = 0.0; + for(int i = 0; i < 9; i++){ + vec3 col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])).rgb; + float bw = 0.2126*col.r + 0.7152*col.g + 0.0722*col.b; + value += laplacian_mat[i]*bw; + } + return value; + } + """ + + tex_impl = """ + // Turning screen coordinates to texture coordinates + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + color = vec3(laplacian_calculator(screenTexture, renorm_tex, res)); + + //color = vec3(1.0, 0.0, 0.0); + fragOutput0 = vec4(color, opacity); + """ + tex_dec = compose_shader([laplacian_operator, lapl_dec]) + + self.off_manager.scene.add(actor) + + self.off_manager.render() + + # Render to second billboard for color map post-processing. + textured_billboard = billboard(center, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) + shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("opacity", opacity) + + # Disables the texture warnings + textured_billboard.GetProperty().GlobalWarningDisplayOff() + + def laplacian_callback(obj, event): + actor.SetVisibility(True) + pos, focal, vu = self.on_manager.scene.get_camera() + self.off_manager.scene.set_camera(pos, focal, vu) + self.off_manager.scene.Modified() + self.off_manager.render() + + window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate") + + actor.SetVisibility(False) + actor.Modified() + + # Initialization + window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate") + + callback_id = self.on_manager.add_iren_callback(laplacian_callback, "RenderEvent") + + self._active_effects[textured_billboard] = callback_id + self._n_active_effects += 1 + + return textured_billboard + + def gaussian_blur(self, center, actor, scale, opacity): + + + gaussian_kernel = """ + const float gauss_kernel[3*3] = {1/16.0, 1/8, 1/16.0, + 1/8.0, 1/4.0, 1/8.0, + 1/16.0, 1/8.0, 1/16.0}; + + const float x_offsets[3*3] = {-1.0, 0.0, 1.0, + -1.0, 0.0, 1.0, + -1.0, 0.0, 1.0}; + + const float y_offsets[3*3] = {-1.0, -1.0, -1.0, + 0.0, 0.0, 0.0, + 1.0, 1.0, 1.0}; + """ + + gauss_dec = """ + float kernel_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ + float value = 0.0; + for(int i = 0; i < 9; i++){ + vec3 col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])).rgb; + float bw = 0.2126*col.r + 0.7152*col.g + 0.0722*col.b; + value += gauss_kernel[i]*bw; + } + return value; + } + """ + + tex_impl = """ + // Turning screen coordinates to texture coordinates + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + color = vec3(kernel_calculator(screenTexture, renorm_tex, res)); + + //color = vec3(1.0, 0.0, 0.0); + fragOutput0 = vec4(color, opacity); + """ + tex_dec = compose_shader([gaussian_kernel, gauss_dec]) + + self.off_manager.scene.add(actor) + + self.off_manager.render() + + # Render to second billboard for color map post-processing. + textured_billboard = billboard(center, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) + shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("opacity", opacity) + + # Disables the texture warnings + textured_billboard.GetProperty().GlobalWarningDisplayOff() + + def kernel_callback(obj, event): + actor.SetVisibility(True) + pos, focal, vu = self.on_manager.scene.get_camera() + self.off_manager.scene.set_camera(pos, focal, vu) + self.off_manager.scene.Modified() + self.off_manager.render() + + window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate") + + actor.SetVisibility(False) + actor.Modified() + + + # Initialization + window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate") + + callback_id = self.on_manager.add_iren_callback(kernel_callback, "RenderEvent") + + self._active_effects[textured_billboard] = callback_id + self._n_active_effects += 1 + + return textured_billboard + + def remove_effect(self, effect_actor): if self._n_active_effects > 0: self.on_manager.scene.RemoveObserver(self._active_effects[effect_actor]) @@ -168,4 +335,5 @@ def remove_effect(self, effect_actor): self._n_active_effects -= 1 else: raise IndexError("Manager has no active effects.") + \ No newline at end of file From 39f43b4bfc6dd41b36a840b9cb42ec1c178df757 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Mon, 24 Jul 2023 14:30:25 -0300 Subject: [PATCH 09/41] feat: Added grayscale effect and changed laplacian and gaussian calculations --- docs/experimental/viz_kde_class.py | 5 +- fury/actors/effect_manager.py | 74 +++++++++++++++++++++++++----- 2 files changed, 66 insertions(+), 13 deletions(-) diff --git a/docs/experimental/viz_kde_class.py b/docs/experimental/viz_kde_class.py index bd773d0a8..07cdfad10 100644 --- a/docs/experimental/viz_kde_class.py +++ b/docs/experimental/viz_kde_class.py @@ -52,7 +52,7 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int effects = EffectManager(manager) -kde_actor = effects.kde(np.array([[0.0, 0.0, 0.0]]), points, sigmas, scale = 20.0, colormap = "viridis") +kde_actor = effects.kde(np.array([[0.0, 0.0, 0.0]]), points, sigmas, scale = 20.0, colormap = "inferno") manager.scene.add(kde_actor) @@ -60,10 +60,11 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int if interactive: manager.start() + +record(scene, out_path = "kde_points.png", size = (800, 800)) effects.remove_effect(kde_actor) -# record(scene, out_path = "kde_points.png", size = (800, 800)) if interactive: manager.start() \ No newline at end of file diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index 99e3c75f8..9ad60090d 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -158,6 +158,62 @@ def kde_callback(obj, event): self._n_active_effects += 1 return textured_billboard + + def grayscale(self, center, actor, scale, opacity): + + + tex_impl = """ + // Turning screen coordinates to texture coordinates + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + vec3 col = texture(screenTexture, renorm_tex).rgb; + float bw = 0.2126*col.r + 0.7152*col.g + 0.0722*col.b; + color = vec3(bw); + + fragOutput0 = vec4(color, opacity); + """ + + self.off_manager.scene.add(actor) + + self.off_manager.render() + + # Render to second billboard for color map post-processing. + textured_billboard = billboard(center, scales=scale, fs_impl=tex_impl) + shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("opacity", opacity) + + # Disables the texture warnings + textured_billboard.GetProperty().GlobalWarningDisplayOff() + + def gray_callback(obj, event): + actor.SetVisibility(True) + pos, focal, vu = self.on_manager.scene.get_camera() + self.off_manager.scene.set_camera(pos, focal, vu) + self.off_manager.render() + + window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate") + + actor.SetVisibility(False) + actor.Modified() + + + # Initialization + window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate") + + callback_id = self.on_manager.add_iren_callback(gray_callback, "RenderEvent") + + self._active_effects[textured_billboard] = callback_id + self._n_active_effects += 1 + + return textured_billboard def laplacian(self, center, actor, scale, opacity): @@ -177,12 +233,11 @@ def laplacian(self, center, actor, scale, opacity): """ lapl_dec = """ - float laplacian_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ - float value = 0.0; + vec3 laplacian_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ + vec3 value = vec3(0.0); for(int i = 0; i < 9; i++){ vec3 col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])).rgb; - float bw = 0.2126*col.r + 0.7152*col.g + 0.0722*col.b; - value += laplacian_mat[i]*bw; + value += vec3(laplacian_mat[i])*col; } return value; } @@ -192,7 +247,7 @@ def laplacian(self, center, actor, scale, opacity): // Turning screen coordinates to texture coordinates vec2 res_factor = vec2(res.y/res.x, 1.0); vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - color = vec3(laplacian_calculator(screenTexture, renorm_tex, res)); + color = laplacian_calculator(screenTexture, renorm_tex, res); //color = vec3(1.0, 0.0, 0.0); fragOutput0 = vec4(color, opacity); @@ -215,7 +270,6 @@ def laplacian_callback(obj, event): actor.SetVisibility(True) pos, focal, vu = self.on_manager.scene.get_camera() self.off_manager.scene.set_camera(pos, focal, vu) - self.off_manager.scene.Modified() self.off_manager.render() window_to_texture( @@ -260,11 +314,10 @@ def gaussian_blur(self, center, actor, scale, opacity): """ gauss_dec = """ - float kernel_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ - float value = 0.0; + vec3 kernel_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ + vec3 value = vec3(0.0); for(int i = 0; i < 9; i++){ vec3 col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])).rgb; - float bw = 0.2126*col.r + 0.7152*col.g + 0.0722*col.b; value += gauss_kernel[i]*bw; } return value; @@ -275,7 +328,7 @@ def gaussian_blur(self, center, actor, scale, opacity): // Turning screen coordinates to texture coordinates vec2 res_factor = vec2(res.y/res.x, 1.0); vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - color = vec3(kernel_calculator(screenTexture, renorm_tex, res)); + color = kernel_calculator(screenTexture, renorm_tex, res); //color = vec3(1.0, 0.0, 0.0); fragOutput0 = vec4(color, opacity); @@ -298,7 +351,6 @@ def kernel_callback(obj, event): actor.SetVisibility(True) pos, focal, vu = self.on_manager.scene.get_camera() self.off_manager.scene.set_camera(pos, focal, vu) - self.off_manager.scene.Modified() self.off_manager.render() window_to_texture( From 13687bed9aadd9c3b46405215a85764f9b0c0040 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Mon, 24 Jul 2023 20:27:13 -0300 Subject: [PATCH 10/41] fix: Fixed gaussian blur issues --- docs/experimental/viz_laplacian.py | 18 ++++++++++++------ fury/actors/effect_manager.py | 26 ++++++++++++++++---------- fury/postprocessing.py | 1 + 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/docs/experimental/viz_laplacian.py b/docs/experimental/viz_laplacian.py index aab55216e..2e86bee29 100644 --- a/docs/experimental/viz_laplacian.py +++ b/docs/experimental/viz_laplacian.py @@ -2,7 +2,12 @@ from fury.actor import cube from fury.actors.effect_manager import EffectManager -from fury.window import Scene, ShowManager, record +from fury.shaders import shader_apply_effects, shader_custom_uniforms +from fury.window import (Scene, ShowManager, record, + gl_set_normal_blending, + gl_set_additive_blending, + gl_disable_depth, + gl_enable_blend) def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int = 0): """Convert an array to a given desired range. @@ -46,15 +51,16 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int manager.initialize() cube_actor = cube(np.array([[0.0, 0.0, 0.0]]), colors = (1.0, 0.5, 0.0)) +cube_actor_2 = cube(np.array([[-1.0, 0.0, 0.0]]), colors = (1.0, 1.0, 0.0)) effects = EffectManager(manager) -lapl_actor = effects.laplacian(np.array([[0.0, 0.0, 0.0]]), cube_actor, 4.0, 1.0) +lapl_actor = effects.gaussian_blur(np.array([[1.0, 0.0, 0.0]]), cube_actor, 4.0, 1.0) -lapl2 = effects.laplacian(np.array([[0.0, 0.0, 0.0]]), lapl_actor, 4.0, 1.0) +# lapl2 = effects.laplacian(np.array([[0.0, 0.0, 0.0]]), lapl_actor, 4.0, 1.0) -# manager.scene.add(cu) -manager.scene.add(lapl2) +manager.scene.add(lapl_actor) +manager.scene.add(cube_actor_2) interactive = True @@ -62,7 +68,7 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int manager.start() effects.remove_effect(lapl_actor) -effects.remove_effect(lapl2) +# effects.remove_effect(lapl2) # record(scene, out_path = "kde_points.png", size = (800, 800)) diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index 9ad60090d..c578da0fd 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -314,11 +314,12 @@ def gaussian_blur(self, center, actor, scale, opacity): """ gauss_dec = """ - vec3 kernel_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ - vec3 value = vec3(0.0); + vec4 kernel_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ + vec4 value = vec4(0.0); for(int i = 0; i < 9; i++){ - vec3 col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])).rgb; - value += gauss_kernel[i]*bw; + vec4 col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); + col.a = col.a*int(vec3(0.0) != col.rgb) + 0.0*int(vec3(0.0) == col.rgb); + value += gauss_kernel[i]*col; } return value; } @@ -328,10 +329,10 @@ def gaussian_blur(self, center, actor, scale, opacity): // Turning screen coordinates to texture coordinates vec2 res_factor = vec2(res.y/res.x, 1.0); vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - color = kernel_calculator(screenTexture, renorm_tex, res); + vec4 kernel_color = kernel_calculator(screenTexture, renorm_tex, res); + + fragOutput0 = vec4(kernel_color.rgb, u_opacity*kernel_color.a); - //color = vec3(1.0, 0.0, 0.0); - fragOutput0 = vec4(color, opacity); """ tex_dec = compose_shader([gaussian_kernel, gauss_dec]) @@ -342,7 +343,8 @@ def gaussian_blur(self, center, actor, scale, opacity): # Render to second billboard for color map post-processing. textured_billboard = billboard(center, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("opacity", opacity) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) + # Disables the texture warnings textured_billboard.GetProperty().GlobalWarningDisplayOff() @@ -357,7 +359,9 @@ def kernel_callback(obj, event): self.off_manager.window, "screenTexture", textured_billboard, - blending_mode="Interpolate") + blending_mode="Interpolate", + border_color=(0.0, 0.0, 0.0, 0.0)) + actor.SetVisibility(False) actor.Modified() @@ -368,7 +372,9 @@ def kernel_callback(obj, event): self.off_manager.window, "screenTexture", textured_billboard, - blending_mode="Interpolate") + blending_mode="Interpolate", + border_color=(0.0, 0.0, 0.0, 0.0)) + callback_id = self.on_manager.add_iren_callback(kernel_callback, "RenderEvent") diff --git a/fury/postprocessing.py b/fury/postprocessing.py index ed9e55eb2..0c9d5e5df 100644 --- a/fury/postprocessing.py +++ b/fury/postprocessing.py @@ -63,6 +63,7 @@ def window_to_texture( windowToImageFilter.Update() texture = Texture() + texture.SetMipmap(True) texture.SetInputConnection(windowToImageFilter.GetOutputPort()) texture.SetBorderColor(*border_color) texture.SetWrap(WRAP_MODE_DIC[wrap_mode.lower()]) From 6be3ddc7e0c39503715843dc68800d231feb2fd4 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Tue, 25 Jul 2023 18:12:51 -0300 Subject: [PATCH 11/41] fix!: Experimenting with multiple effects on the same render and some other details --- docs/experimental/viz_kde_class.py | 12 +-- docs/experimental/viz_laplacian.py | 27 ++--- fury/actors/effect_manager.py | 153 ++++++++++++++++++++--------- fury/postprocessing.py | 11 ++- 4 files changed, 126 insertions(+), 77 deletions(-) diff --git a/docs/experimental/viz_kde_class.py b/docs/experimental/viz_kde_class.py index 07cdfad10..37725597a 100644 --- a/docs/experimental/viz_kde_class.py +++ b/docs/experimental/viz_kde_class.py @@ -49,22 +49,20 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int points = np.random.rand(n_points, 3) points = normalize(points, -5, 5) sigmas = normalize(np.random.rand(n_points, 1), 0.1, 0.3) +offset = np.array([3.0, 0.0, 0.0]) +points = points + np.tile(offset, points.shape[0]).reshape(points.shape) effects = EffectManager(manager) -kde_actor = effects.kde(np.array([[0.0, 0.0, 0.0]]), points, sigmas, scale = 20.0, colormap = "inferno") +kde_actor = effects.kde(points, sigmas, scale = 20.0, colormap = "inferno") manager.scene.add(kde_actor) +# effects.remove_effect(kde_actor) -interactive = False +interactive = True if interactive: manager.start() record(scene, out_path = "kde_points.png", size = (800, 800)) -effects.remove_effect(kde_actor) - - -if interactive: - manager.start() \ No newline at end of file diff --git a/docs/experimental/viz_laplacian.py b/docs/experimental/viz_laplacian.py index 2e86bee29..29541d4a1 100644 --- a/docs/experimental/viz_laplacian.py +++ b/docs/experimental/viz_laplacian.py @@ -1,13 +1,9 @@ import numpy as np -from fury.actor import cube +from fury.actor import cube, sphere from fury.actors.effect_manager import EffectManager from fury.shaders import shader_apply_effects, shader_custom_uniforms -from fury.window import (Scene, ShowManager, record, - gl_set_normal_blending, - gl_set_additive_blending, - gl_disable_depth, - gl_enable_blend) +from fury.window import (Scene, ShowManager, record) def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int = 0): """Convert an array to a given desired range. @@ -50,27 +46,22 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int manager.initialize() -cube_actor = cube(np.array([[0.0, 0.0, 0.0]]), colors = (1.0, 0.5, 0.0)) -cube_actor_2 = cube(np.array([[-1.0, 0.0, 0.0]]), colors = (1.0, 1.0, 0.0)) +cube_actor = cube(np.array([[0.0, 0.0, -3.0]]), colors = (1.0, 0.5, 0.0)) +sphere_actor = sphere(np.array([[0.0, 0.0, 3.0]]), (0.0, 1.0, 1.0), radii = 2.0) effects = EffectManager(manager) -lapl_actor = effects.gaussian_blur(np.array([[1.0, 0.0, 0.0]]), cube_actor, 4.0, 1.0) - -# lapl2 = effects.laplacian(np.array([[0.0, 0.0, 0.0]]), lapl_actor, 4.0, 1.0) +lapl_actor = effects.gaussian_blur(cube_actor, 4.0, 1.0) +lapl_sphere = effects.grayscale(sphere_actor, 4.0, 1.0) +manager.scene.add(lapl_sphere) manager.scene.add(lapl_actor) -manager.scene.add(cube_actor_2) interactive = True if interactive: manager.start() -effects.remove_effect(lapl_actor) -# effects.remove_effect(lapl2) - -# record(scene, out_path = "kde_points.png", size = (800, 800)) +# effects.remove_effect(lapl_actor) -if interactive: - manager.start() \ No newline at end of file +# record(scene, out_path = "kde_points.png", size = (800, 800)) \ No newline at end of file diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index c578da0fd..fc305b5d0 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -23,9 +23,8 @@ class EffectManager(): Target manager that will render post processed actors.""" def __init__(self, manager : ShowManager): self.scene = Scene() - pos, focal, vu = manager.scene.get_camera() - self.scene.set_camera(pos, focal, vu) - self.scene.set_camera() + cam_params = manager.scene.get_camera() + self.scene.set_camera(*cam_params) self.on_manager = manager self.off_manager = ShowManager(self.scene, size=manager.size) @@ -36,7 +35,35 @@ def __init__(self, manager : ShowManager): - def kde(self, center, points : np.ndarray, sigmas, scale = 1, opacity = 1.0, colormap = "viridis", custom_colormap : np.array = None): + def kde(self, + points : np.ndarray, + sigmas, scale = 1.0, + opacity = 1.0, + colormap = "viridis", + custom_colormap : np.array = None): + """Actor that displays the Kernel Density Estimation of a given set of points. + + Parameters + ---------- + points : np.ndarray (N, 3) + Array of points to be displayed. + sigmas : np.ndarray (1, ) or (N, 1) + Array of sigmas to be used in the KDE calculations. Must be one or one for each point. + scale : float, optional + Scale of the actor. + opacity : float, optional + Opacity of the actor. + colormap : str, optional. + Colormap matplotlib name for the KDE rendering. Default is "viridis". + custom_colormap : np.ndarray (N, 4), optional + Custom colormap for the KDE rendering. Default is none which means no + custom colormap is desired. If passed, will overwrite matplotlib colormap + chosen in the previous parameter. + + Returns + ------- + textured_billboard : actor.Actor + KDE rendering actor.""" if not isinstance(sigmas, np.ndarray): sigmas = np.array(sigmas) if sigmas.shape[0] != 1 and sigmas.shape[0] != points.shape[0]: @@ -81,23 +108,24 @@ def kde(self, center, points : np.ndarray, sigmas, scale = 1, opacity = 1.0, col if(intensity<=0.0){ discard; }else{ - fragOutput0 = vec4(color, opacity); + fragOutput0 = vec4(color, u_opacity); } """ fs_dec = compose_shader([varying_dec, kde_dec]) # Scales parameter will be defined by the empirical rule: - # 2*1*sima = 68.27% of data inside the curve - # 2*2*sigma = 95.45% of data inside the curve - # 2*3*sigma = 99.73% of data inside the curve + # 1*sima radius = 68.27% of data inside the curve + # 2*sigma radius = 95.45% of data inside the curve + # 3*sigma radius = 99.73% of data inside the curve scales = 2*3.0*np.copy(sigmas) + center_of_mass = np.average(points, axis = 0) bill = billboard( points, (0.0, - 0.0, - 1.0), + 0.0, + 1.0), scales=scales, fs_dec=fs_dec, fs_impl=kde_impl, @@ -112,14 +140,15 @@ def kde(self, center, points : np.ndarray, sigmas, scale = 1, opacity = 1.0, col attribute_to_actor(bill, np.repeat(sigmas, 4), "in_sigma") attribute_to_actor(bill, np.repeat(scales, 4), "in_scale") + if self._n_active_effects > 0: + self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) self.off_manager.scene.add(bill) - self.off_manager.render() # Render to second billboard for color map post-processing. - textured_billboard = billboard(center, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) + textured_billboard = billboard(np.array([center_of_mass]), scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("opacity", opacity) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) # Disables the texture warnings textured_billboard.GetProperty().GlobalWarningDisplayOff() @@ -132,8 +161,8 @@ def kde(self, center, points : np.ndarray, sigmas, scale = 1, opacity = 1.0, col colormap_to_texture(cmap, "colormapTexture", textured_billboard) def kde_callback(obj, event): - pos, focal, vu = self.on_manager.scene.get_camera() - self.off_manager.scene.set_camera(pos, focal, vu) + cam_params = self.on_manager.scene.get_camera() + self.off_manager.scene.set_camera(*cam_params) self.off_manager.scene.Modified() shader_apply_effects(window, bill, gl_disable_depth) shader_apply_effects(window, bill, gl_set_additive_blending) @@ -143,14 +172,16 @@ def kde_callback(obj, event): self.off_manager.window, "screenTexture", textured_billboard, - blending_mode="Interpolate") + blending_mode="Interpolate", + d_type = "rgba") # Initialization window_to_texture( self.off_manager.window, "screenTexture", textured_billboard, - blending_mode="Interpolate") + blending_mode="Interpolate", + d_type = "rgba") callback_id = self.on_manager.add_iren_callback(kde_callback, "RenderEvent") @@ -159,28 +190,30 @@ def kde_callback(obj, event): return textured_billboard - def grayscale(self, center, actor, scale, opacity): + def grayscale(self, actor, scale, opacity): tex_impl = """ // Turning screen coordinates to texture coordinates vec2 res_factor = vec2(res.y/res.x, 1.0); vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - vec3 col = texture(screenTexture, renorm_tex).rgb; + vec4 col = texture(screenTexture, renorm_tex); + col.a = col.a*int(vec3(0.0) != col.rgb) + 0.0*int(vec3(0.0) == col.rgb); float bw = 0.2126*col.r + 0.7152*col.g + 0.0722*col.b; - color = vec3(bw); - fragOutput0 = vec4(color, opacity); + fragOutput0 = vec4(vec3(bw), u_opacity*col.a); """ - + actor_pos = np.array([actor.GetPosition()]) + + if self._n_active_effects > 0: + self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) self.off_manager.scene.add(actor) - self.off_manager.render() # Render to second billboard for color map post-processing. - textured_billboard = billboard(center, scales=scale, fs_impl=tex_impl) + textured_billboard = billboard(actor_pos, scales=scale, fs_impl=tex_impl) shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("opacity", opacity) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) # Disables the texture warnings textured_billboard.GetProperty().GlobalWarningDisplayOff() @@ -195,7 +228,8 @@ def gray_callback(obj, event): self.off_manager.window, "screenTexture", textured_billboard, - blending_mode="Interpolate") + blending_mode="Interpolate", + d_type = "rgba") actor.SetVisibility(False) actor.Modified() @@ -206,7 +240,8 @@ def gray_callback(obj, event): self.off_manager.window, "screenTexture", textured_billboard, - blending_mode="Interpolate") + blending_mode="Interpolate", + d_type = "rgba") callback_id = self.on_manager.add_iren_callback(gray_callback, "RenderEvent") @@ -215,7 +250,7 @@ def gray_callback(obj, event): return textured_billboard - def laplacian(self, center, actor, scale, opacity): + def laplacian(self, actor, scale, opacity): laplacian_operator = """ @@ -233,11 +268,13 @@ def laplacian(self, center, actor, scale, opacity): """ lapl_dec = """ - vec3 laplacian_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ - vec3 value = vec3(0.0); + vec4 laplacian_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ + vec4 value = vec4(0.0); + vec4 col = vec4(0.0); for(int i = 0; i < 9; i++){ - vec3 col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])).rgb; - value += vec3(laplacian_mat[i])*col; + col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); + col.a = col.a*int(vec3(0.0) != col.rgb) + 0.0*int(vec3(0.0) == col.rgb); + value += vec4(laplacian_mat[i])*col; } return value; } @@ -247,21 +284,23 @@ def laplacian(self, center, actor, scale, opacity): // Turning screen coordinates to texture coordinates vec2 res_factor = vec2(res.y/res.x, 1.0); vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - color = laplacian_calculator(screenTexture, renorm_tex, res); + vec4 lapl_color = laplacian_calculator(screenTexture, renorm_tex, res); - //color = vec3(1.0, 0.0, 0.0); - fragOutput0 = vec4(color, opacity); + fragOutput0 = vec4(lapl_color.rgb, u_opacity*lapl_color.a); """ tex_dec = compose_shader([laplacian_operator, lapl_dec]) + if self._n_active_effects > 0: + self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) self.off_manager.scene.add(actor) - self.off_manager.render() + actor_pos = np.array([actor.GetPosition()]) + # Render to second billboard for color map post-processing. - textured_billboard = billboard(center, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) + textured_billboard = billboard(actor_pos, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("opacity", opacity) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) # Disables the texture warnings textured_billboard.GetProperty().GlobalWarningDisplayOff() @@ -276,7 +315,8 @@ def laplacian_callback(obj, event): self.off_manager.window, "screenTexture", textured_billboard, - blending_mode="Interpolate") + blending_mode="Interpolate", + d_type = "rgba") actor.SetVisibility(False) actor.Modified() @@ -286,7 +326,8 @@ def laplacian_callback(obj, event): self.off_manager.window, "screenTexture", textured_billboard, - blending_mode="Interpolate") + blending_mode="Interpolate", + d_type = "rgba") callback_id = self.on_manager.add_iren_callback(laplacian_callback, "RenderEvent") @@ -296,7 +337,7 @@ def laplacian_callback(obj, event): return textured_billboard - def gaussian_blur(self, center, actor, scale, opacity): + def gaussian_blur(self, actor, scale, opacity): gaussian_kernel = """ @@ -316,8 +357,9 @@ def gaussian_blur(self, center, actor, scale, opacity): gauss_dec = """ vec4 kernel_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ vec4 value = vec4(0.0); + vec4 col = vec4(0.0); for(int i = 0; i < 9; i++){ - vec4 col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); + col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); col.a = col.a*int(vec3(0.0) != col.rgb) + 0.0*int(vec3(0.0) == col.rgb); value += gauss_kernel[i]*col; } @@ -331,17 +373,20 @@ def gaussian_blur(self, center, actor, scale, opacity): vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; vec4 kernel_color = kernel_calculator(screenTexture, renorm_tex, res); + fragOutput0 = vec4(kernel_color.rgb, u_opacity*kernel_color.a); - """ tex_dec = compose_shader([gaussian_kernel, gauss_dec]) + if self._n_active_effects > 0: + self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) self.off_manager.scene.add(actor) - self.off_manager.render() + actor_pos = np.array([actor.GetPosition()]) + # Render to second billboard for color map post-processing. - textured_billboard = billboard(center, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) + textured_billboard = billboard(actor_pos, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) @@ -360,8 +405,8 @@ def kernel_callback(obj, event): "screenTexture", textured_billboard, blending_mode="Interpolate", - border_color=(0.0, 0.0, 0.0, 0.0)) - + border_color=(0.0, 0.0, 0.0, 0.0), + d_type = "rgba") actor.SetVisibility(False) actor.Modified() @@ -373,7 +418,8 @@ def kernel_callback(obj, event): "screenTexture", textured_billboard, blending_mode="Interpolate", - border_color=(0.0, 0.0, 0.0, 0.0)) + border_color=(0.0, 0.0, 0.0, 0.0), + d_type = "rgba") callback_id = self.on_manager.add_iren_callback(kernel_callback, "RenderEvent") @@ -385,8 +431,17 @@ def kernel_callback(obj, event): def remove_effect(self, effect_actor): + """Remove an existing effect from the effects manager. + Beware that the effect and the actor will be removed from the rendering pipeline + and shall not work after this action. + + Parameters + ---------- + effect_actor : actor.Actor + Actor of effect to be removed. + """ if self._n_active_effects > 0: - self.on_manager.scene.RemoveObserver(self._active_effects[effect_actor]) + self.on_manager.iren.RemoveObserver(self._active_effects[effect_actor]) self.on_manager.scene.RemoveActor(effect_actor) self.off_manager.scene.RemoveActor(effect_actor) self._active_effects.pop(effect_actor) diff --git a/fury/postprocessing.py b/fury/postprocessing.py index 0c9d5e5df..2434a70c0 100644 --- a/fury/postprocessing.py +++ b/fury/postprocessing.py @@ -27,7 +27,8 @@ def window_to_texture( 0.0, 0.0, 1.0), - interpolate : bool = True): + interpolate : bool = True, + d_type : str = "rgb"): """Capture a rendered window and pass it as a texture to the given actor. Parameters ---------- @@ -55,11 +56,15 @@ def window_to_texture( border_color : tuple (4, ), optional Texture RGBA border color. interpolate : bool, optional - Texture interpolation.""" + Texture interpolation. + d_type : str, optional + Texture pixel type, "rgb" or "rgba". Default is "rgb" + """ windowToImageFilter = WindowToImageFilter() windowToImageFilter.SetInput(window) - + type_dic = {"rgb" : windowToImageFilter.SetInputBufferTypeToRGB, "rgba" : windowToImageFilter.SetInputBufferTypeToRGBA} + type_dic[d_type.lower()]() windowToImageFilter.Update() texture = Texture() From 6dedaec7477cb343804706b378ebf6890d3a1e1c Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Tue, 25 Jul 2023 18:51:44 -0300 Subject: [PATCH 12/41] fix: Fixed textured_billboard positioning issues --- docs/experimental/viz_laplacian.py | 2 +- fury/actors/effect_manager.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/experimental/viz_laplacian.py b/docs/experimental/viz_laplacian.py index 29541d4a1..27203f0ce 100644 --- a/docs/experimental/viz_laplacian.py +++ b/docs/experimental/viz_laplacian.py @@ -32,7 +32,7 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int width, height = (800, 800) scene = Scene() -scene.set_camera(position=(-6, 5, -10), +scene.set_camera(position=(0, 0, -10), focal_point=(0.0, 0.0, 0.0), diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index fc305b5d0..ae137e3d1 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -203,7 +203,7 @@ def grayscale(self, actor, scale, opacity): fragOutput0 = vec4(vec3(bw), u_opacity*col.a); """ - actor_pos = np.array([actor.GetPosition()]) + actor_pos = np.array([actor.GetCenter()]) if self._n_active_effects > 0: self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) @@ -295,7 +295,7 @@ def laplacian(self, actor, scale, opacity): self.off_manager.scene.add(actor) self.off_manager.render() - actor_pos = np.array([actor.GetPosition()]) + actor_pos = np.array([actor.GetCenter()]) # Render to second billboard for color map post-processing. textured_billboard = billboard(actor_pos, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) @@ -383,7 +383,7 @@ def gaussian_blur(self, actor, scale, opacity): self.off_manager.scene.add(actor) self.off_manager.render() - actor_pos = np.array([actor.GetPosition()]) + actor_pos = np.array([actor.GetCenter()]) # Render to second billboard for color map post-processing. textured_billboard = billboard(actor_pos, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) From bf1503cc8c151fb7c48aed378a9431b17f0c828e Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Thu, 27 Jul 2023 11:26:45 -0300 Subject: [PATCH 13/41] fix: Fixed bugs and cleaned EffectManager class --- docs/experimental/viz_kde_class.py | 2 +- docs/experimental/viz_laplacian.py | 20 +++++------ fury/actors/effect_manager.py | 58 +++++++++++++++++++++++------- 3 files changed, 56 insertions(+), 24 deletions(-) diff --git a/docs/experimental/viz_kde_class.py b/docs/experimental/viz_kde_class.py index 37725597a..c1032ab56 100644 --- a/docs/experimental/viz_kde_class.py +++ b/docs/experimental/viz_kde_class.py @@ -54,7 +54,7 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int effects = EffectManager(manager) -kde_actor = effects.kde(points, sigmas, scale = 20.0, colormap = "inferno") +kde_actor = effects.kde(points, sigmas, colormap = "inferno") manager.scene.add(kde_actor) # effects.remove_effect(kde_actor) diff --git a/docs/experimental/viz_laplacian.py b/docs/experimental/viz_laplacian.py index 27203f0ce..f63d5893d 100644 --- a/docs/experimental/viz_laplacian.py +++ b/docs/experimental/viz_laplacian.py @@ -46,22 +46,22 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int manager.initialize() -cube_actor = cube(np.array([[0.0, 0.0, -3.0]]), colors = (1.0, 0.5, 0.0)) -sphere_actor = sphere(np.array([[0.0, 0.0, 3.0]]), (0.0, 1.0, 1.0), radii = 2.0) +offset = 1.0 +cube_actor = cube(np.array([[0.0, 0.0, -1.0 + offset]]), colors = (1.0, 0.5, 0.0)) +sphere_actor = sphere(np.array([[0.0, 0.0, 1.0 + offset], [1.0, 0.5, 1.0 + offset]]), (0.0, 1.0, 1.0), radii = 0.5) effects = EffectManager(manager) +gauss_cube = effects.gaussian_blur(cube_actor, 1.0) +gray_sphere = effects.grayscale(sphere_actor, 1.0) -lapl_actor = effects.gaussian_blur(cube_actor, 4.0, 1.0) -lapl_sphere = effects.grayscale(sphere_actor, 4.0, 1.0) +manager.scene.add(gray_sphere) +manager.scene.add(gauss_cube) -manager.scene.add(lapl_sphere) -manager.scene.add(lapl_actor) +# effects.remove_effect(gauss_cube) -interactive = True +interactive = False if interactive: manager.start() -# effects.remove_effect(lapl_actor) - -# record(scene, out_path = "kde_points.png", size = (800, 800)) \ No newline at end of file +record(scene, out_path = "post_process.png", size = (800, 800)) \ No newline at end of file diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index ae137e3d1..5d781bbaf 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -37,7 +37,7 @@ def __init__(self, manager : ShowManager): def kde(self, points : np.ndarray, - sigmas, scale = 1.0, + sigmas, opacity = 1.0, colormap = "viridis", custom_colormap : np.array = None): @@ -49,8 +49,6 @@ def kde(self, Array of points to be displayed. sigmas : np.ndarray (1, ) or (N, 1) Array of sigmas to be used in the KDE calculations. Must be one or one for each point. - scale : float, optional - Scale of the actor. opacity : float, optional Opacity of the actor. colormap : str, optional. @@ -103,7 +101,7 @@ def kde(self, vec2 res_factor = vec2(res.y/res.x, 1.0); vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; float intensity = texture(screenTexture, renorm_tex).r; - color = color_mapping(intensity, colormapTexture).rgb; + color = color_mapping(intensity, colormapTexture); if(intensity<=0.0){ discard; @@ -145,6 +143,14 @@ def kde(self, self.off_manager.scene.add(bill) self.off_manager.render() + bill_bounds = bill.GetBounds() + max_sigma = 2*4.0*np.max(sigmas) + + scale = np.array([[bill_bounds[1] - bill_bounds[0] + center_of_mass[0] + max_sigma, + bill_bounds[3] - bill_bounds[2] + center_of_mass[1] + max_sigma, + 0.0]]) + + # Render to second billboard for color map post-processing. textured_billboard = billboard(np.array([center_of_mass]), scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) @@ -190,30 +196,41 @@ def kde_callback(obj, event): return textured_billboard - def grayscale(self, actor, scale, opacity): + def grayscale(self, actor, opacity): tex_impl = """ // Turning screen coordinates to texture coordinates vec2 res_factor = vec2(res.y/res.x, 1.0); - vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + vec2 scale_factor = vec2(u_scale); + vec2 renorm_tex = scale_factor*res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; vec4 col = texture(screenTexture, renorm_tex); - col.a = col.a*int(vec3(0.0) != col.rgb) + 0.0*int(vec3(0.0) == col.rgb); float bw = 0.2126*col.r + 0.7152*col.g + 0.0722*col.b; fragOutput0 = vec4(vec3(bw), u_opacity*col.a); """ - actor_pos = np.array([actor.GetCenter()]) if self._n_active_effects > 0: self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) self.off_manager.scene.add(actor) self.off_manager.render() + actor_pos = np.array([actor.GetCenter()]) + actor_bounds = actor.GetBounds() + + actor_scales = np.array([actor_bounds[1] - actor_bounds[0], + actor_bounds[3] - actor_bounds[2], + 0.0]) + + scale = np.array([[actor_scales.max(), + actor_scales.max(), + 0.0]]) + # Render to second billboard for color map post-processing. textured_billboard = billboard(actor_pos, scales=scale, fs_impl=tex_impl) shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) + shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("u_scale", scale[0, :2]) # Disables the texture warnings textured_billboard.GetProperty().GlobalWarningDisplayOff() @@ -250,7 +267,7 @@ def gray_callback(obj, event): return textured_billboard - def laplacian(self, actor, scale, opacity): + def laplacian(self, actor, opacity): laplacian_operator = """ @@ -273,7 +290,6 @@ def laplacian(self, actor, scale, opacity): vec4 col = vec4(0.0); for(int i = 0; i < 9; i++){ col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); - col.a = col.a*int(vec3(0.0) != col.rgb) + 0.0*int(vec3(0.0) == col.rgb); value += vec4(laplacian_mat[i])*col; } return value; @@ -296,7 +312,16 @@ def laplacian(self, actor, scale, opacity): self.off_manager.render() actor_pos = np.array([actor.GetCenter()]) + actor_bounds = actor.GetBounds() + actor_scales = np.array([actor_bounds[1] - actor_bounds[0], + actor_bounds[3] - actor_bounds[2], + 0.0]) + + scale = np.array([[actor_scales.max(), + actor_scales.max(), + 0.0]]) + # Render to second billboard for color map post-processing. textured_billboard = billboard(actor_pos, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) @@ -337,7 +362,7 @@ def laplacian_callback(obj, event): return textured_billboard - def gaussian_blur(self, actor, scale, opacity): + def gaussian_blur(self, actor, opacity): gaussian_kernel = """ @@ -360,7 +385,6 @@ def gaussian_blur(self, actor, scale, opacity): vec4 col = vec4(0.0); for(int i = 0; i < 9; i++){ col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); - col.a = col.a*int(vec3(0.0) != col.rgb) + 0.0*int(vec3(0.0) == col.rgb); value += gauss_kernel[i]*col; } return value; @@ -373,7 +397,6 @@ def gaussian_blur(self, actor, scale, opacity): vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; vec4 kernel_color = kernel_calculator(screenTexture, renorm_tex, res); - fragOutput0 = vec4(kernel_color.rgb, u_opacity*kernel_color.a); """ tex_dec = compose_shader([gaussian_kernel, gauss_dec]) @@ -384,7 +407,16 @@ def gaussian_blur(self, actor, scale, opacity): self.off_manager.render() actor_pos = np.array([actor.GetCenter()]) + actor_bounds = actor.GetBounds() + actor_scales = np.array([actor_bounds[1] - actor_bounds[0], + actor_bounds[3] - actor_bounds[2], + 0.0]) + + scale = np.array([[actor_scales.max(), + actor_scales.max(), + 0.0]]) + # Render to second billboard for color map post-processing. textured_billboard = billboard(actor_pos, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) From fb024d8cfaed0ddf912d78e51704a633b1e5290f Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Thu, 27 Jul 2023 12:01:31 -0300 Subject: [PATCH 14/41] refactor!: Refactored and cleaned the whole PR --- docs/experimental/viz_kde_2d.py | 164 -------------- docs/experimental/viz_kde_3d_att.py | 340 ---------------------------- docs/experimental/viz_laplacian.py | 3 +- fury/actors/effect_manager.py | 185 ++++++++++++++- fury/postprocessing.py | 169 -------------- 5 files changed, 175 insertions(+), 686 deletions(-) delete mode 100644 docs/experimental/viz_kde_2d.py delete mode 100644 docs/experimental/viz_kde_3d_att.py delete mode 100644 fury/postprocessing.py diff --git a/docs/experimental/viz_kde_2d.py b/docs/experimental/viz_kde_2d.py deleted file mode 100644 index affb59000..000000000 --- a/docs/experimental/viz_kde_2d.py +++ /dev/null @@ -1,164 +0,0 @@ -import numpy as np -from fury import window, actor -from fury.shaders import compose_shader, shader_apply_effects, import_fury_shader, shader_custom_uniforms -from os.path import join -from fury.postprocessing import window_to_texture, colormap_to_texture -from matplotlib import colormaps - - -def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int = 0): - """Converts an array to a given desired range. - - Parameters - ---------- - array : np.ndarray - Array to be normalized. - min : float - Bottom value of the interval of normalization. If no value is given, it is passed as 0.0. - max : float - Upper value of the interval of normalization. If no value is given, it is passed as 1.0. - - Returns - ------- - - array : np.array - Array converted to the given desired range. - """ - if np.max(array) != np.min(array): - return ((array - np.min(array))/(np.max(array) - np.min(array)))*(max - min) + min - else: - raise ValueError( - "Can't normalize an array which maximum and minimum value are the same.") - - -kde_dec = import_fury_shader(join("utils", "normal_distribution.glsl")) - -kde_impl = """ -float current_kde = kde(normalizedVertexMCVSOutput, sigma); -color = vec3(current_kde); -fragOutput0 = vec4(color, 1.0); -""" - -tex_dec = import_fury_shader(join("effects", "color_mapping.glsl")) - -tex_impl = """ -// Turning screen coordinates to texture coordinates -vec2 renorm_tex = normalizedVertexMCVSOutput.xy*0.5 + 0.5; -float intensity = texture(screenTexture, renorm_tex).r; - -if(intensity<=0.0){ - discard; -}else{ - color = color_mapping(intensity, colormapTexture).rgb; - fragOutput0 = vec4(color, 1.0); -} -""" - - -fs_dec = compose_shader([kde_dec]) - -fs_impl = compose_shader([kde_impl]) - - -# Windows and scenes setup -width, height = (1920, 1080) -offWidth, offHeight = (1080, 1080) - -offScene = window.Scene() -offScene.set_camera(position=(-6, 5, -10), - focal_point=(0.0, - 0.0, - 0.0), - view_up=(0.0, 0.0, 0.0)) - -off_manager = window.ShowManager( - offScene, - "demo", - (offWidth, - offHeight)) - -off_manager.window.SetOffScreenRendering(True) - -off_manager.initialize() - - -scene = window.Scene() -scene.set_camera(position=(-6, 5, -10), - focal_point=(0.0, - 0.0, - 0.0), - view_up=(0.0, 0.0, 0.0)) - -manager = window.ShowManager( - scene, - "demo", - (width, - height)) - - -manager.initialize() - -n_points = 1000 -points = np.random.rand(n_points, 3) -points = normalize(points, -5, 5) -sigma = 0.3 -scale = 0.5 - -billboard = actor.billboard( - points, - (0.0, - 0.0, - 1.0), - scales=scale, - fs_dec=fs_dec, - fs_impl=fs_impl) - -# Blending and uniforms setup -shader_apply_effects(off_manager.window, billboard, window.gl_disable_depth) -shader_apply_effects(off_manager.window, billboard, window.gl_set_additive_blending) -shader_custom_uniforms(billboard, "fragment").SetUniformf("sigma", sigma) - -off_manager.scene.add(billboard) - -off_manager.render() - -scale = np.array([width/height, 1.0, 0.0]) - -# Render to second billboard for color map post-processing. -textured_billboard = actor.billboard(np.array([[0.0, 0.0, 0.0]]), (1.0, 1.0, 1.0), - scales=10.0, fs_dec=tex_dec, fs_impl=tex_impl) - -# Disables the texture warnings -textured_billboard.GetProperty().GlobalWarningDisplayOff() - -cmap = colormaps["inferno"] -cmap = np.array([cmap(i) for i in np.arange(0.0, 1.0, 1/256)]) - -colormap_to_texture(cmap, "colormapTexture", textured_billboard) - - -def event_callback(obj, event): - pos, focal, vu = manager.scene.get_camera() - off_manager.scene.set_camera(pos, focal, vu) - off_manager.scene.Modified() - off_manager.render() - - window_to_texture( - off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate") - - - -window_to_texture( - off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate") - -manager.scene.add(textured_billboard) - -manager.add_iren_callback(event_callback, "RenderEvent") - -manager.start() diff --git a/docs/experimental/viz_kde_3d_att.py b/docs/experimental/viz_kde_3d_att.py deleted file mode 100644 index c0a2b9748..000000000 --- a/docs/experimental/viz_kde_3d_att.py +++ /dev/null @@ -1,340 +0,0 @@ -import numpy as np -from fury import window, actor -from fury.shaders import compose_shader, shader_apply_effects, attribute_to_actor -from fury.lib import Texture, WindowToImageFilter -from fury.io import load_image -from fury.utils import rgb_to_vtk -from matplotlib import colormaps - - -def window_to_texture( - window : window.RenderWindow, - texture_name : str, - target_actor : actor.Actor, - blending_mode : str = "None", - wrap_mode : str = "ClampToBorder", - border_color : tuple = ( - 0.0, - 0.0, - 0.0, - 1.0), - interpolate : bool = True): - """Captures a rendered window and pass it as a texture to the given actor. - - Parameters - ---------- - window : window.RenderWindow - Window to be captured. - texture_name : str - Name of the texture to be passed to the actor. - target_actor : actor.Actor - Target actor to receive the texture. - blending_mode : str, optional - Texture blending mode. The options are: - 1. None - 2. Replace - 3. Modulate - 4. Add - 5. AddSigned - 6. Interpolate - 7. Subtract - - wrap_mode : str, optional - Texture wrapping mode. The options are: - 1. ClampToEdge - 2. Repeat - 3. MirroredRepeat - 4. ClampToBorder - - border_color : tuple (4, ), optional - Texture RGBA border color. - interpolate : bool, optional - Texture interpolation.""" - - wrap_mode_dic = {"ClampToEdge" : Texture.ClampToEdge, - "Repeat" : Texture.Repeat, - "MirroredRepeat" : Texture.MirroredRepeat, - "ClampToBorder" : Texture.ClampToBorder} - - blending_mode_dic = {"None" : 0, "Replace" : 1, - "Modulate" : 2, "Add" : 3, - "AddSigned" : 4, "Interpolate" : 5, - "Subtract" : 6} - - r, g, b, a = border_color - - windowToImageFilter = WindowToImageFilter() - windowToImageFilter.SetInput(window) - - windowToImageFilter.Update() - - texture = Texture() - texture.SetInputConnection(windowToImageFilter.GetOutputPort()) - texture.SetBorderColor(r, g, b, a) - texture.SetWrap(wrap_mode_dic[wrap_mode]) - texture.SetInterpolate(interpolate) - texture.MipmapOn() - texture.SetBlendingMode(blending_mode_dic[blending_mode]) - - target_actor.GetProperty().SetTexture(texture_name, texture) - - -def texture_to_actor( - path_to_texture : str, - texture_name : str, - target_actor : actor.Actor, - blending_mode : str = "None", - wrap_mode : str = "ClampToBorder", - border_color : tuple = ( - 0.0, - 0.0, - 0.0, - 1.0), - interpolate : bool = True): - """Passes an imported texture to an actor. - - Parameters - ---------- - path_to_texture : str - Texture image path. - texture_name : str - Name of the texture to be passed to the actor. - target_actor : actor.Actor - Target actor to receive the texture. - blending_mode : str - Texture blending mode. The options are: - 1. None - 2. Replace - 3. Modulate - 4. Add - 5. AddSigned - 6. Interpolate - 7. Subtract - - wrap_mode : str - Texture wrapping mode. The options are: - 1. ClampToEdge - 2. Repeat - 3. MirroredRepeat - 4. ClampToBorder - - border_color : tuple (4, ) - Texture RGBA border color. - interpolate : bool - Texture interpolation.""" - - wrap_mode_dic = {"ClampToEdge" : Texture.ClampToEdge, - "Repeat" : Texture.Repeat, - "MirroredRepeat" : Texture.MirroredRepeat, - "ClampToBorder" : Texture.ClampToBorder} - - blending_mode_dic = {"None" : 0, "Replace" : 1, - "Modulate" : 2, "Add" : 3, - "AddSigned" : 4, "Interpolate" : 5, - "Subtract" : 6} - - r, g, b, a = border_color - - texture = Texture() - - colormapArray = load_image(path_to_texture) - colormapData = rgb_to_vtk(colormapArray) - - texture.SetInputDataObject(colormapData) - texture.SetBorderColor(r, g, b, a) - texture.SetWrap(wrap_mode_dic[wrap_mode]) - texture.SetInterpolate(interpolate) - texture.MipmapOn() - texture.SetBlendingMode(blending_mode_dic[blending_mode]) - - target_actor.GetProperty().SetTexture(texture_name, texture) - - -def colormap_to_texture( - colormap : np.array, - texture_name : str, - target_actor : actor.Actor, - interpolate : bool = True): - """Converts a colormap to a texture and pass it to an actor. - - Parameters - ---------- - colormap : np.array (N, 4) or (1, N, 4) - RGBA color map array. The array can be two dimensional, although a three dimensional one is preferred. - texture_name : str - Name of the color map texture to be passed to the actor. - target_actor : actor.Actor - Target actor to receive the color map texture. - interpolate : bool - Color map texture interpolation.""" - - if len(colormap.shape) == 2: - colormap = np.array([colormap]) - - texture = Texture() - - cmap = (255*colormap).astype(np.uint8) - cmap = rgb_to_vtk(cmap) - - texture.SetInputDataObject(cmap) - texture.SetWrap(Texture.ClampToEdge) - texture.SetInterpolate(interpolate) - texture.MipmapOn() - texture.SetBlendingMode(0) - - target_actor.GetProperty().SetTexture(texture_name, texture) - - -def shader_custom_uniforms(actor : actor.Actor, shader_type : str): - """Eases the passing of uniform values to the shaders by returning ``actor.GetShaderProperty().GetVertexCustomUniforms()``, - that give access to the ``SetUniform`` methods. - Parameters - ---------- - actor : actor.Actor - Actor which the uniform values will be passed to. - shader_type : str - Shader type of the uniform values to be passed. It can be: - * "vertex" - * "fragment" - * "geometry" - """ - if shader_type == "vertex": - return actor.GetShaderProperty().GetVertexCustomUniforms() - elif shader_type == "fragment": - return actor.GetShaderProperty().GetFragmentCustomUniforms() - elif shader_type == "geometry": - return actor.GetShaderProperty().GetGeometryCustomUniforms() - else: - raise ValueError("Shader type unknown.") - - -def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int = 0): - """Converts an array to a given desired range. - - Parameters - ---------- - array : np.ndarray - Array to be normalized. - min : float - Bottom value of the interval of normalization. If no value is given, it is passed as 0.0. - max : float - Upper value of the interval of normalization. If no value is given, it is passed as 1.0. - - Returns - ------- - - array : np.array - Array converted to the given desired range. - """ - if np.max(array) != np.min(array): - return ((array - np.min(array))/(np.max(array) - np.min(array)))*(max - min) + min - else: - raise ValueError( - "Can't normalize an array which maximum and minimum value are the same.") - - -kde_vs_dec = """ -in float in_sigma; -varying float out_sigma; - -in float in_scale; -varying float out_scale; -""" - - -kde_vs_impl = """ -out_sigma = in_sigma; -out_scale = in_scale; -""" - - -kde_dec = """ -varying float out_sigma; -varying float out_scale; -float kde(vec3 point, float sigma, float scale){ - return exp(-1.0*pow(length(point/scale), 2.0)/(2.0*sigma*sigma) ); -} - -vec3 color_mapping(float intensity, float normalizingFactor){ - float normalizedIntensity = intensity/normalizingFactor; - return texture(colormapTexture, vec2(normalizedIntensity,0)).rgb; -} -""" - -kde_impl = """ -float current_kde = kde(normalizedVertexMCVSOutput, out_sigma, out_scale); - -if(current_kde <= discard_value){ - discard; -}else{ - color = color_mapping(current_kde, 1.0).rgb;; - fragOutput0 = vec4(color, 1.0); -} -""" - - - -fs_dec = compose_shader([kde_dec]) - -fs_impl = compose_shader([kde_impl]) - - -# Windows and scenes setup -width, height = (1920, 1080) -offWidth, offHeight = (1080, 1080) - -offScene = window.Scene() - -off_manager = window.ShowManager( - offScene, - "demo", - (width, - height), - reset_camera=True, - order_transparent=True) - - -off_manager.initialize() - - - -n_points = 500 -points = np.random.rand(n_points, 3) -points = normalize(points, -5, 5) -scales = np.random.rand(n_points, 1) -sigmas = normalize(np.random.rand(n_points, 1), 0.3, 0.5) - -sigma = 0.25 -scale = 0.5 - -billboard = actor.billboard( - points, - (0.0, - 0.0, - 1.0), - scales=scales, - fs_dec=fs_dec, - fs_impl=fs_impl, - vs_dec=kde_vs_dec, - vs_impl=kde_vs_impl) - -# Blending and uniforms setup -shader_apply_effects(off_manager.window, billboard, window.gl_disable_depth) -shader_apply_effects(off_manager.window, billboard, window.gl_set_additive_blending) -shader_custom_uniforms(billboard, "fragment").SetUniformf("sigma", sigma) -shader_custom_uniforms(billboard, "fragment").SetUniformf("discard_value", 0.0001) -attribute_to_actor(billboard, np.repeat(scales, 4), "in_scale") -attribute_to_actor(billboard, np.repeat(sigmas, 4), "in_sigma") - -off_manager.scene.add(billboard) - - - -# Render to second billboard for color map post-processing. -cmap = colormaps["inferno"] -cmap = np.array([cmap(i) for i in np.arange(0.0, 1.0, 1/256)]) - - -colormap_to_texture(cmap, "colormapTexture", billboard) - -off_manager.start() diff --git a/docs/experimental/viz_laplacian.py b/docs/experimental/viz_laplacian.py index f63d5893d..df801b8e4 100644 --- a/docs/experimental/viz_laplacian.py +++ b/docs/experimental/viz_laplacian.py @@ -2,7 +2,6 @@ from fury.actor import cube, sphere from fury.actors.effect_manager import EffectManager -from fury.shaders import shader_apply_effects, shader_custom_uniforms from fury.window import (Scene, ShowManager, record) def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int = 0): @@ -59,7 +58,7 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int # effects.remove_effect(gauss_cube) -interactive = False +interactive = True if interactive: manager.start() diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index 5d781bbaf..211d6da42 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -1,18 +1,183 @@ import os import numpy as np -from fury.actor import billboard +from fury.actor import Actor, billboard from fury.colormap import create_colormap -from fury.postprocessing import (colormap_to_texture, - window_to_texture) -from fury.shaders import (import_fury_shader, +from fury.io import load_image +from fury.lib import Texture, WindowToImageFilter +from fury.shaders import (attribute_to_actor, compose_shader, - attribute_to_actor, + import_fury_shader, shader_apply_effects, shader_custom_uniforms) -from fury.window import (ShowManager, - Scene, - gl_disable_depth, - gl_set_additive_blending) +from fury.utils import rgb_to_vtk +from fury.window import (gl_disable_depth, + gl_set_additive_blending, + RenderWindow, + Scene, + ShowManager) + + +WRAP_MODE_DIC = {"clamptoedge" : Texture.ClampToEdge, + "repeat" : Texture.Repeat, + "mirroredrepeat" : Texture.MirroredRepeat, + "clamptoborder" : Texture.ClampToBorder} + +BLENDING_MODE_DIC = {"none" : 0, "replace" : 1, + "modulate" : 2, "add" : 3, + "addsigned" : 4, "interpolate" : 5, + "subtract" : 6} + +def window_to_texture( + window : RenderWindow, + texture_name : str, + target_actor : Actor, + blending_mode : str = "None", + wrap_mode : str = "ClampToBorder", + border_color : tuple = ( + 0.0, + 0.0, + 0.0, + 1.0), + interpolate : bool = True, + d_type : str = "rgb"): + """Capture a rendered window and pass it as a texture to the given actor. + Parameters + ---------- + window : window.RenderWindow + Window to be captured. + texture_name : str + Name of the texture to be passed to the actor. + target_actor : Actor + Target actor to receive the texture. + blending_mode : str, optional + Texture blending mode. The options are: + 1. None + 2. Replace + 3. Modulate + 4. Add + 5. AddSigned + 6. Interpolate + 7. Subtract + wrap_mode : str, optional + Texture wrapping mode. The options are: + 1. ClampToEdge + 2. Repeat + 3. MirroredRepeat + 4. ClampToBorder + border_color : tuple (4, ), optional + Texture RGBA border color. + interpolate : bool, optional + Texture interpolation. + d_type : str, optional + Texture pixel type, "rgb" or "rgba". Default is "rgb" + """ + + windowToImageFilter = WindowToImageFilter() + windowToImageFilter.SetInput(window) + type_dic = {"rgb" : windowToImageFilter.SetInputBufferTypeToRGB, "rgba" : windowToImageFilter.SetInputBufferTypeToRGBA} + type_dic[d_type.lower()]() + windowToImageFilter.Update() + + texture = Texture() + texture.SetMipmap(True) + texture.SetInputConnection(windowToImageFilter.GetOutputPort()) + texture.SetBorderColor(*border_color) + texture.SetWrap(WRAP_MODE_DIC[wrap_mode.lower()]) + texture.SetInterpolate(interpolate) + texture.MipmapOn() + texture.SetBlendingMode(BLENDING_MODE_DIC[blending_mode.lower()]) + + target_actor.GetProperty().SetTexture(texture_name, texture) + + +def texture_to_actor( + path_to_texture : str, + texture_name : str, + target_actor : Actor, + blending_mode : str = "None", + wrap_mode : str = "ClampToBorder", + border_color : tuple = ( + 0.0, + 0.0, + 0.0, + 1.0), + interpolate : bool = True): + """Pass an imported texture to an actor. + Parameters + ---------- + path_to_texture : str + Texture image path. + texture_name : str + Name of the texture to be passed to the actor. + target_actor : Actor + Target actor to receive the texture. + blending_mode : str + Texture blending mode. The options are: + 1. None + 2. Replace + 3. Modulate + 4. Add + 5. AddSigned + 6. Interpolate + 7. Subtract + wrap_mode : str + Texture wrapping mode. The options are: + 1. ClampToEdge + 2. Repeat + 3. MirroredRepeat + 4. ClampToBorder + border_color : tuple (4, ) + Texture RGBA border color. + interpolate : bool + Texture interpolation.""" + + texture = Texture() + + colormapArray = load_image(path_to_texture) + colormapData = rgb_to_vtk(colormapArray) + + texture.SetInputDataObject(colormapData) + texture.SetBorderColor(*border_color) + texture.SetWrap(WRAP_MODE_DIC[wrap_mode.lower()]) + texture.SetInterpolate(interpolate) + texture.MipmapOn() + texture.SetBlendingMode(BLENDING_MODE_DIC[blending_mode.lower()]) + + target_actor.GetProperty().SetTexture(texture_name, texture) + + +def colormap_to_texture( + colormap : np.array, + texture_name : str, + target_actor : Actor, + interpolate : bool = True): + """Convert a colormap to a texture and pass it to an actor. + Parameters + ---------- + colormap : np.array (N, 4) or (1, N, 4) + RGBA color map array. The array can be two dimensional, although a three dimensional one is preferred. + texture_name : str + Name of the color map texture to be passed to the actor. + target_actor : Actor + Target actor to receive the color map texture. + interpolate : bool + Color map texture interpolation.""" + + if len(colormap.shape) == 2: + colormap = np.array([colormap]) + + texture = Texture() + + cmap = (255*colormap).astype(np.uint8) + cmap = rgb_to_vtk(cmap) + + texture.SetInputDataObject(cmap) + texture.SetWrap(Texture.ClampToEdge) + texture.SetInterpolate(interpolate) + texture.MipmapOn() + texture.SetBlendingMode(0) + + target_actor.GetProperty().SetTexture(texture_name, texture) class EffectManager(): """Class that manages the application of post-processing effects on actors. @@ -33,8 +198,6 @@ def __init__(self, manager : ShowManager): self._n_active_effects = 0 self._active_effects = {} - - def kde(self, points : np.ndarray, sigmas, diff --git a/fury/postprocessing.py b/fury/postprocessing.py deleted file mode 100644 index 2434a70c0..000000000 --- a/fury/postprocessing.py +++ /dev/null @@ -1,169 +0,0 @@ -import numpy as np -from fury.actor import Actor -from fury.io import load_image -from fury.lib import Texture, WindowToImageFilter -from fury.utils import rgb_to_vtk -from fury.window import RenderWindow - - -WRAP_MODE_DIC = {"clamptoedge" : Texture.ClampToEdge, - "repeat" : Texture.Repeat, - "mirroredrepeat" : Texture.MirroredRepeat, - "clamptoborder" : Texture.ClampToBorder} - -BLENDING_MODE_DIC = {"none" : 0, "replace" : 1, - "modulate" : 2, "add" : 3, - "addsigned" : 4, "interpolate" : 5, - "subtract" : 6} - -def window_to_texture( - window : RenderWindow, - texture_name : str, - target_actor : Actor, - blending_mode : str = "None", - wrap_mode : str = "ClampToBorder", - border_color : tuple = ( - 0.0, - 0.0, - 0.0, - 1.0), - interpolate : bool = True, - d_type : str = "rgb"): - """Capture a rendered window and pass it as a texture to the given actor. - Parameters - ---------- - window : window.RenderWindow - Window to be captured. - texture_name : str - Name of the texture to be passed to the actor. - target_actor : Actor - Target actor to receive the texture. - blending_mode : str, optional - Texture blending mode. The options are: - 1. None - 2. Replace - 3. Modulate - 4. Add - 5. AddSigned - 6. Interpolate - 7. Subtract - wrap_mode : str, optional - Texture wrapping mode. The options are: - 1. ClampToEdge - 2. Repeat - 3. MirroredRepeat - 4. ClampToBorder - border_color : tuple (4, ), optional - Texture RGBA border color. - interpolate : bool, optional - Texture interpolation. - d_type : str, optional - Texture pixel type, "rgb" or "rgba". Default is "rgb" - """ - - windowToImageFilter = WindowToImageFilter() - windowToImageFilter.SetInput(window) - type_dic = {"rgb" : windowToImageFilter.SetInputBufferTypeToRGB, "rgba" : windowToImageFilter.SetInputBufferTypeToRGBA} - type_dic[d_type.lower()]() - windowToImageFilter.Update() - - texture = Texture() - texture.SetMipmap(True) - texture.SetInputConnection(windowToImageFilter.GetOutputPort()) - texture.SetBorderColor(*border_color) - texture.SetWrap(WRAP_MODE_DIC[wrap_mode.lower()]) - texture.SetInterpolate(interpolate) - texture.MipmapOn() - texture.SetBlendingMode(BLENDING_MODE_DIC[blending_mode.lower()]) - - target_actor.GetProperty().SetTexture(texture_name, texture) - - -def texture_to_actor( - path_to_texture : str, - texture_name : str, - target_actor : Actor, - blending_mode : str = "None", - wrap_mode : str = "ClampToBorder", - border_color : tuple = ( - 0.0, - 0.0, - 0.0, - 1.0), - interpolate : bool = True): - """Pass an imported texture to an actor. - Parameters - ---------- - path_to_texture : str - Texture image path. - texture_name : str - Name of the texture to be passed to the actor. - target_actor : Actor - Target actor to receive the texture. - blending_mode : str - Texture blending mode. The options are: - 1. None - 2. Replace - 3. Modulate - 4. Add - 5. AddSigned - 6. Interpolate - 7. Subtract - wrap_mode : str - Texture wrapping mode. The options are: - 1. ClampToEdge - 2. Repeat - 3. MirroredRepeat - 4. ClampToBorder - border_color : tuple (4, ) - Texture RGBA border color. - interpolate : bool - Texture interpolation.""" - - texture = Texture() - - colormapArray = load_image(path_to_texture) - colormapData = rgb_to_vtk(colormapArray) - - texture.SetInputDataObject(colormapData) - texture.SetBorderColor(*border_color) - texture.SetWrap(WRAP_MODE_DIC[wrap_mode.lower()]) - texture.SetInterpolate(interpolate) - texture.MipmapOn() - texture.SetBlendingMode(BLENDING_MODE_DIC[blending_mode.lower()]) - - target_actor.GetProperty().SetTexture(texture_name, texture) - - -def colormap_to_texture( - colormap : np.array, - texture_name : str, - target_actor : Actor, - interpolate : bool = True): - """Convert a colormap to a texture and pass it to an actor. - Parameters - ---------- - colormap : np.array (N, 4) or (1, N, 4) - RGBA color map array. The array can be two dimensional, although a three dimensional one is preferred. - texture_name : str - Name of the color map texture to be passed to the actor. - target_actor : Actor - Target actor to receive the color map texture. - interpolate : bool - Color map texture interpolation.""" - - if len(colormap.shape) == 2: - colormap = np.array([colormap]) - - texture = Texture() - - cmap = (255*colormap).astype(np.uint8) - cmap = rgb_to_vtk(cmap) - - texture.SetInputDataObject(cmap) - texture.SetWrap(Texture.ClampToEdge) - texture.SetInterpolate(interpolate) - texture.MipmapOn() - texture.SetBlendingMode(0) - - target_actor.GetProperty().SetTexture(texture_name, texture) \ No newline at end of file From 7ecefeb48977a05a9e1882ed494b89e8cf6c7954 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Thu, 27 Jul 2023 15:51:46 -0300 Subject: [PATCH 15/41] feat: Added 5 more kernels for the KDE rendering --- docs/experimental/viz_kde_class.py | 7 +++-- fury/actors/effect_manager.py | 31 +++++++++++++------ fury/shaders/effects/color_mapping.glsl | 4 +-- fury/shaders/utils/cosine_distribution.glsl | 12 +++++++ .../utils/epanechnikov_distribution.glsl | 11 +++++++ .../utils/exponential_distribution.glsl | 11 +++++++ ...bution.glsl => gaussian_distribution.glsl} | 3 +- fury/shaders/utils/linear_distribution.glsl | 11 +++++++ fury/shaders/utils/tophat_distribution.glsl | 11 +++++++ 9 files changed, 86 insertions(+), 15 deletions(-) create mode 100644 fury/shaders/utils/cosine_distribution.glsl create mode 100644 fury/shaders/utils/epanechnikov_distribution.glsl create mode 100644 fury/shaders/utils/exponential_distribution.glsl rename fury/shaders/utils/{normal_distribution.glsl => gaussian_distribution.glsl} (75%) create mode 100644 fury/shaders/utils/linear_distribution.glsl create mode 100644 fury/shaders/utils/tophat_distribution.glsl diff --git a/docs/experimental/viz_kde_class.py b/docs/experimental/viz_kde_class.py index c1032ab56..f3e418396 100644 --- a/docs/experimental/viz_kde_class.py +++ b/docs/experimental/viz_kde_class.py @@ -27,7 +27,7 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int "Can't normalize an array which maximum and minimum value are the same.") -width, height = (800, 800) +width, height = (800, 600) scene = Scene() scene.set_camera(position=(-6, 5, -10), @@ -49,12 +49,13 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int points = np.random.rand(n_points, 3) points = normalize(points, -5, 5) sigmas = normalize(np.random.rand(n_points, 1), 0.1, 0.3) -offset = np.array([3.0, 0.0, 0.0]) +offset = np.array([0.0, 0.0, 0.0]) points = points + np.tile(offset, points.shape[0]).reshape(points.shape) effects = EffectManager(manager) -kde_actor = effects.kde(points, sigmas, colormap = "inferno") +kde_actor = effects.kde(points, sigmas, kernel = "exponential", colormap = "inferno") + manager.scene.add(kde_actor) # effects.remove_effect(kde_actor) diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index 211d6da42..b7231fbb1 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -201,8 +201,9 @@ def __init__(self, manager : ShowManager): def kde(self, points : np.ndarray, sigmas, - opacity = 1.0, - colormap = "viridis", + kernel : str = "gaussian", + opacity : float = 1.0, + colormap : str = "viridis", custom_colormap : np.array = None): """Actor that displays the Kernel Density Estimation of a given set of points. @@ -212,6 +213,15 @@ def kde(self, Array of points to be displayed. sigmas : np.ndarray (1, ) or (N, 1) Array of sigmas to be used in the KDE calculations. Must be one or one for each point. + kernel : str, optional + Kernel to be used for the distribution calculation. The available options are: + * "cosine" + * "epanechnikov" + * "exponential" + * "gaussian" + * "linear" + * "tophat" + opacity : float, optional Opacity of the actor. colormap : str, optional. @@ -235,7 +245,7 @@ def kde(self, varying float out_scale; """ - kde_dec = import_fury_shader(os.path.join("utils", "normal_distribution.glsl")) + kde_dec = import_fury_shader(os.path.join("utils", f"{kernel.lower()}_distribution.glsl")) kde_impl = """ float current_kde = kde(normalizedVertexMCVSOutput*out_scale, out_sigma); @@ -264,12 +274,12 @@ def kde(self, vec2 res_factor = vec2(res.y/res.x, 1.0); vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; float intensity = texture(screenTexture, renorm_tex).r; - color = color_mapping(intensity, colormapTexture); if(intensity<=0.0){ discard; - }else{ - fragOutput0 = vec4(color, u_opacity); + }else{ + vec4 final_color = color_mapping(intensity, colormapTexture); + fragOutput0 = vec4(final_color.rgb, u_opacity*final_color.a); } """ @@ -309,10 +319,13 @@ def kde(self, bill_bounds = bill.GetBounds() max_sigma = 2*4.0*np.max(sigmas) - scale = np.array([[bill_bounds[1] - bill_bounds[0] + center_of_mass[0] + max_sigma, - bill_bounds[3] - bill_bounds[2] + center_of_mass[1] + max_sigma, - 0.0]]) + actor_scales = np.array([[bill_bounds[1] - bill_bounds[0] + center_of_mass[0] + max_sigma, + bill_bounds[3] - bill_bounds[2] + center_of_mass[1] + max_sigma, + 0.0]]) + scale = np.array([[actor_scales.max(), + actor_scales.max(), + 0.0]]) # Render to second billboard for color map post-processing. textured_billboard = billboard(np.array([center_of_mass]), scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) diff --git a/fury/shaders/effects/color_mapping.glsl b/fury/shaders/effects/color_mapping.glsl index ee7215bdb..be9334ec6 100644 --- a/fury/shaders/effects/color_mapping.glsl +++ b/fury/shaders/effects/color_mapping.glsl @@ -1,3 +1,3 @@ -vec3 color_mapping(float intensity, sampler2D colormapTexture){ - return texture(colormapTexture, vec2(intensity,0)).rgb; +vec4 color_mapping(float intensity, sampler2D colormapTexture){ + return texture(colormapTexture, vec2(intensity,0)); } \ No newline at end of file diff --git a/fury/shaders/utils/cosine_distribution.glsl b/fury/shaders/utils/cosine_distribution.glsl new file mode 100644 index 000000000..85c6b4558 --- /dev/null +++ b/fury/shaders/utils/cosine_distribution.glsl @@ -0,0 +1,12 @@ +// This assumes the center of the normal distribution is the center of the screen +#define PI 3.1415926 +float kde(vec3 point, float sigma){ + float norm = (PI/(4.0*sigma)); + return norm*cos(PI*length(point)/(2*sigma))*int(length(point) < sigma); +} + + +// This requires a center to be passed +// float kde(vec3 point, vec3 center, float sigma){ +// return cos(PI*length(center - point)/(2*sigma))*int(length(center - point) < sigma); +// } \ No newline at end of file diff --git a/fury/shaders/utils/epanechnikov_distribution.glsl b/fury/shaders/utils/epanechnikov_distribution.glsl new file mode 100644 index 000000000..17feb52ef --- /dev/null +++ b/fury/shaders/utils/epanechnikov_distribution.glsl @@ -0,0 +1,11 @@ +// This assumes the center of the normal distribution is the center of the screen +float kde(vec3 point, float sigma){ + float norm = (3.0/(4.0*sigma)); + return norm*(1.0 - (length(point)*length(point))/(sigma*sigma)); +} + + +// This requires a center to be passed +// float kde(vec3 point, vec3 center, float sigma){ +// return 1.0 - (length(center - point)*length(center - point))/(sigma*sigma); +// } \ No newline at end of file diff --git a/fury/shaders/utils/exponential_distribution.glsl b/fury/shaders/utils/exponential_distribution.glsl new file mode 100644 index 000000000..417305f69 --- /dev/null +++ b/fury/shaders/utils/exponential_distribution.glsl @@ -0,0 +1,11 @@ +// This assumes the center of the normal distribution is the center of the screen +#define E 2.7182818 +float kde(vec3 point, float sigma){ + return exp(-1.0*length(point)/sigma); +} + + +// This requires a center to be passed +// float kde(vec3 point, vec3 center, float sigma){ +// return exp(-1.0*length(center - point)/sigma); +// } \ No newline at end of file diff --git a/fury/shaders/utils/normal_distribution.glsl b/fury/shaders/utils/gaussian_distribution.glsl similarity index 75% rename from fury/shaders/utils/normal_distribution.glsl rename to fury/shaders/utils/gaussian_distribution.glsl index f725b1b4b..7d1087986 100644 --- a/fury/shaders/utils/normal_distribution.glsl +++ b/fury/shaders/utils/gaussian_distribution.glsl @@ -1,7 +1,8 @@ // This assumes the center of the normal distribution is the center of the screen #define PI 3.1415926 float kde(vec3 point, float sigma){ - return (1/(sigma*sqrt(2.0*PI)))*exp(-1.0*pow(length(point), 2.0)/(2.0*sigma*sigma) ); + float norm = (1/(sigma*sqrt(2.0*PI))); + return norm*exp(-1.0*pow(length(point), 2.0)/(2.0*sigma*sigma) ); } diff --git a/fury/shaders/utils/linear_distribution.glsl b/fury/shaders/utils/linear_distribution.glsl new file mode 100644 index 000000000..4d7bfa874 --- /dev/null +++ b/fury/shaders/utils/linear_distribution.glsl @@ -0,0 +1,11 @@ +// This assumes the center of the normal distribution is the center of the screen +float kde(vec3 point, float sigma){ + float norm = (1.0/sigma); + return norm*(1.0 - length(point)/sigma)*int(length(point) < sigma); +} + + +// This requires a center to be passed +// float kde(vec3 point, vec3 center, float sigma){ +// return (1.0 - length(center - point)/sigma)*int(length(center - point) < sigma); +// } \ No newline at end of file diff --git a/fury/shaders/utils/tophat_distribution.glsl b/fury/shaders/utils/tophat_distribution.glsl new file mode 100644 index 000000000..972c1f3db --- /dev/null +++ b/fury/shaders/utils/tophat_distribution.glsl @@ -0,0 +1,11 @@ +// This assumes the center of the normal distribution is the center of the screen +float kde(vec3 point, float sigma){ + float norm = (1.0/sigma*2.0); + return norm*int(length(point) < sigma); +} + + +// This requires a center to be passed +// float kde(vec3 point, vec3 center, float sigma){ +// return 1.0*int(length(center - point) < sigma); +// } \ No newline at end of file From 68e520e88b1d592d178a86e771e56e5b593b4edd Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Thu, 27 Jul 2023 17:17:24 -0300 Subject: [PATCH 16/41] fix: Added sigma restraining condition --- fury/actors/effect_manager.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index b7231fbb1..5acd69dd1 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -239,6 +239,8 @@ def kde(self, sigmas = np.array(sigmas) if sigmas.shape[0] != 1 and sigmas.shape[0] != points.shape[0]: raise IndexError("sigmas size must be one or points size.") + if np.min(sigmas) <= 0: + raise ValueError("sigmas can't have zero or negative values.") varying_dec = """ varying float out_sigma; From f0a3d6d70a5aa4cea82c48ad6842fca0028f55a5 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Sat, 29 Jul 2023 17:56:57 -0300 Subject: [PATCH 17/41] feat: Testing renormalization approaches to KDE --- docs/experimental/float_to_rgb.py | 28 + docs/experimental/viz_kde_class.py | 10 +- fury/actors/effect_manager_alt.py | 812 +++++++++++++++++++++++++++++ 3 files changed, 845 insertions(+), 5 deletions(-) create mode 100644 docs/experimental/float_to_rgb.py create mode 100644 fury/actors/effect_manager_alt.py diff --git a/docs/experimental/float_to_rgb.py b/docs/experimental/float_to_rgb.py new file mode 100644 index 000000000..ee779f6b3 --- /dev/null +++ b/docs/experimental/float_to_rgb.py @@ -0,0 +1,28 @@ +import numpy as np +i = 255 + 255*256 + 255*256**2 + 255*256**3 +i = 0.000000001 +j = 0.000000001 +f = int(i*256*256*256*256 - 1) +g = int(j*256*256*256*256 - 1) +print("f:", f) + +def converter(n): + f = int(n*256*256*256*256 - 1) + c = np.zeros(4, dtype = float) + c[0] = f % 256 + c[1] = float((f % 256**2 - c[0]) // 256) + c[2] = float((f % 256**3 - c[1] - c[0]) // 256**2) + c[3] = float((f % 256**4 - c[2] - c[1] - c[0]) // 256**3) + + return c/255 + +def de_converter(h): + return (255*(h[0] + h[1]*256 + h[2]*256**2 + h[3]*256**3) + 1.0)/(256*256*256*256) + +c = converter(i) +d = converter(j) +print(f, g) +print(i) +print(np.array(c)) +de = de_converter(c + d) +print(int(de*256*256*256*256 - 1)) \ No newline at end of file diff --git a/docs/experimental/viz_kde_class.py b/docs/experimental/viz_kde_class.py index f3e418396..2286b209a 100644 --- a/docs/experimental/viz_kde_class.py +++ b/docs/experimental/viz_kde_class.py @@ -1,6 +1,6 @@ import numpy as np -from fury.actors.effect_manager import EffectManager +from fury.actors.effect_manager_alt import EffectManager from fury.window import Scene, ShowManager, record def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int = 0): @@ -45,16 +45,16 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int manager.initialize() -n_points = 500 +n_points = 1000 points = np.random.rand(n_points, 3) -points = normalize(points, -5, 5) -sigmas = normalize(np.random.rand(n_points, 1), 0.1, 0.3) +points = normalize(points, -5.0, 5.0) +sigmas = normalize(np.random.rand(n_points, 1), 0.1, 0.5) offset = np.array([0.0, 0.0, 0.0]) points = points + np.tile(offset, points.shape[0]).reshape(points.shape) effects = EffectManager(manager) -kde_actor = effects.kde(points, sigmas, kernel = "exponential", colormap = "inferno") +kde_actor = effects.kde(points, sigmas, kernel = "gaussian", colormap = "inferno") manager.scene.add(kde_actor) diff --git a/fury/actors/effect_manager_alt.py b/fury/actors/effect_manager_alt.py new file mode 100644 index 000000000..1ee0f1f43 --- /dev/null +++ b/fury/actors/effect_manager_alt.py @@ -0,0 +1,812 @@ +import os +import numpy as np +from fury.actor import Actor, billboard +from fury.colormap import create_colormap +from fury.io import load_image +from fury.lib import Texture, WindowToImageFilter, numpy_support +from fury.shaders import (attribute_to_actor, + compose_shader, + import_fury_shader, + shader_apply_effects, + shader_custom_uniforms) +from fury.utils import rgb_to_vtk +from fury.window import (gl_disable_depth, + gl_set_additive_blending, + RenderWindow, + Scene, + ShowManager) + + +WRAP_MODE_DIC = {"clamptoedge" : Texture.ClampToEdge, + "repeat" : Texture.Repeat, + "mirroredrepeat" : Texture.MirroredRepeat, + "clamptoborder" : Texture.ClampToBorder} + +BLENDING_MODE_DIC = {"none" : 0, "replace" : 1, + "modulate" : 2, "add" : 3, + "addsigned" : 4, "interpolate" : 5, + "subtract" : 6} + + + +def window_to_texture( + window : RenderWindow, + texture_name : str, + target_actor : Actor, + blending_mode : str = "None", + wrap_mode : str = "ClampToBorder", + border_color : tuple = ( + 0.0, + 0.0, + 0.0, + 1.0), + interpolate : bool = True, + d_type : str = "rgb"): + """Capture a rendered window and pass it as a texture to the given actor. + Parameters + ---------- + window : window.RenderWindow + Window to be captured. + texture_name : str + Name of the texture to be passed to the actor. + target_actor : Actor + Target actor to receive the texture. + blending_mode : str, optional + Texture blending mode. The options are: + 1. None + 2. Replace + 3. Modulate + 4. Add + 5. AddSigned + 6. Interpolate + 7. Subtract + wrap_mode : str, optional + Texture wrapping mode. The options are: + 1. ClampToEdge + 2. Repeat + 3. MirroredRepeat + 4. ClampToBorder + border_color : tuple (4, ), optional + Texture RGBA border color. + interpolate : bool, optional + Texture interpolation. + d_type : str, optional + Texture pixel type, "rgb" or "rgba". Default is "rgb" + """ + + windowToImageFilter = WindowToImageFilter() + windowToImageFilter.SetInput(window) + type_dic = {"rgb" : windowToImageFilter.SetInputBufferTypeToRGB, "rgba" : windowToImageFilter.SetInputBufferTypeToRGBA} + type_dic[d_type.lower()]() + windowToImageFilter.Update() + + texture = Texture() + texture.SetMipmap(True) + texture.SetInputConnection(windowToImageFilter.GetOutputPort()) + texture.SetBorderColor(*border_color) + texture.SetWrap(WRAP_MODE_DIC[wrap_mode.lower()]) + texture.SetInterpolate(interpolate) + texture.MipmapOn() + texture.SetBlendingMode(BLENDING_MODE_DIC[blending_mode.lower()]) + + target_actor.GetProperty().SetTexture(texture_name, texture) + + img = numpy_support.vtk_to_numpy(texture.GetInput().GetPointData().GetScalars()) + + return img + + + +def texture_to_actor( + path_to_texture : str, + texture_name : str, + target_actor : Actor, + blending_mode : str = "None", + wrap_mode : str = "ClampToBorder", + border_color : tuple = ( + 0.0, + 0.0, + 0.0, + 1.0), + interpolate : bool = True): + """Pass an imported texture to an actor. + Parameters + ---------- + path_to_texture : str + Texture image path. + texture_name : str + Name of the texture to be passed to the actor. + target_actor : Actor + Target actor to receive the texture. + blending_mode : str + Texture blending mode. The options are: + 1. None + 2. Replace + 3. Modulate + 4. Add + 5. AddSigned + 6. Interpolate + 7. Subtract + wrap_mode : str + Texture wrapping mode. The options are: + 1. ClampToEdge + 2. Repeat + 3. MirroredRepeat + 4. ClampToBorder + border_color : tuple (4, ) + Texture RGBA border color. + interpolate : bool + Texture interpolation.""" + + texture = Texture() + + colormapArray = load_image(path_to_texture) + colormapData = rgb_to_vtk(colormapArray) + + texture.SetInputDataObject(colormapData) + texture.SetBorderColor(*border_color) + texture.SetWrap(WRAP_MODE_DIC[wrap_mode.lower()]) + texture.SetInterpolate(interpolate) + texture.MipmapOn() + texture.SetBlendingMode(BLENDING_MODE_DIC[blending_mode.lower()]) + + target_actor.GetProperty().SetTexture(texture_name, texture) + + +def colormap_to_texture( + colormap : np.array, + texture_name : str, + target_actor : Actor, + interpolate : bool = True): + """Convert a colormap to a texture and pass it to an actor. + Parameters + ---------- + colormap : np.array (N, 4) or (1, N, 4) + RGBA color map array. The array can be two dimensional, although a three dimensional one is preferred. + texture_name : str + Name of the color map texture to be passed to the actor. + target_actor : Actor + Target actor to receive the color map texture. + interpolate : bool + Color map texture interpolation.""" + + if len(colormap.shape) == 2: + colormap = np.array([colormap]) + + texture = Texture() + + cmap = (255*colormap).astype(np.uint8) + cmap = rgb_to_vtk(cmap) + + texture.SetInputDataObject(cmap) + texture.SetWrap(Texture.ClampToEdge) + texture.SetInterpolate(interpolate) + texture.MipmapOn() + texture.SetBlendingMode(0) + + target_actor.GetProperty().SetTexture(texture_name, texture) + +def back_converter(h : np.ndarray): + return ((h[:, 0] + h[:, 1]/255. + h[:, 2]/65025. + h[:, 3]/16581375.)/256.0).astype(np.float32) + +class EffectManager(): + """Class that manages the application of post-processing effects on actors. + + Parameters + ---------- + manager : ShowManager + Target manager that will render post processed actors.""" + def __init__(self, manager : ShowManager): + self.scene = Scene() + cam_params = manager.scene.get_camera() + self.scene.set_camera(*cam_params) + self.on_manager = manager + self.off_manager = ShowManager(self.scene, + size=manager.size) + self.off_manager.window.SetOffScreenRendering(True) + self.off_manager.initialize() + self._n_active_effects = 0 + self._active_effects = {} + + def kde(self, + points : np.ndarray, + sigmas, + kernel : str = "gaussian", + opacity : float = 1.0, + colormap : str = "viridis", + custom_colormap : np.array = None): + """Actor that displays the Kernel Density Estimation of a given set of points. + + Parameters + ---------- + points : np.ndarray (N, 3) + Array of points to be displayed. + sigmas : np.ndarray (1, ) or (N, 1) + Array of sigmas to be used in the KDE calculations. Must be one or one for each point. + kernel : str, optional + Kernel to be used for the distribution calculation. The available options are: + * "cosine" + * "epanechnikov" + * "exponential" + * "gaussian" + * "linear" + * "tophat" + + opacity : float, optional + Opacity of the actor. + colormap : str, optional. + Colormap matplotlib name for the KDE rendering. Default is "viridis". + custom_colormap : np.ndarray (N, 4), optional + Custom colormap for the KDE rendering. Default is none which means no + custom colormap is desired. If passed, will overwrite matplotlib colormap + chosen in the previous parameter. + + Returns + ------- + textured_billboard : actor.Actor + KDE rendering actor.""" + if not isinstance(sigmas, np.ndarray): + sigmas = np.array(sigmas) + if sigmas.shape[0] != 1 and sigmas.shape[0] != points.shape[0]: + raise IndexError("sigmas size must be one or points size.") + if np.min(sigmas) <= 0: + raise ValueError("sigmas can't have zero or negative values.") + + varying_dec = """ + varying float out_sigma; + varying float out_scale; + """ + + + # converter = """ + # vec4 float_to_rgba(float value){ + # float ival = floor( value*4294967295.0 ); + # float r = floor( mod(ival, 256.0) ); + # float g = floor( ( mod(ival, 65536.0) - r ) / 256.0 ); + # float b = floor( ( mod(ival, 16777216.0) - g - r ) / 65536.0 ); + # float a = floor( ( mod(ival, 4294967296.0) - b - g - r ) / 16777216.0 ); + + # vec4 rgba = vec4(r, g, b, a)/255.0; + # return rgba; + # } + # """ + + converter = """ + vec4 float_to_rgba(float value) { + vec4 bitEnc = vec4(1.,255.,65025.,16581375.); + vec4 enc = bitEnc * value; + enc = fract(enc); + enc -= enc.yzww * vec2(1./255., 0.).xxxy; + return enc; + } + """ + + kde_dec = import_fury_shader(os.path.join("utils", f"{kernel.lower()}_distribution.glsl")) + + kde_dec = compose_shader([kde_dec, converter]) + + kde_impl = """ + float current_kde = kde(normalizedVertexMCVSOutput*out_scale, out_sigma)/n_points; + // color = vec3(current_kde); + vec4 final_color = float_to_rgba(current_kde); + fragOutput0 = vec4(final_color); + + """ + + kde_vs_dec = """ + in float in_sigma; + varying float out_sigma; + + in float in_scale; + varying float out_scale; + """ + + + kde_vs_impl = """ + out_sigma = in_sigma; + out_scale = in_scale; + """ + + # de_converter = """ + # float rgba_to_float(vec4 value){ + # return (255.0* (value.r*1.0 + value.g*256.0 + value.b*65536.0 + value.a*16777216.0) ) / 4294967295.0; + # } + # """ + + # de_converter = """ + # float rgba_to_float(vec4 packedRGBA) { + # // Convert RGBA values from [0, 1] range to 8-bit integer range [0, 255] + # int r = int(packedRGBA.r * 255.0); + # int g = int(packedRGBA.g * 255.0); + # int b = int(packedRGBA.b * 255.0); + # int a = int(packedRGBA.a * 255.0); + + # // Combine the four 8-bit integers into a 32-bit integer + # int intValue = (r << 24) | (g << 16) | (b << 8) | a; + + # // Convert the 32-bit integer back to the original float value range [0, 1] + # float maxValue = 4294967295.0; // 2^16 - 1 + # return float(intValue) / maxValue; + # } + # """ + + de_converter = """ + float rgba_to_float(vec4 v) { + vec4 bitEnc = vec4(1.,255.,65025.,16581375.); + vec4 bitDec = 1./bitEnc; + return dot(v, bitDec); + } + """ + + gaussian_kernel = """ + const float gauss_kernel[81] = { + 0.000123, 0.000365, 0.000839, 0.001504, 0.002179, 0.002429, 0.002179, 0.001504, 0.000839, + 0.000365, 0.001093, 0.002503, 0.004494, 0.006515, 0.007273, 0.006515, 0.004494, 0.002503, + 0.000839, 0.002503, 0.005737, 0.010263, 0.014888, 0.016590, 0.014888, 0.010263, 0.005737, + 0.001504, 0.004494, 0.010263, 0.018428, 0.026753, 0.029880, 0.026753, 0.018428, 0.010263, + 0.002179, 0.006515, 0.014888, 0.026753, 0.038898, 0.043441, 0.038898, 0.026753, 0.014888, + 0.002429, 0.007273, 0.016590, 0.029880, 0.043441, 0.048489, 0.043441, 0.029880, 0.016590, + 0.002179, 0.006515, 0.014888, 0.026753, 0.038898, 0.043441, 0.038898, 0.026753, 0.014888, + 0.001504, 0.004494, 0.010263, 0.018428, 0.026753, 0.029880, 0.026753, 0.018428, 0.010263, + 0.000839, 0.002503, 0.005737, 0.010263, 0.014888, 0.016590, 0.014888, 0.010263, 0.005737}; + + const float x_offsets[81] = {-4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, + -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, + -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, + -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, + -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, + -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, + -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, + -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, + -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0}; + + const float y_offsets[81] = {-4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, + -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, + -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, + -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, + 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0}; + """ + + gauss_dec = """ + vec4 kernel_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ + vec4 value = vec4(0.0); + vec4 col = vec4(0.0); + for(int i = 0; i < 81; i++){ + col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); + value += gauss_kernel[i]*col; + } + return value; + } + """ + + map_func = """ + float map(float value, float o_min, float o_max, float new_min, float new_max) { + return new_min + (value - o_min) * (new_max - new_min) / (o_max - o_min); + } + """ + + tex_dec = import_fury_shader(os.path.join("effects", "color_mapping.glsl")) + + tex_dec = compose_shader([tex_dec, de_converter, map_func, gaussian_kernel, gauss_dec]) + + tex_impl = """ + // Turning screen coordinates to texture coordinates + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + //vec4 intensity = texture(screenTexture, renorm_tex); + vec4 intensity = kernel_calculator(screenTexture, renorm_tex, res); + //float intensity = texture(screenTexture, renorm_tex).r; + + //float fintensity = intensity.r; + float fintensity = rgba_to_float(intensity); + fintensity = map(fintensity, min_value, max_value, 0.0, 1.0); + + if(fintensity<=0.0){ + discard; + }else{ + vec4 final_color = color_mapping(fintensity, colormapTexture); + fragOutput0 = vec4(final_color.rgb, u_opacity*final_color.a); + } + """ + + fs_dec = compose_shader([varying_dec, kde_dec]) + + # Scales parameter will be defined by the empirical rule: + # 1*sima radius = 68.27% of data inside the curve + # 2*sigma radius = 95.45% of data inside the curve + # 3*sigma radius = 99.73% of data inside the curve + scales = 2*3.0*np.copy(sigmas) + + center_of_mass = np.average(points, axis = 0) + bill = billboard( + points, + (0.0, + 0.0, + 1.0), + scales=scales, + fs_dec=fs_dec, + fs_impl=kde_impl, + vs_dec=kde_vs_dec, + vs_impl=kde_vs_impl) + + # Blending and uniforms setup + window = self.off_manager.window + + shader_apply_effects(window, bill, gl_disable_depth) + shader_apply_effects(window, bill, gl_set_additive_blending) + attribute_to_actor(bill, np.repeat(sigmas, 4), "in_sigma") + attribute_to_actor(bill, np.repeat(scales, 4), "in_scale") + shader_custom_uniforms(bill, "fragment").SetUniformf("n_points", points.shape[0]) + + if self._n_active_effects > 0: + self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) + self.off_manager.scene.add(bill) + self.off_manager.render() + + bill_bounds = bill.GetBounds() + max_sigma = 2*4.0*np.max(sigmas) + + actor_scales = np.array([[bill_bounds[1] - bill_bounds[0] + center_of_mass[0] + max_sigma, + bill_bounds[3] - bill_bounds[2] + center_of_mass[1] + max_sigma, + 0.0]]) + + res = self.off_manager.size + + scale = actor_scales.max()*np.array([[res[0]/res[1], + 1.0, + 0.0]]) + + # Render to second billboard for color map post-processing. + textured_billboard = billboard(np.array([center_of_mass]), scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) + shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", res) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) + + + # Disables the texture warnings + textured_billboard.GetProperty().GlobalWarningDisplayOff() + + if custom_colormap == None: + cmap = create_colormap(np.arange(0.0, 1.0, (1/points.shape[0])), colormap) + else: + cmap = custom_colormap + + colormap_to_texture(cmap, "colormapTexture", textured_billboard) + + def kde_callback(obj, event): + cam_params = self.on_manager.scene.get_camera() + self.off_manager.scene.set_camera(*cam_params) + self.off_manager.scene.Modified() + shader_apply_effects(window, bill, gl_disable_depth) + shader_apply_effects(window, bill, gl_set_additive_blending) + self.off_manager.render() + + img = window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + border_color = (0.0, 0.0, 0.0, 0.0), + blending_mode="Interpolate", + d_type = "rgba") + + converted_img = back_converter(img) + + max_value = np.max(converted_img) + min_value = np.min(converted_img) + print(min_value, max_value) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("min_value", min_value) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("max_value", max_value) + + # Initialization + img = window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + border_color = (0.0, 0.0, 0.0, 0.0), + blending_mode="Interpolate", + d_type = "rgba") + + converted_img = back_converter(img) + + max_value = np.max(converted_img) + min_value = np.min(converted_img) + # print(min_value, max_value) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("min_value", min_value) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("max_value", max_value) + + callback_id = self.on_manager.add_iren_callback(kde_callback, "RenderEvent") + + self._active_effects[textured_billboard] = callback_id + self._n_active_effects += 1 + + return textured_billboard + + def grayscale(self, actor, opacity): + + + tex_impl = """ + // Turning screen coordinates to texture coordinates + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 scale_factor = vec2(u_scale); + vec2 renorm_tex = scale_factor*res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + vec4 col = texture(screenTexture, renorm_tex); + float bw = 0.2126*col.r + 0.7152*col.g + 0.0722*col.b; + + fragOutput0 = vec4(vec3(bw), u_opacity*col.a); + """ + + if self._n_active_effects > 0: + self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) + self.off_manager.scene.add(actor) + self.off_manager.render() + + actor_pos = np.array([actor.GetCenter()]) + actor_bounds = actor.GetBounds() + + actor_scales = np.array([actor_bounds[1] - actor_bounds[0], + actor_bounds[3] - actor_bounds[2], + 0.0]) + + scale = np.array([[actor_scales.max(), + actor_scales.max(), + 0.0]]) + + # Render to second billboard for color map post-processing. + textured_billboard = billboard(actor_pos, scales=scale, fs_impl=tex_impl) + shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.on_manager.size) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) + shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("u_scale", scale[0, :2]) + + # Disables the texture warnings + textured_billboard.GetProperty().GlobalWarningDisplayOff() + + def gray_callback(obj, event): + actor.SetVisibility(True) + pos, focal, vu = self.on_manager.scene.get_camera() + self.off_manager.scene.set_camera(pos, focal, vu) + self.off_manager.render() + + window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate", + d_type = "rgba") + + actor.SetVisibility(False) + actor.Modified() + + + # Initialization + window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate", + d_type = "rgba") + + callback_id = self.on_manager.add_iren_callback(gray_callback, "RenderEvent") + + self._active_effects[textured_billboard] = callback_id + self._n_active_effects += 1 + + return textured_billboard + + def laplacian(self, actor, opacity): + + + laplacian_operator = """ + const float laplacian_mat[3*3] = {0.0, 1.0, 0.0, + 1.0,-4.0, 1.0, + 0.0, 1.0, 0.0}; + + const float x_offsets[3*3] = {-1.0, 0.0, 1.0, + -1.0, 0.0, 1.0, + -1.0, 0.0, 1.0}; + + const float y_offsets[3*3] = {-1.0, -1.0, -1.0, + 0.0, 0.0, 0.0, + 1.0, 1.0, 1.0}; + """ + + lapl_dec = """ + vec4 laplacian_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ + vec4 value = vec4(0.0); + vec4 col = vec4(0.0); + for(int i = 0; i < 9; i++){ + col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); + value += vec4(laplacian_mat[i])*col; + } + return value; + } + """ + + tex_impl = """ + // Turning screen coordinates to texture coordinates + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + vec4 lapl_color = laplacian_calculator(screenTexture, renorm_tex, res); + + fragOutput0 = vec4(lapl_color.rgb, u_opacity*lapl_color.a); + """ + tex_dec = compose_shader([laplacian_operator, lapl_dec]) + + if self._n_active_effects > 0: + self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) + self.off_manager.scene.add(actor) + self.off_manager.render() + + actor_pos = np.array([actor.GetCenter()]) + actor_bounds = actor.GetBounds() + + actor_scales = np.array([actor_bounds[1] - actor_bounds[0], + actor_bounds[3] - actor_bounds[2], + 0.0]) + + scale = np.array([[actor_scales.max(), + actor_scales.max(), + 0.0]]) + + # Render to second billboard for color map post-processing. + textured_billboard = billboard(actor_pos, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) + shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) + + # Disables the texture warnings + textured_billboard.GetProperty().GlobalWarningDisplayOff() + + def laplacian_callback(obj, event): + actor.SetVisibility(True) + pos, focal, vu = self.on_manager.scene.get_camera() + self.off_manager.scene.set_camera(pos, focal, vu) + self.off_manager.render() + + window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate", + d_type = "rgba") + + actor.SetVisibility(False) + actor.Modified() + + # Initialization + window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate", + d_type = "rgba") + + callback_id = self.on_manager.add_iren_callback(laplacian_callback, "RenderEvent") + + self._active_effects[textured_billboard] = callback_id + self._n_active_effects += 1 + + return textured_billboard + + + def gaussian_blur(self, actor, opacity): + + + gaussian_kernel = """ + const float gauss_kernel[3*3] = {1/16.0, 1/8, 1/16.0, + 1/8.0, 1/4.0, 1/8.0, + 1/16.0, 1/8.0, 1/16.0}; + + const float x_offsets[3*3] = {-1.0, 0.0, 1.0, + -1.0, 0.0, 1.0, + -1.0, 0.0, 1.0}; + + const float y_offsets[3*3] = {-1.0, -1.0, -1.0, + 0.0, 0.0, 0.0, + 1.0, 1.0, 1.0}; + """ + + gauss_dec = """ + vec4 kernel_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ + vec4 value = vec4(0.0); + vec4 col = vec4(0.0); + for(int i = 0; i < 9; i++){ + col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); + value += gauss_kernel[i]*col; + } + return value; + } + """ + + tex_impl = """ + // Turning screen coordinates to texture coordinates + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + vec4 kernel_color = kernel_calculator(screenTexture, renorm_tex, res); + + fragOutput0 = vec4(kernel_color.rgb, u_opacity*kernel_color.a); + """ + tex_dec = compose_shader([gaussian_kernel, gauss_dec]) + + if self._n_active_effects > 0: + self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) + self.off_manager.scene.add(actor) + self.off_manager.render() + + actor_pos = np.array([actor.GetCenter()]) + actor_bounds = actor.GetBounds() + + actor_scales = np.array([actor_bounds[1] - actor_bounds[0], + actor_bounds[3] - actor_bounds[2], + 0.0]) + + scale = np.array([[actor_scales.max(), + actor_scales.max(), + 0.0]]) + + # Render to second billboard for color map post-processing. + textured_billboard = billboard(actor_pos, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) + shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) + + + # Disables the texture warnings + textured_billboard.GetProperty().GlobalWarningDisplayOff() + + def kernel_callback(obj, event): + actor.SetVisibility(True) + pos, focal, vu = self.on_manager.scene.get_camera() + self.off_manager.scene.set_camera(pos, focal, vu) + self.off_manager.render() + + window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate", + border_color=(0.0, 0.0, 0.0, 0.0), + d_type = "rgba") + + actor.SetVisibility(False) + actor.Modified() + + + # Initialization + window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate", + border_color=(0.0, 0.0, 0.0, 0.0), + d_type = "rgba") + + + callback_id = self.on_manager.add_iren_callback(kernel_callback, "RenderEvent") + + self._active_effects[textured_billboard] = callback_id + self._n_active_effects += 1 + + return textured_billboard + + + def remove_effect(self, effect_actor): + """Remove an existing effect from the effects manager. + Beware that the effect and the actor will be removed from the rendering pipeline + and shall not work after this action. + + Parameters + ---------- + effect_actor : actor.Actor + Actor of effect to be removed. + """ + if self._n_active_effects > 0: + self.on_manager.iren.RemoveObserver(self._active_effects[effect_actor]) + self.on_manager.scene.RemoveActor(effect_actor) + self.off_manager.scene.RemoveActor(effect_actor) + self._active_effects.pop(effect_actor) + self._n_active_effects -= 1 + else: + raise IndexError("Manager has no active effects.") + + \ No newline at end of file From f35626e6315f5a3562ebdf34744049a7ce680b1a Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Sun, 30 Jul 2023 17:20:27 -0300 Subject: [PATCH 18/41] fix!: Stabilizing approaches to renormalization --- docs/experimental/viz_billboards.py | 28 + docs/experimental/viz_kde_class.py | 6 +- fury/actors/effect_manager.py | 4 +- fury/actors/effect_manager_alt.py | 32 +- fury/actors/effect_manager_alt_2.py | 909 ++++++++++++++++++++++++++++ 5 files changed, 971 insertions(+), 8 deletions(-) create mode 100644 docs/experimental/viz_billboards.py create mode 100644 fury/actors/effect_manager_alt_2.py diff --git a/docs/experimental/viz_billboards.py b/docs/experimental/viz_billboards.py new file mode 100644 index 000000000..90c589f70 --- /dev/null +++ b/docs/experimental/viz_billboards.py @@ -0,0 +1,28 @@ +import numpy as np + +from fury.actor import billboard +from fury.window import Scene, ShowManager + +width, height = (1350, 800) + +scene = Scene() +scene.set_camera(position=(-6, 5, -10), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + +manager = ShowManager( + scene, + "demo", + (width, + height)) + +manager.initialize() + +scale = 3.4*np.array([[width/height, 1.0, 0.0]]) + +bill = billboard(np.array([[0.0, 0.0, 0.0]]), scales=scale,colors = (1.0, 0.0, 0.0)) +manager.scene.add(bill) + +manager.start() \ No newline at end of file diff --git a/docs/experimental/viz_kde_class.py b/docs/experimental/viz_kde_class.py index 2286b209a..764ea9262 100644 --- a/docs/experimental/viz_kde_class.py +++ b/docs/experimental/viz_kde_class.py @@ -47,14 +47,14 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int n_points = 1000 points = np.random.rand(n_points, 3) -points = normalize(points, -5.0, 5.0) -sigmas = normalize(np.random.rand(n_points, 1), 0.1, 0.5) +points = normalize(points, -5, 5) +sigmas = normalize(np.random.rand(n_points, 1), 0.2, 0.9) offset = np.array([0.0, 0.0, 0.0]) points = points + np.tile(offset, points.shape[0]).reshape(points.shape) effects = EffectManager(manager) -kde_actor = effects.kde(points, sigmas, kernel = "gaussian", colormap = "inferno") +kde_actor = effects.kde(points, sigmas, kernel = "linear", colormap = "inferno") manager.scene.add(kde_actor) diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index 5acd69dd1..072e66dc0 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -74,7 +74,9 @@ def window_to_texture( windowToImageFilter = WindowToImageFilter() windowToImageFilter.SetInput(window) - type_dic = {"rgb" : windowToImageFilter.SetInputBufferTypeToRGB, "rgba" : windowToImageFilter.SetInputBufferTypeToRGBA} + type_dic = {"rgb" : windowToImageFilter.SetInputBufferTypeToRGB, + "rgba" : windowToImageFilter.SetInputBufferTypeToRGBA, + "zbuffer" : windowToImageFilter.SetInputBufferTypeToZBuffer} type_dic[d_type.lower()]() windowToImageFilter.Update() diff --git a/fury/actors/effect_manager_alt.py b/fury/actors/effect_manager_alt.py index 1ee0f1f43..7c5dd433d 100644 --- a/fury/actors/effect_manager_alt.py +++ b/fury/actors/effect_manager_alt.py @@ -383,6 +383,23 @@ def kde(self, } """ + avg_filter = """ + vec4 avg_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ + float x_median_offsets[5] = {-1.0, 0.0, 1.0, + 0.0, 0.0}; + + const float y_median_offsets[5] = {0.0, -1.0, 0.0, + 1.0, 0.0}; + vec4 value = vec4(0.0); + vec4 col = vec4(0.0); + for(int i = 0; i < 5; i++){ + col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_median_offsets[i], y_median_offsets[i])); + value += col; + } + return value/5.0; + } + """ + map_func = """ float map(float value, float o_min, float o_max, float new_min, float new_max) { return new_min + (value - o_min) * (new_max - new_min) / (o_max - o_min); @@ -391,7 +408,7 @@ def kde(self, tex_dec = import_fury_shader(os.path.join("effects", "color_mapping.glsl")) - tex_dec = compose_shader([tex_dec, de_converter, map_func, gaussian_kernel, gauss_dec]) + tex_dec = compose_shader([tex_dec, de_converter, map_func, gaussian_kernel, gauss_dec, avg_filter]) tex_impl = """ // Turning screen coordinates to texture coordinates @@ -493,12 +510,19 @@ def kde_callback(obj, event): d_type = "rgba") converted_img = back_converter(img) + converted_img = converted_img[converted_img != 0.0] - max_value = np.max(converted_img) + avg = np.average(converted_img) min_value = np.min(converted_img) - print(min_value, max_value) + low_v = converted_img[converted_img <= avg].shape[0] + high_v = converted_img[converted_img > avg].shape[0] + max_value_2 = avg + (avg - min_value)*(high_v/low_v) + # print(min_value, max_value) + # max_value = np.max(converted_img) + # print(min_value, max_value, max_value_2) + # print(converted_img[converted_img <= max_value_2].shape[0], converted_img[converted_img > max_value_2].shape[0]) shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("min_value", min_value) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("max_value", max_value) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("max_value", max_value_2) # Initialization img = window_to_texture( diff --git a/fury/actors/effect_manager_alt_2.py b/fury/actors/effect_manager_alt_2.py new file mode 100644 index 000000000..85fb0a3bc --- /dev/null +++ b/fury/actors/effect_manager_alt_2.py @@ -0,0 +1,909 @@ +import os +import numpy as np +from fury.actor import Actor, billboard +from fury.colormap import create_colormap +from fury.io import load_image +from fury.lib import Texture, WindowToImageFilter, numpy_support +from fury.shaders import (attribute_to_actor, + compose_shader, + import_fury_shader, + shader_apply_effects, + shader_custom_uniforms) +from fury.utils import rgb_to_vtk +from fury.window import (gl_disable_depth, + gl_set_additive_blending, + RenderWindow, + Scene, + ShowManager) + + +WRAP_MODE_DIC = {"clamptoedge" : Texture.ClampToEdge, + "repeat" : Texture.Repeat, + "mirroredrepeat" : Texture.MirroredRepeat, + "clamptoborder" : Texture.ClampToBorder} + +BLENDING_MODE_DIC = {"none" : 0, "replace" : 1, + "modulate" : 2, "add" : 3, + "addsigned" : 4, "interpolate" : 5, + "subtract" : 6} + + + +def window_to_texture( + window : RenderWindow, + texture_name : str, + target_actor : Actor, + blending_mode : str = "None", + wrap_mode : str = "ClampToBorder", + border_color : tuple = ( + 0.0, + 0.0, + 0.0, + 1.0), + interpolate : bool = True, + d_type : str = "rgb"): + """Capture a rendered window and pass it as a texture to the given actor. + Parameters + ---------- + window : window.RenderWindow + Window to be captured. + texture_name : str + Name of the texture to be passed to the actor. + target_actor : Actor + Target actor to receive the texture. + blending_mode : str, optional + Texture blending mode. The options are: + 1. None + 2. Replace + 3. Modulate + 4. Add + 5. AddSigned + 6. Interpolate + 7. Subtract + wrap_mode : str, optional + Texture wrapping mode. The options are: + 1. ClampToEdge + 2. Repeat + 3. MirroredRepeat + 4. ClampToBorder + border_color : tuple (4, ), optional + Texture RGBA border color. + interpolate : bool, optional + Texture interpolation. + d_type : str, optional + Texture pixel type, "rgb" or "rgba". Default is "rgb" + """ + + windowToImageFilter = WindowToImageFilter() + windowToImageFilter.SetInput(window) + type_dic = {"rgb" : windowToImageFilter.SetInputBufferTypeToRGB, + "rgba" : windowToImageFilter.SetInputBufferTypeToRGBA, + "zbuffer" : windowToImageFilter.SetInputBufferTypeToZBuffer} + type_dic[d_type.lower()]() + windowToImageFilter.Update() + + texture = Texture() + texture.SetMipmap(True) + texture.SetInputConnection(windowToImageFilter.GetOutputPort()) + texture.SetBorderColor(*border_color) + texture.SetWrap(WRAP_MODE_DIC[wrap_mode.lower()]) + texture.SetInterpolate(interpolate) + texture.MipmapOn() + texture.SetBlendingMode(BLENDING_MODE_DIC[blending_mode.lower()]) + + target_actor.GetProperty().SetTexture(texture_name, texture) + + img = numpy_support.vtk_to_numpy(texture.GetInput().GetPointData().GetScalars()) + + return img + + + +def texture_to_actor( + path_to_texture : str, + texture_name : str, + target_actor : Actor, + blending_mode : str = "None", + wrap_mode : str = "ClampToBorder", + border_color : tuple = ( + 0.0, + 0.0, + 0.0, + 1.0), + interpolate : bool = True): + """Pass an imported texture to an actor. + Parameters + ---------- + path_to_texture : str + Texture image path. + texture_name : str + Name of the texture to be passed to the actor. + target_actor : Actor + Target actor to receive the texture. + blending_mode : str + Texture blending mode. The options are: + 1. None + 2. Replace + 3. Modulate + 4. Add + 5. AddSigned + 6. Interpolate + 7. Subtract + wrap_mode : str + Texture wrapping mode. The options are: + 1. ClampToEdge + 2. Repeat + 3. MirroredRepeat + 4. ClampToBorder + border_color : tuple (4, ) + Texture RGBA border color. + interpolate : bool + Texture interpolation.""" + + texture = Texture() + + colormapArray = load_image(path_to_texture) + colormapData = rgb_to_vtk(colormapArray) + + texture.SetInputDataObject(colormapData) + texture.SetBorderColor(*border_color) + texture.SetWrap(WRAP_MODE_DIC[wrap_mode.lower()]) + texture.SetInterpolate(interpolate) + texture.MipmapOn() + texture.SetBlendingMode(BLENDING_MODE_DIC[blending_mode.lower()]) + + target_actor.GetProperty().SetTexture(texture_name, texture) + + +def colormap_to_texture( + colormap : np.array, + texture_name : str, + target_actor : Actor, + interpolate : bool = True): + """Convert a colormap to a texture and pass it to an actor. + Parameters + ---------- + colormap : np.array (N, 4) or (1, N, 4) + RGBA color map array. The array can be two dimensional, although a three dimensional one is preferred. + texture_name : str + Name of the color map texture to be passed to the actor. + target_actor : Actor + Target actor to receive the color map texture. + interpolate : bool + Color map texture interpolation.""" + + if len(colormap.shape) == 2: + colormap = np.array([colormap]) + + texture = Texture() + + cmap = (255*colormap).astype(np.uint8) + cmap = rgb_to_vtk(cmap) + + texture.SetInputDataObject(cmap) + texture.SetWrap(Texture.ClampToEdge) + texture.SetInterpolate(interpolate) + texture.MipmapOn() + texture.SetBlendingMode(0) + + target_actor.GetProperty().SetTexture(texture_name, texture) + +def back_converter(h : np.ndarray): + return ((h[:, 0] + h[:, 1]/255. + h[:, 2]/65025. + h[:, 3]/16581375.)/256.0).astype(np.float32) + +class EffectManager(): + """Class that manages the application of post-processing effects on actors. + + Parameters + ---------- + manager : ShowManager + Target manager that will render post processed actors.""" + def __init__(self, manager : ShowManager): + self.scene = Scene() + cam_params = manager.scene.get_camera() + self.scene.set_camera(*cam_params) + self.on_manager = manager + self.off_manager = ShowManager(self.scene, + size=manager.size) + self.off_manager.window.SetOffScreenRendering(True) + self.off_manager.initialize() + self._n_active_effects = 0 + self._active_effects = {} + + def kde(self, + points : np.ndarray, + sigmas, + kernel : str = "gaussian", + opacity : float = 1.0, + colormap : str = "viridis", + custom_colormap : np.array = None): + """Actor that displays the Kernel Density Estimation of a given set of points. + + Parameters + ---------- + points : np.ndarray (N, 3) + Array of points to be displayed. + sigmas : np.ndarray (1, ) or (N, 1) + Array of sigmas to be used in the KDE calculations. Must be one or one for each point. + kernel : str, optional + Kernel to be used for the distribution calculation. The available options are: + * "cosine" + * "epanechnikov" + * "exponential" + * "gaussian" + * "linear" + * "tophat" + + opacity : float, optional + Opacity of the actor. + colormap : str, optional. + Colormap matplotlib name for the KDE rendering. Default is "viridis". + custom_colormap : np.ndarray (N, 4), optional + Custom colormap for the KDE rendering. Default is none which means no + custom colormap is desired. If passed, will overwrite matplotlib colormap + chosen in the previous parameter. + + Returns + ------- + textured_billboard : actor.Actor + KDE rendering actor.""" + if not isinstance(sigmas, np.ndarray): + sigmas = np.array(sigmas) + if sigmas.shape[0] != 1 and sigmas.shape[0] != points.shape[0]: + raise IndexError("sigmas size must be one or points size.") + if np.min(sigmas) <= 0: + raise ValueError("sigmas can't have zero or negative values.") + + varying_dec = """ + varying float out_sigma; + varying float out_scale; + """ + + + # converter = """ + # vec4 float_to_rgba(float value){ + # float ival = floor( value*4294967295.0 ); + # float r = floor( mod(ival, 256.0) ); + # float g = floor( ( mod(ival, 65536.0) - r ) / 256.0 ); + # float b = floor( ( mod(ival, 16777216.0) - g - r ) / 65536.0 ); + # float a = floor( ( mod(ival, 4294967296.0) - b - g - r ) / 16777216.0 ); + + # vec4 rgba = vec4(r, g, b, a)/255.0; + # return rgba; + # } + # """ + + converter = """ + vec4 float_to_rgba(float value) { + vec4 bitEnc = vec4(1.,255.,65025.,16581375.); + vec4 enc = bitEnc * value; + enc = fract(enc); + enc -= enc.yzww * vec2(1./255., 0.).xxxy; + return enc; + } + """ + + kde_dec = import_fury_shader(os.path.join("utils", f"{kernel.lower()}_distribution.glsl")) + + kde_dec = compose_shader([kde_dec, converter]) + + kde_impl = """ + float current_kde = kde(normalizedVertexMCVSOutput*out_scale, out_sigma)/n_points; + // color = vec3(current_kde); + vec4 final_color = float_to_rgba(current_kde); + fragOutput0 = vec4(final_color); + """ + + kde_vs_dec = """ + in float in_sigma; + varying float out_sigma; + + in float in_scale; + varying float out_scale; + """ + + + kde_vs_impl = """ + out_sigma = in_sigma; + out_scale = in_scale; + """ + + # de_converter = """ + # float rgba_to_float(vec4 value){ + # return (255.0* (value.r*1.0 + value.g*256.0 + value.b*65536.0 + value.a*16777216.0) ) / 4294967295.0; + # } + # """ + + de_converter = """ + float rgba_to_float(vec4 v) { + vec4 bitEnc = vec4(1.,255.,65025.,16581375.); + vec4 bitDec = 1./bitEnc; + return dot(v, bitDec); + } + """ + + gaussian_kernel = """ + const float gauss_kernel[81] = { + 0.000123, 0.000365, 0.000839, 0.001504, 0.002179, 0.002429, 0.002179, 0.001504, 0.000839, + 0.000365, 0.001093, 0.002503, 0.004494, 0.006515, 0.007273, 0.006515, 0.004494, 0.002503, + 0.000839, 0.002503, 0.005737, 0.010263, 0.014888, 0.016590, 0.014888, 0.010263, 0.005737, + 0.001504, 0.004494, 0.010263, 0.018428, 0.026753, 0.029880, 0.026753, 0.018428, 0.010263, + 0.002179, 0.006515, 0.014888, 0.026753, 0.038898, 0.043441, 0.038898, 0.026753, 0.014888, + 0.002429, 0.007273, 0.016590, 0.029880, 0.043441, 0.048489, 0.043441, 0.029880, 0.016590, + 0.002179, 0.006515, 0.014888, 0.026753, 0.038898, 0.043441, 0.038898, 0.026753, 0.014888, + 0.001504, 0.004494, 0.010263, 0.018428, 0.026753, 0.029880, 0.026753, 0.018428, 0.010263, + 0.000839, 0.002503, 0.005737, 0.010263, 0.014888, 0.016590, 0.014888, 0.010263, 0.005737}; + + const float x_offsets[81] = {-4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, + -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, + -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, + -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, + -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, + -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, + -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, + -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, + -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0}; + + const float y_offsets[81] = {-4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, + -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, + -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, + -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, + 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0}; + """ + + gauss_dec = """ + vec4 kernel_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ + vec4 value = vec4(0.0); + vec4 col = vec4(0.0); + for(int i = 0; i < 81; i++){ + col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); + value += gauss_kernel[i]*col; + } + return value; + } + """ + + map_func = """ + float map(float value, float o_min, float o_max, float new_min, float new_max) { + return new_min + (value - o_min) * (new_max - new_min) / (o_max - o_min); + } + """ + + tex_dec = import_fury_shader(os.path.join("effects", "color_mapping.glsl")) + + tex_dec = compose_shader([tex_dec, map_func]) + + tex_impl = """ + // Turning screen coordinates to texture coordinates + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + float intensity = texture(screenTexture, renorm_tex).r; + + intensity = map(intensity, min_value, max_value, 0.0, 1.0); + + if(intensity<=0.0){ + discard; + }else{ + vec4 final_color = color_mapping(intensity, colormapTexture); + fragOutput0 = vec4(final_color.rgb, u_opacity*final_color.a); + } + """ + + gaussian_kernel_3x3 = """ + const float gauss_kernel[3*3] = {1/16.0, 1/8, 1/16.0, + 1/8.0, 1/4.0, 1/8.0, + 1/16.0, 1/8.0, 1/16.0}; + + const float x_offsets[3*3] = {-1.0, 0.0, 1.0, + -1.0, 0.0, 1.0, + -1.0, 0.0, 1.0}; + + const float y_offsets[3*3] = {-1.0, -1.0, -1.0, + 0.0, 0.0, 0.0, + 1.0, 1.0, 1.0}; + """ + + gauss_dec_3x3 = """ + vec4 kernel_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ + vec4 value = vec4(0.0); + vec4 col = vec4(0.0); + for(int i = 0; i < 3*3; i++){ + col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); + value += gauss_kernel[i]*col; + } + return value; + } + """ + + inter_dec = compose_shader([de_converter, gaussian_kernel_3x3, gauss_dec_3x3]) + + inter_impl = """ + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + + vec4 pass_color = vec4(0.0); + + if(f != 0){ + pass_color = kernel_calculator(screenTexture, renorm_tex, res); + } + else{ + pass_color = texture(screenTexture, renorm_tex); + float fintensity = rgba_to_float(pass_color); + + pass_color = vec4(vec3(fintensity), 1.0); + } + + fragOutput0 = vec4(pass_color); + """ + + fs_dec = compose_shader([varying_dec, kde_dec]) + + # Scales parameter will be defined by the empirical rule: + # 1*sima radius = 68.27% of data inside the curve + # 2*sigma radius = 95.45% of data inside the curve + # 3*sigma radius = 99.73% of data inside the curve + scales = 2*3.0*np.copy(sigmas) + + center_of_mass = np.average(points, axis = 0) + bill = billboard( + points, + (0.0, + 0.0, + 1.0), + scales=scales, + fs_dec=fs_dec, + fs_impl=kde_impl, + vs_dec=kde_vs_dec, + vs_impl=kde_vs_impl) + + # Blending and uniforms setup + window = self.off_manager.window + + shader_apply_effects(window, bill, gl_disable_depth) + shader_apply_effects(window, bill, gl_set_additive_blending) + attribute_to_actor(bill, np.repeat(sigmas, 4), "in_sigma") + attribute_to_actor(bill, np.repeat(scales, 4), "in_scale") + shader_custom_uniforms(bill, "fragment").SetUniformf("n_points", points.shape[0]) + + + if self._n_active_effects > 0: + self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) + self.off_manager.scene.add(bill) + self.off_manager.render() + + bill_bounds = bill.GetBounds() + max_sigma = 2*4.0*np.max(sigmas) + + actor_scales = np.array([[bill_bounds[1] - bill_bounds[0] + center_of_mass[0] + max_sigma, + bill_bounds[3] - bill_bounds[2] + center_of_mass[1] + max_sigma, + 0.0]]) + + res = self.off_manager.size + + scale = actor_scales.max()*np.array([[res[0]/res[1], + 1.0, + 0.0]]) + + # Render to second billboard for color map post-processing. + textured_billboard = billboard(np.array([center_of_mass]), scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) + shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", res) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) + + inter_scale = 3.4*np.array([[res[0]/res[1], 1.0, 0.0]]) # hardcoded hack to make billboards occupy the whole screen + inter_billboard = billboard(np.array([center_of_mass]), scales=inter_scale, fs_dec=inter_dec, fs_impl=inter_impl) + shader_custom_uniforms(inter_billboard, "fragment").SetUniform2f("res", res) + inter_billboard.SetVisibility(False) + inter_billboard.Modified() + inter_billboard.GetProperty().GlobalWarningDisplayOff() + self.off_manager.scene.add(inter_billboard) + + + # Disables the texture warnings + textured_billboard.GetProperty().GlobalWarningDisplayOff() + + if custom_colormap == None: + cmap = create_colormap(np.arange(0.0, 1.0, (1/points.shape[0])), colormap) + else: + cmap = custom_colormap + + colormap_to_texture(cmap, "colormapTexture", textured_billboard) + + n_passes = 2 + def kde_callback(obj, event): + # 1° STEP: RENDER ALL KDE RENDERS + bill.SetVisibility(True) + cam_params = self.on_manager.scene.get_camera() + self.off_manager.scene.set_camera(*cam_params) + self.off_manager.scene.Modified() + shader_apply_effects(window, bill, gl_disable_depth) + shader_apply_effects(window, bill, gl_set_additive_blending) + self.off_manager.render() + + # 2° STEP: PASS THIS RENDER AS A TEXTURE TO POST PROCESSING BILLBOARD + window_to_texture( + self.off_manager.window, + "screenTexture", + inter_billboard, + border_color = (0.0, 0.0, 0.0, 0.0), + blending_mode="Interpolate", + d_type = "rgba") + + # 3° STEP: GAUSSIAN BLUR APPLICATION + bill.SetVisibility(False) + bill.Modified() + + inter_billboard.SetVisibility(True) + for i in range(n_passes): + shader_custom_uniforms(inter_billboard, "fragment").SetUniformi("f", i) + self.off_manager.render() + + window_to_texture( + self.off_manager.window, + "screenTexture", + inter_billboard, + border_color = (0.0, 0.0, 0.0, 0.0), + blending_mode="Interpolate", + d_type = "rgba") + + img = window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + border_color = (0.0, 0.0, 0.0, 0.0), + blending_mode="Interpolate", + d_type = "rgba") + + inter_billboard.SetVisibility(False) + inter_billboard.Modified() + + # 4° STEP: RENORMALIZE THE RENDERING + converted_img = back_converter(img) + + max_value = np.max(converted_img) + min_value = np.min(converted_img) + print(min_value, max_value) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("min_value", min_value) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("max_value", max_value) + + # Initialization + window_to_texture( + self.off_manager.window, + "screenTexture", + inter_billboard, + border_color = (0.0, 0.0, 0.0, 0.0), + blending_mode="Interpolate", + d_type = "rgba") + + bill.SetVisibility(False) + bill.Modified() + + inter_billboard.SetVisibility(True) + for i in range(n_passes): + shader_custom_uniforms(inter_billboard, "fragment").SetUniformi("f", i) + self.off_manager.render() + + window_to_texture( + self.off_manager.window, + "screenTexture", + inter_billboard, + border_color = (0.0, 0.0, 0.0, 0.0), + blending_mode="Interpolate", + d_type = "rgba") + + + img = window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + border_color = (0.0, 0.0, 0.0, 0.0), + blending_mode="Interpolate", + d_type = "rgba") + + inter_billboard.SetVisibility(False) + inter_billboard.Modified() + + converted_img = back_converter(img) + + max_value = np.max(converted_img) + min_value = np.min(converted_img) + print(min_value, max_value) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("min_value", min_value) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("max_value", max_value) + + callback_id = self.on_manager.add_iren_callback(kde_callback, "RenderEvent") + + self._active_effects[textured_billboard] = callback_id + self._n_active_effects += 1 + + return textured_billboard + + def grayscale(self, actor, opacity): + + + tex_impl = """ + // Turning screen coordinates to texture coordinates + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 scale_factor = vec2(u_scale); + vec2 renorm_tex = scale_factor*res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + vec4 col = texture(screenTexture, renorm_tex); + float bw = 0.2126*col.r + 0.7152*col.g + 0.0722*col.b; + + fragOutput0 = vec4(vec3(bw), u_opacity*col.a); + """ + + if self._n_active_effects > 0: + self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) + self.off_manager.scene.add(actor) + self.off_manager.render() + + actor_pos = np.array([actor.GetCenter()]) + actor_bounds = actor.GetBounds() + + actor_scales = np.array([actor_bounds[1] - actor_bounds[0], + actor_bounds[3] - actor_bounds[2], + 0.0]) + + scale = np.array([[actor_scales.max(), + actor_scales.max(), + 0.0]]) + + # Render to second billboard for color map post-processing. + textured_billboard = billboard(actor_pos, scales=scale, fs_impl=tex_impl) + shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.on_manager.size) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) + shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("u_scale", scale[0, :2]) + + # Disables the texture warnings + textured_billboard.GetProperty().GlobalWarningDisplayOff() + + def gray_callback(obj, event): + actor.SetVisibility(True) + pos, focal, vu = self.on_manager.scene.get_camera() + self.off_manager.scene.set_camera(pos, focal, vu) + self.off_manager.render() + + window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate", + d_type = "rgba") + + actor.SetVisibility(False) + actor.Modified() + + + # Initialization + window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate", + d_type = "rgba") + + callback_id = self.on_manager.add_iren_callback(gray_callback, "RenderEvent") + + self._active_effects[textured_billboard] = callback_id + self._n_active_effects += 1 + + return textured_billboard + + def laplacian(self, actor, opacity): + + + laplacian_operator = """ + const float laplacian_mat[3*3] = {0.0, 1.0, 0.0, + 1.0,-4.0, 1.0, + 0.0, 1.0, 0.0}; + + const float x_offsets[3*3] = {-1.0, 0.0, 1.0, + -1.0, 0.0, 1.0, + -1.0, 0.0, 1.0}; + + const float y_offsets[3*3] = {-1.0, -1.0, -1.0, + 0.0, 0.0, 0.0, + 1.0, 1.0, 1.0}; + """ + + lapl_dec = """ + vec4 laplacian_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ + vec4 value = vec4(0.0); + vec4 col = vec4(0.0); + for(int i = 0; i < 9; i++){ + col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); + value += vec4(laplacian_mat[i])*col; + } + return value; + } + """ + + tex_impl = """ + // Turning screen coordinates to texture coordinates + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + vec4 lapl_color = laplacian_calculator(screenTexture, renorm_tex, res); + + fragOutput0 = vec4(lapl_color.rgb, u_opacity*lapl_color.a); + """ + tex_dec = compose_shader([laplacian_operator, lapl_dec]) + + if self._n_active_effects > 0: + self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) + self.off_manager.scene.add(actor) + self.off_manager.render() + + actor_pos = np.array([actor.GetCenter()]) + actor_bounds = actor.GetBounds() + + actor_scales = np.array([actor_bounds[1] - actor_bounds[0], + actor_bounds[3] - actor_bounds[2], + 0.0]) + + scale = np.array([[actor_scales.max(), + actor_scales.max(), + 0.0]]) + + # Render to second billboard for color map post-processing. + textured_billboard = billboard(actor_pos, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) + shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) + + # Disables the texture warnings + textured_billboard.GetProperty().GlobalWarningDisplayOff() + + def laplacian_callback(obj, event): + actor.SetVisibility(True) + pos, focal, vu = self.on_manager.scene.get_camera() + self.off_manager.scene.set_camera(pos, focal, vu) + self.off_manager.render() + + window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate", + d_type = "rgba") + + actor.SetVisibility(False) + actor.Modified() + + # Initialization + window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate", + d_type = "rgba") + + callback_id = self.on_manager.add_iren_callback(laplacian_callback, "RenderEvent") + + self._active_effects[textured_billboard] = callback_id + self._n_active_effects += 1 + + return textured_billboard + + + def gaussian_blur(self, actor, opacity): + + + gaussian_kernel = """ + const float gauss_kernel[3*3] = {1/16.0, 1/8, 1/16.0, + 1/8.0, 1/4.0, 1/8.0, + 1/16.0, 1/8.0, 1/16.0}; + + const float x_offsets[3*3] = {-1.0, 0.0, 1.0, + -1.0, 0.0, 1.0, + -1.0, 0.0, 1.0}; + + const float y_offsets[3*3] = {-1.0, -1.0, -1.0, + 0.0, 0.0, 0.0, + 1.0, 1.0, 1.0}; + """ + + gauss_dec = """ + vec4 kernel_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ + vec4 value = vec4(0.0); + vec4 col = vec4(0.0); + for(int i = 0; i < 9; i++){ + col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); + value += gauss_kernel[i]*col; + } + return value; + } + """ + + tex_impl = """ + // Turning screen coordinates to texture coordinates + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + vec4 kernel_color = kernel_calculator(screenTexture, renorm_tex, res); + + fragOutput0 = vec4(kernel_color.rgb, u_opacity*kernel_color.a); + """ + tex_dec = compose_shader([gaussian_kernel, gauss_dec]) + + if self._n_active_effects > 0: + self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) + self.off_manager.scene.add(actor) + self.off_manager.render() + + actor_pos = np.array([actor.GetCenter()]) + actor_bounds = actor.GetBounds() + + actor_scales = np.array([actor_bounds[1] - actor_bounds[0], + actor_bounds[3] - actor_bounds[2], + 0.0]) + + scale = np.array([[actor_scales.max(), + actor_scales.max(), + 0.0]]) + + # Render to second billboard for color map post-processing. + textured_billboard = billboard(actor_pos, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) + shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) + shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) + + + # Disables the texture warnings + textured_billboard.GetProperty().GlobalWarningDisplayOff() + + def kernel_callback(obj, event): + actor.SetVisibility(True) + pos, focal, vu = self.on_manager.scene.get_camera() + self.off_manager.scene.set_camera(pos, focal, vu) + self.off_manager.render() + + window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate", + border_color=(0.0, 0.0, 0.0, 0.0), + d_type = "rgba") + + actor.SetVisibility(False) + actor.Modified() + + + # Initialization + window_to_texture( + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate", + border_color=(0.0, 0.0, 0.0, 0.0), + d_type = "rgba") + + + callback_id = self.on_manager.add_iren_callback(kernel_callback, "RenderEvent") + + self._active_effects[textured_billboard] = callback_id + self._n_active_effects += 1 + + return textured_billboard + + + def remove_effect(self, effect_actor): + """Remove an existing effect from the effects manager. + Beware that the effect and the actor will be removed from the rendering pipeline + and shall not work after this action. + + Parameters + ---------- + effect_actor : actor.Actor + Actor of effect to be removed. + """ + if self._n_active_effects > 0: + self.on_manager.iren.RemoveObserver(self._active_effects[effect_actor]) + self.on_manager.scene.RemoveActor(effect_actor) + self.off_manager.scene.RemoveActor(effect_actor) + self._active_effects.pop(effect_actor) + self._n_active_effects -= 1 + else: + raise IndexError("Manager has no active effects.") + + \ No newline at end of file From 9ad150113149228b8dea7bfb32a8dac3248c9170 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Mon, 31 Jul 2023 11:58:16 -0300 Subject: [PATCH 19/41] fix: Minor refactoring on effect manager --- fury/actors/effect_manager_alt.py | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/fury/actors/effect_manager_alt.py b/fury/actors/effect_manager_alt.py index 7c5dd433d..fb904aa29 100644 --- a/fury/actors/effect_manager_alt.py +++ b/fury/actors/effect_manager_alt.py @@ -462,7 +462,6 @@ def kde(self, if self._n_active_effects > 0: self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) self.off_manager.scene.add(bill) - self.off_manager.render() bill_bounds = bill.GetBounds() max_sigma = 2*4.0*np.max(sigmas) @@ -481,8 +480,7 @@ def kde(self, textured_billboard = billboard(np.array([center_of_mass]), scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", res) shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) - - + # Disables the texture warnings textured_billboard.GetProperty().GlobalWarningDisplayOff() @@ -493,7 +491,7 @@ def kde(self, colormap_to_texture(cmap, "colormapTexture", textured_billboard) - def kde_callback(obj, event): + def kde_callback(obj = None, event = None): cam_params = self.on_manager.scene.get_camera() self.off_manager.scene.set_camera(*cam_params) self.off_manager.scene.Modified() @@ -517,7 +515,6 @@ def kde_callback(obj, event): low_v = converted_img[converted_img <= avg].shape[0] high_v = converted_img[converted_img > avg].shape[0] max_value_2 = avg + (avg - min_value)*(high_v/low_v) - # print(min_value, max_value) # max_value = np.max(converted_img) # print(min_value, max_value, max_value_2) # print(converted_img[converted_img <= max_value_2].shape[0], converted_img[converted_img > max_value_2].shape[0]) @@ -525,21 +522,7 @@ def kde_callback(obj, event): shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("max_value", max_value_2) # Initialization - img = window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - border_color = (0.0, 0.0, 0.0, 0.0), - blending_mode="Interpolate", - d_type = "rgba") - - converted_img = back_converter(img) - - max_value = np.max(converted_img) - min_value = np.min(converted_img) - # print(min_value, max_value) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("min_value", min_value) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("max_value", max_value) + kde_callback() callback_id = self.on_manager.add_iren_callback(kde_callback, "RenderEvent") @@ -587,7 +570,7 @@ def grayscale(self, actor, opacity): # Disables the texture warnings textured_billboard.GetProperty().GlobalWarningDisplayOff() - def gray_callback(obj, event): + def gray_callback(obj = None, event = None): actor.SetVisibility(True) pos, focal, vu = self.on_manager.scene.get_camera() self.off_manager.scene.set_camera(pos, focal, vu) From 0ffaf28204607982d716a696e7e45497faa7d8fc Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Tue, 1 Aug 2023 10:47:09 -0300 Subject: [PATCH 20/41] feat!: Experimenting with intensity slider (unstable) --- docs/experimental/float_to_rgb.py | 51 ++++++++++++++-- docs/experimental/viz_kde_class.py | 9 ++- fury/actors/effect_manager.py | 37 ++++++++---- fury/actors/effect_manager_alt.py | 93 ++++++++++++++++++++---------- 4 files changed, 140 insertions(+), 50 deletions(-) diff --git a/docs/experimental/float_to_rgb.py b/docs/experimental/float_to_rgb.py index ee779f6b3..90a322869 100644 --- a/docs/experimental/float_to_rgb.py +++ b/docs/experimental/float_to_rgb.py @@ -21,8 +21,51 @@ def de_converter(h): c = converter(i) d = converter(j) -print(f, g) -print(i) -print(np.array(c)) +# print(f, g) +# print(i) +# print(np.array(c)) de = de_converter(c + d) -print(int(de*256*256*256*256 - 1)) \ No newline at end of file +# print(int(de*256*256*256*256 - 1)) + + + +def gaussian_kernel(n, sigma = 1.0): + x0 = np.arange(0.0, 1.0, 1/n) + y0 = np.arange(0.0, 1.0, 1/n) + x, y = np.meshgrid(x0, y0) + center = np.array([x0[n // 2], y0[n // 2]]) + mesh = np.stack((x, y), 2) + center = np.repeat(center, x.shape[0]*y.shape[0]).reshape(x.shape[0], y.shape[0], 2) + kernel = np.exp((-1.0*np.linalg.norm(center - mesh, axis = 2)**2)/(2*sigma**2)) + string = f"const float gauss_kernel[{x.shape[0]*y.shape[0]}] = " + kernel = kernel/np.sum(kernel) + flat = str(kernel.flatten()).split(" ") + copy_flat = flat.copy() + taken = 0 + for i in range(len(flat)): + if flat[i] == ' ' or flat[i] == '': + copy_flat.pop(i - taken) + taken += 1 + if "[" in copy_flat[0]: + copy_flat[0] = copy_flat[0][1:] + else: + copy_flat.pop(0) + + if "]" in copy_flat[-1]: + copy_flat[-1] = copy_flat[-1][:-1] + else: + copy_flat.pop(-1) + + if '' == copy_flat[0]: + copy_flat.pop(0) + + if '' == copy_flat[-1]: + copy_flat.pop(-1) + + # copy_flat.pop(-1) + print(copy_flat) + + string += "{" + ", ".join(copy_flat) + "};" + return string + +print(gaussian_kernel(13, 3.0)) \ No newline at end of file diff --git a/docs/experimental/viz_kde_class.py b/docs/experimental/viz_kde_class.py index 764ea9262..1b089dd98 100644 --- a/docs/experimental/viz_kde_class.py +++ b/docs/experimental/viz_kde_class.py @@ -1,6 +1,6 @@ import numpy as np -from fury.actors.effect_manager_alt import EffectManager +from fury.actors.effect_manager import EffectManager from fury.window import Scene, ShowManager, record def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int = 0): @@ -27,7 +27,7 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int "Can't normalize an array which maximum and minimum value are the same.") -width, height = (800, 600) +width, height = (1200, 1000) scene = Scene() scene.set_camera(position=(-6, 5, -10), @@ -48,13 +48,13 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int n_points = 1000 points = np.random.rand(n_points, 3) points = normalize(points, -5, 5) -sigmas = normalize(np.random.rand(n_points, 1), 0.2, 0.9) +sigmas = normalize(np.random.rand(n_points, 1), 0.1, 0.6) offset = np.array([0.0, 0.0, 0.0]) points = points + np.tile(offset, points.shape[0]).reshape(points.shape) effects = EffectManager(manager) -kde_actor = effects.kde(points, sigmas, kernel = "linear", colormap = "inferno") +kde_actor = effects.kde(points, sigmas, kernel = "exponential", colormap = "inferno") manager.scene.add(kde_actor) @@ -66,4 +66,3 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int manager.start() record(scene, out_path = "kde_points.png", size = (800, 800)) - diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index 072e66dc0..48037103e 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -9,6 +9,7 @@ import_fury_shader, shader_apply_effects, shader_custom_uniforms) +from fury.ui import LineSlider2D from fury.utils import rgb_to_vtk from fury.window import (gl_disable_depth, gl_set_additive_blending, @@ -318,7 +319,6 @@ def kde(self, if self._n_active_effects > 0: self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) self.off_manager.scene.add(bill) - self.off_manager.render() bill_bounds = bill.GetBounds() max_sigma = 2*4.0*np.max(sigmas) @@ -330,10 +330,12 @@ def kde(self, scale = np.array([[actor_scales.max(), actor_scales.max(), 0.0]]) + + res = self.off_manager.size # Render to second billboard for color map post-processing. textured_billboard = billboard(np.array([center_of_mass]), scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) - shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) + shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", res) shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) # Disables the texture warnings @@ -344,9 +346,9 @@ def kde(self, else: cmap = custom_colormap - colormap_to_texture(cmap, "colormapTexture", textured_billboard) + colormap_to_texture(cmap, "colormapTexture", textured_billboard) - def kde_callback(obj, event): + def kde_callback(obj = None, event = None): cam_params = self.on_manager.scene.get_camera() self.off_manager.scene.set_camera(*cam_params) self.off_manager.scene.Modified() @@ -362,12 +364,27 @@ def kde_callback(obj, event): d_type = "rgba") # Initialization - window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - d_type = "rgba") + kde_callback() + + minv = 1 + initv = 1000 + maxv = 2000 + offset = 150 + line_slider = LineSlider2D(center = (res[0] - offset, 0 + (res[1]/res[0])*offset), + initial_value = initv, + min_value = minv, max_value = maxv, + text_alignment='bottom', + orientation = 'horizontal') + + def intensity_change(slider): + intensity = slider.value/initv + attribute_to_actor(bill, intensity*np.repeat(sigmas, 4), "in_sigma") + bill.Modified() + kde_callback() + + line_slider.on_moving_slider = intensity_change + + self.on_manager.scene.add(line_slider) callback_id = self.on_manager.add_iren_callback(kde_callback, "RenderEvent") diff --git a/fury/actors/effect_manager_alt.py b/fury/actors/effect_manager_alt.py index fb904aa29..1acf2ca0d 100644 --- a/fury/actors/effect_manager_alt.py +++ b/fury/actors/effect_manager_alt.py @@ -339,43 +339,74 @@ def kde(self, """ gaussian_kernel = """ - const float gauss_kernel[81] = { - 0.000123, 0.000365, 0.000839, 0.001504, 0.002179, 0.002429, 0.002179, 0.001504, 0.000839, - 0.000365, 0.001093, 0.002503, 0.004494, 0.006515, 0.007273, 0.006515, 0.004494, 0.002503, - 0.000839, 0.002503, 0.005737, 0.010263, 0.014888, 0.016590, 0.014888, 0.010263, 0.005737, - 0.001504, 0.004494, 0.010263, 0.018428, 0.026753, 0.029880, 0.026753, 0.018428, 0.010263, - 0.002179, 0.006515, 0.014888, 0.026753, 0.038898, 0.043441, 0.038898, 0.026753, 0.014888, - 0.002429, 0.007273, 0.016590, 0.029880, 0.043441, 0.048489, 0.043441, 0.029880, 0.016590, - 0.002179, 0.006515, 0.014888, 0.026753, 0.038898, 0.043441, 0.038898, 0.026753, 0.014888, - 0.001504, 0.004494, 0.010263, 0.018428, 0.026753, 0.029880, 0.026753, 0.018428, 0.010263, - 0.000839, 0.002503, 0.005737, 0.010263, 0.014888, 0.016590, 0.014888, 0.010263, 0.005737}; - - const float x_offsets[81] = {-4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, - -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, - -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, - -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, - -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, - -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, - -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, - -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, - -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0}; + const float gauss_kernel[169] = {0.00583209, 0.00585322, 0.00587056, 0.00588409, 0.00589377, 0.00589958 + , 0.00590152, 0.00589958, 0.00589377, 0.00588409, 0.00587056, 0.00585322 + , 0.00583209, 0.00585322, 0.00587442, 0.00589183, 0.0059054, 0.00591512 + , 0.00592095, 0.0059229, 0.00592095, 0.00591512, 0.0059054, 0.00589183 + , 0.00587442, 0.00585322, 0.00587056, 0.00589183, 0.00590929, 0.0059229 + , 0.00593264, 0.0059385, 0.00594045, 0.0059385, 0.00593264, 0.0059229 + , 0.00590929, 0.00589183, 0.00587056, 0.00588409, 0.0059054, 0.0059229 + , 0.00593654, 0.00594631, 0.00595218, 0.00595413, 0.00595218, 0.00594631 + , 0.00593654, 0.0059229, 0.0059054, 0.00588409, 0.00589377, 0.00591512 + , 0.00593264, 0.00594631, 0.00595609, 0.00596197, 0.00596393, 0.00596197 + , 0.00595609, 0.00594631, 0.00593264, 0.00591512, 0.00589377, 0.00589958 + , 0.00592095, 0.0059385, 0.00595218, 0.00596197, 0.00596785, 0.00596981 + , 0.00596785, 0.00596197, 0.00595218, 0.0059385, 0.00592095, 0.00589958 + , 0.00590152, 0.0059229, 0.00594045, 0.00595413, 0.00596393, 0.00596981 + , 0.00597178, 0.00596981, 0.00596393, 0.00595413, 0.00594045, 0.0059229 + , 0.00590152, 0.00589958, 0.00592095, 0.0059385, 0.00595218, 0.00596197 + , 0.00596785, 0.00596981, 0.00596785, 0.00596197, 0.00595218, 0.0059385 + , 0.00592095, 0.00589958, 0.00589377, 0.00591512, 0.00593264, 0.00594631 + , 0.00595609, 0.00596197, 0.00596393, 0.00596197, 0.00595609, 0.00594631 + , 0.00593264, 0.00591512, 0.00589377, 0.00588409, 0.0059054, 0.0059229 + , 0.00593654, 0.00594631, 0.00595218, 0.00595413, 0.00595218, 0.00594631 + , 0.00593654, 0.0059229, 0.0059054, 0.00588409, 0.00587056, 0.00589183 + , 0.00590929, 0.0059229, 0.00593264, 0.0059385, 0.00594045, 0.0059385 + , 0.00593264, 0.0059229, 0.00590929, 0.00589183, 0.00587056, 0.00585322 + , 0.00587442, 0.00589183, 0.0059054, 0.00591512, 0.00592095, 0.0059229 + , 0.00592095, 0.00591512, 0.0059054, 0.00589183, 0.00587442, 0.00585322 + , 0.00583209, 0.00585322, 0.00587056, 0.00588409, 0.00589377, 0.00589958 + , 0.00590152, 0.00589958, 0.00589377, 0.00588409, 0.00587056, 0.00585322 + , 0.00583209}; + + const float x_offsets[169] = { + -6.0, -6.0, -6.0, -6.0, -6.0, -6.0, -6.0, -6.0, -6.0, -6.0, -6.0, -6.0, -6.0, + -5.0, -5.0, -5.0, -5.0, -5.0, -5.0, -5.0, -5.0, -5.0, -5.0, -5.0, -5.0, -5.0, + -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, + -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, + -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, + -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, + 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, + 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0 + }; - const float y_offsets[81] = {-4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, - -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, - -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, - -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, - 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, - 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, - 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0}; + const float y_offsets[169] = { + -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, + -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, + -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, + -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, + -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, + -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, + -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, + -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, + -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, + -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, + -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, + -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, + -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 + }; """ gauss_dec = """ vec4 kernel_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ vec4 value = vec4(0.0); vec4 col = vec4(0.0); - for(int i = 0; i < 81; i++){ + for(int i = 0; i < 169; i++){ col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); value += gauss_kernel[i]*col; } @@ -515,7 +546,7 @@ def kde_callback(obj = None, event = None): low_v = converted_img[converted_img <= avg].shape[0] high_v = converted_img[converted_img > avg].shape[0] max_value_2 = avg + (avg - min_value)*(high_v/low_v) - # max_value = np.max(converted_img) + max_value = np.max(converted_img) # print(min_value, max_value, max_value_2) # print(converted_img[converted_img <= max_value_2].shape[0], converted_img[converted_img > max_value_2].shape[0]) shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("min_value", min_value) From 2c1685aeebbbe22917b5e6e41a522ccb3c7716fc Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Tue, 1 Aug 2023 14:02:15 -0300 Subject: [PATCH 21/41] fix: Deleted effects testing file --- docs/experimental/viz_laplacian.py | 66 ------------------------------ 1 file changed, 66 deletions(-) delete mode 100644 docs/experimental/viz_laplacian.py diff --git a/docs/experimental/viz_laplacian.py b/docs/experimental/viz_laplacian.py deleted file mode 100644 index df801b8e4..000000000 --- a/docs/experimental/viz_laplacian.py +++ /dev/null @@ -1,66 +0,0 @@ -import numpy as np - -from fury.actor import cube, sphere -from fury.actors.effect_manager import EffectManager -from fury.window import (Scene, ShowManager, record) - -def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int = 0): - """Convert an array to a given desired range. - - Parameters - ---------- - array : np.ndarray - Array to be normalized. - min : float - Bottom value of the interval of normalization. If no value is given, it is passed as 0.0. - max : float - Upper value of the interval of normalization. If no value is given, it is passed as 1.0. - - Returns - ------- - array : np.array - Array converted to the given desired range. - """ - if np.max(array) != np.min(array): - return ((array - np.min(array))/(np.max(array) - np.min(array)))*(max - min) + min - else: - raise ValueError( - "Can't normalize an array which maximum and minimum value are the same.") - - -width, height = (800, 800) - -scene = Scene() -scene.set_camera(position=(0, 0, -10), - focal_point=(0.0, - 0.0, - 0.0), - view_up=(0.0, 0.0, 0.0)) - -manager = ShowManager( - scene, - "demo", - (width, - height)) - -manager.initialize() - -offset = 1.0 -cube_actor = cube(np.array([[0.0, 0.0, -1.0 + offset]]), colors = (1.0, 0.5, 0.0)) -sphere_actor = sphere(np.array([[0.0, 0.0, 1.0 + offset], [1.0, 0.5, 1.0 + offset]]), (0.0, 1.0, 1.0), radii = 0.5) - -effects = EffectManager(manager) -gauss_cube = effects.gaussian_blur(cube_actor, 1.0) -gray_sphere = effects.grayscale(sphere_actor, 1.0) - -manager.scene.add(gray_sphere) -manager.scene.add(gauss_cube) - -# effects.remove_effect(gauss_cube) - -interactive = True - -if interactive: - manager.start() - -record(scene, out_path = "post_process.png", size = (800, 800)) \ No newline at end of file From 73dfc3fa12bbf936a87d08a1e90eab340e7c4074 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Tue, 1 Aug 2023 14:05:02 -0300 Subject: [PATCH 22/41] fix: Deleted experimental files --- docs/experimental/viz_billboards.py | 28 - fury/actors/effect_manager_alt.py | 850 -------------------------- fury/actors/effect_manager_alt_2.py | 909 ---------------------------- 3 files changed, 1787 deletions(-) delete mode 100644 docs/experimental/viz_billboards.py delete mode 100644 fury/actors/effect_manager_alt.py delete mode 100644 fury/actors/effect_manager_alt_2.py diff --git a/docs/experimental/viz_billboards.py b/docs/experimental/viz_billboards.py deleted file mode 100644 index 90c589f70..000000000 --- a/docs/experimental/viz_billboards.py +++ /dev/null @@ -1,28 +0,0 @@ -import numpy as np - -from fury.actor import billboard -from fury.window import Scene, ShowManager - -width, height = (1350, 800) - -scene = Scene() -scene.set_camera(position=(-6, 5, -10), - focal_point=(0.0, - 0.0, - 0.0), - view_up=(0.0, 0.0, 0.0)) - -manager = ShowManager( - scene, - "demo", - (width, - height)) - -manager.initialize() - -scale = 3.4*np.array([[width/height, 1.0, 0.0]]) - -bill = billboard(np.array([[0.0, 0.0, 0.0]]), scales=scale,colors = (1.0, 0.0, 0.0)) -manager.scene.add(bill) - -manager.start() \ No newline at end of file diff --git a/fury/actors/effect_manager_alt.py b/fury/actors/effect_manager_alt.py deleted file mode 100644 index 1acf2ca0d..000000000 --- a/fury/actors/effect_manager_alt.py +++ /dev/null @@ -1,850 +0,0 @@ -import os -import numpy as np -from fury.actor import Actor, billboard -from fury.colormap import create_colormap -from fury.io import load_image -from fury.lib import Texture, WindowToImageFilter, numpy_support -from fury.shaders import (attribute_to_actor, - compose_shader, - import_fury_shader, - shader_apply_effects, - shader_custom_uniforms) -from fury.utils import rgb_to_vtk -from fury.window import (gl_disable_depth, - gl_set_additive_blending, - RenderWindow, - Scene, - ShowManager) - - -WRAP_MODE_DIC = {"clamptoedge" : Texture.ClampToEdge, - "repeat" : Texture.Repeat, - "mirroredrepeat" : Texture.MirroredRepeat, - "clamptoborder" : Texture.ClampToBorder} - -BLENDING_MODE_DIC = {"none" : 0, "replace" : 1, - "modulate" : 2, "add" : 3, - "addsigned" : 4, "interpolate" : 5, - "subtract" : 6} - - - -def window_to_texture( - window : RenderWindow, - texture_name : str, - target_actor : Actor, - blending_mode : str = "None", - wrap_mode : str = "ClampToBorder", - border_color : tuple = ( - 0.0, - 0.0, - 0.0, - 1.0), - interpolate : bool = True, - d_type : str = "rgb"): - """Capture a rendered window and pass it as a texture to the given actor. - Parameters - ---------- - window : window.RenderWindow - Window to be captured. - texture_name : str - Name of the texture to be passed to the actor. - target_actor : Actor - Target actor to receive the texture. - blending_mode : str, optional - Texture blending mode. The options are: - 1. None - 2. Replace - 3. Modulate - 4. Add - 5. AddSigned - 6. Interpolate - 7. Subtract - wrap_mode : str, optional - Texture wrapping mode. The options are: - 1. ClampToEdge - 2. Repeat - 3. MirroredRepeat - 4. ClampToBorder - border_color : tuple (4, ), optional - Texture RGBA border color. - interpolate : bool, optional - Texture interpolation. - d_type : str, optional - Texture pixel type, "rgb" or "rgba". Default is "rgb" - """ - - windowToImageFilter = WindowToImageFilter() - windowToImageFilter.SetInput(window) - type_dic = {"rgb" : windowToImageFilter.SetInputBufferTypeToRGB, "rgba" : windowToImageFilter.SetInputBufferTypeToRGBA} - type_dic[d_type.lower()]() - windowToImageFilter.Update() - - texture = Texture() - texture.SetMipmap(True) - texture.SetInputConnection(windowToImageFilter.GetOutputPort()) - texture.SetBorderColor(*border_color) - texture.SetWrap(WRAP_MODE_DIC[wrap_mode.lower()]) - texture.SetInterpolate(interpolate) - texture.MipmapOn() - texture.SetBlendingMode(BLENDING_MODE_DIC[blending_mode.lower()]) - - target_actor.GetProperty().SetTexture(texture_name, texture) - - img = numpy_support.vtk_to_numpy(texture.GetInput().GetPointData().GetScalars()) - - return img - - - -def texture_to_actor( - path_to_texture : str, - texture_name : str, - target_actor : Actor, - blending_mode : str = "None", - wrap_mode : str = "ClampToBorder", - border_color : tuple = ( - 0.0, - 0.0, - 0.0, - 1.0), - interpolate : bool = True): - """Pass an imported texture to an actor. - Parameters - ---------- - path_to_texture : str - Texture image path. - texture_name : str - Name of the texture to be passed to the actor. - target_actor : Actor - Target actor to receive the texture. - blending_mode : str - Texture blending mode. The options are: - 1. None - 2. Replace - 3. Modulate - 4. Add - 5. AddSigned - 6. Interpolate - 7. Subtract - wrap_mode : str - Texture wrapping mode. The options are: - 1. ClampToEdge - 2. Repeat - 3. MirroredRepeat - 4. ClampToBorder - border_color : tuple (4, ) - Texture RGBA border color. - interpolate : bool - Texture interpolation.""" - - texture = Texture() - - colormapArray = load_image(path_to_texture) - colormapData = rgb_to_vtk(colormapArray) - - texture.SetInputDataObject(colormapData) - texture.SetBorderColor(*border_color) - texture.SetWrap(WRAP_MODE_DIC[wrap_mode.lower()]) - texture.SetInterpolate(interpolate) - texture.MipmapOn() - texture.SetBlendingMode(BLENDING_MODE_DIC[blending_mode.lower()]) - - target_actor.GetProperty().SetTexture(texture_name, texture) - - -def colormap_to_texture( - colormap : np.array, - texture_name : str, - target_actor : Actor, - interpolate : bool = True): - """Convert a colormap to a texture and pass it to an actor. - Parameters - ---------- - colormap : np.array (N, 4) or (1, N, 4) - RGBA color map array. The array can be two dimensional, although a three dimensional one is preferred. - texture_name : str - Name of the color map texture to be passed to the actor. - target_actor : Actor - Target actor to receive the color map texture. - interpolate : bool - Color map texture interpolation.""" - - if len(colormap.shape) == 2: - colormap = np.array([colormap]) - - texture = Texture() - - cmap = (255*colormap).astype(np.uint8) - cmap = rgb_to_vtk(cmap) - - texture.SetInputDataObject(cmap) - texture.SetWrap(Texture.ClampToEdge) - texture.SetInterpolate(interpolate) - texture.MipmapOn() - texture.SetBlendingMode(0) - - target_actor.GetProperty().SetTexture(texture_name, texture) - -def back_converter(h : np.ndarray): - return ((h[:, 0] + h[:, 1]/255. + h[:, 2]/65025. + h[:, 3]/16581375.)/256.0).astype(np.float32) - -class EffectManager(): - """Class that manages the application of post-processing effects on actors. - - Parameters - ---------- - manager : ShowManager - Target manager that will render post processed actors.""" - def __init__(self, manager : ShowManager): - self.scene = Scene() - cam_params = manager.scene.get_camera() - self.scene.set_camera(*cam_params) - self.on_manager = manager - self.off_manager = ShowManager(self.scene, - size=manager.size) - self.off_manager.window.SetOffScreenRendering(True) - self.off_manager.initialize() - self._n_active_effects = 0 - self._active_effects = {} - - def kde(self, - points : np.ndarray, - sigmas, - kernel : str = "gaussian", - opacity : float = 1.0, - colormap : str = "viridis", - custom_colormap : np.array = None): - """Actor that displays the Kernel Density Estimation of a given set of points. - - Parameters - ---------- - points : np.ndarray (N, 3) - Array of points to be displayed. - sigmas : np.ndarray (1, ) or (N, 1) - Array of sigmas to be used in the KDE calculations. Must be one or one for each point. - kernel : str, optional - Kernel to be used for the distribution calculation. The available options are: - * "cosine" - * "epanechnikov" - * "exponential" - * "gaussian" - * "linear" - * "tophat" - - opacity : float, optional - Opacity of the actor. - colormap : str, optional. - Colormap matplotlib name for the KDE rendering. Default is "viridis". - custom_colormap : np.ndarray (N, 4), optional - Custom colormap for the KDE rendering. Default is none which means no - custom colormap is desired. If passed, will overwrite matplotlib colormap - chosen in the previous parameter. - - Returns - ------- - textured_billboard : actor.Actor - KDE rendering actor.""" - if not isinstance(sigmas, np.ndarray): - sigmas = np.array(sigmas) - if sigmas.shape[0] != 1 and sigmas.shape[0] != points.shape[0]: - raise IndexError("sigmas size must be one or points size.") - if np.min(sigmas) <= 0: - raise ValueError("sigmas can't have zero or negative values.") - - varying_dec = """ - varying float out_sigma; - varying float out_scale; - """ - - - # converter = """ - # vec4 float_to_rgba(float value){ - # float ival = floor( value*4294967295.0 ); - # float r = floor( mod(ival, 256.0) ); - # float g = floor( ( mod(ival, 65536.0) - r ) / 256.0 ); - # float b = floor( ( mod(ival, 16777216.0) - g - r ) / 65536.0 ); - # float a = floor( ( mod(ival, 4294967296.0) - b - g - r ) / 16777216.0 ); - - # vec4 rgba = vec4(r, g, b, a)/255.0; - # return rgba; - # } - # """ - - converter = """ - vec4 float_to_rgba(float value) { - vec4 bitEnc = vec4(1.,255.,65025.,16581375.); - vec4 enc = bitEnc * value; - enc = fract(enc); - enc -= enc.yzww * vec2(1./255., 0.).xxxy; - return enc; - } - """ - - kde_dec = import_fury_shader(os.path.join("utils", f"{kernel.lower()}_distribution.glsl")) - - kde_dec = compose_shader([kde_dec, converter]) - - kde_impl = """ - float current_kde = kde(normalizedVertexMCVSOutput*out_scale, out_sigma)/n_points; - // color = vec3(current_kde); - vec4 final_color = float_to_rgba(current_kde); - fragOutput0 = vec4(final_color); - - """ - - kde_vs_dec = """ - in float in_sigma; - varying float out_sigma; - - in float in_scale; - varying float out_scale; - """ - - - kde_vs_impl = """ - out_sigma = in_sigma; - out_scale = in_scale; - """ - - # de_converter = """ - # float rgba_to_float(vec4 value){ - # return (255.0* (value.r*1.0 + value.g*256.0 + value.b*65536.0 + value.a*16777216.0) ) / 4294967295.0; - # } - # """ - - # de_converter = """ - # float rgba_to_float(vec4 packedRGBA) { - # // Convert RGBA values from [0, 1] range to 8-bit integer range [0, 255] - # int r = int(packedRGBA.r * 255.0); - # int g = int(packedRGBA.g * 255.0); - # int b = int(packedRGBA.b * 255.0); - # int a = int(packedRGBA.a * 255.0); - - # // Combine the four 8-bit integers into a 32-bit integer - # int intValue = (r << 24) | (g << 16) | (b << 8) | a; - - # // Convert the 32-bit integer back to the original float value range [0, 1] - # float maxValue = 4294967295.0; // 2^16 - 1 - # return float(intValue) / maxValue; - # } - # """ - - de_converter = """ - float rgba_to_float(vec4 v) { - vec4 bitEnc = vec4(1.,255.,65025.,16581375.); - vec4 bitDec = 1./bitEnc; - return dot(v, bitDec); - } - """ - - gaussian_kernel = """ - const float gauss_kernel[169] = {0.00583209, 0.00585322, 0.00587056, 0.00588409, 0.00589377, 0.00589958 - , 0.00590152, 0.00589958, 0.00589377, 0.00588409, 0.00587056, 0.00585322 - , 0.00583209, 0.00585322, 0.00587442, 0.00589183, 0.0059054, 0.00591512 - , 0.00592095, 0.0059229, 0.00592095, 0.00591512, 0.0059054, 0.00589183 - , 0.00587442, 0.00585322, 0.00587056, 0.00589183, 0.00590929, 0.0059229 - , 0.00593264, 0.0059385, 0.00594045, 0.0059385, 0.00593264, 0.0059229 - , 0.00590929, 0.00589183, 0.00587056, 0.00588409, 0.0059054, 0.0059229 - , 0.00593654, 0.00594631, 0.00595218, 0.00595413, 0.00595218, 0.00594631 - , 0.00593654, 0.0059229, 0.0059054, 0.00588409, 0.00589377, 0.00591512 - , 0.00593264, 0.00594631, 0.00595609, 0.00596197, 0.00596393, 0.00596197 - , 0.00595609, 0.00594631, 0.00593264, 0.00591512, 0.00589377, 0.00589958 - , 0.00592095, 0.0059385, 0.00595218, 0.00596197, 0.00596785, 0.00596981 - , 0.00596785, 0.00596197, 0.00595218, 0.0059385, 0.00592095, 0.00589958 - , 0.00590152, 0.0059229, 0.00594045, 0.00595413, 0.00596393, 0.00596981 - , 0.00597178, 0.00596981, 0.00596393, 0.00595413, 0.00594045, 0.0059229 - , 0.00590152, 0.00589958, 0.00592095, 0.0059385, 0.00595218, 0.00596197 - , 0.00596785, 0.00596981, 0.00596785, 0.00596197, 0.00595218, 0.0059385 - , 0.00592095, 0.00589958, 0.00589377, 0.00591512, 0.00593264, 0.00594631 - , 0.00595609, 0.00596197, 0.00596393, 0.00596197, 0.00595609, 0.00594631 - , 0.00593264, 0.00591512, 0.00589377, 0.00588409, 0.0059054, 0.0059229 - , 0.00593654, 0.00594631, 0.00595218, 0.00595413, 0.00595218, 0.00594631 - , 0.00593654, 0.0059229, 0.0059054, 0.00588409, 0.00587056, 0.00589183 - , 0.00590929, 0.0059229, 0.00593264, 0.0059385, 0.00594045, 0.0059385 - , 0.00593264, 0.0059229, 0.00590929, 0.00589183, 0.00587056, 0.00585322 - , 0.00587442, 0.00589183, 0.0059054, 0.00591512, 0.00592095, 0.0059229 - , 0.00592095, 0.00591512, 0.0059054, 0.00589183, 0.00587442, 0.00585322 - , 0.00583209, 0.00585322, 0.00587056, 0.00588409, 0.00589377, 0.00589958 - , 0.00590152, 0.00589958, 0.00589377, 0.00588409, 0.00587056, 0.00585322 - , 0.00583209}; - - const float x_offsets[169] = { - -6.0, -6.0, -6.0, -6.0, -6.0, -6.0, -6.0, -6.0, -6.0, -6.0, -6.0, -6.0, -6.0, - -5.0, -5.0, -5.0, -5.0, -5.0, -5.0, -5.0, -5.0, -5.0, -5.0, -5.0, -5.0, -5.0, - -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, - -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, - -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, - -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, - 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, - 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, - 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, - 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0 - }; - - const float y_offsets[169] = { - -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, - -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, - -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, - -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, - -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, - -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, - -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, - -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, - -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, - -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, - -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, - -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, - -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 - }; - """ - - gauss_dec = """ - vec4 kernel_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ - vec4 value = vec4(0.0); - vec4 col = vec4(0.0); - for(int i = 0; i < 169; i++){ - col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); - value += gauss_kernel[i]*col; - } - return value; - } - """ - - avg_filter = """ - vec4 avg_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ - float x_median_offsets[5] = {-1.0, 0.0, 1.0, - 0.0, 0.0}; - - const float y_median_offsets[5] = {0.0, -1.0, 0.0, - 1.0, 0.0}; - vec4 value = vec4(0.0); - vec4 col = vec4(0.0); - for(int i = 0; i < 5; i++){ - col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_median_offsets[i], y_median_offsets[i])); - value += col; - } - return value/5.0; - } - """ - - map_func = """ - float map(float value, float o_min, float o_max, float new_min, float new_max) { - return new_min + (value - o_min) * (new_max - new_min) / (o_max - o_min); - } - """ - - tex_dec = import_fury_shader(os.path.join("effects", "color_mapping.glsl")) - - tex_dec = compose_shader([tex_dec, de_converter, map_func, gaussian_kernel, gauss_dec, avg_filter]) - - tex_impl = """ - // Turning screen coordinates to texture coordinates - vec2 res_factor = vec2(res.y/res.x, 1.0); - vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - //vec4 intensity = texture(screenTexture, renorm_tex); - vec4 intensity = kernel_calculator(screenTexture, renorm_tex, res); - //float intensity = texture(screenTexture, renorm_tex).r; - - //float fintensity = intensity.r; - float fintensity = rgba_to_float(intensity); - fintensity = map(fintensity, min_value, max_value, 0.0, 1.0); - - if(fintensity<=0.0){ - discard; - }else{ - vec4 final_color = color_mapping(fintensity, colormapTexture); - fragOutput0 = vec4(final_color.rgb, u_opacity*final_color.a); - } - """ - - fs_dec = compose_shader([varying_dec, kde_dec]) - - # Scales parameter will be defined by the empirical rule: - # 1*sima radius = 68.27% of data inside the curve - # 2*sigma radius = 95.45% of data inside the curve - # 3*sigma radius = 99.73% of data inside the curve - scales = 2*3.0*np.copy(sigmas) - - center_of_mass = np.average(points, axis = 0) - bill = billboard( - points, - (0.0, - 0.0, - 1.0), - scales=scales, - fs_dec=fs_dec, - fs_impl=kde_impl, - vs_dec=kde_vs_dec, - vs_impl=kde_vs_impl) - - # Blending and uniforms setup - window = self.off_manager.window - - shader_apply_effects(window, bill, gl_disable_depth) - shader_apply_effects(window, bill, gl_set_additive_blending) - attribute_to_actor(bill, np.repeat(sigmas, 4), "in_sigma") - attribute_to_actor(bill, np.repeat(scales, 4), "in_scale") - shader_custom_uniforms(bill, "fragment").SetUniformf("n_points", points.shape[0]) - - if self._n_active_effects > 0: - self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) - self.off_manager.scene.add(bill) - - bill_bounds = bill.GetBounds() - max_sigma = 2*4.0*np.max(sigmas) - - actor_scales = np.array([[bill_bounds[1] - bill_bounds[0] + center_of_mass[0] + max_sigma, - bill_bounds[3] - bill_bounds[2] + center_of_mass[1] + max_sigma, - 0.0]]) - - res = self.off_manager.size - - scale = actor_scales.max()*np.array([[res[0]/res[1], - 1.0, - 0.0]]) - - # Render to second billboard for color map post-processing. - textured_billboard = billboard(np.array([center_of_mass]), scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) - shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", res) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) - - # Disables the texture warnings - textured_billboard.GetProperty().GlobalWarningDisplayOff() - - if custom_colormap == None: - cmap = create_colormap(np.arange(0.0, 1.0, (1/points.shape[0])), colormap) - else: - cmap = custom_colormap - - colormap_to_texture(cmap, "colormapTexture", textured_billboard) - - def kde_callback(obj = None, event = None): - cam_params = self.on_manager.scene.get_camera() - self.off_manager.scene.set_camera(*cam_params) - self.off_manager.scene.Modified() - shader_apply_effects(window, bill, gl_disable_depth) - shader_apply_effects(window, bill, gl_set_additive_blending) - self.off_manager.render() - - img = window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - border_color = (0.0, 0.0, 0.0, 0.0), - blending_mode="Interpolate", - d_type = "rgba") - - converted_img = back_converter(img) - converted_img = converted_img[converted_img != 0.0] - - avg = np.average(converted_img) - min_value = np.min(converted_img) - low_v = converted_img[converted_img <= avg].shape[0] - high_v = converted_img[converted_img > avg].shape[0] - max_value_2 = avg + (avg - min_value)*(high_v/low_v) - max_value = np.max(converted_img) - # print(min_value, max_value, max_value_2) - # print(converted_img[converted_img <= max_value_2].shape[0], converted_img[converted_img > max_value_2].shape[0]) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("min_value", min_value) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("max_value", max_value_2) - - # Initialization - kde_callback() - - callback_id = self.on_manager.add_iren_callback(kde_callback, "RenderEvent") - - self._active_effects[textured_billboard] = callback_id - self._n_active_effects += 1 - - return textured_billboard - - def grayscale(self, actor, opacity): - - - tex_impl = """ - // Turning screen coordinates to texture coordinates - vec2 res_factor = vec2(res.y/res.x, 1.0); - vec2 scale_factor = vec2(u_scale); - vec2 renorm_tex = scale_factor*res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - vec4 col = texture(screenTexture, renorm_tex); - float bw = 0.2126*col.r + 0.7152*col.g + 0.0722*col.b; - - fragOutput0 = vec4(vec3(bw), u_opacity*col.a); - """ - - if self._n_active_effects > 0: - self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) - self.off_manager.scene.add(actor) - self.off_manager.render() - - actor_pos = np.array([actor.GetCenter()]) - actor_bounds = actor.GetBounds() - - actor_scales = np.array([actor_bounds[1] - actor_bounds[0], - actor_bounds[3] - actor_bounds[2], - 0.0]) - - scale = np.array([[actor_scales.max(), - actor_scales.max(), - 0.0]]) - - # Render to second billboard for color map post-processing. - textured_billboard = billboard(actor_pos, scales=scale, fs_impl=tex_impl) - shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.on_manager.size) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) - shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("u_scale", scale[0, :2]) - - # Disables the texture warnings - textured_billboard.GetProperty().GlobalWarningDisplayOff() - - def gray_callback(obj = None, event = None): - actor.SetVisibility(True) - pos, focal, vu = self.on_manager.scene.get_camera() - self.off_manager.scene.set_camera(pos, focal, vu) - self.off_manager.render() - - window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - d_type = "rgba") - - actor.SetVisibility(False) - actor.Modified() - - - # Initialization - window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - d_type = "rgba") - - callback_id = self.on_manager.add_iren_callback(gray_callback, "RenderEvent") - - self._active_effects[textured_billboard] = callback_id - self._n_active_effects += 1 - - return textured_billboard - - def laplacian(self, actor, opacity): - - - laplacian_operator = """ - const float laplacian_mat[3*3] = {0.0, 1.0, 0.0, - 1.0,-4.0, 1.0, - 0.0, 1.0, 0.0}; - - const float x_offsets[3*3] = {-1.0, 0.0, 1.0, - -1.0, 0.0, 1.0, - -1.0, 0.0, 1.0}; - - const float y_offsets[3*3] = {-1.0, -1.0, -1.0, - 0.0, 0.0, 0.0, - 1.0, 1.0, 1.0}; - """ - - lapl_dec = """ - vec4 laplacian_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ - vec4 value = vec4(0.0); - vec4 col = vec4(0.0); - for(int i = 0; i < 9; i++){ - col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); - value += vec4(laplacian_mat[i])*col; - } - return value; - } - """ - - tex_impl = """ - // Turning screen coordinates to texture coordinates - vec2 res_factor = vec2(res.y/res.x, 1.0); - vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - vec4 lapl_color = laplacian_calculator(screenTexture, renorm_tex, res); - - fragOutput0 = vec4(lapl_color.rgb, u_opacity*lapl_color.a); - """ - tex_dec = compose_shader([laplacian_operator, lapl_dec]) - - if self._n_active_effects > 0: - self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) - self.off_manager.scene.add(actor) - self.off_manager.render() - - actor_pos = np.array([actor.GetCenter()]) - actor_bounds = actor.GetBounds() - - actor_scales = np.array([actor_bounds[1] - actor_bounds[0], - actor_bounds[3] - actor_bounds[2], - 0.0]) - - scale = np.array([[actor_scales.max(), - actor_scales.max(), - 0.0]]) - - # Render to second billboard for color map post-processing. - textured_billboard = billboard(actor_pos, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) - shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) - - # Disables the texture warnings - textured_billboard.GetProperty().GlobalWarningDisplayOff() - - def laplacian_callback(obj, event): - actor.SetVisibility(True) - pos, focal, vu = self.on_manager.scene.get_camera() - self.off_manager.scene.set_camera(pos, focal, vu) - self.off_manager.render() - - window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - d_type = "rgba") - - actor.SetVisibility(False) - actor.Modified() - - # Initialization - window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - d_type = "rgba") - - callback_id = self.on_manager.add_iren_callback(laplacian_callback, "RenderEvent") - - self._active_effects[textured_billboard] = callback_id - self._n_active_effects += 1 - - return textured_billboard - - - def gaussian_blur(self, actor, opacity): - - - gaussian_kernel = """ - const float gauss_kernel[3*3] = {1/16.0, 1/8, 1/16.0, - 1/8.0, 1/4.0, 1/8.0, - 1/16.0, 1/8.0, 1/16.0}; - - const float x_offsets[3*3] = {-1.0, 0.0, 1.0, - -1.0, 0.0, 1.0, - -1.0, 0.0, 1.0}; - - const float y_offsets[3*3] = {-1.0, -1.0, -1.0, - 0.0, 0.0, 0.0, - 1.0, 1.0, 1.0}; - """ - - gauss_dec = """ - vec4 kernel_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ - vec4 value = vec4(0.0); - vec4 col = vec4(0.0); - for(int i = 0; i < 9; i++){ - col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); - value += gauss_kernel[i]*col; - } - return value; - } - """ - - tex_impl = """ - // Turning screen coordinates to texture coordinates - vec2 res_factor = vec2(res.y/res.x, 1.0); - vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - vec4 kernel_color = kernel_calculator(screenTexture, renorm_tex, res); - - fragOutput0 = vec4(kernel_color.rgb, u_opacity*kernel_color.a); - """ - tex_dec = compose_shader([gaussian_kernel, gauss_dec]) - - if self._n_active_effects > 0: - self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) - self.off_manager.scene.add(actor) - self.off_manager.render() - - actor_pos = np.array([actor.GetCenter()]) - actor_bounds = actor.GetBounds() - - actor_scales = np.array([actor_bounds[1] - actor_bounds[0], - actor_bounds[3] - actor_bounds[2], - 0.0]) - - scale = np.array([[actor_scales.max(), - actor_scales.max(), - 0.0]]) - - # Render to second billboard for color map post-processing. - textured_billboard = billboard(actor_pos, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) - shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) - - - # Disables the texture warnings - textured_billboard.GetProperty().GlobalWarningDisplayOff() - - def kernel_callback(obj, event): - actor.SetVisibility(True) - pos, focal, vu = self.on_manager.scene.get_camera() - self.off_manager.scene.set_camera(pos, focal, vu) - self.off_manager.render() - - window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - border_color=(0.0, 0.0, 0.0, 0.0), - d_type = "rgba") - - actor.SetVisibility(False) - actor.Modified() - - - # Initialization - window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - border_color=(0.0, 0.0, 0.0, 0.0), - d_type = "rgba") - - - callback_id = self.on_manager.add_iren_callback(kernel_callback, "RenderEvent") - - self._active_effects[textured_billboard] = callback_id - self._n_active_effects += 1 - - return textured_billboard - - - def remove_effect(self, effect_actor): - """Remove an existing effect from the effects manager. - Beware that the effect and the actor will be removed from the rendering pipeline - and shall not work after this action. - - Parameters - ---------- - effect_actor : actor.Actor - Actor of effect to be removed. - """ - if self._n_active_effects > 0: - self.on_manager.iren.RemoveObserver(self._active_effects[effect_actor]) - self.on_manager.scene.RemoveActor(effect_actor) - self.off_manager.scene.RemoveActor(effect_actor) - self._active_effects.pop(effect_actor) - self._n_active_effects -= 1 - else: - raise IndexError("Manager has no active effects.") - - \ No newline at end of file diff --git a/fury/actors/effect_manager_alt_2.py b/fury/actors/effect_manager_alt_2.py deleted file mode 100644 index 85fb0a3bc..000000000 --- a/fury/actors/effect_manager_alt_2.py +++ /dev/null @@ -1,909 +0,0 @@ -import os -import numpy as np -from fury.actor import Actor, billboard -from fury.colormap import create_colormap -from fury.io import load_image -from fury.lib import Texture, WindowToImageFilter, numpy_support -from fury.shaders import (attribute_to_actor, - compose_shader, - import_fury_shader, - shader_apply_effects, - shader_custom_uniforms) -from fury.utils import rgb_to_vtk -from fury.window import (gl_disable_depth, - gl_set_additive_blending, - RenderWindow, - Scene, - ShowManager) - - -WRAP_MODE_DIC = {"clamptoedge" : Texture.ClampToEdge, - "repeat" : Texture.Repeat, - "mirroredrepeat" : Texture.MirroredRepeat, - "clamptoborder" : Texture.ClampToBorder} - -BLENDING_MODE_DIC = {"none" : 0, "replace" : 1, - "modulate" : 2, "add" : 3, - "addsigned" : 4, "interpolate" : 5, - "subtract" : 6} - - - -def window_to_texture( - window : RenderWindow, - texture_name : str, - target_actor : Actor, - blending_mode : str = "None", - wrap_mode : str = "ClampToBorder", - border_color : tuple = ( - 0.0, - 0.0, - 0.0, - 1.0), - interpolate : bool = True, - d_type : str = "rgb"): - """Capture a rendered window and pass it as a texture to the given actor. - Parameters - ---------- - window : window.RenderWindow - Window to be captured. - texture_name : str - Name of the texture to be passed to the actor. - target_actor : Actor - Target actor to receive the texture. - blending_mode : str, optional - Texture blending mode. The options are: - 1. None - 2. Replace - 3. Modulate - 4. Add - 5. AddSigned - 6. Interpolate - 7. Subtract - wrap_mode : str, optional - Texture wrapping mode. The options are: - 1. ClampToEdge - 2. Repeat - 3. MirroredRepeat - 4. ClampToBorder - border_color : tuple (4, ), optional - Texture RGBA border color. - interpolate : bool, optional - Texture interpolation. - d_type : str, optional - Texture pixel type, "rgb" or "rgba". Default is "rgb" - """ - - windowToImageFilter = WindowToImageFilter() - windowToImageFilter.SetInput(window) - type_dic = {"rgb" : windowToImageFilter.SetInputBufferTypeToRGB, - "rgba" : windowToImageFilter.SetInputBufferTypeToRGBA, - "zbuffer" : windowToImageFilter.SetInputBufferTypeToZBuffer} - type_dic[d_type.lower()]() - windowToImageFilter.Update() - - texture = Texture() - texture.SetMipmap(True) - texture.SetInputConnection(windowToImageFilter.GetOutputPort()) - texture.SetBorderColor(*border_color) - texture.SetWrap(WRAP_MODE_DIC[wrap_mode.lower()]) - texture.SetInterpolate(interpolate) - texture.MipmapOn() - texture.SetBlendingMode(BLENDING_MODE_DIC[blending_mode.lower()]) - - target_actor.GetProperty().SetTexture(texture_name, texture) - - img = numpy_support.vtk_to_numpy(texture.GetInput().GetPointData().GetScalars()) - - return img - - - -def texture_to_actor( - path_to_texture : str, - texture_name : str, - target_actor : Actor, - blending_mode : str = "None", - wrap_mode : str = "ClampToBorder", - border_color : tuple = ( - 0.0, - 0.0, - 0.0, - 1.0), - interpolate : bool = True): - """Pass an imported texture to an actor. - Parameters - ---------- - path_to_texture : str - Texture image path. - texture_name : str - Name of the texture to be passed to the actor. - target_actor : Actor - Target actor to receive the texture. - blending_mode : str - Texture blending mode. The options are: - 1. None - 2. Replace - 3. Modulate - 4. Add - 5. AddSigned - 6. Interpolate - 7. Subtract - wrap_mode : str - Texture wrapping mode. The options are: - 1. ClampToEdge - 2. Repeat - 3. MirroredRepeat - 4. ClampToBorder - border_color : tuple (4, ) - Texture RGBA border color. - interpolate : bool - Texture interpolation.""" - - texture = Texture() - - colormapArray = load_image(path_to_texture) - colormapData = rgb_to_vtk(colormapArray) - - texture.SetInputDataObject(colormapData) - texture.SetBorderColor(*border_color) - texture.SetWrap(WRAP_MODE_DIC[wrap_mode.lower()]) - texture.SetInterpolate(interpolate) - texture.MipmapOn() - texture.SetBlendingMode(BLENDING_MODE_DIC[blending_mode.lower()]) - - target_actor.GetProperty().SetTexture(texture_name, texture) - - -def colormap_to_texture( - colormap : np.array, - texture_name : str, - target_actor : Actor, - interpolate : bool = True): - """Convert a colormap to a texture and pass it to an actor. - Parameters - ---------- - colormap : np.array (N, 4) or (1, N, 4) - RGBA color map array. The array can be two dimensional, although a three dimensional one is preferred. - texture_name : str - Name of the color map texture to be passed to the actor. - target_actor : Actor - Target actor to receive the color map texture. - interpolate : bool - Color map texture interpolation.""" - - if len(colormap.shape) == 2: - colormap = np.array([colormap]) - - texture = Texture() - - cmap = (255*colormap).astype(np.uint8) - cmap = rgb_to_vtk(cmap) - - texture.SetInputDataObject(cmap) - texture.SetWrap(Texture.ClampToEdge) - texture.SetInterpolate(interpolate) - texture.MipmapOn() - texture.SetBlendingMode(0) - - target_actor.GetProperty().SetTexture(texture_name, texture) - -def back_converter(h : np.ndarray): - return ((h[:, 0] + h[:, 1]/255. + h[:, 2]/65025. + h[:, 3]/16581375.)/256.0).astype(np.float32) - -class EffectManager(): - """Class that manages the application of post-processing effects on actors. - - Parameters - ---------- - manager : ShowManager - Target manager that will render post processed actors.""" - def __init__(self, manager : ShowManager): - self.scene = Scene() - cam_params = manager.scene.get_camera() - self.scene.set_camera(*cam_params) - self.on_manager = manager - self.off_manager = ShowManager(self.scene, - size=manager.size) - self.off_manager.window.SetOffScreenRendering(True) - self.off_manager.initialize() - self._n_active_effects = 0 - self._active_effects = {} - - def kde(self, - points : np.ndarray, - sigmas, - kernel : str = "gaussian", - opacity : float = 1.0, - colormap : str = "viridis", - custom_colormap : np.array = None): - """Actor that displays the Kernel Density Estimation of a given set of points. - - Parameters - ---------- - points : np.ndarray (N, 3) - Array of points to be displayed. - sigmas : np.ndarray (1, ) or (N, 1) - Array of sigmas to be used in the KDE calculations. Must be one or one for each point. - kernel : str, optional - Kernel to be used for the distribution calculation. The available options are: - * "cosine" - * "epanechnikov" - * "exponential" - * "gaussian" - * "linear" - * "tophat" - - opacity : float, optional - Opacity of the actor. - colormap : str, optional. - Colormap matplotlib name for the KDE rendering. Default is "viridis". - custom_colormap : np.ndarray (N, 4), optional - Custom colormap for the KDE rendering. Default is none which means no - custom colormap is desired. If passed, will overwrite matplotlib colormap - chosen in the previous parameter. - - Returns - ------- - textured_billboard : actor.Actor - KDE rendering actor.""" - if not isinstance(sigmas, np.ndarray): - sigmas = np.array(sigmas) - if sigmas.shape[0] != 1 and sigmas.shape[0] != points.shape[0]: - raise IndexError("sigmas size must be one or points size.") - if np.min(sigmas) <= 0: - raise ValueError("sigmas can't have zero or negative values.") - - varying_dec = """ - varying float out_sigma; - varying float out_scale; - """ - - - # converter = """ - # vec4 float_to_rgba(float value){ - # float ival = floor( value*4294967295.0 ); - # float r = floor( mod(ival, 256.0) ); - # float g = floor( ( mod(ival, 65536.0) - r ) / 256.0 ); - # float b = floor( ( mod(ival, 16777216.0) - g - r ) / 65536.0 ); - # float a = floor( ( mod(ival, 4294967296.0) - b - g - r ) / 16777216.0 ); - - # vec4 rgba = vec4(r, g, b, a)/255.0; - # return rgba; - # } - # """ - - converter = """ - vec4 float_to_rgba(float value) { - vec4 bitEnc = vec4(1.,255.,65025.,16581375.); - vec4 enc = bitEnc * value; - enc = fract(enc); - enc -= enc.yzww * vec2(1./255., 0.).xxxy; - return enc; - } - """ - - kde_dec = import_fury_shader(os.path.join("utils", f"{kernel.lower()}_distribution.glsl")) - - kde_dec = compose_shader([kde_dec, converter]) - - kde_impl = """ - float current_kde = kde(normalizedVertexMCVSOutput*out_scale, out_sigma)/n_points; - // color = vec3(current_kde); - vec4 final_color = float_to_rgba(current_kde); - fragOutput0 = vec4(final_color); - """ - - kde_vs_dec = """ - in float in_sigma; - varying float out_sigma; - - in float in_scale; - varying float out_scale; - """ - - - kde_vs_impl = """ - out_sigma = in_sigma; - out_scale = in_scale; - """ - - # de_converter = """ - # float rgba_to_float(vec4 value){ - # return (255.0* (value.r*1.0 + value.g*256.0 + value.b*65536.0 + value.a*16777216.0) ) / 4294967295.0; - # } - # """ - - de_converter = """ - float rgba_to_float(vec4 v) { - vec4 bitEnc = vec4(1.,255.,65025.,16581375.); - vec4 bitDec = 1./bitEnc; - return dot(v, bitDec); - } - """ - - gaussian_kernel = """ - const float gauss_kernel[81] = { - 0.000123, 0.000365, 0.000839, 0.001504, 0.002179, 0.002429, 0.002179, 0.001504, 0.000839, - 0.000365, 0.001093, 0.002503, 0.004494, 0.006515, 0.007273, 0.006515, 0.004494, 0.002503, - 0.000839, 0.002503, 0.005737, 0.010263, 0.014888, 0.016590, 0.014888, 0.010263, 0.005737, - 0.001504, 0.004494, 0.010263, 0.018428, 0.026753, 0.029880, 0.026753, 0.018428, 0.010263, - 0.002179, 0.006515, 0.014888, 0.026753, 0.038898, 0.043441, 0.038898, 0.026753, 0.014888, - 0.002429, 0.007273, 0.016590, 0.029880, 0.043441, 0.048489, 0.043441, 0.029880, 0.016590, - 0.002179, 0.006515, 0.014888, 0.026753, 0.038898, 0.043441, 0.038898, 0.026753, 0.014888, - 0.001504, 0.004494, 0.010263, 0.018428, 0.026753, 0.029880, 0.026753, 0.018428, 0.010263, - 0.000839, 0.002503, 0.005737, 0.010263, 0.014888, 0.016590, 0.014888, 0.010263, 0.005737}; - - const float x_offsets[81] = {-4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, - -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, - -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, - -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, - -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, - -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, - -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, - -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, - -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0}; - - const float y_offsets[81] = {-4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, -4.0, - -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, - -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, - -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, - 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, - 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, - 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0}; - """ - - gauss_dec = """ - vec4 kernel_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ - vec4 value = vec4(0.0); - vec4 col = vec4(0.0); - for(int i = 0; i < 81; i++){ - col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); - value += gauss_kernel[i]*col; - } - return value; - } - """ - - map_func = """ - float map(float value, float o_min, float o_max, float new_min, float new_max) { - return new_min + (value - o_min) * (new_max - new_min) / (o_max - o_min); - } - """ - - tex_dec = import_fury_shader(os.path.join("effects", "color_mapping.glsl")) - - tex_dec = compose_shader([tex_dec, map_func]) - - tex_impl = """ - // Turning screen coordinates to texture coordinates - vec2 res_factor = vec2(res.y/res.x, 1.0); - vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - float intensity = texture(screenTexture, renorm_tex).r; - - intensity = map(intensity, min_value, max_value, 0.0, 1.0); - - if(intensity<=0.0){ - discard; - }else{ - vec4 final_color = color_mapping(intensity, colormapTexture); - fragOutput0 = vec4(final_color.rgb, u_opacity*final_color.a); - } - """ - - gaussian_kernel_3x3 = """ - const float gauss_kernel[3*3] = {1/16.0, 1/8, 1/16.0, - 1/8.0, 1/4.0, 1/8.0, - 1/16.0, 1/8.0, 1/16.0}; - - const float x_offsets[3*3] = {-1.0, 0.0, 1.0, - -1.0, 0.0, 1.0, - -1.0, 0.0, 1.0}; - - const float y_offsets[3*3] = {-1.0, -1.0, -1.0, - 0.0, 0.0, 0.0, - 1.0, 1.0, 1.0}; - """ - - gauss_dec_3x3 = """ - vec4 kernel_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ - vec4 value = vec4(0.0); - vec4 col = vec4(0.0); - for(int i = 0; i < 3*3; i++){ - col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); - value += gauss_kernel[i]*col; - } - return value; - } - """ - - inter_dec = compose_shader([de_converter, gaussian_kernel_3x3, gauss_dec_3x3]) - - inter_impl = """ - vec2 res_factor = vec2(res.y/res.x, 1.0); - vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - - vec4 pass_color = vec4(0.0); - - if(f != 0){ - pass_color = kernel_calculator(screenTexture, renorm_tex, res); - } - else{ - pass_color = texture(screenTexture, renorm_tex); - float fintensity = rgba_to_float(pass_color); - - pass_color = vec4(vec3(fintensity), 1.0); - } - - fragOutput0 = vec4(pass_color); - """ - - fs_dec = compose_shader([varying_dec, kde_dec]) - - # Scales parameter will be defined by the empirical rule: - # 1*sima radius = 68.27% of data inside the curve - # 2*sigma radius = 95.45% of data inside the curve - # 3*sigma radius = 99.73% of data inside the curve - scales = 2*3.0*np.copy(sigmas) - - center_of_mass = np.average(points, axis = 0) - bill = billboard( - points, - (0.0, - 0.0, - 1.0), - scales=scales, - fs_dec=fs_dec, - fs_impl=kde_impl, - vs_dec=kde_vs_dec, - vs_impl=kde_vs_impl) - - # Blending and uniforms setup - window = self.off_manager.window - - shader_apply_effects(window, bill, gl_disable_depth) - shader_apply_effects(window, bill, gl_set_additive_blending) - attribute_to_actor(bill, np.repeat(sigmas, 4), "in_sigma") - attribute_to_actor(bill, np.repeat(scales, 4), "in_scale") - shader_custom_uniforms(bill, "fragment").SetUniformf("n_points", points.shape[0]) - - - if self._n_active_effects > 0: - self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) - self.off_manager.scene.add(bill) - self.off_manager.render() - - bill_bounds = bill.GetBounds() - max_sigma = 2*4.0*np.max(sigmas) - - actor_scales = np.array([[bill_bounds[1] - bill_bounds[0] + center_of_mass[0] + max_sigma, - bill_bounds[3] - bill_bounds[2] + center_of_mass[1] + max_sigma, - 0.0]]) - - res = self.off_manager.size - - scale = actor_scales.max()*np.array([[res[0]/res[1], - 1.0, - 0.0]]) - - # Render to second billboard for color map post-processing. - textured_billboard = billboard(np.array([center_of_mass]), scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) - shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", res) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) - - inter_scale = 3.4*np.array([[res[0]/res[1], 1.0, 0.0]]) # hardcoded hack to make billboards occupy the whole screen - inter_billboard = billboard(np.array([center_of_mass]), scales=inter_scale, fs_dec=inter_dec, fs_impl=inter_impl) - shader_custom_uniforms(inter_billboard, "fragment").SetUniform2f("res", res) - inter_billboard.SetVisibility(False) - inter_billboard.Modified() - inter_billboard.GetProperty().GlobalWarningDisplayOff() - self.off_manager.scene.add(inter_billboard) - - - # Disables the texture warnings - textured_billboard.GetProperty().GlobalWarningDisplayOff() - - if custom_colormap == None: - cmap = create_colormap(np.arange(0.0, 1.0, (1/points.shape[0])), colormap) - else: - cmap = custom_colormap - - colormap_to_texture(cmap, "colormapTexture", textured_billboard) - - n_passes = 2 - def kde_callback(obj, event): - # 1° STEP: RENDER ALL KDE RENDERS - bill.SetVisibility(True) - cam_params = self.on_manager.scene.get_camera() - self.off_manager.scene.set_camera(*cam_params) - self.off_manager.scene.Modified() - shader_apply_effects(window, bill, gl_disable_depth) - shader_apply_effects(window, bill, gl_set_additive_blending) - self.off_manager.render() - - # 2° STEP: PASS THIS RENDER AS A TEXTURE TO POST PROCESSING BILLBOARD - window_to_texture( - self.off_manager.window, - "screenTexture", - inter_billboard, - border_color = (0.0, 0.0, 0.0, 0.0), - blending_mode="Interpolate", - d_type = "rgba") - - # 3° STEP: GAUSSIAN BLUR APPLICATION - bill.SetVisibility(False) - bill.Modified() - - inter_billboard.SetVisibility(True) - for i in range(n_passes): - shader_custom_uniforms(inter_billboard, "fragment").SetUniformi("f", i) - self.off_manager.render() - - window_to_texture( - self.off_manager.window, - "screenTexture", - inter_billboard, - border_color = (0.0, 0.0, 0.0, 0.0), - blending_mode="Interpolate", - d_type = "rgba") - - img = window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - border_color = (0.0, 0.0, 0.0, 0.0), - blending_mode="Interpolate", - d_type = "rgba") - - inter_billboard.SetVisibility(False) - inter_billboard.Modified() - - # 4° STEP: RENORMALIZE THE RENDERING - converted_img = back_converter(img) - - max_value = np.max(converted_img) - min_value = np.min(converted_img) - print(min_value, max_value) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("min_value", min_value) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("max_value", max_value) - - # Initialization - window_to_texture( - self.off_manager.window, - "screenTexture", - inter_billboard, - border_color = (0.0, 0.0, 0.0, 0.0), - blending_mode="Interpolate", - d_type = "rgba") - - bill.SetVisibility(False) - bill.Modified() - - inter_billboard.SetVisibility(True) - for i in range(n_passes): - shader_custom_uniforms(inter_billboard, "fragment").SetUniformi("f", i) - self.off_manager.render() - - window_to_texture( - self.off_manager.window, - "screenTexture", - inter_billboard, - border_color = (0.0, 0.0, 0.0, 0.0), - blending_mode="Interpolate", - d_type = "rgba") - - - img = window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - border_color = (0.0, 0.0, 0.0, 0.0), - blending_mode="Interpolate", - d_type = "rgba") - - inter_billboard.SetVisibility(False) - inter_billboard.Modified() - - converted_img = back_converter(img) - - max_value = np.max(converted_img) - min_value = np.min(converted_img) - print(min_value, max_value) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("min_value", min_value) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("max_value", max_value) - - callback_id = self.on_manager.add_iren_callback(kde_callback, "RenderEvent") - - self._active_effects[textured_billboard] = callback_id - self._n_active_effects += 1 - - return textured_billboard - - def grayscale(self, actor, opacity): - - - tex_impl = """ - // Turning screen coordinates to texture coordinates - vec2 res_factor = vec2(res.y/res.x, 1.0); - vec2 scale_factor = vec2(u_scale); - vec2 renorm_tex = scale_factor*res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - vec4 col = texture(screenTexture, renorm_tex); - float bw = 0.2126*col.r + 0.7152*col.g + 0.0722*col.b; - - fragOutput0 = vec4(vec3(bw), u_opacity*col.a); - """ - - if self._n_active_effects > 0: - self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) - self.off_manager.scene.add(actor) - self.off_manager.render() - - actor_pos = np.array([actor.GetCenter()]) - actor_bounds = actor.GetBounds() - - actor_scales = np.array([actor_bounds[1] - actor_bounds[0], - actor_bounds[3] - actor_bounds[2], - 0.0]) - - scale = np.array([[actor_scales.max(), - actor_scales.max(), - 0.0]]) - - # Render to second billboard for color map post-processing. - textured_billboard = billboard(actor_pos, scales=scale, fs_impl=tex_impl) - shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.on_manager.size) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) - shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("u_scale", scale[0, :2]) - - # Disables the texture warnings - textured_billboard.GetProperty().GlobalWarningDisplayOff() - - def gray_callback(obj, event): - actor.SetVisibility(True) - pos, focal, vu = self.on_manager.scene.get_camera() - self.off_manager.scene.set_camera(pos, focal, vu) - self.off_manager.render() - - window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - d_type = "rgba") - - actor.SetVisibility(False) - actor.Modified() - - - # Initialization - window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - d_type = "rgba") - - callback_id = self.on_manager.add_iren_callback(gray_callback, "RenderEvent") - - self._active_effects[textured_billboard] = callback_id - self._n_active_effects += 1 - - return textured_billboard - - def laplacian(self, actor, opacity): - - - laplacian_operator = """ - const float laplacian_mat[3*3] = {0.0, 1.0, 0.0, - 1.0,-4.0, 1.0, - 0.0, 1.0, 0.0}; - - const float x_offsets[3*3] = {-1.0, 0.0, 1.0, - -1.0, 0.0, 1.0, - -1.0, 0.0, 1.0}; - - const float y_offsets[3*3] = {-1.0, -1.0, -1.0, - 0.0, 0.0, 0.0, - 1.0, 1.0, 1.0}; - """ - - lapl_dec = """ - vec4 laplacian_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ - vec4 value = vec4(0.0); - vec4 col = vec4(0.0); - for(int i = 0; i < 9; i++){ - col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); - value += vec4(laplacian_mat[i])*col; - } - return value; - } - """ - - tex_impl = """ - // Turning screen coordinates to texture coordinates - vec2 res_factor = vec2(res.y/res.x, 1.0); - vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - vec4 lapl_color = laplacian_calculator(screenTexture, renorm_tex, res); - - fragOutput0 = vec4(lapl_color.rgb, u_opacity*lapl_color.a); - """ - tex_dec = compose_shader([laplacian_operator, lapl_dec]) - - if self._n_active_effects > 0: - self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) - self.off_manager.scene.add(actor) - self.off_manager.render() - - actor_pos = np.array([actor.GetCenter()]) - actor_bounds = actor.GetBounds() - - actor_scales = np.array([actor_bounds[1] - actor_bounds[0], - actor_bounds[3] - actor_bounds[2], - 0.0]) - - scale = np.array([[actor_scales.max(), - actor_scales.max(), - 0.0]]) - - # Render to second billboard for color map post-processing. - textured_billboard = billboard(actor_pos, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) - shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) - - # Disables the texture warnings - textured_billboard.GetProperty().GlobalWarningDisplayOff() - - def laplacian_callback(obj, event): - actor.SetVisibility(True) - pos, focal, vu = self.on_manager.scene.get_camera() - self.off_manager.scene.set_camera(pos, focal, vu) - self.off_manager.render() - - window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - d_type = "rgba") - - actor.SetVisibility(False) - actor.Modified() - - # Initialization - window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - d_type = "rgba") - - callback_id = self.on_manager.add_iren_callback(laplacian_callback, "RenderEvent") - - self._active_effects[textured_billboard] = callback_id - self._n_active_effects += 1 - - return textured_billboard - - - def gaussian_blur(self, actor, opacity): - - - gaussian_kernel = """ - const float gauss_kernel[3*3] = {1/16.0, 1/8, 1/16.0, - 1/8.0, 1/4.0, 1/8.0, - 1/16.0, 1/8.0, 1/16.0}; - - const float x_offsets[3*3] = {-1.0, 0.0, 1.0, - -1.0, 0.0, 1.0, - -1.0, 0.0, 1.0}; - - const float y_offsets[3*3] = {-1.0, -1.0, -1.0, - 0.0, 0.0, 0.0, - 1.0, 1.0, 1.0}; - """ - - gauss_dec = """ - vec4 kernel_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ - vec4 value = vec4(0.0); - vec4 col = vec4(0.0); - for(int i = 0; i < 9; i++){ - col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); - value += gauss_kernel[i]*col; - } - return value; - } - """ - - tex_impl = """ - // Turning screen coordinates to texture coordinates - vec2 res_factor = vec2(res.y/res.x, 1.0); - vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - vec4 kernel_color = kernel_calculator(screenTexture, renorm_tex, res); - - fragOutput0 = vec4(kernel_color.rgb, u_opacity*kernel_color.a); - """ - tex_dec = compose_shader([gaussian_kernel, gauss_dec]) - - if self._n_active_effects > 0: - self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) - self.off_manager.scene.add(actor) - self.off_manager.render() - - actor_pos = np.array([actor.GetCenter()]) - actor_bounds = actor.GetBounds() - - actor_scales = np.array([actor_bounds[1] - actor_bounds[0], - actor_bounds[3] - actor_bounds[2], - 0.0]) - - scale = np.array([[actor_scales.max(), - actor_scales.max(), - 0.0]]) - - # Render to second billboard for color map post-processing. - textured_billboard = billboard(actor_pos, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) - shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) - - - # Disables the texture warnings - textured_billboard.GetProperty().GlobalWarningDisplayOff() - - def kernel_callback(obj, event): - actor.SetVisibility(True) - pos, focal, vu = self.on_manager.scene.get_camera() - self.off_manager.scene.set_camera(pos, focal, vu) - self.off_manager.render() - - window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - border_color=(0.0, 0.0, 0.0, 0.0), - d_type = "rgba") - - actor.SetVisibility(False) - actor.Modified() - - - # Initialization - window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - border_color=(0.0, 0.0, 0.0, 0.0), - d_type = "rgba") - - - callback_id = self.on_manager.add_iren_callback(kernel_callback, "RenderEvent") - - self._active_effects[textured_billboard] = callback_id - self._n_active_effects += 1 - - return textured_billboard - - - def remove_effect(self, effect_actor): - """Remove an existing effect from the effects manager. - Beware that the effect and the actor will be removed from the rendering pipeline - and shall not work after this action. - - Parameters - ---------- - effect_actor : actor.Actor - Actor of effect to be removed. - """ - if self._n_active_effects > 0: - self.on_manager.iren.RemoveObserver(self._active_effects[effect_actor]) - self.on_manager.scene.RemoveActor(effect_actor) - self.off_manager.scene.RemoveActor(effect_actor) - self._active_effects.pop(effect_actor) - self._n_active_effects -= 1 - else: - raise IndexError("Manager has no active effects.") - - \ No newline at end of file From bacae2eb1cded2bc25fe2b400b1f5ce0982baa49 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Tue, 1 Aug 2023 16:05:17 -0300 Subject: [PATCH 23/41] fix: Improved ui and cleaned the code (deleted other effects) --- docs/experimental/float_to_rgb.py | 71 ------- fury/actors/effect_manager.py | 296 +++--------------------------- 2 files changed, 21 insertions(+), 346 deletions(-) delete mode 100644 docs/experimental/float_to_rgb.py diff --git a/docs/experimental/float_to_rgb.py b/docs/experimental/float_to_rgb.py deleted file mode 100644 index 90a322869..000000000 --- a/docs/experimental/float_to_rgb.py +++ /dev/null @@ -1,71 +0,0 @@ -import numpy as np -i = 255 + 255*256 + 255*256**2 + 255*256**3 -i = 0.000000001 -j = 0.000000001 -f = int(i*256*256*256*256 - 1) -g = int(j*256*256*256*256 - 1) -print("f:", f) - -def converter(n): - f = int(n*256*256*256*256 - 1) - c = np.zeros(4, dtype = float) - c[0] = f % 256 - c[1] = float((f % 256**2 - c[0]) // 256) - c[2] = float((f % 256**3 - c[1] - c[0]) // 256**2) - c[3] = float((f % 256**4 - c[2] - c[1] - c[0]) // 256**3) - - return c/255 - -def de_converter(h): - return (255*(h[0] + h[1]*256 + h[2]*256**2 + h[3]*256**3) + 1.0)/(256*256*256*256) - -c = converter(i) -d = converter(j) -# print(f, g) -# print(i) -# print(np.array(c)) -de = de_converter(c + d) -# print(int(de*256*256*256*256 - 1)) - - - -def gaussian_kernel(n, sigma = 1.0): - x0 = np.arange(0.0, 1.0, 1/n) - y0 = np.arange(0.0, 1.0, 1/n) - x, y = np.meshgrid(x0, y0) - center = np.array([x0[n // 2], y0[n // 2]]) - mesh = np.stack((x, y), 2) - center = np.repeat(center, x.shape[0]*y.shape[0]).reshape(x.shape[0], y.shape[0], 2) - kernel = np.exp((-1.0*np.linalg.norm(center - mesh, axis = 2)**2)/(2*sigma**2)) - string = f"const float gauss_kernel[{x.shape[0]*y.shape[0]}] = " - kernel = kernel/np.sum(kernel) - flat = str(kernel.flatten()).split(" ") - copy_flat = flat.copy() - taken = 0 - for i in range(len(flat)): - if flat[i] == ' ' or flat[i] == '': - copy_flat.pop(i - taken) - taken += 1 - if "[" in copy_flat[0]: - copy_flat[0] = copy_flat[0][1:] - else: - copy_flat.pop(0) - - if "]" in copy_flat[-1]: - copy_flat[-1] = copy_flat[-1][:-1] - else: - copy_flat.pop(-1) - - if '' == copy_flat[0]: - copy_flat.pop(0) - - if '' == copy_flat[-1]: - copy_flat.pop(-1) - - # copy_flat.pop(-1) - print(copy_flat) - - string += "{" + ", ".join(copy_flat) + "};" - return string - -print(gaussian_kernel(13, 3.0)) \ No newline at end of file diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index 48037103e..c6be7cc09 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -9,7 +9,7 @@ import_fury_shader, shader_apply_effects, shader_custom_uniforms) -from fury.ui import LineSlider2D +from fury.ui import LineSlider2D, TextBlock2D, Panel2D from fury.utils import rgb_to_vtk from fury.window import (gl_disable_depth, gl_set_additive_blending, @@ -200,6 +200,7 @@ def __init__(self, manager : ShowManager): self.off_manager.initialize() self._n_active_effects = 0 self._active_effects = {} + self._intensity = 1.0 def kde(self, points : np.ndarray, @@ -313,7 +314,7 @@ def kde(self, shader_apply_effects(window, bill, gl_disable_depth) shader_apply_effects(window, bill, gl_set_additive_blending) - attribute_to_actor(bill, np.repeat(sigmas, 4), "in_sigma") + attribute_to_actor(bill, self._intensity*np.repeat(sigmas, 4), "in_sigma") attribute_to_actor(bill, np.repeat(scales, 4), "in_scale") if self._n_active_effects > 0: @@ -354,6 +355,8 @@ def kde_callback(obj = None, event = None): self.off_manager.scene.Modified() shader_apply_effects(window, bill, gl_disable_depth) shader_apply_effects(window, bill, gl_set_additive_blending) + attribute_to_actor(bill, self._intensity*np.repeat(sigmas, 4), "in_sigma") + bill.Modified() self.off_manager.render() window_to_texture( @@ -369,22 +372,31 @@ def kde_callback(obj = None, event = None): minv = 1 initv = 1000 maxv = 2000 - offset = 150 + offset = 25 + text_template = lambda slider: f'{(slider.value/initv):.2f} ({slider.ratio:.0%})' line_slider = LineSlider2D(center = (res[0] - offset, 0 + (res[1]/res[0])*offset), initial_value = initv, min_value = minv, max_value = maxv, text_alignment='bottom', - orientation = 'horizontal') - + orientation = 'horizontal', + text_template = text_template) + + line_slider.size + text_block = TextBlock2D("Intensity") + panel_size = (line_slider.size[0] + text_block.size[0], 2*line_slider.size[1] + text_block.size[1]) + panel = Panel2D(size = (line_slider.size[0] + text_block.size[0], 2*line_slider.size[1] + text_block.size[1]), + position = (res[0] - panel_size[0] - offset, 0 + panel_size[1] + offset), + color = (1, 1, 1), opacity = 0.1, align = 'right') + panel.add_element(line_slider, (0.38, 0.5)) + panel.add_element(text_block, (0.1, 0.5)) + def intensity_change(slider): - intensity = slider.value/initv - attribute_to_actor(bill, intensity*np.repeat(sigmas, 4), "in_sigma") - bill.Modified() + self._intensity = slider.value/initv kde_callback() line_slider.on_moving_slider = intensity_change - self.on_manager.scene.add(line_slider) + self.on_manager.scene.add(panel) callback_id = self.on_manager.add_iren_callback(kde_callback, "RenderEvent") @@ -393,272 +405,6 @@ def intensity_change(slider): return textured_billboard - def grayscale(self, actor, opacity): - - - tex_impl = """ - // Turning screen coordinates to texture coordinates - vec2 res_factor = vec2(res.y/res.x, 1.0); - vec2 scale_factor = vec2(u_scale); - vec2 renorm_tex = scale_factor*res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - vec4 col = texture(screenTexture, renorm_tex); - float bw = 0.2126*col.r + 0.7152*col.g + 0.0722*col.b; - - fragOutput0 = vec4(vec3(bw), u_opacity*col.a); - """ - - if self._n_active_effects > 0: - self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) - self.off_manager.scene.add(actor) - self.off_manager.render() - - actor_pos = np.array([actor.GetCenter()]) - actor_bounds = actor.GetBounds() - - actor_scales = np.array([actor_bounds[1] - actor_bounds[0], - actor_bounds[3] - actor_bounds[2], - 0.0]) - - scale = np.array([[actor_scales.max(), - actor_scales.max(), - 0.0]]) - - # Render to second billboard for color map post-processing. - textured_billboard = billboard(actor_pos, scales=scale, fs_impl=tex_impl) - shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) - shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("u_scale", scale[0, :2]) - - # Disables the texture warnings - textured_billboard.GetProperty().GlobalWarningDisplayOff() - - def gray_callback(obj, event): - actor.SetVisibility(True) - pos, focal, vu = self.on_manager.scene.get_camera() - self.off_manager.scene.set_camera(pos, focal, vu) - self.off_manager.render() - - window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - d_type = "rgba") - - actor.SetVisibility(False) - actor.Modified() - - - # Initialization - window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - d_type = "rgba") - - callback_id = self.on_manager.add_iren_callback(gray_callback, "RenderEvent") - - self._active_effects[textured_billboard] = callback_id - self._n_active_effects += 1 - - return textured_billboard - - def laplacian(self, actor, opacity): - - - laplacian_operator = """ - const float laplacian_mat[3*3] = {0.0, 1.0, 0.0, - 1.0,-4.0, 1.0, - 0.0, 1.0, 0.0}; - - const float x_offsets[3*3] = {-1.0, 0.0, 1.0, - -1.0, 0.0, 1.0, - -1.0, 0.0, 1.0}; - - const float y_offsets[3*3] = {-1.0, -1.0, -1.0, - 0.0, 0.0, 0.0, - 1.0, 1.0, 1.0}; - """ - - lapl_dec = """ - vec4 laplacian_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ - vec4 value = vec4(0.0); - vec4 col = vec4(0.0); - for(int i = 0; i < 9; i++){ - col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); - value += vec4(laplacian_mat[i])*col; - } - return value; - } - """ - - tex_impl = """ - // Turning screen coordinates to texture coordinates - vec2 res_factor = vec2(res.y/res.x, 1.0); - vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - vec4 lapl_color = laplacian_calculator(screenTexture, renorm_tex, res); - - fragOutput0 = vec4(lapl_color.rgb, u_opacity*lapl_color.a); - """ - tex_dec = compose_shader([laplacian_operator, lapl_dec]) - - if self._n_active_effects > 0: - self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) - self.off_manager.scene.add(actor) - self.off_manager.render() - - actor_pos = np.array([actor.GetCenter()]) - actor_bounds = actor.GetBounds() - - actor_scales = np.array([actor_bounds[1] - actor_bounds[0], - actor_bounds[3] - actor_bounds[2], - 0.0]) - - scale = np.array([[actor_scales.max(), - actor_scales.max(), - 0.0]]) - - # Render to second billboard for color map post-processing. - textured_billboard = billboard(actor_pos, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) - shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) - - # Disables the texture warnings - textured_billboard.GetProperty().GlobalWarningDisplayOff() - - def laplacian_callback(obj, event): - actor.SetVisibility(True) - pos, focal, vu = self.on_manager.scene.get_camera() - self.off_manager.scene.set_camera(pos, focal, vu) - self.off_manager.render() - - window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - d_type = "rgba") - - actor.SetVisibility(False) - actor.Modified() - - # Initialization - window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - d_type = "rgba") - - callback_id = self.on_manager.add_iren_callback(laplacian_callback, "RenderEvent") - - self._active_effects[textured_billboard] = callback_id - self._n_active_effects += 1 - - return textured_billboard - - - def gaussian_blur(self, actor, opacity): - - - gaussian_kernel = """ - const float gauss_kernel[3*3] = {1/16.0, 1/8, 1/16.0, - 1/8.0, 1/4.0, 1/8.0, - 1/16.0, 1/8.0, 1/16.0}; - - const float x_offsets[3*3] = {-1.0, 0.0, 1.0, - -1.0, 0.0, 1.0, - -1.0, 0.0, 1.0}; - - const float y_offsets[3*3] = {-1.0, -1.0, -1.0, - 0.0, 0.0, 0.0, - 1.0, 1.0, 1.0}; - """ - - gauss_dec = """ - vec4 kernel_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ - vec4 value = vec4(0.0); - vec4 col = vec4(0.0); - for(int i = 0; i < 9; i++){ - col = texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i])); - value += gauss_kernel[i]*col; - } - return value; - } - """ - - tex_impl = """ - // Turning screen coordinates to texture coordinates - vec2 res_factor = vec2(res.y/res.x, 1.0); - vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - vec4 kernel_color = kernel_calculator(screenTexture, renorm_tex, res); - - fragOutput0 = vec4(kernel_color.rgb, u_opacity*kernel_color.a); - """ - tex_dec = compose_shader([gaussian_kernel, gauss_dec]) - - if self._n_active_effects > 0: - self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) - self.off_manager.scene.add(actor) - self.off_manager.render() - - actor_pos = np.array([actor.GetCenter()]) - actor_bounds = actor.GetBounds() - - actor_scales = np.array([actor_bounds[1] - actor_bounds[0], - actor_bounds[3] - actor_bounds[2], - 0.0]) - - scale = np.array([[actor_scales.max(), - actor_scales.max(), - 0.0]]) - - # Render to second billboard for color map post-processing. - textured_billboard = billboard(actor_pos, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) - shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.off_manager.size) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) - - - # Disables the texture warnings - textured_billboard.GetProperty().GlobalWarningDisplayOff() - - def kernel_callback(obj, event): - actor.SetVisibility(True) - pos, focal, vu = self.on_manager.scene.get_camera() - self.off_manager.scene.set_camera(pos, focal, vu) - self.off_manager.render() - - window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - border_color=(0.0, 0.0, 0.0, 0.0), - d_type = "rgba") - - actor.SetVisibility(False) - actor.Modified() - - - # Initialization - window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - border_color=(0.0, 0.0, 0.0, 0.0), - d_type = "rgba") - - - callback_id = self.on_manager.add_iren_callback(kernel_callback, "RenderEvent") - - self._active_effects[textured_billboard] = callback_id - self._n_active_effects += 1 - - return textured_billboard - - def remove_effect(self, effect_actor): """Remove an existing effect from the effects manager. Beware that the effect and the actor will be removed from the rendering pipeline From 80acf71454489c58a07c6bff58a2cfaecf40aeae Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Tue, 1 Aug 2023 16:27:21 -0300 Subject: [PATCH 24/41] fix: Added ui tracking inside class --- docs/experimental/viz_kde_class.py | 2 +- fury/actors/effect_manager.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/experimental/viz_kde_class.py b/docs/experimental/viz_kde_class.py index 1b089dd98..510de75b0 100644 --- a/docs/experimental/viz_kde_class.py +++ b/docs/experimental/viz_kde_class.py @@ -30,7 +30,7 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int width, height = (1200, 1000) scene = Scene() -scene.set_camera(position=(-6, 5, -10), +scene.set_camera(position=(-24, 20, -40), focal_point=(0.0, 0.0, 0.0), diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index c6be7cc09..21c151de1 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -200,6 +200,7 @@ def __init__(self, manager : ShowManager): self.off_manager.initialize() self._n_active_effects = 0 self._active_effects = {} + self._active_ui = {} self._intensity = 1.0 def kde(self, @@ -381,7 +382,6 @@ def kde_callback(obj = None, event = None): orientation = 'horizontal', text_template = text_template) - line_slider.size text_block = TextBlock2D("Intensity") panel_size = (line_slider.size[0] + text_block.size[0], 2*line_slider.size[1] + text_block.size[1]) panel = Panel2D(size = (line_slider.size[0] + text_block.size[0], 2*line_slider.size[1] + text_block.size[1]), @@ -401,6 +401,7 @@ def intensity_change(slider): callback_id = self.on_manager.add_iren_callback(kde_callback, "RenderEvent") self._active_effects[textured_billboard] = callback_id + self._active_ui[textured_billboard] = panel.actors self._n_active_effects += 1 return textured_billboard @@ -418,6 +419,9 @@ def remove_effect(self, effect_actor): if self._n_active_effects > 0: self.on_manager.iren.RemoveObserver(self._active_effects[effect_actor]) self.on_manager.scene.RemoveActor(effect_actor) + ui_actors = self._active_ui[effect_actor] + for i in range(len(ui_actors)): + self.on_manager.scene.RemoveActor(ui_actors[i]) self.off_manager.scene.RemoveActor(effect_actor) self._active_effects.pop(effect_actor) self._n_active_effects -= 1 From 449095001b0b6e7ef3218592785e72ed79010b09 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Wed, 2 Aug 2023 11:11:17 -0300 Subject: [PATCH 25/41] fix: Fixed formatting issues and removed UI --- docs/experimental/viz_kde_class.py | 10 +- fury/actors/effect_manager.py | 154 ++++++++---------- fury/shaders/utils/cosine_distribution.glsl | 12 +- .../utils/epanechnikov_distribution.glsl | 11 +- .../utils/exponential_distribution.glsl | 9 +- fury/shaders/utils/gaussian_distribution.glsl | 12 +- fury/shaders/utils/linear_distribution.glsl | 11 +- fury/shaders/utils/tophat_distribution.glsl | 11 +- 8 files changed, 80 insertions(+), 150 deletions(-) diff --git a/docs/experimental/viz_kde_class.py b/docs/experimental/viz_kde_class.py index 510de75b0..dbb87e979 100644 --- a/docs/experimental/viz_kde_class.py +++ b/docs/experimental/viz_kde_class.py @@ -3,6 +3,7 @@ from fury.actors.effect_manager import EffectManager from fury.window import Scene, ShowManager, record + def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int = 0): """Convert an array to a given desired range. @@ -48,21 +49,20 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int n_points = 1000 points = np.random.rand(n_points, 3) points = normalize(points, -5, 5) -sigmas = normalize(np.random.rand(n_points, 1), 0.1, 0.6) +sigmas = normalize(np.random.rand(n_points, 1), 0.05, 0.2) offset = np.array([0.0, 0.0, 0.0]) points = points + np.tile(offset, points.shape[0]).reshape(points.shape) effects = EffectManager(manager) -kde_actor = effects.kde(points, sigmas, kernel = "exponential", colormap = "inferno") +kde_actor = effects.kde(points, sigmas, kernel="gaussian", colormap="inferno") manager.scene.add(kde_actor) -# effects.remove_effect(kde_actor) interactive = True if interactive: manager.start() - -record(scene, out_path = "kde_points.png", size = (800, 800)) + +record(scene, out_path="kde_points.png", size=(800, 800)) diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index 21c151de1..eac73bbdb 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -9,7 +9,6 @@ import_fury_shader, shader_apply_effects, shader_custom_uniforms) -from fury.ui import LineSlider2D, TextBlock2D, Panel2D from fury.utils import rgb_to_vtk from fury.window import (gl_disable_depth, gl_set_additive_blending, @@ -28,6 +27,7 @@ "addsigned" : 4, "interpolate" : 5, "subtract" : 6} + def window_to_texture( window : RenderWindow, texture_name : str, @@ -42,6 +42,7 @@ def window_to_texture( interpolate : bool = True, d_type : str = "rgb"): """Capture a rendered window and pass it as a texture to the given actor. + Parameters ---------- window : window.RenderWindow @@ -70,12 +71,12 @@ def window_to_texture( interpolate : bool, optional Texture interpolation. d_type : str, optional - Texture pixel type, "rgb" or "rgba". Default is "rgb" + Texture pixel type, "rgb" or "rgba". Default is "rgb" """ windowToImageFilter = WindowToImageFilter() windowToImageFilter.SetInput(window) - type_dic = {"rgb" : windowToImageFilter.SetInputBufferTypeToRGB, + type_dic = {"rgb" : windowToImageFilter.SetInputBufferTypeToRGB, "rgba" : windowToImageFilter.SetInputBufferTypeToRGBA, "zbuffer" : windowToImageFilter.SetInputBufferTypeToZBuffer} type_dic[d_type.lower()]() @@ -106,6 +107,7 @@ def texture_to_actor( 1.0), interpolate : bool = True): """Pass an imported texture to an actor. + Parameters ---------- path_to_texture : str @@ -133,7 +135,7 @@ def texture_to_actor( Texture RGBA border color. interpolate : bool Texture interpolation.""" - + texture = Texture() colormapArray = load_image(path_to_texture) @@ -155,6 +157,7 @@ def colormap_to_texture( target_actor : Actor, interpolate : bool = True): """Convert a colormap to a texture and pass it to an actor. + Parameters ---------- colormap : np.array (N, 4) or (1, N, 4) @@ -182,6 +185,7 @@ def colormap_to_texture( target_actor.GetProperty().SetTexture(texture_name, texture) + class EffectManager(): """Class that manages the application of post-processing effects on actors. @@ -189,29 +193,28 @@ class EffectManager(): ---------- manager : ShowManager Target manager that will render post processed actors.""" + def __init__(self, manager : ShowManager): self.scene = Scene() cam_params = manager.scene.get_camera() self.scene.set_camera(*cam_params) self.on_manager = manager - self.off_manager = ShowManager(self.scene, - size=manager.size) + self.off_manager = ShowManager(self.scene, + size=manager.size) self.off_manager.window.SetOffScreenRendering(True) self.off_manager.initialize() self._n_active_effects = 0 self._active_effects = {} - self._active_ui = {} - self._intensity = 1.0 - def kde(self, - points : np.ndarray, - sigmas, + def kde(self, + points : np.ndarray, + sigmas, kernel : str = "gaussian", - opacity : float = 1.0, - colormap : str = "viridis", + opacity : float = 1.0, + colormap : str = "viridis", custom_colormap : np.array = None): """Actor that displays the Kernel Density Estimation of a given set of points. - + Parameters ---------- points : np.ndarray (N, 3) @@ -232,10 +235,10 @@ def kde(self, colormap : str, optional. Colormap matplotlib name for the KDE rendering. Default is "viridis". custom_colormap : np.ndarray (N, 4), optional - Custom colormap for the KDE rendering. Default is none which means no - custom colormap is desired. If passed, will overwrite matplotlib colormap + Custom colormap for the KDE rendering. Default is none which means no + custom colormap is desired. If passed, will overwrite matplotlib colormap chosen in the previous parameter. - + Returns ------- textured_billboard : actor.Actor @@ -252,7 +255,10 @@ def kde(self, varying float out_scale; """ - kde_dec = import_fury_shader(os.path.join("utils", f"{kernel.lower()}_distribution.glsl")) + kde_dec = import_fury_shader( + os.path.join( + "utils", + f"{kernel.lower()}_distribution.glsl")) kde_impl = """ float current_kde = kde(normalizedVertexMCVSOutput*out_scale, out_sigma); @@ -268,7 +274,6 @@ def kde(self, varying float out_scale; """ - kde_vs_impl = """ out_sigma = in_sigma; out_scale = in_scale; @@ -284,7 +289,7 @@ def kde(self, if(intensity<=0.0){ discard; - }else{ + }else{ vec4 final_color = color_mapping(intensity, colormapTexture); fragOutput0 = vec4(final_color.rgb, u_opacity*final_color.a); } @@ -292,30 +297,30 @@ def kde(self, fs_dec = compose_shader([varying_dec, kde_dec]) - # Scales parameter will be defined by the empirical rule: - # 1*sima radius = 68.27% of data inside the curve - # 2*sigma radius = 95.45% of data inside the curve - # 3*sigma radius = 99.73% of data inside the curve + """Scales parameter will be defined by the empirical rule: + 1*sima radius = 68.27% of data inside the curve + 2*sigma radius = 95.45% of data inside the curve + 3*sigma radius = 99.73% of data inside the curve""" scales = 2*3.0*np.copy(sigmas) - center_of_mass = np.average(points, axis = 0) + center_of_mass = np.average(points, axis=0) bill = billboard( - points, - (0.0, - 0.0, - 1.0), - scales=scales, - fs_dec=fs_dec, - fs_impl=kde_impl, - vs_dec=kde_vs_dec, - vs_impl=kde_vs_impl) + points, + (0.0, + 0.0, + 1.0), + scales=scales, + fs_dec=fs_dec, + fs_impl=kde_impl, + vs_dec=kde_vs_dec, + vs_impl=kde_vs_impl) # Blending and uniforms setup window = self.off_manager.window shader_apply_effects(window, bill, gl_disable_depth) shader_apply_effects(window, bill, gl_set_additive_blending) - attribute_to_actor(bill, self._intensity*np.repeat(sigmas, 4), "in_sigma") + attribute_to_actor(bill, np.repeat(sigmas, 4), "in_sigma") attribute_to_actor(bill, np.repeat(scales, 4), "in_scale") if self._n_active_effects > 0: @@ -325,90 +330,64 @@ def kde(self, bill_bounds = bill.GetBounds() max_sigma = 2*4.0*np.max(sigmas) - actor_scales = np.array([[bill_bounds[1] - bill_bounds[0] + center_of_mass[0] + max_sigma, - bill_bounds[3] - bill_bounds[2] + center_of_mass[1] + max_sigma, - 0.0]]) - - scale = np.array([[actor_scales.max(), + actor_scales = np.array([[bill_bounds[1] - bill_bounds[0] + + center_of_mass[0] + max_sigma, + bill_bounds[3] - bill_bounds[2] + + center_of_mass[1] + max_sigma, 0.0]]) + + scale = np.array([[actor_scales.max(), actor_scales.max(), 0.0]]) - + res = self.off_manager.size # Render to second billboard for color map post-processing. - textured_billboard = billboard(np.array([center_of_mass]), scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) + textured_billboard = billboard( + np.array([center_of_mass]), + scales=scale, + fs_dec=tex_dec, + fs_impl=tex_impl) shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", res) shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) # Disables the texture warnings - textured_billboard.GetProperty().GlobalWarningDisplayOff() + textured_billboard.GetProperty().GlobalWarningDisplayOff() - if custom_colormap == None: + if custom_colormap is None: cmap = create_colormap(np.arange(0.0, 1.0, 1/256), colormap) else: cmap = custom_colormap - colormap_to_texture(cmap, "colormapTexture", textured_billboard) + colormap_to_texture(cmap, "colormapTexture", textured_billboard) - def kde_callback(obj = None, event = None): + def kde_callback(obj=None, event=None): cam_params = self.on_manager.scene.get_camera() self.off_manager.scene.set_camera(*cam_params) self.off_manager.scene.Modified() shader_apply_effects(window, bill, gl_disable_depth) shader_apply_effects(window, bill, gl_set_additive_blending) - attribute_to_actor(bill, self._intensity*np.repeat(sigmas, 4), "in_sigma") - bill.Modified() self.off_manager.render() window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - d_type = "rgba") + self.off_manager.window, + "screenTexture", + textured_billboard, + blending_mode="Interpolate", + d_type="rgba") # Initialization kde_callback() - minv = 1 - initv = 1000 - maxv = 2000 - offset = 25 - text_template = lambda slider: f'{(slider.value/initv):.2f} ({slider.ratio:.0%})' - line_slider = LineSlider2D(center = (res[0] - offset, 0 + (res[1]/res[0])*offset), - initial_value = initv, - min_value = minv, max_value = maxv, - text_alignment='bottom', - orientation = 'horizontal', - text_template = text_template) - - text_block = TextBlock2D("Intensity") - panel_size = (line_slider.size[0] + text_block.size[0], 2*line_slider.size[1] + text_block.size[1]) - panel = Panel2D(size = (line_slider.size[0] + text_block.size[0], 2*line_slider.size[1] + text_block.size[1]), - position = (res[0] - panel_size[0] - offset, 0 + panel_size[1] + offset), - color = (1, 1, 1), opacity = 0.1, align = 'right') - panel.add_element(line_slider, (0.38, 0.5)) - panel.add_element(text_block, (0.1, 0.5)) - - def intensity_change(slider): - self._intensity = slider.value/initv - kde_callback() - - line_slider.on_moving_slider = intensity_change - - self.on_manager.scene.add(panel) - callback_id = self.on_manager.add_iren_callback(kde_callback, "RenderEvent") self._active_effects[textured_billboard] = callback_id - self._active_ui[textured_billboard] = panel.actors self._n_active_effects += 1 return textured_billboard - + def remove_effect(self, effect_actor): - """Remove an existing effect from the effects manager. - Beware that the effect and the actor will be removed from the rendering pipeline + """Remove an existing effect from the effects manager. + Beware that the effect and the actor will be removed from the rendering pipeline and shall not work after this action. Parameters @@ -419,13 +398,8 @@ def remove_effect(self, effect_actor): if self._n_active_effects > 0: self.on_manager.iren.RemoveObserver(self._active_effects[effect_actor]) self.on_manager.scene.RemoveActor(effect_actor) - ui_actors = self._active_ui[effect_actor] - for i in range(len(ui_actors)): - self.on_manager.scene.RemoveActor(ui_actors[i]) self.off_manager.scene.RemoveActor(effect_actor) self._active_effects.pop(effect_actor) self._n_active_effects -= 1 else: raise IndexError("Manager has no active effects.") - - \ No newline at end of file diff --git a/fury/shaders/utils/cosine_distribution.glsl b/fury/shaders/utils/cosine_distribution.glsl index 85c6b4558..73976d2ab 100644 --- a/fury/shaders/utils/cosine_distribution.glsl +++ b/fury/shaders/utils/cosine_distribution.glsl @@ -1,12 +1,4 @@ // This assumes the center of the normal distribution is the center of the screen -#define PI 3.1415926 float kde(vec3 point, float sigma){ - float norm = (PI/(4.0*sigma)); - return norm*cos(PI*length(point)/(2*sigma))*int(length(point) < sigma); -} - - -// This requires a center to be passed -// float kde(vec3 point, vec3 center, float sigma){ -// return cos(PI*length(center - point)/(2*sigma))*int(length(center - point) < sigma); -// } \ No newline at end of file + return cos(PI*length(point)/(2*sigma))*int(length(point) < sigma); +} \ No newline at end of file diff --git a/fury/shaders/utils/epanechnikov_distribution.glsl b/fury/shaders/utils/epanechnikov_distribution.glsl index 17feb52ef..816fe54d8 100644 --- a/fury/shaders/utils/epanechnikov_distribution.glsl +++ b/fury/shaders/utils/epanechnikov_distribution.glsl @@ -1,11 +1,4 @@ // This assumes the center of the normal distribution is the center of the screen float kde(vec3 point, float sigma){ - float norm = (3.0/(4.0*sigma)); - return norm*(1.0 - (length(point)*length(point))/(sigma*sigma)); -} - - -// This requires a center to be passed -// float kde(vec3 point, vec3 center, float sigma){ -// return 1.0 - (length(center - point)*length(center - point))/(sigma*sigma); -// } \ No newline at end of file + return (1.0 - (length(point)*length(point))/(sigma*sigma)); +} \ No newline at end of file diff --git a/fury/shaders/utils/exponential_distribution.glsl b/fury/shaders/utils/exponential_distribution.glsl index 417305f69..00ae49a92 100644 --- a/fury/shaders/utils/exponential_distribution.glsl +++ b/fury/shaders/utils/exponential_distribution.glsl @@ -1,11 +1,4 @@ // This assumes the center of the normal distribution is the center of the screen -#define E 2.7182818 float kde(vec3 point, float sigma){ return exp(-1.0*length(point)/sigma); -} - - -// This requires a center to be passed -// float kde(vec3 point, vec3 center, float sigma){ -// return exp(-1.0*length(center - point)/sigma); -// } \ No newline at end of file +} \ No newline at end of file diff --git a/fury/shaders/utils/gaussian_distribution.glsl b/fury/shaders/utils/gaussian_distribution.glsl index 7d1087986..40710c5a6 100644 --- a/fury/shaders/utils/gaussian_distribution.glsl +++ b/fury/shaders/utils/gaussian_distribution.glsl @@ -1,12 +1,4 @@ // This assumes the center of the normal distribution is the center of the screen -#define PI 3.1415926 float kde(vec3 point, float sigma){ - float norm = (1/(sigma*sqrt(2.0*PI))); - return norm*exp(-1.0*pow(length(point), 2.0)/(2.0*sigma*sigma) ); -} - - -// This requires a center to be passed -// float kde(vec3 point, vec3 center, float sigma){ -// return (1/(sigma*sqrt(2.0*PI)))*exp(-1.0*pow(length(center - point), 2.0)/(2.0*sigma*sigma) ); -// } \ No newline at end of file + return exp(-1.0*pow(length(point), 2.0)/(2.0*sigma*sigma) ); +} \ No newline at end of file diff --git a/fury/shaders/utils/linear_distribution.glsl b/fury/shaders/utils/linear_distribution.glsl index 4d7bfa874..290b7fba4 100644 --- a/fury/shaders/utils/linear_distribution.glsl +++ b/fury/shaders/utils/linear_distribution.glsl @@ -1,11 +1,4 @@ // This assumes the center of the normal distribution is the center of the screen float kde(vec3 point, float sigma){ - float norm = (1.0/sigma); - return norm*(1.0 - length(point)/sigma)*int(length(point) < sigma); -} - - -// This requires a center to be passed -// float kde(vec3 point, vec3 center, float sigma){ -// return (1.0 - length(center - point)/sigma)*int(length(center - point) < sigma); -// } \ No newline at end of file + return (1.0 - length(point)/sigma)*int(length(point) < sigma); +} \ No newline at end of file diff --git a/fury/shaders/utils/tophat_distribution.glsl b/fury/shaders/utils/tophat_distribution.glsl index 972c1f3db..1edb23689 100644 --- a/fury/shaders/utils/tophat_distribution.glsl +++ b/fury/shaders/utils/tophat_distribution.glsl @@ -1,11 +1,4 @@ // This assumes the center of the normal distribution is the center of the screen float kde(vec3 point, float sigma){ - float norm = (1.0/sigma*2.0); - return norm*int(length(point) < sigma); -} - - -// This requires a center to be passed -// float kde(vec3 point, vec3 center, float sigma){ -// return 1.0*int(length(center - point) < sigma); -// } \ No newline at end of file + return int(length(point) < sigma); +} \ No newline at end of file From 51ae7a6714839bbe377c4ec1490f491242db61f8 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Thu, 3 Aug 2023 14:20:45 -0300 Subject: [PATCH 26/41] feat: Initial draft of testing script --- fury/actors/effect_manager.py | 65 +-------- fury/actors/tests/test_effect_manager.py | 167 +++++++++++++++++++++++ 2 files changed, 170 insertions(+), 62 deletions(-) create mode 100644 fury/actors/tests/test_effect_manager.py diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index eac73bbdb..ebff6474b 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -2,7 +2,6 @@ import numpy as np from fury.actor import Actor, billboard from fury.colormap import create_colormap -from fury.io import load_image from fury.lib import Texture, WindowToImageFilter from fury.shaders import (attribute_to_actor, compose_shader, @@ -93,64 +92,6 @@ def window_to_texture( target_actor.GetProperty().SetTexture(texture_name, texture) - -def texture_to_actor( - path_to_texture : str, - texture_name : str, - target_actor : Actor, - blending_mode : str = "None", - wrap_mode : str = "ClampToBorder", - border_color : tuple = ( - 0.0, - 0.0, - 0.0, - 1.0), - interpolate : bool = True): - """Pass an imported texture to an actor. - - Parameters - ---------- - path_to_texture : str - Texture image path. - texture_name : str - Name of the texture to be passed to the actor. - target_actor : Actor - Target actor to receive the texture. - blending_mode : str - Texture blending mode. The options are: - 1. None - 2. Replace - 3. Modulate - 4. Add - 5. AddSigned - 6. Interpolate - 7. Subtract - wrap_mode : str - Texture wrapping mode. The options are: - 1. ClampToEdge - 2. Repeat - 3. MirroredRepeat - 4. ClampToBorder - border_color : tuple (4, ) - Texture RGBA border color. - interpolate : bool - Texture interpolation.""" - - texture = Texture() - - colormapArray = load_image(path_to_texture) - colormapData = rgb_to_vtk(colormapArray) - - texture.SetInputDataObject(colormapData) - texture.SetBorderColor(*border_color) - texture.SetWrap(WRAP_MODE_DIC[wrap_mode.lower()]) - texture.SetInterpolate(interpolate) - texture.MipmapOn() - texture.SetBlendingMode(BLENDING_MODE_DIC[blending_mode.lower()]) - - target_actor.GetProperty().SetTexture(texture_name, texture) - - def colormap_to_texture( colormap : np.array, texture_name : str, @@ -380,7 +321,7 @@ def kde_callback(obj=None, event=None): callback_id = self.on_manager.add_iren_callback(kde_callback, "RenderEvent") - self._active_effects[textured_billboard] = callback_id + self._active_effects[textured_billboard] = (callback_id, bill) self._n_active_effects += 1 return textured_billboard @@ -396,9 +337,9 @@ def remove_effect(self, effect_actor): Actor of effect to be removed. """ if self._n_active_effects > 0: - self.on_manager.iren.RemoveObserver(self._active_effects[effect_actor]) + self.on_manager.iren.RemoveObserver(self._active_effects[effect_actor][0]) + self.off_manager.scene.RemoveActor(self._active_effects[effect_actor][1]) self.on_manager.scene.RemoveActor(effect_actor) - self.off_manager.scene.RemoveActor(effect_actor) self._active_effects.pop(effect_actor) self._n_active_effects -= 1 else: diff --git a/fury/actors/tests/test_effect_manager.py b/fury/actors/tests/test_effect_manager.py new file mode 100644 index 000000000..883d63173 --- /dev/null +++ b/fury/actors/tests/test_effect_manager.py @@ -0,0 +1,167 @@ +import numpy as np +import numpy.testing as npt + +from fury.actors.effect_manager import EffectManager +from fury.window import Scene, ShowManager +from fury import window + +width, height = (400, 400) + +points = np.array([[0.36600749, 0.65827962, 0.53083986], + [0.97657922, 0.78730041, 0.13946709], + [0.7441061 , 0.26322696, 0.8683115 ], + [0.14606987, 0.05490296, 0.98723486], + [0.71673873, 0.29188497, 0.02825102], + [0.90364963, 0.06387054, 0.91557011], + [0.11106939, 0.73972495, 0.49771819], + [0.63509055, 0.26659524, 0.4790886 ], + [0.20590893, 0.56012136, 0.78304187], + [0.30247726, 0.28023438, 0.6883304 ], + [0.58971475, 0.67312749, 0.47656539], + [0.26257592, 0.23964672, 0.64210249], + [0.26631165, 0.35701288, 0.88390072], + [0.01108113, 0.87276217, 0.99321825], + [0.68792169, 0.42725589, 0.92290326], + [0.09702907, 0.69950028, 0.97210289], + [0.86744636, 0.29614399, 0.2729772 ], + [0.77511449, 0.6912353 , 0.97596621], + [0.5919642 , 0.25713794, 0.0692452 ], + [0.47674521, 0.94254354, 0.71231971], + [0.50177591, 0.19320157, 0.91493713], + [0.27073903, 0.58171665, 0.79582017], + [0.76282237, 0.35119548, 0.80971555], + [0.43065933, 0.87678895, 0.57491155], + [0.34213045, 0.70619672, 0.43970999], + [0.38793158, 0.33048163, 0.91679507], + [0.68375111, 0.47934201, 0.86197378], + [0.67829585, 0.80616031, 0.76974334], + [0.01784785, 0.24857252, 0.89913317], + [0.8458996, 0.51551657, 0.69597985]]) + +sigmas = np.array([[0.56193862], [0.1275334 ], [0.91069059], + [0.01177131], [0.67799239], [0.95772282], + [0.55834784], [0.60151661], [0.25946789], + [0.88343075], [0.24011991], [0.05879632], + [0.6370561 ], [0.23859789], [0.18654873], + [0.70008281], [0.02968318], [0.01304724], + [0.08251756], [0.625351 ], [0.89982588], + [0.62378987], [0.8661594 ], [0.05583442], + [0.60157791], [0.84737657], [0.36433019], + [0.13263502], [0.30937519], [0.88979053]]) + + +def test_window_to_texture(interactive = False): + pass + +def test_colormap_to_actor(interactive = False): + pass + +def test_effect_manager_setup(interactive = False): + width, height = (400, 400) + + scene = Scene() + scene.set_camera(position=(-24, 20, -40), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + + manager = ShowManager( + scene, + "demo", + (width, + height)) + + manager.initialize() + + em = EffectManager(manager) + + npt.assert_equal(True, em.scene.get_camera() == manager.scene.get_camera()) + npt.assert_equal(True, manager == em.on_manager) + npt.assert_array_equal(manager.window.GetSize(), em.off_manager.window.GetSize()) + npt.assert_equal(True, em.scene == em.off_manager.scene) + npt.assert_equal(True, em.off_manager.window.GetOffScreenRendering()) + npt.assert_equal(0, em._n_active_effects) + npt.assert_equal({}, em._active_effects) + +def test_kde_actor(interactive = False): + scene = window.Scene() + scene.set_camera(position=(-24, 20, -40), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + + manager = window.ShowManager( + scene, + "demo", + (width, + height)) + + manager.initialize() + + em = EffectManager(manager) + + + kde_actor = em.kde(points, sigmas, colormap="inferno") + + manager.scene.add(kde_actor) + + if interactive: + window.show(manager.scene) + + off_arr = window.snapshot(em.off_manager.scene) + off_rs = window.analyze_snapshot(off_arr) + off_ascene = window.analyze_scene(em.off_manager.scene) + + on_arr = window.snapshot(manager.scene) + on_rs = window.analyze_snapshot(on_arr) + on_ascene = window.analyze_scene(manager.scene) + + on_obs = em.on_manager.iren.HasObserver("RenderEvent") + + npt.assert_equal(1, off_rs.objects) + npt.assert_equal(1, off_ascene.actors) + npt.assert_equal(1, em._n_active_effects) + npt.assert_equal(1, on_obs) + npt.assert_equal(1, on_rs.objects) + npt.assert_equal(1, on_ascene.actors) + + +def test_remove_effect(interactive = False): + scene = window.Scene() + scene.set_camera(position=(-24, 20, -40), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + + manager = window.ShowManager( + scene, + "demo", + (width, + height)) + + manager.initialize() + + em = EffectManager(manager) + + kde_actor = em.kde(points, sigmas, colormap="inferno") + + manager.scene.add(kde_actor) + em.remove_effect(kde_actor) + + if interactive: + window.show(manager.scene) + + off_ascene = window.analyze_scene(em.off_manager.scene) + on_ascene = window.analyze_scene(manager.scene) + + npt.assert_equal(0, em.on_manager.iren.HasObserver("RenderEvent")) + npt.assert_equal(0, on_ascene.actors) + npt.assert_equal(0, off_ascene.actors) + npt.assert_equal({}, em._active_effects) + npt.assert_equal(0, em._n_active_effects) + + + \ No newline at end of file From e1532970f55997b24d86df462f480a51845ccc81 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Thu, 3 Aug 2023 16:14:48 -0300 Subject: [PATCH 27/41] test: Completed testing scripts --- fury/actors/effect_manager.py | 57 ++++++ fury/actors/tests/test_effect_manager.py | 248 ++++++++++++++++++----- fury/shaders/base.py | 4 +- fury/tests/test_actors.py | 82 ++++++++ 4 files changed, 341 insertions(+), 50 deletions(-) diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index ebff6474b..6a5dfcf2c 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -2,6 +2,7 @@ import numpy as np from fury.actor import Actor, billboard from fury.colormap import create_colormap +from fury.io import load_image from fury.lib import Texture, WindowToImageFilter from fury.shaders import (attribute_to_actor, compose_shader, @@ -87,6 +88,62 @@ def window_to_texture( texture.SetBorderColor(*border_color) texture.SetWrap(WRAP_MODE_DIC[wrap_mode.lower()]) texture.SetInterpolate(interpolate) + texture.SetBlendingMode(BLENDING_MODE_DIC[blending_mode.lower()]) + + target_actor.GetProperty().SetTexture(texture_name, texture) + + +def texture_to_actor( + path_to_texture : str, + texture_name : str, + target_actor : Actor, + blending_mode : str = "None", + wrap_mode : str = "ClampToBorder", + border_color : tuple = ( + 0.0, + 0.0, + 0.0, + 1.0), + interpolate : bool = True): + """Pass an imported texture to an actor. + + Parameters + ---------- + path_to_texture : str + Texture image path. + texture_name : str + Name of the texture to be passed to the actor. + target_actor : Actor + Target actor to receive the texture. + blending_mode : str + Texture blending mode. The options are: + 1. None + 2. Replace + 3. Modulate + 4. Add + 5. AddSigned + 6. Interpolate + 7. Subtract + wrap_mode : str + Texture wrapping mode. The options are: + 1. ClampToEdge + 2. Repeat + 3. MirroredRepeat + 4. ClampToBorder + border_color : tuple (4, ) + Texture RGBA border color. + interpolate : bool + Texture interpolation.""" + + texture = Texture() + + textureArray = load_image(path_to_texture) + textureData = rgb_to_vtk(textureArray) + + texture.SetInputDataObject(textureData) + texture.SetBorderColor(*border_color) + texture.SetWrap(WRAP_MODE_DIC[wrap_mode.lower()]) + texture.SetInterpolate(interpolate) texture.MipmapOn() texture.SetBlendingMode(BLENDING_MODE_DIC[blending_mode.lower()]) diff --git a/fury/actors/tests/test_effect_manager.py b/fury/actors/tests/test_effect_manager.py index 883d63173..57f5153c3 100644 --- a/fury/actors/tests/test_effect_manager.py +++ b/fury/actors/tests/test_effect_manager.py @@ -1,12 +1,33 @@ import numpy as np import numpy.testing as npt - -from fury.actors.effect_manager import EffectManager -from fury.window import Scene, ShowManager +import os + +from fury.actor import (billboard, + cube) +from fury.actors.effect_manager import (colormap_to_texture, + EffectManager, + texture_to_actor, + window_to_texture) +from fury.colormap import create_colormap +from fury.lib import Texture +from fury.shaders import shader_custom_uniforms, import_fury_shader +from fury.window import (Scene, + ShowManager, + record) from fury import window width, height = (400, 400) +WRAP_MODE_DIC = {"clamptoedge" : Texture.ClampToEdge, + "repeat" : Texture.Repeat, + "mirroredrepeat" : Texture.MirroredRepeat, + "clamptoborder" : Texture.ClampToBorder} + +BLENDING_MODE_DIC = {"none" : 0, "replace" : 1, + "modulate" : 2, "add" : 3, + "addsigned" : 4, "interpolate" : 5, + "subtract" : 6} + points = np.array([[0.36600749, 0.65827962, 0.53083986], [0.97657922, 0.78730041, 0.13946709], [0.7441061 , 0.26322696, 0.8683115 ], @@ -51,82 +72,215 @@ def test_window_to_texture(interactive = False): - pass + fs_impl = """ + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + vec4 tex = texture(screenTexture, renorm_tex); + fragOutput0 = vec4(tex); + """ + + on_scene = window.Scene() + on_scene.set_camera(position=(-2, 1, -2), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) -def test_colormap_to_actor(interactive = False): - pass + on_manager = window.ShowManager( + on_scene, + "on_manager", + (width, + height)) -def test_effect_manager_setup(interactive = False): - width, height = (400, 400) + off_scene = window.Scene() + off_scene.set_camera(position=(-4, 2, -4), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) - scene = Scene() - scene.set_camera(position=(-24, 20, -40), + off_manager = window.ShowManager( + off_scene, + "off_manager", + (width, + height)) + + off_manager.window.SetOffScreenRendering(True) + off_manager.initialize() + + scale = np.array([[width/height, 1.0, 0.0]]) + c = cube(np.array([[0.0, 0.0, 0.0]]), colors=(1.0, 1.0, 0.0)) + bill = billboard(np.array([[0.0, 0.0, 0.0]]), scales = scale, fs_impl=fs_impl) + shader_custom_uniforms(bill, "fragment").SetUniform2f("res", off_manager.size) + + off_manager.scene.add(c) + on_manager.scene.add(bill) + + tex_name = "screenTexture" + blending_mode = "Interpolate" + dtype = "RGBA" + wrap_mode = "ClampToBorder" + border_color = (0.0, 0.0, 0.0, 1.0) + interpolate = True + + off_manager.render() + window_to_texture(off_manager.window, + tex_name, + bill, + blending_mode=blending_mode, + wrap_mode=wrap_mode, + border_color=border_color, + interpolate=interpolate, + d_type=dtype) + + if interactive: + window.show(on_manager.scene) + + n_textures = bill.GetProperty().GetNumberOfTextures() + texture = bill.GetProperty().GetTexture(tex_name) + + npt.assert_equal(1, n_textures) + npt.assert_equal(WRAP_MODE_DIC[wrap_mode.lower()], texture.GetWrap()) + npt.assert_equal(BLENDING_MODE_DIC[blending_mode.lower()], texture.GetBlendingMode()) + npt.assert_array_almost_equal(list(border_color), texture.GetBorderColor()) + npt.assert_equal(interpolate, texture.GetInterpolate()) + npt.assert_equal(True, texture.GetMipmap()) + + record(on_manager.scene, out_path="test_window.png", size=(width, height)) + +def test_texture_to_actor(interactive = False): + fs_impl = """ + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + vec4 tex = texture(screenTexture, renorm_tex); + fragOutput0 = vec4(tex); + """ + + on_scene = window.Scene() + on_scene.set_camera(position=(-2, 1, -2), focal_point=(0.0, 0.0, 0.0), view_up=(0.0, 0.0, 0.0)) - manager = ShowManager( - scene, - "demo", + on_manager = window.ShowManager( + on_scene, + "on_manager", (width, height)) - manager.initialize() + scale = np.array([[width/height, 1.0, 0.0]]) + bill = billboard(np.array([[0.0, 0.0, 0.0]]), scales = scale, fs_impl=fs_impl) + shader_custom_uniforms(bill, "fragment").SetUniform2f("res", on_manager.size) + + on_manager.scene.add(bill) + + tex_name = "screenTexture" + blending_mode = "Interpolate" + dtype = "RGBA" + wrap_mode = "ClampToBorder" + border_color = (0.0, 0.0, 0.0, 1.0) + interpolate = True + + texture_to_actor("test_window.png", + tex_name, + bill, + blending_mode=blending_mode, + wrap_mode=wrap_mode, + border_color=border_color, + interpolate=interpolate) + + if interactive: + window.show(on_manager.scene) + + n_textures = bill.GetProperty().GetNumberOfTextures() + texture = bill.GetProperty().GetTexture(tex_name) + + npt.assert_equal(1, n_textures) + npt.assert_equal(WRAP_MODE_DIC[wrap_mode.lower()], texture.GetWrap()) + npt.assert_equal(BLENDING_MODE_DIC[blending_mode.lower()], texture.GetBlendingMode()) + npt.assert_array_almost_equal(list(border_color), texture.GetBorderColor()) + npt.assert_equal(interpolate, texture.GetInterpolate()) + npt.assert_equal(True, texture.GetMipmap()) + +def test_colormap_to_actor(interactive = False): - em = EffectManager(manager) + fs_dec = import_fury_shader(os.path.join("effects", "color_mapping.glsl")) - npt.assert_equal(True, em.scene.get_camera() == manager.scene.get_camera()) - npt.assert_equal(True, manager == em.on_manager) - npt.assert_array_equal(manager.window.GetSize(), em.off_manager.window.GetSize()) - npt.assert_equal(True, em.scene == em.off_manager.scene) - npt.assert_equal(True, em.off_manager.window.GetOffScreenRendering()) - npt.assert_equal(0, em._n_active_effects) - npt.assert_equal({}, em._active_effects) + fs_impl = """ + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + float intensity = renorm_tex.x; + vec4 tex = color_mapping(intensity, colormapTexture); + fragOutput0 = vec4(tex); + """ -def test_kde_actor(interactive = False): - scene = window.Scene() - scene.set_camera(position=(-24, 20, -40), + on_scene = window.Scene() + on_scene.set_camera(position=(-2, 1, -2), focal_point=(0.0, 0.0, 0.0), view_up=(0.0, 0.0, 0.0)) - manager = window.ShowManager( - scene, - "demo", + on_manager = window.ShowManager( + on_scene, + "on_manager", (width, height)) - manager.initialize() + scale = 3.4*np.array([[width/height, 1.0, 0.0]]) + bill = billboard(np.array([[0.0, 0.0, 0.0]]), scales = scale, fs_impl=fs_impl, fs_dec=fs_dec) + shader_custom_uniforms(bill, "fragment").SetUniform2f("res", on_manager.size) - em = EffectManager(manager) + on_manager.scene.add(bill) + tex_name = "colormapTexture" + interpolate = True - kde_actor = em.kde(points, sigmas, colormap="inferno") - - manager.scene.add(kde_actor) + colormap_to_texture(create_colormap(np.arange(0.0, 1.0, 1/256), "viridis"), + tex_name, + bill, + interpolate) + if interactive: - window.show(manager.scene) + window.show(on_manager.scene) - off_arr = window.snapshot(em.off_manager.scene) - off_rs = window.analyze_snapshot(off_arr) - off_ascene = window.analyze_scene(em.off_manager.scene) + n_textures = bill.GetProperty().GetNumberOfTextures() + texture = bill.GetProperty().GetTexture(tex_name) - on_arr = window.snapshot(manager.scene) - on_rs = window.analyze_snapshot(on_arr) - on_ascene = window.analyze_scene(manager.scene) + npt.assert_equal(1, n_textures) + npt.assert_equal(WRAP_MODE_DIC["ClampToEdge".lower()], texture.GetWrap()) + npt.assert_equal(BLENDING_MODE_DIC["None".lower()], texture.GetBlendingMode()) + npt.assert_equal(interpolate, texture.GetInterpolate()) + npt.assert_equal(True, texture.GetMipmap()) - on_obs = em.on_manager.iren.HasObserver("RenderEvent") +def test_effect_manager_setup(interactive = False): - npt.assert_equal(1, off_rs.objects) - npt.assert_equal(1, off_ascene.actors) - npt.assert_equal(1, em._n_active_effects) - npt.assert_equal(1, on_obs) - npt.assert_equal(1, on_rs.objects) - npt.assert_equal(1, on_ascene.actors) + scene = Scene() + scene.set_camera(position=(-24, 20, -40), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + manager = ShowManager( + scene, + "demo", + (width, + height)) + + manager.initialize() + + em = EffectManager(manager) + + npt.assert_equal(True, em.scene.get_camera() == manager.scene.get_camera()) + npt.assert_equal(True, manager == em.on_manager) + npt.assert_array_equal(manager.window.GetSize(), em.off_manager.window.GetSize()) + npt.assert_equal(True, em.scene == em.off_manager.scene) + npt.assert_equal(True, em.off_manager.window.GetOffScreenRendering()) + npt.assert_equal(0, em._n_active_effects) + npt.assert_equal({}, em._active_effects) def test_remove_effect(interactive = False): scene = window.Scene() diff --git a/fury/shaders/base.py b/fury/shaders/base.py index 6d1c825b1..7e64e5488 100644 --- a/fury/shaders/base.py +++ b/fury/shaders/base.py @@ -430,6 +430,4 @@ def shader_custom_uniforms(actor, shader_type): "fragment" : actor.GetShaderProperty().GetFragmentCustomUniforms(), "geometry" : actor.GetShaderProperty().GetGeometryCustomUniforms()} - - - return SHADER_FUNCTIONS[shader_type] + return SHADER_FUNCTIONS[shader_type.lower()] diff --git a/fury/tests/test_actors.py b/fury/tests/test_actors.py index 8d4722bdb..a704c93b9 100644 --- a/fury/tests/test_actors.py +++ b/fury/tests/test_actors.py @@ -13,6 +13,7 @@ from fury.actor import grid from fury.decorators import skip_linux, skip_osx, skip_win from fury.deprecator import ExpiredDeprecationError +from fury.actors.effect_manager import EffectManager # Allow import, but disable doctests if we don't have dipy from fury.optpkg import optional_package @@ -1819,3 +1820,84 @@ def test_actors_primitives_count(): primitives_count = test_case[2] act = act_func(**args) npt.assert_equal(primitives_count_from_actor(act), primitives_count) + +def test_kde_actor(interactive = True): + width, height = (400, 400) + scene = window.Scene() + scene.set_camera(position=(-24, 20, -40), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + + manager = window.ShowManager( + scene, + "demo", + (width, + height)) + + manager.initialize() + + em = EffectManager(manager) + + points = 5.0*np.array([[0.36600749, 0.65827962, 0.53083986], + [0.97657922, 0.78730041, 0.13946709], + [0.7441061 , 0.26322696, 0.8683115 ], + [0.14606987, 0.05490296, 0.98723486], + [0.71673873, 0.29188497, 0.02825102], + [0.90364963, 0.06387054, 0.91557011], + [0.11106939, 0.73972495, 0.49771819], + [0.63509055, 0.26659524, 0.4790886 ], + [0.20590893, 0.56012136, 0.78304187], + [0.30247726, 0.28023438, 0.6883304 ], + [0.58971475, 0.67312749, 0.47656539], + [0.26257592, 0.23964672, 0.64210249], + [0.26631165, 0.35701288, 0.88390072], + [0.01108113, 0.87276217, 0.99321825], + [0.68792169, 0.42725589, 0.92290326], + [0.09702907, 0.69950028, 0.97210289], + [0.86744636, 0.29614399, 0.2729772 ], + [0.77511449, 0.6912353 , 0.97596621], + [0.5919642 , 0.25713794, 0.0692452 ], + [0.47674521, 0.94254354, 0.71231971], + [0.50177591, 0.19320157, 0.91493713], + [0.27073903, 0.58171665, 0.79582017], + [0.76282237, 0.35119548, 0.80971555], + [0.43065933, 0.87678895, 0.57491155], + [0.34213045, 0.70619672, 0.43970999], + [0.38793158, 0.33048163, 0.91679507], + [0.68375111, 0.47934201, 0.86197378], + [0.67829585, 0.80616031, 0.76974334], + [0.01784785, 0.24857252, 0.89913317], + [0.8458996, 0.51551657, 0.69597985]]) + + sigmas = np.array([[0.56193862], [0.1275334 ], [0.91069059], + [0.01177131], [0.67799239], [0.95772282], + [0.55834784], [0.60151661], [0.25946789], + [0.88343075], [0.24011991], [0.05879632], + [0.6370561 ], [0.23859789], [0.18654873], + [0.70008281], [0.02968318], [0.01304724], + [0.08251756], [0.625351 ], [0.89982588], + [0.62378987], [0.8661594 ], [0.05583442], + [0.60157791], [0.84737657], [0.36433019], + [0.13263502], [0.30937519], [0.88979053]]) + + kde_actor = em.kde(points, sigmas, colormap="inferno") + + manager.scene.add(kde_actor) + + if interactive: + window.show(manager.scene) + + off_arr = window.snapshot(em.off_manager.scene) + off_ascene = window.analyze_scene(em.off_manager.scene) + + on_arr = window.snapshot(manager.scene) + on_ascene = window.analyze_scene(manager.scene) + + on_obs = em.on_manager.iren.HasObserver("RenderEvent") + + npt.assert_equal(1, off_ascene.actors) + npt.assert_equal(1, em._n_active_effects) + npt.assert_equal(1, on_obs) + npt.assert_equal(1, on_ascene.actors) From 10bcb90cf0f1a8836b3ed33bd86e6610b64acee1 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Thu, 3 Aug 2023 16:51:13 -0300 Subject: [PATCH 28/41] refactor: Written and relocated file to examples --- docs/examples/_valid_examples.toml | 3 +- docs/examples/viz_kde_render.py | 112 +++++++++++++++++++++++++++++ docs/experimental/viz_kde_class.py | 68 ------------------ 3 files changed, 114 insertions(+), 69 deletions(-) create mode 100644 docs/examples/viz_kde_render.py delete mode 100644 docs/experimental/viz_kde_class.py diff --git a/docs/examples/_valid_examples.toml b/docs/examples/_valid_examples.toml index 2eb4d2a5a..3b6786301 100644 --- a/docs/examples/_valid_examples.toml +++ b/docs/examples/_valid_examples.toml @@ -58,7 +58,8 @@ files = [ "viz_pbr_interactive.py", "viz_emwave_animation.py", "viz_helical_motion.py", - "viz_play_video.py" + "viz_play_video.py", + "viz_kde_render.py" ] [ui] diff --git a/docs/examples/viz_kde_render.py b/docs/examples/viz_kde_render.py new file mode 100644 index 000000000..780380875 --- /dev/null +++ b/docs/examples/viz_kde_render.py @@ -0,0 +1,112 @@ +""" +====================================================================== +Fury Kernel Density Estimation rendering Actor +====================================================================== +This example shows how to use the KDE actor. This is a special actor in Fury that works +with post-processing effects to render kernel density estimations of a given set of points +in real-time to the screen. For better understanding on KDEs, check this +`Wikipedia page `_ about it. + +For this example, you will only need the modules below: +""" +import numpy as np + +from fury.actors.effect_manager import EffectManager +from fury.window import Scene, ShowManager, record + +##################################################################################### +# This function below will help us to relocate the points for a better visualization, +# but it is not required. + +def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int = 0): + """Convert an array to a given desired range. + + Parameters + ---------- + array : np.ndarray + Array to be normalized. + min : float + Bottom value of the interval of normalization. If no value is given, it is passed as 0.0. + max : float + Upper value of the interval of normalization. If no value is given, it is passed as 1.0. + + Returns + ------- + array : np.array + Array converted to the given desired range. + """ + if np.max(array) != np.min(array): + return ((array - np.min(array))/(np.max(array) - np.min(array)))*(max - min) + min + else: + raise ValueError( + "Can't normalize an array which maximum and minimum value are the same.") + +################################################################## +# First, we need to setup the screen we will render the points to. + +width, height = (1200, 1000) + +scene = Scene() +scene.set_camera(position=(-24, 20, -40), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + +manager = ShowManager( + scene, + "demo", + (width, + height)) + +manager.initialize() + +#################################################################### +# `numpy.random.rand` will be used to generate random points, which +# will be then relocated with the function we declared below to the +# range of [-5.0, 5.0], so they get more space between them. In case +# offsetted points are wanted, it can be done just as below. + +n_points = 1000 +points = np.random.rand(n_points, 3) +points = normalize(points, -5, 5) +offset = np.array([0.0, 0.0, 0.0]) +points = points + np.tile(offset, points.shape[0]).reshape(points.shape) + +################################################################### +# For this KDE render, we will use a set of random sigmas as well, +# generated with `numpy.random.rand` as well, which are also +# remapped to the range of [0.05, 0.2]. + +sigmas = normalize(np.random.rand(n_points, 1), 0.05, 0.2) + + +################################################################### +# Now, for the KDE render, a special class is needed, the +# EffectManager. This class is needed to manage the post-processing +# aspect of this kind of render, as it will need to first be +# rendered to an offscreen buffer, retrieved and then processed +# by the final actor that will render it to the screen, but don't +# worry, none of this will need to be setup by you! Just call the +# EffectManager like below, passing the manager to it: + +effects = EffectManager(manager) + +################################################################### +# After having the `effects` setup, just call the kde actor method +# from it, passing the points, sigma, and other optional options +# if wished, like the kernel to be used or the colormap desirred. +# The colormaps are by default taken from matplotlib, but a +# custom one can be passed. After calling it, just pass the actor +# to the scene, and start it as usual. + +kde_actor = effects.kde(points, sigmas, kernel="gaussian", colormap="inferno") + +manager.scene.add(kde_actor) + +interactive = True + +if interactive: + manager.start() + +record(scene, out_path="kde_points.png", size=(800, 800)) diff --git a/docs/experimental/viz_kde_class.py b/docs/experimental/viz_kde_class.py deleted file mode 100644 index dbb87e979..000000000 --- a/docs/experimental/viz_kde_class.py +++ /dev/null @@ -1,68 +0,0 @@ -import numpy as np - -from fury.actors.effect_manager import EffectManager -from fury.window import Scene, ShowManager, record - - -def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int = 0): - """Convert an array to a given desired range. - - Parameters - ---------- - array : np.ndarray - Array to be normalized. - min : float - Bottom value of the interval of normalization. If no value is given, it is passed as 0.0. - max : float - Upper value of the interval of normalization. If no value is given, it is passed as 1.0. - - Returns - ------- - array : np.array - Array converted to the given desired range. - """ - if np.max(array) != np.min(array): - return ((array - np.min(array))/(np.max(array) - np.min(array)))*(max - min) + min - else: - raise ValueError( - "Can't normalize an array which maximum and minimum value are the same.") - - -width, height = (1200, 1000) - -scene = Scene() -scene.set_camera(position=(-24, 20, -40), - focal_point=(0.0, - 0.0, - 0.0), - view_up=(0.0, 0.0, 0.0)) - -manager = ShowManager( - scene, - "demo", - (width, - height)) - -manager.initialize() - - -n_points = 1000 -points = np.random.rand(n_points, 3) -points = normalize(points, -5, 5) -sigmas = normalize(np.random.rand(n_points, 1), 0.05, 0.2) -offset = np.array([0.0, 0.0, 0.0]) -points = points + np.tile(offset, points.shape[0]).reshape(points.shape) - -effects = EffectManager(manager) - -kde_actor = effects.kde(points, sigmas, kernel="gaussian", colormap="inferno") - - -manager.scene.add(kde_actor) - -interactive = True - -if interactive: - manager.start() - -record(scene, out_path="kde_points.png", size=(800, 800)) From e4612b500ed7a40dac88730458c76ebf6944d865 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Fri, 4 Aug 2023 12:03:01 -0300 Subject: [PATCH 29/41] style: Formatted some codes to pep8 and improved example text --- docs/examples/viz_kde_render.py | 18 +- fury/actors/effect_manager.py | 4 +- fury/actors/tests/test_effect_manager.py | 224 ++++++++++++----------- 3 files changed, 124 insertions(+), 122 deletions(-) diff --git a/docs/examples/viz_kde_render.py b/docs/examples/viz_kde_render.py index 780380875..952d1d851 100644 --- a/docs/examples/viz_kde_render.py +++ b/docs/examples/viz_kde_render.py @@ -62,9 +62,9 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int manager.initialize() #################################################################### -# `numpy.random.rand` will be used to generate random points, which +# ``numpy.random.rand`` will be used to generate random points, which # will be then relocated with the function we declared below to the -# range of [-5.0, 5.0], so they get more space between them. In case +# range of ``[-5.0, 5.0]``, so they get more space between them. In case # offsetted points are wanted, it can be done just as below. n_points = 1000 @@ -75,28 +75,28 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int ################################################################### # For this KDE render, we will use a set of random sigmas as well, -# generated with `numpy.random.rand` as well, which are also -# remapped to the range of [0.05, 0.2]. +# generated with ``numpy.random.rand`` as well, which are also +# remapped to the range of ``[0.05, 0.2]``. sigmas = normalize(np.random.rand(n_points, 1), 0.05, 0.2) ################################################################### # Now, for the KDE render, a special class is needed, the -# EffectManager. This class is needed to manage the post-processing +# ``EffectManager``. This class is needed to manage the post-processing # aspect of this kind of render, as it will need to first be # rendered to an offscreen buffer, retrieved and then processed # by the final actor that will render it to the screen, but don't # worry, none of this will need to be setup by you! Just call the -# EffectManager like below, passing the manager to it: +# ``EffectManager`` like below, passing the manager to it: effects = EffectManager(manager) ################################################################### -# After having the `effects` setup, just call the kde actor method +# After having the ``effects`` setup, just call the kde actor method # from it, passing the points, sigma, and other optional options -# if wished, like the kernel to be used or the colormap desirred. -# The colormaps are by default taken from matplotlib, but a +# if wished, like the kernel to be used or the colormap desired. +# The colormaps are by default taken from *matplotlib*, but a # custom one can be passed. After calling it, just pass the actor # to the scene, and start it as usual. diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index 6a5dfcf2c..2a95d7df5 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -254,9 +254,7 @@ def kde(self, """ kde_dec = import_fury_shader( - os.path.join( - "utils", - f"{kernel.lower()}_distribution.glsl")) + os.path.join("utils", f"{kernel.lower()}_distribution.glsl")) kde_impl = """ float current_kde = kde(normalizedVertexMCVSOutput*out_scale, out_sigma); diff --git a/fury/actors/tests/test_effect_manager.py b/fury/actors/tests/test_effect_manager.py index 57f5153c3..0f833fe2f 100644 --- a/fury/actors/tests/test_effect_manager.py +++ b/fury/actors/tests/test_effect_manager.py @@ -2,17 +2,18 @@ import numpy.testing as npt import os -from fury.actor import (billboard, +from fury.actor import (billboard, cube) -from fury.actors.effect_manager import (colormap_to_texture, - EffectManager, +from fury.actors.effect_manager import (colormap_to_texture, + EffectManager, texture_to_actor, window_to_texture) from fury.colormap import create_colormap from fury.lib import Texture -from fury.shaders import shader_custom_uniforms, import_fury_shader -from fury.window import (Scene, - ShowManager, +from fury.shaders import (shader_custom_uniforms, + import_fury_shader) +from fury.window import (Scene, + ShowManager, record) from fury import window @@ -29,49 +30,49 @@ "subtract" : 6} points = np.array([[0.36600749, 0.65827962, 0.53083986], - [0.97657922, 0.78730041, 0.13946709], - [0.7441061 , 0.26322696, 0.8683115 ], - [0.14606987, 0.05490296, 0.98723486], - [0.71673873, 0.29188497, 0.02825102], - [0.90364963, 0.06387054, 0.91557011], - [0.11106939, 0.73972495, 0.49771819], - [0.63509055, 0.26659524, 0.4790886 ], - [0.20590893, 0.56012136, 0.78304187], - [0.30247726, 0.28023438, 0.6883304 ], - [0.58971475, 0.67312749, 0.47656539], - [0.26257592, 0.23964672, 0.64210249], - [0.26631165, 0.35701288, 0.88390072], - [0.01108113, 0.87276217, 0.99321825], - [0.68792169, 0.42725589, 0.92290326], - [0.09702907, 0.69950028, 0.97210289], - [0.86744636, 0.29614399, 0.2729772 ], - [0.77511449, 0.6912353 , 0.97596621], - [0.5919642 , 0.25713794, 0.0692452 ], - [0.47674521, 0.94254354, 0.71231971], - [0.50177591, 0.19320157, 0.91493713], - [0.27073903, 0.58171665, 0.79582017], - [0.76282237, 0.35119548, 0.80971555], - [0.43065933, 0.87678895, 0.57491155], - [0.34213045, 0.70619672, 0.43970999], - [0.38793158, 0.33048163, 0.91679507], - [0.68375111, 0.47934201, 0.86197378], - [0.67829585, 0.80616031, 0.76974334], - [0.01784785, 0.24857252, 0.89913317], - [0.8458996, 0.51551657, 0.69597985]]) - -sigmas = np.array([[0.56193862], [0.1275334 ], [0.91069059], - [0.01177131], [0.67799239], [0.95772282], - [0.55834784], [0.60151661], [0.25946789], - [0.88343075], [0.24011991], [0.05879632], - [0.6370561 ], [0.23859789], [0.18654873], - [0.70008281], [0.02968318], [0.01304724], - [0.08251756], [0.625351 ], [0.89982588], - [0.62378987], [0.8661594 ], [0.05583442], - [0.60157791], [0.84737657], [0.36433019], - [0.13263502], [0.30937519], [0.88979053]]) - - -def test_window_to_texture(interactive = False): + [0.97657922, 0.78730041, 0.13946709], + [0.7441061, 0.26322696, 0.8683115], + [0.14606987, 0.05490296, 0.98723486], + [0.71673873, 0.29188497, 0.02825102], + [0.90364963, 0.06387054, 0.91557011], + [0.11106939, 0.73972495, 0.49771819], + [0.63509055, 0.26659524, 0.4790886], + [0.20590893, 0.56012136, 0.78304187], + [0.30247726, 0.28023438, 0.6883304], + [0.58971475, 0.67312749, 0.47656539], + [0.26257592, 0.23964672, 0.64210249], + [0.26631165, 0.35701288, 0.88390072], + [0.01108113, 0.87276217, 0.99321825], + [0.68792169, 0.42725589, 0.92290326], + [0.09702907, 0.69950028, 0.97210289], + [0.86744636, 0.29614399, 0.2729772], + [0.77511449, 0.6912353, 0.97596621], + [0.5919642, 0.25713794, 0.0692452], + [0.47674521, 0.94254354, 0.71231971], + [0.50177591, 0.19320157, 0.91493713], + [0.27073903, 0.58171665, 0.79582017], + [0.76282237, 0.35119548, 0.80971555], + [0.43065933, 0.87678895, 0.57491155], + [0.34213045, 0.70619672, 0.43970999], + [0.38793158, 0.33048163, 0.91679507], + [0.68375111, 0.47934201, 0.86197378], + [0.67829585, 0.80616031, 0.76974334], + [0.01784785, 0.24857252, 0.89913317], + [0.8458996, 0.51551657, 0.69597985]]) + +sigmas = np.array([[0.56193862], [0.1275334], [0.91069059], + [0.01177131], [0.67799239], [0.95772282], + [0.55834784], [0.60151661], [0.25946789], + [0.88343075], [0.24011991], [0.05879632], + [0.6370561], [0.23859789], [0.18654873], + [0.70008281], [0.02968318], [0.01304724], + [0.08251756], [0.625351], [0.89982588], + [0.62378987], [0.8661594], [0.05583442], + [0.60157791], [0.84737657], [0.36433019], + [0.13263502], [0.30937519], [0.88979053]]) + + +def test_window_to_texture(interactive=False): fs_impl = """ vec2 res_factor = vec2(res.y/res.x, 1.0); vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; @@ -81,36 +82,36 @@ def test_window_to_texture(interactive = False): on_scene = window.Scene() on_scene.set_camera(position=(-2, 1, -2), - focal_point=(0.0, - 0.0, - 0.0), - view_up=(0.0, 0.0, 0.0)) + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) on_manager = window.ShowManager( on_scene, "on_manager", (width, - height)) + height)) off_scene = window.Scene() off_scene.set_camera(position=(-4, 2, -4), - focal_point=(0.0, - 0.0, - 0.0), - view_up=(0.0, 0.0, 0.0)) + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) off_manager = window.ShowManager( off_scene, "off_manager", (width, - height)) - + height)) + off_manager.window.SetOffScreenRendering(True) off_manager.initialize() scale = np.array([[width/height, 1.0, 0.0]]) c = cube(np.array([[0.0, 0.0, 0.0]]), colors=(1.0, 1.0, 0.0)) - bill = billboard(np.array([[0.0, 0.0, 0.0]]), scales = scale, fs_impl=fs_impl) + bill = billboard(np.array([[0.0, 0.0, 0.0]]), scales=scale, fs_impl=fs_impl) shader_custom_uniforms(bill, "fragment").SetUniform2f("res", off_manager.size) off_manager.scene.add(c) @@ -124,15 +125,15 @@ def test_window_to_texture(interactive = False): interpolate = True off_manager.render() - window_to_texture(off_manager.window, - tex_name, - bill, - blending_mode=blending_mode, - wrap_mode=wrap_mode, + window_to_texture(off_manager.window, + tex_name, + bill, + blending_mode=blending_mode, + wrap_mode=wrap_mode, border_color=border_color, interpolate=interpolate, d_type=dtype) - + if interactive: window.show(on_manager.scene) @@ -141,14 +142,16 @@ def test_window_to_texture(interactive = False): npt.assert_equal(1, n_textures) npt.assert_equal(WRAP_MODE_DIC[wrap_mode.lower()], texture.GetWrap()) - npt.assert_equal(BLENDING_MODE_DIC[blending_mode.lower()], texture.GetBlendingMode()) + npt.assert_equal( + BLENDING_MODE_DIC[blending_mode.lower()], texture.GetBlendingMode()) npt.assert_array_almost_equal(list(border_color), texture.GetBorderColor()) npt.assert_equal(interpolate, texture.GetInterpolate()) npt.assert_equal(True, texture.GetMipmap()) record(on_manager.scene, out_path="test_window.png", size=(width, height)) -def test_texture_to_actor(interactive = False): + +def test_texture_to_actor(interactive=False): fs_impl = """ vec2 res_factor = vec2(res.y/res.x, 1.0); vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; @@ -158,19 +161,19 @@ def test_texture_to_actor(interactive = False): on_scene = window.Scene() on_scene.set_camera(position=(-2, 1, -2), - focal_point=(0.0, - 0.0, - 0.0), - view_up=(0.0, 0.0, 0.0)) + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) on_manager = window.ShowManager( on_scene, "on_manager", (width, - height)) + height)) scale = np.array([[width/height, 1.0, 0.0]]) - bill = billboard(np.array([[0.0, 0.0, 0.0]]), scales = scale, fs_impl=fs_impl) + bill = billboard(np.array([[0.0, 0.0, 0.0]]), scales=scale, fs_impl=fs_impl) shader_custom_uniforms(bill, "fragment").SetUniform2f("res", on_manager.size) on_manager.scene.add(bill) @@ -183,13 +186,13 @@ def test_texture_to_actor(interactive = False): interpolate = True texture_to_actor("test_window.png", - tex_name, - bill, - blending_mode=blending_mode, - wrap_mode=wrap_mode, - border_color=border_color, - interpolate=interpolate) - + tex_name, + bill, + blending_mode=blending_mode, + wrap_mode=wrap_mode, + border_color=border_color, + interpolate=interpolate) + if interactive: window.show(on_manager.scene) @@ -198,12 +201,14 @@ def test_texture_to_actor(interactive = False): npt.assert_equal(1, n_textures) npt.assert_equal(WRAP_MODE_DIC[wrap_mode.lower()], texture.GetWrap()) - npt.assert_equal(BLENDING_MODE_DIC[blending_mode.lower()], texture.GetBlendingMode()) + npt.assert_equal( + BLENDING_MODE_DIC[blending_mode.lower()], texture.GetBlendingMode()) npt.assert_array_almost_equal(list(border_color), texture.GetBorderColor()) npt.assert_equal(interpolate, texture.GetInterpolate()) npt.assert_equal(True, texture.GetMipmap()) - -def test_colormap_to_actor(interactive = False): + + +def test_colormap_to_actor(interactive=False): fs_dec = import_fury_shader(os.path.join("effects", "color_mapping.glsl")) @@ -217,19 +222,20 @@ def test_colormap_to_actor(interactive = False): on_scene = window.Scene() on_scene.set_camera(position=(-2, 1, -2), - focal_point=(0.0, - 0.0, - 0.0), - view_up=(0.0, 0.0, 0.0)) + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) on_manager = window.ShowManager( on_scene, "on_manager", (width, - height)) + height)) scale = 3.4*np.array([[width/height, 1.0, 0.0]]) - bill = billboard(np.array([[0.0, 0.0, 0.0]]), scales = scale, fs_impl=fs_impl, fs_dec=fs_dec) + bill = billboard(np.array([[0.0, 0.0, 0.0]]), + scales=scale, fs_impl=fs_impl, fs_dec=fs_dec) shader_custom_uniforms(bill, "fragment").SetUniform2f("res", on_manager.size) on_manager.scene.add(bill) @@ -237,12 +243,11 @@ def test_colormap_to_actor(interactive = False): tex_name = "colormapTexture" interpolate = True - colormap_to_texture(create_colormap(np.arange(0.0, 1.0, 1/256), "viridis"), - tex_name, - bill, + colormap_to_texture(create_colormap(np.arange(0.0, 1.0, 1/256), "viridis"), + tex_name, + bill, interpolate) - if interactive: window.show(on_manager.scene) @@ -255,20 +260,21 @@ def test_colormap_to_actor(interactive = False): npt.assert_equal(interpolate, texture.GetInterpolate()) npt.assert_equal(True, texture.GetMipmap()) -def test_effect_manager_setup(interactive = False): + +def test_effect_manager_setup(interactive=False): scene = Scene() scene.set_camera(position=(-24, 20, -40), - focal_point=(0.0, - 0.0, - 0.0), - view_up=(0.0, 0.0, 0.0)) + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) manager = ShowManager( scene, "demo", (width, - height)) + height)) manager.initialize() @@ -282,19 +288,20 @@ def test_effect_manager_setup(interactive = False): npt.assert_equal(0, em._n_active_effects) npt.assert_equal({}, em._active_effects) -def test_remove_effect(interactive = False): + +def test_remove_effect(interactive=False): scene = window.Scene() scene.set_camera(position=(-24, 20, -40), - focal_point=(0.0, - 0.0, - 0.0), - view_up=(0.0, 0.0, 0.0)) + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) manager = window.ShowManager( scene, "demo", (width, - height)) + height)) manager.initialize() @@ -316,6 +323,3 @@ def test_remove_effect(interactive = False): npt.assert_equal(0, off_ascene.actors) npt.assert_equal({}, em._active_effects) npt.assert_equal(0, em._n_active_effects) - - - \ No newline at end of file From 0e475f62ee87ac548bafdc801f346f6872a3e45f Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Mon, 7 Aug 2023 18:24:13 -0300 Subject: [PATCH 30/41] fix: Fixing view up vector --- docs/examples/viz_kde_render.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/viz_kde_render.py b/docs/examples/viz_kde_render.py index 952d1d851..f3722cd17 100644 --- a/docs/examples/viz_kde_render.py +++ b/docs/examples/viz_kde_render.py @@ -51,7 +51,7 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int focal_point=(0.0, 0.0, 0.0), - view_up=(0.0, 0.0, 0.0)) + view_up=(0.0, 0.0, 1.0)) manager = ShowManager( scene, From 70d911bc758dc18a11254fbacfa2249b15523a6c Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Thu, 10 Aug 2023 14:12:34 -0300 Subject: [PATCH 31/41] fix: Adressing style issues --- fury/actors/effect_manager.py | 57 ++++++++++++++++++----------------- fury/shaders/base.py | 2 ++ fury/tests/test_actors.py | 2 +- 3 files changed, 32 insertions(+), 29 deletions(-) diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index 2a95d7df5..a1296e4f5 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -248,20 +248,6 @@ def kde(self, if np.min(sigmas) <= 0: raise ValueError("sigmas can't have zero or negative values.") - varying_dec = """ - varying float out_sigma; - varying float out_scale; - """ - - kde_dec = import_fury_shader( - os.path.join("utils", f"{kernel.lower()}_distribution.glsl")) - - kde_impl = """ - float current_kde = kde(normalizedVertexMCVSOutput*out_scale, out_sigma); - color = vec3(current_kde); - fragOutput0 = vec4(color, 1.0); - """ - kde_vs_dec = """ in float in_sigma; varying float out_sigma; @@ -275,23 +261,23 @@ def kde(self, out_scale = in_scale; """ - tex_dec = import_fury_shader(os.path.join("effects", "color_mapping.glsl")) - tex_impl = """ - // Turning screen coordinates to texture coordinates - vec2 res_factor = vec2(res.y/res.x, 1.0); - vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - float intensity = texture(screenTexture, renorm_tex).r; + varying_fs_dec = """ + varying float out_sigma; + varying float out_scale; + """ - if(intensity<=0.0){ - discard; - }else{ - vec4 final_color = color_mapping(intensity, colormapTexture); - fragOutput0 = vec4(final_color.rgb, u_opacity*final_color.a); - } + kde_fs_dec = import_fury_shader( + os.path.join("utils", f"{kernel.lower()}_distribution.glsl")) + + kde_fs_impl = """ + float current_kde = kde(normalizedVertexMCVSOutput*out_scale, out_sigma); + color = vec3(current_kde); + fragOutput0 = vec4(color, 1.0); """ - fs_dec = compose_shader([varying_dec, kde_dec]) + + fs_dec = compose_shader([varying_fs_dec, kde_fs_dec]) """Scales parameter will be defined by the empirical rule: 1*sima radius = 68.27% of data inside the curve @@ -307,7 +293,7 @@ def kde(self, 1.0), scales=scales, fs_dec=fs_dec, - fs_impl=kde_impl, + fs_impl=kde_fs_impl, vs_dec=kde_vs_dec, vs_impl=kde_vs_impl) @@ -338,6 +324,21 @@ def kde(self, res = self.off_manager.size # Render to second billboard for color map post-processing. + tex_dec = import_fury_shader(os.path.join("effects", "color_mapping.glsl")) + + tex_impl = """ + // Turning screen coordinates to texture coordinates + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + float intensity = texture(screenTexture, renorm_tex).r; + + if(intensity<=0.0){ + discard; + }else{ + vec4 final_color = color_mapping(intensity, colormapTexture); + fragOutput0 = vec4(final_color.rgb, u_opacity*final_color.a); + } + """ textured_billboard = billboard( np.array([center_of_mass]), scales=scale, diff --git a/fury/shaders/base.py b/fury/shaders/base.py index 7e64e5488..8c1873643 100644 --- a/fury/shaders/base.py +++ b/fury/shaders/base.py @@ -429,5 +429,7 @@ def shader_custom_uniforms(actor, shader_type): SHADER_FUNCTIONS = {"vertex" : actor.GetShaderProperty().GetVertexCustomUniforms(), "fragment" : actor.GetShaderProperty().GetFragmentCustomUniforms(), "geometry" : actor.GetShaderProperty().GetGeometryCustomUniforms()} + if shader_type not in SHADER_FUNCTIONS: + raise ValueError("Shader type should be of type 'vertex', 'fragment' or 'geometry'.") return SHADER_FUNCTIONS[shader_type.lower()] diff --git a/fury/tests/test_actors.py b/fury/tests/test_actors.py index a704c93b9..a6a7450d3 100644 --- a/fury/tests/test_actors.py +++ b/fury/tests/test_actors.py @@ -1821,7 +1821,7 @@ def test_actors_primitives_count(): act = act_func(**args) npt.assert_equal(primitives_count_from_actor(act), primitives_count) -def test_kde_actor(interactive = True): +def test_kde_actor(interactive = False): width, height = (400, 400) scene = window.Scene() scene.set_camera(position=(-24, 20, -40), From 734e8e730994803bc05f2c1e781b19c10ecea773 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Mon, 14 Aug 2023 17:47:25 -0300 Subject: [PATCH 32/41] fix: Fixed major scaling issue, sigmas bug, and adressed style issues --- docs/examples/viz_kde_render.py | 18 ++--- fury/actors/effect_manager.py | 65 ++++++++++--------- fury/actors/tests/test_effect_manager.py | 9 ++- fury/shaders/__init__.py | 1 + fury/shaders/base.py | 4 +- fury/shaders/utils/cosine_distribution.glsl | 4 +- .../utils/epanechnikov_distribution.glsl | 4 +- .../utils/exponential_distribution.glsl | 4 +- fury/shaders/utils/gaussian_distribution.glsl | 4 +- fury/shaders/utils/linear_distribution.glsl | 4 +- fury/shaders/utils/tophat_distribution.glsl | 4 +- fury/tests/test_actors.py | 15 ++--- 12 files changed, 68 insertions(+), 68 deletions(-) diff --git a/docs/examples/viz_kde_render.py b/docs/examples/viz_kde_render.py index f3722cd17..6d7946345 100644 --- a/docs/examples/viz_kde_render.py +++ b/docs/examples/viz_kde_render.py @@ -44,7 +44,7 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int ################################################################## # First, we need to setup the screen we will render the points to. -width, height = (1200, 1000) +size = (1200, 1000) scene = Scene() scene.set_camera(position=(-24, 20, -40), @@ -55,9 +55,9 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int manager = ShowManager( scene, - "demo", - (width, - height)) + "KDE Render", + (size[0], + size[1])) manager.initialize() @@ -74,11 +74,11 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int points = points + np.tile(offset, points.shape[0]).reshape(points.shape) ################################################################### -# For this KDE render, we will use a set of random sigmas as well, +# For this KDE render, we will use a set of random bandwidths, # generated with ``numpy.random.rand`` as well, which are also # remapped to the range of ``[0.05, 0.2]``. -sigmas = normalize(np.random.rand(n_points, 1), 0.05, 0.2) +bandwidths = normalize(np.random.rand(n_points, 1), 0.05, 0.2) ################################################################### @@ -94,17 +94,17 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int ################################################################### # After having the ``effects`` setup, just call the kde actor method -# from it, passing the points, sigma, and other optional options +# from it, passing the points, bandwidth, and other optional options # if wished, like the kernel to be used or the colormap desired. # The colormaps are by default taken from *matplotlib*, but a # custom one can be passed. After calling it, just pass the actor # to the scene, and start it as usual. -kde_actor = effects.kde(points, sigmas, kernel="gaussian", colormap="inferno") +kde_actor = effects.kde(points, bandwidths, kernel="gaussian", colormap="inferno") manager.scene.add(kde_actor) -interactive = True +interactive = False if interactive: manager.start() diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index a1296e4f5..ddb9008a7 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -80,7 +80,6 @@ def window_to_texture( "rgba" : windowToImageFilter.SetInputBufferTypeToRGBA, "zbuffer" : windowToImageFilter.SetInputBufferTypeToZBuffer} type_dic[d_type.lower()]() - windowToImageFilter.Update() texture = Texture() texture.SetMipmap(True) @@ -115,7 +114,7 @@ def texture_to_actor( Name of the texture to be passed to the actor. target_actor : Actor Target actor to receive the texture. - blending_mode : str + blending_mode : str, optional Texture blending mode. The options are: 1. None 2. Replace @@ -124,15 +123,15 @@ def texture_to_actor( 5. AddSigned 6. Interpolate 7. Subtract - wrap_mode : str + wrap_mode : str, optional Texture wrapping mode. The options are: 1. ClampToEdge 2. Repeat 3. MirroredRepeat 4. ClampToBorder - border_color : tuple (4, ) + border_color : tuple (4, ), optional Texture RGBA border color. - interpolate : bool + interpolate : bool, optional Texture interpolation.""" texture = Texture() @@ -164,7 +163,7 @@ def colormap_to_texture( Name of the color map texture to be passed to the actor. target_actor : Actor Target actor to receive the color map texture. - interpolate : bool + interpolate : bool, optional Color map texture interpolation.""" if len(colormap.shape) == 2: @@ -206,7 +205,7 @@ def __init__(self, manager : ShowManager): def kde(self, points : np.ndarray, - sigmas, + bandwidths, kernel : str = "gaussian", opacity : float = 1.0, colormap : str = "viridis", @@ -217,8 +216,8 @@ def kde(self, ---------- points : np.ndarray (N, 3) Array of points to be displayed. - sigmas : np.ndarray (1, ) or (N, 1) - Array of sigmas to be used in the KDE calculations. Must be one or one for each point. + bandwidths : np.ndarray (1, ) or (N, 1) + Array of bandwidths to be used in the KDE calculations. Must be one or one for each point. kernel : str, optional Kernel to be used for the distribution calculation. The available options are: * "cosine" @@ -241,29 +240,31 @@ def kde(self, ------- textured_billboard : actor.Actor KDE rendering actor.""" - if not isinstance(sigmas, np.ndarray): - sigmas = np.array(sigmas) - if sigmas.shape[0] != 1 and sigmas.shape[0] != points.shape[0]: - raise IndexError("sigmas size must be one or points size.") - if np.min(sigmas) <= 0: - raise ValueError("sigmas can't have zero or negative values.") + if not isinstance(bandwidths, np.ndarray): + bandwidths = np.array([bandwidths]) + if bandwidths.shape[0] != 1 and bandwidths.shape[0] != points.shape[0]: + raise IndexError("bandwidths size must be one or points size.") + elif bandwidths.shape[0] == 1: + bandwidths = np.repeat(bandwidths[0], points.shape[0]) + if np.min(bandwidths) <= 0: + raise ValueError("bandwidths can't have zero or negative values.") kde_vs_dec = """ - in float in_sigma; - varying float out_sigma; + in float in_bandwidth; + varying float out_bandwidth; in float in_scale; varying float out_scale; """ kde_vs_impl = """ - out_sigma = in_sigma; + out_bandwidth = in_bandwidth; out_scale = in_scale; """ varying_fs_dec = """ - varying float out_sigma; + varying float out_bandwidth; varying float out_scale; """ @@ -271,7 +272,7 @@ def kde(self, os.path.join("utils", f"{kernel.lower()}_distribution.glsl")) kde_fs_impl = """ - float current_kde = kde(normalizedVertexMCVSOutput*out_scale, out_sigma); + float current_kde = kde(normalizedVertexMCVSOutput*out_scale, out_bandwidth); color = vec3(current_kde); fragOutput0 = vec4(color, 1.0); """ @@ -281,9 +282,9 @@ def kde(self, """Scales parameter will be defined by the empirical rule: 1*sima radius = 68.27% of data inside the curve - 2*sigma radius = 95.45% of data inside the curve - 3*sigma radius = 99.73% of data inside the curve""" - scales = 2*3.0*np.copy(sigmas) + 2*bandwidth radius = 95.45% of data inside the curve + 3*bandwidth radius = 99.73% of data inside the curve""" + scales = 2*3.0*np.copy(bandwidths) center_of_mass = np.average(points, axis=0) bill = billboard( @@ -302,7 +303,7 @@ def kde(self, shader_apply_effects(window, bill, gl_disable_depth) shader_apply_effects(window, bill, gl_set_additive_blending) - attribute_to_actor(bill, np.repeat(sigmas, 4), "in_sigma") + attribute_to_actor(bill, np.repeat(bandwidths, 4), "in_bandwidth") attribute_to_actor(bill, np.repeat(scales, 4), "in_scale") if self._n_active_effects > 0: @@ -310,27 +311,26 @@ def kde(self, self.off_manager.scene.add(bill) bill_bounds = bill.GetBounds() - max_sigma = 2*4.0*np.max(sigmas) + max_bandwidth = 2*4.0*np.max(bandwidths) actor_scales = np.array([[bill_bounds[1] - bill_bounds[0] + - center_of_mass[0] + max_sigma, + center_of_mass[0] + max_bandwidth, bill_bounds[3] - bill_bounds[2] + - center_of_mass[1] + max_sigma, 0.0]]) + center_of_mass[1] + max_bandwidth, 0.0]]) scale = np.array([[actor_scales.max(), actor_scales.max(), 0.0]]) - res = self.off_manager.size + res = np.array(self.off_manager.size) # Render to second billboard for color map post-processing. tex_dec = import_fury_shader(os.path.join("effects", "color_mapping.glsl")) tex_impl = """ // Turning screen coordinates to texture coordinates - vec2 res_factor = vec2(res.y/res.x, 1.0); - vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - float intensity = texture(screenTexture, renorm_tex).r; + vec2 tex_coords = gl_FragCoord.xy/res; + float intensity = texture(screenTexture, tex_coords).r; if(intensity<=0.0){ discard; @@ -360,6 +360,9 @@ def kde(self, def kde_callback(obj=None, event=None): cam_params = self.on_manager.scene.get_camera() self.off_manager.scene.set_camera(*cam_params) + res[0], res[1]= self.on_manager.window.GetSize() + self.off_manager.window.SetSize(res[0], res[1]) + shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", res) self.off_manager.scene.Modified() shader_apply_effects(window, bill, gl_disable_depth) shader_apply_effects(window, bill, gl_set_additive_blending) diff --git a/fury/actors/tests/test_effect_manager.py b/fury/actors/tests/test_effect_manager.py index 0f833fe2f..70728d8de 100644 --- a/fury/actors/tests/test_effect_manager.py +++ b/fury/actors/tests/test_effect_manager.py @@ -60,7 +60,7 @@ [0.01784785, 0.24857252, 0.89913317], [0.8458996, 0.51551657, 0.69597985]]) -sigmas = np.array([[0.56193862], [0.1275334], [0.91069059], +bandwidths = np.array([[0.56193862], [0.1275334], [0.91069059], [0.01177131], [0.67799239], [0.95772282], [0.55834784], [0.60151661], [0.25946789], [0.88343075], [0.24011991], [0.05879632], @@ -148,7 +148,6 @@ def test_window_to_texture(interactive=False): npt.assert_equal(interpolate, texture.GetInterpolate()) npt.assert_equal(True, texture.GetMipmap()) - record(on_manager.scene, out_path="test_window.png", size=(width, height)) def test_texture_to_actor(interactive=False): @@ -272,7 +271,7 @@ def test_effect_manager_setup(interactive=False): manager = ShowManager( scene, - "demo", + "Test EffectManager", (width, height)) @@ -299,7 +298,7 @@ def test_remove_effect(interactive=False): manager = window.ShowManager( scene, - "demo", + "Test remove_effect", (width, height)) @@ -307,7 +306,7 @@ def test_remove_effect(interactive=False): em = EffectManager(manager) - kde_actor = em.kde(points, sigmas, colormap="inferno") + kde_actor = em.kde(points, bandwidths, colormap="inferno") manager.scene.add(kde_actor) em.remove_effect(kde_actor) diff --git a/fury/shaders/__init__.py b/fury/shaders/__init__.py index 37cb94110..fb03e15c0 100644 --- a/fury/shaders/__init__.py +++ b/fury/shaders/__init__.py @@ -21,4 +21,5 @@ 'replace_shader_in_actor', 'shader_apply_effects', 'shader_to_actor', + 'shader_custom_uniforms' ] diff --git a/fury/shaders/base.py b/fury/shaders/base.py index 8c1873643..70189c6d6 100644 --- a/fury/shaders/base.py +++ b/fury/shaders/base.py @@ -414,7 +414,7 @@ def attribute_to_actor(actor, arr, attr_name, deep=True): ) def shader_custom_uniforms(actor, shader_type): - """Eases the passing of uniform values to the shaders by returning ``actor.GetShaderProperty().GetVertexCustomUniforms()``, + """Ease the passing of uniform values to the shaders by returning ``actor.GetShaderProperty().GetVertexCustomUniforms()``, that give access to the ``SetUniform`` methods. Parameters ---------- @@ -429,7 +429,7 @@ def shader_custom_uniforms(actor, shader_type): SHADER_FUNCTIONS = {"vertex" : actor.GetShaderProperty().GetVertexCustomUniforms(), "fragment" : actor.GetShaderProperty().GetFragmentCustomUniforms(), "geometry" : actor.GetShaderProperty().GetGeometryCustomUniforms()} - if shader_type not in SHADER_FUNCTIONS: + if shader_type.lower() not in SHADER_FUNCTIONS: raise ValueError("Shader type should be of type 'vertex', 'fragment' or 'geometry'.") return SHADER_FUNCTIONS[shader_type.lower()] diff --git a/fury/shaders/utils/cosine_distribution.glsl b/fury/shaders/utils/cosine_distribution.glsl index 73976d2ab..54a0a8baf 100644 --- a/fury/shaders/utils/cosine_distribution.glsl +++ b/fury/shaders/utils/cosine_distribution.glsl @@ -1,4 +1,4 @@ // This assumes the center of the normal distribution is the center of the screen -float kde(vec3 point, float sigma){ - return cos(PI*length(point)/(2*sigma))*int(length(point) < sigma); +float kde(vec3 point, float bandwidth){ + return cos(PI*length(point)/(2*bandwidth))*int(length(point) < bandwidth); } \ No newline at end of file diff --git a/fury/shaders/utils/epanechnikov_distribution.glsl b/fury/shaders/utils/epanechnikov_distribution.glsl index 816fe54d8..b7369ca4d 100644 --- a/fury/shaders/utils/epanechnikov_distribution.glsl +++ b/fury/shaders/utils/epanechnikov_distribution.glsl @@ -1,4 +1,4 @@ // This assumes the center of the normal distribution is the center of the screen -float kde(vec3 point, float sigma){ - return (1.0 - (length(point)*length(point))/(sigma*sigma)); +float kde(vec3 point, float bandwidth){ + return (1.0 - (length(point)*length(point))/(bandwidth*bandwidth)); } \ No newline at end of file diff --git a/fury/shaders/utils/exponential_distribution.glsl b/fury/shaders/utils/exponential_distribution.glsl index 00ae49a92..e894ecd2f 100644 --- a/fury/shaders/utils/exponential_distribution.glsl +++ b/fury/shaders/utils/exponential_distribution.glsl @@ -1,4 +1,4 @@ // This assumes the center of the normal distribution is the center of the screen -float kde(vec3 point, float sigma){ - return exp(-1.0*length(point)/sigma); +float kde(vec3 point, float bandwidth){ + return exp(-1.0*length(point)/bandwidth); } \ No newline at end of file diff --git a/fury/shaders/utils/gaussian_distribution.glsl b/fury/shaders/utils/gaussian_distribution.glsl index 40710c5a6..fd2be2275 100644 --- a/fury/shaders/utils/gaussian_distribution.glsl +++ b/fury/shaders/utils/gaussian_distribution.glsl @@ -1,4 +1,4 @@ // This assumes the center of the normal distribution is the center of the screen -float kde(vec3 point, float sigma){ - return exp(-1.0*pow(length(point), 2.0)/(2.0*sigma*sigma) ); +float kde(vec3 point, float bandwidth){ + return exp(-1.0*pow(length(point), 2.0)/(2.0*bandwidth*bandwidth) ); } \ No newline at end of file diff --git a/fury/shaders/utils/linear_distribution.glsl b/fury/shaders/utils/linear_distribution.glsl index 290b7fba4..0b42114e0 100644 --- a/fury/shaders/utils/linear_distribution.glsl +++ b/fury/shaders/utils/linear_distribution.glsl @@ -1,4 +1,4 @@ // This assumes the center of the normal distribution is the center of the screen -float kde(vec3 point, float sigma){ - return (1.0 - length(point)/sigma)*int(length(point) < sigma); +float kde(vec3 point, float bandwidth){ + return (1.0 - length(point)/bandwidth)*int(length(point) < bandwidth); } \ No newline at end of file diff --git a/fury/shaders/utils/tophat_distribution.glsl b/fury/shaders/utils/tophat_distribution.glsl index 1edb23689..76e44f88a 100644 --- a/fury/shaders/utils/tophat_distribution.glsl +++ b/fury/shaders/utils/tophat_distribution.glsl @@ -1,4 +1,4 @@ // This assumes the center of the normal distribution is the center of the screen -float kde(vec3 point, float sigma){ - return int(length(point) < sigma); +float kde(vec3 point, float bandwidth){ + return int(length(point) < bandwidth); } \ No newline at end of file diff --git a/fury/tests/test_actors.py b/fury/tests/test_actors.py index a6a7450d3..3f9fc33e2 100644 --- a/fury/tests/test_actors.py +++ b/fury/tests/test_actors.py @@ -11,9 +11,9 @@ from fury import primitive as fp from fury import shaders, window from fury.actor import grid +from fury.actors.effect_manager import EffectManager from fury.decorators import skip_linux, skip_osx, skip_win from fury.deprecator import ExpiredDeprecationError -from fury.actors.effect_manager import EffectManager # Allow import, but disable doctests if we don't have dipy from fury.optpkg import optional_package @@ -1822,7 +1822,7 @@ def test_actors_primitives_count(): npt.assert_equal(primitives_count_from_actor(act), primitives_count) def test_kde_actor(interactive = False): - width, height = (400, 400) + size = (400, 400) scene = window.Scene() scene.set_camera(position=(-24, 20, -40), focal_point=(0.0, @@ -1832,9 +1832,8 @@ def test_kde_actor(interactive = False): manager = window.ShowManager( scene, - "demo", - (width, - height)) + "Test KDE", + size) manager.initialize() @@ -1871,7 +1870,7 @@ def test_kde_actor(interactive = False): [0.01784785, 0.24857252, 0.89913317], [0.8458996, 0.51551657, 0.69597985]]) - sigmas = np.array([[0.56193862], [0.1275334 ], [0.91069059], + bandwidths = np.array([[0.56193862], [0.1275334 ], [0.91069059], [0.01177131], [0.67799239], [0.95772282], [0.55834784], [0.60151661], [0.25946789], [0.88343075], [0.24011991], [0.05879632], @@ -1882,17 +1881,15 @@ def test_kde_actor(interactive = False): [0.60157791], [0.84737657], [0.36433019], [0.13263502], [0.30937519], [0.88979053]]) - kde_actor = em.kde(points, sigmas, colormap="inferno") + kde_actor = em.kde(points, bandwidths, colormap="inferno") manager.scene.add(kde_actor) if interactive: window.show(manager.scene) - off_arr = window.snapshot(em.off_manager.scene) off_ascene = window.analyze_scene(em.off_manager.scene) - on_arr = window.snapshot(manager.scene) on_ascene = window.analyze_scene(manager.scene) on_obs = em.on_manager.iren.HasObserver("RenderEvent") From 1722b2ee2570805523baae64734a2c816f20cdaa Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Tue, 15 Aug 2023 17:22:27 -0300 Subject: [PATCH 33/41] doc: minor doc fix --- fury/actors/effect_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index ddb9008a7..4c70c49e7 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -281,7 +281,7 @@ def kde(self, fs_dec = compose_shader([varying_fs_dec, kde_fs_dec]) """Scales parameter will be defined by the empirical rule: - 1*sima radius = 68.27% of data inside the curve + 1*bandwidth radius = 68.27% of data inside the curve 2*bandwidth radius = 95.45% of data inside the curve 3*bandwidth radius = 99.73% of data inside the curve""" scales = 2*3.0*np.copy(bandwidths) From c54c97f66f322a55f375b6884bc30b7b62268bf8 Mon Sep 17 00:00:00 2001 From: devmessias Date: Tue, 15 Aug 2023 19:36:03 -0300 Subject: [PATCH 34/41] refactor: use callable approach --- docs/examples/viz_kde_render.py | 33 +++--- fury/actors/effect_manager.py | 184 ++++++++++++++++---------------- 2 files changed, 111 insertions(+), 106 deletions(-) diff --git a/docs/examples/viz_kde_render.py b/docs/examples/viz_kde_render.py index 6d7946345..c516511b8 100644 --- a/docs/examples/viz_kde_render.py +++ b/docs/examples/viz_kde_render.py @@ -2,16 +2,16 @@ ====================================================================== Fury Kernel Density Estimation rendering Actor ====================================================================== -This example shows how to use the KDE actor. This is a special actor in Fury that works -with post-processing effects to render kernel density estimations of a given set of points -in real-time to the screen. For better understanding on KDEs, check this +This example shows how to use the KDE actor. This is a special actor in Fury that works +with post-processing effects to render kernel density estimations of a given set of points +in real-time to the screen. For better understanding on KDEs, check this `Wikipedia page `_ about it. For this example, you will only need the modules below: """ import numpy as np -from fury.actors.effect_manager import EffectManager +from fury.actors.effect_manager import EffectManager, KDE from fury.window import Scene, ShowManager, record ##################################################################################### @@ -63,7 +63,7 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int #################################################################### # ``numpy.random.rand`` will be used to generate random points, which -# will be then relocated with the function we declared below to the +# will be then relocated with the function we declared below to the # range of ``[-5.0, 5.0]``, so they get more space between them. In case # offsetted points are wanted, it can be done just as below. @@ -74,37 +74,38 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int points = points + np.tile(offset, points.shape[0]).reshape(points.shape) ################################################################### -# For this KDE render, we will use a set of random bandwidths, -# generated with ``numpy.random.rand`` as well, which are also +# For this KDE render, we will use a set of random bandwidths, +# generated with ``numpy.random.rand`` as well, which are also # remapped to the range of ``[0.05, 0.2]``. bandwidths = normalize(np.random.rand(n_points, 1), 0.05, 0.2) ################################################################### -# Now, for the KDE render, a special class is needed, the +# Now, for the KDE render, a special class is needed, the # ``EffectManager``. This class is needed to manage the post-processing -# aspect of this kind of render, as it will need to first be -# rendered to an offscreen buffer, retrieved and then processed +# aspect of this kind of render, as it will need to first be +# rendered to an offscreen buffer, retrieved and then processed # by the final actor that will render it to the screen, but don't -# worry, none of this will need to be setup by you! Just call the +# worry, none of this will need to be setup by you! Just call the # ``EffectManager`` like below, passing the manager to it: effects = EffectManager(manager) ################################################################### -# After having the ``effects`` setup, just call the kde actor method +# After having the ``effects`` setup, just call the kde actor method # from it, passing the points, bandwidth, and other optional options # if wished, like the kernel to be used or the colormap desired. # The colormaps are by default taken from *matplotlib*, but a # custom one can be passed. After calling it, just pass the actor # to the scene, and start it as usual. -kde_actor = effects.kde(points, bandwidths, kernel="gaussian", colormap="inferno") +#kde_actor = effects.kde(points, bandwidths, kernel="gaussian", colormap="inferno") +kde_effect = KDE(points, bandwidths, kernel="gaussian", colormap="inferno") +effects.add(kde_effect) +#manager.scene.add(kde_actor) -manager.scene.add(kde_actor) - -interactive = False +interactive = True if interactive: manager.start() diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index 4c70c49e7..bee6e63af 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -1,4 +1,7 @@ import os +from typing import Any +from functools import partial + import numpy as np from fury.actor import Actor, billboard from fury.colormap import create_colormap @@ -183,6 +186,7 @@ def colormap_to_texture( target_actor.GetProperty().SetTexture(texture_name, texture) + class EffectManager(): """Class that manages the application of post-processing effects on actors. @@ -203,43 +207,54 @@ def __init__(self, manager : ShowManager): self._n_active_effects = 0 self._active_effects = {} - def kde(self, + def add(self, effect): + callback = partial(effect, effect_manager=self) + if hasattr(effect, "apply"): + effect.apply(self) + + callback() + callback_id = self.on_manager.add_iren_callback(callback, "RenderEvent") + + + self._active_effects[effect.textured_billboard] = (callback_id, effect.bill) + self._n_active_effects += 1 + self.on_manager.scene.add(effect.textured_billboard) + + return effect.textured_billboard + + def remove_effect(self, effect_actor): + """Remove an existing effect from the effects manager. + Beware that the effect and the actor will be removed from the rendering pipeline + and shall not work after this action. + + Parameters + ---------- + effect_actor : actor.Actor + Actor of effect to be removed. + """ + if self._n_active_effects > 0: + self.on_manager.iren.RemoveObserver(self._active_effects[effect_actor][0]) + self.off_manager.scene.RemoveActor(self._active_effects[effect_actor][1]) + self.on_manager.scene.RemoveActor(effect_actor) + self._active_effects.pop(effect_actor) + self._n_active_effects -= 1 + else: + raise IndexError("Manager has no active effects.") + +class KDE(): + def __init__(self, points : np.ndarray, bandwidths, kernel : str = "gaussian", opacity : float = 1.0, colormap : str = "viridis", custom_colormap : np.array = None): - """Actor that displays the Kernel Density Estimation of a given set of points. - - Parameters - ---------- - points : np.ndarray (N, 3) - Array of points to be displayed. - bandwidths : np.ndarray (1, ) or (N, 1) - Array of bandwidths to be used in the KDE calculations. Must be one or one for each point. - kernel : str, optional - Kernel to be used for the distribution calculation. The available options are: - * "cosine" - * "epanechnikov" - * "exponential" - * "gaussian" - * "linear" - * "tophat" - - opacity : float, optional - Opacity of the actor. - colormap : str, optional. - Colormap matplotlib name for the KDE rendering. Default is "viridis". - custom_colormap : np.ndarray (N, 4), optional - Custom colormap for the KDE rendering. Default is none which means no - custom colormap is desired. If passed, will overwrite matplotlib colormap - chosen in the previous parameter. - - Returns - ------- - textured_billboard : actor.Actor - KDE rendering actor.""" + self.points = points + self.bandwidths = bandwidths + self.kernel = kernel + self.opacity = opacity + self.colormap = colormap + self.custom_colormap = custom_colormap if not isinstance(bandwidths, np.ndarray): bandwidths = np.array([bandwidths]) if bandwidths.shape[0] != 1 and bandwidths.shape[0] != points.shape[0]: @@ -286,8 +301,8 @@ def kde(self, 3*bandwidth radius = 99.73% of data inside the curve""" scales = 2*3.0*np.copy(bandwidths) - center_of_mass = np.average(points, axis=0) - bill = billboard( + self.center_of_mass = np.average(points, axis=0) + self.bill = billboard( points, (0.0, 0.0, @@ -297,24 +312,34 @@ def kde(self, fs_impl=kde_fs_impl, vs_dec=kde_vs_dec, vs_impl=kde_vs_impl) - + self.scales = scales # Blending and uniforms setup - window = self.off_manager.window - shader_apply_effects(window, bill, gl_disable_depth) - shader_apply_effects(window, bill, gl_set_additive_blending) + def apply(self, effect_manager : EffectManager): + self.effect_manager = effect_manager + bandwidths = self.bandwidths + opacity = self.opacity + colormap = self.colormap + custom_colormap = self.custom_colormap + bill = self.bill + scales = self.scales + center_of_mass = self.center_of_mass + + off_window = effect_manager.off_manager.window + shader_apply_effects(off_window, bill, gl_disable_depth) + shader_apply_effects(off_window, bill, gl_set_additive_blending) attribute_to_actor(bill, np.repeat(bandwidths, 4), "in_bandwidth") attribute_to_actor(bill, np.repeat(scales, 4), "in_scale") - if self._n_active_effects > 0: - self.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) - self.off_manager.scene.add(bill) + if effect_manager._n_active_effects > 0: + effect_manager.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) + effect_manager.off_manager.scene.add(self.bill) - bill_bounds = bill.GetBounds() + bill_bounds = self.bill.GetBounds() max_bandwidth = 2*4.0*np.max(bandwidths) actor_scales = np.array([[bill_bounds[1] - bill_bounds[0] + - center_of_mass[0] + max_bandwidth, + center_of_mass[0] + max_bandwidth, bill_bounds[3] - bill_bounds[2] + center_of_mass[1] + max_bandwidth, 0.0]]) @@ -322,7 +347,7 @@ def kde(self, actor_scales.max(), 0.0]]) - res = np.array(self.off_manager.size) + self.res = np.array(effect_manager.off_manager.size) # Render to second billboard for color map post-processing. tex_dec = import_fury_shader(os.path.join("effects", "color_mapping.glsl")) @@ -344,7 +369,8 @@ def kde(self, scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) - shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", res) + self.textured_billboard = textured_billboard + shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.res) shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) # Disables the texture warnings @@ -357,49 +383,27 @@ def kde(self, colormap_to_texture(cmap, "colormapTexture", textured_billboard) - def kde_callback(obj=None, event=None): - cam_params = self.on_manager.scene.get_camera() - self.off_manager.scene.set_camera(*cam_params) - res[0], res[1]= self.on_manager.window.GetSize() - self.off_manager.window.SetSize(res[0], res[1]) - shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", res) - self.off_manager.scene.Modified() - shader_apply_effects(window, bill, gl_disable_depth) - shader_apply_effects(window, bill, gl_set_additive_blending) - self.off_manager.render() - - window_to_texture( - self.off_manager.window, - "screenTexture", - textured_billboard, - blending_mode="Interpolate", - d_type="rgba") - - # Initialization - kde_callback() - - callback_id = self.on_manager.add_iren_callback(kde_callback, "RenderEvent") - - self._active_effects[textured_billboard] = (callback_id, bill) - self._n_active_effects += 1 - - return textured_billboard - - def remove_effect(self, effect_actor): - """Remove an existing effect from the effects manager. - Beware that the effect and the actor will be removed from the rendering pipeline - and shall not work after this action. - - Parameters - ---------- - effect_actor : actor.Actor - Actor of effect to be removed. - """ - if self._n_active_effects > 0: - self.on_manager.iren.RemoveObserver(self._active_effects[effect_actor][0]) - self.off_manager.scene.RemoveActor(self._active_effects[effect_actor][1]) - self.on_manager.scene.RemoveActor(effect_actor) - self._active_effects.pop(effect_actor) - self._n_active_effects -= 1 - else: - raise IndexError("Manager has no active effects.") + def __call__(self, obj=None, event=None, effect_manager=None) -> Any: + + off_manager = effect_manager.off_manager + on_manager = effect_manager.on_manager + + on_window = on_manager.window + off_window = off_manager.window + + cam_params = effect_manager.on_manager.scene.get_camera() + off_manager.scene.set_camera(*cam_params) + self.res[0], self.res[1] = on_window.GetSize() + off_window.SetSize(self.res[0], self.res[1]) + shader_custom_uniforms(self.textured_billboard, "fragment").SetUniform2f("res", self.res) + off_manager.scene.Modified() + shader_apply_effects(off_window, self.bill, gl_disable_depth) + shader_apply_effects(off_window, self.bill, gl_set_additive_blending) + off_manager.render() + + window_to_texture( + off_manager.window, + "screenTexture", + self.textured_billboard, + blending_mode="Interpolate", + d_type="rgba") From 3f7d335b876d249524947cf7683d92af5c159457 Mon Sep 17 00:00:00 2001 From: devmessias Date: Tue, 15 Aug 2023 19:57:28 -0300 Subject: [PATCH 35/41] refactor: effects module --- docs/examples/viz_kde_render.py | 3 +- fury/effects/__init__.py | 2 + fury/effects/effect_manager.py | 64 +++++++++++++ .../effect_manager.py => effects/effects.py} | 93 +++++-------------- 4 files changed, 91 insertions(+), 71 deletions(-) create mode 100644 fury/effects/__init__.py create mode 100644 fury/effects/effect_manager.py rename fury/{actors/effect_manager.py => effects/effects.py} (80%) diff --git a/docs/examples/viz_kde_render.py b/docs/examples/viz_kde_render.py index c516511b8..77989dc94 100644 --- a/docs/examples/viz_kde_render.py +++ b/docs/examples/viz_kde_render.py @@ -11,7 +11,7 @@ """ import numpy as np -from fury.actors.effect_manager import EffectManager, KDE +from fury.effects import EffectManager, KDE from fury.window import Scene, ShowManager, record ##################################################################################### @@ -103,7 +103,6 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int #kde_actor = effects.kde(points, bandwidths, kernel="gaussian", colormap="inferno") kde_effect = KDE(points, bandwidths, kernel="gaussian", colormap="inferno") effects.add(kde_effect) -#manager.scene.add(kde_actor) interactive = True diff --git a/fury/effects/__init__.py b/fury/effects/__init__.py new file mode 100644 index 000000000..ad0aabfce --- /dev/null +++ b/fury/effects/__init__.py @@ -0,0 +1,2 @@ +from fury.effects.effect_manager import EffectManager +from fury.effects.effects import * diff --git a/fury/effects/effect_manager.py b/fury/effects/effect_manager.py new file mode 100644 index 000000000..88fb2848c --- /dev/null +++ b/fury/effects/effect_manager.py @@ -0,0 +1,64 @@ +from functools import partial +from fury.window import Scene, ShowManager + + +class EffectManager(): + """Class that manages the application of post-processing effects on actors. + + Parameters + ---------- + manager : ShowManager + Target manager that will render post processed actors.""" + + def __init__(self, manager : ShowManager): + self.scene = Scene() + cam_params = manager.scene.get_camera() + self.scene.set_camera(*cam_params) + self.on_manager = manager + self.off_manager = ShowManager(self.scene, + size=manager.size) + self.off_manager.window.SetOffScreenRendering(True) + self.off_manager.initialize() + self._n_active_effects = 0 + self._active_effects = {} + + def add(self, effect): + callback = partial( + effect, + off_manager=self.off_manager, + on_manager=self.on_manager + ) + if hasattr(effect, "apply"): + effect.apply( + off_manager = self.off_manager, + n_active_effects = self._n_active_effects + ) + + callback() + callback_id = self.on_manager.add_iren_callback(callback, "RenderEvent") + + + self._active_effects[effect.textured_billboard] = (callback_id, effect.bill) + self._n_active_effects += 1 + self.on_manager.scene.add(effect.textured_billboard) + + return effect.textured_billboard + + def remove_effect(self, effect_actor): + """Remove an existing effect from the effects manager. + Beware that the effect and the actor will be removed from the rendering pipeline + and shall not work after this action. + + Parameters + ---------- + effect_actor : actor.Actor + Actor of effect to be removed. + """ + if self._n_active_effects > 0: + self.on_manager.iren.RemoveObserver(self._active_effects[effect_actor][0]) + self.off_manager.scene.RemoveActor(self._active_effects[effect_actor][1]) + self.on_manager.scene.RemoveActor(effect_actor) + self._active_effects.pop(effect_actor) + self._n_active_effects -= 1 + else: + raise IndexError("Manager has no active effects.") diff --git a/fury/actors/effect_manager.py b/fury/effects/effects.py similarity index 80% rename from fury/actors/effect_manager.py rename to fury/effects/effects.py index bee6e63af..9c3a670bb 100644 --- a/fury/actors/effect_manager.py +++ b/fury/effects/effects.py @@ -1,6 +1,5 @@ import os from typing import Any -from functools import partial import numpy as np from fury.actor import Actor, billboard @@ -16,10 +15,10 @@ from fury.window import (gl_disable_depth, gl_set_additive_blending, RenderWindow, - Scene, ShowManager) + WRAP_MODE_DIC = {"clamptoedge" : Texture.ClampToEdge, "repeat" : Texture.Repeat, "mirroredrepeat" : Texture.MirroredRepeat, @@ -31,6 +30,7 @@ "subtract" : 6} + def window_to_texture( window : RenderWindow, texture_name : str, @@ -186,61 +186,6 @@ def colormap_to_texture( target_actor.GetProperty().SetTexture(texture_name, texture) - -class EffectManager(): - """Class that manages the application of post-processing effects on actors. - - Parameters - ---------- - manager : ShowManager - Target manager that will render post processed actors.""" - - def __init__(self, manager : ShowManager): - self.scene = Scene() - cam_params = manager.scene.get_camera() - self.scene.set_camera(*cam_params) - self.on_manager = manager - self.off_manager = ShowManager(self.scene, - size=manager.size) - self.off_manager.window.SetOffScreenRendering(True) - self.off_manager.initialize() - self._n_active_effects = 0 - self._active_effects = {} - - def add(self, effect): - callback = partial(effect, effect_manager=self) - if hasattr(effect, "apply"): - effect.apply(self) - - callback() - callback_id = self.on_manager.add_iren_callback(callback, "RenderEvent") - - - self._active_effects[effect.textured_billboard] = (callback_id, effect.bill) - self._n_active_effects += 1 - self.on_manager.scene.add(effect.textured_billboard) - - return effect.textured_billboard - - def remove_effect(self, effect_actor): - """Remove an existing effect from the effects manager. - Beware that the effect and the actor will be removed from the rendering pipeline - and shall not work after this action. - - Parameters - ---------- - effect_actor : actor.Actor - Actor of effect to be removed. - """ - if self._n_active_effects > 0: - self.on_manager.iren.RemoveObserver(self._active_effects[effect_actor][0]) - self.off_manager.scene.RemoveActor(self._active_effects[effect_actor][1]) - self.on_manager.scene.RemoveActor(effect_actor) - self._active_effects.pop(effect_actor) - self._n_active_effects -= 1 - else: - raise IndexError("Manager has no active effects.") - class KDE(): def __init__(self, points : np.ndarray, @@ -314,9 +259,19 @@ def __init__(self, vs_impl=kde_vs_impl) self.scales = scales # Blending and uniforms setup + def apply(self, off_manager: ShowManager, n_active_effects : int = 0): + """ + Apply the KDE effect to the given manager. - def apply(self, effect_manager : EffectManager): - self.effect_manager = effect_manager + Parameters + ---------- + off_manager : ShowManager + Manager to apply the KDE effect (OFF SCREEN RENDERING ONLY). + n_active_effects : int, optional + Number of active effects in the manager. Default is 0. + TODO: EXPLICAR AQUI O PORQUE + + """ bandwidths = self.bandwidths opacity = self.opacity colormap = self.colormap @@ -325,15 +280,15 @@ def apply(self, effect_manager : EffectManager): scales = self.scales center_of_mass = self.center_of_mass - off_window = effect_manager.off_manager.window + off_window = off_manager.window shader_apply_effects(off_window, bill, gl_disable_depth) shader_apply_effects(off_window, bill, gl_set_additive_blending) attribute_to_actor(bill, np.repeat(bandwidths, 4), "in_bandwidth") attribute_to_actor(bill, np.repeat(scales, 4), "in_scale") - if effect_manager._n_active_effects > 0: - effect_manager.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) - effect_manager.off_manager.scene.add(self.bill) + if n_active_effects > 0: + off_manager.scene.GetActors().GetLastActor().SetVisibility(False) + off_manager.scene.add(self.bill) bill_bounds = self.bill.GetBounds() max_bandwidth = 2*4.0*np.max(bandwidths) @@ -347,7 +302,7 @@ def apply(self, effect_manager : EffectManager): actor_scales.max(), 0.0]]) - self.res = np.array(effect_manager.off_manager.size) + self.res = np.array(off_manager.size) # Render to second billboard for color map post-processing. tex_dec = import_fury_shader(os.path.join("effects", "color_mapping.glsl")) @@ -383,16 +338,16 @@ def apply(self, effect_manager : EffectManager): colormap_to_texture(cmap, "colormapTexture", textured_billboard) - def __call__(self, obj=None, event=None, effect_manager=None) -> Any: - - off_manager = effect_manager.off_manager - on_manager = effect_manager.on_manager + def __call__( + self, obj=None, event=None, + off_manager: ShowManager=None, on_manager: ShowManager = None) -> Any: on_window = on_manager.window off_window = off_manager.window - cam_params = effect_manager.on_manager.scene.get_camera() + cam_params = on_manager.scene.get_camera() off_manager.scene.set_camera(*cam_params) + self.res[0], self.res[1] = on_window.GetSize() off_window.SetSize(self.res[0], self.res[1]) shader_custom_uniforms(self.textured_billboard, "fragment").SetUniform2f("res", self.res) From f996852408339e33fcf3a41f3c591c8129fde3a8 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Wed, 16 Aug 2023 16:07:13 -0300 Subject: [PATCH 36/41] refactor: Cleaning refactored API --- docs/examples/viz_kde_render.py | 5 +- fury/effects/effect_manager.py | 18 +++---- fury/effects/effects.py | 95 ++++++++++++++++++++++----------- 3 files changed, 75 insertions(+), 43 deletions(-) diff --git a/docs/examples/viz_kde_render.py b/docs/examples/viz_kde_render.py index 77989dc94..b1d9362f8 100644 --- a/docs/examples/viz_kde_render.py +++ b/docs/examples/viz_kde_render.py @@ -56,8 +56,7 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int manager = ShowManager( scene, "KDE Render", - (size[0], - size[1])) + size) manager.initialize() @@ -101,7 +100,7 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int # to the scene, and start it as usual. #kde_actor = effects.kde(points, bandwidths, kernel="gaussian", colormap="inferno") -kde_effect = KDE(points, bandwidths, kernel="gaussian", colormap="inferno") +kde_effect = KDE(points, bandwidths, kernel="exponential", colormap="inferno") effects.add(kde_effect) interactive = True diff --git a/fury/effects/effect_manager.py b/fury/effects/effect_manager.py index 88fb2848c..205700f51 100644 --- a/fury/effects/effect_manager.py +++ b/fury/effects/effect_manager.py @@ -23,26 +23,26 @@ def __init__(self, manager : ShowManager): self._active_effects = {} def add(self, effect): + """Add an effect to the EffectManager. + + Parameters + ---------- + effect : callable + """ callback = partial( effect, off_manager=self.off_manager, on_manager=self.on_manager ) if hasattr(effect, "apply"): - effect.apply( - off_manager = self.off_manager, - n_active_effects = self._n_active_effects - ) + effect.apply(self) callback() callback_id = self.on_manager.add_iren_callback(callback, "RenderEvent") - - self._active_effects[effect.textured_billboard] = (callback_id, effect.bill) + self._active_effects[effect._onscreen_actor] = (callback_id, effect._offscreen_actor) self._n_active_effects += 1 - self.on_manager.scene.add(effect.textured_billboard) - - return effect.textured_billboard + self.on_manager.scene.add(effect._onscreen_actor) def remove_effect(self, effect_actor): """Remove an existing effect from the effects manager. diff --git a/fury/effects/effects.py b/fury/effects/effects.py index 9c3a670bb..d362af01a 100644 --- a/fury/effects/effects.py +++ b/fury/effects/effects.py @@ -4,6 +4,7 @@ import numpy as np from fury.actor import Actor, billboard from fury.colormap import create_colormap +from fury.effects import EffectManager from fury.io import load_image from fury.lib import Texture, WindowToImageFilter from fury.shaders import (attribute_to_actor, @@ -184,9 +185,35 @@ def colormap_to_texture( texture.SetBlendingMode(0) target_actor.GetProperty().SetTexture(texture_name, texture) - + class KDE(): + """ + Class that implements the Kernel Density Estimation effect of a given set of points. + + Parameters + ---------- + points : np.ndarray (N, 3) + Array of points to be displayed. + bandwidths : np.ndarray (1, ) or (N, 1) + Array of bandwidths to be used in the KDE calculations. Must be one or one for each point. + kernel : str, optional + Kernel to be used for the distribution calculation. The available options are: + * "cosine" + * "epanechnikov" + * "exponential" + * "gaussian" + * "linear" + * "tophat" + opacity : float, optional + Opacity of the effect. + colormap : str, optional. + Colormap matplotlib name for the KDE rendering. Default is "viridis". + custom_colormap : np.ndarray (N, 4), optional + Custom colormap for the KDE rendering. Default is none which means no + custom colormap is desired. If passed, will overwrite matplotlib colormap + chosen in the previous parameter. + """ def __init__(self, points : np.ndarray, bandwidths, @@ -194,6 +221,11 @@ def __init__(self, opacity : float = 1.0, colormap : str = "viridis", custom_colormap : np.array = None): + + self._offscreen_actor = None + self._onscreen_actor = None + self.res = None + self.points = points self.bandwidths = bandwidths self.kernel = kernel @@ -222,7 +254,6 @@ def __init__(self, out_scale = in_scale; """ - varying_fs_dec = """ varying float out_bandwidth; varying float out_scale; @@ -237,7 +268,6 @@ def __init__(self, fragOutput0 = vec4(color, 1.0); """ - fs_dec = compose_shader([varying_fs_dec, kde_fs_dec]) """Scales parameter will be defined by the empirical rule: @@ -247,7 +277,7 @@ def __init__(self, scales = 2*3.0*np.copy(bandwidths) self.center_of_mass = np.average(points, axis=0) - self.bill = billboard( + self._offscreen_actor = billboard( points, (0.0, 0.0, @@ -257,40 +287,38 @@ def __init__(self, fs_impl=kde_fs_impl, vs_dec=kde_vs_dec, vs_impl=kde_vs_impl) + self.scales = scales - # Blending and uniforms setup - def apply(self, off_manager: ShowManager, n_active_effects : int = 0): + + def apply(self, effect_manager : EffectManager): """ - Apply the KDE effect to the given manager. + Apply the KDE effect to the given EffectManager. Parameters ---------- - off_manager : ShowManager - Manager to apply the KDE effect (OFF SCREEN RENDERING ONLY). - n_active_effects : int, optional - Number of active effects in the manager. Default is 0. - TODO: EXPLICAR AQUI O PORQUE - + effect_manager : EffectManager. """ bandwidths = self.bandwidths opacity = self.opacity colormap = self.colormap custom_colormap = self.custom_colormap - bill = self.bill + bill = self._offscreen_actor scales = self.scales center_of_mass = self.center_of_mass - off_window = off_manager.window + off_window = effect_manager.off_manager.window + + # Blending and uniforms setup shader_apply_effects(off_window, bill, gl_disable_depth) shader_apply_effects(off_window, bill, gl_set_additive_blending) attribute_to_actor(bill, np.repeat(bandwidths, 4), "in_bandwidth") attribute_to_actor(bill, np.repeat(scales, 4), "in_scale") - if n_active_effects > 0: - off_manager.scene.GetActors().GetLastActor().SetVisibility(False) - off_manager.scene.add(self.bill) + if effect_manager._n_active_effects > 0: + effect_manager.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) + effect_manager.off_manager.scene.add(bill) - bill_bounds = self.bill.GetBounds() + bill_bounds = bill.GetBounds() max_bandwidth = 2*4.0*np.max(bandwidths) actor_scales = np.array([[bill_bounds[1] - bill_bounds[0] + @@ -302,9 +330,9 @@ def apply(self, off_manager: ShowManager, n_active_effects : int = 0): actor_scales.max(), 0.0]]) - self.res = np.array(off_manager.size) + self.res = np.array(effect_manager.off_manager.size) - # Render to second billboard for color map post-processing. + # Shaders for the onscreen textured billboard. tex_dec = import_fury_shader(os.path.join("effects", "color_mapping.glsl")) tex_impl = """ @@ -324,7 +352,7 @@ def apply(self, off_manager: ShowManager, n_active_effects : int = 0): scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) - self.textured_billboard = textured_billboard + self._onscreen_actor = textured_billboard shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.res) shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) @@ -338,27 +366,32 @@ def apply(self, off_manager: ShowManager, n_active_effects : int = 0): colormap_to_texture(cmap, "colormapTexture", textured_billboard) - def __call__( - self, obj=None, event=None, - off_manager: ShowManager=None, on_manager: ShowManager = None) -> Any: + def __call__(self, obj=None, event=None, + off_manager: ShowManager=None, on_manager: ShowManager = None) -> Any: + """Callback of the KDE effect.""" on_window = on_manager.window off_window = off_manager.window + # 1. Updating offscreen renderer. cam_params = on_manager.scene.get_camera() off_manager.scene.set_camera(*cam_params) - self.res[0], self.res[1] = on_window.GetSize() - off_window.SetSize(self.res[0], self.res[1]) - shader_custom_uniforms(self.textured_billboard, "fragment").SetUniform2f("res", self.res) + off_window.SetSize(*self.res) + + # 2. Updating variables. + shader_custom_uniforms(self._onscreen_actor, "fragment").SetUniform2f("res", self.res) + shader_apply_effects(off_window, self._offscreen_actor, gl_disable_depth) + shader_apply_effects(off_window, self._offscreen_actor, gl_set_additive_blending) + + # 3. Renders the offscreen scene. off_manager.scene.Modified() - shader_apply_effects(off_window, self.bill, gl_disable_depth) - shader_apply_effects(off_window, self.bill, gl_set_additive_blending) off_manager.render() + # 4. Passes the offscreen as a texture to the post-processing actor window_to_texture( off_manager.window, "screenTexture", - self.textured_billboard, + self._onscreen_actor, blending_mode="Interpolate", d_type="rgba") From daa810b83a51cc7b1136953b302814bb0361693a Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Thu, 17 Aug 2023 15:16:24 -0300 Subject: [PATCH 37/41] test: Added tests for effects module and tweaked multiple effects handling --- fury/actors/tests/test_effect_manager.py | 324 ----------------------- 1 file changed, 324 deletions(-) delete mode 100644 fury/actors/tests/test_effect_manager.py diff --git a/fury/actors/tests/test_effect_manager.py b/fury/actors/tests/test_effect_manager.py deleted file mode 100644 index 70728d8de..000000000 --- a/fury/actors/tests/test_effect_manager.py +++ /dev/null @@ -1,324 +0,0 @@ -import numpy as np -import numpy.testing as npt -import os - -from fury.actor import (billboard, - cube) -from fury.actors.effect_manager import (colormap_to_texture, - EffectManager, - texture_to_actor, - window_to_texture) -from fury.colormap import create_colormap -from fury.lib import Texture -from fury.shaders import (shader_custom_uniforms, - import_fury_shader) -from fury.window import (Scene, - ShowManager, - record) -from fury import window - -width, height = (400, 400) - -WRAP_MODE_DIC = {"clamptoedge" : Texture.ClampToEdge, - "repeat" : Texture.Repeat, - "mirroredrepeat" : Texture.MirroredRepeat, - "clamptoborder" : Texture.ClampToBorder} - -BLENDING_MODE_DIC = {"none" : 0, "replace" : 1, - "modulate" : 2, "add" : 3, - "addsigned" : 4, "interpolate" : 5, - "subtract" : 6} - -points = np.array([[0.36600749, 0.65827962, 0.53083986], - [0.97657922, 0.78730041, 0.13946709], - [0.7441061, 0.26322696, 0.8683115], - [0.14606987, 0.05490296, 0.98723486], - [0.71673873, 0.29188497, 0.02825102], - [0.90364963, 0.06387054, 0.91557011], - [0.11106939, 0.73972495, 0.49771819], - [0.63509055, 0.26659524, 0.4790886], - [0.20590893, 0.56012136, 0.78304187], - [0.30247726, 0.28023438, 0.6883304], - [0.58971475, 0.67312749, 0.47656539], - [0.26257592, 0.23964672, 0.64210249], - [0.26631165, 0.35701288, 0.88390072], - [0.01108113, 0.87276217, 0.99321825], - [0.68792169, 0.42725589, 0.92290326], - [0.09702907, 0.69950028, 0.97210289], - [0.86744636, 0.29614399, 0.2729772], - [0.77511449, 0.6912353, 0.97596621], - [0.5919642, 0.25713794, 0.0692452], - [0.47674521, 0.94254354, 0.71231971], - [0.50177591, 0.19320157, 0.91493713], - [0.27073903, 0.58171665, 0.79582017], - [0.76282237, 0.35119548, 0.80971555], - [0.43065933, 0.87678895, 0.57491155], - [0.34213045, 0.70619672, 0.43970999], - [0.38793158, 0.33048163, 0.91679507], - [0.68375111, 0.47934201, 0.86197378], - [0.67829585, 0.80616031, 0.76974334], - [0.01784785, 0.24857252, 0.89913317], - [0.8458996, 0.51551657, 0.69597985]]) - -bandwidths = np.array([[0.56193862], [0.1275334], [0.91069059], - [0.01177131], [0.67799239], [0.95772282], - [0.55834784], [0.60151661], [0.25946789], - [0.88343075], [0.24011991], [0.05879632], - [0.6370561], [0.23859789], [0.18654873], - [0.70008281], [0.02968318], [0.01304724], - [0.08251756], [0.625351], [0.89982588], - [0.62378987], [0.8661594], [0.05583442], - [0.60157791], [0.84737657], [0.36433019], - [0.13263502], [0.30937519], [0.88979053]]) - - -def test_window_to_texture(interactive=False): - fs_impl = """ - vec2 res_factor = vec2(res.y/res.x, 1.0); - vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - vec4 tex = texture(screenTexture, renorm_tex); - fragOutput0 = vec4(tex); - """ - - on_scene = window.Scene() - on_scene.set_camera(position=(-2, 1, -2), - focal_point=(0.0, - 0.0, - 0.0), - view_up=(0.0, 0.0, 0.0)) - - on_manager = window.ShowManager( - on_scene, - "on_manager", - (width, - height)) - - off_scene = window.Scene() - off_scene.set_camera(position=(-4, 2, -4), - focal_point=(0.0, - 0.0, - 0.0), - view_up=(0.0, 0.0, 0.0)) - - off_manager = window.ShowManager( - off_scene, - "off_manager", - (width, - height)) - - off_manager.window.SetOffScreenRendering(True) - off_manager.initialize() - - scale = np.array([[width/height, 1.0, 0.0]]) - c = cube(np.array([[0.0, 0.0, 0.0]]), colors=(1.0, 1.0, 0.0)) - bill = billboard(np.array([[0.0, 0.0, 0.0]]), scales=scale, fs_impl=fs_impl) - shader_custom_uniforms(bill, "fragment").SetUniform2f("res", off_manager.size) - - off_manager.scene.add(c) - on_manager.scene.add(bill) - - tex_name = "screenTexture" - blending_mode = "Interpolate" - dtype = "RGBA" - wrap_mode = "ClampToBorder" - border_color = (0.0, 0.0, 0.0, 1.0) - interpolate = True - - off_manager.render() - window_to_texture(off_manager.window, - tex_name, - bill, - blending_mode=blending_mode, - wrap_mode=wrap_mode, - border_color=border_color, - interpolate=interpolate, - d_type=dtype) - - if interactive: - window.show(on_manager.scene) - - n_textures = bill.GetProperty().GetNumberOfTextures() - texture = bill.GetProperty().GetTexture(tex_name) - - npt.assert_equal(1, n_textures) - npt.assert_equal(WRAP_MODE_DIC[wrap_mode.lower()], texture.GetWrap()) - npt.assert_equal( - BLENDING_MODE_DIC[blending_mode.lower()], texture.GetBlendingMode()) - npt.assert_array_almost_equal(list(border_color), texture.GetBorderColor()) - npt.assert_equal(interpolate, texture.GetInterpolate()) - npt.assert_equal(True, texture.GetMipmap()) - - - -def test_texture_to_actor(interactive=False): - fs_impl = """ - vec2 res_factor = vec2(res.y/res.x, 1.0); - vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - vec4 tex = texture(screenTexture, renorm_tex); - fragOutput0 = vec4(tex); - """ - - on_scene = window.Scene() - on_scene.set_camera(position=(-2, 1, -2), - focal_point=(0.0, - 0.0, - 0.0), - view_up=(0.0, 0.0, 0.0)) - - on_manager = window.ShowManager( - on_scene, - "on_manager", - (width, - height)) - - scale = np.array([[width/height, 1.0, 0.0]]) - bill = billboard(np.array([[0.0, 0.0, 0.0]]), scales=scale, fs_impl=fs_impl) - shader_custom_uniforms(bill, "fragment").SetUniform2f("res", on_manager.size) - - on_manager.scene.add(bill) - - tex_name = "screenTexture" - blending_mode = "Interpolate" - dtype = "RGBA" - wrap_mode = "ClampToBorder" - border_color = (0.0, 0.0, 0.0, 1.0) - interpolate = True - - texture_to_actor("test_window.png", - tex_name, - bill, - blending_mode=blending_mode, - wrap_mode=wrap_mode, - border_color=border_color, - interpolate=interpolate) - - if interactive: - window.show(on_manager.scene) - - n_textures = bill.GetProperty().GetNumberOfTextures() - texture = bill.GetProperty().GetTexture(tex_name) - - npt.assert_equal(1, n_textures) - npt.assert_equal(WRAP_MODE_DIC[wrap_mode.lower()], texture.GetWrap()) - npt.assert_equal( - BLENDING_MODE_DIC[blending_mode.lower()], texture.GetBlendingMode()) - npt.assert_array_almost_equal(list(border_color), texture.GetBorderColor()) - npt.assert_equal(interpolate, texture.GetInterpolate()) - npt.assert_equal(True, texture.GetMipmap()) - - -def test_colormap_to_actor(interactive=False): - - fs_dec = import_fury_shader(os.path.join("effects", "color_mapping.glsl")) - - fs_impl = """ - vec2 res_factor = vec2(res.y/res.x, 1.0); - vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - float intensity = renorm_tex.x; - vec4 tex = color_mapping(intensity, colormapTexture); - fragOutput0 = vec4(tex); - """ - - on_scene = window.Scene() - on_scene.set_camera(position=(-2, 1, -2), - focal_point=(0.0, - 0.0, - 0.0), - view_up=(0.0, 0.0, 0.0)) - - on_manager = window.ShowManager( - on_scene, - "on_manager", - (width, - height)) - - scale = 3.4*np.array([[width/height, 1.0, 0.0]]) - bill = billboard(np.array([[0.0, 0.0, 0.0]]), - scales=scale, fs_impl=fs_impl, fs_dec=fs_dec) - shader_custom_uniforms(bill, "fragment").SetUniform2f("res", on_manager.size) - - on_manager.scene.add(bill) - - tex_name = "colormapTexture" - interpolate = True - - colormap_to_texture(create_colormap(np.arange(0.0, 1.0, 1/256), "viridis"), - tex_name, - bill, - interpolate) - - if interactive: - window.show(on_manager.scene) - - n_textures = bill.GetProperty().GetNumberOfTextures() - texture = bill.GetProperty().GetTexture(tex_name) - - npt.assert_equal(1, n_textures) - npt.assert_equal(WRAP_MODE_DIC["ClampToEdge".lower()], texture.GetWrap()) - npt.assert_equal(BLENDING_MODE_DIC["None".lower()], texture.GetBlendingMode()) - npt.assert_equal(interpolate, texture.GetInterpolate()) - npt.assert_equal(True, texture.GetMipmap()) - - -def test_effect_manager_setup(interactive=False): - - scene = Scene() - scene.set_camera(position=(-24, 20, -40), - focal_point=(0.0, - 0.0, - 0.0), - view_up=(0.0, 0.0, 0.0)) - - manager = ShowManager( - scene, - "Test EffectManager", - (width, - height)) - - manager.initialize() - - em = EffectManager(manager) - - npt.assert_equal(True, em.scene.get_camera() == manager.scene.get_camera()) - npt.assert_equal(True, manager == em.on_manager) - npt.assert_array_equal(manager.window.GetSize(), em.off_manager.window.GetSize()) - npt.assert_equal(True, em.scene == em.off_manager.scene) - npt.assert_equal(True, em.off_manager.window.GetOffScreenRendering()) - npt.assert_equal(0, em._n_active_effects) - npt.assert_equal({}, em._active_effects) - - -def test_remove_effect(interactive=False): - scene = window.Scene() - scene.set_camera(position=(-24, 20, -40), - focal_point=(0.0, - 0.0, - 0.0), - view_up=(0.0, 0.0, 0.0)) - - manager = window.ShowManager( - scene, - "Test remove_effect", - (width, - height)) - - manager.initialize() - - em = EffectManager(manager) - - kde_actor = em.kde(points, bandwidths, colormap="inferno") - - manager.scene.add(kde_actor) - em.remove_effect(kde_actor) - - if interactive: - window.show(manager.scene) - - off_ascene = window.analyze_scene(em.off_manager.scene) - on_ascene = window.analyze_scene(manager.scene) - - npt.assert_equal(0, em.on_manager.iren.HasObserver("RenderEvent")) - npt.assert_equal(0, on_ascene.actors) - npt.assert_equal(0, off_ascene.actors) - npt.assert_equal({}, em._active_effects) - npt.assert_equal(0, em._n_active_effects) From f0d855cd5f47951afc189232a9c7ffc98ec5b173 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Thu, 17 Aug 2023 15:18:49 -0300 Subject: [PATCH 38/41] test: Implemented the features from last commit --- docs/examples/viz_kde_render.py | 34 +-- fury/effects/effect_manager.py | 30 +-- fury/effects/effects.py | 92 ++++--- fury/tests/test_actors.py | 79 +----- fury/tests/test_effects.py | 423 ++++++++++++++++++++++++++++++++ 5 files changed, 521 insertions(+), 137 deletions(-) create mode 100644 fury/tests/test_effects.py diff --git a/docs/examples/viz_kde_render.py b/docs/examples/viz_kde_render.py index b1d9362f8..c242b2530 100644 --- a/docs/examples/viz_kde_render.py +++ b/docs/examples/viz_kde_render.py @@ -2,10 +2,11 @@ ====================================================================== Fury Kernel Density Estimation rendering Actor ====================================================================== -This example shows how to use the KDE actor. This is a special actor in Fury that works -with post-processing effects to render kernel density estimations of a given set of points -in real-time to the screen. For better understanding on KDEs, check this -`Wikipedia page `_ about it. +This example shows how to use the KDE effect. This is a feature in Fury +that uses a post-processing stage to render kernel density estimations of a +given set of points in real-time to the screen. For better understanding +on KDEs, check this `Wikipedia page `_ +about it. For this example, you will only need the modules below: """ @@ -58,7 +59,6 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int "KDE Render", size) -manager.initialize() #################################################################### # ``numpy.random.rand`` will be used to generate random points, which @@ -79,7 +79,6 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int bandwidths = normalize(np.random.rand(n_points, 1), 0.05, 0.2) - ################################################################### # Now, for the KDE render, a special class is needed, the # ``EffectManager``. This class is needed to manage the post-processing @@ -89,19 +88,22 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int # worry, none of this will need to be setup by you! Just call the # ``EffectManager`` like below, passing the manager to it: -effects = EffectManager(manager) +effects_m = EffectManager(manager) ################################################################### -# After having the ``effects`` setup, just call the kde actor method -# from it, passing the points, bandwidth, and other optional options -# if wished, like the kernel to be used or the colormap desired. -# The colormaps are by default taken from *matplotlib*, but a -# custom one can be passed. After calling it, just pass the actor -# to the scene, and start it as usual. - -#kde_actor = effects.kde(points, bandwidths, kernel="gaussian", colormap="inferno") +# After having the ``effects_m`` setup, we can add effects to it. +# Here, we will use the ``KDE`` effect, that comes from ``fury.effects.effects`` +# module. When we call it, we pass the points, bandwidth, and other optional options +# if wished, like the kernel to be used or the colormap desired. The available kernels +# can be found inside KDE's description. The colormaps are by default taken from *matplotlib*, +# but a custom one can be passed. After calling it, it returns the KDE class, that will +# then need to be added to the ``EffectManager`` with the ``add()`` method. + kde_effect = KDE(points, bandwidths, kernel="exponential", colormap="inferno") -effects.add(kde_effect) +effects_m.add(kde_effect) + +#################################################################### +# Having that setup, just start the rendering process to see the results, and we are done here! interactive = True diff --git a/fury/effects/effect_manager.py b/fury/effects/effect_manager.py index 205700f51..6279d6576 100644 --- a/fury/effects/effect_manager.py +++ b/fury/effects/effect_manager.py @@ -11,6 +11,7 @@ class EffectManager(): Target manager that will render post processed actors.""" def __init__(self, manager : ShowManager): + manager.initialize() self.scene = Scene() cam_params = manager.scene.get_camera() self.scene.set_camera(*cam_params) @@ -22,12 +23,14 @@ def __init__(self, manager : ShowManager): self._n_active_effects = 0 self._active_effects = {} - def add(self, effect): - """Add an effect to the EffectManager. - + def add(self, effect : callable): + """Add an effect to the EffectManager. The effect must have a callable property, + that will act as the callback for the interactor. Check the KDE effect for reference. + Parameters ---------- effect : callable + Effect to be added to the `EffectManager`. """ callback = partial( effect, @@ -40,25 +43,24 @@ def add(self, effect): callback() callback_id = self.on_manager.add_iren_callback(callback, "RenderEvent") - self._active_effects[effect._onscreen_actor] = (callback_id, effect._offscreen_actor) + self._active_effects[effect] = (callback_id, effect._offscreen_actor) self._n_active_effects += 1 self.on_manager.scene.add(effect._onscreen_actor) - def remove_effect(self, effect_actor): - """Remove an existing effect from the effects manager. - Beware that the effect and the actor will be removed from the rendering pipeline - and shall not work after this action. + def remove_effect(self, effect): + """ + Remove an existing effect from the effect manager. Parameters ---------- - effect_actor : actor.Actor - Actor of effect to be removed. + effect_actor : callable + Effect to be removed. """ if self._n_active_effects > 0: - self.on_manager.iren.RemoveObserver(self._active_effects[effect_actor][0]) - self.off_manager.scene.RemoveActor(self._active_effects[effect_actor][1]) - self.on_manager.scene.RemoveActor(effect_actor) - self._active_effects.pop(effect_actor) + self.on_manager.iren.RemoveObserver(self._active_effects[effect][0]) + self.off_manager.scene.RemoveActor(self._active_effects[effect][1]) + self.on_manager.scene.RemoveActor(effect._onscreen_actor) + self._active_effects.pop(effect) self._n_active_effects -= 1 else: raise IndexError("Manager has no active effects.") diff --git a/fury/effects/effects.py b/fury/effects/effects.py index d362af01a..ababf7f6b 100644 --- a/fury/effects/effects.py +++ b/fury/effects/effects.py @@ -19,7 +19,6 @@ ShowManager) - WRAP_MODE_DIC = {"clamptoedge" : Texture.ClampToEdge, "repeat" : Texture.Repeat, "mirroredrepeat" : Texture.MirroredRepeat, @@ -31,7 +30,6 @@ "subtract" : 6} - def window_to_texture( window : RenderWindow, texture_name : str, @@ -84,6 +82,7 @@ def window_to_texture( "rgba" : windowToImageFilter.SetInputBufferTypeToRGBA, "zbuffer" : windowToImageFilter.SetInputBufferTypeToZBuffer} type_dic[d_type.lower()]() + windowToImageFilter.Update() texture = Texture() texture.SetMipmap(True) @@ -152,6 +151,7 @@ def texture_to_actor( target_actor.GetProperty().SetTexture(texture_name, texture) + def colormap_to_texture( colormap : np.array, texture_name : str, @@ -185,7 +185,7 @@ def colormap_to_texture( texture.SetBlendingMode(0) target_actor.GetProperty().SetTexture(texture_name, texture) - + class KDE(): """ @@ -206,7 +206,7 @@ class KDE(): * "linear" * "tophat" opacity : float, optional - Opacity of the effect. + Opacity of the effect, defined between [0.0, 1.0]. colormap : str, optional. Colormap matplotlib name for the KDE rendering. Default is "viridis". custom_colormap : np.ndarray (N, 4), optional @@ -214,19 +214,24 @@ class KDE(): custom colormap is desired. If passed, will overwrite matplotlib colormap chosen in the previous parameter. """ + def __init__(self, - points : np.ndarray, - bandwidths, - kernel : str = "gaussian", - opacity : float = 1.0, - colormap : str = "viridis", - custom_colormap : np.array = None): - + points : np.ndarray, + bandwidths, + kernel : str = "gaussian", + opacity : float = 1.0, + colormap : str = "viridis", + custom_colormap : np.array = None): + + # Effect required variables self._offscreen_actor = None self._onscreen_actor = None self.res = None - self.points = points + # Unmutable variables + self._points = points + + # Mutable variables self.bandwidths = bandwidths self.kernel = kernel self.opacity = opacity @@ -287,8 +292,11 @@ def __init__(self, fs_impl=kde_fs_impl, vs_dec=kde_vs_dec, vs_impl=kde_vs_impl) - - self.scales = scales + + attribute_to_actor( + self._offscreen_actor, np.repeat( + bandwidths, 4), "in_bandwidth") + attribute_to_actor(self._offscreen_actor, np.repeat(scales, 4), "in_scale") def apply(self, effect_manager : EffectManager): """ @@ -303,17 +311,15 @@ def apply(self, effect_manager : EffectManager): colormap = self.colormap custom_colormap = self.custom_colormap bill = self._offscreen_actor - scales = self.scales center_of_mass = self.center_of_mass off_window = effect_manager.off_manager.window - # Blending and uniforms setup + # Blending setup shader_apply_effects(off_window, bill, gl_disable_depth) shader_apply_effects(off_window, bill, gl_set_additive_blending) - attribute_to_actor(bill, np.repeat(bandwidths, 4), "in_bandwidth") - attribute_to_actor(bill, np.repeat(scales, 4), "in_scale") + # Important step to guarantee API handles multiple effects if effect_manager._n_active_effects > 0: effect_manager.off_manager.scene.GetActors().GetLastActor().SetVisibility(False) effect_manager.off_manager.scene.add(bill) @@ -352,9 +358,17 @@ def apply(self, effect_manager : EffectManager): scales=scale, fs_dec=tex_dec, fs_impl=tex_impl) + shader_custom_uniforms( + textured_billboard, + "fragment").SetUniform2f( + "res", + self.res) + shader_custom_uniforms( + textured_billboard, + "fragment").SetUniformf( + "u_opacity", + opacity) self._onscreen_actor = textured_billboard - shader_custom_uniforms(textured_billboard, "fragment").SetUniform2f("res", self.res) - shader_custom_uniforms(textured_billboard, "fragment").SetUniformf("u_opacity", opacity) # Disables the texture warnings textured_billboard.GetProperty().GlobalWarningDisplayOff() @@ -366,32 +380,52 @@ def apply(self, effect_manager : EffectManager): colormap_to_texture(cmap, "colormapTexture", textured_billboard) + self.res[0], self.res[1] = effect_manager.on_manager.window.GetSize() + def __call__(self, obj=None, event=None, - off_manager: ShowManager=None, on_manager: ShowManager = None) -> Any: - """Callback of the KDE effect.""" + off_manager: ShowManager = None, on_manager: ShowManager = None) -> Any: + """Callback of the KDE effect. + obj : Any + This parameter is passed by VTK. + event : str + This parameter is passed by VTK. + off_manager : ShowManager + Offscreen manager. + on_manager : Show Manager + Onscreen manager.""" on_window = on_manager.window off_window = off_manager.window # 1. Updating offscreen renderer. - cam_params = on_manager.scene.get_camera() - off_manager.scene.set_camera(*cam_params) + camera = on_manager.scene.camera() + camera.SetClippingRange(0.01, 1000.01) + off_manager.scene.SetActiveCamera(camera) self.res[0], self.res[1] = on_window.GetSize() off_window.SetSize(*self.res) # 2. Updating variables. - shader_custom_uniforms(self._onscreen_actor, "fragment").SetUniform2f("res", self.res) - shader_apply_effects(off_window, self._offscreen_actor, gl_disable_depth) - shader_apply_effects(off_window, self._offscreen_actor, gl_set_additive_blending) + shader_custom_uniforms( + self._onscreen_actor, + "fragment").SetUniform2f( + "res", + self.res) - # 3. Renders the offscreen scene. + # 3. Sets the visibility of the current actor to True + self._offscreen_actor.SetVisibility(True) + + # 4. Renders the offscreen scene. off_manager.scene.Modified() off_manager.render() - # 4. Passes the offscreen as a texture to the post-processing actor + # 5. Passes the offscreen as a texture to the post-processing actor window_to_texture( off_manager.window, "screenTexture", self._onscreen_actor, blending_mode="Interpolate", d_type="rgba") + + # 6. Sets visibility of the current actor to False + self._offscreen_actor.SetVisibility(False) + self._offscreen_actor.Modified() diff --git a/fury/tests/test_actors.py b/fury/tests/test_actors.py index 3f9fc33e2..8d8a6903f 100644 --- a/fury/tests/test_actors.py +++ b/fury/tests/test_actors.py @@ -11,7 +11,7 @@ from fury import primitive as fp from fury import shaders, window from fury.actor import grid -from fury.actors.effect_manager import EffectManager +from fury.effects.effect_manager import EffectManager from fury.decorators import skip_linux, skip_osx, skip_win from fury.deprecator import ExpiredDeprecationError @@ -1821,80 +1821,3 @@ def test_actors_primitives_count(): act = act_func(**args) npt.assert_equal(primitives_count_from_actor(act), primitives_count) -def test_kde_actor(interactive = False): - size = (400, 400) - scene = window.Scene() - scene.set_camera(position=(-24, 20, -40), - focal_point=(0.0, - 0.0, - 0.0), - view_up=(0.0, 0.0, 0.0)) - - manager = window.ShowManager( - scene, - "Test KDE", - size) - - manager.initialize() - - em = EffectManager(manager) - - points = 5.0*np.array([[0.36600749, 0.65827962, 0.53083986], - [0.97657922, 0.78730041, 0.13946709], - [0.7441061 , 0.26322696, 0.8683115 ], - [0.14606987, 0.05490296, 0.98723486], - [0.71673873, 0.29188497, 0.02825102], - [0.90364963, 0.06387054, 0.91557011], - [0.11106939, 0.73972495, 0.49771819], - [0.63509055, 0.26659524, 0.4790886 ], - [0.20590893, 0.56012136, 0.78304187], - [0.30247726, 0.28023438, 0.6883304 ], - [0.58971475, 0.67312749, 0.47656539], - [0.26257592, 0.23964672, 0.64210249], - [0.26631165, 0.35701288, 0.88390072], - [0.01108113, 0.87276217, 0.99321825], - [0.68792169, 0.42725589, 0.92290326], - [0.09702907, 0.69950028, 0.97210289], - [0.86744636, 0.29614399, 0.2729772 ], - [0.77511449, 0.6912353 , 0.97596621], - [0.5919642 , 0.25713794, 0.0692452 ], - [0.47674521, 0.94254354, 0.71231971], - [0.50177591, 0.19320157, 0.91493713], - [0.27073903, 0.58171665, 0.79582017], - [0.76282237, 0.35119548, 0.80971555], - [0.43065933, 0.87678895, 0.57491155], - [0.34213045, 0.70619672, 0.43970999], - [0.38793158, 0.33048163, 0.91679507], - [0.68375111, 0.47934201, 0.86197378], - [0.67829585, 0.80616031, 0.76974334], - [0.01784785, 0.24857252, 0.89913317], - [0.8458996, 0.51551657, 0.69597985]]) - - bandwidths = np.array([[0.56193862], [0.1275334 ], [0.91069059], - [0.01177131], [0.67799239], [0.95772282], - [0.55834784], [0.60151661], [0.25946789], - [0.88343075], [0.24011991], [0.05879632], - [0.6370561 ], [0.23859789], [0.18654873], - [0.70008281], [0.02968318], [0.01304724], - [0.08251756], [0.625351 ], [0.89982588], - [0.62378987], [0.8661594 ], [0.05583442], - [0.60157791], [0.84737657], [0.36433019], - [0.13263502], [0.30937519], [0.88979053]]) - - kde_actor = em.kde(points, bandwidths, colormap="inferno") - - manager.scene.add(kde_actor) - - if interactive: - window.show(manager.scene) - - off_ascene = window.analyze_scene(em.off_manager.scene) - - on_ascene = window.analyze_scene(manager.scene) - - on_obs = em.on_manager.iren.HasObserver("RenderEvent") - - npt.assert_equal(1, off_ascene.actors) - npt.assert_equal(1, em._n_active_effects) - npt.assert_equal(1, on_obs) - npt.assert_equal(1, on_ascene.actors) diff --git a/fury/tests/test_effects.py b/fury/tests/test_effects.py new file mode 100644 index 000000000..53f8d3ce1 --- /dev/null +++ b/fury/tests/test_effects.py @@ -0,0 +1,423 @@ +import numpy as np +import numpy.testing as npt +import os + +from fury.actor import (billboard, + cube) +from fury.effects import (colormap_to_texture, + EffectManager, + KDE, + texture_to_actor, + window_to_texture) +from fury.colormap import create_colormap +from fury.lib import Texture +from fury.shaders import (shader_custom_uniforms, + import_fury_shader) +from fury.window import (Scene, + ShowManager, + record) +from fury import window + +size = (400, 400) + +WRAP_MODE_DIC = {"clamptoedge" : Texture.ClampToEdge, + "repeat" : Texture.Repeat, + "mirroredrepeat" : Texture.MirroredRepeat, + "clamptoborder" : Texture.ClampToBorder} + +BLENDING_MODE_DIC = {"none" : 0, "replace" : 1, + "modulate" : 2, "add" : 3, + "addsigned" : 4, "interpolate" : 5, + "subtract" : 6} + +points = 5.0*np.array([[0.36600749, 0.65827962, 0.53083986], + [0.97657922, 0.78730041, 0.13946709], + [0.7441061, 0.26322696, 0.8683115], + [0.14606987, 0.05490296, 0.98723486], + [0.71673873, 0.29188497, 0.02825102], + [0.90364963, 0.06387054, 0.91557011], + [0.11106939, 0.73972495, 0.49771819], + [0.63509055, 0.26659524, 0.4790886], + [0.20590893, 0.56012136, 0.78304187], + [0.30247726, 0.28023438, 0.6883304], + [0.58971475, 0.67312749, 0.47656539], + [0.26257592, 0.23964672, 0.64210249], + [0.26631165, 0.35701288, 0.88390072], + [0.01108113, 0.87276217, 0.99321825], + [0.68792169, 0.42725589, 0.92290326], + [0.09702907, 0.69950028, 0.97210289], + [0.86744636, 0.29614399, 0.2729772], + [0.77511449, 0.6912353, 0.97596621], + [0.5919642, 0.25713794, 0.0692452], + [0.47674521, 0.94254354, 0.71231971], + [0.50177591, 0.19320157, 0.91493713], + [0.27073903, 0.58171665, 0.79582017], + [0.76282237, 0.35119548, 0.80971555], + [0.43065933, 0.87678895, 0.57491155], + [0.34213045, 0.70619672, 0.43970999], + [0.38793158, 0.33048163, 0.91679507], + [0.68375111, 0.47934201, 0.86197378], + [0.67829585, 0.80616031, 0.76974334], + [0.01784785, 0.24857252, 0.89913317], + [0.8458996, 0.51551657, 0.69597985]]) + +bandwidths = np.array([[0.56193862], [0.1275334], [0.91069059], + [0.01177131], [0.67799239], [0.95772282], + [0.55834784], [0.60151661], [0.25946789], + [0.88343075], [0.24011991], [0.05879632], + [0.6370561], [0.23859789], [0.18654873], + [0.70008281], [0.02968318], [0.01304724], + [0.08251756], [0.625351], [0.89982588], + [0.62378987], [0.8661594], [0.05583442], + [0.60157791], [0.84737657], [0.36433019], + [0.13263502], [0.30937519], [0.88979053]]) + + +def test_window_to_texture(interactive=False): + fs_impl = """ + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + vec4 tex = texture(screenTexture, renorm_tex); + fragOutput0 = vec4(tex); + """ + + on_scene = window.Scene() + on_scene.set_camera(position=(-2, 1, -2), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + + on_manager = window.ShowManager( + on_scene, + "on_manager", + size) + + off_scene = window.Scene() + off_scene.set_camera(position=(-4, 2, -4), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + + off_manager = window.ShowManager( + off_scene, + "off_manager", + size) + + off_manager.window.SetOffScreenRendering(True) + off_manager.initialize() + + scale = np.array([[size[0]/size[1], 1.0, 0.0]]) + c = cube(np.array([[0.0, 0.0, 0.0]]), colors=(1.0, 1.0, 0.0)) + bill = billboard(np.array([[0.0, 0.0, 0.0]]), scales=scale, fs_impl=fs_impl) + shader_custom_uniforms(bill, "fragment").SetUniform2f("res", off_manager.size) + + off_manager.scene.add(c) + on_manager.scene.add(bill) + + tex_name = "screenTexture" + blending_mode = "Interpolate" + dtype = "RGBA" + wrap_mode = "ClampToBorder" + border_color = (0.0, 0.0, 0.0, 1.0) + interpolate = True + + off_manager.render() + window_to_texture(off_manager.window, + tex_name, + bill, + blending_mode=blending_mode, + wrap_mode=wrap_mode, + border_color=border_color, + interpolate=interpolate, + d_type=dtype) + + if interactive: + window.show(on_manager.scene) + + n_textures = bill.GetProperty().GetNumberOfTextures() + texture = bill.GetProperty().GetTexture(tex_name) + + npt.assert_equal(1, n_textures) + npt.assert_equal(WRAP_MODE_DIC[wrap_mode.lower()], texture.GetWrap()) + npt.assert_equal( + BLENDING_MODE_DIC[blending_mode.lower()], texture.GetBlendingMode()) + npt.assert_array_almost_equal(list(border_color), texture.GetBorderColor()) + npt.assert_equal(interpolate, texture.GetInterpolate()) + npt.assert_equal(True, texture.GetMipmap()) + + +def test_texture_to_actor(interactive=False): + fs_impl = """ + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + vec4 tex = texture(screenTexture, renorm_tex); + fragOutput0 = vec4(tex); + """ + + on_scene = window.Scene() + on_scene.set_camera(position=(-2, 1, -2), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + + on_manager = window.ShowManager( + on_scene, + "on_manager", + size) + + scale = np.array([[size[0]/size[1], 1.0, 0.0]]) + bill = billboard(np.array([[0.0, 0.0, 0.0]]), scales=scale, fs_impl=fs_impl) + shader_custom_uniforms(bill, "fragment").SetUniform2f("res", on_manager.size) + + on_manager.scene.add(bill) + + tex_name = "screenTexture" + blending_mode = "Interpolate" + wrap_mode = "ClampToBorder" + border_color = (0.0, 0.0, 0.0, 1.0) + interpolate = True + + texture_to_actor("test_surface.png", + tex_name, + bill, + blending_mode=blending_mode, + wrap_mode=wrap_mode, + border_color=border_color, + interpolate=interpolate) + + if interactive: + window.show(on_manager.scene) + + n_textures = bill.GetProperty().GetNumberOfTextures() + texture = bill.GetProperty().GetTexture(tex_name) + + npt.assert_equal(1, n_textures) + npt.assert_equal(WRAP_MODE_DIC[wrap_mode.lower()], texture.GetWrap()) + npt.assert_equal( + BLENDING_MODE_DIC[blending_mode.lower()], texture.GetBlendingMode()) + npt.assert_array_almost_equal(list(border_color), texture.GetBorderColor()) + npt.assert_equal(interpolate, texture.GetInterpolate()) + npt.assert_equal(True, texture.GetMipmap()) + + +def test_colormap_to_actor(interactive=False): + + fs_dec = import_fury_shader(os.path.join("effects", "color_mapping.glsl")) + + fs_impl = """ + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + float intensity = renorm_tex.x; + vec4 tex = color_mapping(intensity, colormapTexture); + fragOutput0 = vec4(tex); + """ + + on_scene = window.Scene() + on_scene.set_camera(position=(-2, 1, -2), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + + on_manager = window.ShowManager( + on_scene, + "on_manager", + size) + + scale = 3.4*np.array([[size[0]/size[1], 1.0, 0.0]]) + bill = billboard(np.array([[0.0, 0.0, 0.0]]), + scales=scale, fs_impl=fs_impl, fs_dec=fs_dec) + shader_custom_uniforms(bill, "fragment").SetUniform2f("res", on_manager.size) + + on_manager.scene.add(bill) + + tex_name = "colormapTexture" + interpolate = True + + colormap_to_texture(create_colormap(np.arange(0.0, 1.0, 1/256), "viridis"), + tex_name, + bill, + interpolate) + + if interactive: + window.show(on_manager.scene) + + n_textures = bill.GetProperty().GetNumberOfTextures() + texture = bill.GetProperty().GetTexture(tex_name) + + npt.assert_equal(1, n_textures) + npt.assert_equal(WRAP_MODE_DIC["ClampToEdge".lower()], texture.GetWrap()) + npt.assert_equal(BLENDING_MODE_DIC["None".lower()], texture.GetBlendingMode()) + npt.assert_equal(interpolate, texture.GetInterpolate()) + npt.assert_equal(True, texture.GetMipmap()) + + +def test_effect_manager_setup(interactive=False): + + scene = Scene() + scene.set_camera(position=(-24, 20, -40), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + + manager = ShowManager( + scene, + "Test EffectManager", + size) + + em = EffectManager(manager) + + npt.assert_equal(True, em.scene.get_camera() == manager.scene.get_camera()) + npt.assert_equal(True, manager == em.on_manager) + npt.assert_array_equal(manager.window.GetSize(), em.off_manager.window.GetSize()) + npt.assert_equal(True, em.scene == em.off_manager.scene) + npt.assert_equal(True, em.off_manager.window.GetOffScreenRendering()) + npt.assert_equal(0, em._n_active_effects) + npt.assert_equal({}, em._active_effects) + + +def test_kde_effect(): + kde_effect = KDE(points, bandwidths, colormap="inferno") + + npt.assert_equal(True, hasattr(kde_effect, "_offscreen_actor")) + npt.assert_equal(True, hasattr(kde_effect, "_onscreen_actor")) + npt.assert_equal(True, hasattr(kde_effect, "res")) + + npt.assert_array_almost_equal(np.average(points, axis=0), kde_effect.center_of_mass) + npt.assert_equal(True, kde_effect._offscreen_actor is not None) + + +def test_kde_apply(): + scene = Scene() + scene.set_camera(position=(-24, 20, -40), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + + manager = ShowManager( + scene, + "Test KDE.apply()", + size) + + em = EffectManager(manager) + + kde_effect = KDE(points, bandwidths, colormap="inferno") + + offset = np.array([[5.0, 5.0, 5.0]]) + kde_effect_2 = KDE(points + offset, bandwidths, colormap="viridis") + + kde_effect.apply(em) + + npt.assert_equal(True, kde_effect._onscreen_actor is not None) + npt.assert_array_equal(size, kde_effect.res) + + +def test_call_kde(): + scene = Scene() + scene.set_camera(position=(-24, 20, -40), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + + manager = ShowManager( + scene, + "Test KDE.apply()", + size) + + em = EffectManager(manager) + + kde_effect = KDE(points, bandwidths, colormap="inferno") + kde_effect.apply(em) + kde_effect(off_manager=em.off_manager, on_manager=em.on_manager) + + off_camera = em.off_manager.scene.camera() + off_clip_range = off_camera.GetClippingRange() + off_cam_params = em.off_manager.scene.get_camera() + off_size = em.off_manager.window.GetSize() + + on_camera = em.on_manager.scene.camera() + on_clip_range = on_camera.GetClippingRange() + on_cam_params = em.on_manager.scene.get_camera() + on_size = em.on_manager.window.GetSize() + + npt.assert_array_equal(on_clip_range, off_clip_range) + npt.assert_array_equal(off_cam_params[0], on_cam_params[0]) + npt.assert_array_equal(off_cam_params[1], on_cam_params[1]) + npt.assert_array_equal(off_cam_params[2], on_cam_params[2]) + npt.assert_array_equal(off_size, on_size) + + +def test_add(interactive=False): + scene = window.Scene() + scene.set_camera(position=(-24, 20, -40), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + + manager = window.ShowManager( + scene, + "Test add", + size) + + em = EffectManager(manager) + + kde_effect = KDE(points, bandwidths, colormap="inferno") + + em.add(kde_effect) + + if interactive: + window.show(manager.scene) + + off_ascene = window.analyze_scene(em.off_manager.scene) + + on_ascene = window.analyze_scene(manager.scene) + + on_obs = em.on_manager.iren.HasObserver("RenderEvent") + + npt.assert_equal(True, em._n_active_effects > 0) + npt.assert_equal(1, off_ascene.actors) + npt.assert_equal(1, em._n_active_effects) + npt.assert_equal(1, len(em._active_effects)) + npt.assert_equal(1, on_obs) + npt.assert_equal(1, on_ascene.actors) + + +def test_remove_effect(interactive=False): + scene = window.Scene() + scene.set_camera(position=(-24, 20, -40), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + + manager = window.ShowManager( + scene, + "Test remove_effect", + size) + + em = EffectManager(manager) + + kde_effect = KDE(points, bandwidths, colormap="inferno") + em.add(kde_effect) + + em.remove_effect(kde_effect) + + if interactive: + window.show(manager.scene) + + off_ascene = window.analyze_scene(em.off_manager.scene) + on_ascene = window.analyze_scene(manager.scene) + on_obs = em.on_manager.iren.HasObserver("RenderEvent") + + npt.assert_equal(0, on_obs) + npt.assert_equal(0, on_ascene.actors) + npt.assert_equal(0, off_ascene.actors) + npt.assert_equal({}, em._active_effects) + npt.assert_equal(0, em._n_active_effects) From 700e0104042ceb2501ec562e93332b385acfc57f Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Sun, 20 Aug 2023 17:30:52 -0300 Subject: [PATCH 39/41] fix: Fixed KDE description and set interactive to False --- docs/examples/viz_kde_render.py | 2 +- fury/effects/effects.py | 2 +- fury/tests/test_actors.py | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/examples/viz_kde_render.py b/docs/examples/viz_kde_render.py index c242b2530..1d7d71514 100644 --- a/docs/examples/viz_kde_render.py +++ b/docs/examples/viz_kde_render.py @@ -105,7 +105,7 @@ def normalize(array : np.array, min : float = 0.0, max : float = 1.0, axis : int #################################################################### # Having that setup, just start the rendering process to see the results, and we are done here! -interactive = True +interactive = False if interactive: manager.start() diff --git a/fury/effects/effects.py b/fury/effects/effects.py index ababf7f6b..864cd8f11 100644 --- a/fury/effects/effects.py +++ b/fury/effects/effects.py @@ -195,7 +195,7 @@ class KDE(): ---------- points : np.ndarray (N, 3) Array of points to be displayed. - bandwidths : np.ndarray (1, ) or (N, 1) + bandwidths : float, np.ndarray (1, ) or (N, 1) Array of bandwidths to be used in the KDE calculations. Must be one or one for each point. kernel : str, optional Kernel to be used for the distribution calculation. The available options are: diff --git a/fury/tests/test_actors.py b/fury/tests/test_actors.py index 8d8a6903f..8d4722bdb 100644 --- a/fury/tests/test_actors.py +++ b/fury/tests/test_actors.py @@ -11,7 +11,6 @@ from fury import primitive as fp from fury import shaders, window from fury.actor import grid -from fury.effects.effect_manager import EffectManager from fury.decorators import skip_linux, skip_osx, skip_win from fury.deprecator import ExpiredDeprecationError @@ -1820,4 +1819,3 @@ def test_actors_primitives_count(): primitives_count = test_case[2] act = act_func(**args) npt.assert_equal(primitives_count_from_actor(act), primitives_count) - From f03bd6a52972d9da66180f4349999e9f1682b8b5 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Mon, 21 Aug 2023 11:39:08 -0300 Subject: [PATCH 40/41] style: Fixing upper case in shaders.base --- fury/shaders/base.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fury/shaders/base.py b/fury/shaders/base.py index 70189c6d6..101b20b53 100644 --- a/fury/shaders/base.py +++ b/fury/shaders/base.py @@ -426,10 +426,10 @@ def shader_custom_uniforms(actor, shader_type): * "fragment" * "geometry" """ - SHADER_FUNCTIONS = {"vertex" : actor.GetShaderProperty().GetVertexCustomUniforms(), + shader_functions = {"vertex" : actor.GetShaderProperty().GetVertexCustomUniforms(), "fragment" : actor.GetShaderProperty().GetFragmentCustomUniforms(), "geometry" : actor.GetShaderProperty().GetGeometryCustomUniforms()} - if shader_type.lower() not in SHADER_FUNCTIONS: + if shader_type.lower() not in shader_functions: raise ValueError("Shader type should be of type 'vertex', 'fragment' or 'geometry'.") - return SHADER_FUNCTIONS[shader_type.lower()] + return shader_functions[shader_type.lower()] From ac2ce0deffe0f68542219b75593799eaad0f66ba Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Mon, 21 Aug 2023 13:57:53 -0300 Subject: [PATCH 41/41] style: Added Union of float and np.ndarray in bandwidth --- fury/effects/effects.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fury/effects/effects.py b/fury/effects/effects.py index 864cd8f11..05ffa29ea 100644 --- a/fury/effects/effects.py +++ b/fury/effects/effects.py @@ -1,5 +1,5 @@ import os -from typing import Any +from typing import Any, Union as tUnion import numpy as np from fury.actor import Actor, billboard @@ -217,7 +217,7 @@ class KDE(): def __init__(self, points : np.ndarray, - bandwidths, + bandwidths : tUnion[float, np.ndarray], kernel : str = "gaussian", opacity : float = 1.0, colormap : str = "viridis",