15 KiB
15 KiB
Image Processing & Rendering
This reference covers accessing raw pixel data, image rendering, and creating new images in OMERO.
Accessing Raw Pixel Data
Get Single Plane
# Get image
image = conn.getObject("Image", image_id)
# Get dimensions
size_z = image.getSizeZ()
size_c = image.getSizeC()
size_t = image.getSizeT()
# Get pixels object
pixels = image.getPrimaryPixels()
# Get single plane (returns NumPy array)
z, c, t = 0, 0, 0 # First Z-section, channel, and timepoint
plane = pixels.getPlane(z, c, t)
print(f"Shape: {plane.shape}")
print(f"Data type: {plane.dtype.name}")
print(f"Min: {plane.min()}, Max: {plane.max()}")
Get Multiple Planes
import numpy as np
# Get Z-stack for specific channel and timepoint
pixels = image.getPrimaryPixels()
c, t = 0, 0 # First channel and timepoint
# Build list of (z, c, t) coordinates
zct_list = [(z, c, t) for z in range(size_z)]
# Get all planes at once
planes = pixels.getPlanes(zct_list)
# Stack into 3D array
z_stack = np.array([p for p in planes])
print(f"Z-stack shape: {z_stack.shape}")
Get Hypercube (Subset of 5D Data)
# Get subset of 5D data (Z, C, T)
zct_list = []
for z in range(size_z // 2, size_z): # Second half of Z
for c in range(size_c): # All channels
for t in range(size_t): # All timepoints
zct_list.append((z, c, t))
# Get planes
planes = pixels.getPlanes(zct_list)
# Process each plane
for i, plane in enumerate(planes):
z, c, t = zct_list[i]
print(f"Plane Z={z}, C={c}, T={t}: Min={plane.min()}, Max={plane.max()}")
Get Tile (Region of Interest)
# Define tile coordinates
x, y = 50, 50 # Top-left corner
width, height = 100, 100 # Tile size
tile = (x, y, width, height)
# Get tile for specific Z, C, T
z, c, t = 0, 0, 0
zct_list = [(z, c, t, tile)]
tiles = pixels.getTiles(zct_list)
tile_data = tiles[0]
print(f"Tile shape: {tile_data.shape}") # Should be (height, width)
Get Multiple Tiles
# Get tiles from Z-stack
c, t = 0, 0
tile = (50, 50, 100, 100) # x, y, width, height
# Build list with tiles
zct_list = [(z, c, t, tile) for z in range(size_z)]
tiles = pixels.getTiles(zct_list)
for i, tile_data in enumerate(tiles):
print(f"Tile Z={i}: {tile_data.shape}, Min={tile_data.min()}")
Image Histograms
Get Histogram
# Get histogram for first channel
channel_index = 0
num_bins = 256
z, t = 0, 0
histogram = image.getHistogram([channel_index], num_bins, False, z, t)
print(f"Histogram bins: {len(histogram)}")
print(f"First 10 bins: {histogram[:10]}")
Multi-Channel Histogram
# Get histograms for all channels
channels = list(range(image.getSizeC()))
histograms = image.getHistogram(channels, 256, False, 0, 0)
for c, hist in enumerate(histograms):
print(f"Channel {c}: Total pixels = {sum(hist)}")
Image Rendering
Render Image with Current Settings
from PIL import Image
from io import BytesIO
# Get image
image = conn.getObject("Image", image_id)
# Render at specific Z and T
z = image.getSizeZ() // 2 # Middle Z-section
t = 0
rendered_image = image.renderImage(z, t)
# rendered_image is a PIL Image object
rendered_image.save("rendered_image.jpg")
Get Thumbnail
from PIL import Image
from io import BytesIO
# Get thumbnail (uses current rendering settings)
thumbnail_data = image.getThumbnail()
# Convert to PIL Image
thumbnail = Image.open(BytesIO(thumbnail_data))
thumbnail.save("thumbnail.jpg")
# Get specific thumbnail size
thumbnail_data = image.getThumbnail(size=(96, 96))
thumbnail = Image.open(BytesIO(thumbnail_data))
Rendering Settings
View Current Settings
# Display rendering settings
print("Current Rendering Settings:")
print(f"Grayscale mode: {image.isGreyscaleRenderingModel()}")
print(f"Default Z: {image.getDefaultZ()}")
print(f"Default T: {image.getDefaultT()}")
print()
# Channel settings
print("Channel Settings:")
for idx, channel in enumerate(image.getChannels()):
print(f"Channel {idx + 1}:")
print(f" Label: {channel.getLabel()}")
print(f" Color: {channel.getColor().getHtml()}")
print(f" Active: {channel.isActive()}")
print(f" Window: {channel.getWindowStart()} - {channel.getWindowEnd()}")
print(f" Min/Max: {channel.getWindowMin()} - {channel.getWindowMax()}")
Set Rendering Model
# Switch to grayscale rendering
image.setGreyscaleRenderingModel()
# Switch to color rendering
image.setColorRenderingModel()
Set Active Channels
# Activate specific channels (1-indexed)
image.setActiveChannels([1, 3]) # Channels 1 and 3 only
# Activate all channels
all_channels = list(range(1, image.getSizeC() + 1))
image.setActiveChannels(all_channels)
# Activate single channel
image.setActiveChannels([2])
Set Channel Colors
# Set channel colors (hex format)
channels = [1, 2, 3]
colors = ['FF0000', '00FF00', '0000FF'] # Red, Green, Blue
image.setActiveChannels(channels, colors=colors)
# Use None to keep existing color
colors = ['FF0000', None, '0000FF'] # Keep channel 2's color
image.setActiveChannels(channels, colors=colors)
Set Channel Window (Intensity Range)
# Set intensity windows for channels
channels = [1, 2]
windows = [
[100.0, 500.0], # Channel 1: 100-500
[50.0, 300.0] # Channel 2: 50-300
]
image.setActiveChannels(channels, windows=windows)
# Use None to keep existing window
windows = [[100.0, 500.0], [None, None]]
image.setActiveChannels(channels, windows=windows)
Set Default Z and T
# Set default Z-section and timepoint
image.setDefaultZ(5)
image.setDefaultT(0)
# Render using defaults
rendered_image = image.renderImage(z=None, t=None)
rendered_image.save("default_rendering.jpg")
Render Individual Channels
Render Each Channel Separately
# Set grayscale mode
image.setGreyscaleRenderingModel()
z = image.getSizeZ() // 2
t = 0
# Render each channel
for c in range(1, image.getSizeC() + 1):
image.setActiveChannels([c])
rendered = image.renderImage(z, t)
rendered.save(f"channel_{c}.jpg")
Render Multi-Channel Composites
# Color composite of first 3 channels
image.setColorRenderingModel()
channels = [1, 2, 3]
colors = ['FF0000', '00FF00', '0000FF'] # RGB
image.setActiveChannels(channels, colors=colors)
rendered = image.renderImage(z, t)
rendered.save("rgb_composite.jpg")
Image Projections
Maximum Intensity Projection
# Set projection type
image.setProjection('intmax')
# Render (projects across all Z)
z, t = 0, 0 # Z is ignored for projections
rendered = image.renderImage(z, t)
rendered.save("max_projection.jpg")
# Reset to normal rendering
image.setProjection('normal')
Mean Intensity Projection
image.setProjection('intmean')
rendered = image.renderImage(z, t)
rendered.save("mean_projection.jpg")
image.setProjection('normal')
Available Projection Types
'normal': No projection (default)'intmax': Maximum intensity projection'intmean': Mean intensity projection'intmin': Minimum intensity projection (if supported)
Save and Reset Rendering Settings
Save Current Settings as Default
# Modify rendering settings
image.setActiveChannels([1, 2])
image.setDefaultZ(5)
# Save as new default
image.saveDefaults()
Reset to Import Settings
# Reset to original import settings
image.resetDefaults(save=True)
Create Images from NumPy Arrays
Create Simple Image
import numpy as np
# Create sample data
size_x, size_y = 512, 512
size_z, size_c, size_t = 10, 2, 1
# Generate planes
def plane_generator():
"""Generator that yields planes"""
for z in range(size_z):
for c in range(size_c):
for t in range(size_t):
# Create synthetic data
plane = np.random.randint(0, 255, (size_y, size_x), dtype=np.uint8)
yield plane
# Create image
image = conn.createImageFromNumpySeq(
plane_generator(),
"Test Image",
size_z, size_c, size_t,
description="Image created from NumPy arrays",
dataset=None
)
print(f"Created image ID: {image.getId()}")
Create Image from Hard-Coded Arrays
from numpy import array, int8
# Define dimensions
size_x, size_y = 5, 4
size_z, size_c, size_t = 1, 2, 1
# Create planes
plane1 = array(
[[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]],
dtype=int8
)
plane2 = array(
[[5, 6, 7, 8, 9],
[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[0, 1, 2, 3, 4]],
dtype=int8
)
planes = [plane1, plane2]
def plane_gen():
for p in planes:
yield p
# Create image
desc = "Image created from hard-coded arrays"
image = conn.createImageFromNumpySeq(
plane_gen(),
"numpy_image",
size_z, size_c, size_t,
description=desc,
dataset=None
)
print(f"Created image: {image.getName()} (ID: {image.getId()})")
Create Image in Dataset
# Get target dataset
dataset = conn.getObject("Dataset", dataset_id)
# Create image
image = conn.createImageFromNumpySeq(
plane_generator(),
"New Analysis Result",
size_z, size_c, size_t,
description="Result from analysis pipeline",
dataset=dataset # Add to dataset
)
Create Derived Image
# Get source image
source = conn.getObject("Image", source_image_id)
size_z = source.getSizeZ()
size_c = source.getSizeC()
size_t = source.getSizeT()
dataset = source.getParent()
pixels = source.getPrimaryPixels()
new_size_c = 1 # Average channels
def plane_gen():
"""Average channels together"""
for z in range(size_z):
for c in range(new_size_c):
for t in range(size_t):
# Get multiple channels
channel0 = pixels.getPlane(z, 0, t)
channel1 = pixels.getPlane(z, 1, t)
# Combine
new_plane = (channel0.astype(float) + channel1.astype(float)) / 2
new_plane = new_plane.astype(channel0.dtype)
yield new_plane
# Create new image
desc = "Averaged channels from source image"
derived = conn.createImageFromNumpySeq(
plane_gen(),
f"{source.getName()}_averaged",
size_z, new_size_c, size_t,
description=desc,
dataset=dataset
)
print(f"Created derived image: {derived.getId()}")
Set Physical Dimensions
Set Pixel Sizes with Units
from omero.model.enums import UnitsLength
import omero.model
# Get image
image = conn.getObject("Image", image_id)
# Create unit objects
pixel_size_x = omero.model.LengthI(0.325, UnitsLength.MICROMETER)
pixel_size_y = omero.model.LengthI(0.325, UnitsLength.MICROMETER)
pixel_size_z = omero.model.LengthI(1.0, UnitsLength.MICROMETER)
# Get pixels object
pixels = image.getPrimaryPixels()._obj
# Set physical sizes
pixels.setPhysicalSizeX(pixel_size_x)
pixels.setPhysicalSizeY(pixel_size_y)
pixels.setPhysicalSizeZ(pixel_size_z)
# Save changes
conn.getUpdateService().saveObject(pixels)
print("Updated pixel dimensions")
Available Length Units
From omero.model.enums.UnitsLength:
ANGSTROMNANOMETERMICROMETERMILLIMETERCENTIMETERMETERPIXEL
Set Pixel Size on New Image
from omero.model.enums import UnitsLength
import omero.model
# Create image
image = conn.createImageFromNumpySeq(
plane_generator(),
"New Image with Dimensions",
size_z, size_c, size_t
)
# Set pixel sizes
pixel_size = omero.model.LengthI(0.5, UnitsLength.MICROMETER)
pixels = image.getPrimaryPixels()._obj
pixels.setPhysicalSizeX(pixel_size)
pixels.setPhysicalSizeY(pixel_size)
z_size = omero.model.LengthI(2.0, UnitsLength.MICROMETER)
pixels.setPhysicalSizeZ(z_size)
conn.getUpdateService().saveObject(pixels)
Complete Example: Image Processing Pipeline
from omero.gateway import BlitzGateway
import numpy as np
HOST = 'omero.example.com'
PORT = 4064
USERNAME = 'user'
PASSWORD = 'pass'
with BlitzGateway(USERNAME, PASSWORD, host=HOST, port=PORT) as conn:
# Get source image
source = conn.getObject("Image", source_image_id)
print(f"Processing: {source.getName()}")
# Get dimensions
size_x = source.getSizeX()
size_y = source.getSizeY()
size_z = source.getSizeZ()
size_c = source.getSizeC()
size_t = source.getSizeT()
pixels = source.getPrimaryPixels()
# Process: Maximum intensity projection over Z
def plane_gen():
for c in range(size_c):
for t in range(size_t):
# Get all Z planes for this C, T
z_stack = []
for z in range(size_z):
plane = pixels.getPlane(z, c, t)
z_stack.append(plane)
# Maximum projection
max_proj = np.max(z_stack, axis=0)
yield max_proj
# Create result image (single Z-section)
result = conn.createImageFromNumpySeq(
plane_gen(),
f"{source.getName()}_MIP",
1, size_c, size_t, # Z=1 for projection
description="Maximum intensity projection",
dataset=source.getParent()
)
print(f"Created MIP image: {result.getId()}")
# Copy pixel sizes (X and Y only, no Z for projection)
from omero.model.enums import UnitsLength
import omero.model
source_pixels = source.getPrimaryPixels()._obj
result_pixels = result.getPrimaryPixels()._obj
result_pixels.setPhysicalSizeX(source_pixels.getPhysicalSizeX())
result_pixels.setPhysicalSizeY(source_pixels.getPhysicalSizeY())
conn.getUpdateService().saveObject(result_pixels)
print("Processing complete")
Working with Different Data Types
Handle Various Pixel Types
# Get pixel type
pixel_type = image.getPixelsType()
print(f"Pixel type: {pixel_type}")
# Common types: uint8, uint16, uint32, int8, int16, int32, float, double
# Get plane with correct dtype
plane = pixels.getPlane(z, c, t)
print(f"NumPy dtype: {plane.dtype}")
# Convert if needed for processing
if plane.dtype == np.uint16:
# Convert to float for processing
plane_float = plane.astype(np.float32)
# Process...
# Convert back
result = plane_float.astype(np.uint16)
Handle Large Images
# Process large images in tiles to save memory
tile_size = 512
size_x = image.getSizeX()
size_y = image.getSizeY()
for y in range(0, size_y, tile_size):
for x in range(0, size_x, tile_size):
# Get tile dimensions
w = min(tile_size, size_x - x)
h = min(tile_size, size_y - y)
tile = (x, y, w, h)
# Get tile data
zct_list = [(z, c, t, tile)]
tile_data = pixels.getTiles(zct_list)[0]
# Process tile
# ...
Best Practices
- Use Generators: For creating images, use generators to avoid loading all data in memory
- Specify Data Types: Match NumPy dtypes to OMERO pixel types
- Set Physical Dimensions: Always set pixel sizes for new images
- Tile Large Images: Process large images in tiles to manage memory
- Close Connections: Always close connections when done
- Rendering Efficiency: Cache rendering settings when rendering multiple images
- Channel Indexing: Remember channels are 1-indexed for rendering, 0-indexed for pixel access
- Save Settings: Save rendering settings if they should be permanent
- Compression: Use compression parameter in renderImage() for faster transfers
- Error Handling: Check for None returns and handle exceptions