Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apply Black formatting #13

Merged
merged 3 commits into from
Jun 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ jobs:
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics

- name: Check black formatting
uses: psf/black@stable

- name: Test with pytest
run: |
python -m pytest tests/unit/
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<img src="https://img.shields.io/github/last-commit/ch-sa/labelCloud?color=blue">
<img src="https://img.shields.io/badge/python-3.6%20%7C%203.7%20%7C%203.8-blue" />
<img src="https://github.com/ch-sa/labelCloud/workflows/Tests/badge.svg" />
<img src="https://img.shields.io/badge/code%20style-black-000000.svg" />
</p>


Expand Down
6 changes: 4 additions & 2 deletions labelCloud.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
if __name__ == '__main__':
if __name__ == "__main__":

import sys

sys.path.insert(0, "labelCloud")
from labelCloud import app
app.run()

app.run()
4 changes: 3 additions & 1 deletion labelCloud/__main__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import sys

sys.path.insert(0, "labelCloud")

if __name__ == '__main__':
if __name__ == "__main__":
import app

app.run()
58 changes: 43 additions & 15 deletions labelCloud/control/alignmode.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
"""A module for aligning point clouds with the floor. The user has to span a triangle with three points on the plane
that serves as the ground. Then the old point cloud will be saved up and the aligned current will overwrite the old."""
"""
A module for aligning point clouds with the floor. The user has to span a triangle with
three points on the plane that serves as the ground. Then the old point cloud will be
saved up and the aligned current will overwrite the old.
"""
from typing import Union, TYPE_CHECKING

import numpy as np

import utils.oglhelper as ogl
from control.pcd_manager import PointCloudManger
from .pcd_manager import PointCloudManger

if TYPE_CHECKING:
from view.gui import GUI

Expand All @@ -24,7 +28,7 @@ def __init__(self, pcd_manager: PointCloudManger):
self.tmp_p2 = None
self.tmp_p3 = None

def set_view(self, view: 'GUI'):
def set_view(self, view: "GUI"):
self.view = view
self.view.glWidget.align_mode = self

Expand All @@ -41,9 +45,14 @@ def change_activation(self, force=None):
self.activated = True

if self.activated:
self.view.update_status("Select three points on the plane that should be the floor.", "alignment")
self.view.update_status(
"Select three points on the plane that should be the floor.",
"alignment",
)
self.view.action_alignpcd.setChecked(self.activated)
self.view.activate_draw_modes(not self.activated) # Prevent bbox drawing while aligning
self.view.activate_draw_modes(
not self.activated
) # Prevent bbox drawing while aligning
print("Alignmode was changed to %s!" % self.activated)

def reset(self, points_only: bool = False):
Expand All @@ -57,7 +66,9 @@ def register_point(self, new_point):
self.plane1 = new_point
elif not self.plane2:
self.plane2 = new_point
self.view.update_status("The triangle area should be part over and part under the floor points.")
self.view.update_status(
"The triangle area should be part over and part under the floor points."
)
elif not self.plane3:
self.plane3 = new_point
self.calculate_angles()
Expand Down Expand Up @@ -85,28 +96,45 @@ def draw_preview(self):
if self.plane3:
self.tmp_p3 = self.plane3

ogl.draw_points([self.plane1, self.plane2, self.tmp_p3], color=self.point_color)
ogl.draw_triangles([self.plane1, self.plane2, self.tmp_p3], color=self.area_color)
ogl.draw_points(
[self.plane1, self.plane2, self.tmp_p3], color=self.point_color
)
ogl.draw_triangles(
[self.plane1, self.plane2, self.tmp_p3], color=self.area_color
)

elif self.plane1 and self.plane2 and self.plane3:

ogl.draw_points([self.plane1, self.plane2, self.plane3], color=self.point_color)
ogl.draw_triangles([self.plane1, self.plane2, self.plane3], color=self.area_color)
ogl.draw_points(
[self.plane1, self.plane2, self.plane3], color=self.point_color
)
ogl.draw_triangles(
[self.plane1, self.plane2, self.plane3], color=self.area_color
)

def calculate_angles(self):
# Calculate plane normal with self.plane1 as origin
plane_normal = np.cross(np.subtract(self.plane2, self.plane1), np.subtract(self.plane3, self.plane1))
plane_normal = np.cross(
np.subtract(self.plane2, self.plane1), np.subtract(self.plane3, self.plane1)
)
pn_normalized = plane_normal / np.linalg.norm(plane_normal) # normalize normal
z_axis = np.array([0, 0, 1])

# Calculate axis-angle-rotation
rotation_angle = np.arccos(np.dot(pn_normalized, z_axis))
rotation_axis = np.cross(pn_normalized, z_axis) / np.linalg.norm(np.cross(pn_normalized, z_axis))
print("Alignment rotation: %s around %s" % (round(rotation_angle, 2), np.round(rotation_axis, 2)))
rotation_axis = np.cross(pn_normalized, z_axis) / np.linalg.norm(
np.cross(pn_normalized, z_axis)
)
print(
f"Alignment rotation: {round(rotation_angle, 2)} "
f"around {np.round(rotation_axis, 2)}"
)

