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

Refactoring v1.0 prep

parent 11da9c42
No related branches found
No related tags found
1 merge request!139Refactoring v1.0 prep
Showing
with 61 additions and 52 deletions
%% Cell type:code id: tags:
``` python
import qim3d
```
%% Cell type:markdown id: tags:
### Blob detection notebook
%% Cell type:markdown id: tags:
This notebook shows how to do **blob detection** in a 3D volume using the `qim3d` library.
Blob detection is done by using the `qim3d.processing.blob_detection` method, which detects blobs by using the Difference of Gaussian (DoG) blob detection method, and returns two arrays:
- `blobs`: The blobs found in the volume stored as `(p, r, c, radius)`
- `binary_volume`: A binary mask of the volume with the blobs marked as `True`
%% Cell type:markdown id: tags:
### **Example 1**: Blob detection in cement volume
%% Cell type:markdown id: tags:
**Applying Gaussian filter to volume**
%% Cell type:code id: tags:
``` python
# Import 3D volume of cement
cement = qim3d.examples.cement_128x128x128
# Visualize slices of the original cement volume
qim3d.viz.slices(cement, n_slices = 5, show = True)
qim3d.viz.slices_grid(cement, n_slices = 5, show = True)
# Apply Gaussian filter to the cement volume
cement_filtered = qim3d.processing.gaussian(cement, sigma = 2)
# Visualize slices of the filtered cement volume
qim3d.viz.slices(cement_filtered)
qim3d.viz.slices_grid(cement_filtered)
```
%% Output
<Figure size 1000x200 with 5 Axes>
%% Cell type:markdown id: tags:
**Detecting blobs in volume**
%% Cell type:code id: tags:
``` python
# Detect blobs, and get binary mask
blobs, mask = qim3d.processing.blob_detection(
cement_filtered,
min_sigma=1,
max_sigma=8,
threshold=0.001,
overlap=0.1,
background="bright"
)
# Number of blobs found
print(f'Number of blobs found in the volume: {len(blobs)} blobs')
```
%% Output
Bright background selected, volume will be inverted.
Number of blobs found in the volume: 1813 blobs
%% Cell type:code id: tags:
``` python
# Visualize blobs on slices of cement volume
qim3d.viz.detection.circles(blobs, cement, alpha = 0.8, show = True, color = 'red')
```
%% Output
interactive(children=(IntSlider(value=64, description='Slice', max=127), Output()), layout=Layout(align_items=…
%% Cell type:markdown id: tags:
**Binary mask of detected blobs**
%% Cell type:code id: tags:
``` python
# Visualize binary mask
qim3d.viz.slicer(mask)
```
%% Output
interactive(children=(IntSlider(value=64, description='Slice', max=127), Output()), layout=Layout(align_items=…
......
%% Cell type:code id: tags:
 
``` python
import qim3d
```
 
%% Output
 
WARNING:root:Could not load CuPy: No module named 'cupy'
 
%% Cell type:markdown id: tags:
 
### Local thickness notebook
 
%% Cell type:markdown id: tags:
 
This notebook shows an example of how to determine the **local thickness** of an object in either 2D and 3D using the `qim3d` library. The local thickness at a point within the object is defined as the radius of the largest circle (2D) or sphere (3D) that contains the point and is inside the object.
 
%% Cell type:markdown id: tags:
 
In the following, the local thickness is computed for three examples:
 
* **Example 1**: 2D image of blobs
* **Example 2**: 3D volume of shell
* **Example 3**: 3D volume of cement
 
The local thickness algorithm is applied by using the `qim3d.processing.local_thickness` function, which expects a binary image/volume. If the input is not already binary, then it will be binarized automatically using Otsu's thresholding method. The `qim3d.processing.local_thickness` function returns a 2D or 3D Numpy array representing the local thickness of the input image/volume.
 
%% Cell type:markdown id: tags:
 
#### **Example 1**: Local thickness of 2D image
This example uses a 2D image of blobs (256 x 256), which can be acquired from `qim3d.examples.blobs_256x256`.
 
%% Cell type:code id: tags:
 
``` python
# Import 2D image of blobs
blobs = qim3d.examples.blobs_256x256
 
# Compute the local thickness of the blobs
lt_blobs = qim3d.processing.local_thickness(blobs, visualize = True)
```
 
%% Output
 
Input image is not binary. It will be binarized using Otsu's method with threshold: 74
WARNING:qim3d:Input image is not binary. It will be binarized using Otsu's method with threshold: 74
 
 
%% Cell type:markdown id: tags:
 
#### **Example 2**: Local thickness of 3D volume
This example uses a 3D volume of a shell (225 x 128 x 128), which can be acquired from `qim3d.examples.shell_225x128x128`.
 
%% Cell type:code id: tags:
 
``` python
# Import 3D volume of shell
shell = qim3d.examples.shell_225x128x128
 
# Compute the local thickness of shell
lt_shell = qim3d.processing.local_thickness(shell, visualize = True, axis = 0)
```
 
%% Output
 
Input image is not binary. It will be binarized using Otsu's method with threshold: 65
WARNING:qim3d:Input image is not binary. It will be binarized using Otsu's method with threshold: 65
 
 
%% Cell type:markdown id: tags:
 
#### **Example 3**: Local thickness of (binary) 3D volume
This example uses a 3D volume of cement (128 x 128 x 128), which can be acquired from `qim3d.examples.cement_128x128x128`.
 
For the previous two examples, the original image/volume was passed directly to the `qim3d.processing.local_thickness` function, which automatically binarized the input prior to computing the local thickness.
 
For this example, the original volume will instead first be manually binarized with the `qim3d.processing.Blob.get_mask` method (see details in the documentation for `qim3d.processing.Blob`). Then the binarized volume (i.e. mask) will be passed to the `qim3d.processing.local_thickness` function, which can then directly compute the local thickness.
 
%% Cell type:markdown id: tags:
 
**Importing, filtering and visualizing volume**
 
%% Cell type:code id: tags:
 
``` python
# Import 3D volume of cement
cement = qim3d.examples.cement_128x128x128
 
# Visualize slices of the original cement volume
qim3d.viz.slices(cement, n_slices = 5, show = True)
qim3d.viz.slices_grid(cement, n_slices = 5, show = True)
 
# Apply Gaussian filter to the cement volume
cement_filtered = qim3d.processing.gaussian(cement, sigma = 2)
 
# Visualize slices of the filtered cement volume
qim3d.viz.slices(cement_filtered)
qim3d.viz.slices_grid(cement_filtered)
```
 
%% Output
 
 
<Figure size 1000x200 with 5 Axes>
 
%% Cell type:markdown id: tags:
 
**Detecting blobs in volume, creating binary mask**
 
%% Cell type:code id: tags:
 
``` python
# Initialize Blob detector
blob_detector = qim3d.processing.Blob(
min_sigma=1,
max_sigma=8,
threshold=0.001,
overlap=0.1,
background="bright"
)
 
# Detect blobs
cement_blobs = blob_detector.detect(cement_filtered)
 
# Get binary mask and visualize
cement_mask = blob_detector.get_mask()
qim3d.viz.slicer(cement_mask)
```
 
%% Output
 
interactive(children=(IntSlider(value=64, description='Slice', max=127), Output()), layout=Layout(align_items=…
 
%% Cell type:markdown id: tags:
 
**Computing local thickness**
 
%% Cell type:code id: tags:
 
``` python
# Compute the local thickness of cement (input: binary mask)
lt_cement = qim3d.processing.local_thickness(cement_mask, visualize = True, axis = 0)
```
 
%% Output
 
......
......@@ -114,7 +114,7 @@ This version focus on the increased usability of the `qim3d` library
- Online documentation available at [https://platform.qim.dk/qim3d](https://platform.qim.dk/qim3d)
- Virtual stacks also available for `txm` files
- Updated GUI launch pipeline
- New functionalities for `qim3d.viz.slices`
- New functionalities for `qim3d.viz.slices_grid`
- Introduction of `qim3d.processing.filters` 🎉
- Introduction of `qim3d.viz.vol` 🎉
......@@ -140,7 +140,7 @@ Includes new develoments toward the usability of the library, as well as its int
- For the local thicknes GUI, now it is possible to pass and receive numpy arrays instead of using the upload functionality.
- Improved data loader
- Now the extensions `tif`, `h5` and `txm` are supported.
- Added `qim3d.viz.slices` for easy slice visualization.
- Added `qim3d.viz.slices_grid` for easy slice visualization.
- U-net model creation
- Model availabe from `qim3d.models.UNet`
- Data augmentation class at `qim3d.utils.Augmentation`
......
......@@ -8,7 +8,7 @@ Documentation available at https://platform.qim.dk/qim3d/
"""
__version__ = "0.4.5"
__version__ = "0.9.0"
import importlib as _importlib
......@@ -33,18 +33,23 @@ class _LazyLoader:
# List of submodules
_submodules = [
"examples",
"generate",
"gui",
"io",
"models",
"processing",
"tests",
"utils",
"viz",
"cli",
'examples',
'generate',
'gui',
'io',
'ml',
'processing',
'tests',
'utils',
'viz',
'cli',
'filters',
'segmentation',
'mesh',
'features',
'operations',
]
# Creating lazy loaders for each submodule
for submodule in _submodules:
globals()[submodule] = _LazyLoader(f"qim3d.{submodule}")
globals()[submodule] = _LazyLoader(f'qim3d.{submodule}')
......@@ -177,7 +177,7 @@ def main():
elif args.method == "k3d":
volume = qim3d.io.load(str(args.source))
print("\nGenerating k3d plot...")
qim3d.viz.vol(volume, show=False, save=str(args.destination))
qim3d.viz.volumetric(volume, show=False, save=str(args.destination))
print(f"Done, plot available at <{args.destination}>")
if not args.no_browser:
print("Opening in default browser...")
......
from qim3d.detection._common_detection_methods import *
\ No newline at end of file
""" Blob detection using Difference of Gaussian (DoG) method """
import numpy as np
from qim3d.utils.logger import log
from qim3d.utils._logger import log
__all__ = ["blob_detection"]
def blob_detection(
vol: np.ndarray,
......
""" Example images for testing and demonstration purposes. """
from pathlib import Path as _Path
from qim3d.utils.logger import log as _log
from qim3d.utils._logger import log as _log
from qim3d.io import load as _load
# Save the original log level and set to ERROR
......
from ._common_features_methods import volume, area, sphericity
import numpy as np
import qim3d.processing
from qim3d.utils.logger import log
from qim3d.utils._logger import log
import trimesh
import qim3d
......@@ -44,7 +44,7 @@ def volume(obj, **mesh_kwargs) -> float:
"""
if isinstance(obj, np.ndarray):
log.info("Converting volume to mesh.")
obj = qim3d.processing.create_mesh(obj, **mesh_kwargs)
obj = qim3d.mesh.from_volume(obj, **mesh_kwargs)
return obj.volume
......
from ._common_filter_methods import *
\ No newline at end of file
......@@ -8,7 +8,7 @@ from skimage import morphology
import dask.array as da
import dask_image.ndfilters as dask_ndfilters
from qim3d.utils.logger import log
from qim3d.utils._logger import log
__all__ = [
"Gaussian",
......@@ -119,7 +119,7 @@ class Pipeline:
vol = qim3d.examples.fly_150x256x256
# Show original
qim3d.viz.slices(vol, axis=0, show=True)
qim3d.viz.slices_grid(vol, axis=0, show=True)
# Create filter pipeline
pipeline = Pipeline(
......@@ -134,7 +134,7 @@ class Pipeline:
vol_filtered = pipeline(vol)
# Show filtered
qim3d.viz.slices(vol_filtered, axis=0)
qim3d.viz.slices_grid(vol_filtered, axis=0)
```
![original volume](assets/screenshots/filter_original.png)
![filtered volume](assets/screenshots/filter_processed.png)
......
from .blob_ import blob
from .collection_ import collection
\ No newline at end of file
from ._generators import noise_object
from ._aggregators import noise_object_collection
......@@ -3,7 +3,7 @@ import scipy.ndimage
from tqdm.notebook import tqdm
import qim3d.generate
from qim3d.utils.logger import log
from qim3d.utils._logger import log
def random_placement(
......@@ -120,7 +120,7 @@ def specific_placement(
return collection, placed, positions
def collection(
def noise_object_collection(
collection_shape: tuple = (200, 200, 200),
num_objects: int = 15,
positions: list[tuple] = None,
......@@ -255,7 +255,7 @@ def collection(
```python
# Visualize slices
qim3d.viz.slices(vol, n_slices=15)
qim3d.viz.slices_grid(vol, n_slices=15)
```
![synthetic_collection_cylinder](assets/screenshots/synthetic_collection_cylinder_slices.png)
......@@ -285,7 +285,7 @@ def collection(
```python
# Visualize slices
qim3d.viz.slices(vol, n_slices=15, axis=1)
qim3d.viz.slices_grid(vol, n_slices=15, axis=1)
```
![synthetic_collection_tube](assets/screenshots/synthetic_collection_tube_slices.png)
"""
......@@ -350,7 +350,7 @@ def collection(
log.debug(f"- Threshold: {threshold:.3f}")
# Generate synthetic object
blob = qim3d.generate.blob(
blob = qim3d.generate.noise_object(
base_shape=blob_shape,
final_shape=final_shape,
noise_scale=noise_scale,
......
......@@ -4,7 +4,7 @@ from noise import pnoise3
import qim3d.processing
def blob(
def noise_object(
base_shape: tuple = (128, 128, 128),
final_shape: tuple = (128, 128, 128),
noise_scale: float = 0.05,
......@@ -32,7 +32,7 @@ def blob(
dtype (str, optional): Desired data type of the output volume. Defaults to "uint8".
Returns:
synthetic_blob (numpy.ndarray): Generated 3D volume with specified parameters.
noise_object (numpy.ndarray): Generated 3D volume with specified parameters.
Raises:
TypeError: If `final_shape` is not a tuple or does not have three elements.
......@@ -52,7 +52,7 @@ def blob(
```python
# Visualize slices
qim3d.viz.slices(synthetic_blob, vmin = 0, vmax = 255, n_slices = 15)
qim3d.viz.slices_grid(synthetic_blob, vmin = 0, vmax = 255, n_slices = 15)
```
![synthetic_blob](assets/screenshots/synthetic_blob_slices.png)
......@@ -69,14 +69,14 @@ def blob(
object_shape = "cylinder"
)
# Visualize synthetic blob
# Visualize synthetic object
qim3d.viz.vol(vol)
```
<iframe src="https://platform.qim.dk/k3d/synthetic_blob_cylinder.html" width="100%" height="500" frameborder="0"></iframe>
```python
# Visualize slices
qim3d.viz.slices(vol, n_slices=15, axis=1)
qim3d.viz.slices_grid(vol, n_slices=15, axis=1)
```
![synthetic_blob_cylinder_slice](assets/screenshots/synthetic_blob_cylinder_slice.png)
......@@ -100,7 +100,7 @@ def blob(
```python
# Visualize
qim3d.viz.slices(vol, n_slices=15)
qim3d.viz.slices_grid(vol, n_slices=15)
```
![synthetic_blob_tube_slice](assets/screenshots/synthetic_blob_tube_slice.png)
"""
......
......@@ -29,7 +29,7 @@ import numpy as np
from PIL import Image
from qim3d.io import load, save
from qim3d.processing.operations import overlay_rgb_images
from qim3d.operations._common_operations_methods import overlay_rgb_images
from qim3d.gui.interface import BaseInterface
# TODO: img in launch should be self.img
......
......@@ -25,8 +25,8 @@ import numpy as np
import outputformat as ouf
from qim3d.io import load
from qim3d.utils.logger import log
from qim3d.utils import misc
from qim3d.utils._logger import log
from qim3d.utils import _misc
from qim3d.gui.interface import BaseInterface
......@@ -550,7 +550,7 @@ class Interface(BaseInterface):
def show_data_summary(self):
summary_dict = {
"Last modified": datetime.datetime.fromtimestamp(os.path.getmtime(self.file_path)).strftime("%Y-%m-%d %H:%M"),
"File size": misc.sizeof(os.path.getsize(self.file_path)),
"File size": _misc.sizeof(os.path.getsize(self.file_path)),
"Z-size": str(self.vol.shape[self.axis_dict["Z"]]),
"Y-size": str(self.vol.shape[self.axis_dict["Y"]]),
"X-size": str(self.vol.shape[self.axis_dict["X"]]),
......
......@@ -23,7 +23,7 @@ import plotly.graph_objects as go
from scipy import ndimage
from qim3d.io import load
from qim3d.utils.logger import log
from qim3d.utils._logger import log
from qim3d.gui.interface import InterfaceWithExamples
......
......@@ -27,7 +27,7 @@ from .interface import BaseInterface
# from qim3d.processing import layers2d as l2d
from qim3d.processing import overlay_rgb_images, segment_layers, get_lines
from qim3d.io import load
from qim3d.viz.layers2d import image_with_lines
from qim3d.viz._layers2d import image_with_lines
#TODO figure out how not update anything and go through processing when there are no data loaded
# So user could play with the widgets but it doesnt throw error
......
from .loading import DataLoader, load, load_mesh
from .downloader import Downloader
from .saving import DataSaver, save, save_mesh
from .sync import Sync
from .convert import convert
from ..utils import logger
from .ome_zarr import export_ome_zarr, import_ome_zarr
from ._loading import load, load_mesh
from ._downloader import Downloader
from ._saving import save, save_mesh
# from ._sync import Sync # this will be added back after future development
from ._convert import convert
from ._ome_zarr import export_ome_zarr, import_ome_zarr
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment