Skip to content

Commit

Permalink
Merge pull request #10 from ch-sa/gui-settings
Browse files Browse the repository at this point in the history
Add visual config editor
  • Loading branch information
ch-sa authored May 16, 2021
2 parents 971d03b + 1a72ed5 commit eb1c82c
Show file tree
Hide file tree
Showing 16 changed files with 1,120 additions and 244 deletions.
77 changes: 48 additions & 29 deletions config.ini
Original file line number Diff line number Diff line change
@@ -1,35 +1,54 @@
[FILE]
; Source of point clouds
POINTCLOUD_FOLDER = pointclouds/
; Sink for label files
LABEL_FOLDER = labels/
; source of point clouds
pointcloud_folder = pointclouds/
; sink for label files
label_folder = labels/

[POINTCLOUD]
; Drawing size for points in point cloud
POINT_SIZE = 4
; Point color for colorless point clouds (r,g,b)
COLORLESS_COLOR = 0.9, 0.9, 0.9
; Colerize colorless point clouds by height value
COLORLESS_COLORIZE = True
STD_TRANSLATION = 0.03
STD_ZOOM = 0.0025
; drawing size for points in point cloud
point_size = 4.0
; point color for colorless point clouds (r,g,b)
colorless_color = 0.9, 0.9, 0.9
; colerize colorless point clouds by height value
colorless_colorize = True
; standard step for point cloud translation (for mouse move)
std_translation = 0.03
; standard step for zooming (for scrolling)
std_zoom = 0.0025

[LABEL]
LABEL_FORMAT = centroid_abs
OBJECT_CLASSES = cart, box
STD_OBJECT_CLASS = cart
Z_ROTATION_ONLY = True
EXPORT_PRECISION = 8
VIEWING_PRECISION = 3
MIN_BOUNDINGBOX_DIMENSION = 0.01
STD_BOUNDINGBOX_LENGTH = 0.75
STD_BOUNDINGBOX_WIDTH = 0.55
STD_BOUNDINGBOX_HEIGHT = 0.15
STD_TRANSLATION = 0.03
STD_ROTATION = 0.5
STD_SCALING = 0.03
; format for exporting labels. choose from (vertices, centroid_rel, centroid_abs, kitti)
label_format = centroid_abs
; list of object classes for autocompletion in the text field
object_classes = cart, box
; default object class for new bounding boxes
std_object_class = cart
; number of decimal places for exporting the bounding box parameter.
export_precision = 8
; default length of the bounding box (for picking mode)
std_boundingbox_length = 0.75
; default width of the bounding box (for picking mode)
std_boundingbox_width = 0.55
; default height of the bounding box (for picking mode)
std_boundingbox_height = 0.15
; standard step for translating the bounding box with button or key (in meter)
std_translation = 0.03
; standard step for rotating the bounding box with button or key (in degree)
std_rotation = 0.5
; standard step for scaling the bounding box with button
std_scaling = 0.03
; minimum value for the length, width and height of a bounding box
min_boundingbox_dimension = 0.01

[USER_INTERFACE]
; only allow z-rotation of bounding boxes. set false to also label x- & y-rotation
z_rotation_only = True
; visualizes the pointcloud floor (x-y-plane) as a grid
show_floor = True
; visualizes the object's orientation with an arrow
show_orientation = True
; background color of the point cloud viewer (rgb)
background_color = 100, 100, 100
; number of decimal places shown for the parameters of the active bounding box
viewing_precision = 2

