How to Add New Dataset Support to LingBot-Map’s Benchmark Evaluation Framework
To add new dataset support to the LingBot-Map benchmark evaluation framework, subclass BaseDataset from benchmark/benchmark/dataset/base.py, implement the required data loading interface, register the class in benchmark/datasets/__init__.py, and reference it in your benchmark configuration file.
LingBot-Map provides a flexible evaluation pipeline for mapping and localization algorithms. By implementing a standardized interface, you can integrate custom data sources—from indoor scans to drone footage—without modifying any core evaluation logic.
Understanding the BaseDataset Interface
The benchmark framework discovers datasets through Python classes that inherit from the abstract BaseDataset defined in benchmark/benchmark/dataset/base.py. This contract standardizes how the evaluation pipeline accesses scene lists, frame sequences, and sensor readings.
Required Methods
Your subclass must implement three mandatory methods and one optional method:
get_scenes(self) → List[str]– Returns a list of scene identifiers, usually folder names. See the reference implementation inKittiDataset.get_scenesatbenchmark/datasets/kitti.pylines 78-86.get_frame_list(self, scene: str) → List[int]– Returns the frame indices to be processed for a given scene. Reference implementation:benchmark/datasets/kitti.pylines 88-92.load_frame_data(self, scene: str, frame_id: int) → Dict[str, Any]– Loads per-frame data and returns a dictionary containing at least the keyrgb(auint8image). Optional keys includedepth,mask,pose,intrinsics, and custom data. Reference:benchmark/datasets/kitti.pylines 93-106.load_global_data(self, scene: str) → Dict[str, Any](optional) – Returns scene-level data such as a ground-truth point cloud. Reference:benchmark/datasets/kitti.pylines 53-57.
The base class also provides a default apply_sampling method that you can reuse or override if your dataset requires a custom sampling strategy.
Optional Save Hooks
If your dataset outputs extra modalities (e.g., semantic masks or surface normals), implement methods named __save_<key>_file__ with the signature (self, output_dir: Path, base_name: str, data: np.ndarray). The framework automatically discovers these hooks during BSS-Saver initialization. See the documentation in benchmark/benchmark/dataset/base.py lines 21-30.
Step-by-Step Implementation Guide
Step 1: Create the Dataset Module
Create a new Python file under benchmark/datasets/, for example my_dataset.py. This file will contain your loader implementation.
Step 2: Implement the Interface
Import BaseDataset and implement the required methods. Ensure load_frame_data returns a dictionary with at least the rgb key to satisfy the pipeline requirements.
Step 3: Register the Dataset
Add an import statement to benchmark/datasets/__init__.py to expose your class to the configuration loader:
from .my_dataset import MyDataset # noqa: F401
If __init__.py does not exist, create it with this import statement.
Step 4: Configure the Benchmark
Reference your dataset in a YAML or JSON configuration file (consumed by benchmark/prepare.py). Set dataset.name to the fully-qualified class name and provide constructor arguments under dataset.args:
dataset:
name: benchmark.datasets.my_dataset.MyDataset
args:
raw_data_root: /path/to/my_dataset_root
sequences: [train, test]
The loader in benchmark/benchmark/core/loader.py instantiates your class via importlib and passes the supplied kwargs to your constructor.
Complete Working Example
Below is a minimal implementation for a folder-structured image sequence dataset.
# benchmark/datasets/my_dataset.py
import numpy as np
from pathlib import Path
from PIL import Image
from benchmark.benchmark.dataset.base import BaseDataset
class MyDataset(BaseDataset):
"""Simple loader for a folder-structured image sequence.
Expected layout:
└─ <raw_root>/
├─ scene_A/
│ ├─ images/
│ │ ├─ 000000.png
│ │ ├─ 000001.png
│ │ └─ …
│ └─ poses.txt # optional, one 4x4 matrix per line
└─ scene_B/
…
"""
def __init__(self, raw_data_root: str, sequences: list | None = None):
super().__init__(raw_data_root)
# optional whitelist of scenes
self._whitelist = sequences
def get_scenes(self) -> list[str]:
if self._whitelist is not None:
return self._whitelist
return sorted(p.name for p in self.raw_data_root.iterdir() if p.is_dir())
def get_frame_list(self, scene: str) -> list[int]:
img_dir = self.raw_data_root / scene / "images"
return sorted(int(p.stem) for p in img_dir.glob("*.png"))
def load_frame_data(self, scene: str, frame_id: int) -> dict:
img_path = self.raw_data_root / scene / "images" / f"{frame_id:06d}.png"
rgb = np.array(Image.open(img_path).convert("RGB"))
# optional pose loading
pose = None
poses_path = self.raw_data_root / scene / "poses.txt"
if poses_path.is_file():
pose = np.loadtxt(poses_path)[frame_id]
# intrinsics are hard-coded here but could be read from a calibration file
intrinsics = np.array([500.0, 500.0, 320.0, 240.0], dtype=np.float32)
return {"rgb": rgb, "pose": pose, "intrinsics": intrinsics}
Register the class in benchmark/datasets/__init__.py:
from .my_dataset import MyDataset # noqa: F401
Example benchmark configuration:
dataset:
name: benchmark.datasets.my_dataset.MyDataset
args:
raw_data_root: /data/my_dataset
sequences: [scene_A, scene_B]
Running python benchmark/prepare.py or python benchmark/run.py will now process scene_A and scene_B using your custom logic.
Summary
- Subclass
BaseDatasetfrombenchmark/benchmark/dataset/base.pyto ensure compatibility with the evaluation pipeline. - Implement four core methods: Provide
get_scenes,get_frame_list,load_frame_data(must includergbkey), and optionallyload_global_datafor scene-level ground truth. - Register in
__init__.py: Import your class inbenchmark/datasets/__init__.pyto make it discoverable by name in configuration files. - Configure via YAML: Reference the fully-qualified class name (e.g.,
benchmark.datasets.my_dataset.MyDataset) in the benchmark config underdataset.name, passing constructor arguments viadataset.args. - Leverage save hooks: Implement
__save_<key>_file__methods to output custom modalities beyond standard RGB and depth data.
Frequently Asked Questions
What file format should the RGB images be in?
The load_frame_data method must return a dictionary with an rgb key containing a NumPy array of shape (H, W, 3) with dtype uint8. Your loader can read any image format (PNG, JPEG, etc.) using libraries like PIL or OpenCV, provided you convert the result to this NumPy representation before returning.
Do I need to modify the core benchmark code to add my dataset?
No. The framework uses dynamic loading via importlib in benchmark/benchmark/core/loader.py to instantiate your dataset class from the configuration file. As long as you subclass BaseDataset and register the import in benchmark/datasets/__init__.py, no changes to prepare.py, run.py, or other core modules are required.
How do I handle datasets without pose information?
The pose key in the dictionary returned by load_frame_data is optional. If your dataset lacks ground-truth poses, simply omit the key or set it to None. The benchmark pipeline will skip pose-dependent evaluations unless specifically configured otherwise in the experiment config.
Can I implement custom sampling strategies for my dataset?
Yes. While BaseDataset provides a default apply_sampling implementation, you can override this method in your subclass to implement custom frame skipping, random sampling, or temporal windowing specific to your dataset's characteristics. The framework calls this method before processing frames if sampling is enabled in the configuration.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →