Source code for patato.utils.mask_operations

#  Copyright (c) Thomas Else 2023.
#  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 type(vertices) == np.ndarray or type(vertices) == 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 type(vert_path) in [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 type(fov_x) not in [list, tuple, np.ndarray]: fov_x = [-fov_x/2, fov_x/2] fov_y = [-fov_y/2, fov_y/2] if type(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 type(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 type(fov_x) not in [list, tuple, np.ndarray]: fov_x = [-fov_x/2, fov_x/2] fov_y = [-fov_y/2, fov_y/2] if type(vertices) in [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