Skip to content
Snippets Groups Projects
Commit 8e345709 authored by s204159's avatar s204159 :sunglasses: Committed by fima
Browse files

Fade viz

parent e78615ef
No related branches found
No related tags found
1 merge request!92Fade viz
docs/assets/screenshots/interactive_edge_fading.png

364 KiB

docs/assets/screenshots/operations-edge_fade_after.png

457 KiB

docs/assets/screenshots/operations-edge_fade_before.png

508 KiB

%% Cell type:code id:0b73f2d8 tags:
``` python
import qim3d
```
%% Cell type:code id:73db6886 tags:
``` python
vol = qim3d.examples.bone_128x128x128
```
%% Cell type:code id:22d86d4d tags:
``` python
qim3d.viz.orthogonal(vol)
```
%% Output
HBox(children=(interactive(children=(IntSlider(value=64, description='Z', max=127), Output()), layout=Layout(a…
......@@ -16,3 +16,4 @@ Here, we provide functionalities designed specifically for 3D image analysis and
members:
- remove_background
- watershed
- edge_fade
......@@ -12,6 +12,7 @@ The `qim3d` library aims to provide easy ways to explore and get insights from v
- vectors
- plot_cc
- colormaps
- interactive_edge_fade
::: qim3d.viz.colormaps
options:
......
......@@ -2,12 +2,14 @@ import logging
logging.basicConfig(level=logging.ERROR)
from qim3d import io
from qim3d import gui
from qim3d import viz
from qim3d import utils
from qim3d import models
from qim3d import processing
from . import io
from . import gui
from . import viz
from . import utils
from . import models
from . import processing
__version__ = "0.3.2"
examples = io.ImgExamples()
......
......@@ -5,8 +5,8 @@ import numpy as np
from typing import Optional
from skimage.filters import threshold_otsu
from qim3d.io.logger import log
from qim3d.viz import local_thickness as viz_local_thickness
#from qim3d.viz import local_thickness as viz_local_thickness
import qim3d
def local_thickness(
image: np.ndarray,
......@@ -96,6 +96,6 @@ def local_thickness(
# Visualize the local thickness if requested
if visualize:
display(viz_local_thickness(image, local_thickness, **viz_kwargs))
display(qim3d.viz.local_thickness(image, local_thickness, **viz_kwargs))
return local_thickness
import numpy as np
import qim3d.processing.filters as filters
import scipy
import skimage
import qim3d.processing.filters as filters
from qim3d.io.logger import log
......@@ -105,4 +106,82 @@ def watershed(
num_labels = len(np.unique(labeled_volume))-1
log.info(f"Total number of objects found: {num_labels}")
return labeled_volume, num_labels
\ No newline at end of file
return labeled_volume, num_labels
def fade_mask(
vol: np.ndarray,
decay_rate: float = 10,
ratio: float = 0.5,
geometry: str = "sphere",
invert=False,
axis: int = 0,
):
"""
Apply edge fading to a volume.
Args:
vol (np.ndarray): The volume to apply edge fading to.
decay_rate (float, optional): The decay rate of the fading. Defaults to 10.
ratio (float, optional): The ratio of the volume to fade. Defaults to 0.
geometric (str, optional): The geometric shape of the fading. Can be 'spherical' or 'cylindrical'. Defaults to 'spherical'.
axis (int, optional): The axis along which to apply the fading. Defaults to 0.
Returns:
np.ndarray: The volume with edge fading applied.
Example:
```python
import qim3d
qim3d.viz.vol(vol)
```
Image before edge fading has visible artifacts from the support. Which obscures the object of interest.
![operations-edge_fade_before](assets/screenshots/operations-edge_fade_before.png)
```python
import qim3d
vol_faded = qim3d.processing.operations.edge_fade(vol, decay_rate=4, ratio=0.45, geometric='cylindrical')
qim3d.viz.vol(vol_faded)
```
Afterwards the artifacts are faded out, making the object of interest more visible for visualization purposes.
![operations-edge_fade_after](assets/screenshots/operations-edge_fade_after.png)
"""
if 0 > axis or axis >= vol.ndim:
raise ValueError("Axis must be between 0 and the number of dimensions of the volume")
# Generate the coordinates of each point in the array
shape = vol.shape
z, y, x = np.indices(shape)
# Calculate the center of the array
center = np.array([(s - 1) / 2 for s in shape])
# Calculate the distance of each point from the center
if geometry == "sphere":
distance = np.linalg.norm([z - center[0], y - center[1], x - center[2]], axis=0)
elif geometry == "cilinder":
distance_list = np.array([z - center[0], y - center[1], x - center[2]])
# remove the axis along which the fading is not applied
distance_list = np.delete(distance_list, axis, axis=0)
distance = np.linalg.norm(distance_list, axis=0)
else:
raise ValueError("geometric must be 'spherical' or 'cylindrical'")
# Normalize the distances so that they go from 0 at the center to 1 at the farthest point
max_distance = np.linalg.norm(center)
normalized_distance = distance / (max_distance*ratio)
# Apply the decay rate
faded_distance = normalized_distance ** decay_rate
# Invert the distances to have 1 at the center and 0 at the edges
fade_array = 1 - faded_distance
fade_array[fade_array<=0]=0
if invert:
fade_array = -(fade_array-1)
# Apply the fading to the volume
vol_faded = vol * fade_array
return vol_faded
from .visualizations import plot_metrics
from .img import grid_pred, grid_overview, slices, slicer, orthogonal, vol_masked
from .k3d import vol
from .structure_tensor import vectors
from .local_thickness_ import local_thickness
from .cc import plot_cc
#from .colormaps import objects
from . import colormaps
from .cc import plot_cc
from .detection import circles
from .img import (
grid_overview,
grid_pred,
interactive_fade_mask,
orthogonal,
slicer,
slices,
vol_masked,
)
from .k3d import vol
from .local_thickness_ import local_thickness
from .structure_tensor import vectors
from .visualizations import plot_metrics
......@@ -3,9 +3,8 @@ import numpy as np
from qim3d.io.logger import log
from qim3d.processing.cc import CC
from qim3d.viz import slices
from qim3d.viz.colormaps import objects as qim3dCmap
import qim3d
def plot_cc(
connected_components,
......@@ -66,18 +65,18 @@ def plot_cc(
overlay_crop = overlay[bb]
# use cc as mask for overlay_crop, where all values in cc set to 0 should be masked out, cc contains integers
overlay_crop = np.where(cc == 0, 0, overlay_crop)
fig = slices(overlay_crop, show=show, **kwargs)
fig = qim3d.viz.slices(overlay_crop, show=show, **kwargs)
else:
cc = connected_components.get_cc(component, crop=False)
overlay_crop = np.where(cc == 0, 0, overlay)
fig = slices(overlay_crop, show=show, **kwargs)
fig = qim3d.viz.slices(overlay_crop, show=show, **kwargs)
else:
# assigns discrete color map to each connected component if not given
if "cmap" not in kwargs:
kwargs["cmap"] = qim3dCmap(len(component_indexs))
# Plot the connected component without overlay
fig = slices(connected_components.get_cc(component, crop=crop), show=show, **kwargs)
fig = qim3d.viz.slices(connected_components.get_cc(component, crop=crop), show=show, **kwargs)
figs.append(fig)
......
import matplotlib.pyplot as plt
from qim3d.viz import slices
from qim3d.io.logger import log
import numpy as np
import ipywidgets as widgets
from IPython.display import clear_output, display
import qim3d
def circles(blobs, vol, alpha=0.5, color="#ff9900", **kwargs):
"""
......@@ -29,7 +28,7 @@ def circles(blobs, vol, alpha=0.5, color="#ff9900", **kwargs):
def _slicer(z_slice):
clear_output(wait=True)
fig = slices(
fig = qim3d.viz.slices(
vol,
n_slices=1,
position=z_slice,
......
......@@ -5,14 +5,15 @@ Provides a collection of visualization functions.
import math
from typing import List, Optional, Union
import dask.array as da
import ipywidgets as widgets
import matplotlib.pyplot as plt
import numpy as np
import torch
from matplotlib import colormaps
from matplotlib.colors import LinearSegmentedColormap
import dask.array as da
import qim3d
from qim3d.io.logger import log
......@@ -557,3 +558,83 @@ def vol_masked(vol, vol_mask, viz_delta=128):
vol_masked = background + foreground
return vol_masked
def interactive_fade_mask(vol: np.ndarray, axis: int = 0):
""" Interactive widget for visualizing the effect of edge fading on a 3D volume.
Args:
vol (np.ndarray): The volume to apply edge fading to.
axis (int, optional): The axis along which to apply the fading. Defaults to 0.
Returns:
np.ndarray: The volume with edge fading applied.
Example:
```python
import qim3d
qim3d.viz.img.interactive_edge_fade(vol, geometric='cylindrical', axis=0)
```
![operations-edge_fade_before](assets/screenshots/interactive_edge_fading.png)
"""
# Create the interactive widget
def _slicer(position, decay_rate, ratio, geometry, invert):
fig, axes = plt.subplots(1, 3, figsize=(9, 3))
axes[0].imshow(vol[position, :, :], cmap='viridis')
axes[0].set_title('Original')
axes[0].axis('off')
mask = qim3d.processing.operations.fade_mask(np.ones_like(vol), decay_rate=decay_rate, ratio=ratio, geometry=geometry, axis=axis, invert=invert)
axes[1].imshow(mask[position, :, :], cmap='viridis')
axes[1].set_title('Mask')
axes[1].axis('off')
masked_vol = qim3d.processing.operations.fade_mask(vol, decay_rate=decay_rate, ratio=ratio, geometry=geometry, axis=axis, invert=invert)
axes[2].imshow(masked_vol[position, :, :], cmap='viridis')
axes[2].set_title('Masked')
axes[2].axis('off')
return fig
shape_dropdown = widgets.Dropdown(
options=['sphere', 'cilinder'],
value='sphere', # default value
description='Geometry',
)
position_slider = widgets.IntSlider(
value=vol.shape[0] // 2,
min=0,
max=vol.shape[0] - 1,
description="Slice",
continuous_update=False,
)
decay_rate_slider = widgets.FloatSlider(
value=10,
min=1,
max=50,
step=1.0,
description="Decay Rate",
continuous_update=False,
)
ratio_slider = widgets.FloatSlider(
value=0.5,
min=0.1,
max=1,
step=0.01,
description="Ratio",
continuous_update=False,
)
# Create the Checkbox widget
invert_checkbox = widgets.Checkbox(
value=False, # default value
description='Invert'
)
slicer_obj = widgets.interactive(_slicer, position=position_slider, decay_rate=decay_rate_slider, ratio=ratio_slider, geometry=shape_dropdown, invert=invert_checkbox)
slicer_obj.layout = widgets.Layout(align_items="flex-start")
return slicer_obj
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment