import numpy as np
import cv2
from typing import Tuple
[docs]def ckernel(size: int) -> np.ndarray:
"""Circular filter kernel
"""
if not size % 2: size = size - 1
index = int(size / 2)
y, x = np.ogrid[-index:size - index, -index:size - index]
r = int(size / 2)
mask = x * x + y * y <= r * r
array = np.zeros([size, size], dtype=np.uint8)
array[mask] = 255
return array
[docs]def crop_mask(mask: np.ndarray) -> Tuple[np.ndarray, np.ndarray, Tuple[int, int]]:
"""Crop a binary mask image to its minimal size
(to exclude unnecessary regions)
"""
nz = np.nonzero(mask)
row_0 = nz[0].min()
row_1 = nz[0].max()+1
col_0 = nz[1].min()
col_1 = nz[1].max()+1
cropped_mask = mask[row_0:row_1, col_0:col_1].copy()
return cropped_mask, \
np.array([row_0, row_1, col_0, col_1]), \
(int((row_0+row_1-1)/2), int((col_0+col_1-1)/2))
[docs]def mask(image: np.ndarray, mask: np.ndarray, rect: np.ndarray):
cropped_image = image[rect[0]:rect[1], rect[2]:rect[3]].copy()
return cv2.bitwise_and(cropped_image, mask)
[docs]def area_pixelsum(image):
"""Calculate area in px^2 by summing pixels
:param image: Binary input image (numpy array).
Should already be masked and filtered.
:return: Area as # of pixels
"""
if image is not None:
return np.sum(image > 1)
[docs]def to_mask(image: np.ndarray, kernel: np.ndarray) -> np.ndarray:
"""Convert a .png image to a binary mask
"""
# Convert to grayscale
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Threshold to binary
ret, image = cv2.threshold(image, 254, 255, cv2.THRESH_BINARY)
# Expand binary region to deal with
# 'under-thresholding' due to high setting (254/255)
image = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel)
# The binary threshold does not always map the same binary value
# to the center of the mask (should be the darker tone)
# To circumvent this, we assume that the outer edge is
# not included in the mask (should be ok in normal cases)
# We want to end up with the mask as 255, the background as 0
# Apparently that's not it, do the arithmetic
# in float & convert to uint8 afterwards!
if image[0, 0] == 255: # todo: we're hardcoding pixel 0,0 as background here, this is not ideal!
# Do the arithmetic in float & convert to uint8 afterwards!
return np.array(
np.abs(
np.subtract(
255, np.array(
image, dtype=np.float
)
)
), dtype=np.uint8
)
else:
return image