From 6b46eb5128680c40971f76e16a5ecd0bbced35d7 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 3 May 2026 13:17:36 +0000 Subject: [PATCH] composite: use east frame background with bilateral filter at 70% zoom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch monthly pipeline from render_phase_closeup (black background) to composite_full_moon (east frame + NASA moon overlay) when an east frame and detection are available. Falls back to render_phase_closeup when east verification is disabled. Changes: - composite_full_moon: bilateral filter (d=9, σ=75) on upscaled background to smooth pixelation artifacts before compositing the NASA moon - composite_full_moon: caption now placed below the moon disk instead of overlapping it (paste_y + target_size + 12) - run_phase: keep detection result (best_det) alongside best_frame - _render_and_post: accept east_frame + detection, route to the right renderer - Monthly pipeline uses moon_height_pct=0.70 for the east-frame composite https://claude.ai/code/session_01JieSeNQZ3X6fhsb11YyJrK --- moon_composite.py | 10 ++++++++-- moon_phase_monthly.py | 31 +++++++++++++++++++++++-------- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/moon_composite.py b/moon_composite.py index 315ba38..374a234 100755 --- a/moon_composite.py +++ b/moon_composite.py @@ -191,6 +191,11 @@ def composite_full_moon( crop = src.crop((left, top, left + crop_w, top + crop_h)) bg = crop.resize(output_size, LANCZOS) + # ── Smooth upscaling artifacts in the sky background ── + import cv2 + bg_arr = cv2.bilateralFilter(np.array(bg), d=9, sigmaColor=75, sigmaSpace=75) + bg = Image.fromarray(bg_arr) + # ── Where is the moon's center within the upscaled background? ── moon_x_in_crop = cx - left moon_y_in_crop = cy - top @@ -230,10 +235,11 @@ def composite_full_moon( paste_y = max(0, min(paste_y, output_size[1] - target_size)) bg.paste(ref_rot, (paste_x, paste_y), mask) - # ── Caption ── + # ── Caption — below the moon disk, not overlapping it ── if caption: draw = ImageDraw.Draw(bg) - _draw_caption(draw, caption, (22, output_size[1] - 48), output_size[0]) + caption_y = min(paste_y + target_size + 12, output_size[1] - 30) + _draw_caption(draw, caption, (22, caption_y), output_size[0]) Path(out_path).parent.mkdir(parents=True, exist_ok=True) bg.save(out_path, quality=92) diff --git a/moon_phase_monthly.py b/moon_phase_monthly.py index 5c180ea..6f886e9 100755 --- a/moon_phase_monthly.py +++ b/moon_phase_monthly.py @@ -337,11 +337,13 @@ def run_phase(phase: str, target_utc: datetime | None, cam: str, dark_frames.sort(key=lambda x: abs(x[1] - target_utc)) best_frame: str | None = None + best_det = None best_dt: datetime | None = None for fpath, fdt in dark_frames: det = detect_moon(fpath) if det is not None and det.quality >= MIN_QUALITY: best_frame = fpath + best_det = det best_dt = fdt offset_min = int((fdt - target_utc).total_seconds() / 60) print(f'best frame: {pathlib.Path(fpath).name} quality={det.quality:.3f} ' @@ -368,11 +370,14 @@ def run_phase(phase: str, target_utc: datetime | None, cam: str, phase, spec, target_utc, best_dt, cam, dry_run, no_upload, out_path, witness_text=f'sky-cam {cam} — {illum_pct}% lit — {_format_local(best_dt)}', + east_frame=best_frame, + detection=best_det, ) def _render_and_post(phase, spec, target_utc, when_utc, cam, - dry_run, no_upload, out_path, witness_text): + dry_run, no_upload, out_path, witness_text, + east_frame=None, detection=None): if out_path is None: out_dir = pathlib.Path(MOVIES_DIR) / cam / _output_subdir(phase) out_dir.mkdir(parents=True, exist_ok=True) @@ -393,17 +398,27 @@ def _render_and_post(phase, spec, target_utc, when_utc, cam, print(f'dial-a-moon: {nasa_path}') illum_pct = round(_mp.illumination(when_utc) * 100) - from moon_composite import render_phase_closeup caption = ( f"{spec['label']} — {illum_pct}% lit — {target_utc.strftime('%B %Y')} — " f"{witness_text} — NASA SVS Dial-a-Moon" ) - render_phase_closeup( - str(nasa_path), out_path, - output_size=(OUT_W, OUT_H), moon_height_pct=MOON_PCT, - caption=caption, - when_utc=when_utc, - ) + if east_frame is not None and detection is not None: + from moon_composite import composite_full_moon + composite_full_moon( + east_frame, detection, when_utc, + str(nasa_path), out_path, + output_size=(OUT_W, OUT_H), + moon_height_pct=0.70, + caption=caption, + ) + else: + from moon_composite import render_phase_closeup + render_phase_closeup( + str(nasa_path), out_path, + output_size=(OUT_W, OUT_H), moon_height_pct=MOON_PCT, + caption=caption, + when_utc=when_utc, + ) print(f'wrote {out_path}') if no_upload: