Skip to content

Smile232323/PlaceForge

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Point Cloud Place Recognition Benchmark + Protocols

一个面向点云地点识别(Place Recognition)的 UGP 风格 Benchmark 骨架,目标是:

  • 提供 cross-datasetcross-distance 两套协议
  • 统一 manifest -> descriptors -> evaluator 评测链路
  • 支持 Oxford + MulRan 两个数据集 adapter
  • 接入 MinkLoc3D / TransLoc3D / CASSPR 三个 baseline 的“只推理”接口

项目背景与动机

在点云地点识别(Place Recognition)领域,过去几年涌现出大量方法并取得了显著进展。然而,由于不同工作在数据划分、正样本定义、描述子提取流程、距离度量与评估指标上的设置差异,现有结果往往缺乏可比性,限制了该方向的系统性发展。为此,我们构建了 PlaceForge,一个面向点云地点识别的统一评测基准与协议框架。当前版本支持 Oxford 与 MulRan 两个数据集适配器,提供 cross-datasetcross-distance 两类协议,统一描述子评估接口,并接入 MinkLoc3D、TransLoc3D 与 CASSPR 的“仅推理”评测流程。

PlaceForge 通过统一 manifest、position-based positives 构建规则、以及标准化 evaluator(Recall@1、Recall@1%、可选 mAP)实现可复现与公平比较。基于该框架,我们可系统分析关键技术因素(如距离度量、正样本半径、密度分桶设定)对性能与鲁棒性的影响,并结合失败案例切片(稀疏场景、别名匹配、动态干扰)定位现有方法的薄弱环节。我们期望 PlaceForge 成为点云地点识别研究中的基础设施,促进统一报告、可靠复现与后续方法创新。

当前状态:

  • evaluator / positives / protocol runner 可运行
  • Oxford / MulRan adapter 为最小可用版本(读取 pose + 生成 manifest)
  • MinkLoc3D wrapper 为最小可跑版(依赖 baseline 环境)
  • TransLoc3D wrapper 为最小可跑推理实现(依赖 baseline 环境与配置)
  • CASSPR wrapper 为最小可跑推理实现(依赖 baseline 环境与配置)

1) 项目目标(PR 版 UGP)

本项目用于标准化点云地点识别评测流程:

  1. 使用 adapter 将数据集转换为统一 manifest
  2. 使用 baseline wrapper 读取 manifest 导出 descriptors
  3. 使用统一 evaluator 计算 recall@1recall@1%(可选 mAP)
  4. 使用 protocol YAML 复现实验配置并保存 runs/<protocol>/metrics.json

2) 目录结构与文件职责

.
├── bench/
│   ├── baselines/
│   │   ├── base.py                  # baseline 抽象接口(ExtractionRequest / Result)
│   │   ├── minkloc3d_wrapper.py     # MinkLoc3D 最小可跑推理 wrapper
│   │   ├── transloc3d_wrapper.py    # TransLoc3D 最小可跑推理 wrapper
│   │   └── casspr_wrapper.py        # CASSPR 最小可跑推理 wrapper
│   ├── datasets/
│   │   ├── common.py                # pose/scan 通用解析工具
│   │   ├── oxford.py                # Oxford adapter -> manifest_db/q.json
│   │   └── mulran.py                # MulRan adapter -> manifest_db/q.json
│   ├── evaluator.py                 # 检索评测主逻辑(cosine/l2, topk, recall/mAP)
│   ├── metrics.py                   # 指标函数(recall@k, AP, mAP, FPR@95Recall)
│   ├── positives.py                 # 按位置半径生成 positives
│   ├── types.py                     # Sample / Database / Query dataclass
│   ├── manifest.py                  # manifest 读写辅助与分桶辅助
│   ├── io.py                        # JSON/manifest I/O
│   └── local_config.py              # 单文件本地路径与运行配置(推荐修改)
├── protocols/
│   ├── cross_dataset.yaml           # 跨数据集协议模板
│   └── cross_distance.yaml          # 跨距离(分桶)协议模板
├── scripts/
│   ├── prepare_oxford.py            # 生成 Oxford manifests
│   ├── prepare_mulran.py            # 生成 MulRan manifests
│   ├── prepare_data.py              # 通用 prepare 入口
│   ├── build_positives.py           # 从 q/db 坐标生成 positives.json
│   ├── extract_desc.py              # baseline descriptor 导出总入口
│   ├── eval_from_npy.py             # 读取 npy/positives 直接评测
│   ├── run_eval.py                  # eval_from_npy 别名入口
│   ├── run_protocol.py              # 协议执行入口
│   ├── run_from_config.py           # 读取 bench/local_config.py 的一键入口
│   ├── run_all_baselines.sh         # 一键跑多个 baseline 的提特征+评测
│   ├── analyze_failures.py          # 失败案例统计 + 文本卡片 PNG
│   └── toy_example.py               # toy 数据快速验证 evaluator
├── assets/
│   ├── failures/                    # 失败案例输出目录
│   └── plot_failure_cards.py        # failure 分析辅助调用脚本
├── tests/
│   ├── test_evaluator.py
│   ├── test_evaluator_cosine.py
│   ├── test_evaluator_l2.py
│   ├── test_positives.py
│   ├── test_pose_parsing.py
│   └── test_baseline_wrapper_utils.py
├── requirements.txt
└── README.md

