Source code for acia.segm.processor.omnipose
"""Omnipose segmentation implementation"""
from pathlib import Path
import numpy as np
import torch
from cellpose_omni import models
from tqdm.auto import tqdm
from acia.attribute import attribute_segmentation
from acia.base import ImageSequenceSource, Overlay
from acia.segm.formats import overlay_from_masks
from . import SegmentationProcessor
[docs]
def batch(iterable, n=1):
length = len(iterable)
for ndx in range(0, length, n):
yield iterable[ndx : min(ndx + n, length)]
[docs]
class OmniposeSegmenter(SegmentationProcessor):
"""Omnipose segmentation implementation"""
[docs]
def __init__(
self,
use_GPU: bool | None = None,
model="bact_phase_omni",
autorelease: bool = True,
):
super().__init__(autorelease=autorelease)
if use_GPU is None:
use_GPU = torch.cuda.is_available()
self.use_GPU = use_GPU
self.model_spec = model
def _load_model(self):
model = self.model_spec
model_type = None
model_path = None
if Path(model).exists() and Path(model).is_file():
model_path = model
elif model in models.MODEL_NAMES:
model_type = model
else:
raise ValueError(
"Specified model is neither predefined nor a url to download"
)
if model_type:
return models.CellposeModel(gpu=self.use_GPU, model_type=model_type)
return models.CellposeModel(
gpu=self.use_GPU, pretrained_model=model_path, nclasses=3, nchan=2
)
@staticmethod
def __predict(
images, model, omnipose_parameters: dict | None = None, batch_size=20
):
if omnipose_parameters is None:
omnipose_parameters = {}
chans = [0, 0] # this means segment based on first channel, no second channel
# define parameters
mask_threshold = -1
verbose = 0 # turn on if you want to see more output
transparency = True # transparency in flow output
rescale = (
None # give this a number if you need to upscale or downscale your images
)
omni = True # we can turn off Omnipose mask reconstruction, not advised
flow_threshold = 0.4 # default is .4, but only needed if there are spurious masks to clean up; slows down output
resample = (
True # whether or not to run dynamics on rescaled grid or original grid
)
cluster = True # use DBSCAN clustering
all_masks = []
pbar = tqdm(
total=len(images),
desc="Batched Omnipose prediction...",
)
for image_batch in batch(images, n=batch_size):
# Make evaluation (flows and styles are not needed)
masks, _, _ = model.eval(
image_batch,
channels=chans,
rescale=rescale,
mask_threshold=mask_threshold,
transparency=transparency,
flow_threshold=flow_threshold,
omni=omni,
cluster=cluster,
resample=resample,
verbose=verbose,
model_loaded=True,
show_progress=False,
**omnipose_parameters,
)
all_masks.append(masks)
pbar.update(len(image_batch))
return np.concatenate(all_masks)
[docs]
def predict(self, images: ImageSequenceSource) -> Overlay:
return self(images)
def _segment(
self, images: ImageSequenceSource, omnipose_parameters: dict | None = None
) -> Overlay:
imgs = []
for image in images:
raw_image = image.raw
# Reduce HxWxC=1 image to HxW shape
if len(raw_image.shape) == 3:
if raw_image.shape[2] != 1:
raise ValueError(
f"Omnipose Segmenter only accepts a single channel image. Currently it is HxWxC: {raw_image.shape}"
)
# make it a grayscale image
raw_image = raw_image[..., 0]
imgs.append(raw_image)
masks = self.__predict(
imgs, self.model, omnipose_parameters=omnipose_parameters
)
ov = overlay_from_masks(masks)
attribute_segmentation(ov, self)
return ov