From 78e23a61434ee162ce2d236a51d55d36f4657c70 Mon Sep 17 00:00:00 2001 From: ch-sa Date: Sun, 27 Jun 2021 14:21:19 +0200 Subject: [PATCH] Clean comments, strings, imports --- labelCloud/control/alignmode.py | 13 +++-- labelCloud/control/bbox_controller.py | 48 +++++++++++------ labelCloud/control/config_manager.py | 9 +++- labelCloud/control/controller.py | 75 ++++++++++++--------------- labelCloud/control/drawing_manager.py | 4 +- labelCloud/control/label_manager.py | 10 ++-- labelCloud/control/pcd_manager.py | 16 ++---- labelCloud/model/bbox.py | 10 ++-- labelCloud/utils/oglhelper.py | 5 +- labelCloud/view/gui.py | 13 ++--- labelCloud/view/viewer.py | 16 +++--- 11 files changed, 111 insertions(+), 108 deletions(-) diff --git a/labelCloud/control/alignmode.py b/labelCloud/control/alignmode.py index 6da4226..ef3583a 100644 --- a/labelCloud/control/alignmode.py +++ b/labelCloud/control/alignmode.py @@ -1,11 +1,14 @@ -"""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 @@ -123,8 +126,8 @@ def calculate_angles(self): np.cross(pn_normalized, z_axis) ) print( - "Alignment rotation: %s around %s" - % (round(rotation_angle, 2), np.round(rotation_axis, 2)) + f"Alignment rotation: {round(rotation_angle, 2)} " + f"around {np.round(rotation_axis, 2)}" ) # Initiate point cloud rotation diff --git a/labelCloud/control/bbox_controller.py b/labelCloud/control/bbox_controller.py index 83d5021..bc29a12 100644 --- a/labelCloud/control/bbox_controller.py +++ b/labelCloud/control/bbox_controller.py @@ -1,15 +1,17 @@ """ -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 @@ -17,7 +19,11 @@ # 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: @@ -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: @@ -209,9 +219,6 @@ def rotate_with_mouse( 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) @@ -251,9 +258,14 @@ def translate_along_z(self, distance: float = None, down: bool = False): 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 @@ -267,7 +279,7 @@ 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): + def select_bbox_by_ray(self, x: int, y: int) -> None: intersected_bbox_id = oglhelper.get_intersected_bboxes( x, y, @@ -281,26 +293,30 @@ def select_bbox_by_ray(self, x: int, y: int): # 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: diff --git a/labelCloud/control/config_manager.py b/labelCloud/control/config_manager.py index 653d933..70d23ca 100644 --- a/labelCloud/control/config_manager.py +++ b/labelCloud/control/config_manager.py @@ -1,12 +1,17 @@ """Load configuration from .ini file.""" import configparser -# Read local file `config.ini`. + import os -from typing import List, Union, Optional +from typing import List class ExtendedConfigParser(configparser.ConfigParser): + """Extends the ConfigParser with the ability to read and parse lists. + + Can automatically parse float values besides plain strings. + """ + def getlist(self, section, option, raw=False, vars=None, fallback=None) -> List: raw_value = self.get(section, option, raw=raw, vars=vars, fallback=fallback) if "," in raw_value: diff --git a/labelCloud/control/controller.py b/labelCloud/control/controller.py index 0d4406b..bf209b7 100644 --- a/labelCloud/control/controller.py +++ b/labelCloud/control/controller.py @@ -2,17 +2,19 @@ from PyQt5 import QtGui, QtCore -from control.alignmode import AlignMode -from control.drawing_manager import DrawingManager -from control.pcd_manager import PointCloudManger -from model.bbox import BBox -from control.bbox_controller import BoundingBoxController -from view.gui import GUI from utils import oglhelper +from model.bbox import BBox +from view.gui import GUI +from .alignmode import AlignMode +from .bbox_controller import BoundingBoxController +from .drawing_manager import DrawingManager +from .pcd_manager import PointCloudManger class Controller: + MOVEMENT_THRESHOLD = 0.1 + def __init__(self): """Initializes all controllers and managers.""" self.view: Union["GUI", None] = None @@ -172,12 +174,8 @@ def mouse_move_event(self, a0: QtGui.QMouseEvent): and (not self.align_mode.is_active()) ): if a0.buttons() & QtCore.Qt.LeftButton: # bbox rotation - # self.bbox_controller.rotate_around_x(-dy) - # self.bbox_controller.rotate_around_y(-dx) self.bbox_controller.rotate_with_mouse(-dx, -dy) elif a0.buttons() & QtCore.Qt.RightButton: # bbox translation - # self.bbox_controller.translate_along_x(-dx / 30) - # self.bbox_controller.translate_along_y(dy / 30) new_center = self.view.glWidget.get_world_coords( a0.x(), a0.y(), correction=True ) @@ -190,9 +188,8 @@ def mouse_move_event(self, a0: QtGui.QMouseEvent): self.pcd_manager.translate_along_x(dx) self.pcd_manager.translate_along_y(dy) - if ( - dx > 0.1 or dy > 0.1 - ): # Reset scroll locks if significant cursor movements + # Reset scroll locks of "side scrolling" for significant cursor movements + if dx > Controller.MOVEMENT_THRESHOLD or dy > Controller.MOVEMENT_THRESHOLD: if self.side_mode: self.side_mode = False else: @@ -243,45 +240,41 @@ def key_press_event(self, a0: QtGui.QKeyEvent): print("Resetted selected points!") # BBOX MANIPULATION - elif (a0.key() == QtCore.Qt.Key_Y) or ( - a0.key() == QtCore.Qt.Key_Comma - ): # z rotate counterclockwise + elif (a0.key() == QtCore.Qt.Key_Y) or (a0.key() == QtCore.Qt.Key_Comma): + # z rotate counterclockwise self.bbox_controller.rotate_around_z() - elif (a0.key() == QtCore.Qt.Key_X) or ( - a0.key() == QtCore.Qt.Key_Period - ): # z rotate clockwise + elif (a0.key() == QtCore.Qt.Key_X) or (a0.key() == QtCore.Qt.Key_Period): + # z rotate clockwise self.bbox_controller.rotate_around_z(clockwise=True) - elif a0.key() == QtCore.Qt.Key_C: # y rotate counterclockwise + elif a0.key() == QtCore.Qt.Key_C: + # y rotate counterclockwise self.bbox_controller.rotate_around_y() - elif a0.key() == QtCore.Qt.Key_V: # y rotate clockwise + elif a0.key() == QtCore.Qt.Key_V: + # y rotate clockwise self.bbox_controller.rotate_around_y(clockwise=True) - elif a0.key() == QtCore.Qt.Key_B: # x rotate counterclockwise + elif a0.key() == QtCore.Qt.Key_B: + # x rotate counterclockwise self.bbox_controller.rotate_around_x() - elif a0.key() == QtCore.Qt.Key_N: # x rotate clockwise + elif a0.key() == QtCore.Qt.Key_N: + # x rotate clockwise self.bbox_controller.rotate_around_x(clockwise=True) - elif (a0.key() == QtCore.Qt.Key_W) or ( - a0.key() == QtCore.Qt.Key_Up - ): # move backward + elif (a0.key() == QtCore.Qt.Key_W) or (a0.key() == QtCore.Qt.Key_Up): + # move backward self.bbox_controller.translate_along_y() - elif (a0.key() == QtCore.Qt.Key_S) or ( - a0.key() == QtCore.Qt.Key_Down - ): # move forward + elif (a0.key() == QtCore.Qt.Key_S) or (a0.key() == QtCore.Qt.Key_Down): + # move forward self.bbox_controller.translate_along_y(forward=True) - elif (a0.key() == QtCore.Qt.Key_A) or ( - a0.key() == QtCore.Qt.Key_Left - ): # move left + elif (a0.key() == QtCore.Qt.Key_A) or (a0.key() == QtCore.Qt.Key_Left): + # move left self.bbox_controller.translate_along_x(left=True) - elif (a0.key() == QtCore.Qt.Key_D) or ( - a0.key() == QtCore.Qt.Key_Right - ): # move right + elif (a0.key() == QtCore.Qt.Key_D) or (a0.key() == QtCore.Qt.Key_Right): + # move right self.bbox_controller.translate_along_x() - elif (a0.key() == QtCore.Qt.Key_Q) or ( - a0.key() == QtCore.Qt.Key_PageUp - ): # move up + elif (a0.key() == QtCore.Qt.Key_Q) or (a0.key() == QtCore.Qt.Key_PageUp): + # move up self.bbox_controller.translate_along_z() - elif (a0.key() == QtCore.Qt.Key_E) or ( - a0.key() == QtCore.Qt.Key_PageDown - ): # move down + elif (a0.key() == QtCore.Qt.Key_E) or (a0.key() == QtCore.Qt.Key_PageDown): + # move down self.bbox_controller.translate_along_z(down=True) def key_release_event(self, a0: QtGui.QKeyEvent): diff --git a/labelCloud/control/drawing_manager.py b/labelCloud/control/drawing_manager.py index 7c61016..18fc933 100644 --- a/labelCloud/control/drawing_manager.py +++ b/labelCloud/control/drawing_manager.py @@ -5,9 +5,9 @@ import utils.math3d as math3d import utils.oglhelper as ogl -from control.bbox_controller import BoundingBoxController -from control.config_manager import config from model.bbox import BBox +from .bbox_controller import BoundingBoxController +from .config_manager import config if TYPE_CHECKING: from view.gui import GUI diff --git a/labelCloud/control/label_manager.py b/labelCloud/control/label_manager.py index d072cd0..10fa592 100644 --- a/labelCloud/control/label_manager.py +++ b/labelCloud/control/label_manager.py @@ -6,9 +6,9 @@ import numpy as np -from utils import math3d -from control.config_manager import config from model.bbox import BBox +from utils import math3d +from .config_manager import config def get_label_strategy(export_format: str, label_folder: str) -> "IFormattingInterface": @@ -31,11 +31,9 @@ class LabelManager: "centroid_rel", "centroid_abs", "kitti", - ] # supported export formats + ] STD_LABEL_FORMAT = config.get("LABEL", "label_format") - EXPORT_PRECISION = config.getint( - "LABEL", "export_precision" - ) # Number of decimal places + EXPORT_PRECISION = config.getint("LABEL", "export_precision") def __init__( self, strategy: str = STD_LABEL_FORMAT, path_to_label_folder: str = None diff --git a/labelCloud/control/pcd_manager.py b/labelCloud/control/pcd_manager.py index 27399e5..81f470b 100644 --- a/labelCloud/control/pcd_manager.py +++ b/labelCloud/control/pcd_manager.py @@ -4,16 +4,16 @@ """ import ntpath import os +from shutil import copyfile from typing import List, Tuple, TYPE_CHECKING, Optional import numpy as np import open3d as o3d -from shutil import copyfile -from control.config_manager import config -from control.label_manager import LabelManager from model.bbox import BBox from model.point_cloud import PointCloud +from .config_manager import config +from .label_manager import LabelManager if TYPE_CHECKING: from view.gui import GUI @@ -247,8 +247,6 @@ def rotate_pointcloud( self.get_current_path(), os.path.join(originals_path, self.get_current_name()), ) - # o3d.io.write_point_cloud(os.path.join(originals_path, self.get_current_name()), self.current_o3d_pcd) - # Rotate and translate point cloud rotation_matrix = o3d.geometry.get_rotation_matrix_from_axis_angle( np.multiply(axis, angle) @@ -267,12 +265,6 @@ def rotate_pointcloud( center=(0, 0, 0), ) - # Overwrite current point cloud and reload - # if os.path.splitext(self.get_current_path())[1] == ".bin": - # points = np.asarray(self.current_o3d_pcd.points) - # flattened_points = np.append(points, np.zeros((len(points), 1)), axis=1).flatten() # add dummy reflection - # flattened_points.tofile(self.get_current_path()) - # else: save_path = self.get_current_path() if os.path.splitext(save_path)[1] == ".bin": save_path = save_path[:-4] + ".pcd" @@ -284,9 +276,7 @@ def rotate_pointcloud( def get_perspective(self): x_rotation = self.pointcloud.rot_x - # y_rotation = self.pcdc.get_pointcloud().rot_y z_rotation = self.pointcloud.rot_z - # print("PCD-ROTX/y/z: %s, %s, %s" % (x_rotation, y_rotation, z_rotation)) cosz = round(np.cos(np.deg2rad(z_rotation)), 1) sinz = -round(np.sin(np.deg2rad(z_rotation)), 1) diff --git a/labelCloud/model/bbox.py b/labelCloud/model/bbox.py index 902009d..a027b4a 100644 --- a/labelCloud/model/bbox.py +++ b/labelCloud/model/bbox.py @@ -9,14 +9,14 @@ class BBox: - # Defines the order in which the BBox edges are drawn + # order in which the bounding box edges are drawn BBOX_EDGES = [ (0, 1), (0, 3), (0, 4), (2, 1), (2, 3), - (2, 6), # lines to draw the bbox + (2, 6), (5, 1), (5, 4), (5, 6), @@ -24,11 +24,11 @@ class BBox: (7, 4), (7, 6), ] - + # vertices of each side BBOX_SIDES = { "top": [4, 5, 6, 7], "bottom": [0, 1, 2, 3], - "right": [2, 3, 7, 6], # vertices of each side + "right": [2, 3, 7, 6], "back": [0, 3, 7, 4], "left": [0, 1, 5, 4], "front": [1, 2, 6, 5], @@ -242,7 +242,7 @@ def translate_side(self, p_id_s, p_id_o, distance): def change_side(self, side, distance): # ToDo: Move to controller? if side == "right" and self.length + distance > BBox.MIN_DIMENSION: self.length += distance - self.translate_side(3, 0, distance) # TODO: Make dependen from side list + self.translate_side(3, 0, distance) # TODO: Make dependend from side list if side == "left" and self.length + distance > BBox.MIN_DIMENSION: self.length += distance self.translate_side(0, 3, distance) diff --git a/labelCloud/utils/oglhelper.py b/labelCloud/utils/oglhelper.py index ccfdd80..de4d2db 100644 --- a/labelCloud/utils/oglhelper.py +++ b/labelCloud/utils/oglhelper.py @@ -1,12 +1,11 @@ +from typing import List, Tuple, Union + import OpenGL.GL as GL import numpy as np from OpenGL import GLU -from typing import List, Tuple, Union from utils import math3d from model.bbox import BBox - -# DRAWING from model.point_cloud import PointCloud Color4f = Tuple[float, float, float, float] # type alias for type hinting diff --git a/labelCloud/view/gui.py b/labelCloud/view/gui.py index a44f00e..e7b038c 100644 --- a/labelCloud/view/gui.py +++ b/labelCloud/view/gui.py @@ -6,8 +6,8 @@ from PyQt5.QtWidgets import QCompleter, QFileDialog, QActionGroup, QAction, QMessageBox from control.config_manager import config -from view.settings_dialog import SettingsDialog -from view.viewer import GLWidget +from .settings_dialog import SettingsDialog +from .viewer import GLWidget if TYPE_CHECKING: from control.controller import Controller @@ -363,12 +363,13 @@ def show_no_pointcloud_dialog(self, pcd_folder: str, pcd_extensions: List[str]): msg = QMessageBox(self) msg.setIcon(QMessageBox.Warning) msg.setText( - f"labelCloud could not find any valid point cloud files inside the specified folder." + "labelCloud could not find any valid point cloud files inside the " + "specified folder." ) msg.setInformativeText( - f"Please copy all your point clouds into {pcd_folder} or change the point " - f"cloud folder location. labelCloud supports the following point cloud file formats:\n" - f"{', '.join(pcd_extensions)}." + f"Please copy all your point clouds into {pcd_folder} or change " + "the point cloud folder location. labelCloud supports the following point " + f"cloud file formats:\n {', '.join(pcd_extensions)}." ) msg.setWindowTitle("No Point Clouds Found") msg.exec_() diff --git a/labelCloud/view/viewer.py b/labelCloud/view/viewer.py index e7f8499..9d117b8 100644 --- a/labelCloud/view/viewer.py +++ b/labelCloud/view/viewer.py @@ -60,7 +60,8 @@ def initializeGL(self): GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA) print("Intialized widget.") - self.pcd_manager.get_pointcloud().write_vbo() # Must be written again, due to buffer clearing + # Must be written again, due to buffer clearing + self.pcd_manager.get_pointcloud().write_vbo() def resizeGL(self, width, height): print("Resized widget.") @@ -83,11 +84,9 @@ def paintGL(self): self.modelview = GL.glGetDoublev(GL.GL_MODELVIEW_MATRIX) self.projection = GL.glGetDoublev(GL.GL_PROJECTION_MATRIX) - GL.glDepthMask( - GL.GL_FALSE - ) # Do not write decoration and preview elements in depth buffer - # Draw floor net - # if self.draw_floor: + # Do not write decoration and preview elements in depth buffer + GL.glDepthMask(GL.GL_FALSE) + if config.getboolean("USER_INTERFACE", "show_floor"): oglhelper.draw_xy_plane(self.pcd_manager.get_pointcloud()) @@ -128,9 +127,8 @@ def get_world_coords( x *= self.DEVICE_PIXEL_RATIO # For fixing mac retina bug y *= self.DEVICE_PIXEL_RATIO - viewport = GL.glGetIntegerv( - GL.GL_VIEWPORT - ) # Stored projection matrices are taken from loop + # Stored projection matrices are taken from loop + viewport = GL.glGetIntegerv(GL.GL_VIEWPORT) real_y = viewport[3] - y # adjust for down-facing y positions if z is None: