Download runtime/glance_runtime/vision.py from bpHigh/glance-0.1: direct link, hf CLI and curl.
- Browser
- Download file 3.39 kB
-
https://huggingface.co/bpHigh/glance-0.1/resolve/main/runtime/glance_runtime/vision.py
- Command line
-
hf download hf://bpHigh/glance-0.1/runtime/glance_runtime/vision.py
-
curl -L -o vision.py https://huggingface.co/bpHigh/glance-0.1/resolve/main/runtime/glance_runtime/vision.py
3.39 kB
| """Image inputs. | |
| Qwen3.5 reads images natively: its processor cuts each image into 16-px patches, the vision | |
| encoder merges 2×2 patches into one token, and the language model sees those tokens between | |
| ``<|vision_start|>`` and ``<|vision_end|>`` at ``<|image_pad|>`` placeholders, with 3-D (M-RoPE) | |
| positions. ``ImageEncoder`` turns one decision image into the placeholder ids for the layout and | |
| the pixel tensors for ``collate``; ``DecisionModel.hidden`` scatters the encoder's features into | |
| the placeholders. | |
| Glance 0.1 was trained and evaluated on images at most 640 px on the long side; | |
| ``encode_image`` prepares an image the same way (downscaled, JPEG at quality 88, as a ``data:`` URL). | |
| """ | |
| from __future__ import annotations | |
| import base64 | |
| import io | |
| from pathlib import Path | |
| from typing import Any | |
| from PIL import Image | |
| MAX_SIDE = 640 | |
| JPEG_QUALITY = 88 | |
| def load_image(entry: dict[str, Any]) -> Image.Image: | |
| """The image an entry refers to (``image``, ``path`` or ``data_url``), as RGB.""" | |
| if "image" in entry: | |
| return entry["image"].convert("RGB") | |
| if "path" in entry: | |
| with Image.open(Path(entry["path"])) as im: | |
| return im.convert("RGB") | |
| if "data_url" in entry: | |
| data = base64.b64decode(entry["data_url"].split(",", 1)[1]) | |
| with Image.open(io.BytesIO(data)) as im: | |
| return im.convert("RGB") | |
| raise ValueError("image entry needs image, path or data_url") | |
| def to_data_url(img: Image.Image, max_side: int | None = MAX_SIDE, quality: int = JPEG_QUALITY) -> str: | |
| img = img.convert("RGB") | |
| if max_side and max(img.size) > max_side: | |
| scale = max_side / max(img.size) | |
| img = img.resize((max(1, round(img.width * scale)), max(1, round(img.height * scale))), Image.BILINEAR) | |
| buf = io.BytesIO() | |
| img.save(buf, format="JPEG", quality=quality) | |
| return "data:image/jpeg;base64," + base64.b64encode(buf.getvalue()).decode("ascii") | |
| def encode_image(image: "str | Path | Image.Image", max_side: int | None = MAX_SIDE, quality: int = JPEG_QUALITY) -> str: | |
| """A file path, PIL image or ``data:`` URL as the ``data:`` URL a request carries.""" | |
| if isinstance(image, Image.Image): | |
| return to_data_url(image, max_side, quality) | |
| if isinstance(image, str) and image.startswith("data:"): | |
| return image | |
| with Image.open(Path(image)) as im: | |
| return to_data_url(im, max_side, quality) | |
| class ImageEncoder: | |
| def __init__(self, image_processor: Any, *, image_token_id: int, start_id: int | None = None, | |
| end_id: int | None = None, merge_size: int = 2): | |
| self.image_processor = image_processor | |
| self.image_token_id = image_token_id | |
| self.start_id, self.end_id = start_id, end_id | |
| self.merge = merge_size | |
| def __call__(self, entry: dict[str, Any]) -> tuple[list[int], dict[str, Any]]: | |
| """Placeholder ids for one image, and its ``pixel_values`` / ``image_grid_thw``.""" | |
| out = self.image_processor(images=[load_image(entry)], return_tensors="pt") | |
| grid = out["image_grid_thw"] | |
| n = int(grid.prod()) // self.merge ** 2 | |
| ids = [self.image_token_id] * n | |
| if self.start_id is not None: | |
| ids = [self.start_id, *ids] | |
| if self.end_id is not None: | |
| ids = [*ids, self.end_id] | |
| return ids, {"pixel_values": out["pixel_values"], "image_grid_thw": grid} | |