Skip to content

Instantly share code, notes, and snippets.

@dominikl
Created November 6, 2020 11:05
Show Gist options
  • Save dominikl/0c65cc18d57b51c509e93e53c1c44bc6 to your computer and use it in GitHub Desktop.
Save dominikl/0c65cc18d57b51c509e93e53c1c44bc6 to your computer and use it in GitHub Desktop.
Segement an OMERO image and add the result as masks
import matplotlib.pyplot as plt
import numpy as np
import omero
from omero.gateway import BlitzGateway, ColorHolder
from omero.model import MaskI, RoiI
from omero.rtypes import rdouble, rint, rstring
from skimage.filters import threshold_otsu
from skimage.measure import label, regionprops
OME_HOST = "merge-ci"
OME_USER = "xxx"
OME_PASS = "yyy"
IMAGE_ID = 100222 # well A1 of plate 'plate1_1_013'
# IMAGE_ID = 100218 # well B2 of plate 'plate1_1_013'
DISPLAY = True
def connect(hostname, username, password):
"""
Connect to an OMERO server
:param hostname: Host name
:param username: User
:param password: Password
:return: Connected BlitzGateway
"""
conn = BlitzGateway(username, password, host=hostname, port=4064, secure=True)
conn.connect()
conn.c.enableKeepAlive(60)
return conn
def get_pixels(image):
"""Get the pixel values of an OMERO image
(of the first Z/T plane and channel).
Args:
conn (BlitzGateway): The OMERO BlitzGateway.
image (ImageWrapper): The image.
Returns:
array: Two dimensional array of the pixel values.
"""
pp = image.getPrimaryPixels()
plane = pp.getPlane(0, 0, 0)
return plane
def threshold(pixs):
m = np.mean(pixs)
stdev = np.std(pixs)
t = threshold_otsu(pixs)
print(f"{m}, {stdev}, {t}")
res = pixs > t
return res
def mask_from_binary_image(
binim, rgba=None, z=None, c=None, t=None, text=None,
raise_on_no_mask=True):
"""
Create a mask shape from a binary image (background=0)
:param numpy.array binim: Binary 2D array, must contain values [0, 1] only
:param rgba int-4-tuple: Optional (red, green, blue, alpha) colour
:param z: Optional Z-index for the mask
:param c: Optional C-index for the mask
:param t: Optional T-index for the mask
:param text: Optional text for the mask
:param raise_on_no_mask: If True (default) throw an exception if no mask
found, otherwise return an empty Mask
:return: An OMERO mask
:raises NoMaskFound: If no labels were found
:raises InvalidBinaryImage: If the maximum labels is greater than 1
"""
# Find bounding box to minimise size of mask
xmask = binim.sum(0).nonzero()[0]
ymask = binim.sum(1).nonzero()[0]
if any(xmask) and any(ymask):
x0 = min(xmask)
w = max(xmask) - x0 + 1
y0 = min(ymask)
h = max(ymask) - y0 + 1
submask = binim[y0:(y0 + h), x0:(x0 + w)]
if (not np.array_equal(np.unique(submask), [0, 1]) and not
np.array_equal(np.unique(submask), [1])):
raise
else:
if raise_on_no_mask:
raise
x0 = 0
w = 0
y0 = 0
h = 0
submask = []
mask = MaskI()
# BUG in older versions of Numpy:
# https://github.com/numpy/numpy/issues/5377
# Need to convert to an int array
# mask.setBytes(np.packbits(submask))
mask.setBytes(np.packbits(np.asarray(submask, dtype=int)))
mask.setWidth(rdouble(w))
mask.setHeight(rdouble(h))
mask.setX(rdouble(x0))
mask.setY(rdouble(y0))
if rgba is not None:
ch = ColorHolder.fromRGBA(*rgba)
mask.setFillColor(rint(ch.getInt()))
if z is not None:
mask.setTheZ(rint(z))
if c is not None:
mask.setTheC(rint(c))
if t is not None:
mask.setTheT(rint(t))
if text is not None:
mask.setTextValue(rstring(text))
return mask
def masks_from_label_image(
labelim, rgba=None, z=None, c=None, t=None, text=None,
raise_on_no_mask=False):
"""
Create mask shapes from a label image (background=0)
:param numpy.array labelim: 2D label array
:param rgba int-4-tuple: Optional (red, green, blue, alpha) colour
:param z: Optional Z-index for the mask
:param c: Optional C-index for the mask
:param t: Optional T-index for the mask
:param text: Optional text for the mask
:param raise_on_no_mask: If True (default) throw an exception if no mask
found, otherwise return an empty Mask
:return: A list of OMERO masks in label order ([] if no labels found)
"""
masks = []
for i in range(1, labelim.max() + 1):
mask = mask_from_binary_image(labelim == i, rgba, z, c, t, text,
raise_on_no_mask)
masks.append(mask)
return masks
def save_rois(conn, im, rois):
print("Saving {} ROIs for image {}:{}".format(len(rois), im.id, im.name))
us = conn.getUpdateService()
for roi in rois:
im = conn.getObject('Image', im.id)
roi.setImage(im._obj)
us.saveAndReturnObject(roi)
def delete_rois(conn, im):
result = conn.getRoiService().findByImage(im.id, None)
to_delete = []
for roi in result.rois:
to_delete.append(roi.getId().getValue())
if to_delete:
print("Deleting existing {} rois".format(len(to_delete)))
conn.deleteObjects("Roi", to_delete, deleteChildren=True, wait=True)
conn = connect(OME_HOST, OME_USER, OME_PASS)
print(f"Connected as {conn.getUser().getName()}")
img = conn.getObject("Image", IMAGE_ID)
print(f"Image {img.getName()} loaded")
pix = get_pixels(img)
thres = threshold(pix)
lab = label(thres, background=0, connectivity=2)
if DISPLAY:
plt.imshow(lab, cmap=plt.cm.gray)
plt.show()
RGBA = (0, 0, 255, 255)
delete_rois(conn, img)
masks = masks_from_label_image(lab, rgba=RGBA, z=None, c=None, t=None, text=None)
print("{} masks created.".format(len(masks)))
rois = []
for label, mask in enumerate(masks):
roi = omero.model.RoiI()
roi.addShape(mask)
rois.append(roi)
print("{} rois created.".format(len(rois)))
save_rois(conn, img, rois)
conn.close()
@dominikl
Copy link
Author

dominikl commented Nov 6, 2020

Needs omero-py and scikit-image.

Just adjust the variables

OME_HOST = "merge-ci"
OME_USER = "xxx"
OME_PASS = "yyy"
IMAGE_ID = 100222 

, run it and you should get some masks on the image.
Uses scikit-image's otsu method for thresholding.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment