Source code for patato.utils.mask_operations

#  Copyright (c) Thomas Else 2023-25.
#  License: MIT

from __future__ import annotations

from typing import List
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from .rois.roi_type import ROI

import numpy as np


[docs] def to_binary_mask(vertices, min_x, max_x, nx, min_y, max_y, ny): from matplotlib import path from shapely.geometry import Polygon, Point, MultiPolygon if isinstance(vertices, (np.ndarray, list)): vert_path = path.Path(vertices, closed=False) else: vert_path = vertices xs = np.linspace(min_x, max_x, nx) ys = np.linspace(min_y, max_y, ny) X, Y = np.meshgrid(xs, ys) points = np.array([X.flatten(), Y.flatten()]).T if isinstance(vert_path, (Polygon, MultiPolygon)): points = [Point(r) for r in points] mask = np.array([vert_path.contains(r) for r in points]) # .reshape(X.shape) else: mask = vert_path.contains_points(points) return mask.reshape(X.shape)
[docs] def get_polygon_mask(p, fov_x, fov_y, nx, ny): from shapely.geometry import Polygon, MultiPolygon if not isinstance(fov_x, (list, tuple, np.ndarray)): fov_x = [-fov_x / 2, fov_x / 2] fov_y = [-fov_y / 2, fov_y / 2] if isinstance(p, Polygon): mask = to_binary_mask( np.array(p.exterior.coords.xy).T, fov_x[0], fov_x[1], nx, fov_y[0], fov_y[1], ny, ) for interior in p.interiors: mask &= ~to_binary_mask( np.array(interior.coords.xy).T, fov_x[0], fov_x[1], nx, fov_y[0], fov_y[1], ny, ) elif isinstance(p, MultiPolygon): geoms = list(p.geoms) mask = get_polygon_mask(geoms[0], fov_x, fov_y, nx, ny) for g in geoms[1:]: mask |= get_polygon_mask(g, fov_x, fov_y, nx, ny) else: print("WARNING: something strange happening...") mask = np.zeros((nx, ny)) return mask
[docs] def generate_mask(vertices, fov_x, nx, fov_y=None, ny=None): from shapely.geometry import Polygon, MultiPolygon if fov_y is None: fov_y = fov_x if ny is None: ny = nx if not isinstance(fov_x, (list, tuple, np.ndarray)): fov_x = [-fov_x / 2, fov_x / 2] fov_y = [-fov_y / 2, fov_y / 2] if isinstance(vertices, (Polygon, MultiPolygon)): mask = get_polygon_mask(vertices, fov_x, fov_y, nx, ny) else: mask = to_binary_mask(vertices, fov_x[0], fov_x[1], nx, fov_y[0], fov_y[1], ny) return mask
[docs] def interpolate_rois(rois: List["ROI"], z_positions): if len(rois) <= 1 or all([rois[0].z == r.z for r in rois]): return [] from itk import ( MorphologicalContourInterpolator, image_view_from_array, array_view_from_image, ) from .rois.roi_type import ROI buffer = 10 indices = [r.ax0_index[0] for r in rois] min_index = min(indices) max_index = max(indices) zs = z_positions[min_index : max_index + 1, 0] points = [r.points for r in rois] x_0 = np.min([np.min(p, axis=0) for p in points], axis=0) x_1 = np.max([np.max(p, axis=0) for p in points], axis=0) dx = np.min((x_1 - x_0) / 200) nx = ((x_1 - x_0) / dx).astype(np.int32) minx = x_0 - buffer * dx maxx = x_1 + (buffer - 1) * dx nx = nx + 20 mask = np.zeros((zs.shape[0], nx[1], nx[0]), np.uint16) for r, p in zip(rois, points): mask[r.ax0_index - min_index] = to_binary_mask( p, minx[0], maxx[0], nx[0], minx[1], maxx[1], nx[1] ) mask = mask im = image_view_from_array(mask) interp = MorphologicalContourInterpolator(im) np_view = array_view_from_image(interp) import cv2 as cv shape = [ np.squeeze(cv.findContours(i, 1, 2)[0][0].astype(float)) for i in np_view.astype(np.uint8) ] paths = [] for j, s in enumerate(shape): i = j + min_index if i not in indices: x, y = s.T x *= dx y *= -dx x += x_0[0] - 10.5 * dx y = -x_0[1] - y - nx[1] * dx + 10.5 * dx paths.append( ROI( np.array([x, -y]).T, zs[j], rois[0].run, rois[0].repetition, rois[0].roi_class + "~interpolated", rois[0].position, rois[0].generated, [i], ) ) return paths