# coding: utf-8
# docs
from __future__ import annotations
from typing import Dict, List, Optional, Tuple
import cv2
from supervisely._utils import unwrap_if_numpy
from supervisely.geometry.constants import (
CLASS_ID,
CREATED_AT,
ID,
LABELER_LOGIN,
UPDATED_AT,
)
from supervisely.geometry.geometry import Geometry
from supervisely.geometry.image_rotator import ImageRotator
from supervisely.geometry.point_location import PointLocation
from supervisely.geometry.rectangle import Rectangle
[docs]
class Point(Geometry):
"""2D point at (row, col). Immutable."""
def __init__(
self,
row: int,
col: 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,
):
"""
Point geometry for a single :class:`~supervisely.annotation.label.Label`. :class:`~supervisely.geometry.point.Point` object is immutable.
:param row: Position of Point on height.
:type row: int or float
:param col: Position of Point on width.
:type col: int or float
:param sly_id: Point ID in Supervisely server.
:type sly_id: int, optional
:param class_id: ID of ObjClass to which Point belongs.
:type class_id: int, optional
:param labeler_login: Login of the user who created Point.
:type labeler_login: str, optional
:param updated_at: Date and Time when Point 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 Point was created. Date Format is the same as in "updated_at" parameter.
:type created_at: str, optional
:Usage Example:
.. code-block:: python
import supervisely as sly
row = 100
col = 200
figure = sly.Point(row, col)
"""
super().__init__(
sly_id=sly_id,
class_id=class_id,
labeler_login=labeler_login,
updated_at=updated_at,
created_at=created_at,
)
self._row = round(unwrap_if_numpy(row))
self._col = round(unwrap_if_numpy(col))
@property
def row(self) -> int:
"""
Position of Point height.
:returns: Height of Point.
:rtype: int
:Usage Example:
.. code-block:: python
print(figure.row)
# Output: 100
"""
return self._row
@property
def col(self) -> int:
"""
Position of Point width.
:returns: Width of Point.
:rtype: int
:Usage Example:
.. code-block:: python
print(figure.col)
# Output: 200
"""
return self._col
[docs]
@classmethod
def from_point_location(
cls,
pt: PointLocation,
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,
) -> Point:
"""
Create Point from given :class:`~supervisely.geometry.point_location.PointLocation` object.
:param pt: PointLocation to create Point from.
:type pt: :class:`~supervisely.geometry.point_location.PointLocation`
:param sly_id: Point ID in Supervisely server.
:type sly_id: int, optional
:param class_id: ID of ObjClass to which Point belongs.
:type class_id: int, optional
:param labeler_login: Login of the user who created :class:`~supervisely.geometry.point.Point`.
:type labeler_login: str, optional
:param updated_at: Date and Time when Point 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 Point was created. Date Format is the same as in "updated_at" parameter.
:type created_at: str, optional
:returns: :class:`~supervisely.geometry.point.Point` object
:rtype: :class:`~supervisely.geometry.point.Point`
:Usage Example:
.. code-block:: python
import supervisely as sly
figure_loc = sly.PointLocation(100, 200)
figure = sly.Point.from_point_location(figure_loc)
"""
return cls(
row=pt.row,
col=pt.col,
sly_id=sly_id,
class_id=class_id,
labeler_login=labeler_login,
updated_at=updated_at,
created_at=created_at,
)
@property
def point_location(self) -> PointLocation:
"""
Get PointLocation from Point.
:returns: PointLocation from Point.
:rtype: :class:`~supervisely.geometry.point_location.PointLocation`
:Usage Example:
.. code-block:: python
figure_loc = figure.point_location
"""
return PointLocation(row=self.row, col=self.col)
@staticmethod
def geometry_name():
""" """
return "point"
[docs]
def crop(self, rect: Rectangle) -> List[Point]:
"""
Crops current Point.
:param rect: Rectangle to crop Point from.
:type rect: :class:`~supervisely.geometry.rectangle.Rectangle`
:returns: List of Points from Rectangle.
:rtype: List[:class:`~supervisely.geometry.point.Point`]
:Usage Example:
.. code-block:: python
import supervisely as sly
crop_figures = figure.crop(sly.Rectangle(1, 1, 300, 350))
"""
return [self.clone()] if rect.contains_point_location(self.point_location) else []
[docs]
def rotate(self, rotator: ImageRotator) -> Point:
"""
Rotates current Point.
:param rotator: Class for object rotation.
:type rotator: :class:`~supervisely.geometry.image_rotator.ImageRotator`
:returns: Rotated Point.
:rtype: :class:`~supervisely.geometry.point.Point`
:Usage Example:
.. code-block:: python
import supervisely as sly
from supervisely.geometry.image_rotator import ImageRotator
# Remember that Point class object is immutable, and we need to assign new instance of Point to a new variable
height, width = 300, 400
rotator = ImageRotator((height, width), 25)
rotate_figure = figure.rotate(rotator)
"""
return self.from_point_location(self.point_location.rotate(rotator))
[docs]
def resize(self, in_size: Tuple[int, int], out_size: Tuple[int, int]) -> Point:
"""
Resizes current Point.
:param in_size: Input image size (height, width) to which belongs Point.
:type in_size: Tuple[int, int]
:param out_size: Desired output image size (height, width) to which belongs Point.
:type out_size: Tuple[int, int]
:returns: Resized Point.
:rtype: :class:`~supervisely.geometry.point.Point`
:Usage Example:
.. code-block:: python
# Remember that Point class object is immutable, and we need to assign new instance of Point to a new variable
in_height, in_width = 300, 400
out_height, out_width = 600, 800
resize_figure = figure.resize((in_height, in_width), (out_height, out_width))
"""
return self.from_point_location(self.point_location.resize(in_size, out_size))
[docs]
def fliplr(self, img_size: Tuple[int, int]) -> Point:
"""
Flips current Point in horizontal.
:param img_size: Input image size (height, width) to which belongs Point.
:type img_size: Tuple[int, int]
:returns: Flipped Point.
:rtype: :class:`~supervisely.geometry.point.Point`
:Usage Example:
.. code-block:: python
# Remember that Point class object is immutable, and we need to assign new instance of Point to a new variable
height, width = 300, 400
fliplr_figure = figure.fliplr((height, width))
"""
return self.from_point_location(self.point_location.fliplr(img_size))
[docs]
def flipud(self, img_size: Tuple[int, int]) -> Point:
"""
Flips current Point in vertical.
:param img_size: Input image size (height, width) to which belongs Point.
:type img_size: Tuple[int, int]
:returns: Flipped Point.
:rtype: :class:`~supervisely.geometry.point.Point`
:Usage Example:
.. code-block:: python
# Remember that Point class object is immutable, and we need to assign new instance of Point to a new variable
height, width = 300, 400
flipud_figure = figure.flipud((height, width))
"""
return self.from_point_location(self.point_location.flipud(img_size))
[docs]
def scale(self, factor: float) -> Point:
"""
Scales current Point.
:param factor: Scale parameter.
:type factor: float
:returns: Scaled Point.
:rtype: :class:`~supervisely.geometry.point.Point`
:Usage Example:
.. code-block:: python
# Remember that Point class object is immutable, and we need to assign new instance of Point to a new variable
scale_figure = figure.scale(0.75)
"""
return self.from_point_location(self.point_location.scale(factor))
[docs]
def translate(self, drow: int, dcol: int) -> Point:
"""
Translates current Point.
:param drow: Horizontal shift.
:type drow: int
:param dcol: Vertical shift.
:type dcol: int
:returns: Translated Point.
:rtype: :class:`~supervisely.geometry.point.Point`
:Usage Example:
.. code-block:: python
# Remember that Point class object is immutable, and we need to assign new instance of Point to a new variable
translate_figure = figure.translate(150, 350)
"""
return self.from_point_location(self.point_location.translate(drow, dcol))
def _draw_impl(self, bitmap, color, thickness=1, config=None):
r = round(thickness / 2) # @TODO: relation between thickness and point radius - ???
cv2.circle(bitmap, (self.col, self.row), radius=r, color=color, thickness=cv2.FILLED)
def _draw_contour_impl(self, bitmap, color, thickness=1, config=None):
# @TODO: mb dummy operation for Point
r = round((thickness + 1) / 2)
cv2.circle(bitmap, (self.col, self.row), radius=r, color=color, thickness=cv2.FILLED)
@property
def area(self) -> float:
"""
Point area.
:returns: Area of current Point, always 0.0
:rtype: float
:Usage Example:
.. code-block:: python
print(figure.area)
# Output: 0.0
"""
return 0.0
[docs]
def to_bbox(self) -> Rectangle:
"""
Create Rectangle object from current Point.
:returns: Rectangle from Point.
:rtype: :class:`~supervisely.geometry.rectangle.Rectangle`
:Usage Example:
.. code-block:: python
rectangle = figure.to_bbox()
"""
return Rectangle(top=self.row, left=self.col, bottom=self.row, right=self.col)
[docs]
def to_json(self) -> Dict:
"""
Convert the Point to a json dict. Read more about `Supervisely format <https://docs.supervisely.com/data-organization/00_ann_format_navi>`_.
:returns: Point in json format as a dict.
:rtype: dict
:Usage Example:
.. code-block:: python
figure_json = figure.to_json()
print(figure_json)
# Output: {
# "points": {
# "exterior": [
# [200, 100]
# ],
# "interior": []
# }
# }
"""
res = self.point_location.to_json()
self._add_creation_info(res)
return res
[docs]
@classmethod
def from_json(cls, data: Dict) -> Point:
"""
Convert a json dict to Point. Read more about `Supervisely format <https://docs.supervisely.com/data-organization/00_ann_format_navi>`_.
:param data: Point in json format as a dict.
:type data: dict
:returns: Point from json.
:rtype: :class:`~supervisely.geometry.point.Point`
:Usage Example:
.. code-block:: python
import supervisely as sly
figure_json = {
"points": {
"exterior": [[200, 100]],
"interior": []
}
}
figure = sly.Point.from_json(figure_json)
"""
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.from_point_location(
PointLocation.from_json(data),
sly_id=sly_id,
class_id=class_id,
labeler_login=labeler_login,
updated_at=updated_at,
created_at=created_at,
)