Initial commit
This commit is contained in:
665
skills/omero-integration/references/image_processing.md
Normal file
665
skills/omero-integration/references/image_processing.md
Normal file
@@ -0,0 +1,665 @@
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
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)
|
||||
|
||||
```python
|
||||
# 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)
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# Switch to grayscale rendering
|
||||
image.setGreyscaleRenderingModel()
|
||||
|
||||
# Switch to color rendering
|
||||
image.setColorRenderingModel()
|
||||
```
|
||||
|
||||
### Set Active Channels
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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)
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
# Modify rendering settings
|
||||
image.setActiveChannels([1, 2])
|
||||
image.setDefaultZ(5)
|
||||
|
||||
# Save as new default
|
||||
image.saveDefaults()
|
||||
```
|
||||
|
||||
### Reset to Import Settings
|
||||
|
||||
```python
|
||||
# Reset to original import settings
|
||||
image.resetDefaults(save=True)
|
||||
```
|
||||
|
||||
## Create Images from NumPy Arrays
|
||||
|
||||
### Create Simple Image
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
Reference in New Issue
Block a user