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

Moved patch mask-specific processing method to base

parent 70a9e13e
No related branches found
No related tags found
No related merge requests found
......@@ -4,6 +4,7 @@ Image processing utility functions
from math import ceil, floor
import numpy as np
import skimage
from skimage.exposure import rescale_intensity
......@@ -85,3 +86,43 @@ def make_rgb(nda):
outdata = np.zeros((h, w, 3, nz), dtype=nda.dtype)
outdata[:, :, 0:c, :] = nda[:, :, :, :]
return outdata
def mask_largest_object(
img: np.ndarray,
max_allowed: int = 10,
verbose: bool = True
) -> np.ndarray:
"""
Where more than one connected component is found in an image, return the largest object by area
:param img: (np.ndarray) containing object labels or binary mask
:param max_allowed: raise an error if more than this number of objects is found
:param verbose: print a message each time more than one object is found
:return: np.ndarray of same size as img
"""
if is_mask(img): # assign object labels
ob_id = skimage.measure.label(img)
else: # assume img is contains object labels
ob_id = img
num_obj = len(np.unique(ob_id)) - 1
if num_obj > max_allowed:
raise TooManyObjectError(f'Found {num_obj} objects in frame')
if num_obj > 1:
if verbose:
print(f'Found {num_obj} nonzero unique values in object map; keeping the one with the largest area')
val, cts = np.unique(ob_id, return_counts=True)
mask = ob_id == val[1 + cts[1:].argmax()]
return mask * img
else:
return img
class Error(Exception):
pass
class TooManyObjectError(Exception):
pass
......@@ -18,7 +18,8 @@ from model_server.base.models import InstanceSegmentationModel
from model_server.base.process import pad, rescale, resample_to_8bit, make_rgb
from model_server.extensions.chaeo.annotators import draw_box_on_patch, draw_contours_on_patch, draw_boxes_on_3d_image
from model_server.extensions.chaeo.accessors import write_patch_to_file, MonoPatchStack, Multichannel3dPatchStack
from model_server.extensions.chaeo.process import mask_largest_object
from base.process import mask_largest_object
class PatchParams(BaseModel):
draw_bounding_box: bool = False
......
import numpy as np
import skimage
from model_server.base.process import is_mask
def mask_largest_object(
img: np.ndarray,
max_allowed: int = 10,
verbose: bool = True
) -> np.ndarray:
"""
Where more than one connected component is found in an image, return the largest object by area
:param img: (np.ndarray) containing object labels or binary mask
:param max_allowed: raise an error if more than this number of objects is found
:param verbose: print a message each time more than one object is found
:return: np.ndarray of same size as img
"""
if is_mask(img): # assign object labels
ob_id = skimage.measure.label(img)
else: # assume img is contains object labels
ob_id = img
# import skimage
# from pathlib import Path
# where = Path('c:/Users/rhodes/projects/proj0011-plankton-seg/tmp')
# skimage.io.imsave(where / 'raw.png', img)
num_obj = len(np.unique(ob_id)) - 1
if num_obj > max_allowed:
raise TooManyObjectError(f'Found {num_obj} objects in frame')
if num_obj > 1:
if verbose:
print(f'Found {num_obj} nonzero unique values in object map; keeping the one with the largest area')
# pr = regionprops_table(ob_id, properties=['label', 'area'])
val, cts = np.unique(ob_id, return_counts=True)
mask = ob_id == val[1 + cts[1:].argmax()]
# idx_max_area = pr['area'].argmax()
# mask = ob_id == pr['label'][idx_max_area]
return mask * img
else:
return img
class Error(Exception):
pass
class TooManyObjectError(Exception):
pass
import unittest
import numpy as np
from model_server.extensions.chaeo.process import mask_largest_object
class TestMaskLargestObject(unittest.TestCase):
def test_mask_largest_touching_object(self):
arr = np.zeros([5, 5], dtype='uint8')
arr[0:3, 0:3] = 2
arr[3:, 2:] = 4
masked = mask_largest_object(arr)
self.assertTrue(np.all(np.unique(masked) == [0, 2]))
self.assertTrue(np.all(masked[4:5, 0:2] == 0))
self.assertTrue(np.all(masked[0:3, 3:5] == 0))
def test_no_change(self):
arr = np.zeros([5, 5], dtype='uint8')
arr[0:3, 0:3] = 2
masked = mask_largest_object(arr)
self.assertTrue(np.all(masked == arr))
def test_mask_multiple_objects_in_binary_maks(self):
arr = np.zeros([5, 5], dtype='uint8')
arr[0:3, 0:3] = 255
arr[4, 2:5] = 255
masked = mask_largest_object(arr)
print(np.unique(masked))
self.assertTrue(np.all(np.unique(masked) == [0, 255]))
self.assertTrue(np.all(masked[:, 3:5] == 0))
self.assertTrue(np.all(masked[3:5, :] == 0))
......@@ -9,7 +9,7 @@ from skimage.morphology import dilation
from sklearn.model_selection import train_test_split
from base.roiset import RoiSetMetaParams, RoiSetExportParams
from model_server.extensions.chaeo.process import mask_largest_object
from base.process import mask_largest_object
from base.roiset import _get_label_ids, RoiSet
from model_server.base.accessors import generate_file_accessor, InMemoryDataAccessor, write_accessor_data_to_file
......
......@@ -2,6 +2,7 @@ import unittest
import numpy as np
from base.process import mask_largest_object
from model_server.base.process import pad
class TestProcessingUtilityMethods(unittest.TestCase):
......@@ -27,4 +28,31 @@ class TestProcessingUtilityMethods(unittest.TestCase):
nc = self.data4d.shape[2]
nz = self.data4d.shape[3]
padded = pad(self.data4d, 256)
self.assertEqual(padded.shape, (256, 256, nc, nz))
\ No newline at end of file
self.assertEqual(padded.shape, (256, 256, nc, nz))
class TestMaskLargestObject(unittest.TestCase):
def test_mask_largest_touching_object(self):
arr = np.zeros([5, 5], dtype='uint8')
arr[0:3, 0:3] = 2
arr[3:, 2:] = 4
masked = mask_largest_object(arr)
self.assertTrue(np.all(np.unique(masked) == [0, 2]))
self.assertTrue(np.all(masked[4:5, 0:2] == 0))
self.assertTrue(np.all(masked[0:3, 3:5] == 0))
def test_no_change(self):
arr = np.zeros([5, 5], dtype='uint8')
arr[0:3, 0:3] = 2
masked = mask_largest_object(arr)
self.assertTrue(np.all(masked == arr))
def test_mask_multiple_objects_in_binary_maks(self):
arr = np.zeros([5, 5], dtype='uint8')
arr[0:3, 0:3] = 255
arr[4, 2:5] = 255
masked = mask_largest_object(arr)
print(np.unique(masked))
self.assertTrue(np.all(np.unique(masked) == [0, 255]))
self.assertTrue(np.all(masked[:, 3:5] == 0))
self.assertTrue(np.all(masked[3:5, :] == 0))
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