Skip to content
Snippets Groups Projects
Commit 9ca6186c authored by s214735's avatar s214735
Browse files

added and changed more notebooks

parent 0536d5e7
No related branches found
No related tags found
No related merge requests found
...@@ -396,7 +396,7 @@ ...@@ -396,7 +396,7 @@
], ],
"metadata": { "metadata": {
"kernelspec": { "kernelspec": {
"display_name": "Python 3 (ipykernel)", "display_name": "qim3d-env",
"language": "python", "language": "python",
"name": "python3" "name": "python3"
}, },
...@@ -410,7 +410,7 @@ ...@@ -410,7 +410,7 @@
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.11.5" "version": "3.11.10"
} }
}, },
"nbformat": 4, "nbformat": 4,
......
%% Cell type:markdown id: tags:
## Features and mesh notebook
This notebook will demonstrate how to extract features from a volumetric object. Since these operations treat the volume as a mesh, we will also explore the use of the volume as a mesh, both visualizing and saving the object.
First, we can generate an object using the `generate` module, and explore the shape using the `volumetric` method from the `viz` module:
%% Cell type:code id: tags:
``` python
import qim3d
vol = qim3d.generate.noise_object(
base_shape = (128, 128, 128),
final_shape= (128, 128, 128),
noise_scale= 0.00,
gamma= 1.0,
max_value= 255,
threshold= 0.5,
object_shape = 'cylinder'
)
qim3d.viz.volumetric(vol)
```
%% Output
%% Cell type:markdown id: tags:
Now, that we have generate a massive volume of a ball, we can extract some features from it using the `features` module:
%% Cell type:code id: tags:
``` python
area = qim3d.features.area(vol)
volume = qim3d.features.volume(vol)
sphericity = qim3d.features.sphericity(vol)
```
%% Output
Converting volume to mesh.
Computed level using Otsu's method: 0
Padded volume with (2, 2, 2) to shape: (132, 132, 132)
Converting volume to mesh.
Computed level using Otsu's method: 0
Padded volume with (2, 2, 2) to shape: (132, 132, 132)
Converting volume to mesh.
Computed level using Otsu's method: 0
Padded volume with (2, 2, 2) to shape: (132, 132, 132)
%% Cell type:code id: tags:
``` python
print(f"Area: {area}. \nVolume: {volume}. \nSphericity: {sphericity}.")
```
%% Output
Area: 42597.44460846406.
Volume: 729161.3333333334.
Sphericity: 0.919707365529198.
%% Cell type:markdown id: tags:
As we can see in the output logs, the volume is converted to a mesh, after which it's features are calculated. We can also transform the volume to a mesh first, followed by visualization and feature extraction:
%% Cell type:code id: tags:
``` python
mesh = qim3d.mesh.from_volume(vol)
qim3d.viz.mesh(mesh.vertices, mesh.faces, wireframe=True)
```
%% Output
Computed level using Otsu's method: 0
Padded volume with (2, 2, 2) to shape: (132, 132, 132)
%% Cell type:code id: tags:
``` python
area_mesh = qim3d.features.area(mesh)
volume_mesh = qim3d.features.volume(mesh)
sphericity_mesh = qim3d.features.sphericity(mesh)
print(f"Mesh area: {area_mesh}. \nMesh volume: {volume_mesh}. \nMesh sphericity: {sphericity_mesh}.")
```
%% Output
Mesh area: 42597.44460846406.
Mesh volume: 729161.3333333334.
Mesh sphericity: 0.919707365529198.
%% Cell type:markdown id: tags:
After having created the mesh and found features of the mesh, we can save it, such that it can be visualized in other programs or reused at a later point:
%% Cell type:code id: tags:
``` python
qim3d.io.save_mesh('circular_mesh.obj', mesh)
```
%% Cell type:markdown id: tags:
Should we wish to use it, it can be imported using:
%% Cell type:code id: tags:
``` python
mesh = qim3d.io.load_mesh('circular_mesh.obj')
```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
   
