65 lines
2.6 KiB
Python
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()
|