Source code for supervisely.geometry.polyline

# coding: utf-8


# docs

from __future__ import annotations

from typing import Dict, List, Optional, Tuple, Union

import cv2
import numpy as np
from shapely.geometry import LineString
from shapely.geometry import Polygon as ShapelyPolygon
from shapely.geometry import mapping

from supervisely.sly_logger import logger
from supervisely.geometry import validation
from supervisely.geometry.constants import (
    CLASS_ID,
    CREATED_AT,
    EXTERIOR,
    ID,
    LABELER_LOGIN,
    POINTS,
    UPDATED_AT,
)
from supervisely.geometry.conversions import shapely_figure_to_coords_list
from supervisely.geometry.point import PointLocation
from supervisely.geometry.point_location import row_col_list_to_points
from supervisely.geometry.rectangle import Rectangle
from supervisely.geometry.vector_geometry import VectorGeometry


[docs] class Polyline(VectorGeometry): """Open 2D polyline (sequence of connected line segments). Immutable."""
[docs] @staticmethod def geometry_name(): """ Returns the name of the geometry. :returns: name of the geometry :rtype: str """ return "line"
def __init__( self, exterior: Union[List[PointLocation], List[List[int, int]], List[Tuple[int, int]]], sly_id: Optional[int] = None, class_id: Optional[int] = None, labeler_login: Optional[int] = None, updated_at: Optional[str] = None, created_at: Optional[str] = None, ): """ Polyline geometry for a single :class:`~supervisely.annotation.label.Label`. :class:`~supervisely.geometry.polyline.Polyline` object is immutable. :param exterior: List of exterior coordinates, the Polyline is defined with these points. :type exterior: List[:class:`~supervisely.geometry.point_location.PointLocation`], List[List[int, int]], List[Tuple[int, int] :param sly_id: Polyline ID in Supervisely server. :type sly_id: int, optional :param class_id: ID of ObjClass to which Polyline belongs. :type class_id: int, optional :param labeler_login: Login of the user who created Polyline. :type labeler_login: str, optional :param updated_at: Date and Time when Polyline was modified last. Date Format: Year:Month:Day:Hour:Minute:Seconds. Example: '2021-01-22T19:37:50.158Z'. :type updated_at: str, optional :param created_at: Date and Time when Polyline was created. Date Format is the same as in "updated_at" parameter. :type created_at: str, optional :raises ValueError: field exterior must contain at least two points to create Polyline. :Usage Example: .. code-block:: python import supervisely as sly exterior = [sly.PointLocation(730, 2104), sly.PointLocation(2479, 402), sly.PointLocation(1500, 780)] # or exterior = [[730, 2104], [2479, 402], [1500, 780]] # or exterior = [(730, 2104), (2479, 402), (1500, 780)] figure = sly.Polyline(exterior) """ if len(exterior) < 2: raise ValueError( f'"{EXTERIOR}" field must contain at least two points to create "Polyline" object.' ) super().__init__( exterior=exterior, interior=[], sly_id=sly_id, class_id=class_id, labeler_login=labeler_login, updated_at=updated_at, created_at=created_at, )
[docs] @classmethod def from_json(cls, data: Dict) -> Polyline: """ Convert a json dict to Polyline. Read more about `Supervisely format <https://docs.supervisely.com/data-organization/00_ann_format_navi>`_. :param data: Polyline in json format as a dict. :type data: dict :returns: Polyline from json. :rtype: :class:`~supervisely.geometry.polyline.Polyline` :Usage Example: .. code-block:: python import supervisely as sly figure_json = { "points": { "exterior": [ [2104, 730], [402, 2479], [780, 1500] ], "interior": [] } } figure = sly.Polyline.from_json(figure_json) """ validation.validate_geometry_points_fields(data) labeler_login = data.get(LABELER_LOGIN, None) updated_at = data.get(UPDATED_AT, None) created_at = data.get(CREATED_AT, None) sly_id = data.get(ID, None) class_id = data.get(CLASS_ID, None) return cls( exterior=row_col_list_to_points(data[POINTS][EXTERIOR], flip_row_col_order=True), sly_id=sly_id, class_id=class_id, labeler_login=labeler_login, updated_at=updated_at, created_at=created_at, )
[docs] def crop(self, rect: Rectangle) -> List[Polyline]: """ Crops current Polyline. :param rect: Rectangle to crop Polyline from. :type rect: :class:`~supervisely.geometry.rectangle.Rectangle` :returns: List of Polygons from Rectangle. :rtype: List[:class:`~supervisely.geometry.polyline.Polyline`] :Usage Example: .. code-block:: python import supervisely as sly crop_figures = figure.crop(sly.Rectangle(0, 0, 100, 200)) """ try: clipping_window = [ [rect.top, rect.left], [rect.top, rect.right], [rect.bottom, rect.right], [rect.bottom, rect.left], ] clipping_window_shpl = ShapelyPolygon(clipping_window) exterior = self.exterior_np intersections_polygon = LineString(exterior).intersection(clipping_window_shpl) mapping_shpl = mapping(intersections_polygon) except Exception: logger.warning("Line cropping exception, shapely.", exc_info=False) raise res_lines_pts = shapely_figure_to_coords_list(mapping_shpl) # tiny hack to combine consecutive segments lines_combined = [] if res_lines_pts != [()]: for simple_l in res_lines_pts: if len(lines_combined) > 0: prev = lines_combined[-1] if prev[-1] == simple_l[0]: lines_combined[-1] = list(prev) + list(simple_l[1:]) continue lines_combined.append(simple_l) return [Polyline(row_col_list_to_points(line)) for line in lines_combined]
def _draw_impl(self, bitmap: np.ndarray, color, thickness=1, config=None): """ """ self._draw_contour_impl(bitmap, color, thickness, config=config) def _draw_contour_impl(self, bitmap: np.ndarray, color, thickness=1, config=None): """ """ exterior = self.exterior_np[:, ::-1] cv2.polylines(bitmap, pts=[exterior], isClosed=False, color=color, thickness=thickness) @property def area(self) -> float: """ Polyline area, always 0.0. :returns: Area of current Polyline, always 0.0 :rtype: float :Usage Example: .. code-block:: python print(figure.area) # Output: 0.0 """ return 0.0
[docs] def approx_dp(self, epsilon: float) -> Polyline: """ Approximates a Polyline with the specified precision. :param epsilon: Specifying the approximation accuracy. :type epsilon: float :returns: Approximated Polyline. :rtype: :class:`~supervisely.geometry.polyline.Polyline` :Usage Example: .. code-block:: python # Remember that Polyline class object is immutable, and we need to assign new instance of Polyline to a new variable approx_figure = figure.approx_dp(0.75) """ exterior_np = self._approx_ring_dp(self.exterior_np, epsilon, closed=True).tolist() exterior = row_col_list_to_points(exterior_np, do_round=True) return Polyline(exterior)
[docs] @classmethod def allowed_transforms(cls): """ Returns the allowed transforms for the Polyline. """ from supervisely.geometry.alpha_mask import AlphaMask from supervisely.geometry.any_geometry import AnyGeometry from supervisely.geometry.bitmap import Bitmap from supervisely.geometry.polygon import Polygon from supervisely.geometry.rectangle import Rectangle return [AnyGeometry, Rectangle, Bitmap, Polygon, AlphaMask]