Skip to content

Commit

Permalink
Add keyboard shortcuts for scaling, allow loading files recursively (#…
Browse files Browse the repository at this point in the history
…161)

* Add keyboard shortcuts for scaling

* Find pointclouds files recursively

* rm duplicate keys and improve readme

* rename size_increase to step

* apply black formatting and set pip to use correct black version

* Fix use in for single element, restore moved type:ignore
  • Loading branch information
adivardi authored Apr 30, 2024
1 parent 647f14d commit 0e49ce1
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 36 deletions.
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,16 @@ All rotations are counterclockwise (i.e. a z-rotation of 90°/π is from the pos
| `W`, `A`, `S`, `D` | Translates the Bounding Box back, left, front, right |
| `Ctrl` + Right Mouse Button | Translates the Bounding Box in all dimensions |
| `Q`, `E` | Lifts the Bounding Box up, down |
| `Z`, `X` | Rotates the Boundign Box around z-Axis |
| `C`, `V` | Rotates the Boundign Box around y-Axis |
| `B`, `N` | Rotates the Boundign Box around x-Axis |
| `Z`, `X` | Rotates the Bounding Box around z-Axis |
| `C`, `V` | Rotates the Bounding Box around y-Axis |
| `B`, `N` | Rotates the Bounding Box around x-Axis |
| `I`/ `O` | Increase/Decrease the Bounding Box length |
| `K`/ `L` | Increase/Decrease the Bounding Box width |
| `,`/ `.` | Increase/Decrease the Bounding Box height |
| Scrolling with the Cursor above a Bounding Box Side ("Side Pulling") | Changes the Dimension of the Bounding Box |
| `R`/`Left`, `F`/`Right` | Previous/Next sample |
| `T`/`Up`, `G`/`Down` | Previous/Next bbox |
| `Y`/`,`,`H`/`.` | Change current bbox class to previous/next in list |
| `Y`, `H` | Change current bbox class to previous/next in list |
| `1`-`9` | Select any of first 9 bboxes with number keys |
| *General* | |
| `Del` | Deletes Current Bounding Box |
Expand Down Expand Up @@ -150,11 +153,11 @@ If you are using the tool for a scientific project please consider citing our [p
author = {Christoph Sager and Patrick Zschech and Niklas Kuhl},
title = {{labelCloud}: A Lightweight Labeling Tool for Domain-Agnostic 3D Object Detection in Point Clouds},
journal = {Computer-Aided Design and Applications}
}
}

