Spaces:
Running
Running
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)) | |