A geometry-only pipeline that segments indoor 3D point clouds into semantic components — floor, ceiling, walls, and furniture — using RANSAC plane fitting and DBSCAN clustering. No deep learning, no pretrained models.
Given a .ply or .pcd scan of an indoor room, this pipeline:
- Preprocesses the point cloud (denoise + voxel downsample)
- Iteratively fits planes via RANSAC, labelling each as floor, ceiling, or wall
- Clusters remaining points with DBSCAN to identify furniture
- Generates outputs — colour-coded point cloud, bounding box report, and a 2D floor-plan PNG
All labelling is purely rule-based (height, orientation, size) — no training data required.
| Floor (Blue) | Ceiling (White) | Walls (Red) | Furniture (Green) |
|---|
Sample run on Area_1_office_14_sample35:
After preprocessing : 4,295 points
Clusters found : 6
Cluster 0 (724 pts) → CEILING — 1.44m × 0.78m × 0.09m
Cluster 1 (669 pts) → FLOOR — 0.39m × 0.83m × 0.07m
Cluster 2 (669 pts) → WALL — 0.92m × 0.67m × 0.97m
Cluster 3 (497 pts) → CEILING — 1.47m × 1.15m × 0.05m
Cluster 4 (316 pts) → WALL — 1.55m × 1.40m × 0.41m
Cluster 5 ( 82 pts) → FURNITURE — 0.24m × 0.14m × 0.07m
Runtime: 0.09s
3d-room-segmentation/
├── main.py # CLI entry point
├── preprocessing.py # Load, denoise, voxel-downsample
├── plane_fitting.py # Iterative RANSAC plane extraction
├── clustering.py # DBSCAN clustering on residual points
├── semantic_labeling.py # Rule-based label assignment
├── bounding_box.py # AABB computation & report generation
├── cluster_merging.py # Post-processing: merge adjacent same-label clusters
├── visualization.py # Colour coding, PLY export, viewer, PNG floor-plan
├── utils.py # Label constants, colour map, helpers
├── merge_fragments.py # Utility: merge Open3D demo scan fragments
└── requirements.txt
Requires Python 3.11 — Open3D does not yet support 3.12/3.13.
python3.11 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtDependencies: numpy, scikit-learn, open3d, plyfile, Pillow
This pipeline is built for the VX-S3DIS dataset (Stanford 3D Indoor Spaces).
Download:
- Create a free account at huggingface.co
- Request access at the dataset page linked above
- Download
global_view.zipand extract:
mkdir -p data/s3dis_raw/s3dis
7z x global_view.zip -o./data/s3dis_raw/s3disAny .ply file from an indoor 3D scan will also work.
source .venv/bin/activate
python main.py ./data/s3dis_raw/s3dis/Area_1_office_14_sample35_simCtr_385.ply \
--eps 0.05 --min_samples 10 --voxel_size 0.02 --output_dir ./outputView the 3D result interactively:
python -c "
import open3d as o3d
pcd = o3d.io.read_point_cloud('./output/Area_1_office_14_sample35_simCtr_385_segmented.ply')
o3d.visualization.draw_geometries([pcd], window_name='Segmented Room', width=1280, height=800)
"Or pass --visualize to open the viewer automatically after processing.
Input PLY/PCD
│
▼
1. Preprocessing
├── Statistical outlier removal
└── Voxel downsampling (default 0.02 m)
│
▼
2. Iterative RANSAC Plane Segmentation
├── Extract dominant planes one by one
└── Classify each: FLOOR / CEILING / WALL
(planes with z_range > 0.3 m → reclassified as WALL)
│
▼
3. DBSCAN on Remaining Points
└── Non-planar residual → FURNITURE clusters
│
▼
4. Degenerate Cluster Filtering
├── Drop clusters with any dimension < 1.5 cm
└── Drop clusters with negative Z centroid
│
▼
5. Output Generation
├── Colour-coded segmented .ply
├── Bounding box report (.txt + .json)
└── 2D top-down floor-plan (.png)
| Label | Rule |
|---|---|
| FLOOR | Horizontal plane (nz ≥ 0.7), thin (z_range < 0.3 m), mean Z near ground |
| CEILING | Horizontal plane (nz ≥ 0.7), thin (z_range < 0.3 m), mean Z near room top |
| WALL | Vertical plane (nz < 0.7) or thick horizontal plane (z_range ≥ 0.3 m) |
| FURNITURE | Non-planar DBSCAN clusters (tables, chairs, shelves, monitors, …) |
python main.py <input_file> [options]
| Argument | Default | Description |
|---|---|---|
input_file |
— | Path to .ply or .pcd file |
--output_dir |
./output |
Output directory |
--voxel_size |
0.05 |
Downsampling voxel size (metres) |
--eps |
0.1 |
DBSCAN neighbourhood radius |
--min_samples |
5 |
DBSCAN min points per core point |
--min_cluster_size |
75 |
Drop clusters smaller than this |
--visualize |
off | Open interactive 3D viewer after run |
--merge_clusters |
off | Merge adjacent same-label clusters |
--merge_distance |
0.5 |
Max centroid distance for merging (metres) |
--no_json |
off | Skip JSON report |
--no_png |
off | Skip floor-plan PNG |
| File | Description |
|---|---|
*_segmented.ply |
Colour-coded point cloud — open in CloudCompare or MeshLab |
*_bbox_report.txt |
Human-readable bounding box report with dimensions and centroids |
*_bbox_report.json |
Machine-readable JSON version of the same report |
*_floorplan.png |
2D top-down projection of the segmented scene |
MIT