Skip to content
Snippets Groups Projects
Commit 212820a1 authored by Murat Ambarkutuk's avatar Murat Ambarkutuk :robot:
Browse files

initial work

parent 40285a48
1 merge request!1Dependency issue
.env 0 → 100644
DISPLAY=:1
UID=1000
GID=1000
DOCKER_ID=eroniki
DOCKER_TAG=0.0.1
MPLCONFIGDIR=/tmp/matplotlib_cache
\ No newline at end of file
# Use the official Python image as the base image
FROM python:3.9-slim
RUN apt-get update -y
RUN apt-get install -y libx11-dev
RUN apt-get install -y python3-tk
# Set the working directory in the container
WORKDIR /app
RUN pip install pip --upgrade
# Copy the Python script into the container
COPY requirements.txt /tmp/requirements.txt
RUN pip install --no-cache-dir -r /tmp/requirements.txt
# Run the Python script
# CMD ["python", "plot.py"]
CMD ["/bin/bash"]
\ No newline at end of file
Makefile 0 → 100644
SHELL=/bin/bash
env_file = .env
include ${env_file}
.PHONY: clean up down restart stop reload ps logs logsf push env
all: reload
default: all
unsupervised-segmentation:
docker exec -it $@ bash
clean: pyclean prune
up:
@echo "Starting up containers for $(PROJECT_NAME)..."
docker-compose up --build --detach --remove-orphans
down:
@echo "Removing containers."
docker-compose down --remove-orphans
restart:
@echo "Restarting containers."
docker-compose restart
stop:
@echo "Stopping containers for $(PROJECT_NAME)..."
docker-compose stop
reload: reload_env reload_conts
reload_conts: stop down up
reload_env:
@echo "Reloading the environment variables."
@source ${env_file}
ps:
@docker ps --filter name="$(PROJECT_NAME)*"
logs:
@echo "Displaying past containers logs"
docker-compose logs
logsf:
@echo "Follow containers logs output"
docker-compose logs -f
push:
@docker login
@docker-compose push
pyclean:
@sudo find . -regex '^.*\(__pycache__\|\.py[co]\)$$' -delete
prune: prune_conts prune_vols
prune_conts:
docker system prune --all --force
prune_vols:
docker volume prune --force
...@@ -91,3 +91,56 @@ For open source projects, say how it is licensed. ...@@ -91,3 +91,56 @@ For open source projects, say how it is licensed.
## Project status ## Project status
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
# Unsupervised Segmentation
## Problem Description
You have a LiDAR scanning the environment and you received a pointcloud with each point having attributes (x, y, z) in sensor coordinates.
The goal is to segment the pointcloud so as to identify the objects in the scene.
- Each segment should be assigned a unique ID.
- All points belonging to a segment should share the same ID as the segment.
- Each point should be associated with only one segment.
## Things to Consider
- Consider only the points that don't belong to the ground plane for the segmentation task.
## Implementation Details
- Folder `data` contains a pointcloud data.
- File `implementation.py` has been setup to load pointcloud from folder `data` into a numpy array and visualization has been provided to visualize how input 3D pointcloud looks like.
## Expectation
- It is expected that the core-parts of the algorithm to be written from scratch.
- 3rd-party libraries can be used for parts of the algorithm, like Linear Algebra routines, optimization routines, etc.
- Provide explanation as to why a specific algorithm has been selected over others methods.
## Example
Input:
```python
pointcloud = [
[0.0, 0.0, 0.0], # Point-1
[2.0, 2.0, 2.0], # Point-2
[2.16, 4.89, 5.78], # Point-3
[0.01, 0.1, 0.03], # Point-4
# ...
]
```
Output:
```python
segment_ids = [0, # id = 0 as Point-1 belongs to segment-0
1, # id = 1 as Point-2 belongs to segment-1
2, # id = 2 as Point-3 belongs to segment-2
0, # id = 0 as Point-4 belongs to segment-0
#...
]
```
`len(segment_ids)` is same as `len(pointcloud)`
File added
version: '3.8'
x-service: &service
init: true
tty: true
stdin_open: true
user: "${UID}:${GID}"
environment:
- DISPLAY=${DISPLAY}
- MPLCONFIGDIR=${MPLCONFIGDIR}
volumes:
- .:/app
- /tmp/.X11-unix:/tmp/.X11-unix:rw
- /etc/group:/etc/group:ro
- /etc/passwd:/etc/passwd:ro
- /etc/shadow:/etc/shadow:ro
- /etc/sudoers.d:/etc/sudoers.d:ro
services:
unsupervised-segmentation:
<<: *service
build: .
image: "${DOCKER_ID}/unsupervised-segmentation:${DOCKER_TAG}"
container_name: "unsupervised-segmentation"
#%% imports
import matplotlib
matplotlib.use('TkAgg')
import numpy as np
from enum import IntEnum
import matplotlib.pyplot as plt
from segmentation.base import BaseSegmenter
from segmentation.segmentation import SurfaceSegmenter
#%% types
class Index(IntEnum):
X = 0
Y = 1
Z = 2
#%% helper functions
def visualize_pointcloud_downsampled(pc:np.ndarray, downsample_factor:int=10) -> None:
fig = plt.figure(figsize=(25, 25))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(pc[::downsample_factor, Index.X],
pc[::downsample_factor, Index.Y],
pc[::downsample_factor, Index.Z],
color="red", s=0.1)
ax.set_xlabel("x (m)", fontsize=14)
ax.set_ylabel("y (m)", fontsize=14)
ax.set_zlabel("z (m)", fontsize=14)
ax.set_xlim(-20, 20)
ax.set_ylim(-20, 20)
ax.set_zlim(-20, 50)
ax.set_title("Pointcloud (3D)", fontsize=14)
plt.show()
# make this plot occupy 30% of the figure's width and 100% of its height
plt.figure(figsize=(25, 25))
plt.plot(pc[:, Index.X], pc[:, Index.Y], "rx", markersize=1, alpha=0.2)
plt.xlabel("x (m)", fontsize=14)
plt.ylabel("y (m)", fontsize=14)
plt.grid(True)
plt.gca().set_aspect('equal', adjustable='box')
plt.title("Pointcloud (Top View)", fontsize=14)
plt.show()
def visualize_pointcloud_downsampled_with_segment_ids(pc: np.ndarray, segment_ids: np.ndarray,
downsample_factor:int=10) -> None:
fig = plt.figure(figsize=(25, 25))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(pc[::downsample_factor, Index.X],
pc[::downsample_factor, Index.Y],
pc[::downsample_factor, Index.Z],
c=segment_ids[::downsample_factor],
cmap="tab20",
s=0.2)
ax.set_xlabel("x (m)", fontsize=14)
ax.set_ylabel("y (m)", fontsize=14)
ax.set_zlabel("z (m)", fontsize=14)
ax.set_xlim(-20, 20)
ax.set_ylim(-20, 20)
ax.set_zlim(-20, 50)
ax.set_title("Pointcloud (3D)", fontsize=14)
plt.show()
# make this plot occupy 30% of the figure's width and 100% of its height
plt.figure(figsize=(25, 25))
plt.scatter(pc[:, Index.X], pc[:, Index.Y], c=segment_ids, cmap="tab20", s=1, alpha=0.5)
plt.xlabel("x (m)", fontsize=14)
plt.ylabel("y (m)", fontsize=14)
plt.grid(True)
plt.gca().set_aspect('equal', adjustable='box')
plt.title("Pointcloud (Top View)", fontsize=14)
plt.show()
#%%
pointcloud = np.load("data/cyngn_interview_question_pointcloud_data.npy")
visualize_pointcloud_downsampled(pointcloud, downsample_factor=5) # use 'downsample_factor=1' for no downsampling during visualization
##### TODO: REQUIRES IMPLEMENTATION ##############################
##################################################################
def segment_pointcloud(pointcloud:np.ndarray) -> np.ndarray:
# input is a pointcloud of shape (N, 3)
awesome_segmenter = SurfaceSegmenter()
mask_surfaces = awesome_segmenter.predict(pointcloud)
mask = mask_surfaces
# output is a segmentation mask of shape (N,)
# where each element is an integer representing the segment id
return np.array(mask)
segment_ids = segment_pointcloud(pointcloud)
visualize_pointcloud_downsampled_with_segment_ids(pointcloud, segment_ids, downsample_factor=5) # use 'downsample_factor=1' for no downsampling during visualization
# %%
{
"folders": [
{
"path": "."
}
],
"settings": {
"terminal.integrated.gpuAcceleration": "off",
"editor.wordWrap": "on",
"latex-workshop.latex.recipe.default": "lastUsed",
"latex-workshop.latex.autoBuild.run": "never",
"latex-workshop.latex.recipes": [
{
"name": "magic 🐟",
"tools": [
"pdflatex",
"bibtex",
"pdflatex",
"makeglossaries",
// "makeindex",
"pdflatex"
]
}
],
"files.associations": {
"*.tikz": "latex",
"*.table": "latex",
"*.figure": "latex"
},
"latex-workshop.latex.tools": [
{
"name": "makeindex",
"command": "makeindex",
"args": [
"%DOCFILE%.nlo",
"-s",
"nomencl.ist",
"-o",
"%DOCFILE%.nls"
]
},
{
"name": "pdflatex",
"command": "pdflatex",
"args": [
"-synctex=1",
"-interaction=nonstopmode",
"-file-line-error",
"%DOC%"
],
"env": {}
},
{
"name": "bibtex",
"command": "bibtex",
"args": [
"%DOCFILE%"
],
"env": {}
},
{
"name": "makeglossaries",
"command": "makeglossaries",
"args": [
"%DOCFILE%"
]
}
],
},
"tasks": {
"version": "2.0.0",
"tasks": []
}
}
numpy==1.26.4
scipy==1.12.0
matplotlib==3.8.3
seaborn==0.13.2
scikit-learn==1.4.1.post1
open3d
\ No newline at end of file
File added
File added
File added
import numpy as np
import sklearn as sk
from sklearn.cluster import DBSCAN
class BaseSegmenter(sk.base.BaseEstimator):
def __init__(self, param=1):
super().__init__()
self.param = param
def fit(self, X, y=None):
raise NotImplementedError("In case we want to try some supervised method")
def predict(self, point_cloud):
dbscan = DBSCAN(eps=0.2, min_samples=5)
clusters = dbscan.fit_predict(point_cloud)
return clusters
if __name__ == '__main__':
pass
\ No newline at end of file
import numpy as np
from segmentation.base import BaseSegmenter
import open3d as o3d
class SurfaceSegmenter(BaseSegmenter):
def __init__(self, param=1):
super().__init__(param)
def fit(self, X, y=None):
raise NotImplementedError("In case we want to try some supervised method")
def predict(self, point_cloud):
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(point_cloud)
# Use RANSAC to find a plane in the point cloud
plane_model, inliers = pcd.segment_plane(distance_threshold=0.01,
ransac_n=3,
num_iterations=1000)
inlier_cloud = pcd.select_by_index(inliers)
# Visualize the inlier points
o3d.visualization.draw_geometries([inlier_cloud])
return inlier_cloud
\ No newline at end of file
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment