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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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 bd335f10cc5241d636b022fadb75d61dabe50cb9 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Tue, 1 Aug 2023 17:07:20 -0300 Subject: [PATCH 21/30] fix: Camera position update --- docs/experimental/viz_kde_class.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/experimental/viz_kde_class.py b/docs/experimental/viz_kde_class.py index 1b089dd98..e8bc3abe5 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): @@ -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), @@ -48,7 +48,7 @@ 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.1, 0.3) offset = np.array([0.0, 0.0, 0.0]) points = points + np.tile(offset, points.shape[0]).reshape(points.shape) From a173dfd22f44716263149f463c0ed75c45434c0c Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Wed, 2 Aug 2023 11:18:21 -0300 Subject: [PATCH 22/30] feat: Updating UI --- docs/experimental/viz_kde_class.py | 2 +- fury/actors/effect_manager.py | 311 +++-------------------------- 2 files changed, 33 insertions(+), 280 deletions(-) diff --git a/docs/experimental/viz_kde_class.py b/docs/experimental/viz_kde_class.py index e8bc3abe5..bcff6f376 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): diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index 48037103e..91fa27178 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, @@ -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 @@ -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 @@ -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) @@ -200,6 +203,8 @@ 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, points : np.ndarray, @@ -290,10 +295,10 @@ 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) @@ -313,7 +318,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 +359,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,296 +376,39 @@ 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) + + 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") self._active_effects[textured_billboard] = callback_id + self._active_ui[textured_billboard] = panel.actors 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.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 @@ -672,6 +422,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 25921c366430c912100feacf075d648adbfe079d Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Thu, 3 Aug 2023 14:25:36 -0300 Subject: [PATCH 23/30] refactor: Added tracking of original actors of applied effects --- fury/actors/effect_manager.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/fury/actors/effect_manager.py b/fury/actors/effect_manager.py index 91fa27178..bccf1e1af 100644 --- a/fury/actors/effect_manager.py +++ b/fury/actors/effect_manager.py @@ -93,7 +93,6 @@ def window_to_texture( target_actor.GetProperty().SetTexture(texture_name, texture) - def texture_to_actor( path_to_texture : str, texture_name : str, @@ -135,13 +134,13 @@ def texture_to_actor( Texture RGBA border color. interpolate : bool Texture interpolation.""" - + texture = Texture() - colormapArray = load_image(path_to_texture) - colormapData = rgb_to_vtk(colormapArray) + textureArray = load_image(path_to_texture) + textureData = rgb_to_vtk(textureArray) - texture.SetInputDataObject(colormapData) + texture.SetInputDataObject(textureData) texture.SetBorderColor(*border_color) texture.SetWrap(WRAP_MODE_DIC[wrap_mode.lower()]) texture.SetInterpolate(interpolate) @@ -150,7 +149,6 @@ def texture_to_actor( target_actor.GetProperty().SetTexture(texture_name, texture) - def colormap_to_texture( colormap : np.array, texture_name : str, @@ -403,7 +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_effects[textured_billboard] = (callback_id, bill) self._active_ui[textured_billboard] = panel.actors self._n_active_effects += 1 @@ -420,12 +418,12 @@ 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) 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: From 2d72b48d43da5ec40bf3ee52c1eb21c0b08b668f Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Sun, 6 Aug 2023 16:22:25 -0300 Subject: [PATCH 24/30] fix: Trying to fix the noise --- docs/experimental/viz_kde_class.py | 2 +- fury/actors/effect_manager_alt.py | 26 ++++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/docs/experimental/viz_kde_class.py b/docs/experimental/viz_kde_class.py index bcff6f376..e8bc3abe5 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): diff --git a/fury/actors/effect_manager_alt.py b/fury/actors/effect_manager_alt.py index 1acf2ca0d..3d56a1946 100644 --- a/fury/actors/effect_manager_alt.py +++ b/fury/actors/effect_manager_alt.py @@ -281,6 +281,19 @@ def kde(self, } """ + + # converter = """ + # vec4 float_to_rgba(float value) { + # const float max = 4294967295.0; + # float fval = max*value; + # float a = fval/16777216.0 - fract(fval/16777216.0); + # float b = (fval - a)/65536.0 - fract((fval - a)/65536.0); + # float g = (fval - a - b)/256.0 - fract((fval - a - b)/256.0); + # float r = fval - a - b - g - fract(fval - a - b - g); + # return vec4(r, g, b, a)/255.0; + # } + # """ + kde_dec = import_fury_shader(os.path.join("utils", f"{kernel.lower()}_distribution.glsl")) kde_dec = compose_shader([kde_dec, converter]) @@ -338,6 +351,15 @@ def kde(self, } """ + + # de_converter = """ + # float rgba_to_float(vec4 v) { + # const float max = 4294967295.0; + # vec4 bitEnc = vec4(1.,256.,65536.0,16777216.0); + # return 255.0*dot(v, bitEnc)/max; + # } + # """ + 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 @@ -445,8 +467,8 @@ def kde(self, // 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); + 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; From 90069bfb7c53be98a02e52cdedde71dc53c7202e Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Mon, 7 Aug 2023 15:25:10 -0300 Subject: [PATCH 25/30] build: Changed viz_laplacian.py effect manager lib --- docs/experimental/viz_laplacian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/experimental/viz_laplacian.py b/docs/experimental/viz_laplacian.py index df801b8e4..4f819fc25 100644 --- a/docs/experimental/viz_laplacian.py +++ b/docs/experimental/viz_laplacian.py @@ -1,7 +1,7 @@ import numpy as np from fury.actor import cube, sphere -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): From 18bda117a21c887718f6933bc8d6b618ff7f0b3c Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Thu, 10 Aug 2023 16:49:18 -0300 Subject: [PATCH 26/30] feat: Hardcoded fullscreen billboard --- docs/experimental/viz_billboards.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/docs/experimental/viz_billboards.py b/docs/experimental/viz_billboards.py index 90c589f70..0c9d348ed 100644 --- a/docs/experimental/viz_billboards.py +++ b/docs/experimental/viz_billboards.py @@ -4,9 +4,10 @@ from fury.window import Scene, ShowManager width, height = (1350, 800) +cam_pos = np.array([0.0, 0.0, -1.0]) scene = Scene() -scene.set_camera(position=(-6, 5, -10), +scene.set_camera(position=(cam_pos[0], cam_pos[1], cam_pos[2]), focal_point=(0.0, 0.0, 0.0), @@ -20,9 +21,27 @@ manager.initialize() -scale = 3.4*np.array([[width/height, 1.0, 0.0]]) +center = np.array([[0.0, 0.0, 20.0]]) + + +#hardcoding the billboard to be fullscreen and centered +scale_factor_2 = np.abs(np.linalg.norm(center[0] - cam_pos))*np.sin(np.deg2rad(scene.camera().GetViewAngle()/2.0)) +print(scale_factor_2) + +scale = scale_factor_2*np.array([[width/height, 1.0, 0.0]]) + +bill = billboard(center, scales=scale, colors = (1.0, 0.0, 0.0)) + +def callback(obj = None, event = None): + pos, fp, vu = manager.scene.get_camera() + scale_factor_2 = np.abs(np.linalg.norm(center[0] - np.array(pos)))*np.sin(np.deg2rad(scene.camera().GetViewAngle()/2.0)) + scale = scale_factor_2*np.array([[width/height, 1.0, 0.0]]) + bill.SetScale(scale[0, 0], scale[0, 1], scale[0, 2]) + bill.Modified() + +callback() -bill = billboard(np.array([[0.0, 0.0, 0.0]]), scales=scale,colors = (1.0, 0.0, 0.0)) manager.scene.add(bill) +manager.add_iren_callback(callback, "RenderEvent") manager.start() \ No newline at end of file From 6ff0f0b409ff8d797e5dd4a8f1a0b798479e740d Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Thu, 10 Aug 2023 23:07:41 -0300 Subject: [PATCH 27/30] feat: testing billboard properties --- docs/experimental/viz_billboards_2.py | 66 +++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 docs/experimental/viz_billboards_2.py diff --git a/docs/experimental/viz_billboards_2.py b/docs/experimental/viz_billboards_2.py new file mode 100644 index 000000000..bc011aa4b --- /dev/null +++ b/docs/experimental/viz_billboards_2.py @@ -0,0 +1,66 @@ +import numpy as np + +from fury.actor import billboard +from fury.actors.effect_manager import texture_to_actor, shader_custom_uniforms +from fury.window import Scene, ShowManager + +width, height = (1200, 800) +cam_pos = np.array([0.0, 0.0, -1.0]) + +scene = Scene() +scene.set_camera(position=(cam_pos[0], cam_pos[1], cam_pos[2]), + focal_point=(0.0, + 0.0, + 0.0), + view_up=(0.0, 0.0, 0.0)) + +manager = ShowManager( + scene, + "demo", + (width, + height)) + +manager.initialize() + +center = np.array([[0.0, 0.0, 20.0]]) + + +#hardcoding the billboard to be fullscreen and centered +# scale_factor_2 = np.abs(np.linalg.norm(center[0] - cam_pos))*np.sin(np.deg2rad(scene.camera().GetViewAngle()/2.0)) +# print(scale_factor_2) + +tex_impl = """ + // Turning screen coordinates to texture coordinates + vec2 res_factor = vec2(res.y/res.x, 1.0); + vec2 tex_2 = gl_FragCoord.xy/res; + vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; + vec4 tex = texture(screenTexture, tex_2); + + fragOutput0 = vec4(tex); + //fragOutput0 = vec4(tex_2, 0.0, 1.0); + """ + +scale_factor_2 = 4 + +scale = scale_factor_2*np.array([[width/height, 1.0, 0.0]]) + +bill = billboard(center, scales=scale, colors = (1.0, 0.0, 0.0), fs_impl=tex_impl) + +texture_to_actor("C:\\Users\\Lampada\\Desktop\\fraser.png", "screenTexture", bill) +shader_custom_uniforms(bill, "fragment").SetUniform2f("res", [width, height]) + + + +# def callback(obj = None, event = None): +# pos, fp, vu = manager.scene.get_camera() +# scale_factor_2 = np.abs(np.linalg.norm(center[0] - np.array(pos)))*np.sin(np.deg2rad(scene.camera().GetViewAngle()/2.0)) +# scale = scale_factor_2*np.array([[width/height, 1.0, 0.0]]) +# bill.SetScale(scale[0, 0], scale[0, 1], scale[0, 2]) +# bill.Modified() + +# callback() + +manager.scene.add(bill) +# manager.add_iren_callback(callback, "RenderEvent") + +manager.start() \ No newline at end of file From ca2e1e3ab9546ef346232dc06d237bf978f5665e Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Sat, 12 Aug 2023 21:01:18 -0300 Subject: [PATCH 28/30] refactor: Toying around with zbuffer --- docs/experimental/viz_billboards_2.py | 70 +++++++++++++++++++++++---- fury/actors/effect_manager_alt.py | 3 +- 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/docs/experimental/viz_billboards_2.py b/docs/experimental/viz_billboards_2.py index bc011aa4b..c39cf0dd4 100644 --- a/docs/experimental/viz_billboards_2.py +++ b/docs/experimental/viz_billboards_2.py @@ -1,11 +1,13 @@ import numpy as np -from fury.actor import billboard -from fury.actors.effect_manager import texture_to_actor, shader_custom_uniforms -from fury.window import Scene, ShowManager +from fury.actor import billboard, cube, sphere +from fury.actors.effect_manager import texture_to_actor, shader_custom_uniforms, window_to_texture +from fury.shaders import shader_to_actor, shader_apply_effects +from fury.window import Scene, ShowManager, gl_disable_depth +from vtk import vtkOpenGLState width, height = (1200, 800) -cam_pos = np.array([0.0, 0.0, -1.0]) +cam_pos = np.array([5.0, 7.0, -1.0]) scene = Scene() scene.set_camera(position=(cam_pos[0], cam_pos[1], cam_pos[2]), @@ -22,7 +24,21 @@ manager.initialize() -center = np.array([[0.0, 0.0, 20.0]]) +off_scene = Scene() +off_scene.set_camera(*(scene.get_camera())) +off_scene.GetActiveCamera().SetClippingRange(0.1, 10.0) + +off_manager = ShowManager( + off_scene, + "demo", + (width, + height)) + +off_manager.window.SetOffScreenRendering(True) + +off_manager.initialize() + +center = np.array([[0.0, 0.0, 0.0]]) + 0.0*np.array([cam_pos/np.linalg.norm(cam_pos)]) #hardcoding the billboard to be fullscreen and centered @@ -30,13 +46,20 @@ # print(scale_factor_2) tex_impl = """ + //gl_FragDepth = gl_FragCoord.x/res.x; + //gl_FragDepth = gl_FragCoord.z; + fragOutput0 = vec4(0.0, 1.0, 0.0, 1.0); + """ + +tex_impl_2 = """ // Turning screen coordinates to texture coordinates vec2 res_factor = vec2(res.y/res.x, 1.0); vec2 tex_2 = gl_FragCoord.xy/res; vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - vec4 tex = texture(screenTexture, tex_2); + //float tex = texture(screenTexture, renorm_tex).b; + vec4 tex = texture(screenTexture, renorm_tex); - fragOutput0 = vec4(tex); + fragOutput0 = vec4(tex.rgb, 1.0); //fragOutput0 = vec4(tex_2, 0.0, 1.0); """ @@ -45,9 +68,23 @@ scale = scale_factor_2*np.array([[width/height, 1.0, 0.0]]) bill = billboard(center, scales=scale, colors = (1.0, 0.0, 0.0), fs_impl=tex_impl) +cube_actor = cube(center) +shader_to_actor(cube_actor, "fragment", tex_impl) +shader_custom_uniforms(cube_actor, "fragment").SetUniform2f("res", [width, height]) +# shader_apply_effects(off_manager.window, cube_actor, gl_disable_depth) + + +sphere_actor = sphere(center + np.array([[1.0, 1.0, 1.0]]), colors = (0.0, 1.0, 0.0)) +shader_to_actor(sphere_actor, "fragment", tex_impl) +shader_custom_uniforms(cube_actor, "fragment").SetUniform2f("res", [width, height]) +# shader_apply_effects(off_manager.window, sphere_actor, gl_disable_depth) -texture_to_actor("C:\\Users\\Lampada\\Desktop\\fraser.png", "screenTexture", bill) -shader_custom_uniforms(bill, "fragment").SetUniform2f("res", [width, height]) + + +bill_2 = billboard(center, scales=scale, colors = (1.0, 0.0, 0.0), fs_impl=tex_impl_2) +shader_custom_uniforms(bill_2, "fragment").SetUniform2f("res", [width, height]) + +manager.scene.add(bill_2) @@ -60,7 +97,20 @@ # callback() -manager.scene.add(bill) + + +off_manager.scene.add(sphere_actor) +off_manager.scene.add(cube_actor) + +gl_state = vtkOpenGLState() +gl_state.vtkglDepthFunc(519) + + + +# off_manager.scene.add(bill) # manager.add_iren_callback(callback, "RenderEvent") +off_manager.render() +window_to_texture(off_manager.window, "screenTexture", bill_2, d_type="zbuffer") + manager.start() \ No newline at end of file diff --git a/fury/actors/effect_manager_alt.py b/fury/actors/effect_manager_alt.py index 3d56a1946..18b5431d4 100644 --- a/fury/actors/effect_manager_alt.py +++ b/fury/actors/effect_manager_alt.py @@ -76,7 +76,8 @@ 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() From 82fbbb22d111d1a8c208c93062ba86f8a3dda650 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Sat, 19 Aug 2023 12:04:28 -0300 Subject: [PATCH 29/30] feat: Trying median filter --- fury/actors/effect_manager_alt.py | 47 +++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/fury/actors/effect_manager_alt.py b/fury/actors/effect_manager_alt.py index 18b5431d4..025dee1e2 100644 --- a/fury/actors/effect_manager_alt.py +++ b/fury/actors/effect_manager_alt.py @@ -274,7 +274,7 @@ def kde(self, converter = """ vec4 float_to_rgba(float value) { - vec4 bitEnc = vec4(1.,255.,65025.,16581375.); + vec4 bitEnc = vec4(1.,256.,65536.0,16777216.0); vec4 enc = bitEnc * value; enc = fract(enc); enc -= enc.yzww * vec2(1./255., 0.).xxxy; @@ -346,7 +346,7 @@ def kde(self, de_converter = """ float rgba_to_float(vec4 v) { - vec4 bitEnc = vec4(1.,255.,65025.,16581375.); + vec4 bitEnc = vec4(1.,256.,65536.0,16777216.0); vec4 bitDec = 1./bitEnc; return dot(v, bitDec); } @@ -425,6 +425,39 @@ def kde(self, }; """ + + offsets = """ + 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}; + """ + + + median_filter = """ + float median_filter(sampler2D screenTexture, vec2 tex_coords, vec2 res){ + float sorted[9] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + + for(int i = 0; i < 9; i++) + sorted[i] = rgba_to_float(texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i]))); + + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 8 - i; j++) { + if (sorted[j] > sorted[j + 1]) { + float temp = sorted[j]; + sorted[j] = sorted[j + 1]; + sorted[j + 1] = temp; + } + } + } + + return sorted[4]; + } + """ + gauss_dec = """ vec4 kernel_calculator(sampler2D screenTexture, vec2 tex_coords, vec2 res){ vec4 value = vec4(0.0); @@ -462,18 +495,20 @@ 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, avg_filter]) + 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); + //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); + //float fintensity = median_filter(screenTexture, renorm_tex, res); fintensity = map(fintensity, min_value, max_value, 0.0, 1.0); if(fintensity<=0.0){ @@ -558,7 +593,7 @@ def kde_callback(obj = None, event = None): "screenTexture", textured_billboard, border_color = (0.0, 0.0, 0.0, 0.0), - blending_mode="Interpolate", + blending_mode="None", d_type = "rgba") converted_img = back_converter(img) From 1c4c52cb87a8d204a85e2ca7ee17a0d1d46a0014 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Sun, 27 Aug 2023 18:11:23 -0300 Subject: [PATCH 30/30] feat: UI slider for bandwidth intensity --- docs/experimental/viz_billboards.py | 47 -- docs/experimental/viz_billboards_2.py | 116 ---- docs/experimental/viz_kde_class.py | 2 +- docs/experimental/viz_laplacian.py | 66 -- fury/actors/effect_manager_alt.py | 908 ------------------------- fury/actors/effect_manager_alt_2.py | 909 -------------------------- 6 files changed, 1 insertion(+), 2047 deletions(-) delete mode 100644 docs/experimental/viz_billboards.py delete mode 100644 docs/experimental/viz_billboards_2.py delete mode 100644 docs/experimental/viz_laplacian.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 0c9d348ed..000000000 --- a/docs/experimental/viz_billboards.py +++ /dev/null @@ -1,47 +0,0 @@ -import numpy as np - -from fury.actor import billboard -from fury.window import Scene, ShowManager - -width, height = (1350, 800) -cam_pos = np.array([0.0, 0.0, -1.0]) - -scene = Scene() -scene.set_camera(position=(cam_pos[0], cam_pos[1], cam_pos[2]), - focal_point=(0.0, - 0.0, - 0.0), - view_up=(0.0, 0.0, 0.0)) - -manager = ShowManager( - scene, - "demo", - (width, - height)) - -manager.initialize() - -center = np.array([[0.0, 0.0, 20.0]]) - - -#hardcoding the billboard to be fullscreen and centered -scale_factor_2 = np.abs(np.linalg.norm(center[0] - cam_pos))*np.sin(np.deg2rad(scene.camera().GetViewAngle()/2.0)) -print(scale_factor_2) - -scale = scale_factor_2*np.array([[width/height, 1.0, 0.0]]) - -bill = billboard(center, scales=scale, colors = (1.0, 0.0, 0.0)) - -def callback(obj = None, event = None): - pos, fp, vu = manager.scene.get_camera() - scale_factor_2 = np.abs(np.linalg.norm(center[0] - np.array(pos)))*np.sin(np.deg2rad(scene.camera().GetViewAngle()/2.0)) - scale = scale_factor_2*np.array([[width/height, 1.0, 0.0]]) - bill.SetScale(scale[0, 0], scale[0, 1], scale[0, 2]) - bill.Modified() - -callback() - -manager.scene.add(bill) -manager.add_iren_callback(callback, "RenderEvent") - -manager.start() \ No newline at end of file diff --git a/docs/experimental/viz_billboards_2.py b/docs/experimental/viz_billboards_2.py deleted file mode 100644 index c39cf0dd4..000000000 --- a/docs/experimental/viz_billboards_2.py +++ /dev/null @@ -1,116 +0,0 @@ -import numpy as np - -from fury.actor import billboard, cube, sphere -from fury.actors.effect_manager import texture_to_actor, shader_custom_uniforms, window_to_texture -from fury.shaders import shader_to_actor, shader_apply_effects -from fury.window import Scene, ShowManager, gl_disable_depth -from vtk import vtkOpenGLState - -width, height = (1200, 800) -cam_pos = np.array([5.0, 7.0, -1.0]) - -scene = Scene() -scene.set_camera(position=(cam_pos[0], cam_pos[1], cam_pos[2]), - focal_point=(0.0, - 0.0, - 0.0), - view_up=(0.0, 0.0, 0.0)) - -manager = ShowManager( - scene, - "demo", - (width, - height)) - -manager.initialize() - -off_scene = Scene() -off_scene.set_camera(*(scene.get_camera())) -off_scene.GetActiveCamera().SetClippingRange(0.1, 10.0) - -off_manager = ShowManager( - off_scene, - "demo", - (width, - height)) - -off_manager.window.SetOffScreenRendering(True) - -off_manager.initialize() - -center = np.array([[0.0, 0.0, 0.0]]) + 0.0*np.array([cam_pos/np.linalg.norm(cam_pos)]) - - -#hardcoding the billboard to be fullscreen and centered -# scale_factor_2 = np.abs(np.linalg.norm(center[0] - cam_pos))*np.sin(np.deg2rad(scene.camera().GetViewAngle()/2.0)) -# print(scale_factor_2) - -tex_impl = """ - //gl_FragDepth = gl_FragCoord.x/res.x; - //gl_FragDepth = gl_FragCoord.z; - fragOutput0 = vec4(0.0, 1.0, 0.0, 1.0); - """ - -tex_impl_2 = """ - // Turning screen coordinates to texture coordinates - vec2 res_factor = vec2(res.y/res.x, 1.0); - vec2 tex_2 = gl_FragCoord.xy/res; - vec2 renorm_tex = res_factor*normalizedVertexMCVSOutput.xy*0.5 + 0.5; - //float tex = texture(screenTexture, renorm_tex).b; - vec4 tex = texture(screenTexture, renorm_tex); - - fragOutput0 = vec4(tex.rgb, 1.0); - //fragOutput0 = vec4(tex_2, 0.0, 1.0); - """ - -scale_factor_2 = 4 - -scale = scale_factor_2*np.array([[width/height, 1.0, 0.0]]) - -bill = billboard(center, scales=scale, colors = (1.0, 0.0, 0.0), fs_impl=tex_impl) -cube_actor = cube(center) -shader_to_actor(cube_actor, "fragment", tex_impl) -shader_custom_uniforms(cube_actor, "fragment").SetUniform2f("res", [width, height]) -# shader_apply_effects(off_manager.window, cube_actor, gl_disable_depth) - - -sphere_actor = sphere(center + np.array([[1.0, 1.0, 1.0]]), colors = (0.0, 1.0, 0.0)) -shader_to_actor(sphere_actor, "fragment", tex_impl) -shader_custom_uniforms(cube_actor, "fragment").SetUniform2f("res", [width, height]) -# shader_apply_effects(off_manager.window, sphere_actor, gl_disable_depth) - - - -bill_2 = billboard(center, scales=scale, colors = (1.0, 0.0, 0.0), fs_impl=tex_impl_2) -shader_custom_uniforms(bill_2, "fragment").SetUniform2f("res", [width, height]) - -manager.scene.add(bill_2) - - - -# def callback(obj = None, event = None): -# pos, fp, vu = manager.scene.get_camera() -# scale_factor_2 = np.abs(np.linalg.norm(center[0] - np.array(pos)))*np.sin(np.deg2rad(scene.camera().GetViewAngle()/2.0)) -# scale = scale_factor_2*np.array([[width/height, 1.0, 0.0]]) -# bill.SetScale(scale[0, 0], scale[0, 1], scale[0, 2]) -# bill.Modified() - -# callback() - - - -off_manager.scene.add(sphere_actor) -off_manager.scene.add(cube_actor) - -gl_state = vtkOpenGLState() -gl_state.vtkglDepthFunc(519) - - - -# off_manager.scene.add(bill) -# manager.add_iren_callback(callback, "RenderEvent") - -off_manager.render() -window_to_texture(off_manager.window, "screenTexture", bill_2, d_type="zbuffer") - -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 e8bc3abe5..bcff6f376 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): diff --git a/docs/experimental/viz_laplacian.py b/docs/experimental/viz_laplacian.py deleted file mode 100644 index 4f819fc25..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_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): - """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 diff --git a/fury/actors/effect_manager_alt.py b/fury/actors/effect_manager_alt.py deleted file mode 100644 index 025dee1e2..000000000 --- a/fury/actors/effect_manager_alt.py +++ /dev/null @@ -1,908 +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.,256.,65536.0,16777216.0); - vec4 enc = bitEnc * value; - enc = fract(enc); - enc -= enc.yzww * vec2(1./255., 0.).xxxy; - return enc; - } - """ - - - # converter = """ - # vec4 float_to_rgba(float value) { - # const float max = 4294967295.0; - # float fval = max*value; - # float a = fval/16777216.0 - fract(fval/16777216.0); - # float b = (fval - a)/65536.0 - fract((fval - a)/65536.0); - # float g = (fval - a - b)/256.0 - fract((fval - a - b)/256.0); - # float r = fval - a - b - g - fract(fval - a - b - g); - # return vec4(r, g, b, a)/255.0; - # } - # """ - - 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.,256.,65536.0,16777216.0); - vec4 bitDec = 1./bitEnc; - return dot(v, bitDec); - } - """ - - - # de_converter = """ - # float rgba_to_float(vec4 v) { - # const float max = 4294967295.0; - # vec4 bitEnc = vec4(1.,256.,65536.0,16777216.0); - # return 255.0*dot(v, bitEnc)/max; - # } - # """ - - 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 - }; - """ - - - offsets = """ - 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}; - """ - - - median_filter = """ - float median_filter(sampler2D screenTexture, vec2 tex_coords, vec2 res){ - float sorted[9] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; - - for(int i = 0; i < 9; i++) - sorted[i] = rgba_to_float(texture(screenTexture, tex_coords + vec2(1/res.x, 1/res.y)*vec2(x_offsets[i], y_offsets[i]))); - - for (int i = 0; i < 9; i++) { - for (int j = 0; j < 8 - i; j++) { - if (sorted[j] > sorted[j + 1]) { - float temp = sorted[j]; - sorted[j] = sorted[j + 1]; - sorted[j + 1] = temp; - } - } - } - - return sorted[4]; - } - """ - - 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]) - - 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); - //float fintensity = median_filter(screenTexture, renorm_tex, res); - 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="None", - 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