Skip to content
Snippets Groups Projects
Commit a854d29a authored by fima's avatar fima :beers:
Browse files

Merge branch 'k3d_samples' into 'main'

K3d samples

See merge request !81
parents 8abe2b39 1d6f4129
No related branches found
No related tags found
1 merge request!81K3d samples
...@@ -303,3 +303,45 @@ def get_css(): ...@@ -303,3 +303,45 @@ def get_css():
css_content = file.read() css_content = file.read()
return css_content return css_content
def scale_to_float16(arr):
"""
Scale a NumPy array to fit within the limits of the float16 data type.
Parameters:
- arr (numpy.ndarray): The input array to be scaled.
Returns:
- numpy.ndarray: The scaled array, with values adjusted to fit within the limits of float16 data type.
This function takes a NumPy array as input and checks if its maximum and minimum values
exceed the limits of the float16 data type. If necessary, it scales the positive and negative
parts of the array independently to fit within the range of float16.
"""
# Determine maximum and minimum values of the array
arr_max = np.max(arr)
arr_min = np.min(arr)
# Check if scaling is necessary for positive and negative parts separately
if arr_max > np.finfo(np.float16).max:
pos_scaled_arr = np.interp(arr[arr >= 0], (0, arr_max), (0, np.finfo(np.float16).max))
else:
pos_scaled_arr = arr[arr >= 0].astype(np.float16)
if arr_min < -np.finfo(np.float16).max:
neg_scaled_arr = np.interp(arr[arr < 0], (arr_min, 0), (-np.finfo(np.float16).max, 0))
else:
neg_scaled_arr = arr[arr < 0].astype(np.float16)
# Combine the scaled positive and negative parts
scaled_arr = np.concatenate((neg_scaled_arr, pos_scaled_arr))
# Reshape the scaled array to match the original shape
scaled_arr = scaled_arr.reshape(arr.shape)
# Convert the scaled array to float16 data type
scaled_arr = scaled_arr.astype(np.float16)
return scaled_arr
\ No newline at end of file
...@@ -9,9 +9,10 @@ Volumetric visualization using K3D ...@@ -9,9 +9,10 @@ Volumetric visualization using K3D
import k3d import k3d
import numpy as np import numpy as np
from qim3d.utils.internal_tools import scale_to_float16
def vol(img, aspectmode="data", show=True, save=False, grid_visible=False, cmap=None, **kwargs): def vol(img, aspectmode="data", show=True, save=False, grid_visible=False, cmap=None, samples="auto", **kwargs):
""" """
Visualizes a 3D volume using volumetric rendering. Visualizes a 3D volume using volumetric rendering.
...@@ -26,6 +27,9 @@ def vol(img, aspectmode="data", show=True, save=False, grid_visible=False, cmap= ...@@ -26,6 +27,9 @@ def vol(img, aspectmode="data", show=True, save=False, grid_visible=False, cmap=
If a string is provided, it's interpreted as the file path where the HTML If a string is provided, it's interpreted as the file path where the HTML
file will be saved. Defaults to False. file will be saved. Defaults to False.
grid_visible (bool, optional): If True, the grid is visible in the plot. Defaults to False. grid_visible (bool, optional): If True, the grid is visible in the plot. Defaults to False.
cmap (list, optional): The color map to be used for the volume rendering. Defaults to None.
samples (int, optional): The number of samples to be used for the volume rendering in k3d. Defaults to 512.
Lower values will render faster but with lower quality.
**kwargs: Additional keyword arguments to be passed to the `k3d.plot` function. **kwargs: Additional keyword arguments to be passed to the `k3d.plot` function.
Returns: Returns:
...@@ -54,18 +58,33 @@ def vol(img, aspectmode="data", show=True, save=False, grid_visible=False, cmap= ...@@ -54,18 +58,33 @@ def vol(img, aspectmode="data", show=True, save=False, grid_visible=False, cmap=
``` ```
""" """
pixel_count = img.shape[0] * img.shape[1] * img.shape[2]
# target is 60fps on m1 macbook pro, using test volume: https://data.qim.dk/pages/foam.html
if samples == "auto":
y1,x1 = 256, 16777216 # 256 samples at res 256*256*256=16.777.216
y2,x2 = 32, 134217728 # 32 samples at res 512*512*512=134.217.728
# we fit linear function to the two points
a = (y1-y2)/(x1-x2)
b = y1 - a*x1
samples = int(min(max(a*pixel_count+b,32),512))
else:
samples = int(samples) # make sure it's an integer
if aspectmode.lower() not in ["data", "cube"]: if aspectmode.lower() not in ["data", "cube"]:
raise ValueError("aspectmode should be either 'data' or 'cube'") raise ValueError("aspectmode should be either 'data' or 'cube'")
plt_volume = k3d.volume( plt_volume = k3d.volume(
img.astype(np.float32), scale_to_float16(img),
bounds=( bounds=(
[0, img.shape[0], 0, img.shape[1], 0, img.shape[2]] [0, img.shape[0], 0, img.shape[1], 0, img.shape[2]]
if aspectmode.lower() == "data" if aspectmode.lower() == "data"
else None else None
), ),
color_map=cmap, color_map=cmap,
samples=samples,
) )
plot = k3d.plot(grid_visible=grid_visible,**kwargs) plot = k3d.plot(grid_visible=grid_visible,**kwargs)
plot += plt_volume plot += plt_volume
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment