Files
juzi/tools/optimize_delivery_images.py
T

65 lines
2.6 KiB
Python

#!/usr/bin/env python3
"""Export smaller delivery JPEGs; never overwrite the PSD-derived originals.
Only opaque-on-white product cards are candidates. Other WebPs are already
smaller than JPEG, and logos/icons/masks need their original transparency.
"""
import hashlib
import io
import json
import math
from pathlib import Path
from PIL import Image, ImageChops, ImageStat
ROOT = Path(__file__).resolve().parents[1]
DIRECTORY = ROOT / 'h5/assets/img/ui-20261012'
def main():
assets = []
for age in (25, 35, 50):
source = DIRECTORY / f'product-{age}.webp'
rgba = Image.open(source).convert('RGBA')
image = Image.new('RGB', rgba.size, 'white')
image.paste(rgba, mask=rgba.getchannel('A'))
for quality in (90, 92, 94, 96):
buffer = io.BytesIO()
image.save(buffer, 'JPEG', quality=quality, subsampling=0,
optimize=True, progressive=True)
encoded = buffer.getvalue()
decoded = Image.open(io.BytesIO(encoded)).convert('RGB')
rms = ImageStat.Stat(ImageChops.difference(image, decoded)).rms
mse = sum(value ** 2 for value in rms) / 3
psnr = 10 * math.log10(255 ** 2 / mse) if mse else 100
if psnr >= 40 and len(encoded) < source.stat().st_size * .9:
break
else:
raise RuntimeError(f'No suitably small high-quality JPEG for {source}')
target = source.with_suffix('.jpg')
target.write_bytes(encoded)
assets.append({
'source': str(source.relative_to(ROOT)),
'source_sha256': hashlib.sha256(source.read_bytes()).hexdigest(),
'source_bytes': source.stat().st_size,
'file': str(target.relative_to(ROOT)),
'sha256': hashlib.sha256(encoded).hexdigest(),
'bytes': len(encoded), 'size': list(image.size),
'quality': quality, 'subsampling': 0, 'progressive': True,
'matte': '#FFFFFF', 'psnr_db': round(psnr, 2),
})
manifest = {
'description': 'Smaller product-card delivery images. Original PSD crops preserved.',
'retained': 'Background/scan WebPs are smaller than JPEG; transparent icons/masks remain PNG.',
'assets': assets,
'source_bytes': sum(row['source_bytes'] for row in assets),
'delivery_bytes': sum(row['bytes'] for row in assets),
}
destination = ROOT / 'design/ui-20261012/delivery-optimization-20261012.json'
destination.write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + '\n', encoding='utf8')
print(json.dumps(manifest, ensure_ascii=False, indent=2))
if __name__ == '__main__':
main()