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

Crop the points inside a box and save as as a separate point cloud #129

Merged
merged 10 commits into from
Feb 2, 2023
1 change: 0 additions & 1 deletion labelCloud.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from labelCloud.__main__ import main

if __name__ == "__main__":

main()
11 changes: 11 additions & 0 deletions labelCloud/control/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,3 +315,14 @@ def key_release_event(self, a0: QtGui.QKeyEvent) -> None:
if a0.key() == QtCore.Qt.Key_Control:
self.ctrl_pressed = False
self.view.status_manager.clear_message(Context.CONTROL_PRESSED)

def crop_pointcloud_inside_active_bbox(self) -> None:
bbox = self.bbox_controller.get_active_bbox()
assert bbox is not None
assert self.pcd_manager.pointcloud is not None
points_inside = bbox.is_inside(self.pcd_manager.pointcloud.points)
pointcloud = self.pcd_manager.pointcloud.get_filtered_pointcloud(points_inside)
if pointcloud is None:
logging.warning("No points found inside the box. Ignored.")
return
self.view.save_point_cloud_as(pointcloud)
3 changes: 1 addition & 2 deletions labelCloud/control/pcd_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
from typing import TYPE_CHECKING, List, Optional, Set, Tuple

import numpy as np
import pkg_resources

import open3d as o3d
import pkg_resources

from ..definitions.types import LabelingMode, Point3D
from ..io.labels.config import LabelConfig
Expand Down
1 change: 0 additions & 1 deletion labelCloud/io/labels/kitti.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ def import_labels(self, pcd_path: Path) -> List[BBox]:

label_path = self.label_folder.joinpath(pcd_path.stem + self.FILE_ENDING)
if label_path.is_file():

with label_path.open("r") as read_file:
label_lines = read_file.readlines()

Expand Down
2 changes: 0 additions & 2 deletions labelCloud/model/bbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@


class BBox(object):

MIN_DIMENSION: float = config.getfloat("LABEL", "MIN_BOUNDINGBOX_DIMENSION")
HIGHLIGHTED_COLOR: Color3f = Color3f(0, 1, 0)

