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