composite: use east frame background with bilateral filter at 70% zoom
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
This commit is contained in:
+8
-2
@@ -191,6 +191,11 @@ def composite_full_moon(
|
|||||||
crop = src.crop((left, top, left + crop_w, top + crop_h))
|
crop = src.crop((left, top, left + crop_w, top + crop_h))
|
||||||
bg = crop.resize(output_size, LANCZOS)
|
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? ──
|
# ── Where is the moon's center within the upscaled background? ──
|
||||||
moon_x_in_crop = cx - left
|
moon_x_in_crop = cx - left
|
||||||
moon_y_in_crop = cy - top
|
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))
|
paste_y = max(0, min(paste_y, output_size[1] - target_size))
|
||||||
bg.paste(ref_rot, (paste_x, paste_y), mask)
|
bg.paste(ref_rot, (paste_x, paste_y), mask)
|
||||||
|
|
||||||
# ── Caption ──
|
# ── Caption — below the moon disk, not overlapping it ──
|
||||||
if caption:
|
if caption:
|
||||||
draw = ImageDraw.Draw(bg)
|
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)
|
Path(out_path).parent.mkdir(parents=True, exist_ok=True)
|
||||||
bg.save(out_path, quality=92)
|
bg.save(out_path, quality=92)
|
||||||
|
|||||||
+23
-8
@@ -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))
|
dark_frames.sort(key=lambda x: abs(x[1] - target_utc))
|
||||||
|
|
||||||
best_frame: str | None = None
|
best_frame: str | None = None
|
||||||
|
best_det = None
|
||||||
best_dt: datetime | None = None
|
best_dt: datetime | None = None
|
||||||
for fpath, fdt in dark_frames:
|
for fpath, fdt in dark_frames:
|
||||||
det = detect_moon(fpath)
|
det = detect_moon(fpath)
|
||||||
if det is not None and det.quality >= MIN_QUALITY:
|
if det is not None and det.quality >= MIN_QUALITY:
|
||||||
best_frame = fpath
|
best_frame = fpath
|
||||||
|
best_det = det
|
||||||
best_dt = fdt
|
best_dt = fdt
|
||||||
offset_min = int((fdt - target_utc).total_seconds() / 60)
|
offset_min = int((fdt - target_utc).total_seconds() / 60)
|
||||||
print(f'best frame: {pathlib.Path(fpath).name} quality={det.quality:.3f} '
|
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,
|
phase, spec, target_utc, best_dt,
|
||||||
cam, dry_run, no_upload, out_path,
|
cam, dry_run, no_upload, out_path,
|
||||||
witness_text=f'sky-cam {cam} — {illum_pct}% lit — {_format_local(best_dt)}',
|
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,
|
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:
|
if out_path is None:
|
||||||
out_dir = pathlib.Path(MOVIES_DIR) / cam / _output_subdir(phase)
|
out_dir = pathlib.Path(MOVIES_DIR) / cam / _output_subdir(phase)
|
||||||
out_dir.mkdir(parents=True, exist_ok=True)
|
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}')
|
print(f'dial-a-moon: {nasa_path}')
|
||||||
|
|
||||||
illum_pct = round(_mp.illumination(when_utc) * 100)
|
illum_pct = round(_mp.illumination(when_utc) * 100)
|
||||||
from moon_composite import render_phase_closeup
|
|
||||||
caption = (
|
caption = (
|
||||||
f"{spec['label']} — {illum_pct}% lit — {target_utc.strftime('%B %Y')} — "
|
f"{spec['label']} — {illum_pct}% lit — {target_utc.strftime('%B %Y')} — "
|
||||||
f"{witness_text} — NASA SVS Dial-a-Moon"
|
f"{witness_text} — NASA SVS Dial-a-Moon"
|
||||||
)
|
)
|
||||||
render_phase_closeup(
|
if east_frame is not None and detection is not None:
|
||||||
str(nasa_path), out_path,
|
from moon_composite import composite_full_moon
|
||||||
output_size=(OUT_W, OUT_H), moon_height_pct=MOON_PCT,
|
composite_full_moon(
|
||||||
caption=caption,
|
east_frame, detection, when_utc,
|
||||||
when_utc=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}')
|
print(f'wrote {out_path}')
|
||||||
|
|
||||||
if no_upload:
|
if no_upload:
|
||||||
|
|||||||
Reference in New Issue
Block a user