mattdawkins commited on
Commit
0803818
·
verified ·
1 Parent(s): 1fb7858

Add model card

Browse files
Files changed (1) hide show
  1. README.md +168 -0
README.md CHANGED
@@ -1,3 +1,171 @@
1
  ---
2
  license: cc-by-4.0
 
 
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  license: cc-by-4.0
3
+ tags:
4
+ - onnx
5
+ - stereo
6
+ - stereo-matching
7
+ - computer-vision
8
+ - viame
9
  ---
10
+
11
+ # VIAME Stereo Template Matcher
12
+
13
+ Epipolar template matching for stereo correspondence, exported from
14
+ [VIAME](https://github.com/VIAME/VIAME) as a single ONNX graph. Given a set of points in
15
+ the left image of a calibrated stereo pair, it returns the matching points in the right
16
+ image, plus match scores.
17
+
18
+ This is VIAME's stereo measurement **method 1**
19
+ (`epipolar_template_matching`): for each source point it walks the epipolar curve implied
20
+ by the calibration, sampling candidate depths, and picks the best NCC (`TM_CCOEFF_NORMED`)
21
+ template match. Pair it with two-view triangulation to measure real-world lengths — the
22
+ usual application is measuring fish in stereo camera rigs.
23
+
24
+ **There are no learned weights.** The graph is pure geometry plus normalized
25
+ cross-correlation, which is why it is 89 KB. Nothing here was trained, and there is no
26
+ training data or evaluation set.
27
+
28
+ ## Files
29
+
30
+ | File | Size | Notes |
31
+ | --- | --- | --- |
32
+ | `stereo_match.onnx` | 89 KB | opset 18, IR 8, float32 throughout |
33
+
34
+ ## Baked-in constants
35
+
36
+ Two parameters are **frozen into the graph at export time** and are not runtime inputs:
37
+
38
+ | Constant | Value |
39
+ | --- | --- |
40
+ | `template_size` | 13 (13×13 NCC patch) |
41
+ | `num_samples` | 5000 (depth samples along the epipolar curve) |
42
+
43
+ These match `configs/pipelines/interactive_stereo_template.conf` in VIAME, which is what
44
+ the DIVE desktop interactive stereo service loads. Note they are **not** the export
45
+ script's own defaults (25 / 5000) — re-exporting without explicit flags produces a model
46
+ that loads identically but matches differently. To reproduce this exact file:
47
+
48
+ ```bash
49
+ python plugins/onnx/export_stereo_mapping.py --model match \
50
+ --out stereo_match.onnx --template-size 13 --num-samples 5000
51
+ ```
52
+
53
+ ## Inputs
54
+
55
+ All float32. The world frame is the **left camera**, so the left camera is normally
56
+ `R_left = I`, `t_left = 0`, and the right camera carries the rig's relative pose.
57
+ Matrices are row-major.
58
+
59
+ | Name | Shape | Meaning |
60
+ | --- | --- | --- |
61
+ | `left_gray` | `[H, W]` | Left image, grayscale, 0–255 |
62
+ | `right_gray` | `[Hr, Wr]` | Right image, grayscale, 0–255 |
63
+ | `points_left` | `[P, 2]` | Source points `(x, y)` in left-image pixels |
64
+ | `K_left` | `[3, 3]` | Left intrinsics |
65
+ | `dist_left` | `[8]` | Left distortion `[k1, k2, p1, p2, k3, k4, k5, k6]`, zero-padded |
66
+ | `R_left` | `[3, 3]` | Left rotation (identity in the usual convention) |
67
+ | `t_left` | `[3]` | Left translation (zero in the usual convention) |
68
+ | `K_right` | `[3, 3]` | Right intrinsics |
69
+ | `dist_right` | `[8]` | Right distortion |
70
+ | `R_right` | `[3, 3]` | Rig rotation, left → right |
71
+ | `t_right` | `[3]` | Rig translation, in calibration units |
72
+ | `min_depth` | scalar | Near bound of the depth search |
73
+ | `max_depth` | scalar | Far bound of the depth search |
74
+
75
+ Grayscale must use BT.601 luma (`0.299 R + 0.587 G + 0.114 B`) to match the OpenCV
76
+ `BGR2GRAY` the reference implementation uses.
77
+
78
+ ## Outputs
79
+
80
+ | Name | Shape | Meaning |
81
+ | --- | --- | --- |
82
+ | `right_points` | `[P, 2]` | Matched points in right-image pixels |
83
+ | `best_score` | `[P]` | Best NCC score |
84
+ | `second_score` | `[P]` | Best NCC score outside a `template_size` neighborhood of the winner |
85
+
86
+ ## Acceptance thresholds
87
+
88
+ The graph deliberately does **not** apply a score threshold — it returns the best match
89
+ unconditionally, so the host decides what to accept. VIAME's two reference hosts disagree,
90
+ and both are defensible:
91
+
92
+ | Host | Threshold | Uniqueness ratio |
93
+ | --- | --- | --- |
94
+ | `interactive_stereo_template.conf` (DIVE desktop) | 0.5 | none |
95
+ | `plugins/onnx/run_epipolar_onnx.py` | 0.2 | 0.85 |
96
+
97
+ The uniqueness test, where used, rejects a match when
98
+ `second_score / best_score > ratio` — i.e. the winner was not clearly better than an
99
+ unrelated candidate elsewhere on the curve. Useful on repetitive texture.
100
+
101
+ ## Search range
102
+
103
+ The graph takes a **depth** range, but disparity is usually the more natural way to think
104
+ about it. Convert with:
105
+
106
+ ```
107
+ min_depth = fx * baseline / max_disparity
108
+ max_depth = fx * baseline / min_disparity
109
+ ```
110
+
111
+ where `fx = K_left[0][0]` and `baseline = ||t_right||`.
112
+
113
+ A disparity range of **2–300 px** matches the DIVE desktop interactive stereo config, but
114
+ this is scene-dependent: VIAME's batch measurement pipelines ship 7–724 for other rigs. Too
115
+ wide invites false matches; too narrow misses the target entirely. Calibrate it to how far
116
+ the same object actually shifts between your two cameras.
117
+
118
+ ## Usage
119
+
120
+ ### Python
121
+
122
+ ```python
123
+ import numpy as np, onnxruntime as ort
124
+
125
+ sess = ort.InferenceSession("stereo_match.onnx", providers=["CPUExecutionProvider"])
126
+ out = sess.run(None, {
127
+ "left_gray": left.astype(np.float32), # [H, W], 0-255
128
+ "right_gray": right.astype(np.float32),
129
+ "points_left": np.array([[330.4, 234.8]], np.float32),
130
+ "K_left": K1, "dist_left": d1, "R_left": np.eye(3, dtype=np.float32),
131
+ "t_left": np.zeros(3, np.float32),
132
+ "K_right": K2, "dist_right": d2, "R_right": R, "t_right": T,
133
+ "min_depth": np.float32(fx * baseline / 300),
134
+ "max_depth": np.float32(fx * baseline / 2),
135
+ })
136
+ right_points, best_score, second_score = out
137
+ accepted = best_score >= 0.5
138
+ ```
139
+
140
+ ### Browser / Node (onnxruntime-web)
141
+
142
+ The graph is small and CPU-only, so it runs comfortably in a browser via the WASM
143
+ execution provider — this is how [DIVE](https://github.com/Kitware/dive) warps a detection
144
+ from one camera to the other with no backend. A complete client-side implementation lives
145
+ in `client/dive-common/use/stereo/` (see
146
+ [Kitware/dive#1709](https://github.com/Kitware/dive/pull/1709)), covering calibration
147
+ parsing, the search-range conversion above, and two-view triangulation.
148
+
149
+ ### Not a transformers.js model
150
+
151
+ This is a bespoke geometry graph, not a transformer. It has no `config.json`, no tokenizer
152
+ or image processor, and no architecture in the transformers.js registry, so `pipeline()` /
153
+ `AutoModel` will not load it. Use onnxruntime (or onnxruntime-web) directly.
154
+
155
+ ## Swapping left and right
156
+
157
+ The graph always matches *left → right*. To warp a point annotated on the right camera,
158
+ invert the rig instead of swapping the inputs: the new world frame is the old right camera,
159
+ so `R' = Rᵀ` and `T' = -Rᵀ·T`, with the intrinsics and distortion swapped between sides.
160
+
161
+ ## Accuracy
162
+
163
+ Validated against the VIAME C++ / Python reference implementation to roughly a quarter
164
+ pixel. Note the exporter's own verification reports a sub-pixel `right_points` difference
165
+ between eager PyTorch and onnxruntime (about 0.04 px at `template_size=13`) caused by
166
+ ties between equally-scoring epipolar candidates; scores match to ~1e-8.
167
+
168
+ ## License and provenance
169
+
170
+ CC-BY-4.0. Produced by `plugins/onnx/export_stereo_mapping.py` in
171
+ [VIAME](https://github.com/VIAME/VIAME), whose core infrastructure is BSD-3-Clause.