### Local thickness notebook ## Local thickness notebook
   
%% Cell type:markdown id: tags: %% 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. 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: %% Cell type:markdown id: tags:
   
In the following, the local thickness is computed for three examples: In the following, the local thickness is computed for three examples:
   
* **Example 1**: 2D image of blobs * **Example 1**: 2D image of blobs
* **Example 2**: 3D volume of shell * **Example 2**: 3D volume of shell
* **Example 3**: 3D volume of cement * **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. 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: %% Cell type:markdown id: tags:
   
#### **Example 1**: Local thickness of 2D image #### **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`. This example uses a 2D image of blobs (256 x 256), which can be acquired from `qim3d.examples.blobs_256x256`.
   
The image is loaded using the `load` method from the `io` module. This is followed by a calculation of the local thicknes of the image. The image is loaded using the `load` method from the `io` module. This is followed by a calculation of the local thicknes of the image.
   
%% Cell type:code id: tags: %% Cell type:code id: tags:
   
``` python ``` python
import qim3d import qim3d
# Load 2D image of blobs # Load 2D image of blobs
img = qim3d.io.load('../../qim3d/examples/blobs_256x256.png', progress_bar=False) img = qim3d.io.load('../../qim3d/examples/blobs_256x256.png', progress_bar=False)
   
# Compute the local thickness of the blobs # Compute the local thickness of the blobs
img_lt = qim3d.processing.local_thickness(img, visualize=True) img_lt = qim3d.processing.local_thickness(img, visualize=True)
``` ```
   
%% Output %% Output
   
Volume using 64.0 KB of memory Volume using 64.0 KB of memory
System memory: System memory:
• Total.: 30.8 GB • Total.: 30.8 GB
• Used..: 26.3 GB (85.3%) • Used..: 26.3 GB (85.3%)
• Free..: 4.5 GB (14.7%) • Free..: 4.5 GB (14.7%)
Input image is not binary. It will be binarized using Otsu's method with threshold: 70 Input image is not binary. It will be binarized using Otsu's method with threshold: 70
   
   
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
   
#### **Example 2**: Local thickness of 3D volume #### **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`. This example uses a 3D volume of a shell (225 x 128 x 128), which can be acquired from `qim3d.examples.shell_225x128x128`.
   
Likewise, the local thickness is calculated after. Since this image is 3D, there is a slider to evaluate the local thickness through the layers of the volume. Likewise, the local thickness is calculated after. Since this image is 3D, there is a slider to evaluate the local thickness through the layers of the volume.
   
%% Cell type:code id: tags: %% Cell type:code id: tags:
   
``` python ``` python
# Import 3D volume of shell # Import 3D volume of shell
vol = qim3d.examples.shell_225x128x128 vol = qim3d.examples.shell_225x128x128
   
# Compute the local thickness of shell # Compute the local thickness of shell
vol_lt = qim3d.processing.local_thickness(vol, visualize=True, axis=0) vol_lt = qim3d.processing.local_thickness(vol, visualize=True, axis=0)
``` ```
   
%% Output %% Output
   
Input image is not binary. It will be binarized using Otsu's method with threshold: 65 Input image is not binary. It will be binarized using Otsu's method with threshold: 65
   
   
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
   
#### **Example 3**: Local thickness of (binary) 3D volume #### **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`. 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 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.detection.blobs` method (see details in the documentation for `qim3d.detection.blobs`). 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. For this example, the original volume will instead first be manually binarized with the `qim3d.detection.blobs` method (see details in the documentation for `qim3d.detection.blobs`). 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: %% Cell type:markdown id: tags:
   
**Importing, filtering and visualizing volume** **Importing, filtering and visualizing volume**
   
First the image is loaded and filtered. First the image is loaded and filtered.
   
%% Cell type:code id: tags: %% Cell type:code id: tags:
   
