Skip to content
Snippets Groups Projects
Commit 531fac85 authored by Christopher Randolph Rhodes's avatar Christopher Randolph Rhodes
Browse files

Implemented polygon fitting

parent 36de097e
No related branches found
No related tags found
No related merge requests found
......@@ -10,7 +10,8 @@ from pydantic import BaseModel
from scipy.stats import moment
from skimage.filters import sobel
from skimage.measure import label, regionprops_table, shannon_entropy
from skimage.measure import approximate_polygon, find_contours, label, points_in_poly, regionprops, regionprops_table, shannon_entropy
from skimage.morphology import binary_dilation, disk
from .accessors import GenericImageDataAccessor, InMemoryDataAccessor, write_accessor_data_to_file
from .models import InstanceSegmentationModel
......@@ -180,6 +181,10 @@ def make_df_from_object_ids(acc_raw, acc_obj_ids, expand_box_by) -> pd.DataFrame
)
return df
# TODO: implement
def make_df_from_polygons(acc_raw, polygons:np.ndarray) -> pd.DataFrame:
pass
def df_insert_slices(df: pd.DataFrame, sd: dict, expand_box_by) -> pd.DataFrame:
h = sd['Y']
......@@ -347,6 +352,11 @@ class RoiSet(object):
"""
return RoiSet.from_object_ids(acc_raw, get_label_ids(acc_seg, allow_3d=allow_3d, connect_3d=connect_3d), params)
@staticmethod
#TODO: implement
def from_polygons(acc_raw, polygons: np.ndarray):
pass
# TODO: get overlapping segments
def get_overlap_seg(self) -> pd.DataFrame:
......@@ -764,6 +774,33 @@ class RoiSet(object):
record['tight_patch_masks'] = list(se_pa)
return record
def get_polygons(self, poly_threshold=0, dilation_radius=1):
pad_to = 1
def _poly_from_mask(roi):
# mask = generate_file_accessor(roi.mask_path).data[:, :, 0, 0]
mask = roi.binary_mask
# label and fill holes
labeled = label(mask)
filled = [rp.image_filled for rp in regionprops(labeled)]
assert (np.unique(labeled)[-1] == 1) and (len(filled) == 1), 'Cannot fit multiple polygons in a single patch mask'
closed = binary_dilation(filled[0], footprint=disk(dilation_radius))
padded = np.pad(closed, pad_to) * 1.0
all_contours = find_contours(padded)
nc = len(all_contours)
for j in range(0, nc):
if all([points_in_poly(all_contours[k], all_contours[j]).all() for k in range(0, nc)]):
contour = all_contours[j]
break
rel_polygon = approximate_polygon(contour[:, [1, 0]], poly_threshold) - [pad_to, pad_to]
return rel_polygon + [roi.x0, roi.y0]
return self._df.apply(_poly_from_mask, axis=1)
# TODO: implement
def serialize_coco(self, where: Path, prefix='') -> dict:
"""
......
import os
import re
import unittest
......@@ -6,6 +5,7 @@ import numpy as np
from pathlib import Path
import pandas as pd
from skimage import draw
from model_server.base.roiset import filter_overlap_bbox, RoiSetExportParams, RoiSetMetaParams
from model_server.base.roiset import RoiSet
......@@ -664,3 +664,27 @@ class TestRoiSetSerialization(unittest.TestCase):
)
roiset.serialize_coco(output_path / 'serialize_coco')
self.assertEqual(1, 0)
class TestRoiSetPolygons(BaseTestRoiSetMonoProducts, unittest.TestCase):
def test_compute_polygons(self):
roiset = RoiSet.from_binary_mask(
self.stack_ch_pa,
self.seg_mask,
params=RoiSetMetaParams(
mask_type='contours',
filters={'area': {'min': 1e1, 'max': 1e6}}
)
)
poly = roiset.get_polygons()
binary_poly = np.zeros(self.seg_mask.hw, dtype=bool)
for p in poly:
pidcs = draw.polygon(p[:, 1], p[:, 0])
binary_poly[pidcs] = True
test_mask = np.logical_and(
np.logical_not(binary_poly),
(self.seg_mask.data[:, :, 0, 0] == 255)
)
self.assertLess(test_mask.sum() / test_mask.size, 0.001) # most mask pixels are within in fitted polygon
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment