Source code for arthropod_describer.plugins.profile_register.properties.contour

import copy
import typing
from pathlib import Path
from typing import Optional

import cv2
import numpy as np
import skimage
from skimage import io

from arthropod_describer.common.common import Info
from arthropod_describer.common.label_image import RegionProperty, PropertyType
from arthropod_describer.common.photo import Photo
from arthropod_describer.common.plugin import PropertyComputation
from arthropod_describer.common.regions_cache import RegionsCache
from arthropod_describer.common.user_params import UserParam


[docs]class Contour(PropertyComputation): """ GROUP: Basic properties NAME: Contour DESCRIPTION: Compute contour feature vector KEY: contour """ sample_count = 40 def __init__(self, info: Optional[Info] = None): super().__init__(info) def __call__(self, photo: Photo, region_labels: typing.List[int], regions_cache: RegionsCache) -> \ typing.List[RegionProperty]: lab_img = photo['Labels'].label_image # lab_hier = photo['Labels'].label_hierarchy props: typing.List[RegionProperty] = [] # path = Path(f'C:/Users/radoslav/Desktop/contour_debug/{photo.image_name}/') # if not path.exists(): # path.mkdir() for label in region_labels: # lab_path = path / lab_hier.nodes[label].name # if not lab_path.exists(): # lab_path.mkdir() # region = lab_img == label if label not in regions_cache.regions: continue region_obj = regions_cache.regions[label] region = region_obj.mask # roi_rgb = cv2.cvtColor(255 * region.astype(np.uint8), cv2.COLOR_GRAY2BGR) # cv2.imwrite(str(lab_path / 'region.png'), roi_rgb) yy, xx = np.nonzero(region) x_c, y_c = round(np.mean(xx)), round(np.mean(yy)) # centroid for nonzero pixels in `roi` # roi_cent = cv2.circle(roi_rgb, (x_c, y_c), 3, [0, 255, 0]) # cv2.imwrite(str(lab_path / 'roi_centroid.png'), roi_cent) reg_orient = skimage.measure.regionprops_table(1 * region, properties=('orientation',)) # get the angle between y-axis and the major axis of the region in `roi` angle = np.rad2deg(reg_orient['orientation'][0]) # rotate `roi` so that the major axis coincides with y-axis rotated = skimage.transform.rotate(region, angle=-angle, center=(x_c, y_c), resize=True) # io.imsave(str(lab_path / 'roi_rotated.png'), rotated) outline = np.logical_xor(rotated, cv2.erode(255 * rotated.astype(np.uint8), np.ones((3, 3)), borderValue=0, borderType=cv2.BORDER_CONSTANT) > 0) # io.imsave(str(lab_path / 'outline.png'), outline) outline_yy, outline_xx = np.nonzero(outline) xs_by_y: typing.Dict[int, typing.List[int]] = {} for y, x in zip(outline_yy, outline_xx): xs_by_y.setdefault(y, []).append(x) outline_yy = np.unique(outline_yy) # step = outline_yy.shape[0] / float(self.sample_count) step = (np.max(outline_yy) - np.min(outline_yy)) / float(self.sample_count - 1) feat_vector: typing.List[float] = [] y_start = outline_yy[0] y_curr = y_start offset = 0 # viz = cv2.cvtColor(255 * outline.copy().astype(np.uint8), cv2.COLOR_GRAY2BGR) for i in range(self.sample_count): y_curr = min(int(round(y_start + offset)), max(outline_yy)) offset += step if y_curr not in xs_by_y: left = 0 right = -1 else: left = min(xs_by_y[y_curr]) right = max(xs_by_y[y_curr]) # viz[y_curr, left] = [255, 0, 0] # viz[y_curr, right] = [0, 255, 0] width = 0.5 * (right - left + 1) if photo.image_scale is not None: width /= photo.image_scale.value feat_vector.append(width) # cv2.imwrite(str(lab_path / 'viz.png'), viz) prop = self.example('contour') if photo.image_scale is not None: prop.value = (feat_vector, self._px_unit / photo.image_scale.unit) else: prop.value = (feat_vector, self._px_unit) prop.label = label props.append(prop) return props @property def user_params(self) -> typing.List[UserParam]: return super().user_params @property def region_restricted(self) -> bool: return super().region_restricted @property def computes(self) -> typing.Dict[str, Info]: return {self.info.key: self.info}
[docs] def example(self, prop_name: str) -> RegionProperty: prop = RegionProperty() prop.label = 0 prop.info = copy.deepcopy(self.info) prop.value = None prop.num_vals = self.sample_count prop.prop_type = PropertyType.Vector prop.val_names = [] return prop
[docs] def target_worksheet(self, prop_name: str) -> str: return "Contours"
@property def group(self) -> str: return super().group