Files
2025-11-30 08:30:10 +08:00

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:

  • ANGSTROM
  • NANOMETER
  • MICROMETER
  • MILLIMETER
  • CENTIMETER
  • METER
  • PIXEL

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

  1. Use Generators: For creating images, use generators to avoid loading all data in memory
  2. Specify Data Types: Match NumPy dtypes to OMERO pixel types
  3. Set Physical Dimensions: Always set pixel sizes for new images
  4. Tile Large Images: Process large images in tiles to manage memory
  5. Close Connections: Always close connections when done
  6. Rendering Efficiency: Cache rendering settings when rendering multiple images
  7. Channel Indexing: Remember channels are 1-indexed for rendering, 0-indexed for pixel access
  8. Save Settings: Save rendering settings if they should be permanent
  9. Compression: Use compression parameter in renderImage() for faster transfers
  10. Error Handling: Check for None returns and handle exceptions