leopoldo-zugasti
4 年前
当前提交
30dc05b5
共有 11 个文件被更改,包括 747 次插入 和 105 次删除
-
5com.unity.perception/Editor/Pyrception/PyrceptionInstaller.cs
-
261com.unity.perception/Editor/Pyrception/pyrception-utils/pyrception_utils/preview.py
-
73com.unity.perception/Editor/Pyrception/pyrception-utils/pyrception_utils/pyrception.py
-
2com.unity.perception/Editor/Pyrception/pyrception-utils/requirements.txt
-
2com.unity.perception/Editor/Pyrception/pyrception-utils/setup.py
-
2com.unity.perception/Runtime/GroundTruth/Labelers/BoundingBox3DLabeler.cs
-
3com.unity.perception/Runtime/GroundTruth/PerceptionCamera.cs
-
311com.unity.perception/Editor/Pyrception/pyrception-utils/pyrception_utils/bbox.py
-
7com.unity.perception/Editor/Pyrception/pyrception-utils/pyrception_utils/bbox.py.meta
-
179com.unity.perception/Editor/Pyrception/pyrception-utils/pyrception_utils/bbox3d_plot.py
-
7com.unity.perception/Editor/Pyrception/pyrception-utils/pyrception_utils/bbox3d_plot.py.meta
|
|||
import math |
|||
|
|||
import numpy as np |
|||
from pyquaternion import Quaternion |
|||
|
|||
|
|||
def group_bbox2d_per_label(bboxes): |
|||
"""Group 2D bounding boxes with same label. |
|||
|
|||
Args: |
|||
bboxes (list[BBox2D]): a list of 2D bounding boxes |
|||
|
|||
Returns: |
|||
dict: a dictionary of 2d boundign box group. |
|||
{label1: [bbox1, bboxes2, ...], label2: [bbox1, ...]} |
|||
""" |
|||
bboxes_per_label = {} |
|||
for box in bboxes: |
|||
if box.label not in bboxes_per_label: |
|||
bboxes_per_label[box.label] = [] |
|||
bboxes_per_label[box.label].append(box) |
|||
|
|||
return bboxes_per_label |
|||
|
|||
|
|||
class BBox2D: |
|||
"""Canonical Representation of a 2D bounding box. |
|||
|
|||
Attributes: |
|||
label (str): string representation of the label. |
|||
x (float): x pixel coordinate of the upper left corner. |
|||
y (float): y pixel coordinate of the upper left corner. |
|||
w (float): width (number of pixels)of the bounding box. |
|||
h (float): height (number of pixels) of the bounding box. |
|||
score (float): detection confidence score. Default is set to score=1. |
|||
if this is a ground truth bounding box. |
|||
|
|||
Examples: |
|||
Here is an example about how to use this class. |
|||
|
|||
.. code-block:: |
|||
|
|||
>>> gt_bbox = BBox2D(label='car', x=2, y=6, w=2, h=4) |
|||
>>> gt_bbox |
|||
"label='car'|score=1.0|x=2.0|y=6.0|w=2.0|h=4.0" |
|||
>>> pred_bbox = BBox2D(label='car', x=2, y=5, w=2, h=4, score=0.79) |
|||
>>> pred_bbox.area |
|||
8 |
|||
>>> pred_bbox.intersect_with(gt_bbox) |
|||
True |
|||
>>> pred_bbox.intersection(gt_bbox) |
|||
6 |
|||
>>> pred_bbox.union(gt_bbox) |
|||
10 |
|||
>>> pred_bbox.iou(gt_bbox) |
|||
0.6 |
|||
|
|||
""" |
|||
|
|||
def __init__(self, label, x, y, w, h, score=1.0): |
|||
""" Initialize 2D bounding box object |
|||
|
|||
Args: |
|||
label (str): string representation of the label |
|||
x (float): x pixel coordinate of the upper left corner |
|||
y (float): y pixel coordinate of the upper left corner |
|||
w (float): width (number of pixels)of the bounding box |
|||
h (float): height (number of pixels) of the bounding box |
|||
score (float): detection confidence score |
|||
""" |
|||
self.label = label |
|||
self.x = x |
|||
self.y = y |
|||
self.w = w |
|||
self.h = h |
|||
self.score = score |
|||
|
|||
def __repr__(self): |
|||
return ( |
|||
f"label={self.label}|score={self.score:.2f}|" |
|||
f"x={self.x:.2f}|y={self.y:.2f}|w={self.w:.2f}|h={self.h:.2f}" |
|||
) |
|||
|
|||
def __eq__(self, other): |
|||
return ( |
|||
self.x == other.x |
|||
and self.y == other.y |
|||
and self.w == other.w |
|||
and self.h == other.h |
|||
and self.label == other.label |
|||
and math.isclose(self.score, other.score, rel_tol=1e-07) |
|||
) |
|||
|
|||
@property |
|||
def area(self): |
|||
"""Calculate area of this bounding box |
|||
|
|||
Returns: |
|||
width x height of the bound box |
|||
""" |
|||
return self.w * self.h |
|||
|
|||
def intersect_with(self, other): |
|||
"""Check whether this box intersects with other bounding box |
|||
|
|||
Args: |
|||
other (BBox2D): other bounding box object to check intersection |
|||
|
|||
Returns: |
|||
True if two bounding boxes intersect, False otherwise |
|||
""" |
|||
if self.x > other.x + other.w: |
|||
return False |
|||
if other.x > self.x + self.w: |
|||
return False |
|||
if self.y + self.h < other.y: |
|||
return False |
|||
if self.y > other.y + other.h: |
|||
return False |
|||
return True |
|||
|
|||
def intersection(self, other): |
|||
"""Calculate the intersection area with other bounding box |
|||
|
|||
Args: |
|||
other (BBox2D): other bounding box object to calculate intersection |
|||
|
|||
Returns: |
|||
float of the intersection area for two bounding boxes |
|||
""" |
|||
x1 = max(self.x, other.x) |
|||
y1 = max(self.y, other.y) |
|||
x2 = min(self.x + self.w, other.x + other.w) |
|||
y2 = min(self.y + self.h, other.y + other.h) |
|||
return (x2 - x1) * (y2 - y1) |
|||
|
|||
def union(self, other, intersection_area=None): |
|||
"""Calculate union area with other bounding box |
|||
|
|||
Args: |
|||
other (BBox2D): other bounding box object to calculate union |
|||
intersection_area (float): pre-calculated area of intersection |
|||
|
|||
Returns: |
|||
float of the union area for two bounding boxes |
|||
""" |
|||
area_a = self.area |
|||
area_b = other.area |
|||
if not intersection_area: |
|||
intersection_area = self.intersection(other) |
|||
return float(area_a + area_b - intersection_area) |
|||
|
|||
def iou(self, other): |
|||
"""Calculate intersection over union area with other bounding box |
|||
|
|||
.. math:: |
|||
IOU = \\frac{intersection}{union} |
|||
|
|||
Args: |
|||
other (BBox2D): other bounding box object to calculate iou |
|||
|
|||
Returns: |
|||
float of the union area for two bounding boxes |
|||
""" |
|||
# if boxes don't intersect |
|||
if not self.intersect_with(other): |
|||
return 0 |
|||
intersection_area = self.intersection(other) |
|||
union_area = self.union(other, intersection_area=intersection_area) |
|||
# intersection over union |
|||
iou = intersection_area / union_area |
|||
return iou |
|||
|
|||
|
|||
class BBox3D: |
|||
""" |
|||
Class for 3d bounding boxes which can either be predictions or |
|||
ground-truths. This class is the primary representation in this repo of 3d |
|||
bounding boxes and is based off of the Nuscenes style dataset. |
|||
""" |
|||
|
|||
def __init__( |
|||
self, |
|||
translation, |
|||
size, |
|||
label, |
|||
sample_token, |
|||
score=1, |
|||
rotation: Quaternion = Quaternion(), |
|||
velocity=(np.nan, np.nan, np.nan), |
|||
): |
|||
self.sample_token = sample_token |
|||
self.translation = translation |
|||
self.size = size |
|||
self.width, self.height, self.length = size |
|||
self.rotation = rotation |
|||
self.velocity = velocity |
|||
self.label = label |
|||
self.score = score |
|||
|
|||
def _local2world_coordinate(self, x): |
|||
""" |
|||
|
|||
Args: |
|||
x: vector describing point (x,y,z) in local coordinates (where the |
|||
center of the box is 0,0,0) |
|||
|
|||
Returns: the x,y,z coordinates of the input point in global coordinates |
|||
|
|||
""" |
|||
|
|||
y = np.array(self.translation) + self.rotation.rotate(x) |
|||
return y |
|||
|
|||
@property |
|||
def back_left_bottom_pt(self): |
|||
""" |
|||
|
|||
Returns: :py:class:`float`: Back-left-bottom point. |
|||
|
|||
""" |
|||
p = np.array([-self.width / 2, -self.height / 2, -self.length / 2]) |
|||
p = self._local2world_coordinate(p) |
|||
return p |
|||
|
|||
@property |
|||
def front_left_bottom_pt(self): |
|||
""" |
|||
:py:class:`float`: Front-left-bottom point. |
|||
""" |
|||
p = np.array([-self.width / 2, -self.height / 2, self.length / 2]) |
|||
p = self._local2world_coordinate(p) |
|||
return p |
|||
|
|||
@property |
|||
def front_right_bottom_pt(self): |
|||
""" |
|||
:py:class:`float`: Front-right-bottom point. |
|||
""" |
|||
p = np.array([self.width / 2, -self.height / 2, self.length / 2]) |
|||
p = self._local2world_coordinate(p) |
|||
return p |
|||
|
|||
@property |
|||
def back_right_bottom_pt(self): |
|||
""" |
|||
:py:class:`float`: Back-right-bottom point. |
|||
""" |
|||
p = np.array([self.width / 2, -self.height / 2, -self.length / 2]) |
|||
p = self._local2world_coordinate(p) |
|||
return p |
|||
|
|||
@property |
|||
def back_left_top_pt(self): |
|||
""" |
|||
:py:class:`float`: Back-left-top point. |
|||
""" |
|||
p = np.array([-self.width / 2, self.height / 2, -self.length / 2]) |
|||
p = self._local2world_coordinate(p) |
|||
return p |
|||
|
|||
@property |
|||
def front_left_top_pt(self): |
|||
""" |
|||
:py:class:`float`: Front-left-top point. |
|||
""" |
|||
p = np.array([-self.width / 2, self.height / 2, self.length / 2]) |
|||
p = self._local2world_coordinate(p) |
|||
return p |
|||
|
|||
@property |
|||
def front_right_top_pt(self): |
|||
""" |
|||
:py:class:`float`: Front-right-top point. |
|||
""" |
|||
p = np.array([self.width / 2, self.height / 2, self.length / 2]) |
|||
p = self._local2world_coordinate(p) |
|||
return p |
|||
|
|||
@property |
|||
def back_right_top_pt(self): |
|||
""" |
|||
:py:class:`float`: Back-right-top point. |
|||
""" |
|||
p = np.array([self.width / 2, self.height / 2, -self.length / 2]) |
|||
p = self._local2world_coordinate(p) |
|||
return p |
|||
|
|||
@property |
|||
def p(self) -> np.ndarray: |
|||
""" |
|||
|
|||
Returns: list of all 8 corners of the box beginning with the the bottom |
|||
four corners and then the top |
|||
four corners, both in counterclockwise order (from birds eye view) |
|||
beginning with the back-left corner |
|||
|
|||
""" |
|||
x = np.vstack( |
|||
[ |
|||
self.back_left_bottom_pt, |
|||
self.front_left_bottom_pt, |
|||
self.front_right_bottom_pt, |
|||
self.back_right_bottom_pt, |
|||
self.back_left_top_pt, |
|||
self.front_left_top_pt, |
|||
self.front_right_top_pt, |
|||
self.back_right_top_pt, |
|||
] |
|||
) |
|||
return x |
|
|||
fileFormatVersion: 2 |
|||
guid: 4122f2f81144716438e5281967ce7272 |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
""" Helper bounding box 3d library to plot pretty 3D boundign |
|||
boxes with a simple Python API. |
|||
""" |
|||
|
|||
import cv2 |
|||
import numpy |
|||
import streamlit as st |
|||
|
|||
|
|||
def _add_single_bbox3d_on_image( |
|||
image, |
|||
front_bottom_left, |
|||
front_upper_left, |
|||
front_upper_right, |
|||
front_bottom_right, |
|||
back_bottom_left, |
|||
back_upper_left, |
|||
back_upper_right, |
|||
back_bottom_right, |
|||
color=None, |
|||
box_line_width=2, |
|||
): |
|||
""" Add a single 3D bounding box to the passed in image. |
|||
|
|||
For this version of the method, all of the passed in coordinates should be |
|||
integer tuples already projected in image pixel coordinate space. |
|||
|
|||
Args: |
|||
image (numpy array): numpy array version of the image |
|||
front_bottom_left (int tuple): Front bottom left coordinate of the 3D |
|||
bounding box in pixel space |
|||
front_upper_left (int tuple): Front upper left coordinate of the 3D |
|||
bounding box in pixel space |
|||
front_upper_right (int tuple): Front upper right coordinate of the 3D |
|||
bounding box in pixel space |
|||
front_bottom_right (int tuple): Front bottom right coordinate of the 3D |
|||
bounding box in pixel space |
|||
back_bottom_left (int tuple): Back bottom left coordinate of the 3D |
|||
bounding box in pixel space |
|||
back_upper_left (int tuple): Back bottom left coordinate of the 3D |
|||
bounding box in pixel space |
|||
back_upper_right (int tuple): Back bottom left coordinate of the 3D |
|||
bounding box in pixel space |
|||
back_bottom_right (int tuple): Back bottom left coordinate of the 3D |
|||
bounding box in pixel space |
|||
color (tuple): RGBA color of the bounding box. Defaults to None. If |
|||
color = None the the tuple of [0, 255, 0, 255] (Green) will be used. |
|||
box_line_width: The width of the drawn box. Defaults to 2. |
|||
""" |
|||
try: |
|||
fbl = (front_bottom_left[0], front_bottom_left[1]) |
|||
ful = (front_upper_left[0], front_upper_left[1]) |
|||
fur = (front_upper_right[0], front_upper_right[1]) |
|||
fbr = (front_bottom_right[0], front_bottom_right[1]) |
|||
|
|||
bbl = (back_bottom_left[0], back_bottom_left[1]) |
|||
bul = (back_upper_left[0], back_upper_left[1]) |
|||
bur = (back_upper_right[0], back_upper_right[1]) |
|||
bbr = (back_bottom_right[0], back_bottom_right[1]) |
|||
|
|||
except ValueError: |
|||
raise TypeError("all box coorinates must be a number") |
|||
|
|||
if color is None: |
|||
color = [0, 255, 0, 255] |
|||
|
|||
cv2.line(image, fbl, ful, color, box_line_width) # front left |
|||
cv2.line(image, ful, fur, color, box_line_width) # front top |
|||
cv2.line(image, fbr, fur, color, box_line_width) # front right |
|||
cv2.line(image, fbl, fbr, color, box_line_width) # front bottom |
|||
|
|||
cv2.line(image, bbl, bul, color, box_line_width) # back left |
|||
cv2.line(image, bul, bur, color, box_line_width) # back top |
|||
cv2.line(image, bbr, bur, color, box_line_width) # back right |
|||
cv2.line(image, bbl, bbr, color, box_line_width) # back bottom |
|||
|
|||
cv2.line(image, ful, bul, color, box_line_width) # top left |
|||
cv2.line(image, fur, bur, color, box_line_width) # top right |
|||
cv2.line(image, fbl, bbl, color, box_line_width) # bottom left |
|||
cv2.line(image, fbr, bbr, color, box_line_width) # bottom right |
|||
|
|||
|
|||
def add_single_bbox3d_on_image( |
|||
image, box, proj, color=None, orthographic=False, box_line_width=2, |
|||
): |
|||
"""" Add single 3D bounding box on a given image. |
|||
|
|||
Args: |
|||
image (numpy array): a numpy array for an image |
|||
box (BBox3D): a 3D bounding box in camera's coordinate system |
|||
proj (numpy 2D array): camera's 3x3 projection matrix |
|||
color(tuple): RGBA color of the bounding box. Defaults to None. If |
|||
color = None the the tuple of [0, 255, 0, 255] (Green) will be used. |
|||
box_line_width (int): line width of the bounding boxes. Defaults to 2. |
|||
""" |
|||
img_height, img_width, _ = image.shape |
|||
|
|||
fll = box.back_left_bottom_pt |
|||
ful = box.back_left_top_pt |
|||
fur = box.back_right_top_pt |
|||
flr = box.back_right_bottom_pt |
|||
|
|||
bll = box.front_left_bottom_pt |
|||
bul = box.front_left_top_pt |
|||
bur = box.front_right_top_pt |
|||
blr = box.front_right_bottom_pt |
|||
|
|||
pixel_location_fun = _project_pt_to_pixel_location_orthographic if orthographic else _project_pt_to_pixel_location |
|||
|
|||
fll_raster = pixel_location_fun(fll, proj, img_height, img_width) |
|||
ful_raster = pixel_location_fun(ful, proj, img_height, img_width) |
|||
fur_raster = pixel_location_fun(fur, proj, img_height, img_width) |
|||
flr_raster = pixel_location_fun(flr, proj, img_height, img_width) |
|||
bll_raster = pixel_location_fun(bll, proj, img_height, img_width) |
|||
bul_raster = pixel_location_fun(bul, proj, img_height, img_width) |
|||
bur_raster = pixel_location_fun(bur, proj, img_height, img_width) |
|||
blr_raster = pixel_location_fun(blr, proj, img_height, img_width) |
|||
|
|||
_add_single_bbox3d_on_image( |
|||
image, |
|||
fll_raster, |
|||
ful_raster, |
|||
fur_raster, |
|||
flr_raster, |
|||
bll_raster, |
|||
bul_raster, |
|||
bur_raster, |
|||
blr_raster, |
|||
color, |
|||
box_line_width, |
|||
) |
|||
|
|||
def _project_pt_to_pixel_location(pt, projection, img_height, img_width): |
|||
""" Projects a 3D coordinate into a pixel location. |
|||
|
|||
Applies the passed in projection matrix to project a point from the camera's |
|||
coordinate space into pixel space. |
|||
|
|||
For a description of the math used in this method, see: |
|||
https://www.scratchapixel.com/lessons/3d-basic-rendering/computing-pixel-coordinates-of-3d-point/ |
|||
|
|||
Args: |
|||
pt (numpy array): The 3D point to project. |
|||
projection (numpy 2D array): The camera's 3x3 projection matrix. |
|||
img_height (int): The height of the image in pixels. |
|||
img_width (int): The width of the image in pixels. |
|||
|
|||
Returns: |
|||
numpy array: a one-dimensional array with two values (x and y) |
|||
representing a point's pixel coordinate in an image. |
|||
""" |
|||
|
|||
_pt = projection.dot(pt) |
|||
|
|||
# compute the perspective divide. Near clipping plane should take care of |
|||
# divide by zero cases, but we will check to be sure |
|||
if _pt[2] != 0: |
|||
_pt /= _pt[2] |
|||
|
|||
return numpy.array( |
|||
[ |
|||
int(-(_pt[0] * img_width) / 2.0 + (img_width * 0.5)), |
|||
int((_pt[1] * img_height) / 2.0 + (img_height * 0.5)), |
|||
] |
|||
) |
|||
|
|||
def _project_pt_to_pixel_location_orthographic(pt, projection, img_height, img_width): |
|||
projection = numpy.array([ |
|||
[projection[0][0], 0, 0], |
|||
[0, -projection[1][1], 0], |
|||
[0, 0, projection[2][2]] |
|||
]) |
|||
temp = projection.dot(pt) |
|||
|
|||
pixel = [ |
|||
int((temp[0] + 1)*0.5 * img_width), |
|||
int((temp[1] + 1)*0.5 * img_height) |
|||
] |
|||
return pixel |
|
|||
fileFormatVersion: 2 |
|||
guid: 6ebe9fac6325103488e689f91f4e486e |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
撰写
预览
正在加载...
取消
保存
Reference in new issue