Created
November 6, 2020 11:05
-
-
Save dominikl/0c65cc18d57b51c509e93e53c1c44bc6 to your computer and use it in GitHub Desktop.
Segement an OMERO image and add the result as masks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Needs
omero-py
andscikit-image
.Just adjust the variables
, run it and you should get some masks on the image.
Uses scikit-image's otsu method for thresholding.