3) 安装环境

  • Python 3.10+
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

requirements.txt(最小依赖):

  • numpy
  • PyYAML
  • matplotlib(失败案例文本卡片绘图)

4) 统一数据结构

bench/types.py 中定义:

  • Sample: id, path, pos, 可选 timestamp, npoints, bucket, dynamic_ratio
  • Database: name + samples
  • Query: name + samples

Oxford / MulRan adapter 统一输出 list[Sample.to_dict()] 的 JSON manifest。


5) 数据集支持

5.1 Oxford adapter

入口:scripts/prepare_oxford.py

关键参数:

  • --data_root
  • --sequences
  • --pose_file_relpath(默认 poses.csv,支持 .csv/.txt/.npy
  • --pose_format / --timestamp_col / --x_col / --y_col / --z_col(可配置 pose 列映射)
  • --scan_relpath(默认 pointclouds
  • --scan_ext(默认 .bin .pcd .ply
  • --db_stride, --q_stride, --split_seed

输出:

  • data/oxford/manifest_db.json
  • data/oxford/manifest_q.json

5.2 MulRan adapter

入口:scripts/prepare_mulran.py

关键参数:

  • --data_root
  • --sequences
  • --pose_file_relpath(默认 poses.csv,常见可设为 global_pose.csv
  • --pose_format(支持 csv/txt/npy)与列/索引参数(timestamp_col/txt_*_index/npy_*_index
  • --scan_relpath(默认 Ouster
  • --scan_ext(默认 .bin .pcd .ply
  • --db_stride, --q_stride, --split_seed

输出:

  • data/mulran/manifest_db.json
  • data/mulran/manifest_q.json

5.3 MulRan 目录映射示例

若解压后为:

data/mulran_raw/
  KAIST01/
    Ouster/
      *.bin
    global_pose.csv

可执行:

python3 scripts/prepare_mulran.py \
  --data_root data/mulran_raw \
  --sequences KAIST01 \
  --pose_file_relpath global_pose.csv \
  --scan_relpath Ouster \
  --pose_format csv \
  --timestamp_col timestamp --x_col x --y_col y --z_col z \
  --db_stride 10 --q_stride 10

6) Evaluator 规范

实现:bench/evaluator.py + bench/metrics.py

输入

  • db_desc: (N_db, D) float32
  • q_desc: (N_q, D) float32
  • db_pos: (N_db, 2|3) float64
  • q_pos: (N_q, 2|3) float64
  • positives: list[list[int]]
  • 可选:q_ids, db_ids

输出

  • 必选:recall@1, recall@1%
    • 1% = max(1, floor(0.01 * N_db))
  • 可选:mAP
  • per_query topk 结果(用于 failure 分析)

检索模式

  • cosine(默认,向量归一化)
  • l2

Top-k 使用矩阵乘法 + argpartition 快速检索。


7) Positives 生成

实现:bench/positives.py

接口:

  • build_positives(q_pos, db_pos, radius_m, use_dim='xy'|'xyz', q_yaw=None, db_yaw=None, max_yaw_diff_deg=None) -> list[list[int]]

CLI:scripts/build_positives.py

python3 scripts/build_positives.py \
  --q_pos q_pos.npy \
  --db_pos db_pos.npy \
  --radius_m 25 \
  --use_xy \
  --out positives.json

若需要加 yaw 约束:

python3 scripts/build_positives.py \
  --q_pos q_pos.npy \
  --db_pos db_pos.npy \
  --q_yaw q_yaw.npy \
  --db_yaw db_yaw.npy \
  --max_yaw_diff_deg 45 \
  --yaw_unit deg \
  --radius_m 25 \
  --use_xy \
  --out positives.json

默认半径建议:25m(可按协议修改)。

可选角度约束(yaw):当提供 q_yaw/db_yaw 且设置 max_yaw_diff_deg 后,positives 会同时满足距离与角度阈值。


8) Protocols

8.1 protocols/cross_dataset.yaml

定义:

  • train_domain / test_domain
  • prepare 配置(按 domain)
  • positives 半径、xy/xyz,以及可选 yaw 角度约束(max_yaw_diff_deg
  • metric(cosine/l2)、topk
  • descriptor 路径

8.2 protocols/cross_distance.yaml

定义:

  • 单域 domain
  • bucketing.field(默认 npoints
  • train_buckets=[dense, medium]
  • test_buckets=[sparse]
  • 检索设置(metric、radius、topk)

8.4 Pose 文件格式说明(adapter 可配置)

Oxford / MulRan adapter 现在都支持:

  • pose_format: auto / csv / txt / npy
  • CSV 列映射:timestamp_col, x_col, y_col, z_col
  • TXT 索引映射:txt_timestamp_index, txt_x_index, txt_y_index, txt_z_index(可配 txt_delimiter
  • NPY 索引映射:npy_timestamp_index, npy_x_index, npy_y_index, npy_z_index

常见 MulRan pose 来源(global_pose.csv / odom.txt / poses.npy)都可通过上述参数对齐。


8.3 Runner

入口:scripts/run_protocol.py

行为:

  1. 读取 YAML
  2. 若 manifest 不存在,调用对应 adapter prepare
  3. 若 descriptors 存在,执行 evaluator
  4. 输出 runs/<protocol_name>/metrics.json(包含分阶段运行耗时 runtime_ms

当数据路径/descriptor 缺失时,会将跳过状态与错误信息写入 metrics.json


9) Baseline 推理接入

入口:scripts/extract_desc.py

统一命令:

python3 scripts/extract_desc.py \
  --baseline minkloc3d \
  --repo_root ../MinkLoc3D \
  --ckpt ../MinkLoc3D/weights/minkloc3d_baseline.pth \
  --manifest data/oxford/manifest_db.json \
  --out runs/descriptors/oxford_db_desc.npy

统一输出:

  • desc.npy (float32, (N, D))
  • ids.json(与 manifest 顺序一致)

支持:

  • --batch_size
  • --device {auto,cpu,cuda}
  • --fp16(默认关闭)

状态说明:

  • minkloc3d: 最小可跑实现
  • transloc3d: 最小可跑实现(动态导入 baseline 仓库)
  • casspr: 最小可跑实现(动态导入 baseline 仓库)

9.0) 单文件配置模式(推荐)

如果你希望“只改一个文件就能跑”,请直接编辑:

  • bench/local_config.py

然后执行:

python3 scripts/run_from_config.py --baselines minkloc3d

或一次跑多个 baseline:

python3 scripts/run_from_config.py --baselines minkloc3d,transloc3d,casspr

这个入口会自动:

  1. local_config.py 里的 prepare 配置生成 manifest
  2. baselines 配置导出 descriptors
  3. evaluation.protocol 运行协议评测并输出 runs/<run_prefix>_<baseline>/metrics.json

9.1) 必须替换的地址(请先改这个)

本仓库里的很多路径是占位路径,你需要替换成自己的本地路径(支持相对路径)。

如果你采用单文件模式,优先修改 bench/local_config.py 即可; 不采用单文件模式时,再分别改命令行参数和 protocol YAML。

A. 数据集相关(prepare 脚本)

你至少要替换这些参数:

  • --data_root
  • --sequences
  • --pose_file_relpath
  • --scan_relpath

示例(Oxford):

python3 scripts/prepare_oxford.py \
  --data_root data/oxford_raw \
  --sequences 2015-08-14-14-54-57 \
  --pose_file_relpath poses.csv \
  --scan_relpath pointclouds

示例(MulRan):

python3 scripts/prepare_mulran.py \
  --data_root data/mulran_raw \
  --sequences KAIST01 \
  --pose_file_relpath global_pose.csv \
  --scan_relpath Ouster

B. baseline 相关(extract_desc)

你必须替换:

  • --repo_root(baseline 仓库目录)
  • --ckpt(checkpoint)
  • --config / --model_config(TransLoc3D、CASSPR 常需要)

示例:

python3 scripts/extract_desc.py \
  --baseline transloc3d \
  --repo_root ../TransLoc3D \
  --ckpt <your_transloc_ckpt> \
  --config ../TransLoc3D/configs/transloc3d_baseline_cfg.py \
  --manifest data/mulran/manifest_db.json \
  --out runs/descriptors/mulran_db_desc.npy

C. protocol 相关(YAML)

你需要检查并替换 protocols/cross_dataset.yamlprotocols/cross_distance.yaml 中这些字段:

  • prepare.*.data_root
  • prepare.*.sequences
  • prepare.*.pose_file_relpath
  • prepare.*.scan_relpath
  • paths.db_manifest / paths.q_manifest
  • descriptors.db / descriptors.q

如果你用 scripts/run_all_baselines.sh,脚本会自动生成一个临时 protocol 并覆盖 descriptors.db/q,但其余字段(尤其 prepare.*)仍要与你的数据目录匹配。


9.2) 数据格式要求(输入/输出)

1) Pose 文件格式(adapter 输入)

支持 csv/txt/npy 三种格式。

  • csv:必须能映射到 timestamp,x,y,z(列名可通过参数改)
    • 例:
      • timestamp,x,y,z
      • 1000,1.0,2.0,0.0
  • txt:每行是数值,默认索引是 timestamp=0, x=1, y=2, z=3(可通过 --txt_*_index 改)
    • 例:1000 1.0 2.0 0.0
  • npy:二维数组 [N, C],默认索引是 timestamp=0, x=1, y=2, z=3(可通过 --npy_*_index 改)

2) 点云文件格式(baseline 推理输入)

wrapper 当前支持:

  • .npy(N,3)(N,4)(前 3 列为 xyz)
  • 二进制 .bin
    • float64 xyz(长度可被 3 整除)
    • float32 xyz/xyzi(长度可被 3 或 4 整除)

3) Manifest 格式(prepare 输出 / extract 输入)

manifest_db.jsonmanifest_q.json 都是 list[dict],每条样本至少需要:

  • id: str
  • path: str(点云文件路径)
  • pos: list[float],长度 2 或 3

可选字段:timestamp, npoints, bucket, dynamic_ratio

最小示例:

[
  {"id": "mulran:KAIST01:db:00000000", "path": "data/mulran_raw/KAIST01/Ouster/123.bin", "pos": [10.0, 20.0, 0.0], "timestamp": 123.0}
]

4) Descriptor 格式(extract 输出 / evaluator 输入)

  • db_desc.npy: float32,形状 (N_db, D)
  • q_desc.npy: float32,形状 (N_q, D)
  • 行数必须与对应 manifest 样本数一致

5) Positives 格式(build_positives 输出)

positives.jsonlist[list[int]],长度必须等于 query 数量。

示例:

[[0,1,2],[5],[3,7]]

6) Evaluator 结果格式(eval_from_npy / run_protocol 输出)

results.json / metrics.json 至少包含:

  • recall@1
  • recall@1%
  • per_query(每个 query 的 topk 索引/分数/距离)

可选:mAP


10) Quickstart(从 clone baseline 到 metrics.json)

Step 1: clone 三个 baseline(不修改源码)

git clone https://github.com/jac99/MinkLoc3D.git ../MinkLoc3D
git clone https://github.com/slothfulxtx/TransLoc3D.git ../TransLoc3D
git clone https://github.com/Yan-Xia/CASSPR.git ../CASSPR

Step 2: 准备 manifests

python3 scripts/prepare_oxford.py \
  --data_root data/oxford_raw \
  --sequences 2015-08-14-14-54-57 \
  --pose_file_relpath poses.csv \
  --scan_relpath pointclouds \
  --db_stride 10 --q_stride 10

python3 scripts/prepare_mulran.py \
  --data_root data/mulran_raw \
  --sequences KAIST01 \
  --pose_file_relpath global_pose.csv \
  --scan_relpath Ouster \
  --pose_format csv \
  --timestamp_col timestamp --x_col x --y_col y --z_col z \
  --db_stride 10 --q_stride 10

Step 3: 导出 descriptors(示例:MinkLoc3D)

python3 scripts/extract_desc.py \
  --baseline minkloc3d \
  --repo_root ../MinkLoc3D \
  --ckpt ../MinkLoc3D/weights/minkloc3d_baseline.pth \
  --manifest data/mulran/manifest_db.json \
  --out runs/descriptors/mulran_db_desc.npy

python3 scripts/extract_desc.py \
  --baseline minkloc3d \
  --repo_root ../MinkLoc3D \
  --ckpt ../MinkLoc3D/weights/minkloc3d_baseline.pth \
  --manifest data/mulran/manifest_q.json \
  --out runs/descriptors/mulran_q_desc.npy

Step 4: 协议评测

python3 scripts/run_protocol.py --protocol protocols/cross_dataset.yaml

输出:runs/cross_dataset/metrics.json

Step 5: 一键跑多个 baseline(可选)

bash scripts/run_all_baselines.sh \
  --baselines minkloc3d,transloc3d,casspr \
  --ckpt_minkloc3d ../MinkLoc3D/weights/minkloc3d_baseline.pth \
  --ckpt_transloc3d <your_transloc_ckpt> \
  --ckpt_casspr <your_casspr_ckpt> \
  --trans_config ../TransLoc3D/configs/transloc3d_baseline_cfg.py \
  --casspr_config ../CASSPR/config/config_oxford.txt \
  --casspr_model_config ../CASSPR/config/model_config_oxford.txt

