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

Annotation tool 4 0

parent defd3d14
No related branches found
No related tags found
1 merge request!59Annotation tool 4 0
%% Cell type:markdown id: tags:
# Image annotation tool
This notebook shows how the annotation interface can be used to create masks for images
%% Cell type:code id: tags:
``` python
import qim3d
from scipy import ndimage
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
%matplotlib inline
```
%% Cell type:code id: tags:
qim3d.io.logger.level("info")
``` python
# Load 2D example image
img = qim3d.examples.blobs_256x256
# Display image
plt.imshow(img)
plt.show()
```
%% Cell type:code id: tags:
# Load example image
img = qim3d.examples.bone_128x128x128
``` python
# Start annotation tool
interface = qim3d.gui.annotation_tool.Interface()
interface.max_masks = 4
# We can directly pass the image we loaded to the interface
interface.launch(img=img)
```
%% Cell type:code id: tags:
``` python
# When 'prepare mask for download' is pressed once, the mask can be retrieved with the get_result() method
mask = interface.get_result()
```
%% Cell type:markdown id: tags:
## Check the obtained mask
%% Cell type:code id: tags:
``` python
print (f"Original image shape..: {img.shape}")
print (f"Mask image shape......: {mask.shape}")
print (f"\nNumber of masks: {np.max(mask)}")
```
%% Cell type:markdown id: tags:
## Show the masked regions
%% Cell type:code id: tags:
``` python
%matplotlib inline
nmasks = np.max(mask)
fig, axs = plt.subplots(nrows=1, ncols=nmasks+2, figsize=(12,3))
# Show original image
axs[0].imshow(img)
axs[0].set_title("Original")
axs[0].axis('off')
# Show masks
cmap = mpl.colormaps["rainbow"].copy()
cmap.set_under(color='black') # Sets the background to black
axs[1].imshow(mask, interpolation='none', cmap=cmap, vmin=1, vmax=nmasks+1)
axs[1].set_title("Masks")
axs[1].axis('off')
# Show masked regions
for idx in np.arange(2, nmasks+2):
mask_id = idx-1
submask = mask.copy()
submask[submask != mask_id] = 0
masked_img = img.copy()
masked_img[submask==0] = 0
axs[idx].imshow(masked_img)
axs[idx].set_title(f"Mask {mask_id}")
axs[idx].axis('off')
plt.show()
#interface.launch()
interface.launch(ndimage.zoom(img[0], 3, order=0))
```
......
......@@ -6,6 +6,11 @@ icon: fontawesome/solid/screwdriver-wrench
A set of tools to ease managment of the system, with the common needs for large data in mind.
::: qim3d.utils.img
options:
members:
- overlay_rgb_images
::: qim3d.utils.system
options:
members:
......
......@@ -203,6 +203,7 @@
--button-small-text-weight: 400;
--button-transition: none;
}
/* Up here is the override for dark mode */
/* Now comes our custom CSS */
......@@ -231,6 +232,7 @@ h2 {
.no-border {
border-width: 0px !important;
}
/* Hides Gradio footer */
footer {
visibility: hidden;
......@@ -620,3 +622,94 @@ div.svelte-1frtwj3 {
.wrap.default {
visibility: hidden !important;
}
/* Annotation tool CSS */
.annotation-tool .layer-wrap {
display: none !important;
}
.annotation-tool button[title="Image button"] {
display: none !important;
}
.annotation-tool:not(.no-img) button[title="Upload button"] {
display: none !important;
}
.annotation-tool button[title="Clear canvas"] {
display: none !important;
}
.annotation-tool button[title="Color button"] {
color: var(--block-label-text-color) !important;
margin-inline-end: 32px !important;
}
.annotation-tool button[title="Color button"]::after {
content: "Select mask";
}
.annotation-tool button[title="Size button"] {
color: var(--block-label-text-color) !important;
}
.annotation-tool button[title="Size button"]::after {
content: "Brush size";
}
.annotation-tool .row-wrap {
justify-content: flex-start !important;
}
.annotation-tool .controls-wrap .small {
width: 20px !important;
height: 20px !important;
}
.annotation-tool .controls-wrap {
top: 0px !important;
left: 0px !important;
}
.annotation-tool .controls-wrap .padded {
padding: 8px !important;
border: 0px !important;
background-color: rgba(255, 255, 255, 0.8);
}
.annotation-tool .stage-wrap {
margin-top: 0px !important;
margin-bottom: 16px !important;
}
.annotation-tool .svelte-b3dw9m {
display: flex !important;
visibility: visible !important;
opacity: 1 !important;
}
.annotation-tool .bottom {
bottom: 8px !important;
left: 0px !important;
position: absolute !important;
}
.annotation-tool .download {
min-width: 4rem !important;
width: 10%;
white-space: nowrap;
text-align: right;
}
.annotation-tool .stage-wrap canvas {
border: 0px !important;
max-width: 512px !important;
max-height: 512px !important;
height: 512px !important;
width: fit-content !important;
}
This diff is collapsed.
......@@ -5,3 +5,4 @@ from .data import Dataset, prepare_datasets, prepare_dataloaders
#from .doi import get_bibtex, get_reference
from . import doi
from .system import Memory
from .img import overlay_rgb_images
\ No newline at end of file
import numpy as np
def overlay_rgb_images(background, foreground, alpha=0.5):
"""Overlay multiple RGB foreground onto an RGB background image using alpha blending.
Args:
background (numpy.ndarray): The background RGB image.
foreground (numpy.ndarray): The foreground RGB image (usually masks).
alpha (float, optional): The alpha value for blending. Defaults to 0.5.
Returns:
numpy.ndarray: The composite RGB image with overlaid foreground.
Raises:
ValueError: If input images have different shapes.
Note:
- The function performs alpha blending to overlay the foreground onto the background.
- It ensures that the background and foreground have the same shape before blending.
- It calculates the maximum projection of the foreground and blends them onto the background.
- Brightness outside the foreground is adjusted to maintain consistency with the background.
"""
# Igonore alpha in case its there
background = background[..., :3]
foreground = foreground[..., :3]
# Ensure both images have the same shape
if background.shape != foreground.shape:
raise ValueError("Input images must have the same shape")
# Perform alpha blending
foreground_max_projection = np.amax(foreground, axis=2)
foreground_max_projection = np.stack((foreground_max_projection,) * 3, axis=-1)
# Normalize if we have something
if np.max(foreground_max_projection) > 0:
foreground_max_projection = foreground_max_projection / np.max(foreground_max_projection)
composite = background * (1 - alpha) + foreground * alpha
composite = np.clip(composite, 0, 255).astype("uint8")
# Adjust brightness outside foreground
composite = composite + (background * (1 - alpha)) * (1 - foreground_max_projection)
return composite.astype("uint8")
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment