Conversation
f1a7e66 to
0fb5ca9
Compare
15167e5 to
b9925bc
Compare
8fce4c3 to
9c23856
Compare
|
Follow-ups from the first SR-hardware run. I rebased onto the updated R1 and force-pushed with lease;
Verified: macOS build, |
|
+1.7 px vertical residual on the SR laptop: diagnosed. The runtime rectifier is correct; the calibration doesn't match the frames. Data: David's upload from the SR laptop (device I rectified the same raw frame with OpenCV
OpenCV's P1 also matches the runtime's to 0.5 px in f and 0.01 px in the principal point. So the rectifier reproduces OpenCV exactly, and OpenCV has the same residual: the ymls don't describe these frames perfectly. The residual is not a pure offset: a fit gives dy ≈ +2.6 px at the centre with a −0.47 px / 100 px slope in y, which reads as a small pitch/vertical-scale mismatch, not a single cy error. (That kills my "one eye's cy" lead hypothesis.) Most likely cause: the ProgramData ymls are a cache and not what SR uses at runtime. The SR Server log on the 8K box reads "Fetching Stereo Camera calibration data from FPC", i.e. the live calibration comes from the device itself. Next:
Script: OpenCV 4.10, SIFT 4000, ratio 0.7. 🤖 Generated with Claude Code |
|
Online vertical-alignment refinement added ( What it does
Results (macOS)
On-hardware check (Windows SR box)
Not done:
🤖 Generated with Claude Code |
|
Review of f433d31 (PRESENT_OWNER quota counts distinct owner executables), from mac/holocall: approve for this branch.
🤖 Generated with Claude Code |
…emap, no OpenCV (R2) Vendor-neutral rectifier for XR_DXR_stereo_camera (ADR-043 R2), in three layers so a GPU path can replace only the last one: - geometry: OpenCV's stereoRectify(CALIB_ZERO_DISPARITY, alpha = 0) in C (half rotations + baseline onto x, min-fy focal, corner-centroid principal point, 9x9 inner-rectangle zoom), plus a border check of the real maps that zooms further until no output pixel samples outside its raw image; - float maps (what a GPU backend uploads as an RG32F texture); - CPU backend: fixed-point bilinear taps over the whole SBS image, 1/2/4 channels (GRAY8, NV12 Y + half-res UV, BGRA8). RADTAN5 / RADTAN8 / KB4 lens models, calibration rescaled when it was taken at a different size than the frames. Also u_stereo_camera_estimate_offset: SSD block matching over dx AND dy, for row-alignment checks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd truth (R2) SIM_DISPLAY_FAKE_STEREO_CAMERA_DISTORT=1 renders the fake's scene through two RAW cameras: different per-eye intrinsics, RADTAN5 barrel lenses, and each camera turned by half of (pitch 0.35, yaw 0.25, roll 0.45 deg) in opposite senses (~6 px vertical misalignment, 0.9 deg relative roll). The symmetric split makes Bouguet recover exactly the virtual parallel pair the scene is defined in, so the ground truth after rectification is exact: aligned rows and disparity f_rect * B / Z. The camera drops NATIVELY_RECTIFIED and its calibration slot returns the truth, so RECTIFIED output exercises the service's rectifier. The scene is sampled continuously (bilinear value noise), so resampling is honest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the distorted fake tests_stereo_rectify, with fixtures generated offline by tests/fixtures/gen_stereo_rectify_fixtures.py (OpenCV 4.10) for three calibrations (the sim fake, an SR-like pair with a non-axial baseline, a RADTAN8 pair calibrated at 2x the frame size): - R1/R2 equal OpenCV's to 1e-12; principal point equals the converged reference to 1e-4 px (OpenCV's own P1 within 0.75 px: it stops undistortPoints after 5 iterations); focal within 1 % (measured +0.05..0.13 %); - maps equal initUndistortRectifyMap to 3e-5 px; - alpha = 0 really leaves no black pixel (full and half resolution); - end to end: raw |dy| median 2.9 px -> rectified worst block 0.061 px; disparity 11.33 / 37.77 px ground truth, worst block error 0.063 px; - NV12 / BGRA8 planes agree with GRAY8; hidden [.perf] case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…fied calibration (R2) A per-camera rectifier is built at manager create from the plug-in's RAW calibration when it is CALIBRATED and not NATIVELY_RECTIFIED. It runs once per source frame on the camera thread, outside the manager lock, and only while a started stream wants RECTIFIED; RAW streams read the original frame. A RECTIFIED stream never receives unrectified pixels (a frame produced before it started is skipped). get_calibration(RECTIFIED) returns the same geometry: one pinhole for both eyes (zero disparity at infinity), no distortion, rightFromLeft = identity + (+baseline, 0, 0). horizontalFovDeg is the rectified one. The browser (PRESENT_OWNER) is refused a camera the rectifier rejected instead of getting the RAW-flagged fallback native clients get. The seam is struct scam_rectifier (backend-neutral geometry + an apply() backend: CPU LUT today). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lignment, --rectified gates it Every probe now 2-D block-matches the last frame (textured 32x32 blocks) and prints |dy| median / p90 / blocks above 1 px, plus the dominant disparities as depths Z = f*B/d from the RECTIFIED calibration. probe --rectified asks for RECTIFIED output and exits 5 when frames come back RAW or the median |dy| exceeds 0.5 px. Against the distorted fake: median 0.016 px, depths 2.004 m and 0.599 m (truth 2.0 / 0.6); --raw shows the 2.6 px it fixes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…measured numbers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… camera at -x A swapped SBS pair still rectifies (Bouguet does not care), but every disparity comes out negative and rightFromLeft points the wrong way. The L1 Leia provider decides eye order from the sign of T; this makes a wrong decision visible in the service log instead of only in a probe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MSVC only defines M_PI when _USE_MATH_DEFINES precedes the first <cmath>, which Catch may already have pulled in. Use a local constant. First MSVC build of R2; the test passes (716 assertions, 7 cases). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…, signed dy, bound hits flagged First SR-hardware run: a face at 0.6 m on the 120 mm tracker is ~95 px of disparity, past the probe's 0..64 search, and the SSD matcher's +-10 px dy window clamped too — it reported "d = 64, |dy| = 10.000" as if measured. - u_stereo_camera_match_block: zero-mean NCC (blind to the two sensors' gain/offset), coarse pass on every 2nd pixel + full-res hill-climb, parabolic sub-pixel on both axes, the searched window returned, and dx_at_edge / dy_at_edge when the integer peak sits ON a bound (a clamp, not a measurement). - camera probe: disparity window f*B/0.4 m from the camera (R1 helper), dy +-24 px, accept NCC >= 0.90; prints accepted / low-NCC / at-bound counts, warns when > 20 % of blocks hit a bound, reports the SIGNED dy median, and --rectified now also fails on a constant offset (|signed median| > 0.5 px). --max-disparity / --max-dy / --min-ncc override. - sim fake: the DISTORTED variant honours _BASELINE_MM too (truth + slot). - tests: large-baseline matcher (bar 94.9 px, signed dy +1.68 recovered to 0.07 px, gain/offset, flat refused, the old 64 px window is REJECTED not reported); END TO END at 120 mm (bar 90.64 px GT, measured 90.66); the [vshift] hypotheses for the field's +1.7 px residual (per-eye cy error = constant dy ~ -dcy, transposed R = large roll-shaped dy, calib-size rescale and the plug-in's half swap exact, identity = pure scale about cy). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…torted fake (R2) SIM_DISPLAY_FAKE_STEREO_CAMERA_DY (px) and _DY_SLOPE (px per 100 px) shift the right eye's rows on top of the DISTORTED pair without telling the reported calibration, like a device whose stored calibration is slightly off (the Leia SR laptop: +2.6 px, -0.47 px / 100 px). Drives the online refinement's tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…reo_vrefine) On a Leia SR laptop the rectified rows stayed +1.71 px apart, and OpenCV's stereoRectify reproduces that on the same frame: the stored calibration is off, not the rectifier. u_stereo_vrefine measures the residual dy = a + b (y - cy) on rectified frames (Shi-Tomasi patches, half-res NCC search, full-res NCC refinement, gated on NCC, d > 0, unclamped peaks and vertical curvature), fits it robustly (median + Tukey IRLS) and runs the controller: duty cycle, >= 50 matches over >= 3 frames, 0.2 px deadband, |a| <= 6 px, |b| <= 2 px/100 px. u_stereo_rectify folds the correction in as v_offset = a/f, v_slope = b: a symmetric per-eye vertical affine in normalized rectified coordinates applied BEFORE the principal point and the alpha = 0 crop, so P1/P2 describe the corrected frames. Adds u_stereo_rectify_rect_from_raw (the inverse map). tests_stereo_vrefine: the fake with the SR model converges to +0.000 px in one rebuild; degenerate frames never apply; clamps hold; and a REAL SR raw frame (62 KB JPEG + calibration + 63 OpenCV SIFT matches as an independent judge): signed dy median +1.715 px -> +0.075 px. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…reports it A per-camera worker measures rectified frames the camera thread parks for it (every 250 ms for 5 s after an open or an update, then every 30 s; 2.7 ms per measurement at -O2, off the frame path), rebuilds geometry + LUTs when the correction moves, and the camera thread swaps them in between frames and bumps calibration_generation. WARN on first application, INFO after, re-verified on re-open, DXR_STEREO_CAMERA_REFINE=0 kill switch. Stream stats carry the state, applied a/b, match count and dy before/after; camera probe prints them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… |dy| median On the SR laptop with a face at 0.6 m the refined rows were aligned (signed median -0.10/-0.18 px) but --rectified FAILED: block-match noise alone put the unsigned |dy| median at 1.03/1.10 px. Gate on |signed median| <= 0.5 px, MAD about it <= 1.5 px and >= 20 blocks; print the gate numbers; keep |dy| stats informational. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tions
On the SR laptop the B1 browser's "3D Camera (DisplayXR)" never appeared:
[CAP] refusing client pid=... ('ChromiumStereoCamera'): class PRESENT_OWNER
quota 2/2 exhausted (#960). One browser already takes both slots (GPU
process weaves, browser process binds the window), so its video-capture
utility - the XR_DXR_stereo_camera client, which declares PRESENT_OWNER
by enabling XR_DXR_weave - was the third connection and was refused
(xrCreateInstance -10 in the capture service).
A connection from the same executable as an admitted PRESENT_OWNER is now
a sibling and takes no new slot; the quota is compared against DISTINCT
owner executables, so two different browsers still fill it. A peer whose
image path is unreadable is never a sibling (fails closed).
Verified on the SR laptop: all three Chromium processes admitted, the
page lists and opens "3D Camera (DisplayXR)" (sbs 1280x480), stream
RECTIFIED.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
8a8c30f to
cd2927a
Compare
f433d31 to
0191f54
Compare
This PR is phase R2 of the stereo camera source (ADR-043, spec, roadmap). The service now rectifies any calibrated camera itself, in vendor-neutral C with no OpenCV dependency. Clients get RECTIFIED frames and rectified calibration. RAW frames are still refused to the browser.
Stacked on #1746 (
feat/stereo-camera-r1); merge #1746 first. Rebased 2026-10-03 onto the post-lift R1 (lift #1781 is merged, v2.22.0): order is now R1 → R2 → L1 → B1; the ABI positions (camera slots aftercreate_dp_d3d11_lift+get_platform_state, type values1004999300–310) are in #1746's rebase comment. The Windows tester'sf433d31fc(PRESENT_OWNER owner quota) survives as0191f5481. The first real source is the Leia provider (L1): DisplayXR/displayxr-leia-plugin#273, which carries the on-hardware recipe.What's in it
auxiliary/util/u_stereo_rectify.{h,c}has three layers, so a GPU path can replace only the last one:u_stereo_rectify_computere-implements OpenCV'sstereoRectify(CALIB_ZERO_DISPARITY, alpha = 0). Each camera is rotated by half the relative rotation, the baseline is put on the x axis, focal = min fy, the principal point is the corner centroid, and the image is zoomed to the valid region. The zoom is then checked on every border pixel of the real maps and increased until no output pixel samples outside its raw image. No convergence shear is applied.u_stereo_rectify_build_mapis what a GPU backend uploads as an RG32F texture.ipc_server_stereo_camera.c):struct scam_rectifierper camera is built at create time from the plug-in's RAW calibration, whenever the camera isCALIBRATEDand notNATIVELY_RECTIFIED. The WARN line reports f, principal point, baseline, zoom and build time.get_calibration(RECTIFIED)returns the same geometry: one pinhole for both eyes, model NONE, andrightFromLeft= identity plus (+B, 0, 0).horizontalFovDegis the rectified value.PRESENT_OWNER) is refused (INPUT_UNSUPPORTED) a camera the rectifier rejected. It no longer gets the RAW-flagged fallback that native clients get.rectify ms/frame.SIM_DISPLAY_FAKE_STEREO_CAMERA_DISTORT=1produces a raw pair with exact ground truth:camera calib <id> --rectifiedprints P1/P2 andZ = f·B/d.probenow reports row alignment: 2-D block matching over dx and dy on textured blocks, giving |dy| median / p90 / blocks > 1 px, and the dominant disparities converted to depths.probe --rectifiedexits 5 if the frames come back RAW or the median |dy| is above 0.5 px.Test evidence (macOS, M1 Pro)
tests_stereo_rectify(716 assertions) uses golden fixtures fromtests/fixtures/gen_stereo_rectify_fixtures.py(OpenCV 4.10, run offline, header committed). There are 3 calibrations: the sim fake, an SR-like pair with a non-axial T, and a RADTAN8 pair calibrated at 2× the frame size.undistortPointsafter 5 iterations; we iterate to convergenceinitUndistortRectifyMapEnd to end on the distorted fake:
NV12 and BGRA8 planes are bit-identical to GRAY8 where comparable.
tests_stereo_camera(R1) andtests_ipc_protostill pass.Live service + CLI (Debug service,
SIM_DISPLAY_FAKE_STEREO_CAMERA=1 _DISTORT=1 DXR_STEREO_CAMERA_DEV_ALLOW=1):NATIVELY_RECTIFIED) fake is unchanged: no rectifier is built, disparities are 12.00 / 40.00 px, and |dy| median is 0.004 px.MinGW-w64:
build-mingw-check.sh aux_util drv_sim_displaypasses, with no warnings from the new files. It caught a-Wpedanticarray-qualifier issue, which is fixed.Performance (CPU budget)
Measured single-threaded on an M1 Pro at -O2, per 1280×480 frame:
u_stereo_rectify_lut_apply_rows, or move to the GPU backend.Deviations from the design
undistortPoints(OpenCV stops at 5 iterations).Not tested
calibrationGenerationstays 0, and the rectifier is built once per service lifetime.stereoRectifydoes not model fisheye lenses.🤖 Generated with Claude Code