[SETTINGS]
BACKGROUND_COLOR = 100, 100, 100
SHOW_FLOOR = True
SHOW_ORIENTATION = True
45 changes: 23 additions & 22 deletions docs/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,28 +36,29 @@ The following parameters can be changed:
| Parameter | Description | Default/ Example |
| :---: | --- | :---: |
| **[FILE]** |
| `POINTCLOUD_FOLDER` | Source of point clouds | *pointclouds/* |
| `LABEL_FOLDER`| Sink for label files | *labels/* |
| `POINTCLOUD_FOLDER` | Folder from which the point cloud files are loaded. | *pointclouds/* |
| `LABEL_FOLDER`| Folder where the label files will be saved. | *labels/* |
| **[POINTCLOUD]** |
| `POINT_SIZE` | Drawing size for points in point cloud | *4* |
| `COLORLESS_COLOR` | Point color for colorless point clouds (r,g,b) | *0.9, 0.9, 0.9* |
| `COLORLESS_COLORIZE` | Colerize colorless point clouds by height value | *True* |
| `STD_TRANSLATION` | Standard step for point cloud translation | *0.03* |
| `STD_ZOOM` | Standard step for zooming | *0.0025* |
| `POINT_SIZE` | Drawing size for points in point cloud (rasterized diameter). | *4* |
| `COLORLESS_COLOR` | Point color for colorless point clouds (r,g,b). | *0.9, 0.9, 0.9* |
| `COLORLESS_COLORIZE` | Colerize colorless point clouds by height value. | *True* |
| `STD_TRANSLATION` | Standard step for point cloud translation (with mouse move). | *0.03* |
| `STD_ZOOM` | Standard step for zooming (with mouse scroll). | *0.0025* |
| **[LABEL]** |
| `LABEL_FORMAT` | Format for exporting labels. Choose from (`vertices`, `centroid_rel`, `centroid_abs`, `kitti`) | *centroid_abs* |
| `OBJECT_CLASSES` | List of object classes for autocompletion | *class1, class2, ...* |
| `STD_OBJECT_CLASS` | Default object class | *default_class* |
| `Z_ROTATION_ONLY` | Only allow z-rotation of bounding box. Deactivate to also label x- & y-rotation | *True* |
| `EXPORT_PRECISION` | Number of decimal places for export label. | *8* |
| `MIN_BOUNDINGBOX_DIMENSION` | Minimum bounding box dimension. | *0.01* |
| `STD_BOUNDINGBOX_LENGTH` | Default lenght of the bounding box (for picking mode) | *0.75* |
| `STD_BOUNDINGBOX_WIDTH` | Default width of the bounding box (for picking mode) | *0.55* |
| `STD_BOUNDINGBOX_HEIGHT`| Default height of the bounding box (for picking mode) | *0.15* |
| `STD_TRANSLATION`| Standard step for translating the bounding box | *0.03* |
| `STD_ROTATION` | Standard step for rotating the bounding box | *0.5* |
| `STD_SCALING` | Standard step for scaling the bounding box | *0.03* |
| `LABEL_FORMAT` | Format for exporting labels, choose from `vertices`, `centroid_rel`, `centroid_abs` or `kitti`. | *centroid_abs* |
| `OBJECT_CLASSES` | List of object classes for autocompletion in the class text field. | *class1, class2, ...* |
| `STD_OBJECT_CLASS` | Default object class for new bounding boxes. | *default_class* |
| `EXPORT_PRECISION` | Number of decimal places for exporting the bounding box parameters. | *8* |
| `STD_BOUNDINGBOX_LENGTH` | Default length of the bounding box (for picking mode). | *0.75* |
| `STD_BOUNDINGBOX_WIDTH` | Default width of the bounding box (for picking mode). | *0.55* |
| `STD_BOUNDINGBOX_HEIGHT`| Default height of the bounding box (for picking mode). | *0.15* |
| `STD_TRANSLATION`| Standard step for translating the bounding box (with key or button press). | *0.03* |
| `STD_ROTATION` | Standard step for rotating the bounding box (with key press). | *0.5* |
| `STD_SCALING` | Standard step for scaling the bounding box (with button press). | *0.03* |
| `MIN_BOUNDINGBOX_DIMENSION` | Minimum value for the length, width and height of a bounding box. | *0.01* |
| **[SETTINGS]** |
| `BACKGROUND_COLOR` | Background color of the point cloud viewer (rgb) | *100, 100, 100* |
| `SHOW_FLOOR` | Visualizes the floor (x-y-plane) as a grid | *True* |
| `SHOW_ORIENTATION` | Visualizes the object's orientation as an arrow | *True* |
| `Z_ROTATION_ONLY` | Only allow z-rotation of bounding box; deactivate to also label x- & y-rotation. | *True* |
| `SHOW_FLOOR` | Visualizes the floor (x-y-plane) as a grid. | *True* |
| `SHOW_ORIENTATION` | Visualizes the object's orientation as an arrow. | *True* |
| `BACKGROUND_COLOR` | Background color of the point cloud viewer (rgb). | *100, 100, 100* |
| `VIEWING_PRECISION` | Number of decimal places shown on the right side for the parameters of the active bounding box. | *3* |
46 changes: 23 additions & 23 deletions labelCloud/control/bbox_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import numpy as np

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

if TYPE_CHECKING:
Expand All @@ -22,29 +22,27 @@ def wrapper(*args, **kwargs): # plays function if active bbox exists
return func(*args, **kwargs)
else:
print("There is currently no active bounding box to manipulate.")

return wrapper


def only_zrotation_decorator(func):
def wrapper(*args, **kwargs): # plays function if active bbox exists
if not args[0].only_z_rotation:
if not config.getboolean("USER_INTERFACE", "z_rotation_only"):
return func(*args, **kwargs)
else:
print("Rotations around the x- or y-axis are not supported in this mode.")

return wrapper


class BoundingBoxController:
Z_ROTATION = config_parser.get_label_settings("Z_ROTATION_ONLY")
STD_TRANSLATION = config_parser.get_label_settings("STD_TRANSLATION")
STD_ROTATION = config_parser.get_label_settings("STD_ROTATION")
STD_SCALING = config_parser.get_label_settings("STD_SCALING")
STD_SCALING = config.getfloat("LABEL", "std_scaling")

def __init__(self):
self.view = None
self.bboxes = []
self.active_bbox_id = -1 # -1 means zero bboxes
self.only_z_rotation = BoundingBoxController.Z_ROTATION
self.pcdc = None

# GETTERS
Expand All @@ -71,24 +69,19 @@ def get_classname(self):

def set_view(self, view: 'GUI'):
self.view = view
self.view.action_zrotation.setChecked(self.only_z_rotation)

def set_rotation_mode(self, state: bool):
self.only_z_rotation = state
print("Z-rotation only mode set to %s." % state)

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")

def update_bbox(self, bbox_id, bbox):
def update_bbox(self, bbox_id: int, bbox: BBox):
if isinstance(bbox, BBox) and (0 <= bbox_id < len(self.bboxes)):
self.bboxes[bbox_id] = bbox
self.update_label_list()

def delete_bbox(self, bbox_id):
def delete_bbox(self, bbox_id: int):
if 0 <= bbox_id < len(self.bboxes):
del self.bboxes[bbox_id]
if bbox_id == self.active_bbox_id:
Expand All @@ -100,7 +93,7 @@ def delete_current_bbox(self):
selected_item_id = self.view.label_list.currentRow()
self.delete_bbox(selected_item_id)

def set_active_bbox(self, bbox_id):
def set_active_bbox(self, bbox_id: int):
if 0 <= bbox_id < len(self.bboxes):
self.active_bbox_id = bbox_id
self.update_all()
Expand All @@ -114,7 +107,7 @@ def set_classname(self, new_class: str):
self.update_label_list()

@has_active_bbox_decorator
def set_center(self, cx, cy, cz):
def set_center(self, cx: float, cy: float, cz: float):
self.get_active_bbox().center = (cx, cy, cz)

def set_bboxes(self, bboxes: List[BBox]):
Expand Down Expand Up @@ -167,20 +160,23 @@ def update_rotation(self, axis: str, value: float):

@only_zrotation_decorator
@has_active_bbox_decorator
def rotate_around_x(self, dangle=STD_ROTATION, clockwise=False):
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)

@only_zrotation_decorator
@has_active_bbox_decorator
def rotate_around_y(self, dangle=STD_ROTATION, clockwise=False):
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)

@has_active_bbox_decorator
def rotate_around_z(self, dangle=STD_ROTATION, clockwise=False, absolute=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:
Expand All @@ -207,30 +203,34 @@ def rotate_with_mouse(self, x_angle: float, y_angle: float): # TODO: Make more
self.rotate_around_z(x_angle)

@has_active_bbox_decorator
def translate_along_x(self, distance=STD_TRANSLATION, left=False):
def translate_along_x(self, distance: float = None, left: bool = False):
distance = distance or config.getfloat("LABEL", "std_translation")
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)

@has_active_bbox_decorator
def translate_along_y(self, distance=STD_TRANSLATION, forward=False):
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)

@has_active_bbox_decorator
def translate_along_z(self, distance=STD_TRANSLATION, down=False):
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)

# Scale bbox while keeping ratios
@has_active_bbox_decorator
def scale(self, length_increase=STD_SCALING, decrease=False):
def scale(self, length_increase: float = None, decrease: bool = False):
length_increase = length_increase or config.getfloat("LABEL", "std_scaling")
if decrease:
length_increase *= -1
length, width, height = self.get_active_bbox().get_dimensions()
Expand Down
48 changes: 48 additions & 0 deletions labelCloud/control/config_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""Load configuration from .ini file."""
import configparser

# Read local file `config.ini`.
import os
from typing import List, Union, Optional


class ExtendedConfigParser(configparser.ConfigParser):

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:
values = [x.strip() for x in raw_value.split(',')]
try:
return [float(item) for item in values]
except ValueError:
return values
return raw_value


class ConfigManager(object):
PATH_TO_CONFIG = "config.ini"
PATH_TO_DEFAULT_CONFIG = "ressources/default_config.ini"

def __init__(self):
self.config = ExtendedConfigParser(comment_prefixes='/', allow_no_value=True)
self.read_from_file()

def read_from_file(self):
if os.path.isfile(ConfigManager.PATH_TO_CONFIG):
self.config.read(ConfigManager.PATH_TO_CONFIG)
else:
self.config.read(ConfigManager.PATH_TO_DEFAULT_CONFIG)

def write_into_file(self):
with open(ConfigManager.PATH_TO_CONFIG, 'w') as configfile:
self.config.write(configfile, space_around_delimiters=True)

def reset_to_default(self):
self.config.read(ConfigManager.PATH_TO_DEFAULT_CONFIG)

def get_file_settings(self, key: str) -> str:
return self.config["FILE"][key]


config_manager = ConfigManager()
config = config_manager.config
Loading

0 comments on commit eb1c82c

Please sign in to comment.