Files
homepage/tools/process_images.py

120 lines
4.7 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 -*-
"""
从 PPT 提取的原始素材生成网站用图:
1. logo 与 7 张黑底产品图:边缘 flood-fill 镂空(背景变透明,适配深色网页)
2. 水培架图背景复杂(39% 均匀度),不镂空,走统一描边容器展示
3. 截图/场景/照片转 WebP 压缩;二维码保持 PNG 无损
用法: uv run --with pillow python3 tools/process_images.py <pptx解压目录>/ppt/media <输出目录>
"""
import sys
from collections import deque
from pathlib import Path
from PIL import Image
src = Path(sys.argv[1])
out = Path(sys.argv[2])
out.mkdir(parents=True, exist_ok=True)
RENAME = {
'image2.png': 'logo.png', # 原样无损输出(见 PASS_THROUGH)
'image1.jpeg': 'hero-bg.webp', # v2 起不再做 hero 背景,仅作 og:image
'image3.png': 'about-main.webp', # v2 起弃用(PPT 拼贴图有白缝)
'image4.png': 'about-base.webp',
'image5.png': 'collar-sg03-mini.webp',
'image13.png': 'collar-sg03.webp',
'image10.png': 'ear-tag-st01.webp',
'image11.png': 'base-station-bg01.webp',
'image12.png': 'grazing-scene-1.webp',
'image18.png': 'grazing-scene-2.webp',
'image19.png': 'hydroponics-hs01.webp',
'image20.png': 'planting-scene.webp',
'image31.png': 'terminal-bb03.webp',
'image32.png': 'uwb-tag-sl01.webp',
'image33.png': 'bt-tag-st02.webp',
'image29.png': 'asset-scene-1.webp',
'image30.jpeg': 'asset-scene-2.webp',
'image34.png': 'platform-grazing-1.webp',
'image35.png': 'platform-grazing-app.webp',
'image36.png': 'platform-planting-1.webp',
'image37.png': 'platform-planting-2.webp',
'image38.png': 'platform-asset-1.webp',
'image39.png': 'platform-asset-2.webp',
'image40.png': 'custom-liangchang-1.webp',
'image41.png': 'custom-liangchang-2.webp',
'image42.png': 'custom-simulation.webp',
'image43.png': 'custom-agent.webp',
'image44.png': 'qrcode.png',
}
# 这些图做边缘 flood-fill 镂空(检测过:边缘色 96-100% 均匀的纯黑底)
# 注意:logo 不镂空——其底色 #000020 与四角检测色差超容差,v2 曾清成斑驳残留导致看不清
CUTOUT = {
'image5.png', 'image13.png', 'image10.png', 'image11.png',
'image31.png', 'image32.png', 'image33.png',
}
CUTOUT_TOL = 30 # 曼哈顿距离阈值,宁小勿大:黑底纯度高,产品暗部 (#20 系) 距离约 96 不会被误清
def cutout_bg(path: Path, tol: int) -> Image.Image:
"""从画布四边缘 flood-fill 清除均匀底色,只清与边缘连通的背景,不动被包围的内容"""
im = Image.open(path).convert('RGBA')
w, h = im.size
px = im.load()
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)
# 镂空比例健全性检查:太低=没清干净,太高=误伤内容
if not 0.10 < ratio < 0.92:
raise SystemExit(f'镂空异常 {path.name}: 清除 {ratio:.0%},需人工检查')
return im
# 原样无损拷贝:logo 色块文字经不起有损压缩,二维码必须无损
PASS_THROUGH = {'image2.png', 'image44.png'}
for src_name, dst_name in RENAME.items():
src_path = src / src_name
if not src_path.exists():
print(f' 跳过缺失: {src_name}')
continue
dst_path = out / dst_name
if src_name in PASS_THROUGH:
Image.open(src_path).save(dst_path)
elif src_name in CUTOUT:
im = cutout_bg(src_path, CUTOUT_TOL)
# 镂空图必须带 alpha,webp 无损存 alpha(有损会弄脏半透明边缘)
im.save(dst_path, 'WEBP', quality=100, lossless=True, method=6)
else:
im = Image.open(src_path).convert('RGB')
if im.width > 1600:
im = im.resize((1600, round(im.height * 1600 / im.width)), Image.LANCZOS)
im.save(dst_path, 'WEBP', quality=82, method=6)
kb = dst_path.stat().st_size // 1024
tag = ' [镂空]' if src_name in CUTOUT else ''
print(f' {src_name:14s} -> {dst_name:28s} {kb}KB{tag}')
print(f'完成,共 {len(RENAME)} 张 -> {out}')