Repository navigation
v0.3.0: track cleaning, pole-first search, per-camera optical centre - #1
Merged
Merged
Conversation
… them When a camera sends nothing, the CDN serves a 320x240 "No Images!" card under each frame's name. nights.frame_files splits a block's frames into real ones and these placeholders (read from the JPEG header only), and read_frames and frame_at skip the placeholders. A block that is mostly placeholders fails with reason "no images: ...", and its overlay is one of the placeholder cards. The gallery shows them as a fourth outcome, noimg.
…er version solve and solve_wide take an optional `trace` list and append one dict per step to it: the attempt, the pole fit and its kept samples, the coarse scan, the starts, every rung of every start's refinement, and the verdict. Nothing reads it back, so results don't change. - animate.py and `calibrate animate [--kept] [--full] [--mark A-B,C]`: the trace played back as an H.264 video (through ffmpeg; OpenCV's mp4v without it). - explore.py and `calibrate explore`: the trace as data, every star position projected in Python by the solver's model, for hpwren/explore.html, a replay page to scrub, zoom and inspect. It masks stars too far off the boresight, where the lens polynomial folds back and would put stars behind the camera in the frame. - The gallery links a block's video and replay when they exist. overlay.draw(full=True) draws the whole frame. - Results and ledger entries name the solver that made them (`solver`: the version, and the commit when run from a checkout, `.dirty` when it differed). - docs/records.md describes every record the package ships or writes, and tests/test_records.py fails on a field it doesn't mention. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o the frame Tracks (tracks.py, hpwren/calibrate.py): - tracks.clean splits tracks the nearest-neighbour linker handed from one star to another (tracks.split: RANSAC a cubic path per star, cutting at gaps over 10 min), drops squiggly tracks (cloud texture, haze) unless one smooth part holds half their points, drops near-stationary tracks in HPWREN's clock banner, and drops duplicates. - load_tracks closes a colour camera's track after 300 s unseen (it had no limit within a block), and the pickle records the link and clean recipes it was made with, so a changed recipe rebuilds it. Fold-back (fisheye.py): project_fisheye returns NaN past 100 deg off the boresight or the lens polynomial's fold, whichever comes first, so a star behind the camera no longer lands in the frame. explore.py drops its own mask; the solver, overlay and video handle NaN. On the cache's CDN blocks: 130 -> 133 solved (smer-tcs8 09-11 at d_az +39.5, bm-e 09-19, rdd-e 09-20, wlfd-s 09-20 gained; bi-s 09-20 lost to the coarse scan), poses of blocks solved both times moved 0.011 deg median, and the five solved blocks whose pole fit sat at |p| 0.47-0.59 (banner tracks) are at 0.99-1.01. docs/records.md has the measurements. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e grid only when needed - Level-prior starts (solve._level_starts): of the pole's one-angle family of poses, the right one is almost always near where pitch and roll are both small. Near each of the two such places the best-scoring angle within 8 deg becomes an extra start. - solve_wide runs the pole attempts first and the published-pose grid only if they haven't solved with 12+ stars (grid="fallback", sure_stars=12; grid="always" is the old order). The grid's old job, rescuing blocks with a low |p|, went with the banner tracks. Over 180 CDN blocks: bi-s 09-20 solves (the coarse scan had no start for it), no pose moves, and solving is about five times faster. On a thin solve the grid still runs, since it can end in a tighter fit of the same stars (mp-n 09-11: 0.90 px against the pole's 1.32). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e through it The solver assumed each lens's optical centre was the middle of the frame. On HPWREN's units it sits a median 32 px off, stable night to night. - A camera record may carry cx, cy (pixels right and down from the middle); fisheye, pole.pixel_to_cam/estimate/lens_scale and solve use them. Solve results record the centre they assumed, the overlay draws a result through its own centre, and ledger entries carry cx, cy (an interface change: a caller turning pixels into bearings with a ledger pose must apply its centre; entries without one mean (0, 0)). - intrinsics.py fits one centre and lens per camera jointly across its solved nights, with a pose per night, as a series that starts a new segment where the centre jumps. A segment with fewer than 2 nights and 20 stars keeps the middle. - `calibrate intrinsics` writes hpwren/intrinsics.json; hpwren.camera() gives the solver a camera's record with the centre for its frame size and date. intrinsics.json is fitted from the cache's 137 solved CDN blocks: 47 of 76 cameras get a centre, and a camera's single-night centres sit a median 2.5 px from its joint centre. Re-solved through it: +bm-s 09-11 and +bi-s 09-20 over the track cleaning alone, median residual 1.23 -> 0.78 px, median stars 25 -> 28, and two solves of one camera within 30 days agree to 0.015 deg median and 0.05 deg worst (0.026 and 0.75 before). Poses move a median 0.76 deg and up to 3.1 deg (bm-e, centre (-63, +39) px), each by about its centre offset. The shipped pose_ledger.json is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…centre The ledger gains cx/cy (a stable-record change), so this is a minor bump. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Regenerated with `calibrate ledger` from the cache re-solved at a10c451, so every entry's solver is 0.3.0+ga10c45148b. It keeps all 106 camera-nights of the ledger it replaces (assembled in plume-triangulation, tag results-2026-09-26) and adds 31. The READMEs and records.md quote its numbers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ses are off HPWREN's table describes each camera's view, which is what the network was built for; triangulation is a use it wasn't designed around. Drop the count of off-azimuth cameras and the camera names, and describe the lens, centre and dated poses as what this adds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
v0.3.0: track cleaning, pole-first search, per-camera optical centre
This release changes the solver in four ways. More blocks solve, the fits are tighter, and two nights of the same camera now land on the same pose to within 0.05°. The ledger gains the optical centre (
cx,cy). That changes a stable record, so plume-triangulation has to adopt this release in a coordinated change.What changes
Track cleaning (
tracks.clean, 7ac45b8). The nearest-neighbour linker lets a track wander:Each linked track is now split into the stars it contains, by fitting one smooth cubic path per star (RANSAC, 3 px). Squiggles, the camera's burned-in banner text and repeated tracks are dropped. The tracks pickle keeps both the linked and the cleaned tracks, each with the settings that made it. The linker's gap limit for colour cameras is 300 s.
The fold-back fix (7ac45b8). With k1 < 0 the fisheye radius kθ(1 + k1θ²) peaks near 118° off-axis and then shrinks. Stars behind the camera were therefore projected back into the frame, and the wide search scored up to 4–5 of these phantom stars per pose.
project_fisheyenow returns NaN past min(100°, the peak).Pole-first search (7fbb62c).
solve_widestarts from the celestial pole implied by the trails, which fixes two of the three pose angles, and scans the third. It adds two starts where the camera would be level. The grid around the published pose runs only when the pole search hasn't already solved with 12 or more stars. bi-s 2026-09-20 solves with 14 stars, about 5× faster.Per-camera optical centre (6bbe217). The lens's optical axis sits a median 32 px from the frame's middle (up to 75 px, on bm-e). Solved as if centred, the pose tilted to make up for it.
intrinsics.pyfits one centre per camera from all its solved nights together, and records a new segment when the centre jumps. The result ships ashpwren/intrinsics.json, and every solve and ledger entry records the centre it used.calibrate intrinsicsrebuilds the file.Results
Every cached CDN block was re-solved at a10c451 (the version bump). All numbers below come from those solves.
The shipped ledger (649fdba)
hpwren/pose_ledger.jsonhas been regenerated withcalibrate ledger. It now holds 137 solves on 76 cameras, over 18 nights from 2026-07-14 to 2026-09-27. It keeps every one of the 106 camera-nights of the v0.2.1 ledger and adds 31. Each entry'ssolveris0.3.0+ga10c45148b. The READMEs anddocs/records.mdquote the new numbers.Interface change: plume-triangulation
cx/cyfields are documented indocs/records.md. An entry with no centre is treated as centred:ledger.lookupreads a missing value as 0.geom.pyandcorrected_cammust apply the centre. Otherwise v0.3.0 poses will be about as far off as the centre offset.stars.publishTesting
pytest: 96 tests pass at every commit.test_tracks.py), the centre fit (test_intrinsics.py), the fold, solves through a known centre on a synthetic night, andpixel_to_camwith a centre.Frames and camera metadata: HPWREN, https://www.hpwren.ucsd.edu/.
🤖 Generated with Claude Code