import math
import numpy as np
from . import BaseAnnotationDefinition
[docs]class Cube(BaseAnnotationDefinition):
"""
Cube annotation object
"""
type = "cube"
def __init__(self, label, front_tl, front_tr, front_br, front_bl,
back_tl, back_tr, back_br, back_bl, angle=None,
attributes=None, description=None):
super().__init__(description=description, attributes=attributes)
self.front_bl = front_bl
self.front_br = front_br
self.front_tr = front_tr
self.front_tl = front_tl
self.back_bl = back_bl
self.back_br = back_br
self.back_tr = back_tr
self.back_tl = back_tl
self._angle = angle
self.label = label
self.keys = ["front_tl", "front_tr", "front_br", "front_bl",
"back_tl", "back_tr", "back_br", "back_bl"]
@staticmethod
def calculate_angle(b, c):
a = [b[0] + 200, b[1]]
if b in (a, c):
return 0
ang = math.degrees(
math.atan2(c[1] - b[1], c[0] - b[0]) - math.atan2(a[1] - b[1], a[0] - b[0]))
return ang + 360 if ang < 0 else ang
@property
def angle(self):
if self._angle is None:
self._angle = Cube.calculate_angle(self.front_tl, self.front_tr)
return self._angle
@property
def x(self):
return self.geo[:, 0]
@property
def y(self):
return self.geo[:, 1]
@property
def geo(self):
return np.asarray([self.front_tl,
self.front_tr,
self.front_br,
self.front_bl,
self.back_tl,
self.back_tr,
self.back_br,
self.back_bl
])
[docs] def show(self, image, thickness, with_text, height, width, annotation_format, color, alpha=1):
"""
Show annotation as ndarray
:param image: empty or image to draw on
:param thickness:
:param with_text: not required
:param height: item height
:param width: item width
:param annotation_format: options: list(dl.ViewAnnotationOptions)
:param color: color
:param alpha: opacity value [0 1], default 1
:return: ndarray
"""
try:
import cv2
except (ImportError, ModuleNotFoundError):
self.logger.error(
'Import Error! Cant import cv2. Annotations operations will be limited. import manually and fix errors')
raise
image = cv2.polylines(image,
pts=[np.asarray(
[self.front_bl, self.front_br, self.front_tr, self.front_tl]).round().astype(int)],
isClosed=True,
color=color,
thickness=thickness)
image = cv2.polylines(image,
pts=[np.asarray([self.back_bl, self.back_br, self.back_tr, self.back_tl]).round().astype(
int)],
isClosed=True,
color=color,
thickness=thickness)
image = cv2.line(image,
pt1=tuple(np.asarray(self.front_bl).round().astype(int)),
pt2=tuple(np.asarray(self.back_bl).round().astype(int)),
color=color,
thickness=thickness)
image = cv2.line(image,
pt1=tuple(np.asarray(self.front_br).round().astype(int)),
pt2=tuple(np.asarray(self.back_br).round().astype(int)),
color=color,
thickness=thickness)
image = cv2.line(image,
pt1=tuple(np.asarray(self.front_tl).round().astype(int)),
pt2=tuple(np.asarray(self.back_tl).round().astype(int)),
color=color,
thickness=thickness)
image = cv2.line(image,
pt1=tuple(np.asarray(self.front_tr).round().astype(int)),
pt2=tuple(np.asarray(self.back_tr).round().astype(int)),
color=color,
thickness=thickness)
return image
def to_coordinates(self, color):
coordinates = {self.keys[idx]: {"x": float(x), "y": float(y), "z": 0}
for idx, [x, y] in enumerate(self.geo)}
coordinates['angle'] = self.angle
return coordinates
@staticmethod
def from_coordinates(coordinates):
geo = list()
for key, pt in enumerate(coordinates):
geo.append([pt["x"], pt["y"]])
return np.asarray(geo)
@classmethod
def from_json(cls, _json):
if "coordinates" in _json:
key = "coordinates"
elif "data" in _json:
key = "data"
else:
raise ValueError('can not find "coordinates" or "data" in annotation. id: {}'.format(_json["id"]))
return cls(
front_bl=np.asarray([_json[key]["front_bl"]['x'], _json[key]["front_bl"]['y']]),
front_br=np.asarray([_json[key]["front_br"]['x'], _json[key]["front_br"]['y']]),
front_tl=np.asarray([_json[key]["front_tl"]['x'], _json[key]["front_tl"]['y']]),
front_tr=np.asarray([_json[key]["front_tr"]['x'], _json[key]["front_tr"]['y']]),
back_bl=np.asarray([_json[key]["back_bl"]['x'], _json[key]["back_bl"]['y']]),
back_br=np.asarray([_json[key]["back_br"]['x'], _json[key]["back_br"]['y']]),
back_tl=np.asarray([_json[key]["back_tl"]['x'], _json[key]["back_tl"]['y']]),
back_tr=np.asarray([_json[key]["back_tr"]['x'], _json[key]["back_tr"]['y']]),
label=_json["label"],
angle=_json[key]["angle"],
attributes=_json.get("attributes", None)
)
@staticmethod
def rotate(center, point, angle):
angle = math.radians(angle)
cx, cy = center
px, py = point
qx = cx + math.cos(angle) * (px - cx) - math.sin(angle) * (py - cy)
qy = cy + math.sin(angle) * (px - cx) + math.cos(angle) * (py - cy)
return [qx, qy]
[docs] @classmethod
def from_boxes_and_angle(cls,
front_left, front_top, front_right, front_bottom,
back_left, back_top, back_right, back_bottom,
label, angle=0, attributes=None):
"""
Create cuboid by given front and back boxes with angle
the angle calculate fom the center of each box
"""
if angle != 0:
front_center = [front_left + (front_right - front_left) / 2, front_top + (front_bottom - front_top) / 2]
back_center = [back_left + (back_right - back_left) / 2, back_top + (back_bottom - back_top) / 2]
front_tl = Cube.rotate(center=front_center, point=[front_left, front_top], angle=angle)
front_tr = Cube.rotate(center=front_center, point=[front_right, front_top], angle=angle)
front_br = Cube.rotate(center=front_center, point=[front_right, front_bottom], angle=angle)
front_bl = Cube.rotate(center=front_center, point=[front_left, front_bottom], angle=angle)
back_tl = Cube.rotate(center=back_center, point=[back_left, back_top], angle=angle)
back_tr = Cube.rotate(center=back_center, point=[back_right, back_top], angle=angle)
back_br = Cube.rotate(center=back_center, point=[back_right, back_bottom], angle=angle)
back_bl = Cube.rotate(center=back_center, point=[back_left, back_bottom], angle=angle)
else:
front_tl = [front_left, front_top]
front_tr = [front_right, front_top]
front_br = [front_right, front_bottom]
front_bl = [front_left, front_bottom]
back_tl = [back_left, back_top]
back_tr = [back_right, back_top]
back_br = [back_right, back_bottom]
back_bl = [back_left, back_bottom]
return cls(
front_tl=front_tl, front_tr=front_tr, front_br=front_br, front_bl=front_bl,
back_tl=back_tl, back_tr=back_tr, back_br=back_br, back_bl=back_bl,
label=label,
angle=angle,
attributes=attributes
)