``` python ``` python
# Import 3D volume of cement # Import 3D volume of cement
vol = qim3d.examples.cement_128x128x128 vol = qim3d.examples.cement_128x128x128
   
# Visualize slices of the original cement volume # Visualize slices of the original cement volume
fig1 = qim3d.viz.slices_grid(vol, num_slices=5, display_figure=True) fig1 = qim3d.viz.slices_grid(vol, num_slices=5, display_figure=True)
   
# Apply Gaussian filter to the cement volume # Apply Gaussian filter to the cement volume
vol_filtered = qim3d.filters.gaussian(vol, sigma=2) vol_filtered = qim3d.filters.gaussian(vol, sigma=2)
   
# Visualize slices of the filtered cement volume # Visualize slices of the filtered cement volume
fig2 = qim3d.viz.slices_grid(vol_filtered, num_slices=5, display_figure=True) fig2 = qim3d.viz.slices_grid(vol_filtered, num_slices=5, display_figure=True)
``` ```
   
%% Output %% Output
   
   
   
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
   
**Detecting blobs in volume, creating binary mask** **Detecting blobs in volume, creating binary mask**
   
Now blobs are detected in the volume, generating both blobs and a binary mask. The mask is visualized to get a sense of the structure. Now blobs are detected in the volume, generating both blobs and a binary mask. The mask is visualized to get a sense of the structure.
   
%% Cell type:code id: tags: %% Cell type:code id: tags:
   
``` python ``` python
# Detect blobs in the volume # Detect blobs in the volume
blobs, mask = qim3d.detection.blobs( blobs, mask = qim3d.detection.blobs(
vol_filtered, vol_filtered,
min_sigma=1, min_sigma=1,
max_sigma=8, max_sigma=8,
threshold=0.001, threshold=0.001,
overlap=0.1, overlap=0.1,
background="bright" background="bright"
) )
   
# Visualize the blob mask # Visualize the blob mask
qim3d.viz.slicer(mask) qim3d.viz.slicer(mask)
``` ```
   
%% Output %% Output
   
Bright background selected, volume will be inverted. Bright background selected, volume will be inverted.
   