# Initiate point cloud rotation
self.pcd_manager.rotate_pointcloud(rotation_axis, rotation_angle, self.plane1)

self.view.update_status("Aligned point cloud with the selected floor.", "navigation")
self.view.update_status(
"Aligned point cloud with the selected floor.", "navigation"
)
self.change_activation(force=False)
self.reset()
107 changes: 76 additions & 31 deletions labelCloud/control/bbox_controller.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
"""
A class to handle all user manipulations of the bounding boxes and collect all labeling settings in one place.
Bounding Box Management: adding, updating, deleting bboxes; changing the active bounding box
A class to handle all user manipulations of the bounding boxes and collect all labeling
settings in one place.
Bounding Box Management: adding, selecting updating, deleting bboxes;
Possible Active Bounding Box Manipulations: rotation, translation, scaling
"""
from typing import TYPE_CHECKING, Union, List

import numpy as np


from utils import oglhelper
from control.config_manager import config
from model.bbox import BBox
from .config_manager import config

if TYPE_CHECKING:
from view.gui import GUI


# DECORATORS
def has_active_bbox_decorator(func):
def wrapper(*args, **kwargs): # plays function if active bbox exists
"""
Only execute bounding box manipulation if there is an active bounding box.
"""

def wrapper(*args, **kwargs):
if args[0].has_active_bbox():
return func(*args, **kwargs)
else:
Expand All @@ -27,7 +33,11 @@ def wrapper(*args, **kwargs): # plays function if active bbox exists


def only_zrotation_decorator(func):
def wrapper(*args, **kwargs): # plays function if active bbox exists
"""
Only execute x- and y-rotation if z_rotation_only mode is not activated.
"""

def wrapper(*args, **kwargs):
if not config.getboolean("USER_INTERFACE", "z_rotation_only"):
return func(*args, **kwargs)
else:
Expand Down Expand Up @@ -67,14 +77,16 @@ def get_classname(self):

# SETTERS

def set_view(self, view: 'GUI'):
def set_view(self, view: "GUI"):
self.view = view

def add_bbox(self, bbox: BBox):
if isinstance(bbox, BBox):
self.bboxes.append(bbox)
self.set_active_bbox(self.bboxes.index(bbox))
self.view.update_status("Bounding Box added, it can now be corrected.", mode="correction")
self.view.update_status(
"Bounding Box added, it can now be corrected.", mode="correction"
)

def update_bbox(self, bbox_id: int, bbox: BBox):
if isinstance(bbox, BBox) and (0 <= bbox_id < len(self.bboxes)):
Expand All @@ -97,7 +109,9 @@ def set_active_bbox(self, bbox_id: int):
if 0 <= bbox_id < len(self.bboxes):
self.active_bbox_id = bbox_id
self.update_all()
self.view.update_status("Bounding Box selected, it can now be corrected.", mode="correction")
self.view.update_status(
"Bounding Box selected, it can now be corrected.", mode="correction"
)
else:
self.deselect_bbox()

Expand Down Expand Up @@ -164,29 +178,39 @@ def rotate_around_x(self, dangle: float = None, clockwise: bool = False):
dangle = dangle or config.getfloat("LABEL", "std_rotation")
if clockwise:
dangle *= -1
self.get_active_bbox().set_x_rotation(self.get_active_bbox().get_x_rotation() + dangle)
self.get_active_bbox().set_x_rotation(
self.get_active_bbox().get_x_rotation() + dangle
)

@only_zrotation_decorator
@has_active_bbox_decorator
def rotate_around_y(self, dangle: float = None, clockwise: bool = False):
dangle = dangle or config.getfloat("LABEL", "std_rotation")
if clockwise:
dangle *= -1
self.get_active_bbox().set_y_rotation(self.get_active_bbox().get_y_rotation() + dangle)
self.get_active_bbox().set_y_rotation(
self.get_active_bbox().get_y_rotation() + dangle
)

@has_active_bbox_decorator
def rotate_around_z(self, dangle: float = None, clockwise: bool = False, absolute: bool = False):
def rotate_around_z(
self, dangle: float = None, clockwise: bool = False, absolute: bool = False
):
dangle = dangle or config.getfloat("LABEL", "std_rotation")
if clockwise:
dangle *= -1
if absolute:
self.get_active_bbox().set_z_rotation(dangle)
else:
self.get_active_bbox().set_z_rotation(self.get_active_bbox().get_z_rotation() + dangle)
self.get_active_bbox().set_z_rotation(
self.get_active_bbox().get_z_rotation() + dangle
)
self.update_all()

