molecule
Nametag Blank Large LED
Public Unreviewedby adonis
USB-C powered 160 x 48 mm LED nametag blank: six side-firing OSRAM LW A6SG white LEDs on a 2-layer 1.6 mm FR4 carrier, with four large and two medium Adom machine pins for workcell mounting.
main
adonis
hero: flood-fill knockout (preserves surfaces the naive colour key destroyed)
8bc0a07
1mo ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
#!/usr/bin/env python3
"""Border-seeded flood-fill knockout (per transparent-hero-renders.md).
Numpy-vectorised BFS: same algorithm, fast. Never a global colour key."""
import sys, numpy as np
from PIL import Image
SRC, DOC, ICON = sys.argv[1], sys.argv[2], sys.argv[3]
FILL_TOL, T1 = 12, 30
im = Image.open(SRC).convert("RGB")
a = np.asarray(im).astype(np.int16)
h, w, _ = a.shape
corners = [tuple(a[2,2]), tuple(a[2,w-3]), tuple(a[h-3,2]), tuple(a[h-3,w-3])]
KEY = np.array(corners[0], dtype=np.int16)
print("corners:", corners, "| flat:", len(set(corners)) == 1)
near = (np.abs(a - KEY).max(axis=2) <= FILL_TOL) # candidate background
# border-seeded flood fill, vectorised BFS over `near`
bg = np.zeros((h, w), bool)
seed = np.zeros((h, w), bool)
seed[0,:] = seed[-1,:] = True; seed[:,0] = seed[:,-1] = True
frontier = near & seed
bg |= frontier
while frontier.any():
nxt = np.zeros((h, w), bool)
nxt[1:,:] |= frontier[:-1,:]
nxt[:-1,:] |= frontier[1:,:]
nxt[:,1:] |= frontier[:,:-1]
nxt[:,:-1] |= frontier[:,1:]
frontier = nxt & near & ~bg
bg |= frontier
# fringe = non-bg pixels 8-adjacent to bg
nb = np.zeros((h, w), bool)
for dy in (-1,0,1):
for dx in (-1,0,1):
if dy == dx == 0: continue
nb[max(0,dy):h+min(0,dy), max(0,dx):w+min(0,dx)] |= bg[max(0,-dy):h+min(0,-dy), max(0,-dx):w+min(0,-dx)]
fringe = nb & ~bg
out = np.zeros((h, w, 4), np.uint8)
solid = ~bg & ~fringe
out[solid, :3] = a[solid]; out[solid, 3] = 255
d = np.abs(a - KEY).max(axis=2).astype(np.float32)
al = np.clip(d / T1, 0, 1)
af = np.maximum(al, 1e-3)[..., None]
un = KEY + (a - KEY) / af # un-premultiply the blend
un = np.clip(un, 0, 255)
out[fringe, :3] = un[fringe].astype(np.uint8)
out[fringe, 3] = (al[fringe] * 255).round().astype(np.uint8)
img = Image.fromarray(out, "RGBA")
img.save(DOC)
img.crop(img.getbbox()).save(ICON)
ex = img.getchannel("A").getextrema()
print(f"bg px {int(bg.sum())} | fringe {int(fringe.sum())} | alpha {ex} | doc {img.size} | icon {img.crop(img.getbbox()).size}")