Speckle-Scanner — 3D Scanning Pipeline
A modular pipeline for stereo 3D scanning: camera calibration → image rectification → disparity computation → point cloud generation → point cloud colouring.
Quick start — clone and prepare your machine
1. Clone into home
The pipeline expects this repository at ~/Speckle-Scanner. All paths in config.py are anchored to your home directory.
cd ~
git clone https://gitea.subseascanning.com/AsadUllah/Speckle-Scanner.git
This creates ~/Speckle-Scanner/ with the code only — no scan or calibration data is included in the repo.
2. Create the required data folders (siblings of the repo)
Three folders must exist next to Speckle-Scanner under ~/. They are not part of the git repository; you create or copy data into them locally.
mkdir -p ~/3D-Scans ~/Calib-data
# ~/Speckle-Scanner_Processing_data/ is created automatically by rectification (step 2)
| Folder | Required? | Purpose | You provide |
|---|---|---|---|
~/Speckle-Scanner/ |
yes (clone) | Pipeline source code, scripts, READMEs | git clone |
~/3D-Scans/ |
yes | Raw stereo scan images | Copy/mount your project data |
~/Calib-data/ |
yes | Calibration chessboard images | Copy/mount calibration sessions |
~/Speckle-Scanner_Processing_data/ |
auto | Rectified images, disparity, point clouds | Created by step 2 (rectification) |
Layout after clone:
~/
├── Speckle-Scanner/ ← git clone (this repo)
├── 3D-Scans/ ← you create + add raw scans
│ └── <project>/<date>/<session>/<scan>/01_raw_images/
├── Calib-data/ ← you create + add calibration images
│ └── <project>/<date>/<calib_name>/{lc,rc,rg,ir}/ + params/
└── Speckle-Scanner_Processing_data/ ← auto-created on first rectification run
└── <project>/<date>/<session>/...
Do not move the repo out of
~/. Scripts resolve paths viaconfig.pyusingPath.home() / "Speckle-Scanner". Cloning elsewhere will break path resolution unless you changeconfig.py.
3. Python environment and dependencies
conda create -n speckle python=3.9 -y
conda activate speckle
pip install -r ~/Speckle-Scanner/requirements.txt
Per-step installs are also available (see Per-step requirements below).
4. Build libSGM (GPU disparity — one-time per machine)
Requires NVIDIA CUDA + CMake ≥ 3.18. The binary is not committed to git; rebuild on each machine.
cd ~/Speckle-Scanner/05_disparity/libsgm
mkdir -p build && cd build
cmake .. -DENABLE_SAMPLES=on
make stereosgm_new -j4
Verify:
ls ~/Speckle-Scanner/05_disparity/libsgm/build/sample/stereosgm_new
5. Copy your data and run the pipeline
Calibration data → ~/Calib-data/<project>/<date>/<calib_name>/
Folders: lc/, rc/, rg/ (or rgb/), ir/ with chessboard images.
Raw scans → ~/3D-Scans/<project>/<date>/<session>/<scan>/01_raw_images/
Files: lc_ts*.png, rc_ts*.png, ir_*.png, rg_*.png, etc.
Then run the pipeline (replace names with yours):
# Step 1 — calibrate
cd ~/Speckle-Scanner/02_Calibration
python main.py --project Olsen_wings --date 2026-05-12 --calib_name calib1
# Step 2 — rectify (creates ~/Speckle-Scanner_Processing_data/...)
cd ~/Speckle-Scanner/04_Rectification
python main.py --project Olsen_wings --date 2026-05-12 --calib_name calib1
# Steps 3–5 — disparity, point cloud, colouring (see sections below)
Sanity check after setup:
ls ~/Calib-data/ # your calibration projects
ls ~/3D-Scans/ # your raw scan projects
ls ~/Speckle-Scanner/config.py # repo present
System requirements
| Requirement | Version |
|---|---|
| OS | Ubuntu 20.04 / 22.04 (Linux) |
| Python | 3.9 or newer |
| NVIDIA GPU + CUDA | 11.x or 12.x (required for libSGM; optional for GPU ZNCC) |
| CMake | ≥ 3.18 (for libSGM build) |
| Conda | Recommended for environment management |
Directory layout — where folders live on the machine
All four top-level directories sit directly inside the home folder (~/). Only Speckle-Scanner comes from git; the others are local data or auto-generated output.
~/
├── Speckle-Scanner/ ← clone this repository here
├── 3D-Scans/ ← raw scan images (copy/mount data here)
├── Calib-data/ ← calibration session images (copy here)
└── Speckle-Scanner_Processing_data/ ← auto-created by the rectification step
Never move Speckle-Scanner out of ~/. All path resolution is anchored to ~/ via config.py — no paths ever need manual editing.
Folder structure inside each directory
~/3D-Scans/
└── <project>/
└── <date>/
└── <session>/
└── <scan>/
└── 01_raw_images/ ← lc_ts*.png rc_ts*.png ir_*.png rg_*.png
~/Calib-data/
└── <project>/
└── <date>/
└── <calib_name>/ ← e.g. calib1
├── lc/ ← left camera calibration images
├── rc/ rg/ ir/ ← right camera images
└── params/ ← calibration outputs (auto-created)
~/Speckle-Scanner_Processing_data/ ← created automatically by step 2 (rectification)
└── <project>/
└── <date>/
└── <session>/
├── params_link/ ← calibration files copied here once per session
└── <scan>/
├── 01_raw_images/
├── 02_rect_images/
├── 03_sgm_disp_map/
├── 04_zncc_disp_map/
├── 05_sgm_pcl/
├── 06_zncc_pcl/
├── 07_sgm_pcl_col/
└── 08_zncc_pcl_col/
config.py — how path resolution works
config.py (at the root of this repo) is the single source of truth for all directory paths. It anchors everything to Path.home():
HOME_DIR = Path.home()
SOURCE_CODE_DIR = HOME_DIR / "Speckle-Scanner"
RAW_DATA_DIR = HOME_DIR / "3D-Scans"
CALIB_DATA_DIR = HOME_DIR / "Calib-data"
PROCESSING_DIR = HOME_DIR / "Speckle-Scanner_Processing_data"
Every pipeline script imports config and uses these variables — no script ever has a hardcoded path. To move the pipeline to a new machine, just clone the repo to ~/Speckle-Scanner/ and copy the data folders.
Per-step requirements.txt
Each pipeline step has its own requirements.txt for minimal installs:
| Step | File |
|---|---|
| Calibration | 02_Calibration/requirements.txt |
| Rectification | 04_Rectification/requirements.txt |
| SGM disparity | 05_disparity/libsgm/requirements.txt (build deps; no pip) |
| ZNCC disparity | 05_disparity/zncc/requirements.txt |
| Point cloud | 06_Pointcloud/requirements.txt |
| Colouring | 09_coloring/requirements.txt |
The root requirements.txt is the merged union of all Python steps — use it when setting up one conda/venv for the full pipeline.
Setup on a new machine (summary)
If you already followed Quick start above, you can skip this block. It repeats the same steps in one place:
cd ~
git clone https://gitea.subseascanning.com/AsadUllah/Speckle-Scanner.git
mkdir -p ~/3D-Scans ~/Calib-data
conda create -n speckle python=3.9 -y
conda activate speckle
pip install -r ~/Speckle-Scanner/requirements.txt
# GPU ZNCC (optional — match your CUDA version)
# pip install cupy-cuda11x # CUDA 11.x
# pip install cupy-cuda12x # CUDA 12.x
cd ~/Speckle-Scanner/05_disparity/libsgm
mkdir -p build && cd build
cmake .. -DENABLE_SAMPLES=on
make stereosgm_new -j4
Copy calibration images to ~/Calib-data/ and raw scans to ~/3D-Scans/, then run the pipeline steps below.
Note: The libSGM binary is machine-specific (compiled CUDA C++). It must be rebuilt on every new machine. The Python code is fully portable.
Full pipeline — step by step
Replace Olsen_wings, 2026-05-12, calib1 with your actual project, date, and calibration folder name.
Step 1 — Calibrate
Two-step calibration (detect features → JSON, then mono + stereo). One command calibrates lc vs rc, rg, and ir:
cd ~/Speckle-Scanner/02_Calibration
python main.py \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--chessboard_size 8,7 --square_size 0.045
Or run steps separately: detect_features.py then calibrate.py. Results go to ~/Calib-data/<project>/<date>/<calib_name>/params/ (lc-rc_*, lc-rg_*, lc-ir_*).
See 02_Calibration/README.md for all options.
Step 2 — Rectify
Reads raw scans from ~/3D-Scans/, applies calibration, and creates the full Speckle-Scanner_Processing_data/ structure (including params_link/, 01_raw_images/, 02_rect_images/).
cd ~/Speckle-Scanner/04_Rectification
# All sessions under a date
python main.py --project Olsen_wings --date 2026-05-12 --calib_name calib1
# Single session only
python main.py --project Olsen_wings --date 2026-05-12 --calib_name calib1 --session session1
See 04_Rectification/README.md for all options.
Step 3 — Disparity
Reads rectified images from 02_rect_images/. Choose SGM, ZNCC, or run both.
SGM (libSGM — CUDA C++):
cd ~/Speckle-Scanner/05_disparity/libsgm
python run_sgm_pipeline.py --project Olsen_wings --date 2026-05-12
# Single session:
python run_sgm_pipeline.py --project Olsen_wings --date 2026-05-12 --session session1
# Single scan:
python run_sgm_pipeline.py --project Olsen_wings --date 2026-05-12 --session session1 --scan Scan000001
ZNCC (CPU/GPU — Python):
cd ~/Speckle-Scanner/05_disparity/zncc
python run_zncc_pipeline.py --project Olsen_wings --date 2026-05-12
# Single scan — use only the last 3 image pairs (highest timestamps):
python run_zncc_pipeline.py \
--project Olsen_wings --date 2026-05-12 --session session1 --scan Scan000001 \
--num_images 3 --window_size 7 --zncc_threshold 0.5
See 05_disparity/libsgm/README.md and 05_disparity/zncc/Readme.md for all options.
Step 4 — Point Cloud
Converts disparity maps to 3D point clouds (.ply by default; .txt with --troubleshooting).
cd ~/Speckle-Scanner/06_Pointcloud
# Both SGM and ZNCC (PLY only)
python run_pcl_pipeline.py --project Olsen_wings --date 2026-05-12
# SGM only / ZNCC only
python run_pcl_pipeline.py --project Olsen_wings --date 2026-05-12 --mode sgm
python run_pcl_pipeline.py --project Olsen_wings --date 2026-05-12 --mode zncc
# Single scan
python run_pcl_pipeline.py \
--project Olsen_wings --date 2026-05-12 --session session1 --scan Scan000001
# Also save ASCII .txt exports
python run_pcl_pipeline.py \
--project Olsen_wings --date 2026-05-12 --troubleshooting
See 06_Pointcloud/README.md for all options.
Step 5 — Colour Point Cloud
Projects IR and RGB camera images onto the point cloud. Saves dual-texture binary PLY (RGB + IR channels).
cd ~/Speckle-Scanner/09_coloring
# Both SGM and ZNCC point clouds
python run_coloring_pipeline.py --project Olsen_wings --date 2026-05-12
# SGM only / ZNCC only
python run_coloring_pipeline.py --project Olsen_wings --date 2026-05-12 --mode sgm
python run_coloring_pipeline.py --project Olsen_wings --date 2026-05-12 --mode zncc
# Single scan
python run_coloring_pipeline.py \
--project Olsen_wings --date 2026-05-12 --session session1 --scan Scan000001
See 09_coloring/README.md for all options.
Output files per scan (summary)
| Folder | Contents | Created by |
|---|---|---|
01_raw_images/ |
Copy of source images | Step 2 |
02_rect_images/ |
Rectified lc_*, rc_*, rg_*, ir_* images |
Step 2 |
03_sgm_disp_map/ |
disparity.xml, disparity_color.png |
Step 3 SGM |
04_zncc_disp_map/ |
disparity.npy, colorbar PNGs |
Step 3 ZNCC |
05_sgm_pcl/ |
Point_cloud.ply (+ .txt with --troubleshooting) |
Step 4 |
06_zncc_pcl/ |
Point_cloud.ply (+ .txt with --troubleshooting) |
Step 4 |
07_sgm_pcl_col/ |
Point_cloud_colored.ply (RGB + IR) |
Step 5 |
08_zncc_pcl_col/ |
Point_cloud_colored.ply (RGB + IR) |
Step 5 |
Common --session and --scan options
All step 3–5 pipeline runners share the same scope arguments:
| Argument | Default | Effect |
|---|---|---|
--session |
(omit) | Process all sessions under the date |
--session session1 |
— | Process only session1 |
--scan |
(omit) | Process all scans in the session |
--scan Scan000001 |
— | Process only Scan000001 |
Quick check after setup
# Verify calibration outputs
ls ~/Calib-data/Olsen_wings/2026-05-12/calib1/params/
# Expected: lc-rc_stereo_cam_model.yaml lc-rc_Q.cvstore lc-rg_* lc-ir_*
# Verify rectification created the processing structure
ls ~/Speckle-Scanner_Processing_data/Olsen_wings/2026-05-12/session1/params_link/
ls ~/Speckle-Scanner_Processing_data/Olsen_wings/2026-05-12/session1/Scan000001/02_rect_images/ | head
# Verify libSGM binary
~/Speckle-Scanner/05_disparity/libsgm/build/sample/stereosgm_new --help