Image-Text-to-Text
Transformers
Safetensors
qwen3_5
qwen3.5
video
video-captioning
data-engine
conversational
Instructions to use nvidia/Flora-Captioner with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use nvidia/Flora-Captioner with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="nvidia/Flora-Captioner") 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 AutoProcessor, AutoModelForMultimodalLM processor = AutoProcessor.from_pretrained("nvidia/Flora-Captioner") model = AutoModelForMultimodalLM.from_pretrained("nvidia/Flora-Captioner", device_map="auto") 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?"} ] }, ] inputs = processor.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=256) print(processor.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use nvidia/Flora-Captioner with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "nvidia/Flora-Captioner" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "nvidia/Flora-Captioner", "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/nvidia/Flora-Captioner
- SGLang
How to use nvidia/Flora-Captioner 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 "nvidia/Flora-Captioner" \ --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": "nvidia/Flora-Captioner", "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 "nvidia/Flora-Captioner" \ --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": "nvidia/Flora-Captioner", "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 nvidia/Flora-Captioner with Docker Model Runner:
docker model run hf.co/nvidia/Flora-Captioner
Download batch_utils/media.py from nvidia/Flora-Captioner: direct link, hf CLI and curl.
- Browser
- Download file 5.14 kB
-
https://huggingface.co/nvidia/Flora-Captioner/resolve/refs%2Fpr%2F2/batch_utils/media.py
- Command line
-
hf download hf://nvidia/Flora-Captioner@refs/pr/2/batch_utils/media.py
-
curl -L -o media.py https://huggingface.co/nvidia/Flora-Captioner/resolve/refs%2Fpr%2F2/batch_utils/media.py
5.14 kB
| from dataclasses import dataclass | |
| from fractions import Fraction | |
| from pathlib import Path | |
| import hashlib | |
| import av | |
| from .config import MAX_DURATION_S, MAX_DIMENSION, MAX_FRAME_COUNT | |
| class InputError(ValueError): | |
| pass | |
| class Video: | |
| path: Path | |
| sha256: str | |
| rate: Fraction | |
| pts_us: tuple | |
| duration_us: int | |
| size: tuple | |
| cfr: bool | |
| def duration_s(self): | |
| return self.duration_us / 1000000 | |
| def probe(path): | |
| with av.open(str(path), options={'err_detect': 'explode'}) as container: | |
| if len(container.streams.video) != 1: | |
| raise InputError('Use a file containing one video stream.') | |
| stream = container.streams.video[0] | |
| stream.codec_context.thread_count = 1 | |
| rate = stream.average_rate | |
| if not rate or rate < 4: | |
| raise InputError('The input needs at least 4 source frames per second.') | |
| size = (stream.codec_context.width, stream.codec_context.height) | |
| if min(size) <= 0 or max(size) > MAX_DIMENSION: | |
| raise InputError('Video dimensions must be at most 4096 pixels per side.') | |
| times, zero, last_length = ([], None, None) | |
| for frame in container.decode(stream): | |
| if frame.pts is None: | |
| raise InputError('Every source frame needs a presentation timestamp.') | |
| zero = frame.pts if zero is None else zero | |
| t = Fraction(frame.pts - zero) * stream.time_base * 1000000 | |
| if times and t <= times[-1]: | |
| raise InputError('Source timestamps must be strictly increasing.') | |
| if t >= MAX_DURATION_S * 1000000 or len(times) >= MAX_FRAME_COUNT: | |
| raise InputError(f'Video exceeds {MAX_DURATION_S:g} seconds or {MAX_FRAME_COUNT} frames.') | |
| times.append(t) | |
| last_length = Fraction(frame.duration) * stream.time_base * 1000000 if frame.duration else None | |
| if len(times) < 2: | |
| raise InputError('The video contains too few source frames.') | |
| tail = last_length or Fraction(1000000, rate) | |
| duration = round(times[-1] + tail) | |
| if duration > MAX_DURATION_S * 1000000: | |
| raise InputError(f'Video exceeds {MAX_DURATION_S:g} seconds.') | |
| cfr = all((abs(t - Fraction(i * 1000000, rate)) <= 1 for i, t in enumerate(times))) | |
| with path.open('rb') as f: | |
| digest = hashlib.file_digest(f, 'sha256').hexdigest() | |
| return Video(path, digest, rate, tuple(times), duration, size, cfr) | |
| def check_synchronized(videos): | |
| main = videos['main'] | |
| if len(videos) == 3: | |
| for role, video in videos.items(): | |
| if video.rate != main.rate or video.pts_us != main.pts_us or video.duration_us != main.duration_us: | |
| raise InputError('Three camera files must have identical relative frame timestamps, frame counts and duration.') | |
| if not main.cfr: | |
| raise InputError('The three-view profile requires constant-rate synchronized source videos.') | |
| def decode_selected(video, wanted, canonical=False): | |
| wanted = set(wanted) | |
| result = {} | |
| if not wanted: | |
| return result | |
| if min(wanted) < 0 or max(wanted) >= len(video.pts_us): | |
| raise InputError('A requested source frame is outside the video.') | |
| with av.open(str(video.path), options={'err_detect': 'explode'}) as container: | |
| stream = container.streams.video[0] | |
| stream.codec_context.thread_count = 1 | |
| zero = None | |
| for i, frame in enumerate(container.decode(stream)): | |
| zero = frame.pts if zero is None else zero | |
| if i in wanted: | |
| if Fraction(frame.pts - zero) * stream.time_base * 1000000 != video.pts_us[i]: | |
| raise InputError('Source frame timestamps changed while decoding.') | |
| use_canonical = canonical and frame.format.name == 'yuv420p' and (frame.width % 16 == 0) and (frame.height % 2 == 0) and (int(frame.colorspace) in (2, 5, 6)) and (int(frame.color_range) in (0, 1)) | |
| if use_canonical: | |
| from canonical_rgb import canonical_rgb | |
| result[i] = canonical_rgb(frame) | |
| else: | |
| result[i] = frame.to_image().convert('RGB') | |
| if i >= max(wanted): | |
| break | |
| if set(result) != wanted: | |
| raise InputError('A requested source frame could not be decoded.') | |
| return result | |
| def frames_for(videos, wanted, mosaic='YAM'): | |
| frames = {role: decode_selected(video, wanted, canonical=len(videos) == 3) for role, video in videos.items()} | |
| if len(videos) == 1: | |
| return frames['main'] | |
| from composers import compose_top_dominant_mosaic, compose_frame | |
| if mosaic == 'YAM': | |
| return {i: compose_top_dominant_mosaic(frames['main'][i], frames['left_wrist'][i], frames['right_wrist'][i]) for i in wanted} | |
| if mosaic == 'Dex': | |
| return {i: compose_frame(dict(ego=frames['main'][i], left_wrist=frames['left_wrist'][i], right_wrist=frames['right_wrist'][i]), ('ego', 'left_wrist', 'right_wrist')) for i in wanted} | |
| raise InputError('Choose a supported camera layout.') | |