havel: flat-field harmonize the satellite mosaic quilt#106
Conversation
The Havel ver-9 skin is a quilt of unrelated ESRI acquisitions (season,
sun-angle, white-balance differences) with visible tile seams — a big hazy
grey tile dominated the mid-lower region and two pale strips ran down the
right. Raw, it reads as arbitrary bright/dark patches, not terrain.
An earlier global per-channel "Weißabgleich" made it worse: a global stretch
pushes hazy tiles toward white (near-white 0.32% -> 5.17%). White balance has
no notion of "this brightness is a seam."
Replace it with a flat-field pass (scripts/harmonize_demgrid.py, run offline
on the .demgrid before the bake):
- radius-80 Gaussian estimates the quilt's low-frequency exposure field;
- subtract (low - global_mean) * 0.80 to flatten it toward one mean while a
genuinely dark forest vs bright field block survives (1.0 erases them);
- water protection: dark pixels flatten less, so lakes/river stay dark;
- chroma compression toward a common vegetation magnitude (target 28).
Only the rgb block is rewritten; header/lats/elev are copied byte-for-byte,
so geometry and elevation are untouched. Near-white 5.17% -> 0.10%; the grey
tile and pale strips pull into a coherent forest green. Ships the 19.4M-vert
rebake as cockpit/public/havel.v8grid.soa.gz; the script reproduces the
shipped .demgrid byte-for-byte.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012jEwwaT5JZ5x8qWvcnaMYC
|
Warning Review limit reached
Next review available in: 39 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (2)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_d3acdef1-4b27-4a73-b809-1f38134f15ff) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4657a8d7d3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| harm = harmonize(dem["rgb"], args.strength, args.radius, args.chroma, args.water_knee, args.water_soft) | ||
| write_demgrid(args.output, dem, harm) |
There was a problem hiding this comment.
Keep classification separate from the harmonized skin
When this harmonized .demgrid is fed to the standard iceland_dem --grid --skin bake, these rewritten RGB bytes are also the input to classify_kind (geo/src/bin/iceland_dem.rs:327-328), not just the photoreal skin. That makes the flat-field/chroma grade change the BSO2 kind block that Topo mode renders via palette[kind] and that wet masks read; in the shipped Havel asset I compared against the parent, 8,151,475 of 19,399,908 kind entries changed while geometry stayed byte-identical. For a skin-only harmonization, preserve the original imagery for kind classification or carry a separate harmonized skin so topo/material semantics do not shift with color grading.
Useful? React with 👍 / 👎.
What
The Havel ver-9 satellite skin is a quilt — a mosaic of unrelated ESRI acquisitions differing in season, sun angle, and white balance, with visible tile seams. A big hazy grey acquisition tile dominated the mid-lower region and two pale strips ran down the right side. Used raw as a terrain texture, the quilt reads as arbitrary bright/dark/desaturated patches rather than as terrain.
This PR replaces the earlier global white-balance step with a flat-field harmonization and ships the rebaked asset.
Why the previous approach made it worse
The earlier "Weißabgleich" was a global per-channel percentile stretch. A global stretch has no notion of "this brightness is a seam, not the scene", so it pushed the hazy tiles toward white — near-white pixels went 0.32% → 5.17% (16×), amplifying the exact quilt it was meant to hide.
The fix — flat-field harmonization
scripts/harmonize_demgrid.py, run offline on the.demgridbefore the bake (the global WB is dropped):(low − global_mean) · 0.80to flatten that field toward one global mean.0.80removes the seams while a genuinely dark forest block vs a bright agricultural block survives (1.0erases them into a muddy uniform green — tried and rejected).Only the
rgbblock is rewritten; header /lats/elevare copied byte-for-byte, so geometry and elevation are untouched.Result
Files
cockpit/public/havel.v8grid.soa.gz— the 19.4M-vert rebake of the harmonized skin (67.8 MB gz; on-demand release-hosting is the separate follow-up).scripts/harmonize_demgrid.py— the reproducible transform. Verified it reproduces the shipped.demgridbyte-for-byte with the Havel-approved--strength 0.80 --radius 80 --chroma 28.claude-notes/plans/2026-07-10-garmin-dem-satellite-skin.md— round write-up.Data-asset + standalone Python script + doc only — no Rust/TS code changed, so the workspace build/tests are unaffected.
🤖 Generated with Claude Code
Generated by Claude Code