Files
homepage/tools/make_logo_light.py

72 lines
2.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""
生成深色站反白版 logo(logo-light.png):
1. flood-fill 镂空深蓝底(容差 84 = v1 验证成功参数,能吃掉 #000020 底色)
2. 不透明内容亮度反转 L' = 1 - L(黑字→白字,红/青/绿提亮,色相保留)
深色页黑字看不清、白底块又突兀,反白是深色站标准解法。
用法: uv run --with pillow python3 tools/make_logo_light.py
"""
import colorsys
from collections import deque
from PIL import Image
SRC = 'assets/img/logo.png'
OUT = 'assets/img/logo-light.png'
TOL = 84 # 曼哈顿距离容差(v1 实测:清除 83%、红色主体完整保留)
im = Image.open(SRC).convert('RGBA')
w, h = im.size
px = im.load()
# --- flood-fill 清底 ---
corners = [px[0, 0], px[w-1, 0], px[0, h-1], px[w-1, h-1]]
bg = tuple(sum(c[i] for c in corners) // 4 for i in range(3))
def is_bg(p):
if p[3] < 10:
return True
return abs(p[0]-bg[0]) + abs(p[1]-bg[1]) + abs(p[2]-bg[2]) <= TOL
seen = [[False]*w for _ in range(h)]
q = deque()
for x in range(w):
q.extend(((x, 0), (x, h-1)))
for y in range(h):
q.extend(((0, y), (w-1, y)))
cleared = 0
while q:
x, y = q.popleft()
if x < 0 or x >= w or y < 0 or y >= h or seen[y][x]:
continue
seen[y][x] = True
p = px[x, y]
if not is_bg(p):
continue
px[x, y] = (0, 0, 0, 0)
cleared += 1
q.extend(((x+1, y), (x-1, y), (x, y+1), (x, y-1)))
ratio = cleared / (w * h)
assert 0.10 < ratio < 0.92, f'镂空异常: 清除 {ratio:.0%}'
# --- 暗部提亮、亮部保留(logo 含 ~20% 浅色元素,全量反转会把它们变黑) ---
# L' = max(L, 1-L):暗像素翻到亮侧(黑→白),亮像素不动,色相全保留
opaque = [px[x, y] for y in range(h) for x in range(w) if px[x, y][3] > 128]
assert opaque, '镂空后内容为空'
for y in range(h):
for x in range(w):
r, g, b, a = px[x, y]
if a == 0:
continue
hh, ll, ss = colorsys.rgb_to_hls(r / 255, g / 255, b / 255)
r2, g2, b2 = colorsys.hls_to_rgb(hh, max(ll, 1 - ll), ss)
px[x, y] = (round(r2 * 255), round(g2 * 255), round(b2 * 255), a)
im.save(OUT, 'PNG', optimize=True)
# 验证:反白后所有不透明像素亮度下限
final = [px[x, y] for y in range(h) for x in range(w) if px[x, y][3] > 128]
dark = sum(1 for p in final if 0.299*p[0]+0.587*p[1]+0.114*p[2] < 90) / len(final)
print(f'完成: {OUT} 镂空 {ratio:.0%},暗部像素剩 {dark:.1%}(应为 0)')