Source code for supervisely.geometry.geometry

# coding: utf-8

from copy import deepcopy
from typing import List, Tuple

import numpy as np
from supervisely.io.json import JsonSerializable
from supervisely.geometry.constants import (
    ANY_SHAPE,
    LABELER_LOGIN,
    UPDATED_AT,
    CREATED_AT,
    ID,
    CLASS_ID,
)
from supervisely import logger

import warnings

warnings.simplefilter(action="ignore", category=FutureWarning)

if not hasattr(np, "bool"):
    np.bool = np.bool_


# @TODO: use properties instead of field if it makes sense
[docs]class Geometry(JsonSerializable): """ """ def __init__( self, sly_id=None, class_id=None, labeler_login=None, updated_at=None, created_at=None ): self.sly_id = sly_id self.labeler_login = labeler_login self.updated_at = updated_at self.created_at = created_at self.class_id = class_id def _add_creation_info(self, d): """ """ if self.labeler_login is not None: d[LABELER_LOGIN] = self.labeler_login if self.updated_at is not None: d[UPDATED_AT] = self.updated_at if self.created_at is not None: d[CREATED_AT] = self.created_at if self.sly_id is not None: d[ID] = self.sly_id if self.class_id is not None: d[CLASS_ID] = self.class_id def _copy_creation_info_inplace(self, g): """ """ self.labeler_login = g.labeler_login self.updated_at = g.updated_at self.created_at = g.created_at self.sly_id = g.sly_id
[docs] @staticmethod def geometry_name(): """ :return: string with name of geometry """ raise NotImplementedError()
[docs] @classmethod def name(cls): """ Same as geometry_name(), but shorter. In order to make the code more concise. :return: string with name of geometry """ return cls.geometry_name()
[docs] def crop(self, rect): """ :param rect: Rectangle :return: list of Geometry """ raise NotImplementedError()
[docs] def relative_crop(self, rect): """Crops object like "crop" method, but return results with coordinates relative to rect :param rect: :return: list of Geometry """ return [geom.translate(drow=-rect.top, dcol=-rect.left) for geom in self.crop(rect)]
[docs] def rotate(self, rotator): """Rotates around image center -> New Geometry :param rotator: ImageRotator :return: Geometry """ raise NotImplementedError()
[docs] def resize(self, in_size, out_size): """ :param in_size: (rows, cols) :param out_size: (128, 256) (128, KEEP_ASPECT_RATIO) (KEEP_ASPECT_RATIO, 256) :return: Geometry """ raise NotImplementedError()
[docs] def scale(self, factor): """Scales around origin with a given factor. :param: factor (float): :return: Geometry """ raise NotImplementedError()
[docs] def translate(self, drow, dcol): """ :param drow: int rows shift :param dcol: int cols shift :return: Geometry """ raise NotImplementedError()
[docs] def fliplr(self, img_size): """ :param img_size: (rows, cols) :return: Geometry """ raise NotImplementedError()
[docs] def flipud(self, img_size): """ :param img_size: (rows, cols) :return: Geometry """ raise NotImplementedError()
def _draw_bool_compatible(self, draw_fn, bitmap, color, thickness, config=None): """ """ if bitmap.dtype == np.bool: # Cannot draw on the canvas directly, create a temporary with different type. temp_bitmap = np.zeros(bitmap.shape[:2], dtype=np.uint8) draw_fn(temp_bitmap, 1, thickness=thickness, config=config) bitmap[temp_bitmap == 1] = color else: # Pass through the canvas without temp bitmap for efficiency. draw_fn(bitmap, color, thickness=thickness, config=config)
[docs] def draw(self, bitmap, color, thickness=1, config=None): """ :param bitmap: np.ndarray :param color: [R, G, B] :param thickness: used only in Polyline and Point :param config: drawing config specific to a concrete subclass, e.g. per edge colors """ self._draw_bool_compatible(self._draw_impl, bitmap, color, thickness, config)
[docs] def get_mask(self, img_size: Tuple[int, int]): """Returns 2D boolean mask of the geometry. With shape as img_size (height, width) and filled with True values inside the geometry and False values outside. dtype = np.bool shape = img_size :param img_size: size of the image (height, width) :type img_size: Tuple[int, int] :return: 2D boolean mask of the geometry :rtype: np.ndarray """ bitmap = np.zeros(img_size + (3,), dtype=np.uint8) self.draw(bitmap, color=[255, 255, 255], thickness=-1) return np.any(bitmap != 0, axis=-1)
def _draw_impl(self, bitmap, color, thickness=1, config=None): """ :param bitmap: np.ndarray :param color: [R, G, B] :param thickness: used only in Polyline and Point """ raise NotImplementedError()
[docs] def draw_contour(self, bitmap, color, thickness=1, config=None): """Draws the figure contour on a given bitmap canvas :param bitmap: np.ndarray :param color: [R, G, B] :param thickness: (int) :param config: drawing config specific to a concrete subclass, e.g. per edge colors """ self._draw_bool_compatible(self._draw_contour_impl, bitmap, color, thickness, config)
def _draw_contour_impl(self, bitmap, color, thickness=1, config=None): """Draws the figure contour on a given bitmap canvas :param bitmap: np.ndarray :param color: [R, G, B] :param thickness: (int) """ raise NotImplementedError() @property def area(self): """ :return: float """ raise NotImplementedError()
[docs] def to_bbox(self): """ :return: Rectangle """ raise NotImplementedError()
[docs] def clone(self): """Clone from GEOMETRYYY""" return deepcopy(self)
def validate(self, obj_class_shape, settings): """ """ if obj_class_shape != ANY_SHAPE: if self.geometry_name() != obj_class_shape: raise ValueError("Geometry validation error: shape names are mismatched!") @staticmethod def config_from_json(config): """ """ return config @staticmethod def config_to_json(config): """ """ return config @classmethod def allowed_transforms(cls): """ """ # raise NotImplementedError("{!r}".format(cls.geometry_name())) return [] def convert(self, new_geometry, contour_radius=0, approx_epsilon=None): """ """ from supervisely.geometry.any_geometry import AnyGeometry if type(self) == new_geometry or new_geometry == AnyGeometry: return [self] allowed_transforms = self.allowed_transforms() if new_geometry not in allowed_transforms: raise NotImplementedError( "from {!r} to {!r}".format(self.geometry_name(), new_geometry.geometry_name()) ) from supervisely.geometry.bitmap import Bitmap from supervisely.geometry.rectangle import Rectangle from supervisely.geometry.polygon import Polygon from supervisely.geometry.helpers import geometry_to_bitmap, geometry_to_polygon res = [] if new_geometry == Bitmap: res = geometry_to_bitmap(self, radius=contour_radius) elif new_geometry == Rectangle: res = [self.to_bbox()] elif new_geometry == Polygon: res = geometry_to_polygon(self, approx_epsilon=approx_epsilon) if len(res) == 0: logger.warn( "Can not convert geometry {} to {} because geometry to convert is very small".format( self.geometry_name(), new_geometry.geometry_name() ) ) return res