# -*- 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)')