# CAD Conference
@misc{sager2021labelcloud,
title={labelCloud: A Lightweight Domain-Independent Labeling Tool for 3D Object Detection in Point Clouds},
title={labelCloud: A Lightweight Domain-Independent Labeling Tool for 3D Object Detection in Point Clouds},
author={Christoph Sager and Patrick Zschech and Niklas Kühl},
year={2021},
eprint={2103.04970},
Expand Down
23 changes: 16 additions & 7 deletions docs/shortcuts.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,25 @@ There are a number of shortcuts supported for frequently used actions.
| Shortcut | Description |
| :------------------------------------------------------------------: | ---------------------------------------------------- |
| *Navigation* | |
| Left Mouse Button | Rotates the Point Cloud |
| Right Mouse Button | Translates the Point Cloud |
| Left Mouse Button | Rotates the camera around Point Cloud centroid |
| Right Mouse Button | Translates the camera |
| Mouse Wheel | Zooms into the Point Cloud |
| *Correction* | |
| `W`, `A`, `S`, `D` <br> `Ctrl` + Right Mouse Button | Translates the Bounding Box back, left, front, right |
| `W`, `A`, `S`, `D` | Translates the Bounding Box back, left, front, right |
| `Ctrl` + Right Mouse Button | Translates the Bounding Box in all dimensions |
| `Q`, `E` | Lifts the Bounding Box up, down |
| `X`, `Y` | Rotates the Boundign Box around z-Axis |
| `Z`, `X` | Rotates the Bounding Box around z-Axis |
| `C`, `V` | Rotates the Bounding Box around y-Axis |
| `B`, `N` | Rotates the Bounding Box around x-Axis |
| `I`/ `O` | Increase/Decrease the Bounding Box length |
| `K`/ `L` | Increase/Decrease the Bounding Box width |
| `,`/ `.` | Increase/Decrease the Bounding Box height |
| Scrolling with the Cursor above a Bounding Box Side ("Side Pulling") | Changes the Dimension of the Bounding Box |
| `C` & `V`, `B` & `N` | Rotates the Bounding Box around y-Axis, x-Axis |
| `R`/`Left`, `F`/`Right` | Previous/Next sample |
| `T`/`Up`, `G`/`Down` | Previous/Next bbox |
| `Y`, `H` | Change current bbox class to previous/next in list |
| `1`-`9` | Select any of first 9 bboxes with number keys |
| *General* | |
| `Del` | Deletes Current Bounding Box |
| `R` | Resets Perspective |
| `Esc` | Cancels Selected Points |
| `P`/`Home` | Resets Perspective |
| `Esc` | Cancels Selected Points |
1 change: 1 addition & 0 deletions labelCloud/control/alignmode.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
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.
"""

import logging
from typing import TYPE_CHECKING, Optional

Expand Down
40 changes: 40 additions & 0 deletions labelCloud/control/bbox_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Bounding Box Management: adding, selecting updating, deleting bboxes;
Possible Active Bounding Box Manipulations: rotation, translation, scaling
"""

import logging
from functools import wraps
from typing import TYPE_CHECKING, List, Optional
Expand Down Expand Up @@ -296,6 +297,45 @@ def scale(

self.get_active_bbox().set_dimensions(new_length, new_width, new_height) # type: ignore

@has_active_bbox_decorator
def scale_along_length(
self, step: Optional[float] = None, decrease: bool = False
) -> None:
step = step or config.getfloat("LABEL", "std_scaling")
if decrease:
step *= -1

active_bbox: Bbox = self.get_active_bbox() # type: ignore
length, width, height = active_bbox.get_dimensions()
new_length = length + step
active_bbox.set_dimensions(new_length, width, height)

@has_active_bbox_decorator
def scale_along_width(
self, step: Optional[float] = None, decrease: bool = False
) -> None:
step = step or config.getfloat("LABEL", "std_scaling")
if decrease:
step *= -1

active_bbox: Bbox = self.get_active_bbox() # type: ignore
length, width, height = active_bbox.get_dimensions()
new_width = width + step
active_bbox.set_dimensions(length, new_width, height)

@has_active_bbox_decorator
def scale_along_height(
self, step: Optional[float] = None, decrease: bool = False
) -> None:
step = step or config.getfloat("LABEL", "std_scaling")
if decrease:
step *= -1

active_bbox: Bbox = self.get_active_bbox() # type: ignore
length, width, height = active_bbox.get_dimensions()
new_height = height + step
active_bbox.set_dimensions(length, width, new_height)

def select_bbox_by_ray(self, x: int, y: int) -> None:
intersected_bbox_id = oglhelper.get_intersected_bboxes(
x,
Expand Down
1 change: 1 addition & 0 deletions labelCloud/control/config_manager.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Load configuration from .ini file."""

import configparser
from pathlib import Path
from typing import List, Union
Expand Down
25 changes: 23 additions & 2 deletions labelCloud/control/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,27 @@ def key_press_event(self, a0: QtGui.QKeyEvent) -> None:
elif a0.key() == Keys.Key_E:
# move down
self.bbox_controller.translate_along_z(down=True)

# BBOX Scaling
elif a0.key() == Keys.Key_I:
# increase length
self.bbox_controller.scale_along_length()
elif a0.key() == Keys.Key_O:
# decrease length
self.bbox_controller.scale_along_length(decrease=True)
elif a0.key() == Keys.Key_K:
# increase width
self.bbox_controller.scale_along_width()
elif a0.key() == Keys.Key_L:
# decrease width
self.bbox_controller.scale_along_width(decrease=True)
elif a0.key() == Keys.Key_Comma:
# increase height
self.bbox_controller.scale_along_height()
elif a0.key() == Keys.Key_Period:
# decrease height
self.bbox_controller.scale_along_height(decrease=True)

elif a0.key() in [Keys.Key_R, Keys.Key_Left]:
# load previous sample
self.prev_pcd()
Expand All @@ -323,10 +344,10 @@ def key_press_event(self, a0: QtGui.QKeyEvent) -> None:
elif a0.key() in [Keys.Key_G, Keys.Key_Down]:
# select previous bbox
self.select_relative_bbox(1)
elif a0.key() in [Keys.Key_Y, Keys.Key_Comma]:
elif a0.key() == Keys.Key_Y:
# change bbox class to previous available class
self.select_relative_class(-1)
elif a0.key() in [Keys.Key_H, Keys.Key_Period]:
elif a0.key() == Keys.Key_H:
# change bbox class to next available class
self.select_relative_class(1)
elif a0.key() in list(range(49, 58)):
Expand Down
8 changes: 4 additions & 4 deletions labelCloud/control/pcd_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Module to manage the point clouds (loading, navigation, floor alignment).
Sets the point cloud and original point cloud path. Initiate the writing to the virtual object buffer.
"""

import logging
from pathlib import Path
from shutil import copyfile
Expand Down Expand Up @@ -41,9 +42,8 @@ def __init__(self) -> None:

# Point cloud control
self.pointcloud: Optional[PointCloud] = None
self.collected_object_classes: Set[
str
] = set() # TODO: this should integrate with the new label definition setup.
# TODO: this should integrate with the new label definition setup.
self.collected_object_classes: Set[str] = set()
self.saved_perspective: Optional[Perspective] = None

@property
Expand All @@ -60,7 +60,7 @@ def read_pointcloud_folder(self) -> None:
"""Checks point cloud folder and sets self.pcds to all valid point cloud file names."""
if self.pcd_folder.is_dir():
self.pcds = []
for file in sorted(self.pcd_folder.iterdir()):
for file in sorted(self.pcd_folder.rglob("*")):
if file.suffix in PointCloudManger.PCD_EXTENSIONS:
self.pcds.append(file)
else:
Expand Down
22 changes: 14 additions & 8 deletions labelCloud/model/point_cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,19 +378,25 @@ def print_details(self) -> None:
print_column(
[
"Number of Points:",
green(len(self.points))
if len(self.points) > 0
else red(len(self.points)),
(
green(len(self.points))
if len(self.points) > 0
else red(len(self.points))
),
]
)
print_column(
[
"Number of Colors:",
yellow("None")
if self.colorless
else green(len(self.colors)) # type: ignore
if len(self.colors) == len(self.points) # type: ignore
else red(len(self.colors)), # type: ignore
(
yellow("None")
if self.colorless
else (
green(len(self.colors)) # type: ignore
if len(self.colors) == len(self.points) # type: ignore
else red(len(self.colors)) # type: ignore
)
),
]
)
print_column(["Point Cloud Center:", str(np.round(self.center, 2))])
Expand Down
12 changes: 6 additions & 6 deletions labelCloud/utils/oglhelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
from ..model import BBox, PointCloud


DEVICE_PIXEL_RATIO: Optional[
float
] = None # is set once and for every window resize (retina display fix)
DEVICE_PIXEL_RATIO: Optional[float] = (
None # is set once and for every window resize (retina display fix)
)


def draw_points(
Expand Down Expand Up @@ -178,9 +178,9 @@ def get_intersected_sides(
p0, p1 = get_pick_ray(x, y, modelview, projection) # Calculate picking ray
vertices = bbox.get_vertices()

intersections: List[
Tuple[list, str]
] = list() # (intersection_point, bounding box side)
intersections: List[Tuple[list, str]] = (
list()
) # (intersection_point, bounding box side)
for side, indices in BBOX_SIDES.items():
# Calculate plane equation
pl1 = vertices[indices[0]] # point in plane
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ pytest~=7.3.1
pytest-qt~=4.2.0

# Development
black~=23.1.0
black>=23.1.0
mypy~=1.3.0
PyQt5-stubs~=5.15.6
types-setuptools~=67.8.0
types-pkg-resources~=0.1.3
types-pkg-resources~=0.1.3

0 comments on commit 0e49ce1

Please sign in to comment.