interactive(children=(IntSlider(value=64, description='Slice', max=127), Output()), layout=Layout(align_items=… interactive(children=(IntSlider(value=64, description='Slice', max=127), Output()), layout=Layout(align_items=…
   
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
   
**Computing local thickness** **Computing local thickness**
   
Lastly, the local thickness of the mask is calculated. Once again, since the volume is 3D, a slider allows for exploration of the local thickness. Lastly, the local thickness of the mask is calculated. Once again, since the volume is 3D, a slider allows for exploration of the local thickness.
   
%% Cell type:code id: tags: %% Cell type:code id: tags:
   
``` python ``` python
# Compute the local thickness of cement (input: binary mask) # Compute the local thickness of cement (input: binary mask)
mask_lt = qim3d.processing.local_thickness(mask, visualize=True, axis=0) mask_lt = qim3d.processing.local_thickness(mask, visualize=True, axis=0)
``` ```
   
%% Output %% Output
   
......
%% Cell type:markdown id: tags:
## OME-Zarr notebook
This notebook will demonstrate the `qim3d` functionalities that specifically target the OME-Zarr file format.
OME-Zarr is a cloud-native, multi-dimensional file format optimized for storing and analyzing large-scale volumetric imaging data. It leverages the Zarr storage framework, which organizes data in a chunked, hierarchical structure, enabling efficient random access to specific regions without loading entire datasets into memory. The format supports multi-resolution storage, allowing visualization and analysis at different levels of detail, improving performance for large datasets. OME-Zarr stores metadata in JSON format following the Open Microscopy Environment (OME) model, ensuring interoperability across a wide range of bioimaging tools. Additionally, its compatibility with modern data science libraries like Dask and Xarray allows for scalable parallel processing, making it an ideal choice for applications in microscopy, medical imaging, and machine learning.
First we will fetch a large data file, which we can save to the OME-Zarr file type. Here we will use the `io.Downloader` class. First we define the variable, and then we evaluate what data we would like to download, by visualizing the options with the 'help' command.
%% Cell type:code id: tags:
``` python
import qim3d
downloader = qim3d.io.Downloader()
help(downloader)
```
%% Output
Help on Downloader in module qim3d.io._downloader object:
class Downloader(builtins.object)
| Class for downloading large data files available on the [QIM data repository](https://data.qim.dk/).
|
| Attributes:
| folder_name (str or os.PathLike): Folder class with the name of the folder in <https://data.qim.dk/>
|
| Syntax for downloading and loading a file is `qim3d.io.Downloader().{folder_name}.{file_name}(load_file=True)`
|
| ??? info "Overview of available data"
| Below is a table of the available folders and files on the [QIM data repository](https://data.qim.dk/).
|
| Folder name | File name | File size
| ------------------- | ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------
| `Coal` | `CoalBrikett` <br> `CoalBrikett_Zoom` <br> `CoalBrikettZoom_DOWNSAMPLED` | 2.23 GB <br> 3.72 GB <br> 238 MB
| `Corals` | `Coral_1` <br> `Coral_2` <br> `Coral2_DOWNSAMPLED` <br> `MexCoral` | 2.26 GB <br> 2.38 GB <br> 162 MB <br> 2.23 GB
| `Cowry_Shell` | `Cowry_Shell` <br> `Cowry_DOWNSAMPLED` | 1.82 GB <br> 116 MB
| `Crab` | `HerrmitCrab` <br> `OkinawaCrab` | 2.38 GB <br> 1.86 GB
| `Deer_Mandible` | `Animal_Mandible` <br> `DeerMandible_DOWNSAMPLED` <br> | 2.79 GB <br> 638 MB
| `Foam` | `Foam` <br> `Foam_DOWNSAMPLED` <br> `Foam_2` <br> `Foam_2_zoom` | 3.72 GB <br> 238 MB <br> 3.72 GB <br> 3.72 GB
| `Hourglass` | `Hourglass` <br> `Hourglass_4X_80kV_Air_9s_1_97um` <br> `Hourglass_longexp_rerun` | 3.72 GB <br> 1.83 GB <br> 3.72 GB
| `Kiwi` | `Kiwi` | 2.86 GB
| `Loofah` | `Loofah` <br> `Loofah_DOWNSAMPLED` | 2.23 GB <br> 143 MB
| `Marine_Gastropods` | `MarineGatropod_1` <br> `MarineGastropod1_DOWNSAMPLED` <br> `MarineGatropod_2` <br> `MarineGastropod2_DOWNSAMPLED` | 2.23 GB <br> 143 MB <br> 2.60 GB <br> 166 MB
| `Mussel` | `ClosedMussel1` <br> `ClosedMussel1_DOWNSAMPLED` | 2.23 GB <br> 143 MB
| `Oak_Branch` | `Oak_branch` <br> `OakBranch_DOWNSAMPLED` | 2.38 GB <br> 152 MB
| `Okinawa_Forams` | `Okinawa_Foram_1` <br> `Okinawa_Foram_2` | 1.84 GB <br> 1.84 GB
| `Physalis` | `Physalis` <br> `Physalis_DOWNSAMPLED` | 3.72 GB <br> 238 MB
| `Raspberry` | `Raspberry2` <br> `Raspberry2_DOWNSAMPLED` | 2.97 GB <br> 190 MB
| `Rope` | `FibreRope1` <br> `FibreRope1_DOWNSAMPLED` | 1.82 GB <br> 686 MB
| `Sea_Urchin` | `SeaUrchin` <br> `Cordatum_Shell` <br> `Cordatum_Spine` | 2.60 GB <br> 1.85 GB <br> 183 MB
| `Snail` | `Escargot` | 2.60 GB
| `Sponge` | `Sponge` | 1.11 GB
|
| Example:
| ```python
| import qim3d
|
| downloader = qim3d.io.Downloader()
| data = downloader.Cowry_Shell.Cowry_DOWNSAMPLED(load_file=True)
|
| qim3d.viz.slicer_orthogonal(data, color_map="magma")
| ```
| ![cowry shell](assets/screenshots/cowry_shell_slicer.gif)
|
| Methods defined here:
|
| __init__(self)
| Initialize self. See help(type(self)) for accurate signature.
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables
|
| __weakref__
| list of weak references to the object
%% Cell type:markdown id: tags:
After deciding to use the coral data, we fetch the data. We use the 'load_file' argument to load it while downloading it. Additionally, we can use the vizualisation tool `slicer_orthogonal` too explore the volume from three different axes.
%% Cell type:code id: tags:
``` python
vol = downloader.Corals.Coral2_DOWNSAMPLED(load_file=True)
qim3d.viz.slicer_orthogonal(vol, color_map="magma")
```
%% Output
File already downloaded:
/home/s214735/qim3d/docs/notebooks/Corals/Coral2_DOWNSAMPLED.tif
Loading Coral2_DOWNSAMPLED.tif
Loaded shape: (500, 400, 400)
Using virtual stack
HBox(children=(interactive(children=(IntSlider(value=250, description='Z', max=499), Output()), layout=Layout(…
%% Cell type:markdown id: tags:
Attempting to visualize the volume in a three-dimensional space will require a lot of computing power due to the size of the volume. However using the OME-Zarr data type, we can visualize it in chunks. Additionally we can save it in the .zarr format.
First we export the file using the `export_ome_zarr` method.
%% Cell type:code id: tags:
``` python
qim3d.io.export_ome_zarr(
'coral.zarr',
vol,
chunk_size=200,
downsample_rate=2,
replace=True)
```
%% Output
Exporting data to OME-Zarr format at coral.zarr
Number of scales: 3
Calculating the multi-scale pyramid
- Scale 0: (500, 400, 400)
- Scale 1: (250, 200, 200)
- Scale 2: (125, 100, 100)
Writing data to disk
All done!
%% Cell type:markdown id: tags:
We can then use the `chunks` method to visualize the chunks from the volume. Here we have both options for exploring the volume slices or the entire 3D object.
%% Cell type:code id: tags:
``` python
qim3d.viz.chunks('coral.zarr')
```
%% Output
%% Cell type:markdown id: tags:
After looking at the object, we see that some of it is obstructed by background noise. Therefore we attempt to remove that with a threshold, followed by another export and visualization of the volume:
%% Cell type:code id: tags:
``` python
vol_t = vol > 30000
qim3d.io.export_ome_zarr(
'coral_threshold.zarr',
vol_t,
chunk_size=200,
downsample_rate=2,
replace=True)
qim3d.viz.chunks('coral_threshold.zarr')
```
%% Output
Exporting data to OME-Zarr format at coral_threshold.zarr
Number of scales: 3
Calculating the multi-scale pyramid
- Scale 0: (500, 400, 400)
- Scale 1: (250, 200, 200)
- Scale 2: (125, 100, 100)
Writing data to disk
All done!
%% Cell type:markdown id: tags:
For next time the volume is to be used, we can import it easily using the `import_ome_zarr` method:
%% Cell type:code id: tags:
``` python
vol_t = qim3d.io.import_ome_zarr('coral_threshold.zarr')
```
%% Output
Data contains 3 scales:
- Scale 0: (500, 400, 400)
- Scale 1: (250, 200, 200)
- Scale 2: (125, 100, 100)
Loading scale 0 with shape (500, 400, 400)
%% Cell type:markdown id: tags:
## Segmentation notebook
In this notebook we explore some of the segmentation tools the `qim3d` library provides.
This example contains an image of air bubbles in a 3d volume representing a cutout of cement. The goal of the example is to detect the amount of holes and visualize the largest of them.
First we load the image from the `qim3d.examples` module:
%% Cell type:code id: tags:
``` python
import qim3d
vol = qim3d.examples.cement_128x128x128
qim3d.viz.slicer(vol)
```
%% Output
interactive(children=(IntSlider(value=64, description='Slice', max=127), Output()), layout=Layout(align_items=…
%% Cell type:markdown id: tags:
Next we binarize the image by applying a threshold. We visualize once again to confirm the threshold is not too high or low:
%% Cell type:code id: tags:
``` python
vol_t = vol < 60
qim3d.viz.slicer(vol_t)
```
%% Output
interactive(children=(IntSlider(value=64, description='Slice', max=127), Output()), layout=Layout(align_items=…
%% Cell type:markdown id: tags:
Now the image can be segmented using the `get_3d_cc` method. This method calculates the connected components of a volume and returns a custom CC class. This class contains the `get_cc` method, that returns the segmented volume.
%% Cell type:code id: tags:
``` python
cc = qim3d.segmentation.get_3d_cc(vol_t)
vol_cc = cc.get_cc()
```
%% Output
Total number of connected components found: 1845
%% Cell type:markdown id: tags:
We see that 1845 connectec components were wound. However many of these are very small, and we would only like to find the largest ones. Therefore, we create a for-loop over the connected components, and remove any that are smaller than 300 voxels:
%% Cell type:code id: tags:
``` python
for i in range(1, len(cc)):
if (vol_cc==i).sum() < 300:
vol_cc[vol_cc == i] = 0
vol_cc = vol_cc > 100
```
%% Cell type:markdown id: tags:
Now we can visualize it once again to confirm our operations have worked as intended:
%% Cell type:code id: tags:
``` python
qim3d.viz.slicer(vol_cc)
```
%% Output
interactive(children=(IntSlider(value=64, description='Slice', max=127), Output()), layout=Layout(align_items=…
%% Cell type:markdown id: tags:
In order to enable better visual distinction between the airbubbles, we can use the `watershed` method:
%% Cell type:code id: tags:
``` python
vol_label, num_labels = qim3d.segmentation.watershed(vol_cc, min_distance=5)
```
%% Output
Total number of objects found: 210
%% Cell type:markdown id: tags:
To make different color for every airbubble, we create our own colormap using the `viz.colormaps.segmentation` method:
%% Cell type:code id: tags:
``` python
color_map = qim3d.viz.colormaps.segmentation(num_labels)
qim3d.viz.slicer(vol_label, color_map=color_map)
```
%% Output
interactive(children=(IntSlider(value=64, description='Slice', max=127), Output()), layout=Layout(align_items=…
...@@ -5,7 +5,7 @@ import trimesh ...@@ -5,7 +5,7 @@ import trimesh
import qim3d import qim3d
def volume(obj, **mesh_kwargs) -> float: def volume(obj, logs: bool = True, **mesh_kwargs) -> float:
""" """
Compute the volume of a 3D volume or mesh. Compute the volume of a 3D volume or mesh.
...@@ -45,13 +45,14 @@ def volume(obj, **mesh_kwargs) -> float: ...@@ -45,13 +45,14 @@ def volume(obj, **mesh_kwargs) -> float:
""" """
if isinstance(obj, np.ndarray): if isinstance(obj, np.ndarray):
if logs:
log.info("Converting volume to mesh.") log.info("Converting volume to mesh.")
obj = qim3d.mesh.from_volume(obj, **mesh_kwargs) obj = qim3d.mesh.from_volume(obj, logs=logs, **mesh_kwargs)
return obj.volume return obj.volume
def area(obj, **mesh_kwargs) -> float: def area(obj, logs: bool = True, **mesh_kwargs) -> float:
""" """
Compute the surface area of a 3D volume or mesh. Compute the surface area of a 3D volume or mesh.
...@@ -91,14 +92,14 @@ def area(obj, **mesh_kwargs) -> float: ...@@ -91,14 +92,14 @@ def area(obj, **mesh_kwargs) -> float:
``` ```
""" """
if isinstance(obj, np.ndarray): if isinstance(obj, np.ndarray):
if logs:
log.info("Converting volume to mesh.") log.info("Converting volume to mesh.")
obj = qim3d.mesh.from_volume(obj, **mesh_kwargs) obj = qim3d.mesh.from_volume(obj, logs=logs, **mesh_kwargs)
obj = qim3d.mesh.from_volume(obj, **mesh_kwargs)
return obj.area return obj.area
def sphericity(obj, **mesh_kwargs) -> float: def sphericity(obj, logs: bool = True, **mesh_kwargs) -> float:
""" """
Compute the sphericity of a 3D volume or mesh. Compute the sphericity of a 3D volume or mesh.
...@@ -145,19 +146,16 @@ def sphericity(obj, **mesh_kwargs) -> float: ...@@ -145,19 +146,16 @@ def sphericity(obj, **mesh_kwargs) -> float:
Higher resolution meshes may mitigate these errors but often at the cost of increased computational demands. Higher resolution meshes may mitigate these errors but often at the cost of increased computational demands.
""" """
if isinstance(obj, np.ndarray): if isinstance(obj, np.ndarray):
if logs:
log.info("Converting volume to mesh.") log.info("Converting volume to mesh.")
obj = qim3d.mesh.from_volume(obj, **mesh_kwargs) obj = qim3d.mesh.from_volume(obj, logs=logs, **mesh_kwargs)
obj = qim3d.mesh.from_volume(obj, **mesh_kwargs)
volume = qim3d.features.volume(obj) volume = qim3d.features.volume(obj, logs=logs)
area = qim3d.features.area(obj) area = qim3d.features.area(obj, logs=logs)
volume = qim3d.features.volume(obj)
area = qim3d.features.area(obj)
if area == 0: if area == 0:
log.warning("Surface area is zero, sphericity is undefined.") log.warning("Surface area is zero, sphericity is undefined.")
return np.nan return np.nan
sphericity = (np.pi ** (1 / 3) * (6 * volume) ** (2 / 3)) / area sphericity = (np.pi ** (1 / 3) * (6 * volume) ** (2 / 3)) / area
log.info(f"Sphericity: {sphericity}")
return sphericity return sphericity
...@@ -11,6 +11,7 @@ def from_volume( ...@@ -11,6 +11,7 @@ def from_volume(
step_size=1, step_size=1,
allow_degenerate=False, allow_degenerate=False,
padding: Tuple[int, int, int] = (2, 2, 2), padding: Tuple[int, int, int] = (2, 2, 2),
logs: bool = True,
**kwargs: Any, **kwargs: Any,
) -> trimesh.Trimesh: ) -> trimesh.Trimesh:
""" """
...@@ -50,6 +51,7 @@ def from_volume( ...@@ -50,6 +51,7 @@ def from_volume(
# Compute the threshold level if not provided # Compute the threshold level if not provided
if level is None: if level is None:
level = filters.threshold_otsu(volume) level = filters.threshold_otsu(volume)
if logs:
log.info(f"Computed level using Otsu's method: {level}") log.info(f"Computed level using Otsu's method: {level}")
# Apply padding to the volume # Apply padding to the volume
...@@ -62,6 +64,7 @@ def from_volume( ...@@ -62,6 +64,7 @@ def from_volume(
mode="constant", mode="constant",
constant_values=padding_value, constant_values=padding_value,
) )
if logs:
log.info(f"Padded volume with {padding} to shape: {volume.shape}") log.info(f"Padded volume with {padding} to shape: {volume.shape}")
# Call skimage.measure.marching_cubes with user-provided kwargs # Call skimage.measure.marching_cubes with user-provided kwargs
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment