From 069681cd6cbe65102865a141e12bfa569630476f Mon Sep 17 00:00:00 2001 From: Parv Maheshwari Date: Thu, 21 May 2026 23:05:31 +0000 Subject: [PATCH 1/2] Add RELLIS-3D evaluation harness + reproducibility note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The released OTAS code does not include the multi-class evaluation script that produced the RELLIS-3D Table V mIoU numbers. Only the single-(pos, neg) -> binary-mask `single_inference` API is shipped, and there is no RELLIS-3D dataset loader. This PR adds the missing pieces so the result can be reproduced from a single OTAS checkout: - own_eval/own_RELLIS.py: RELLIS-3D driver, with `--paper_config` to match the hyperparameters described in supplementary §VII.A. - own_eval/rellis_dataset.py: PyTorch Dataset for RELLIS-3D's 20-class ontology.yaml ontology (also exposes the HRNet-19 `label_mapping` variant for completeness, default off). - own_eval/eval_common.py: per-frame inference + confusion-matrix-pooled IoU, forwards arbitrary config_overrides to OTASEncoder. - own_eval/otas_segmentor.py: N-way adapter that wraps `language_map.embed_image` -> per-class `clip_similarity` -> argmax. When `enable_mask_refinement=True`, delegates per-class refinement to OTAS's own `semantic_mask.binary_mask_refined(..., ret_dict=True)` so the SAM-on path reuses upstream code verbatim. - docs/RELLIS_REPLICATION.md: PR description with the paper claim, our matching config, the result, the ruled-out hypotheses, and the remaining open questions for upstream. Replication result: 15.43 mIoU under the §VII.A-matching config, vs the paper Table V claim of 48.48 mIoU (DINOv2 ViT-S/14, no mask refinement). ~32-point gap remains unexplained. Also bundles a one-line MaskCLIP import fix (`import packaging.version`) required to run on Python 3.12, and a `.gitignore` entry for the cached eval outputs under `result/`. See docs/RELLIS_REPLICATION.md for the full reproduction recipe and the list of hypotheses we tested + the ones we cannot test without the upstream eval script. Co-Authored-By: Claude Opus 4.7 (1M context) --- .gitignore | 4 +- docs/RELLIS_REPLICATION.md | 149 ++++++++++++ own_eval/eval_common.py | 219 +++++++++++++++++ own_eval/otas_segmentor.py | 240 +++++++++++++++++++ own_eval/own_RELLIS.py | 139 +++++++++++ own_eval/rellis_dataset.py | 252 ++++++++++++++++++++ src/foundation_models/maskclip_onnx/clip.py | 1 + 7 files changed, 1003 insertions(+), 1 deletion(-) create mode 100644 docs/RELLIS_REPLICATION.md create mode 100644 own_eval/eval_common.py create mode 100644 own_eval/otas_segmentor.py create mode 100644 own_eval/own_RELLIS.py create mode 100644 own_eval/rellis_dataset.py diff --git a/.gitignore b/.gitignore index 35e3147..82f7847 100644 --- a/.gitignore +++ b/.gitignore @@ -191,4 +191,6 @@ cache/* # Example photos (they are big and should not be cloned by everyone using the repo) img/outdoor_reconstruction/* -saved_maps/* \ No newline at end of file +saved_maps/* + +result/* \ No newline at end of file diff --git a/docs/RELLIS_REPLICATION.md b/docs/RELLIS_REPLICATION.md new file mode 100644 index 0000000..d75d974 --- /dev/null +++ b/docs/RELLIS_REPLICATION.md @@ -0,0 +1,149 @@ +# RELLIS-3D Table V replication attempt + +## Summary + +This PR adds a self-contained RELLIS-3D evaluation harness to `own_eval/` (the `2D` semantic-segmentation track is not currently part of the released code, only ORFD via `own_GOD.py` analogues and TartanAir for the 3D track). We use the harness to attempt to replicate the published RELLIS-3D number from **Table V** of the paper. + +**Result:** a ~32-point mIoU gap that we have not been able to close. + +| Config | shared_feat_res | n_components | dinov2_input_size | input resize | mIoU (full, 20-cls) | fg-only mIoU | +|---|---:|---:|---:|---|---:|---:| +| OTAS default (`OTAS_small.json`-shaped) | 32 | 12 | 518 | 480×640 | **16.70** | 17.58 | +| Paper §VII.A match (`--paper_config`) | **64** | **24** | **224** (16×16 native → bilinear-interp to 64×64) | **1024×1024** | **15.43** | 16.24 | +| **Paper Table V claims (DINOv2 ViT-S/14)** | — | — | — | — | **48.48** | — | + +Both of our runs are zero-shot, no mask refinement, no negative prompt, all 20 RELLIS class names from the released `ontology.yaml` as positive prompts — matching the protocol described in §VII.A as closely as we can determine from the paper alone. + +## Why we believe the released code does not let us reproduce Table V + +The released OTAS repository ships: + +- `src/` — the OTAS core (`language_map`, `semantic_mask`, `single_inference.similarity_single`, mask refinement, etc.). +- `src/inference.py:single_inference.similarity_single` — a single-(pos, neg) → single similarity map → optional binary mask pipeline. Designed for binary segmentation: "is this pixel `road` vs. `not road`". +- `src/inference.py:single_inference.segmentation_single` — wraps `binary_mask_refined` / `binary_mask_interpolated`. Again, binary per (pos, neg) pair. +- A `demo.ipynb` that exercises only the single-map binary segmentation API. + +The released code does **not** contain: + +- The N-way / multi-class evaluation script that produced the per-class mIoU in Table V. There is no `eval_rellis.py`, no `own_RELLIS.py`, no `eval_segmentation.py`. The only `__main__` entry points are the ROS 2 node and the demo notebook. +- A `RELLIS_dataset` loader. ORFD and TartanAir loaders are similarly absent — Table III and Table II are also produced by code outside the public release. +- A documented procedure for going from the (pos, neg)-binary similarity map to an N-class mIoU. §III.B describes the per-class scoring formula `Scombined = sum(pos_sims) - sum(neg_sims)`; §III.C describes the binary refinement to a single mask `M`. Neither directly answers "how is mIoU over 20 classes computed when you have 20 different positive prompts and no negative prompt?". + +So a reproduction effort has to (a) write an N-way adapter on top of OTAS's binary-flavoured API, (b) write a RELLIS-3D dataset loader, (c) make a judgment call about whether to argmax over per-class similarity maps or to threshold each one independently. We made the argmax choice (matches the natural interpretation of §III.B + §III.C with no mask refinement) and got 15.43–16.70 mIoU instead of 48.48. + +## What this PR adds + +| File | Purpose | +|---|---| +| `own_eval/own_RELLIS.py` | RELLIS-3D driver. CLI: `--paper_config` reproduces the §VII.A hyperparameters; `--enable_mask_refinement` toggles SAM2; `--ontology raw20\|hrnet19` switches the class set. | +| `own_eval/rellis_dataset.py` | RELLIS-3D PyTorch `Dataset`. Parses `test.lst` / `val.lst` / `train.lst` (2-col `