Initial commit
This commit is contained in:
637
skills/omero-integration/references/scripts.md
Normal file
637
skills/omero-integration/references/scripts.md
Normal file
@@ -0,0 +1,637 @@
|
||||
# Scripts & Batch Operations
|
||||
|
||||
This reference covers creating OMERO.scripts for server-side processing and batch operations.
|
||||
|
||||
## OMERO.scripts Overview
|
||||
|
||||
OMERO.scripts are Python scripts that run on the OMERO server and can be called from OMERO clients (web, insight, CLI). They function as plugins that extend OMERO functionality.
|
||||
|
||||
### Key Features
|
||||
|
||||
- **Server-Side Execution**: Scripts run on the server, avoiding data transfer
|
||||
- **Client Integration**: Callable from any OMERO client with auto-generated UI
|
||||
- **Parameter Handling**: Define input parameters with validation
|
||||
- **Result Reporting**: Return images, files, or messages to clients
|
||||
- **Batch Processing**: Process multiple images or datasets efficiently
|
||||
|
||||
## Basic Script Structure
|
||||
|
||||
### Minimal Script Template
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import omero
|
||||
from omero.gateway import BlitzGateway
|
||||
import omero.scripts as scripts
|
||||
from omero.rtypes import rlong, rstring, robject
|
||||
|
||||
def run_script():
|
||||
"""
|
||||
Main script function.
|
||||
"""
|
||||
# Script definition
|
||||
client = scripts.client(
|
||||
'Script_Name.py',
|
||||
"""
|
||||
Description of what this script does.
|
||||
""",
|
||||
|
||||
# Input parameters
|
||||
scripts.String("Data_Type", optional=False, grouping="1",
|
||||
description="Choose source of images",
|
||||
values=[rstring('Dataset'), rstring('Image')],
|
||||
default=rstring('Dataset')),
|
||||
|
||||
scripts.Long("IDs", optional=False, grouping="2",
|
||||
description="Dataset or Image ID(s)").ofType(rlong(0)),
|
||||
|
||||
# Outputs
|
||||
namespaces=[omero.constants.namespaces.NSDYNAMIC],
|
||||
version="1.0"
|
||||
)
|
||||
|
||||
try:
|
||||
# Get connection
|
||||
conn = BlitzGateway(client_obj=client)
|
||||
|
||||
# Get script parameters
|
||||
script_params = client.getInputs(unwrap=True)
|
||||
data_type = script_params["Data_Type"]
|
||||
ids = script_params["IDs"]
|
||||
|
||||
# Process data
|
||||
message = process_data(conn, data_type, ids)
|
||||
|
||||
# Return results
|
||||
client.setOutput("Message", rstring(message))
|
||||
|
||||
finally:
|
||||
client.closeSession()
|
||||
|
||||
def process_data(conn, data_type, ids):
|
||||
"""
|
||||
Process images based on parameters.
|
||||
"""
|
||||
# Implementation here
|
||||
return "Processing complete"
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_script()
|
||||
```
|
||||
|
||||
## Script Parameters
|
||||
|
||||
### Parameter Types
|
||||
|
||||
```python
|
||||
# String parameter
|
||||
scripts.String("Name", optional=False,
|
||||
description="Enter a name")
|
||||
|
||||
# String with choices
|
||||
scripts.String("Mode", optional=False,
|
||||
values=[rstring('Fast'), rstring('Accurate')],
|
||||
default=rstring('Fast'))
|
||||
|
||||
# Integer parameter
|
||||
scripts.Long("ImageID", optional=False,
|
||||
description="Image to process").ofType(rlong(0))
|
||||
|
||||
# List of integers
|
||||
scripts.List("ImageIDs", optional=False,
|
||||
description="Multiple images").ofType(rlong(0))
|
||||
|
||||
# Float parameter
|
||||
scripts.Float("Threshold", optional=True,
|
||||
description="Threshold value",
|
||||
min=0.0, max=1.0, default=0.5)
|
||||
|
||||
# Boolean parameter
|
||||
scripts.Bool("SaveResults", optional=True,
|
||||
description="Save results to OMERO",
|
||||
default=True)
|
||||
```
|
||||
|
||||
### Parameter Grouping
|
||||
|
||||
```python
|
||||
# Group related parameters
|
||||
scripts.String("Data_Type", grouping="1",
|
||||
description="Source type",
|
||||
values=[rstring('Dataset'), rstring('Image')])
|
||||
|
||||
scripts.Long("Dataset_ID", grouping="1.1",
|
||||
description="Dataset ID").ofType(rlong(0))
|
||||
|
||||
scripts.List("Image_IDs", grouping="1.2",
|
||||
description="Image IDs").ofType(rlong(0))
|
||||
```
|
||||
|
||||
## Accessing Input Data
|
||||
|
||||
### Get Script Parameters
|
||||
|
||||
```python
|
||||
# Inside run_script()
|
||||
client = scripts.client(...)
|
||||
|
||||
# Get parameters as Python objects
|
||||
script_params = client.getInputs(unwrap=True)
|
||||
|
||||
# Access individual parameters
|
||||
data_type = script_params.get("Data_Type", "Image")
|
||||
image_ids = script_params.get("Image_IDs", [])
|
||||
threshold = script_params.get("Threshold", 0.5)
|
||||
save_results = script_params.get("SaveResults", True)
|
||||
```
|
||||
|
||||
### Get Images from Parameters
|
||||
|
||||
```python
|
||||
def get_images_from_params(conn, script_params):
|
||||
"""
|
||||
Get image objects based on script parameters.
|
||||
"""
|
||||
images = []
|
||||
|
||||
data_type = script_params["Data_Type"]
|
||||
|
||||
if data_type == "Dataset":
|
||||
dataset_id = script_params["Dataset_ID"]
|
||||
dataset = conn.getObject("Dataset", dataset_id)
|
||||
if dataset:
|
||||
images = list(dataset.listChildren())
|
||||
|
||||
elif data_type == "Image":
|
||||
image_ids = script_params["Image_IDs"]
|
||||
for image_id in image_ids:
|
||||
image = conn.getObject("Image", image_id)
|
||||
if image:
|
||||
images.append(image)
|
||||
|
||||
return images
|
||||
```
|
||||
|
||||
## Processing Images
|
||||
|
||||
### Batch Image Processing
|
||||
|
||||
```python
|
||||
def process_images(conn, images, threshold):
|
||||
"""
|
||||
Process multiple images.
|
||||
"""
|
||||
results = []
|
||||
|
||||
for image in images:
|
||||
print(f"Processing: {image.getName()}")
|
||||
|
||||
# Get pixel data
|
||||
pixels = image.getPrimaryPixels()
|
||||
size_z = image.getSizeZ()
|
||||
size_c = image.getSizeC()
|
||||
size_t = image.getSizeT()
|
||||
|
||||
# Process each plane
|
||||
for z in range(size_z):
|
||||
for c in range(size_c):
|
||||
for t in range(size_t):
|
||||
plane = pixels.getPlane(z, c, t)
|
||||
|
||||
# Apply threshold
|
||||
binary = (plane > threshold).astype(np.uint8)
|
||||
|
||||
# Count features
|
||||
feature_count = count_features(binary)
|
||||
|
||||
results.append({
|
||||
'image_id': image.getId(),
|
||||
'image_name': image.getName(),
|
||||
'z': z, 'c': c, 't': t,
|
||||
'feature_count': feature_count
|
||||
})
|
||||
|
||||
return results
|
||||
```
|
||||
|
||||
## Generating Outputs
|
||||
|
||||
### Return Messages
|
||||
|
||||
```python
|
||||
# Simple message
|
||||
message = "Processed 10 images successfully"
|
||||
client.setOutput("Message", rstring(message))
|
||||
|
||||
# Detailed message
|
||||
message = "Results:\n"
|
||||
for result in results:
|
||||
message += f"Image {result['image_id']}: {result['count']} cells\n"
|
||||
client.setOutput("Message", rstring(message))
|
||||
```
|
||||
|
||||
### Return Images
|
||||
|
||||
```python
|
||||
# Return newly created image
|
||||
new_image = conn.createImageFromNumpySeq(...)
|
||||
client.setOutput("New_Image", robject(new_image._obj))
|
||||
```
|
||||
|
||||
### Return Files
|
||||
|
||||
```python
|
||||
# Create and return file annotation
|
||||
file_ann = conn.createFileAnnfromLocalFile(
|
||||
output_file_path,
|
||||
mimetype="text/csv",
|
||||
ns="analysis.results"
|
||||
)
|
||||
|
||||
client.setOutput("Result_File", robject(file_ann._obj))
|
||||
```
|
||||
|
||||
### Return Tables
|
||||
|
||||
```python
|
||||
# Create OMERO table and return
|
||||
resources = conn.c.sf.sharedResources()
|
||||
table = create_results_table(resources, results)
|
||||
orig_file = table.getOriginalFile()
|
||||
table.close()
|
||||
|
||||
# Create file annotation
|
||||
file_ann = omero.model.FileAnnotationI()
|
||||
file_ann.setFile(orig_file)
|
||||
file_ann = conn.getUpdateService().saveAndReturnObject(file_ann)
|
||||
|
||||
client.setOutput("Results_Table", robject(file_ann._obj))
|
||||
```
|
||||
|
||||
## Complete Example Scripts
|
||||
|
||||
### Example 1: Maximum Intensity Projection
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import omero
|
||||
from omero.gateway import BlitzGateway
|
||||
import omero.scripts as scripts
|
||||
from omero.rtypes import rlong, rstring, robject
|
||||
import numpy as np
|
||||
|
||||
def run_script():
|
||||
client = scripts.client(
|
||||
'Maximum_Intensity_Projection.py',
|
||||
"""
|
||||
Creates maximum intensity projection from Z-stack images.
|
||||
""",
|
||||
|
||||
scripts.String("Data_Type", optional=False, grouping="1",
|
||||
description="Process images from",
|
||||
values=[rstring('Dataset'), rstring('Image')],
|
||||
default=rstring('Image')),
|
||||
|
||||
scripts.List("IDs", optional=False, grouping="2",
|
||||
description="Dataset or Image ID(s)").ofType(rlong(0)),
|
||||
|
||||
scripts.Bool("Link_to_Source", optional=True, grouping="3",
|
||||
description="Link results to source dataset",
|
||||
default=True),
|
||||
|
||||
version="1.0"
|
||||
)
|
||||
|
||||
try:
|
||||
conn = BlitzGateway(client_obj=client)
|
||||
script_params = client.getInputs(unwrap=True)
|
||||
|
||||
# Get images
|
||||
images = get_images(conn, script_params)
|
||||
created_images = []
|
||||
|
||||
for image in images:
|
||||
print(f"Processing: {image.getName()}")
|
||||
|
||||
# Create MIP
|
||||
mip_image = create_mip(conn, image)
|
||||
if mip_image:
|
||||
created_images.append(mip_image)
|
||||
|
||||
# Report results
|
||||
if created_images:
|
||||
message = f"Created {len(created_images)} MIP images"
|
||||
# Return first image for display
|
||||
client.setOutput("Message", rstring(message))
|
||||
client.setOutput("Result", robject(created_images[0]._obj))
|
||||
else:
|
||||
client.setOutput("Message", rstring("No images created"))
|
||||
|
||||
finally:
|
||||
client.closeSession()
|
||||
|
||||
def get_images(conn, script_params):
|
||||
"""Get images from script parameters."""
|
||||
images = []
|
||||
data_type = script_params["Data_Type"]
|
||||
ids = script_params["IDs"]
|
||||
|
||||
if data_type == "Dataset":
|
||||
for dataset_id in ids:
|
||||
dataset = conn.getObject("Dataset", dataset_id)
|
||||
if dataset:
|
||||
images.extend(list(dataset.listChildren()))
|
||||
else:
|
||||
for image_id in ids:
|
||||
image = conn.getObject("Image", image_id)
|
||||
if image:
|
||||
images.append(image)
|
||||
|
||||
return images
|
||||
|
||||
def create_mip(conn, source_image):
|
||||
"""Create maximum intensity projection."""
|
||||
pixels = source_image.getPrimaryPixels()
|
||||
size_z = source_image.getSizeZ()
|
||||
size_c = source_image.getSizeC()
|
||||
size_t = source_image.getSizeT()
|
||||
|
||||
if size_z == 1:
|
||||
print(" Skipping (single Z-section)")
|
||||
return None
|
||||
|
||||
def plane_gen():
|
||||
for c in range(size_c):
|
||||
for t in range(size_t):
|
||||
# Get Z-stack
|
||||
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 new image
|
||||
new_image = conn.createImageFromNumpySeq(
|
||||
plane_gen(),
|
||||
f"{source_image.getName()}_MIP",
|
||||
1, size_c, size_t,
|
||||
description="Maximum intensity projection",
|
||||
dataset=source_image.getParent()
|
||||
)
|
||||
|
||||
return new_image
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_script()
|
||||
```
|
||||
|
||||
### Example 2: Batch ROI Analysis
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import omero
|
||||
from omero.gateway import BlitzGateway
|
||||
import omero.scripts as scripts
|
||||
from omero.rtypes import rlong, rstring, robject
|
||||
import omero.grid
|
||||
|
||||
def run_script():
|
||||
client = scripts.client(
|
||||
'Batch_ROI_Analysis.py',
|
||||
"""
|
||||
Analyzes ROIs across multiple images and creates results table.
|
||||
""",
|
||||
|
||||
scripts.Long("Dataset_ID", optional=False,
|
||||
description="Dataset with images and ROIs").ofType(rlong(0)),
|
||||
|
||||
scripts.Long("Channel_Index", optional=True,
|
||||
description="Channel to analyze (0-indexed)",
|
||||
default=0, min=0),
|
||||
|
||||
version="1.0"
|
||||
)
|
||||
|
||||
try:
|
||||
conn = BlitzGateway(client_obj=client)
|
||||
script_params = client.getInputs(unwrap=True)
|
||||
|
||||
dataset_id = script_params["Dataset_ID"]
|
||||
channel_index = script_params["Channel_Index"]
|
||||
|
||||
# Get dataset
|
||||
dataset = conn.getObject("Dataset", dataset_id)
|
||||
if not dataset:
|
||||
client.setOutput("Message", rstring("Dataset not found"))
|
||||
return
|
||||
|
||||
# Analyze ROIs
|
||||
results = analyze_rois(conn, dataset, channel_index)
|
||||
|
||||
# Create table
|
||||
table_file = create_results_table(conn, dataset, results)
|
||||
|
||||
# Report
|
||||
message = f"Analyzed {len(results)} ROIs from {dataset.getName()}"
|
||||
client.setOutput("Message", rstring(message))
|
||||
client.setOutput("Results_Table", robject(table_file._obj))
|
||||
|
||||
finally:
|
||||
client.closeSession()
|
||||
|
||||
def analyze_rois(conn, dataset, channel_index):
|
||||
"""Analyze all ROIs in dataset images."""
|
||||
roi_service = conn.getRoiService()
|
||||
results = []
|
||||
|
||||
for image in dataset.listChildren():
|
||||
result = roi_service.findByImage(image.getId(), None)
|
||||
|
||||
if not result.rois:
|
||||
continue
|
||||
|
||||
# Get shape IDs
|
||||
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, [channel_index]
|
||||
)
|
||||
|
||||
# Store results
|
||||
for i, stat in enumerate(stats):
|
||||
results.append({
|
||||
'image_id': image.getId(),
|
||||
'image_name': image.getName(),
|
||||
'shape_id': shape_ids[i],
|
||||
'mean': stat.mean[channel_index],
|
||||
'min': stat.min[channel_index],
|
||||
'max': stat.max[channel_index],
|
||||
'sum': stat.sum[channel_index],
|
||||
'area': stat.pointsCount[channel_index]
|
||||
})
|
||||
|
||||
return results
|
||||
|
||||
def create_results_table(conn, dataset, results):
|
||||
"""Create OMERO table from results."""
|
||||
# Prepare data
|
||||
image_ids = [r['image_id'] for r in results]
|
||||
shape_ids = [r['shape_id'] for r in results]
|
||||
means = [r['mean'] for r in results]
|
||||
mins = [r['min'] for r in results]
|
||||
maxs = [r['max'] for r in results]
|
||||
sums = [r['sum'] for r in results]
|
||||
areas = [r['area'] for r in results]
|
||||
|
||||
# Create table
|
||||
resources = conn.c.sf.sharedResources()
|
||||
repository_id = resources.repositories().descriptions[0].getId().getValue()
|
||||
table = resources.newTable(repository_id, f"ROI_Analysis_{dataset.getId()}")
|
||||
|
||||
# Define columns
|
||||
columns = [
|
||||
omero.grid.ImageColumn('Image', 'Source image', []),
|
||||
omero.grid.LongColumn('ShapeID', 'ROI shape ID', []),
|
||||
omero.grid.DoubleColumn('Mean', 'Mean intensity', []),
|
||||
omero.grid.DoubleColumn('Min', 'Min intensity', []),
|
||||
omero.grid.DoubleColumn('Max', 'Max intensity', []),
|
||||
omero.grid.DoubleColumn('Sum', 'Integrated density', []),
|
||||
omero.grid.LongColumn('Area', 'Area in pixels', [])
|
||||
]
|
||||
table.initialize(columns)
|
||||
|
||||
# Add data
|
||||
data = [
|
||||
omero.grid.ImageColumn('Image', 'Source image', image_ids),
|
||||
omero.grid.LongColumn('ShapeID', 'ROI shape ID', shape_ids),
|
||||
omero.grid.DoubleColumn('Mean', 'Mean intensity', means),
|
||||
omero.grid.DoubleColumn('Min', 'Min intensity', mins),
|
||||
omero.grid.DoubleColumn('Max', 'Max intensity', maxs),
|
||||
omero.grid.DoubleColumn('Sum', 'Integrated density', sums),
|
||||
omero.grid.LongColumn('Area', 'Area in pixels', areas)
|
||||
]
|
||||
table.addData(data)
|
||||
|
||||
orig_file = table.getOriginalFile()
|
||||
table.close()
|
||||
|
||||
# Link to dataset
|
||||
file_ann = omero.model.FileAnnotationI()
|
||||
file_ann.setFile(orig_file)
|
||||
file_ann = conn.getUpdateService().saveAndReturnObject(file_ann)
|
||||
|
||||
link = omero.model.DatasetAnnotationLinkI()
|
||||
link.setParent(dataset._obj)
|
||||
link.setChild(file_ann)
|
||||
conn.getUpdateService().saveAndReturnObject(link)
|
||||
|
||||
return file_ann
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_script()
|
||||
```
|
||||
|
||||
## Script Deployment
|
||||
|
||||
### Installation Location
|
||||
|
||||
Scripts should be placed in the OMERO server scripts directory:
|
||||
```
|
||||
OMERO_DIR/lib/scripts/
|
||||
```
|
||||
|
||||
### Recommended Structure
|
||||
|
||||
```
|
||||
lib/scripts/
|
||||
├── analysis/
|
||||
│ ├── Cell_Counter.py
|
||||
│ └── ROI_Analyzer.py
|
||||
├── export/
|
||||
│ ├── Export_Images.py
|
||||
│ └── Export_ROIs.py
|
||||
└── util/
|
||||
└── Helper_Functions.py
|
||||
```
|
||||
|
||||
### Testing Scripts
|
||||
|
||||
```bash
|
||||
# Test script syntax
|
||||
python Script_Name.py
|
||||
|
||||
# Upload to OMERO
|
||||
omero script upload Script_Name.py
|
||||
|
||||
# List scripts
|
||||
omero script list
|
||||
|
||||
# Run script from CLI
|
||||
omero script launch Script_ID Dataset_ID=123
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Error Handling**: Always use try-finally to close session
|
||||
2. **Progress Updates**: Print status messages for long operations
|
||||
3. **Parameter Validation**: Check parameters before processing
|
||||
4. **Memory Management**: Process large datasets in batches
|
||||
5. **Documentation**: Include clear description and parameter docs
|
||||
6. **Versioning**: Include version number in script
|
||||
7. **Namespaces**: Use appropriate namespaces for outputs
|
||||
8. **Return Objects**: Return created objects for client display
|
||||
9. **Logging**: Use print() for server logs
|
||||
10. **Testing**: Test with various input combinations
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Progress Reporting
|
||||
|
||||
```python
|
||||
total = len(images)
|
||||
for idx, image in enumerate(images):
|
||||
print(f"Processing {idx + 1}/{total}: {image.getName()}")
|
||||
# Process image
|
||||
```
|
||||
|
||||
### Error Collection
|
||||
|
||||
```python
|
||||
errors = []
|
||||
for image in images:
|
||||
try:
|
||||
process_image(image)
|
||||
except Exception as e:
|
||||
errors.append(f"{image.getName()}: {str(e)}")
|
||||
|
||||
if errors:
|
||||
message = "Completed with errors:\n" + "\n".join(errors)
|
||||
else:
|
||||
message = "All images processed successfully"
|
||||
```
|
||||
|
||||
### Resource Cleanup
|
||||
|
||||
```python
|
||||
try:
|
||||
# Script processing
|
||||
pass
|
||||
finally:
|
||||
# Clean up temporary files
|
||||
if os.path.exists(temp_file):
|
||||
os.remove(temp_file)
|
||||
client.closeSession()
|
||||
```
|
||||
Reference in New Issue
Block a user