Expand Down Expand Up @@ -255,7 +254,6 @@ def change_side(
self.translate_side(0, 4, distance)

def is_inside(self, points: npt.NDArray[np.float32]) -> npt.NDArray[np.bool_]:

vertices = self.get_vertices().copy()

# .------------.
Expand Down
19 changes: 19 additions & 0 deletions labelCloud/model/point_cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,25 @@ def reset_perspective(self) -> None:
self.trans_x, self.trans_y, self.trans_z = self.init_rotation
self.rot_x, self.rot_y, self.rot_z = self.init_rotation

def get_filtered_pointcloud(
self, indicies: npt.NDArray[np.bool_]
) -> Optional["PointCloud"]:
assert self.points is not None
assert self.colors is not None
points = self.points[indicies]
if points.shape[0] == 0:
return None
colors = self.colors[indicies]
labels = self.labels[indicies] if self.labels is not None else None
path = self.path.parent / (self.path.stem + "_cropped" + self.path.suffix)
return PointCloud(
path=path,
points=points,
colors=colors,
segmentation_labels=labels,
write_buffer=False,
)

def print_details(self) -> None:
print_column(
[
Expand Down
2 changes: 0 additions & 2 deletions labelCloud/tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ def pytest_configure(config):

@pytest.fixture
def startup_pyqt(qtbot, qapp, monkeypatch):

# Setup Model-View-Control structure
control = Controller()

Expand All @@ -39,5 +38,4 @@ def startup_pyqt(qtbot, qapp, monkeypatch):

@pytest.fixture
def bbox():

return BBox(cx=0, cy=0, cz=0, length=3, width=2, height=1)
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ def test_create_labels(handler: NumpySegmentationHandler) -> None:


def test_write_labels(handler: NumpySegmentationHandler) -> None:

labels = np.random.randint(low=0, high=4, size=(420,), dtype=np.int8)
with tempfile.TemporaryDirectory() as tempdir:
label_path = Path(tempdir) / Path("foo.bin")
Expand Down
43 changes: 42 additions & 1 deletion labelCloud/view/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
import re
import sys
import traceback
from pathlib import Path
from typing import TYPE_CHECKING, Optional, Set

Expand All @@ -22,7 +23,9 @@
from ..control.config_manager import config
from ..definitions.types import Color3f, LabelingMode
from ..io.labels.config import LabelConfig
from ..io.pointclouds import BasePointCloudHandler
from ..labeling_strategies import PickingStrategy, SpanningStrategy
from ..model.point_cloud import PointCloud
from .settings_dialog import SettingsDialog # type: ignore
from .startup_dialog import StartupDialog
from .status_manager import StatusManager
Expand Down Expand Up @@ -201,7 +204,14 @@ def __init__(self, control: "Controller") -> None:
# self.act_rename_class = QtWidgets.QAction("Rename class") #TODO: Implement!
self.act_change_class_color = QtWidgets.QAction("Change class color")
self.act_delete_class = QtWidgets.QAction("Delete label")
self.label_list.addActions([self.act_change_class_color, self.act_delete_class])
self.act_crop_pointcloud_inside = QtWidgets.QAction("Save points inside as")
self.label_list.addActions(
[
self.act_change_class_color,
self.act_delete_class,
self.act_crop_pointcloud_inside,
]
)
self.label_list.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)

# BOUNDING BOX PARAMETER EDITS
Expand Down Expand Up @@ -316,6 +326,9 @@ def connect_events(self) -> None:
self.act_delete_class.triggered.connect(
self.controller.bbox_controller.delete_current_bbox
)
self.act_crop_pointcloud_inside.triggered.connect(
self.controller.crop_pointcloud_inside_active_bbox
)
self.act_change_class_color.triggered.connect(self.change_label_color)

# open_2D_img
Expand Down Expand Up @@ -658,3 +671,31 @@ def change_label_color(self):
LabelConfig().set_class_color(
bbox.classname, Color3f.from_qcolor(QColorDialog.getColor())
)

@staticmethod
def save_point_cloud_as(pointcloud: PointCloud) -> None:
extensions = BasePointCloudHandler.get_supported_extensions()
make_filter = " ".join(["*" + extension for extension in extensions])
file_filter = f"Point Cloud File ({make_filter})"
file_name, _ = QFileDialog.getSaveFileName(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice with the selectable extensions 👍

caption="Select a file name to save the point cloud",
directory=str(pointcloud.path.parent),
filter=file_filter,
initialFilter=file_filter,
)
if file_name == "":
logging.warning("No file path provided. Ignored.")
return

try:
path = Path(file_name)
handler = BasePointCloudHandler.get_handler(path.suffix)
handler.write_point_cloud(path, pointcloud)
except Exception as e:
msg = QMessageBox()
msg.setWindowTitle("Failed to save a point cloud")
msg.setText(e.__class__.__name__)
msg.setInformativeText(traceback.format_exc())
msg.setIcon(QMessageBox.Critical)
msg.setStandardButtons(QMessageBox.Cancel)
msg.exec_()
3 changes: 1 addition & 2 deletions labelCloud/view/viewer.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import logging
from typing import Optional, Tuple, Union

from PyQt5 import QtGui, QtOpenGL

import numpy as np
import numpy.typing as npt
import OpenGL.GL as GL
from OpenGL import GLU
from PyQt5 import QtGui, QtOpenGL

from ..control.alignmode import AlignMode
from ..control.bbox_controller import BoundingBoxController
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pytest~=7.1.2
pytest-qt~=4.1.0

# Development
black~=22.3.0
black~=23.1.0
Copy link
Collaborator Author

@chingyulin chingyulin Feb 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ch-sa FYI black released a new major version yesterday and that is used in the Github Action. Update black in requirements.txt to align with Github Action.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to know 👍

mypy~=0.971
PyQt5-stubs~=5.15.6
types-setuptools~=57.4.17
Expand Down