Matthias Bisping 4be91de036 Refactoring
Further clean up Rectangle class
2023-01-04 15:26:39 +01:00

129 lines
3.6 KiB
Python

# See https://stackoverflow.com/a/33533514
from __future__ import annotations
from json import dumps
from operator import itemgetter
from typing import Iterable, Union, Dict
from funcy import identity
from cv_analysis.utils.spacial import adjacent, contains, intersection, iou, area, is_contained
Coord = Union[int, float]
class Rectangle:
def __init__(self, x1, y1, x2, y2, discrete=True):
"""Creates a rectangle from two points."""
nearest_valid = int if discrete else identity
self.__x1 = nearest_valid(x1)
self.__y1 = nearest_valid(y1)
self.__x2 = nearest_valid(x2)
self.__y2 = nearest_valid(y2)
self.__w = nearest_valid(x2 - x1)
self.__h = nearest_valid(y2 - y1)
@property
def x1(self):
return self.__x1
@property
def x2(self):
return self.__x2
@property
def y1(self):
return self.__y1
@property
def y2(self):
return self.__y2
@property
def w(self):
return self.__w
@property
def h(self):
return self.__h
def __str__(self):
return dumps(self.json())
def __repr__(self):
return str(self)
def __iter__(self):
return list(self.json().values()).__iter__()
def __eq__(self, other: Rectangle):
return all([self.x1 == other.x1, self.y1 == other.y1, self.w == other.w, self.h == other.h])
def __hash__(self):
return hash((self.x1, self.y1, self.x2, self.y2))
@classmethod
def from_xyxy(cls, xyxy: Iterable[Coord], discrete=True):
"""Creates a rectangle from two points."""
return cls(*xyxy, discrete=discrete)
@classmethod
def from_xywh(cls, xywh: Iterable[Coord], discrete=True):
"""Creates a rectangle from a point and a width and height."""
x1, y1, w, h = xywh
x2 = x1 + w
y2 = y1 + h
return cls(x1, y1, x2, y2, discrete=discrete)
@classmethod
def from_dict_xywh(cls, xywh: Dict[str, Coord], discrete=True):
"""Creates a rectangle from a point and a width and height."""
x1, y1, w, h = itemgetter("x", "y", "width", "height")(xywh)
x2 = x1 + w
y2 = y1 + h
return cls(x1, y1, x2, y2, discrete=discrete)
def xyxy(self):
return self.x1, self.y1, self.x2, self.y2
def xywh(self):
return self.x1, self.y1, self.w, self.h
def json(self):
return self.json_xywh()
def json_full(self):
return {**self.json_xyxy(), "width": self.w, "height": self.h}
def json_xyxy(self):
return {"x1": self.x1, "y1": self.y1, "x2": self.x2, "y2": self.y2}
def json_xywh(self):
return {"x": self.x1, "y": self.y1, "width": self.w, "height": self.h}
def intersection(self, other):
"""Calculates the intersection of this and another rectangle."""
return intersection(self, other)
def area(self):
"""Calculates the area of this rectangle."""
return area(self)
def iou(self, other: Rectangle):
"""Calculates the intersection over union of this and another rectangle."""
return iou(self, other)
def includes(self, other: Rectangle, tol=3):
"""Checks if this rectangle contains another."""
return contains(self, other, tol)
def is_included(self, rectangles: Iterable[Rectangle]):
"""Checks if this rectangle is contained by any of the given rectangles."""
return is_contained(self, rectangles)
def adjacent(self, other: Rectangle, tolerance=7):
"""Checks if this rectangle is adjacent to another."""
return adjacent(self, other, tolerance)