Image-Text-to-Text
Transformers
Safetensors
English
cviu_chat
feature-extraction
qupaint
2d-materials
quantum-materials
microscopy
scientific-discovery
multimodal
visual-grounding
cvpr2026
conversational
custom_code
Instructions to use uark-cviu/QuPAINT-4B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use uark-cviu/QuPAINT-4B with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="uark-cviu/QuPAINT-4B", trust_remote_code=True) messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] pipe(text=messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("uark-cviu/QuPAINT-4B", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use uark-cviu/QuPAINT-4B with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "uark-cviu/QuPAINT-4B" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "uark-cviu/QuPAINT-4B", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/uark-cviu/QuPAINT-4B
- SGLang
How to use uark-cviu/QuPAINT-4B with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "uark-cviu/QuPAINT-4B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "uark-cviu/QuPAINT-4B", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "uark-cviu/QuPAINT-4B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "uark-cviu/QuPAINT-4B", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }' - Docker Model Runner
How to use uark-cviu/QuPAINT-4B with Docker Model Runner:
docker model run hf.co/uark-cviu/QuPAINT-4B
Download preprocess.py from uark-cviu/QuPAINT-4B: direct link, hf CLI and curl.
- Browser
- Download file 4.3 kB
-
https://huggingface.co/uark-cviu/QuPAINT-4B/resolve/main/preprocess.py
- Command line
-
hf download hf://uark-cviu/QuPAINT-4B/preprocess.py
-
curl -L -o preprocess.py https://huggingface.co/uark-cviu/QuPAINT-4B/resolve/main/preprocess.py
4.3 kB
| """Image preprocessing for QuPAINT. | |
| The released checkpoint expects the same pipeline used at tuning time: | |
| 1. the micrograph is resized to a fixed 1792x1344 canvas, | |
| 2. that canvas is tiled into 448x448 patches by aspect-ratio matching | |
| (InternVL-style dynamic tiling, up to `max_num` tiles), | |
| 3. a downscaled thumbnail of the whole image is appended as a final tile, | |
| 4. every tile is normalised with ImageNet statistics. | |
| Because boxes are predicted in *percent* of image size, the fixed canvas does | |
| not distort results -- but changing `DEFAULT_CANVAS` moves the model away from | |
| the resolution it was tuned at, so keep it unless you know why you are changing it. | |
| """ | |
| from typing import List, Tuple | |
| import torch | |
| import torchvision.transforms as T | |
| from PIL import Image | |
| from torchvision.transforms.functional import InterpolationMode | |
| IMAGENET_MEAN = (0.485, 0.456, 0.406) | |
| IMAGENET_STD = (0.229, 0.224, 0.225) | |
| DEFAULT_CANVAS: Tuple[int, int] = (1792, 1344) | |
| DEFAULT_TILE_SIZE: int = 448 | |
| DEFAULT_MAX_TILES: int = 12 | |
| def build_transform(input_size: int = DEFAULT_TILE_SIZE) -> T.Compose: | |
| """Tile transform: RGB, bicubic resize to `input_size`, ImageNet normalisation.""" | |
| return T.Compose( | |
| [ | |
| T.Lambda(lambda img: img.convert("RGB") if img.mode != "RGB" else img), | |
| T.Resize((input_size, input_size), interpolation=InterpolationMode.BICUBIC), | |
| T.ToTensor(), | |
| T.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD), | |
| ] | |
| ) | |
| def find_closest_aspect_ratio(aspect_ratio, target_ratios, width, height, image_size): | |
| best_ratio_diff = float("inf") | |
| best_ratio = (1, 1) | |
| area = width * height | |
| for ratio in target_ratios: | |
| target_aspect_ratio = ratio[0] / ratio[1] | |
| ratio_diff = abs(aspect_ratio - target_aspect_ratio) | |
| if ratio_diff < best_ratio_diff: | |
| best_ratio_diff = ratio_diff | |
| best_ratio = ratio | |
| elif ratio_diff == best_ratio_diff: | |
| if area > 0.5 * image_size * image_size * ratio[0] * ratio[1]: | |
| best_ratio = ratio | |
| return best_ratio | |
| def dynamic_preprocess( | |
| image: Image.Image, | |
| min_num: int = 1, | |
| max_num: int = DEFAULT_MAX_TILES, | |
| image_size: int = DEFAULT_TILE_SIZE, | |
| use_thumbnail: bool = False, | |
| ) -> List[Image.Image]: | |
| """Split `image` into `image_size` tiles using the closest allowed aspect ratio.""" | |
| orig_width, orig_height = image.size | |
| aspect_ratio = orig_width / orig_height | |
| target_ratios = sorted( | |
| { | |
| (i, j) | |
| for n in range(min_num, max_num + 1) | |
| for i in range(1, n + 1) | |
| for j in range(1, n + 1) | |
| if min_num <= i * j <= max_num | |
| }, | |
| key=lambda x: x[0] * x[1], | |
| ) | |
| target_aspect_ratio = find_closest_aspect_ratio( | |
| aspect_ratio, target_ratios, orig_width, orig_height, image_size | |
| ) | |
| target_width = image_size * target_aspect_ratio[0] | |
| target_height = image_size * target_aspect_ratio[1] | |
| blocks = target_aspect_ratio[0] * target_aspect_ratio[1] | |
| resized_img = image.resize((target_width, target_height)) | |
| cols = target_width // image_size | |
| processed_images = [] | |
| for i in range(blocks): | |
| box = ( | |
| (i % cols) * image_size, | |
| (i // cols) * image_size, | |
| ((i % cols) + 1) * image_size, | |
| ((i // cols) + 1) * image_size, | |
| ) | |
| processed_images.append(resized_img.crop(box)) | |
| assert len(processed_images) == blocks | |
| if use_thumbnail and len(processed_images) != 1: | |
| processed_images.append(image.resize((image_size, image_size))) | |
| return processed_images | |
| def load_image( | |
| image_file, | |
| input_size: int = DEFAULT_TILE_SIZE, | |
| max_num: int = DEFAULT_MAX_TILES, | |
| canvas: Tuple[int, int] = DEFAULT_CANVAS, | |
| ) -> torch.Tensor: | |
| """Load a micrograph and return the stacked tile tensor of shape (tiles, 3, S, S).""" | |
| image = image_file | |
| if not isinstance(image, Image.Image): | |
| image = Image.open(image_file) | |
| image = image.convert("RGB").resize(canvas) | |
| transform = build_transform(input_size=input_size) | |
| tiles = dynamic_preprocess( | |
| image, image_size=input_size, use_thumbnail=True, max_num=max_num | |
| ) | |
| return torch.stack([transform(tile) for tile in tiles]) | |