关于 omero-integration
这是面向显微成像研究者和 Python 分析人员的 OMERO 集成技能,通过 omero-py 连接 OMERO 并操作图像数据。它支持像素与 ROI 获取,以及将分析结果作为标注写回服务器。
主要功能
- 01浏览项目、数据集和图像层级
- 02将图像平面或图块下载为 NumPy 数组
- 03管理标签与键值标注
- 04读取图像 ROI 形状
- 05按元数据查询图像
适用场景
- 01从 OMERO 获取图像用于 Python 分析
- 02批量为图像添加质控标签或结果
- 03读取 ROI 以进行下游定量
- 04将分析参数作为标注记录
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jaechang-hits/ sciagent-skills --skill omero-integration安装后在 Claude Code 中输入/omero-integration确认已加载。
详细介绍
omero-integration
概述
OMERO 是一款开源图像数据管理系统,广泛应用于显微镜设施和核心实验室。omero-py 库提供了一个 Python 客户端(BlitzGateway),可连接到 OMERO 服务,以编程方式访问图像、数据集、项目、标签、注释和 ROI。可使用它构建自动化分析工作流,从 OMERO 获取图像,在 Python 中处理,并将结果作为注释写回。
适用场景
- 以编程方式从 OMERO 获取图像:将显微图像下载为 numpy 数组,以供后续分析,而无需使用 OMERO Insight GUI。
- 批量添加注释和标签:根据分析结果,为大量图像/数据集添加标签、键值对注释或评论。
- 访问和管理 ROI:读取存储在 OMERO 中的分割 ROI(形状),以供后续定量分析或导出。
- 将 OMERO 集成到 Python 分析流水线中:将 OMERO 图像数据接入 scikit-image、OpenCV、CellPose 或其他图像分析工具。
- 自动化 QC 工作流:按元数据(通道、采集日期、实验人员)查询图像,并标记不符合质量标准的图像。
- 数据溯源跟踪:将分析溯源信息(参数、工具版本)作为结构化键值注释附加到图像。
- 如需在没有 OMERO 服务的情况下进行本地图像分析,请直接使用
tifffile、aicsimageio或imageio。
先决条件
- Python 包:
omero-py、numpy、Pillow - 系统:Java 8+(
omero-py内部依赖),Ice 3.6(通过 conda 自动安装) - 数据要求:具备运行中的 OMERO 服务的访问凭据(主机、端口、用户名、密码)
- 环境:强烈建议使用 Conda;
omero-py的依赖关系较复杂
conda create -n omero python=3.9
conda activate omero
conda install -c ome -c conda-forge omero-py
pip install numpy Pillow
快速开始
import omero
from omero.gateway import BlitzGateway
# Connect to OMERO server
conn = BlitzGateway("username", "password", host="omero.example.org", port=4064)
conn.connect()
print(f"Connected: {conn.isConnected()}, user: {conn.getUser().getName()}")
# Get an image by ID and download as numpy array
image = conn.getObject("Image", 12345)
pixels = image.getPrimaryPixels()
plane = pixels.getPlane(0, 0, 0) # z=0, c=0, t=0
print(f"Image shape: {plane.shape}, dtype: {plane.dtype}")
conn.close()
核心 API
模块 1:BlitzGateway — 连接管理
BlitzGateway 是与服务交互的主要入口。
from omero.gateway import BlitzGateway
# Establish connection
conn = BlitzGateway(
username="user",
passwd="password",
host="omero.example.org",
port=4064,
secure=True,
)
success = conn.connect()
print(f"Connected: {success}")
print(f"Server version: {conn.getServerVersion()}")
print(f"Current group: {conn.getGroupFromContext().getName()}")
# Always close when done
conn.close()
# Context manager pattern for automatic cleanup
class OmeroConnection:
def __init__(self, **kwargs):
self.conn = BlitzGateway(**kwargs)
def __enter__(self):
self.conn.connect()
return self.conn
def __exit__(self, *args):
self.conn.close()
with OmeroConnection(username="user", passwd="pass",
host="omero.example.org", port=4064) as conn:
print(f"Connected as: {conn.getUser().getFullName()}")
模块 2:项目、数据集和图像查询
遍历 OMERO 数据层级(Project → Dataset → Image)。
# List all projects for the current user
for project in conn.listProjects():
print(f"Project {project.getId()}: {project.getName()}")
for dataset in project.listChildren():
print(f" Dataset {dataset.getId()}: {dataset.getName()}")
for image in dataset.listChildren():
print(f" Image {image.getId()}: {image.getName()}")
# Search for images by name
results = conn.searchObjects(["Image"], "GFP_control")
for img in results:
print(f" Found: {img.getId()} - {img.getName()}")
# Get a specific object by ID
image = conn.getObject("Image", 12345)
dataset = conn.getObject("Dataset", 678)
project = conn.getObject("Project", 90)
print(f"Image: {image.getName()}, size: {image.getSizeX()}x{image.getSizeY()}")
print(f"Channels: {image.getSizeC()}, Z-slices: {image.getSizeZ()}, timepoints: {image.getSizeT()}")
模块 3:将图像下载为 NumPy 数组
获取像素数据作为 numpy 数组进行处理。
import numpy as np
image = conn.getObject("Image", 12345)
pixels = image.getPrimaryPixels()
# Get a single 2D plane: getPlane(z_index, channel_index, time_index)
plane = pixels.getPlane(0, 0, 0)
print(f"Plane shape: {plane.shape}, dtype: {plane.dtype}")
# Get all channels at z=0, t=0
planes = [pixels.getPlane(0, c, 0) for c in range(image.getSizeC())]
stack = np.stack(planes, axis=0) # shape: (C, Y, X)
print(f"Multi-channel stack: {stack.shape}")
# Efficient bulk download using getTiles (for large images)
image = conn.getObject("Image", 12345)
pixels = image.getPrimaryPixels()
tile_coords = [(0, 0, 0, (0, 0, 512, 512))] # (z, c, t, (x, y, w, h))
for tile in pixels.getTiles(tile_coords):
print(f"Tile shape: {tile.shape}") # (512, 512) numpy array
模块 4:标签和注释管理
在 OMERO 对象上添加、获取和更新标签及键值对注释。
import omero
# Add a tag to an image
tag_ann = omero.gateway.TagAnnotationWrapper(conn)
tag_ann.setValue("passed_QC")
tag_ann.setNs("my.analysis.namespace")
tag_ann.save()
image = conn.getObject("Image", 12345)
image.linkAnnotation(tag_ann)
print(f"Tag '{tag_ann.getValue()}' linked to image {image.getId()}")
# Add key-value pairs (MapAnnotation) to an image
map_ann = omero.gateway.MapAnnotationWrapper(conn)
map_ann.setNs("openmicroscopy.org/omero/client/mapAnnotation")
kv_pairs = [
["analysis_tool", "CellProfiler 4.2"],
["cell_count", "342"],
["mean_intensity", "1847.3"],
["analysis_date", "2026-02-18"],
]
map_ann.setValue(kv_pairs)
map_ann.save()
image.linkAnnotation(map_ann)
print(f"Key-value annotation attached to image {image.getId()}")
# Read existing annotations
for ann in image.listAnnotations():
print(f" {ann.OMERO_TYPE}: {ann.getValue()}")
模块 5:ROI 访问
读取存储在 OMERO 中的分割 ROI(形状),以供后续定量分析。
from omero.model import RoiI
# Get all ROIs for an image
roi_service = conn.getRoiService()
result = roi_service.findByImage(12345, None)
for roi in result.rois:
for shape in roi.copyShapes():
shape_type = shape.__class__.__name__
print(f" ROI {roi.id.val}: {shape_type}")
if shape_type == "RectangleI":
print(f" x={shape.x.val:.1f}, y={shape.y.val:.1f}, "
f"w={shape.width.val:.1f}, h={shape.height.val:.1f}")
elif shape_type == "EllipseI":
print(f" cx={shape.x.val:.1f}, cy={shape.y.val:.1f}, "
f"rx={shape.radiusX.val:.1f}, ry={shape.radiusY.val:.1f}")
# Convert ROI masks to numpy boolean arrays
import numpy as np
from omero.gateway import BlitzGateway
def roi_to_mask(shape, height, width):
"""Convert a rectangle ROI to a boolean numpy mask."""
mask = np.zeros((height, width), dtype=bool)
x = int(shape.x.val)
y = int(shape.y.val)
w = int(shape.width.val)
h = int(shape.height.val)
mask[y:y+h, x:x+w] = True
return mask
image = conn.getObject("Image", 12345)
height = image.getSizeY()
width = image.getSizeX()
result = conn.getRoiService().findByImage(12345, None)
for roi in result.rois:
for shape in roi.copyShapes():
if shape.__class__.__name__ == "RectangleI":
mask = roi_to_mask(shape, height, width)
print(f"ROI mask: {mask.sum()} pixels selected")
核心概念
OMERO 数据层级
OMERO 按 Project → Dataset → Image 的层级组织数据。图像包含像素数据和元数据(通道、Z 切片、时间点)。注释(标签、键值对、评论)可以附加到层级中的任意对象。
# Hierarchy navigation
project = conn.getObject("Project", 90)
for dataset in project.listChildren():
imgs = list(dataset.listChildren())
print(f"Dataset '{dataset.getName()}': {len(imgs)} images")
像素访问模式
OMERO 将像素存储为(Z、C、T)堆栈。getPlane(z, c, t) 返回一个 2D numpy 数组。对于大型图像,请使用 getTiles 下载空间子区域。像素类型(uint8、uint16、float32)与原始采集格式一致。
image = conn.getObject("Image", 12345)
pixels = image.getPrimaryPixels()
ptype = pixels.getPixelsType().getValue()
print(f"Pixel type: {ptype}") # e.g., "uint16"
print(f"Dimensions: XY={image.getSizeX()}x{image.getSizeY()}, "
f"Z={image.getSizeZ()}, C={image.getSizeC()}, T={image.getSizeT()}")
常见工作流
工作流 1:批量下载和分析
目标:下载数据集中的所有图像,进行处理,并将结果存储为键值注释。
import numpy as np
from omero.gateway import BlitzGateway, MapAnnotationWrapper
def mean_intensity(plane):
return float(plane.mean())
conn = BlitzGateway("user", "pass", host="omero.example.org", port=4064)
conn.connect()
dataset = conn.getObject("Dataset", 678)
results = []
for image in dataset.listChildren():
pixels = image.getPrimaryPixels()
plane = pixels.getPlane(0, 0, 0) # first z, channel 0, t=0
mi = mean_intensity(plane)
results.append((image, mi))
# Attach mean intensity as key-value annotation
ann = MapAnnotationWrapper(conn)
ann.setNs("my.pipeline.v1")
ann.setValue([["mean_intensity_ch0", f"{mi:.2f}"]])
ann.save()
image.linkAnnotation(ann)
print(f"Image {image.getId()} '{image.getName()}': mean={mi:.2f}")
print(f"\nProcessed {len(results)} images in dataset '{dataset.getName()}'")
conn.close()
工作流 2:按标签获取图像并导出
目标:查找所有带有 "screen_hits" 标签的图像,下载通道 1 并保存为 TIFF 文件。
import numpy as np
import tifffile
from omero.gateway import BlitzGateway
conn = BlitzGateway("user", "pass", host="omero.example.org", port=4064)
conn.connect()
# Find images with a specific tag
tag_value = "screen_hits"
tagged_images = []
for ann in conn.getObjects("TagAnnotation", attributes={"textValue": tag_value}):
for image in ann.listLinkedObjects(["Image"]):
tagged_images.append(image)
print(f"Found {len(tagged_images)} images tagged '{tag_value}'")
for image in tagged_images:
pixels = image.getPrimaryPixels()
# Download DAPI channel (index 0) at z=0, t=0
plane = pixels.getPlane(0, 0, 0)
fname = f"image_{image.getId()}_DAPI.tif"
tifffile.imwrite(fname, plane)
print(f"Saved {fname}: shape={plane.shape}, dtype={plane.dtype}")
conn.close()
print("Export complete")
关键参数
| 参数 | 模块 | 默认值 | 范围 / 选项 | 作用 |
|---|---|---|---|---|
host | BlitzGateway | 必填 | 主机名或 IP | OMERO 服务地址 |
port | BlitzGateway | 4064 | 1024–65535 | OMERO 服务端口(4064 = 标准端口) |
secure | BlitzGateway | False | True、False | 使用 SSL/TLS 加密连接 |
z, c, t | getPlane | 0, 0, 0 | 0 – size-1 | Z 切片、通道、时间点索引 |
tile_coords | getTiles | — | (z,c,t,(x,y,w,h)) 列表 | 空间子区域下载坐标 |
Ns | annotations | None | 任意 URI 字符串 | 用于筛选注释的命名空间 |
最佳实践
-
始终关闭连接:完成所有操作后调用
conn.close(),或使用上下文管理器。未关闭的连接会占用服务资源,并可能导致会话超时。 -
为注释使用命名空间:为创建的每条注释设置唯一的
Ns(命名空间 URI),以便区分程序生成的注释、手动添加的注释和其他工具生成的注释。 -
只下载所需图像平面:对于大型延时成像或 Z-stack 图像,使用带有具体索引的
getPlane(z, c, t),而不是下载所有图像平面。若需空间子集,请使用getTiles。 -
批量写入注释以减少服务端往返:收集多张图像上的键值对,并通过循环写入,而不是针对每张图像的每个键分别发起 RPC 调用。
-
处理前检查图像尺寸:访问像素数据前,务必读取
getSizeX()、getSizeY()、getSizeC()、getSizeZ()、getSizeT(),以避免数据集尺寸异常时发生索引越界错误。
常用示例
示例:列出图像上的所有标签
适用场景:添加新标签前检查现有注释,避免重复。
image = conn.getObject("Image", 12345)
for ann in image.listAnnotations():
if ann.OMERO_TYPE == "TagAnnotation":
print(f" Tag: '{ann.getValue()}' (ns={ann.getNs()})")
elif ann.OMERO_TYPE == "MapAnnotation":
for k, v in ann.getValue():
print(f" KV: {k} = {v}")
示例:多通道最大值投影
适用场景:为指定通道创建跨所有 Z 切片的最大强度投影。
import numpy as np
def max_projection(image, channel=0, timepoint=0):
pixels = image.getPrimaryPixels()
planes = [pixels.getPlane(z, channel, timepoint)
for z in range(image.getSizeZ())]
stack = np.stack(planes, axis=0)
return stack.max(axis=0)
image = conn.getObject("Image", 12345)
proj = max_projection(image, channel=1)
print(f"Max projection shape: {proj.shape}, max value: {proj.max()}")
预期输出
getPlane()返回的 NumPy 数组:形状为(Y, X),数据类型与采集数据一致(uint8、uint16、float32)- 多通道堆栈:堆叠图像平面时,形状为
(C, Y, X) - 附加到图像的注释可在 OMERO.web 和 OMERO Insight GUI 中查看
- ROI 形状以
omero.model对象形式返回,并包含坐标属性
故障排查
| 问题 | 原因 | 解决方案 |
|---|---|---|
Ice.ConnectionRefusedException | 主机/端口错误或服务端已关闭 | 验证 host 和 port=4064;确认服务端正在运行 |
omero.SecurityViolation | 对对象的权限不足 | 检查组成员身份;请服务端管理员授予访问权限 |
ImportError: omero | 未通过 conda 安装 omero-py | 使用 conda install -c ome -c conda-forge omero-py;pip 安装不可靠 |
AttributeError: 'NoneType' object | 服务端上未找到对象 ID | 验证对象是否存在:conn.getObject(type, id) 是否返回非 None |
Ice.MemoryLimitException | 一次性下载的图像过大 | 使用 getTiles() 下载空间子集,或逐个切片使用 getPlane() |
| 下载速度慢 | 按顺序下载大量小图像平面 | 使用 getTiles() 并传入所有坐标的列表进行批量下载 |
| 运行中会话超时 | 长时间分析超过服务端空闲超时时间 | 在较长的循环中定期调用 conn.keepAlive() |
参考资料
- OMERO Python API 文档 — omero-py 官方文档
- OMERO 开发者文档 — Python 客户端指南
- OMERO GitHub (ome/omero-py) — 源代码和问题跟踪器
- Linkert 等人(2010),J Cell Biol — OMERO 论文 — OMERO 的原始出版物