Ctrl K
ComunidadGuías…

figma-export/extract_shape.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
from PIL import Image, ImageFilter, ImageDraw

SRC = r"C:\Users\Abolfazl\Desktop\Phonix\figma-export\assets\exports\499-5224.png"
im = Image.open(SRC).convert("RGB")
W, H = im.size  # 1920 x 5800

# crop the hero panel region (below the header, above the stats bar)
CROP = (0, 180, W, 1010)
region = im.crop(CROP)
rw, rh = region.size

# downscale for tracing
PW = 480
scale = PW / rw
PH = int(rh * scale)
small = region.resize((PW, PH))

# binary mask: foreground = panel composition (brighter than dark bg)
THRESH = 80
gray = small.convert("L")
px = small.load()
mask = Image.new("L", (PW, PH), 0)
mp = mask.load()
for y in range(PH):
    for x in range(PW):
        r, g, b = px[x, y]
        if (r + g + b) > THRESH:
            mp[x, y] = 255

# morphological open to drop small dots / texture, then close to fill
mask = mask.filter(ImageFilter.MinFilter(3)).filter(ImageFilter.MaxFilter(5)).filter(ImageFilter.MinFilter(3))
mask.save(r"C:\Users\Abolfazl\Desktop\Phonix\figma-export\mask.png")

# keep only the largest blob via flood fill from the centre
from collections import deque
mp = mask.load()
seed = (PW // 2, PH // 2)
if mp[seed] == 0:
    # search outward for a fg seed
    for rad in range(1, PH):
        found = False
        for dy in (-rad, rad):
            for x in range(max(0, PW//2-rad), min(PW, PW//2+rad)):
                if 0 <= seed[1]+dy < PH and mp[x, seed[1]+dy] == 255:
                    seed = (x, seed[1]+dy); found = True; break
            if found: break
        if found: break

comp = Image.new("L", (PW, PH), 0)
cp = comp.load()
dq = deque([seed]); cp[seed] = 255
while dq:
    x, y = dq.popleft()
    for nx, ny in ((x+1,y),(x-1,y),(x,y+1),(x,y-1)):
        if 0 <= nx < PW and 0 <= ny < PH and mp[nx, ny] == 255 and cp[nx, ny] == 0:
            cp[nx, ny] = 255; dq.append((nx, ny))

# fill horizontal gaps inside the blob (per row, fill between first & last fg)
for y in range(PH):
    xs = [x for x in range(PW) if cp[x, y] == 255]
    if xs:
        for x in range(min(xs), max(xs)+1):
            cp[x, y] = 255

# Moore boundary trace (clockwise) starting at topmost-leftmost fg pixel
def first_fg():
    for y in range(PH):
        for x in range(PW):
            if cp[x, y] == 255:
                return (x, y)
    return None

start = first_fg()
boundary = []
if start:
    nbrs = [(-1,-1),(0,-1),(1,-1),(1,0),(1,1),(0,1),(-1,1),(-1,0)]
    cur = start
    b_idx = 7  # came from "left"
    boundary.append(cur)
    safety = 0
    while safety < 20000:
        safety += 1
        found_next = False
        for k in range(8):
            idx = (b_idx + 1 + k) % 8
            nx, ny = cur[0]+nbrs[idx][0], cur[1]+nbrs[idx][1]
            if 0 <= nx < PW and 0 <= ny < PH and cp[nx, ny] == 255:
                b_idx = (idx + 4) % 8
                cur = (nx, ny)
                boundary.append(cur)
                found_next = True
                break
        if not found_next:
            break
        if cur == start and len(boundary) > 3:
            break

# Douglas-Peucker simplify
def dp(points, eps):
    if len(points) < 3:
        return points
    def dist(p, a, b):
        if a == b:
            return ((p[0]-a[0])**2 + (p[1]-a[1])**2) ** 0.5
        num = abs((b[0]-a[0])*(a[1]-p[1]) - (a[0]-p[0])*(b[1]-a[1]))
        den = ((b[0]-a[0])**2 + (b[1]-a[1])**2) ** 0.5
        return num/den
    dmax, idx = 0, 0
    for i in range(1, len(points)-1):
        d = dist(points[i], points[0], points[-1])
        if d > dmax:
            dmax, idx = d, i
    if dmax > eps:
        left = dp(points[:idx+1], eps)
        right = dp(points[idx:], eps)
        return left[:-1] + right
    return [points[0], points[-1]]

simp = dp(boundary, eps=6) if boundary else []
print(f"boundary pts: {len(boundary)} -> simplified: {len(simp)}")

# draw preview
prev = small.convert("RGB")
d = ImageDraw.Draw(prev)
if simp:
    d.line(simp + [simp[0]], fill=(0, 255, 0), width=2)
    for p in simp:
        d.ellipse([p[0]-3, p[1]-3, p[0]+3, p[1]+3], fill=(255, 255, 0))
prev.save(r"C:\Users\Abolfazl\Desktop\Phonix\figma-export\shape_preview.png")

# clip-path in % of the panel box
print("\nclip-path: polygon(")
poly = []
for (x, y) in simp:
    px_ = round(x / PW * 100, 1)
    py_ = round(y / PH * 100, 1)
    poly.append(f"{px_}% {py_}%")
print("  " + ", ".join(poly))
print(")")