Nodes/ComfyUI_InternVL2/Dynamic Preprocess
ComfyUI Node

Dynamic Preprocess

InternVL's dynamic resolution, as a node

By leeguandong·Created 2 years ago·Updated 2 years ago· 13
Dynamic Preprocess
  • image
  • image
min_num1
max_num6
image_size448
use_thumbnailtrue

Your image is too big to look at

Vision-language models like InternVL2 aren't trained on "an image" - they're trained on fixed-size crops, 448 pixels square. Feed one a full 1024×1024 render and something has to give: either the whole thing gets squashed down to a postage stamp and the model misses all the detail, or you split it into 448px tiles and let it read them all. That second path is "dynamic resolution," and this node is the exact preprocessor that makes it work.

You only need Dynamic Preprocess because of how the rest of this pack is built. The typical graph is: InternVL Model Loader → (your image → Dynamic Preprocess) → InternVL HF Inference → text out. If you skip this node and wire a raw ComfyUI image straight into inference, you get garbage - the model's chat() method expects these normalized pixel values, not an ordinary 0–255 image. This little node is the bridge.

How it works

This isn't the author's invention; it's OpenGVLab's own preprocess (the same one in their official InternVL repo) lifted into a node. The mechanism, reading the source:

  1. It picks the closest matching aspect ratio to your image from a grid of tile counts - pairs (i, j) where min_num ≤ i×j ≤ max_num. A 16:9 image wants a 2×1 tile grid; a square one wants 1×1.
  2. It resizes the whole image to fill that many 448px tiles, then crops each tile out as its own image.
  3. With use_thumbnail on (the default), it also adds a single full-image thumbnail shrunk to 448px, so the model gets both the big-picture context and the detail crops.
  4. Each tile gets bicubic-resized, turned into a tensor, and normalized with ImageNet mean/std - then stacked into one batch.

The output is a batch of normalized tiles, not a viewable image. If you drag a preview node onto it you'll see a dim, wrong-colored blob and think it's broken. It isn't - that's the pixel-values tensor the VLM is trained to eat, and it's not meant for human eyes.

The inputs that matter

There's only one required input, image (a ComfyUI IMAGE). The optional four are mostly "leave alone until you have a reason":

  • min_num / max_num - the low and high bounds on how many tiles the model may use. Defaults are 1 and 6. Each tile costs tokens and VRAM, so cranking max_num up for "more detail" can blow out your context or your card. Start at the defaults.
  • image_size - tile size, default 448. This is what InternVL2 was trained at; there's rarely a reason to move it.
  • use_thumbnail - default on. Keep it on; the model expects that global view.

Install

It ships inside the ComfyUI_InternVL2 pack. In ComfyUI Manager, search "InternVL" and install, or clone it manually:

cd ComfyUI/custom_nodes
git clone https://github.com/leeguandong/ComfyUI_InternVL2

Then restart ComfyUI. There's no requirements.txt - the pack leans on your ComfyUI environment (plus transformers and huggingface_hub, which the loader needs). On a clean install where those aren't present you'll get a ModuleNotFoundError on startup; pip install transformers huggingface_hub fixes it.

Gotchas

Beyond the "don't preview the output" trap: this pack has been dormant since mid-2024 and only wraps the InternVL2 generation. It still works fine for captioning and image understanding - InternVL2 was the state-of-the-art captioning model when it shipped - but don't expect the newer InternVL 2.5/3.x names to appear in the loader's list. And if you're here for batch captioning, remember each image becomes several tiles; that's the price of the model actually seeing your high-res render, and it's why the inference node has a keep_model_loaded option.

Categoryinternvl

Inputs (5)

NameTypeDefaultDescription
imageIMAGE
min_numoptINT11–40
max_numoptINT61–40
image_sizeoptINT448
use_thumbnailoptBOOLEANtrue

Outputs (1)

NameTypeDescription
imageIMAGE