Spaces:
Running on Zero
Running on Zero
Download slotread/images.py from infinitylogesh/GUI-Decisions: direct link, hf CLI and curl.
- Browser
- Download file 2.39 kB
-
https://huggingface.co/spaces/infinitylogesh/GUI-Decisions/resolve/main/slotread/images.py
- Command line
-
hf download hf://spaces/infinitylogesh/GUI-Decisions/slotread/images.py
-
curl -L -o images.py https://huggingface.co/spaces/infinitylogesh/GUI-Decisions/resolve/main/slotread/images.py
2.39 kB
| """Images: opening examples, capping their size, and encoding screenshots for the server.""" | |
| import base64, io, math, os | |
| from PIL import Image | |
| # max pixels per image for training and serving (0: as given). GUI-Owl uses 1,048,576 so training and | |
| # serving see the same image; Gemma 4 resizes to its own fixed token budget either way. | |
| IMAGE_CAP = int(os.environ.get("IMAGE_CAP", 0)) | |
| def cap_image(im, cap=None, factor=32): | |
| """Downscale to at most `cap` pixels, sides multiples of `factor` (Qwen3-VL: patch 16 x merge 2), | |
| so the processor keeps the size.""" | |
| cap = IMAGE_CAP if cap is None else cap | |
| W, H = im.size | |
| if not cap or W * H <= cap: | |
| return im | |
| s = math.sqrt(cap / (W * H)) | |
| w, h = max(factor, int(W * s) // factor * factor), max(factor, int(H * s) // factor * factor) | |
| return im.resize((w, h), Image.BICUBIC) | |
| def image_of(ex): | |
| """An example's image as RGB; a path is opened only when needed (keeps thousands of examples cheap).""" | |
| im = ex["image"] | |
| if isinstance(im, str): | |
| im = Image.open(im) | |
| return cap_image(im.convert("RGB") if im.mode != "RGB" else im) | |
| def image_size(ex): | |
| if "size" in ex: | |
| return ex["size"] | |
| im = ex["image"] | |
| if isinstance(im, str): | |
| with Image.open(im) as f: | |
| return f.size | |
| return im.size | |
| # How screenshots travel to the server. The fast path, and the default for serving: JPEG at the model's | |
| # input size (Gemma 4's encoder sees about 896 x 896 at its default budget): ~8 ms to build instead of | |
| # ~118 ms for a full-size PNG, and cheaper for the server to decode. IMG_FMT=png IMG_MAX_PX=0 sends the | |
| # lossless full-size image instead. | |
| IMG_FMT = os.environ.get("IMG_FMT", "jpeg") | |
| IMG_MAX_PX = int(os.environ.get("IMG_MAX_PX", 896 * 896)) | |
| def data_url(im, fmt=None, max_px=None): | |
| fmt = fmt or IMG_FMT | |
| max_px = IMG_MAX_PX if max_px is None else max_px | |
| im = im.convert("RGB") | |
| if max_px and im.width * im.height > max_px: | |
| s = (max_px / (im.width * im.height)) ** 0.5 | |
| im = im.resize((max(1, round(im.width * s)), max(1, round(im.height * s))), Image.BICUBIC) | |
| b = io.BytesIO() | |
| if fmt == "jpeg": | |
| im.save(b, "JPEG", quality=90) | |
| return "data:image/jpeg;base64," + base64.b64encode(b.getvalue()).decode() | |
| im.save(b, "PNG") | |
| return "data:image/png;base64," + base64.b64encode(b.getvalue()).decode() | |