Initial commit
This commit is contained in:
648
skills/omero-integration/references/rois.md
Normal file
648
skills/omero-integration/references/rois.md
Normal file
@@ -0,0 +1,648 @@
|
||||
# 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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
1. **Organize Shapes**: Group related shapes in single ROIs
|
||||
2. **Label Shapes**: Use textValue for identification
|
||||
3. **Set Z and T**: Always specify Z-section and timepoint
|
||||
4. **Color Coding**: Use consistent colors for shape types
|
||||
5. **Validate Coordinates**: Ensure shapes are within image bounds
|
||||
6. **Batch Creation**: Create multiple ROIs in single transaction when possible
|
||||
7. **Delete Unused**: Remove temporary or test ROIs
|
||||
8. **Export Data**: Store ROI statistics in tables for later analysis
|
||||
9. **Version Control**: Document ROI creation methods in annotations
|
||||
10. **Performance**: Use shape statistics service instead of manual pixel extraction
|
||||
Reference in New Issue
Block a user