Dataset-Maker / src /optimizer.py
arittrabag's picture
perf: PNG compress_level 6 instead of optimize/9 (faster pack, ~same size on black-bg pieces)
cf41176 verified
"""Image optimization for the exported pieces.
Torn pieces are mostly black background -> they compress extremely well as PNG.
We:
* tight-crop to the fragment bbox (already done in tearing),
* optionally quantize the foreground to <=256 colors (palette PNG) to shrink
size further when `lossy` is on,
* always write with PNG optimize + max zlib compression.
Palette quantization is Theta(P) over piece pixels (Pillow median-cut).
"""
from __future__ import annotations
import io
import numpy as np
from PIL import Image
def encode_piece(rgb: np.ndarray, lossy: bool = False, colors: int = 64) -> bytes:
"""Encode an (h, w, 3) uint8 piece to optimized PNG bytes."""
img = Image.fromarray(rgb, mode="RGB")
if lossy:
# Median-cut palette; black background collapses to one palette entry.
img = img.quantize(colors=max(2, min(256, colors)), method=Image.MEDIANCUT)
buf = io.BytesIO()
# compress_level=6 (zlib default) instead of optimize=True/level 9: the latter
# is far slower per piece for ~no size gain on mostly-black fragments, which
# dominates pack time when there are hundreds of pieces.
img.save(buf, format="PNG", compress_level=6)
return buf.getvalue()
def encode_preview(rgb: np.ndarray, max_side: int = 1024) -> np.ndarray:
"""Downscale a page/preview for fast UI display (keeps aspect)."""
H, W = rgb.shape[:2]
scale = min(1.0, max_side / max(H, W))
if scale >= 1.0:
return rgb
nw, nh = int(W * scale), int(H * scale)
return np.asarray(Image.fromarray(rgb).resize((nw, nh), Image.BILINEAR))