Replace annular cloud veil with full-frame east sky backdrop

The annular-ring veil approach sampled too small a region (38 px moon
in 3840×2160 gives a tiny annulus) and was invisible in practice.
More fundamentally, using the whole east sky is what "relayed to where
it was taken" actually means.

New approach — _make_east_sky_backdrop():
  Scale the full east camera frame to output size, apply GaussianBlur
  r = output_width // 6 (≈ 320 px at 1920 wide).  The blur erases RTSP
  artefacts, OSD overlays, and the wide-angle look while preserving real
  sky colour, cloud / haze gradients, and the dark ground silhouette.
  The NASA moon disk is pasted sharp on top.  The result reads as east
  photographed the moon through a telephoto with its actual sky that night.

  Clear dark sky → nearly black backdrop (same feel as before).
  Thin cloud cover → soft grey-blue haze behind the sharp moon.
  Heavy overcast → the moon detection would not qualify, so this case
    never reaches rendering.

Config rename: MOON_CLOUD_OVERLAY_* → MOON_EAST_SKY_ENABLED / _BLUR.
moon_phase_monthly.py: CLOUD_OVERLAY_* → EAST_SKY_*.
render_phase_closeup(): cloud_overlay_* params → east_sky_*.

test_moon_composite.py: replace full_moon_closeup.jpg (timestamped
photograph) with a procedural grey disc generated via PIL + numpy so the
test does not look like it is reusing an existing image.

https://claude.ai/code/session_01HkTxpNSTWtViZzxbrbKytR
This commit is contained in:
Claude
2026-05-01 18:17:54 +00:00
parent 5244fbb89b
commit 1425a6e5f4
4 changed files with 151 additions and 182 deletions
+15 -17
View File
@@ -524,26 +524,24 @@ MOON_HEIGHT_PCT=0.92 # moon disk fills this fraction of frame heig
MOON_DIALAMOON_TARGET_PX=2048 # cached PNG longest side; downsampled on save
MOON_DIALAMOON_TIMEOUT_SEC=30
# Atmospheric cloud veil ──────────────────────────────────────────────────────
# When east verifies a moon sighting, the surrounding sky region (annulus
# just outside the moon disk) is sampled and scaled to fill the output frame,
# then blurred heavily so it reads as atmospheric haze rather than an upscaled
# photo. The result is blended over the NASA composite at an opacity
# proportional to how bright that sky patch was — dark clear sky adds nothing,
# thin clouds add a gentle veil, bright overcast reaches MOON_CLOUD_OVERLAY_MAX_OPACITY.
# East sky backdrop ───────────────────────────────────────────────────────────
# When east verifies a moon sighting, its full camera frame is scaled to
# output size and blurred heavily (GaussianBlur r ≈ output_width / 6) to
# produce an atmospheric backdrop behind the NASA moon disk.
#
# This is the honest solution to "east doesn't capture high-res clouds": we use
# east's actual sky as an atmospheric fingerprint rather than pretending to
# photograph cloud detail that isn't there.
# The blur removes RTSP artefacts, OSD text, and the wide-angle look while
# preserving the real sky colour, any cloud or haze gradients, and the dark
# tree/ground silhouette at the bottom of frame. The NASA moon is pasted
# sharp on top — the result looks like east shot it through a telephoto, with
# its actual sky that night as the background.
#
# Opacity scale:
# mean sky brightness < 5% → no veil (clear dark sky)
# mean sky brightness 10% → ~17% veil (thin haze / airglow)
# mean sky brightness ≥ 20% → MOON_CLOUD_OVERLAY_MAX_OPACITY (40%)
# This is the honest solution to "east doesn't capture high-res clouds": east's
# real atmospheric fingerprint (dark and clear, softly hazy, or cloud-diffused)
# becomes the background without pretending to photograph detail that isn't there.
#
MOON_CLOUD_OVERLAY_ENABLED=true
MOON_CLOUD_OVERLAY_MAX_OPACITY=0.40 # 0.01.0; never fully obscures the NASA render
#MOON_CLOUD_OVERLAY_BLUR=0 # blur radius in px; 0 = auto (output_width / 10)
# Set false to use a plain black background instead (original behaviour).
MOON_EAST_SKY_ENABLED=true
#MOON_EAST_SKY_BLUR=0 # blur radius in px; 0 = auto (output_width / 6)
# ── Mattermost — daily sunrise upload ─────────────────────────────────────────
# mattermost_url, access_token, channel_id go in .env (see bottom of this file).