feat: 沙香科技官网 v2.2 基线(深色科技风/双语/局域网+公网双部署)

This commit is contained in:
dafe
2026-11-02 14:57:08 +08:00
commit 924bca9f98
91 changed files with 1632 additions and 0 deletions

119
tools/process_images.py Normal file
View File

@@ -0,0 +1,119 @@
# -*- 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}')