Quicksviewer-8B / image_processing_quicksviewer.py
qijithu's picture
Add files using upload-large-folder tool
0efe9d5 verified
Raw History Blame Contribute Delete
3.88 kB
"""Quicksviewer image ingress: PIL bicubic RGB, CPU uint8, no normalization."""
import base64
import io
import os
from urllib.request import urlopen
import numpy as np
import torch
from PIL import Image
from transformers.image_processing_utils import BaseImageProcessor
from transformers.feature_extraction_utils import BatchFeature
SIGLIP_SIZE = 980
def read_source(source):
if isinstance(source, os.PathLike):
source = os.fspath(source)
if isinstance(source, str) and source.startswith("data:"):
header, payload = source.split(",", 1)
if ";base64" not in header:
raise ValueError("Media data URLs must use base64 encoding")
return io.BytesIO(base64.b64decode(payload))
if isinstance(source, str) and source.startswith(("https://", "http://")):
with urlopen(source, timeout=120) as response:
return io.BytesIO(response.read())
if isinstance(source, str) and source.startswith("file://"):
return source[7:]
if isinstance(source, bytes):
return io.BytesIO(source)
return source
def to_pil(image):
if isinstance(image, Image.Image):
return image
if isinstance(image, (str, bytes, os.PathLike)) or hasattr(image, "read"):
with Image.open(read_source(image)) as opened:
return opened.convert("RGB")
if isinstance(image, torch.Tensor):
image = image.detach().cpu().numpy()
if not isinstance(image, np.ndarray):
raise TypeError(f"Unsupported image type: {type(image).__name__}")
if image.ndim == 3 and image.shape[0] in (1, 3, 4) and image.shape[-1] not in (1, 3, 4):
image = np.moveaxis(image, 0, -1)
if image.ndim not in (2, 3):
raise ValueError("An image must have shape HWC or CHW")
if image.dtype != np.uint8:
if not np.isfinite(image).all() or image.min() < 0 or image.max() > 255:
raise ValueError("Image values must be finite and in [0,255] (or [0,1])")
if np.issubdtype(image.dtype, np.floating) and image.max() <= 1:
image = image * 255
image = np.rint(image).astype(np.uint8)
if image.ndim == 3 and image.shape[-1] == 1:
image = image[..., 0]
return Image.fromarray(image)
class QuicksviewerImageProcessor(BaseImageProcessor):
model_input_names = ["pixel_values"]
def __init__(self, size=None, **kwargs):
super().__init__(**kwargs)
self.size = size or {"height": SIGLIP_SIZE, "width": SIGLIP_SIZE}
if self.size != {"height": SIGLIP_SIZE, "width": SIGLIP_SIZE}:
raise ValueError("Quicksviewer requires 980x980 images")
self.do_normalize = False
self.do_rescale = False
def preprocess_one(self, image):
image = to_pil(image).convert("RGB").resize(
(SIGLIP_SIZE, SIGLIP_SIZE), Image.Resampling.BICUBIC
)
array = np.array(image, dtype=np.uint8, copy=True)
return torch.from_numpy(array).permute(2, 0, 1).contiguous()
def preprocess(self, images, return_tensors="pt", **kwargs):
if kwargs:
raise TypeError(f"Unsupported image options: {sorted(kwargs)}")
if isinstance(images, (np.ndarray, torch.Tensor)) and images.ndim == 4:
images = list(images)
elif not isinstance(images, (list, tuple)):
images = [images]
frames = [self.preprocess_one(image) for image in images]
pixels = torch.stack(frames) if frames else torch.empty(
(0, 3, SIGLIP_SIZE, SIGLIP_SIZE), dtype=torch.uint8
)
if return_tensors not in (None, "pt", "np"):
raise ValueError("return_tensors must be 'pt', 'np', or None")
if return_tensors == "np":
pixels = pixels.numpy()
return BatchFeature({"pixel_values": pixels})
QuicksviewerImageProcessor.register_for_auto_class("AutoImageProcessor")