输出:

  • descriptors: runs/descriptors/<baseline>_{db,q}_desc.npy
  • metrics: runs/cross_dataset_<baseline>/metrics.json

11) 结果输出说明

11.1 runs/ 目录

runs/<run_name>/
└── metrics.json

11.2 metrics.json 关键字段

  • metric
  • num_db, num_query, num_valid_query
  • empty_positive_ratio
  • one_percent_k
  • recall@1, recall@1%
  • mAP(可选)
  • runtime_msprepare/load_inputs/bucket_filter/evaluate/total,按协议类型出现)
  • per_query(topk 详细结果)

12) 失败案例分析

入口:scripts/analyze_failures.py

python3 scripts/analyze_failures.py \
  --results runs/cross_dataset/metrics.json \
  --manifest_q data/mulran/manifest_q.json \
  --manifest_db data/mulran/manifest_db.json \
  --positives positives.json \
  --out_dir assets/failures \
  --topk 5

输出:

  • assets/failures/summary.csv
  • assets/failures/summary.json
  • assets/failures/fail_qXXXXXX.png

失败切片:

  • sparse
  • aliasing_proxy
  • dynamic_proxy(若 manifest 有 dynamic_ratio

13) Toy Example(最小安装验证)

python3 scripts/toy_example.py

你也可以用 CLI 版本:

python3 - <<'PY'
import json
import numpy as np
from pathlib import Path

out = Path('toy')
out.mkdir(exist_ok=True)

N_db, N_q, D = 100, 10, 16
rng = np.random.default_rng(0)

db_desc = rng.normal(size=(N_db, D)).astype(np.float32)
q_desc = db_desc[:N_q].copy()
db_pos = rng.normal(size=(N_db, 3)).astype(np.float64)
q_pos = db_pos[:N_q].copy()
positives = [[i] for i in range(N_q)]

np.save(out / 'db_desc.npy', db_desc)
np.save(out / 'q_desc.npy', q_desc)
np.save(out / 'db_pos.npy', db_pos)
np.save(out / 'q_pos.npy', q_pos)
(out / 'positives.json').write_text(json.dumps(positives), encoding='utf-8')
PY

python3 scripts/eval_from_npy.py \
  --db_desc toy/db_desc.npy \
  --q_desc toy/q_desc.npy \
  --db_pos toy/db_pos.npy \
  --q_pos toy/q_pos.npy \
  --positives toy/positives.json \
  --out toy/results.json

14) 常见问题(FAQ)

  1. 缺少 pose 文件

    • 报错:pose file not found
    • 处理:检查 --pose_file_relpath 是否相对每个 sequence 根目录正确。
  2. 点云路径不匹配

    • 报错:scan directory not found / No scan files found
    • 处理:检查 --scan_relpath--scan_ext
  3. 显存不足

    • 处理:减小 extract_desc.py --batch_size,必要时使用 --device cpu
  4. 评测速度慢

    • 处理:增大 evaluator 的 --batch_size,或增大 db/q stride 减少样本。
  5. TransLoc3D / CASSPR 运行报错

    • 处理:先确认 baseline 依赖(Torch+MinkowskiEngine+对应 CUDA)与 --config/--model_config 是否和 checkpoint 匹配。

About

Point cloud place recognition benchmark and protocols (cross-dataset and cross-distance) with Oxford/MulRan adapters and baseline inference evaluation.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors