1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
|
"""
Uniform and Non-Uniform Coordinate Systems
==========================================
This example demonstrates the use of uniform and non-uniform coordinate systems
with images in Sigima. It shows how to create, visualize, and work with both
types of coordinate systems, highlighting their differences and appropriate
use cases.
The example shows:
* Creating images with uniform coordinate systems
* Creating images with non-uniform coordinate systems
* Visualizing the coordinate grids
* Comparing pixel spacing and coordinate mapping
* Working with real-world units and coordinates
* Understanding when to use each coordinate system type
This tutorial uses PlotPy for visualization, providing interactive plots
that allow you to explore the coordinate system effects in detail.
"""
# %%
# Importing necessary modules
# ---------------------------
# We'll start by importing all the required modules for image processing
# and visualization.
import numpy as np
from sigima.objects import create_image
from sigima.tests.vistools import view_images_side_by_side
# %%
# Creating test images with uniform coordinates
# ---------------------------------------------
# Uniform coordinates have constant spacing between pixels in both directions.
# This is the most common case for regular imaging systems.
# Create a simple test pattern - a sine wave pattern
size = 100
x_uniform = np.linspace(0, 4 * np.pi, size)
y_uniform = np.linspace(0, 4 * np.pi, size)
X_uniform, Y_uniform = np.meshgrid(x_uniform, y_uniform)
# Create a 2D sine wave pattern
data_uniform = np.sin(X_uniform) * np.cos(Y_uniform)
# Create the image object with uniform coordinates
uniform_image = create_image(
title="Uniform Coordinates",
data=data_uniform,
units=("μm", "μm", "intensity"),
labels=("X position", "Y position", "Signal"),
)
# Set uniform coordinate system with specific spacing and origin
dx = 0.1 # 0.1 μm per pixel in X
dy = 0.1 # 0.1 μm per pixel in Y
x0 = -2.0 # X origin at -2.0 μm
y0 = -2.0 # Y origin at -2.0 μm
uniform_image.set_uniform_coords(dx, dy, x0=x0, y0=y0)
print("✓ Uniform coordinate image created!")
print(f"Image shape: {uniform_image.data.shape}")
coord_type = "Uniform" if uniform_image.is_uniform_coords else "Non-uniform"
print(f"Coordinate system: {coord_type}")
print(f"X spacing (dx): {uniform_image.dx} μm")
print(f"Y spacing (dy): {uniform_image.dy} μm")
print(f"X origin: {uniform_image.x0} μm")
print(f"Y origin: {uniform_image.y0} μm")
x_end = uniform_image.x0 + uniform_image.dx * (uniform_image.data.shape[1] - 1)
y_end = uniform_image.y0 + uniform_image.dy * (uniform_image.data.shape[0] - 1)
print(f"X range: {uniform_image.x0:.1f} to {x_end:.1f} μm")
print(f"Y range: {uniform_image.y0:.1f} to {y_end:.1f} μm")
# %%
# Creating test images with non-uniform coordinates
# -------------------------------------------------
# Non-uniform coordinates allow for variable spacing between pixels,
# useful for adaptive sampling, curved coordinates, or irregular grids.
# Create the same sine wave pattern but with non-uniform coordinates
data_nonuniform = data_uniform.copy()
# Create non-uniform coordinate arrays
# X coordinates: denser near the center, sparser at edges
x_center = 2 * np.pi
x_range = 4 * np.pi
x_nonuniform = x_center + (x_range / 2) * np.tanh(np.linspace(-2, 2, size))
# Y coordinates: quadratic spacing (denser at bottom)
y_start = 0
y_end = 4 * np.pi
y_nonuniform = y_start + (y_end - y_start) * (np.linspace(0, 1, size) ** 2)
# Create the image object with non-uniform coordinates
nonuniform_image = create_image(
title="Non-Uniform Coordinates",
data=data_nonuniform,
units=("μm", "μm", "intensity"),
labels=("X position", "Y position", "Signal"),
)
# Set non-uniform coordinate system
nonuniform_image.set_coords(xcoords=x_nonuniform, ycoords=y_nonuniform)
print("\n✓ Non-uniform coordinate image created!")
print(f"Image shape: {nonuniform_image.data.shape}")
coord_type = "Uniform" if nonuniform_image.is_uniform_coords else "Non-uniform"
print(f"Coordinate system: {coord_type}")
print(f"X coordinates range: {x_nonuniform[0]:.3f} to {x_nonuniform[-1]:.3f} μm")
print(f"Y coordinates range: {y_nonuniform[0]:.3f} to {y_nonuniform[-1]:.3f} μm")
x_spacing_min = np.min(np.diff(x_nonuniform))
x_spacing_max = np.max(np.diff(x_nonuniform))
y_spacing_min = np.min(np.diff(y_nonuniform))
y_spacing_max = np.max(np.diff(y_nonuniform))
print(f"X spacing varies from {x_spacing_min:.4f} to {x_spacing_max:.4f} μm")
print(f"Y spacing varies from {y_spacing_min:.4f} to {y_spacing_max:.4f} μm")
# %%
# Visualizing coordinate system differences
# -----------------------------------------
# Let's create coordinate grid visualizations to highlight the differences
# between uniform and non-uniform coordinate systems.
# Create coordinate grid images for visualization
grid_uniform = np.zeros_like(data_uniform)
grid_nonuniform = np.zeros_like(data_nonuniform)
# Add grid lines every 10 pixels for uniform coordinates
grid_uniform[::10, :] = 1.0 # Horizontal lines
grid_uniform[:, ::10] = 1.0 # Vertical lines
# Add grid lines every 10 pixels for non-uniform coordinates
grid_nonuniform[::10, :] = 1.0 # Horizontal lines
grid_nonuniform[:, ::10] = 1.0 # Vertical lines
# Create grid visualization images
uniform_grid = create_image(
title="Uniform Grid",
data=grid_uniform,
units=("μm", "μm", "grid"),
labels=("X position", "Y position", "Grid lines"),
)
uniform_grid.set_uniform_coords(dx, dy, x0=x0, y0=y0)
nonuniform_grid = create_image(
title="Non-Uniform Grid",
data=grid_nonuniform,
units=("μm", "μm", "grid"),
labels=("X position", "Y position", "Grid lines"),
)
nonuniform_grid.set_coords(xcoords=x_nonuniform, ycoords=y_nonuniform)
print("\n✓ Coordinate grid visualizations created!")
# Display the coordinate system comparison
view_images_side_by_side(
[uniform_image, nonuniform_image],
["Uniform Coordinates", "Non-Uniform Coordinates"],
title="Coordinate Systems Comparison - Data Images",
share_axes=False,
)
view_images_side_by_side(
[uniform_grid, nonuniform_grid],
["Uniform Grid", "Non-Uniform Grid"],
title="Coordinate Systems Comparison - Grid Visualization",
share_axes=False,
)
# %%
# Creating specialized non-uniform coordinate examples
# ----------------------------------------------------
# Let's create some more realistic examples of non-uniform coordinates
# that might be encountered in real applications.
# Example 1: Logarithmic spacing (common in spectroscopy)
size_log = 80
wavelengths = np.logspace(np.log10(400), np.log10(800), size_log) # 400-800 nm
intensities = np.linspace(0, 1, size_log)
W, intensity_grid = np.meshgrid(wavelengths, intensities)
# Create a spectral response pattern
spectral_data = np.exp(-(((W - 550) / 50) ** 2)) * (
1 + 0.3 * np.sin(10 * intensity_grid)
)
spectral_image = create_image(
title="Spectroscopy Data (Log Wavelength)",
data=spectral_data,
units=("nm", "a.u.", "counts"),
labels=("Wavelength", "Intensity", "Signal"),
)
spectral_image.set_coords(xcoords=wavelengths, ycoords=intensities)
print("\n✓ Spectroscopy example created!")
print(f"Wavelength range: {wavelengths[0]:.1f} to {wavelengths[-1]:.1f} nm")
wl_spacing_min = np.min(np.diff(wavelengths))
wl_spacing_max = np.max(np.diff(wavelengths))
print(f"Log spacing: {wl_spacing_min:.2f} to {wl_spacing_max:.2f} nm")
# Example 2: Polar to Cartesian mapping
size_polar = 60
r_coords = np.linspace(1, 10, size_polar)
theta_coords = np.linspace(0, 2 * np.pi, size_polar)
# Convert to Cartesian coordinates for non-uniform mapping
x_polar = np.zeros((size_polar, size_polar))
y_polar = np.zeros((size_polar, size_polar))
for i, r in enumerate(r_coords):
for j, theta in enumerate(theta_coords):
x_polar[i, j] = r * np.cos(theta)
y_polar[i, j] = r * np.sin(theta)
# Create a radial pattern
polar_data = np.zeros((size_polar, size_polar))
for i, r in enumerate(r_coords):
for j, theta in enumerate(theta_coords):
polar_data[i, j] = np.sin(3 * theta) * np.exp(-r / 5)
# Note: For this example, we'll use the polar coordinates directly
# In practice, you might want to interpolate to a regular Cartesian grid
polar_image = create_image(
title="Polar Coordinate Mapping",
data=polar_data,
units=("mm", "rad", "signal"),
labels=("Radius", "Angle", "Amplitude"),
)
polar_image.set_coords(xcoords=r_coords, ycoords=theta_coords)
print("\n✓ Polar coordinate example created!")
print(f"Radial range: {r_coords[0]:.1f} to {r_coords[-1]:.1f} mm")
print(f"Angular range: {theta_coords[0]:.2f} to {theta_coords[-1]:.2f} rad")
# Display the specialized examples
view_images_side_by_side(
[spectral_image, polar_image],
["Spectroscopy (Log Scale)", "Polar Coordinates"],
title="Specialized Non-Uniform Coordinate Examples",
share_axes=False,
)
# %%
# Summary and best practices
# --------------------------
# Let's summarize when to use each coordinate system type.
print("\n" + "=" * 60)
print("COORDINATE SYSTEMS SUMMARY")
print("=" * 60)
print("\n🔲 UNIFORM COORDINATES:")
print(" ✓ Regular imaging systems (cameras, microscopes)")
print(" ✓ Constant pixel spacing in physical units")
print(" ✓ Simple and memory-efficient")
print(" ✓ Fast computations and interpolations")
print(" ✓ Easy integration with standard image processing")
print("\n🔳 NON-UNIFORM COORDINATES:")
print(" ✓ Adaptive sampling systems")
print(" ✓ Curved or distorted coordinate systems")
print(" ✓ Logarithmic or specialized scales")
print(" ✓ Irregular measurement grids")
print(" ✓ Coordinate transformations (polar to Cartesian)")
print("\n📊 PERFORMANCE CONSIDERATIONS:")
print(" • Uniform: O(1) coordinate lookup")
print(" • Non-uniform: O(n) coordinate lookup")
print(" • Memory: Uniform uses 4 parameters, Non-uniform uses 2×N arrays")
print("\n💾 FILE FORMAT SUPPORT:")
print(" • Both coordinate types supported in Sigima HDF5 format")
print(" • Coordinated text format supports non-uniform coordinates")
print(" • Standard image formats (TIFF, etc.) assume uniform coordinates")
# Final comparison view
view_images_side_by_side(
[uniform_image, nonuniform_image, spectral_image, polar_image],
[
"Uniform\n(Regular Grid)",
"Non-Uniform\n(Variable Grid)",
"Logarithmic\n(Spectroscopy)",
"Polar\n(Radial Data)",
],
title="Complete Coordinate Systems Overview",
share_axes=False,
)
print("\n✨ Example completed successfully!")
print("This demonstrates the flexibility of Sigima's coordinate system support.")
|