Source code for lunavl.sdk.image_utils.geometry

"""
Module contains geometric structures (Rect, Point, Size)
"""
from typing import TypeVar, Generic, Union, List

from FaceEngine import Vector2i, Vector2f  # pylint: disable=E0611,E0401
from FaceEngine import Rect as CoreRectI, RectFloat as CoreRectF  # pylint: disable=E0611,E0401

from FaceEngine import Landmarks5, Landmarks68, IrisLandmarks, EyelidLandmarks  # pylint: disable=E0611,E0401

from lunavl.sdk.estimators.base_estimation import BaseEstimation

COORDINATE_TYPE = TypeVar('COORDINATE_TYPE', float, int)  #: generic type for allowed values type of coordinates
LANDMARKS = TypeVar('LANDMARKS', Landmarks5, Landmarks68, IrisLandmarks,
                    EyelidLandmarks)  #: generic type for allowed values type of landmarks


[docs]class Size(Generic[COORDINATE_TYPE]): """ Rect size. Attributes: width (CoordinateType): width height (CoordinateType): height """ __slots__ = ["width", "height"] def __init__(self, width: COORDINATE_TYPE, height: COORDINATE_TYPE): """ Init Args: width: width height: height """ self.width = width self.height = height def __repr__(self) -> str: """ Size representation. Returns: "width = {self.width}, height = {self.height}" >>> str(Size(1, 2)) 'width = 1, height = 2' """ return "width = {}, height = {}".format(self.width, self.height)
[docs] def asDict(self) -> dict: """ Convert to dict Returns: {"width": self.width, "height": self.height} >>> Size(1, 2).asDict() {'width': 1, 'height': 2} >>> Size(1.0, 2.0).asDict() {'width': 1.0, 'height': 2.0} """ return {"width": self.width, "height": self.height}
[docs]class Point(Generic[COORDINATE_TYPE]): """ Point. Attributes: x (CoordinateType): x-coordinate y (CoordinateType): y-coordinate """ __slots__ = ["x", "y"] def __init__(self, x: COORDINATE_TYPE, y: COORDINATE_TYPE): # pylint: disable=C0103 """ Init Args: x: x y: y """ self.x = x # pylint: disable=C0103 self.y = y # pylint: disable=C0103
[docs] @classmethod def fromVector2(cls, vec2: Union[Vector2f, Vector2i]) -> 'Point': """ Create Point from Core Vector2i and Vector2f Args: vec2: vector2i or vector2f Returns: point """ point = Point(0, 0) point.x = vec2.x point.y = vec2.y return point
[docs] def toVector2(self) -> Union[Vector2i, Vector2f]: """ Create Vector2i or Vector2f from point Returns: Vector2i if x and y are integer otherwise Vector2f >>> vec2 = Point(1, 2).toVector2() >>> isinstance(vec2, Vector2i) True >>> vec2 = Point(1.0, 2.0).toVector2() >>> isinstance(vec2, Vector2f) True """ if isinstance(self.x, int) and isinstance(self.y, int): return Vector2i(self.x, self.y) return Vector2f(self.x, self.y)
[docs] def asDict(self) -> List[COORDINATE_TYPE]: """ Convert point to list Returns: [self.x, self.y] >>> Point(1, 2).asDict() [1, 2] >>> Point(1.0, 2.0).asDict() [1.0, 2.0] """ return [self.x, self.y]
def __repr__(self): return "x = {}, y = {}".format(self.x, self.y)
[docs]class Rect(Generic[COORDINATE_TYPE]): """ Rect Attributes: coreRect (CoreRect): core rect object """ def __init__(self, x: COORDINATE_TYPE = 0, y: COORDINATE_TYPE = 0, # pylint: disable=C0103 width: COORDINATE_TYPE = 0, height: COORDINATE_TYPE = 0): """ Init. If there are argument of type float coreRect will be type CoreRectF otherwise CoreRectI. Args: x: x y: y width: width height: height """ if any((isinstance(x, float), isinstance(y, float), isinstance(width, float), isinstance(height, float))): self.coreRect = CoreRectF(x, y, width, height) else: self.coreRect = CoreRectI(x, y, width, height)
[docs] @classmethod def fromCoreRect(cls, rect: Union[CoreRectF, CoreRectI]) -> 'Rect': """ Load rect from core rect Args: rect: core rect Returns: new rect """ newRect = cls() newRect.coreRect = rect return newRect
[docs] @classmethod def initByCorners(cls, topLeftCorner: Point[COORDINATE_TYPE], bottomRightBottom: Point[COORDINATE_TYPE]) -> 'Rect': """ Init rect by top left corner, bottom right bottom Args: topLeftCorner: top left corner bottomRightBottom: bottom right bottom Returns: new rect """ newRect = cls() newRect.coreRect = newRect.coreRect.set(topLeftCorner.toVector2(), bottomRightBottom.toVector2()) return newRect
@property def x(self) -> COORDINATE_TYPE: # pylint: disable=C0103 """ Getter of x coordinate Returns: self._rect.x """ return self.coreRect.x @x.setter def x(self, value: COORDINATE_TYPE): # pylint: disable=C0103 """ Setter of x Args: value: new value """ self.coreRect.x = value @property def y(self) -> COORDINATE_TYPE: # pylint: disable=C0103 """ Getter of y coordinate Returns: self._rect.y """ return self.coreRect.y @y.setter def y(self, value: COORDINATE_TYPE): # pylint: disable=C0103 """ Setter of y Args: value: new value """ self.coreRect.y = value @property def width(self) -> COORDINATE_TYPE: """ Getter of width Returns: self._rect.width """ return self.coreRect.width @width.setter def width(self, value: COORDINATE_TYPE): """ Setter of width Args: value: new value """ self.coreRect.width = value @property def height(self) -> COORDINATE_TYPE: """ Getter of height Returns: self._rect.height """ return self.coreRect.height @height.setter def height(self, value: COORDINATE_TYPE): """ Setter of height Args: value: new value """ self.coreRect.height = value @property def bottom(self) -> COORDINATE_TYPE: # real signature unknown; restored from __doc__ """ Get lower y-coordinate of the rect Returns: self.y + self.width >>> Rect(1, 2, 3, 4).bottom 6 """ vector = self.coreRect.bottom() return vector @property def bottomRight(self) -> Point[COORDINATE_TYPE]: """ Get coordinates of the right bottom angle Returns: point >>> Rect(1, 2, 3, 4).bottomRight x = 4, y = 6 """ vector = self.coreRect.bottomRight() return Point.fromVector2(vector) @property def top(self) -> COORDINATE_TYPE: """ Get upper y-coordinate of the rect Returns: self.y >>> Rect(1, 2, 3, 4).top 2 >>> Rect(1, 2, 3, -4).top 2 """ vector = self.coreRect.top() return vector @property def topLeft(self) -> Point[COORDINATE_TYPE]: # real signature unknown; restored from __doc__ """ Get coordinates of the top left angle Returns: point >>> Rect(1, 2, 3, 4).topLeft x = 1, y = 2 """ vector = self.coreRect.topLeft() return Point.fromVector2(vector) @property def left(self) -> COORDINATE_TYPE: """ Get lower x-coordinate of the rect Returns: self.x >>> Rect(1, 2, 3, 4).left 1 >>> Rect(1, 2, -3, 4).left 1 """ return self.coreRect.left() @property def right(self) -> COORDINATE_TYPE: """ Get upper x-coordinate of the rect Returns: self.x >>> Rect(1, 2, 3, 4).right 4 >>> Rect(1, 2, -3, 4).right -2 """ return self.coreRect.right() @property def center(self) -> Point[COORDINATE_TYPE]: """ Get coordinates of the center Returns: point >>> Rect(1, 2, 3, 4).center x = 2, y = 4 >>> Rect(1, 2, 3, 5).center x = 2, y = 4 >>> Rect(1, 2, 4, 4).center x = 3, y = 4 >>> Rect(1.0, 2.0, 4.0, 5.0).center x = 3.0, y = 4.5 """ vector = self.coreRect.center() return Point.fromVector2(vector)
[docs] def getArea(self) -> COORDINATE_TYPE: """ Get rect area Returns: self.width * self.height >>> Rect(1, 2, 3, 4).getArea() 12 >>> Rect(1.0, 2.0, 3.5, 4.5).getArea() 15.75 """ return self.coreRect.getArea()
[docs] def isInside(self, other: 'Rect') -> bool: """ Check other rect is inside in this or not Args: other: other rect Returns: true if this inside of the 'other' >>> first = Rect(1, 2, 3, 4) >>> second = Rect(1, 2, 3, 3) >>> first.isInside(second) False >>> second.isInside(first) True """ return self.coreRect.inside(other.coreRect)
[docs] def isValid(self) -> bool: """ Validate width and height of the rect Returns: True if width and height > 0 otherwise False >>> Rect(1, 2, 3, 4).isValid() True >>> Rect(1, 2, -3, 3).isValid() False """ return self.coreRect.isValid()
@property def size(self) -> Size[COORDINATE_TYPE]: """ Get rect size Returns: size >>> Rect(1, 2, 3, 4).size width = 3, height = 4 """ return Size(self.width, self.height) def __and__(self, other: 'Rect[COORDINATE_TYPE]') -> 'Rect[COORDINATE_TYPE]': """ Calculate an intersection of rects. Args: other: other rect Returns: intersection of rects >>> first = Rect(1, 2, 3, 4) >>> second = Rect(2, 1, 3, 3) >>> first and second x = 2, y = 1, width = 2, height = 1 """ return self.coreRect and other.coreRect def __eq__(self, other: 'Rect[COORDINATE_TYPE]') -> bool: """ Compare two rect Args: other: other rect Returns: True if x, y, width, height other rect are equal x, y, width, height this rect >>> first = Rect(1, 2, 3, 4) >>> second = Rect(1, 2, 3, 3) >>> first == second False >>> third = Rect(1, 2, 3, 4) >>> first == third True """ return self.coreRect == other.coreRect def __ne__(self, other: 'Rect[COORDINATE_TYPE]') -> bool: """ Compare two rect Args: other: other rect Returns: True if any of x, y, width, height other rect are not equal corresponding x, y, width, height of this rect >>> first = Rect(1, 2, 3, 4) >>> second = Rect(1, 2, 3, 3) >>> first != second True >>> third = Rect(1, 2, 3, 4) >>> first != third False """ return self.coreRect != other.coreRect
[docs] def adjust(self, dx: COORDINATE_TYPE, dy: COORDINATE_TYPE, dw: COORDINATE_TYPE, # pylint: disable=C0103 dh: COORDINATE_TYPE) -> None: """ Adjusts the rect by given amounts. Args: dx: adjustment for upper left corner x coordinate dy: adjustment for upper left corner y coordinate dw: adjustment for width dh: adjustment for height """ self.coreRect.adjust(dx, dy, dw, dh)
# pylint: disable=C0103
[docs] def adjusted(self, dx: COORDINATE_TYPE, dy: COORDINATE_TYPE, dw: COORDINATE_TYPE, dh: COORDINATE_TYPE) -> 'Rect': """ Copies and adjusts the rect by given amounts. Args: dx: adjustment for upper left corner x coordinate dy: adjustment for upper left corner y coordinate dw: adjustment for width dh: adjustment for height Returns: return copy. """ newRect = Rect() newRect.coreRect = self.coreRect.adjusted(dx, dy, dw, dh) return newRect
[docs] def asDict(self) -> dict: """ Convert rect to dict Returns: {"x": self.x, "y": self.y, "width": self.width, "height": self.height} >>> Rect(1, 2, 3, 4).asDict() {'x': 1, 'y': 2, 'width': 3, 'height': 4} >>> Rect(1.0, 2, 3.0, 4.0).asDict() {'x': 1.0, 'y': 2.0, 'width': 3.0, 'height': 4.0} """ return {"x": self.x, "y": self.y, "width": self.width, "height": self.height}
def __repr__(self) -> str: """ Dumps rect to string Returns: "x = {self.x}, y = {self.y}, width = {self.width}, width = {self.height}" >>> "{}".format(Rect(1, 2, 3, 4)) 'x = 1, y = 2, width = 3, height = 4' >>> "{}".format(Rect(1.0, 2.0, 3.0, 4.0)) 'x = 1.000000, y = 2.000000, width = 3.000000, height = 4.000000' """ return self.coreRect.__repr__()
[docs]class Landmarks(BaseEstimation): """ Base class for landmarks Attributes: _points (Optional[List[Point[float]]]): lazy load attributes, converted to point list core landmarks """ __slots__ = ["_points", "_coreLandmarks"] def __init__(self, coreLandmarks: LANDMARKS): """ Init Args: coreLandmarks (LANDMARKS): core landmarks """ super().__init__(coreLandmarks) self._points = None @property def points(self) -> List[Point[float]]: """ Lazy load of points. Returns: list of points """ if self._points is None: self._points = [Point.fromVector2(point) for point in self._coreEstimation] return self._points
[docs] def asDict(self) -> List[List[float]]: """ Convert to dict Returns: list to list points """ return [point.asDict() for point in self.points]