Plotting Utilities¶
API Documentation
siapy.utils.plots
The plotting utilities module provides interactive tools for pixel and area selection, as well as visualization functions for spectral images and signals.
Interactive Pixel Selection¶
Point-based Selection¶
Select individual pixels from an image by clicking on them.
import numpy as np
from siapy.entities import SpectralImage
from siapy.utils.plots import pixels_select_click
# Create a mock spectral image with 4 bands
rng = np.random.default_rng(seed=42)
image_array = rng.random((50, 50, 4)) # height, width, bands
image = SpectralImage.from_numpy(image_array)
# Interactive pixel selection
# Click on pixels in the displayed image, then press Enter to finish
selected_pixels = pixels_select_click(image)
print(f"Selected {len(selected_pixels)} pixels:")
print(selected_pixels.df)
Area-based Selection¶
Select irregular areas from an image using lasso selection tool.
import numpy as np
from siapy.entities import SpectralImage
from siapy.utils.plots import pixels_select_lasso
# Create a mock spectral image
rng = np.random.default_rng(seed=42)
image_array = rng.random((50, 50, 4))
image = SpectralImage.from_numpy(image_array)
# Interactive area selection with custom selector properties
selector_props = {"color": "blue", "linewidth": 3, "linestyle": "--"}
# Draw lasso shapes around areas of interest, then press Enter to finish
selected_areas = pixels_select_lasso(image, selector_props=selector_props)
print(f"Selected {len(selected_areas)} areas:")
for i, area in enumerate(selected_areas):
print(f"Area {i}: {len(area)} pixels")
print(f" Sample coordinates: {area.df.head()}")
# Each area is a separate Pixels object containing all coordinates within the lasso
total_pixels = sum(len(area) for area in selected_areas)
print(f"Total pixels selected across all areas: {total_pixels}")
Image Visualization¶
Display Images with Selected Areas¶
Visualize spectral images with overlaid selected pixels or areas.
import numpy as np
from siapy.entities import Pixels, SpectralImage
from siapy.utils.plots import display_image_with_areas
# Create a mock spectral image
rng = np.random.default_rng(seed=42)
image_array = rng.random((50, 50, 4))
image = SpectralImage.from_numpy(image_array)
# Create predefined areas manually
area1 = Pixels.from_iterable([(10, 15), (12, 18), (15, 20)])
area2 = Pixels.from_iterable([(i, j) for i in range(20, 25) for j in range(30, 35)])
predefined_areas = [area1, area2]
# Display image with predefined areas
display_image_with_areas(image, predefined_areas, color="white")
Multiple Image Comparison¶
Display multiple images side by side with their corresponding selected areas.
import numpy as np
from siapy.entities import Pixels, SpectralImage
from siapy.utils.plots import InteractiveButtonsEnum, display_multiple_images_with_areas
# Create two mock spectral images (e.g., simulating VNIR and SWIR)
rng = np.random.default_rng(seed=42)
# VNIR-like image (4 bands)
vnir_array = rng.random((50, 50, 4))
vnir_image = SpectralImage.from_numpy(vnir_array)
# SWIR-like image (6 bands)
swir_array = rng.random((50, 50, 6))
swir_image = SpectralImage.from_numpy(swir_array)
# Define corresponding areas for both images
vnir_areas = [
Pixels.from_iterable([(10, 15), (12, 18), (15, 20)]),
Pixels.from_iterable([(25, 30), (28, 32), (30, 35)]),
]
swir_areas = [
Pixels.from_iterable([(9, 14), (11, 17), (14, 19)]), # Slightly offset coordinates
Pixels.from_iterable([(24, 29), (27, 31), (29, 34)]),
]
# Display multiple images with interactive buttons
result = display_multiple_images_with_areas(
images_with_areas=[
(vnir_image, vnir_areas),
(swir_image, swir_areas),
],
color="white",
plot_interactive_buttons=True,
)
# Handle user interaction result
if result == InteractiveButtonsEnum.SAVE:
print("User chose to save the selection")
elif result == InteractiveButtonsEnum.REPEAT:
print("User chose to repeat the process")
elif result == InteractiveButtonsEnum.SKIP:
print("User chose to skip this step")
Signal Visualization¶
Plot mean spectral signatures with standard deviation bands for different classes.
import numpy as np
from siapy.datasets.schemas import TabularDatasetData
from siapy.utils.plots import display_signals
# Create sample spectral signatures dataset
rng = np.random.default_rng(seed=42)
# Generate synthetic spectral data (simulate 4 bands: R, G, B, NIR)
n_samples = 50
n_bands = 4
spectral_data = rng.normal(0.5, 0.2, (n_samples, n_bands))
spectral_data = np.clip(spectral_data, 0, 1) # Ensure valid reflectance values
# Create two classes with different spectral characteristics
class_a_indices = np.arange(0, 25)
class_b_indices = np.arange(25, 50)
# Modify spectral characteristics for each class
spectral_data[class_a_indices, 3] += 0.3 # Higher NIR for vegetation-like class
spectral_data[class_b_indices, 0] += 0.2 # Higher red for soil-like class
# Create dataset structure
data_dict = {
"pixels": {
"x": list(range(n_samples)),
"y": [0] * n_samples, # All from same row for simplicity
},
"signals": {f"band_{i}": spectral_data[:, i].tolist() for i in range(n_bands)},
"metadata": {
"sample_id": [f"sample_{i}" for i in range(n_samples)],
},
"target": {
"label": ["vegetation"] * 25 + ["soil"] * 25,
"value": [0] * 25 + [1] * 25,
"encoding": {"vegetation": 0, "soil": 1},
},
}
# Create TabularDatasetData object
dataset = TabularDatasetData.from_dict(data_dict)
# Basic signal plotting
print("Displaying basic spectral signals...")
display_signals(dataset)
# Customized signal plotting
print("Displaying customized spectral signals...")
display_signals(
dataset,
figsize=(10, 6),
dpi=100,
colormap="plasma",
x_label="Spectral Bands (R, G, B, NIR)",
y_label="Reflectance",
label_fontsize=16,
tick_params_label_size=14,
legend_fontsize=12,
legend_frameon=True,
)
# Additional example with more classes
print("Creating dataset with multiple classes...")
# Extend dataset to include a third class
extended_data = data_dict.copy()
extended_data["pixels"]["x"].extend(list(range(50, 75)))
extended_data["pixels"]["y"].extend([0] * 25)
# Add water-like spectral characteristics (low NIR, moderate blue)
water_spectra = rng.normal(0.3, 0.1, (25, n_bands))
water_spectra[:, 1] += 0.2 # Higher blue
water_spectra[:, 3] -= 0.2 # Lower NIR
water_spectra = np.clip(water_spectra, 0, 1)
for i in range(n_bands):
extended_data["signals"][f"band_{i}"].extend(water_spectra[:, i].tolist())
extended_data["metadata"]["sample_id"].extend([f"sample_{i}" for i in range(50, 75)])
extended_data["target"]["label"].extend(["water"] * 25)
extended_data["target"]["value"].extend([2] * 25)
extended_data["target"]["encoding"]["water"] = 2
extended_dataset = TabularDatasetData.from_dict(extended_data)
print("Displaying multi-class spectral signals...")
display_signals(
extended_dataset,
figsize=(12, 8),
colormap="viridis",
x_label="Spectral Bands",
y_label="Reflectance Values",
)