@has_active_bbox_decorator
def rotate_with_mouse(self, x_angle: float, y_angle: float): # TODO: Make more intuitive
def rotate_with_mouse(
self, x_angle: float, y_angle: float
): # TODO: Make more intuitive
# Get bbox perspective
pcd_z_rotation = self.pcdc.get_pointcloud().rot_z
bbox_z_rotation = self.get_active_bbox().get_z_rotation()
Expand All @@ -195,9 +219,6 @@ def rotate_with_mouse(self, x_angle: float, y_angle: float): # TODO: Make more
bbox_cosz = round(np.cos(np.deg2rad(total_z_rotation)), 0)
bbox_sinz = -round(np.sin(np.deg2rad(total_z_rotation)), 0)

# self.rotate_around_x(y_angle * bbox_cosz + x_angle * bbox_sinz)
# self.rotate_around_y(y_angle * bbox_sinz + x_angle * bbox_cosz)

self.rotate_around_x(y_angle * bbox_cosz)
self.rotate_around_y(y_angle * bbox_sinz)
self.rotate_around_z(x_angle)
Expand All @@ -208,28 +229,43 @@ def translate_along_x(self, distance: float = None, left: bool = False):
if left:
distance *= -1
cosz, sinz, bu = self.pcdc.get_perspective()
self.get_active_bbox().set_x_translation(self.get_active_bbox().center[0] + distance * cosz)
self.get_active_bbox().set_y_translation(self.get_active_bbox().center[1] + distance * sinz)
self.get_active_bbox().set_x_translation(
self.get_active_bbox().center[0] + distance * cosz
)
self.get_active_bbox().set_y_translation(
self.get_active_bbox().center[1] + distance * sinz
)

@has_active_bbox_decorator
def translate_along_y(self, distance: float = None, forward: bool = False):
distance = distance or config.getfloat("LABEL", "std_translation")
if forward:
distance *= -1
cosz, sinz, bu = self.pcdc.get_perspective()
self.get_active_bbox().set_x_translation(self.get_active_bbox().center[0] + distance * bu * -sinz)
self.get_active_bbox().set_y_translation(self.get_active_bbox().center[1] + distance * bu * cosz)
self.get_active_bbox().set_x_translation(
self.get_active_bbox().center[0] + distance * bu * -sinz
)
self.get_active_bbox().set_y_translation(
self.get_active_bbox().center[1] + distance * bu * cosz
)

@has_active_bbox_decorator
def translate_along_z(self, distance: float = None, down: bool = False):
distance = distance or config.getfloat("LABEL", "std_translation")
if down:
distance *= -1
self.get_active_bbox().set_z_translation(self.get_active_bbox().center[2] + distance)
self.get_active_bbox().set_z_translation(
self.get_active_bbox().center[2] + distance
)

# Scale bbox while keeping ratios
@has_active_bbox_decorator
def scale(self, length_increase: float = None, decrease: bool = False):
def scale(self, length_increase: float = None, decrease: bool = False) -> None:
"""Scales a bounding box while keeping the previous aspect ratio.

:param length_increase: factor by which the length should be increased
:param decrease: if True, reverses the length_increasee (* -1)
:return: None
"""
length_increase = length_increase or config.getfloat("LABEL", "std_scaling")
if decrease:
length_increase *= -1
Expand All @@ -243,35 +279,44 @@ def scale(self, length_increase: float = None, decrease: bool = False):

self.get_active_bbox().set_dimensions(new_length, new_width, new_height)

def select_bbox_by_ray(self, x: int, y: int):
intersected_bbox_id = oglhelper.get_intersected_bboxes(x, y, self.bboxes, self.view.glWidget.modelview,
self.view.glWidget.projection)
def select_bbox_by_ray(self, x: int, y: int) -> None:
intersected_bbox_id = oglhelper.get_intersected_bboxes(
x,
y,
self.bboxes,
self.view.glWidget.modelview,
self.view.glWidget.projection,
)
if intersected_bbox_id is not None:
self.set_active_bbox(intersected_bbox_id)
print("Selected bounding box %s." % intersected_bbox_id)

# HELPER

def update_all(self):
def update_all(self) -> None:
self.update_z_dial()
self.update_curr_class()
self.update_label_list()
self.view.update_bbox_stats(self.get_active_bbox())

@has_active_bbox_decorator
def update_z_dial(self):
def update_z_dial(self) -> None:
self.view.dial_zrotation.blockSignals(True) # To brake signal loop
self.view.dial_zrotation.setValue(int(self.get_active_bbox().get_z_rotation()))
self.view.dial_zrotation.blockSignals(False)

def update_curr_class(self):
def update_curr_class(self) -> None:
if self.has_active_bbox():
self.view.update_curr_class_edit()
else:
self.view.update_curr_class_edit(force="")

# Updating the list of existing labels if bboxes changed
def update_label_list(self):
def update_label_list(self) -> None:
"""Updates the list of drawn labels and highlights the active label.

Should be always called if the bounding boxes changed.
:return: None
"""
self.view.label_list.blockSignals(True) # To brake signal loop
self.view.label_list.clear()
for bbox in self.bboxes:
Expand Down
Loading