16 KiB
16 KiB
Regions of Interest (ROIs)
This reference covers creating, retrieving, and analyzing ROIs in OMERO.
ROI Overview
ROIs (Regions of Interest) in OMERO are containers for geometric shapes that mark specific regions on images. Each ROI can contain multiple shapes, and shapes can be specific to Z-sections and timepoints.
Supported Shape Types
- Rectangle: Rectangular regions
- Ellipse: Circular and elliptical regions
- Line: Line segments
- Point: Single points
- Polygon: Multi-point polygons
- Mask: Pixel-based masks
- Polyline: Multi-segment lines
Creating ROIs
Helper Functions
from omero.rtypes import rdouble, rint, rstring
import omero.model
def create_roi(conn, image, shapes):
"""
Create an ROI and link it to shapes.
Args:
conn: BlitzGateway connection
image: Image object
shapes: List of shape objects
Returns:
Saved ROI object
"""
roi = omero.model.RoiI()
roi.setImage(image._obj)
for shape in shapes:
roi.addShape(shape)
updateService = conn.getUpdateService()
return updateService.saveAndReturnObject(roi)
def rgba_to_int(red, green, blue, alpha=255):
"""
Convert RGBA values (0-255) to integer encoding for OMERO.
Args:
red, green, blue, alpha: Color values (0-255)
Returns:
Integer color value
"""
return int.from_bytes([red, green, blue, alpha],
byteorder='big', signed=True)
Rectangle ROI
from omero.rtypes import rdouble, rint, rstring
import omero.model
# Get image
image = conn.getObject("Image", image_id)
# Define position and size
x, y = 50, 100
width, height = 200, 150
z, t = 0, 0 # Z-section and timepoint
# Create rectangle
rect = omero.model.RectangleI()
rect.x = rdouble(x)
rect.y = rdouble(y)
rect.width = rdouble(width)
rect.height = rdouble(height)
rect.theZ = rint(z)
rect.theT = rint(t)
# Set label and colors
rect.textValue = rstring("Cell Region")
rect.fillColor = rint(rgba_to_int(255, 0, 0, 50)) # Red, semi-transparent
rect.strokeColor = rint(rgba_to_int(255, 255, 0, 255)) # Yellow border
# Create ROI
roi = create_roi(conn, image, [rect])
print(f"Created ROI ID: {roi.getId().getValue()}")
Ellipse ROI
# Center position and radii
center_x, center_y = 250, 250
radius_x, radius_y = 100, 75
z, t = 0, 0
# Create ellipse
ellipse = omero.model.EllipseI()
ellipse.x = rdouble(center_x)
ellipse.y = rdouble(center_y)
ellipse.radiusX = rdouble(radius_x)
ellipse.radiusY = rdouble(radius_y)
ellipse.theZ = rint(z)
ellipse.theT = rint(t)
ellipse.textValue = rstring("Nucleus")
ellipse.fillColor = rint(rgba_to_int(0, 255, 0, 50))
# Create ROI
roi = create_roi(conn, image, [ellipse])
Line ROI
# Line endpoints
x1, y1 = 100, 100
x2, y2 = 300, 200
z, t = 0, 0
# Create line
line = omero.model.LineI()
line.x1 = rdouble(x1)
line.y1 = rdouble(y1)
line.x2 = rdouble(x2)
line.y2 = rdouble(y2)
line.theZ = rint(z)
line.theT = rint(t)
line.textValue = rstring("Measurement Line")
line.strokeColor = rint(rgba_to_int(0, 0, 255, 255))
# Create ROI
roi = create_roi(conn, image, [line])
Point ROI
# Point position
x, y = 150, 150
z, t = 0, 0
# Create point
point = omero.model.PointI()
point.x = rdouble(x)
point.y = rdouble(y)
point.theZ = rint(z)
point.theT = rint(t)
point.textValue = rstring("Feature Point")
# Create ROI
roi = create_roi(conn, image, [point])
Polygon ROI
from omero.model.enums import UnitsLength
# Define vertices as string "x1,y1 x2,y2 x3,y3 ..."
vertices = "10,20 50,150 200,200 250,75"
z, t = 0, 0
# Create polygon
polygon = omero.model.PolygonI()
polygon.points = rstring(vertices)
polygon.theZ = rint(z)
polygon.theT = rint(t)
polygon.textValue = rstring("Cell Outline")
# Set colors and stroke width
polygon.fillColor = rint(rgba_to_int(255, 0, 255, 50))
polygon.strokeColor = rint(rgba_to_int(255, 255, 0, 255))
polygon.strokeWidth = omero.model.LengthI(2, UnitsLength.PIXEL)
# Create ROI
roi = create_roi(conn, image, [polygon])
Mask ROI
import numpy as np
import struct
import math
def create_mask_bytes(mask_array, bytes_per_pixel=1):
"""
Convert binary mask array to bit-packed bytes for OMERO.
Args:
mask_array: Binary numpy array (0s and 1s)
bytes_per_pixel: 1 or 2
Returns:
Byte array for OMERO mask
"""
if bytes_per_pixel == 2:
divider = 16.0
format_string = "H"
byte_factor = 0.5
elif bytes_per_pixel == 1:
divider = 8.0
format_string = "B"
byte_factor = 1
else:
raise ValueError("bytes_per_pixel must be 1 or 2")
mask_bytes = mask_array.astype(np.uint8).tobytes()
steps = math.ceil(len(mask_bytes) / divider)
packed_mask = []
for i in range(int(steps)):
binary = mask_bytes[i * int(divider):
i * int(divider) + int(divider)]
format_str = str(int(byte_factor * len(binary))) + format_string
binary = struct.unpack(format_str, binary)
s = "".join(str(bit) for bit in binary)
packed_mask.append(int(s, 2))
return bytearray(packed_mask)
# Create binary mask (1s and 0s)
mask_w, mask_h = 100, 100
mask_array = np.fromfunction(
lambda x, y: ((x - 50)**2 + (y - 50)**2) < 40**2, # Circle
(mask_w, mask_h)
)
# Pack mask
mask_packed = create_mask_bytes(mask_array, bytes_per_pixel=1)
# Mask position
mask_x, mask_y = 50, 50
z, t, c = 0, 0, 0
# Create mask
mask = omero.model.MaskI()
mask.setX(rdouble(mask_x))
mask.setY(rdouble(mask_y))
mask.setWidth(rdouble(mask_w))
mask.setHeight(rdouble(mask_h))
mask.setTheZ(rint(z))
mask.setTheT(rint(t))
mask.setTheC(rint(c))
mask.setBytes(mask_packed)
mask.textValue = rstring("Segmentation Mask")
# Set color
from omero.gateway import ColorHolder
mask_color = ColorHolder()
mask_color.setRed(255)
mask_color.setGreen(0)
mask_color.setBlue(0)
mask_color.setAlpha(100)
mask.setFillColor(rint(mask_color.getInt()))
# Create ROI
roi = create_roi(conn, image, [mask])
Multiple Shapes in One ROI
# Create multiple shapes for the same ROI
shapes = []
# Rectangle
rect = omero.model.RectangleI()
rect.x = rdouble(100)
rect.y = rdouble(100)
rect.width = rdouble(50)
rect.height = rdouble(50)
rect.theZ = rint(0)
rect.theT = rint(0)
shapes.append(rect)
# Ellipse
ellipse = omero.model.EllipseI()
ellipse.x = rdouble(125)
ellipse.y = rdouble(125)
ellipse.radiusX = rdouble(20)
ellipse.radiusY = rdouble(20)
ellipse.theZ = rint(0)
ellipse.theT = rint(0)
shapes.append(ellipse)
# Create single ROI with both shapes
roi = create_roi(conn, image, shapes)
Retrieving ROIs
Get All ROIs for Image
# Get ROI service
roi_service = conn.getRoiService()
# Find all ROIs for image
result = roi_service.findByImage(image_id, None)
print(f"Found {len(result.rois)} ROIs")
for roi in result.rois:
print(f"ROI ID: {roi.getId().getValue()}")
print(f" Number of shapes: {len(roi.copyShapes())}")
Parse ROI Shapes
import omero.model
result = roi_service.findByImage(image_id, None)
for roi in result.rois:
roi_id = roi.getId().getValue()
print(f"ROI ID: {roi_id}")
for shape in roi.copyShapes():
shape_id = shape.getId().getValue()
z = shape.getTheZ().getValue() if shape.getTheZ() else None
t = shape.getTheT().getValue() if shape.getTheT() else None
# Get label
label = ""
if shape.getTextValue():
label = shape.getTextValue().getValue()
print(f" Shape ID: {shape_id}, Z: {z}, T: {t}, Label: {label}")
# Type-specific parsing
if isinstance(shape, omero.model.RectangleI):
x = shape.getX().getValue()
y = shape.getY().getValue()
width = shape.getWidth().getValue()
height = shape.getHeight().getValue()
print(f" Rectangle: ({x}, {y}) {width}x{height}")
elif isinstance(shape, omero.model.EllipseI):
x = shape.getX().getValue()
y = shape.getY().getValue()
rx = shape.getRadiusX().getValue()
ry = shape.getRadiusY().getValue()
print(f" Ellipse: center ({x}, {y}), radii ({rx}, {ry})")
elif isinstance(shape, omero.model.PointI):
x = shape.getX().getValue()
y = shape.getY().getValue()
print(f" Point: ({x}, {y})")
elif isinstance(shape, omero.model.LineI):
x1 = shape.getX1().getValue()
y1 = shape.getY1().getValue()
x2 = shape.getX2().getValue()
y2 = shape.getY2().getValue()
print(f" Line: ({x1}, {y1}) to ({x2}, {y2})")
elif isinstance(shape, omero.model.PolygonI):
points = shape.getPoints().getValue()
print(f" Polygon: {points}")
elif isinstance(shape, omero.model.MaskI):
x = shape.getX().getValue()
y = shape.getY().getValue()
width = shape.getWidth().getValue()
height = shape.getHeight().getValue()
print(f" Mask: ({x}, {y}) {width}x{height}")
Analyzing ROI Intensities
Get Statistics for ROI Shapes
# Get all shapes from ROIs
roi_service = conn.getRoiService()
result = roi_service.findByImage(image_id, None)
shape_ids = []
for roi in result.rois:
for shape in roi.copyShapes():
shape_ids.append(shape.id.val)
# Define position
z, t = 0, 0
channel_index = 0
# Get statistics
stats = roi_service.getShapeStatsRestricted(
shape_ids, z, t, [channel_index]
)
# Display statistics
for i, stat in enumerate(stats):
shape_id = shape_ids[i]
print(f"Shape {shape_id} statistics:")
print(f" Points Count: {stat.pointsCount[channel_index]}")
print(f" Min: {stat.min[channel_index]}")
print(f" Mean: {stat.mean[channel_index]}")
print(f" Max: {stat.max[channel_index]}")
print(f" Sum: {stat.sum[channel_index]}")
print(f" Std Dev: {stat.stdDev[channel_index]}")
Extract Pixel Values Within ROI
import numpy as np
# Get image and ROI
image = conn.getObject("Image", image_id)
result = roi_service.findByImage(image_id, None)
# Get first rectangle shape
roi = result.rois[0]
rect = roi.copyShapes()[0]
# Get rectangle bounds
x = int(rect.getX().getValue())
y = int(rect.getY().getValue())
width = int(rect.getWidth().getValue())
height = int(rect.getHeight().getValue())
z = rect.getTheZ().getValue()
t = rect.getTheT().getValue()
# Get pixel data
pixels = image.getPrimaryPixels()
# Extract region for each channel
for c in range(image.getSizeC()):
# Get plane
plane = pixels.getPlane(z, c, t)
# Extract ROI region
roi_region = plane[y:y+height, x:x+width]
print(f"Channel {c}:")
print(f" Mean intensity: {np.mean(roi_region)}")
print(f" Max intensity: {np.max(roi_region)}")
Modifying ROIs
Update Shape Properties
# Get ROI and shape
result = roi_service.findByImage(image_id, None)
roi = result.rois[0]
shape = roi.copyShapes()[0]
# Modify shape (example: change rectangle size)
if isinstance(shape, omero.model.RectangleI):
shape.setWidth(rdouble(150))
shape.setHeight(rdouble(100))
shape.setTextValue(rstring("Updated Rectangle"))
# Save changes
updateService = conn.getUpdateService()
updated_roi = updateService.saveAndReturnObject(roi._obj)
Remove Shape from ROI
result = roi_service.findByImage(image_id, None)
for roi in result.rois:
for shape in roi.copyShapes():
# Check condition (e.g., remove by label)
if (shape.getTextValue() and
shape.getTextValue().getValue() == "test-Ellipse"):
print(f"Removing shape {shape.getId().getValue()}")
roi.removeShape(shape)
# Save modified ROI
updateService = conn.getUpdateService()
roi = updateService.saveAndReturnObject(roi)
Deleting ROIs
Delete Single ROI
# Delete ROI by ID
roi_id = 123
conn.deleteObjects("Roi", [roi_id], wait=True)
print(f"Deleted ROI {roi_id}")
Delete All ROIs for Image
# Get all ROI IDs for image
result = roi_service.findByImage(image_id, None)
roi_ids = [roi.getId().getValue() for roi in result.rois]
# Delete all
if roi_ids:
conn.deleteObjects("Roi", roi_ids, wait=True)
print(f"Deleted {len(roi_ids)} ROIs")
Batch ROI Creation
Create ROIs for Multiple Images
# Get images
dataset = conn.getObject("Dataset", dataset_id)
for image in dataset.listChildren():
# Create rectangle at center of each image
x = image.getSizeX() // 2 - 50
y = image.getSizeY() // 2 - 50
rect = omero.model.RectangleI()
rect.x = rdouble(x)
rect.y = rdouble(y)
rect.width = rdouble(100)
rect.height = rdouble(100)
rect.theZ = rint(0)
rect.theT = rint(0)
rect.textValue = rstring("Auto ROI")
roi = create_roi(conn, image, [rect])
print(f"Created ROI for image {image.getName()}")
Create ROIs Across Z-Stack
image = conn.getObject("Image", image_id)
size_z = image.getSizeZ()
# Create rectangle on each Z-section
shapes = []
for z in range(size_z):
rect = omero.model.RectangleI()
rect.x = rdouble(100)
rect.y = rdouble(100)
rect.width = rdouble(50)
rect.height = rdouble(50)
rect.theZ = rint(z)
rect.theT = rint(0)
shapes.append(rect)
# Single ROI with shapes across Z
roi = create_roi(conn, image, shapes)
Complete Example
from omero.gateway import BlitzGateway
from omero.rtypes import rdouble, rint, rstring
import omero.model
HOST = 'omero.example.com'
PORT = 4064
USERNAME = 'user'
PASSWORD = 'pass'
def rgba_to_int(r, g, b, a=255):
return int.from_bytes([r, g, b, a], byteorder='big', signed=True)
with BlitzGateway(USERNAME, PASSWORD, host=HOST, port=PORT) as conn:
# Get image
image = conn.getObject("Image", image_id)
print(f"Processing: {image.getName()}")
# Create multiple ROIs
updateService = conn.getUpdateService()
# ROI 1: Rectangle
roi1 = omero.model.RoiI()
roi1.setImage(image._obj)
rect = omero.model.RectangleI()
rect.x = rdouble(50)
rect.y = rdouble(50)
rect.width = rdouble(100)
rect.height = rdouble(100)
rect.theZ = rint(0)
rect.theT = rint(0)
rect.textValue = rstring("Cell 1")
rect.strokeColor = rint(rgba_to_int(255, 0, 0, 255))
roi1.addShape(rect)
roi1 = updateService.saveAndReturnObject(roi1)
print(f"Created ROI 1: {roi1.getId().getValue()}")
# ROI 2: Ellipse
roi2 = omero.model.RoiI()
roi2.setImage(image._obj)
ellipse = omero.model.EllipseI()
ellipse.x = rdouble(200)
ellipse.y = rdouble(150)
ellipse.radiusX = rdouble(40)
ellipse.radiusY = rdouble(30)
ellipse.theZ = rint(0)
ellipse.theT = rint(0)
ellipse.textValue = rstring("Cell 2")
ellipse.strokeColor = rint(rgba_to_int(0, 255, 0, 255))
roi2.addShape(ellipse)
roi2 = updateService.saveAndReturnObject(roi2)
print(f"Created ROI 2: {roi2.getId().getValue()}")
# Retrieve and analyze
roi_service = conn.getRoiService()
result = roi_service.findByImage(image_id, None)
shape_ids = []
for roi in result.rois:
for shape in roi.copyShapes():
shape_ids.append(shape.id.val)
# Get statistics
stats = roi_service.getShapeStatsRestricted(shape_ids, 0, 0, [0])
for i, stat in enumerate(stats):
print(f"Shape {shape_ids[i]}:")
print(f" Mean intensity: {stat.mean[0]:.2f}")
Best Practices
- Organize Shapes: Group related shapes in single ROIs
- Label Shapes: Use textValue for identification
- Set Z and T: Always specify Z-section and timepoint
- Color Coding: Use consistent colors for shape types
- Validate Coordinates: Ensure shapes are within image bounds
- Batch Creation: Create multiple ROIs in single transaction when possible
- Delete Unused: Remove temporary or test ROIs
- Export Data: Store ROI statistics in tables for later analysis
- Version Control: Document ROI creation methods in annotations
- Performance: Use shape statistics service instead of manual pixel extraction