Repository navigation
mtmd : lfm2 tiles and layout match transformers - #30261
Merged
ngxson merged 4 commits intoOct 20, 2026
Merged
Conversation
ykhrustalev
marked this pull request as draft
October 13, 2026 14:31
ykhrustalev
marked this pull request as ready for review
October 13, 2026 23:54
ykhrustalev
force-pushed
the
ykhrustalev/lfm2-tiles-transformers
branch
from
October 17, 2026 13:02
af737d3 to
76b9ea3
Compare
ngxson
approved these changes
Oct 19, 2026
ngxson
left a comment
Collaborator
There was a problem hiding this comment.
could you please rebase this?
Lfm2VlImageProcessor.crop_image_to_patches resizes the image straight to (512 * rows, 512 * cols) before it cuts the tiles. llama.cpp kept the aspect ratio and added black bars for LFM2 (only d1-omni stretched), so the tiles of d1-3B and every LFM2-VL / LFM2.5-VL model differed from transformers unless the image had the aspect ratio of the grid. With PAD_NONE the tiles are the same as in transformers.
Lfm2VlImageProcessor.smart_resize rounds with Python's round (half to even) and computes beta and the tile grid aspect ratio in double. llama.cpp used std::round and float, so 31926 of the 16.7M sizes in [1,4096]^2 got another thumbnail size or grid than transformers (and d1-omni vision.py), e.g. 400x600 -> 416x608 instead of 384x608 and 720x1600 -> 2x5 tiles instead of 2x4. The lfm2 smart resize and grid choice now follow the Python code and match it on every size of that range.
ykhrustalev
force-pushed
the
ykhrustalev/lfm2-tiles-transformers
branch
from
October 19, 2026 23:45
76b9ea3 to
61042fe
Compare
3 of 6 tasks
auRose94
pushed a commit
to auRose94/llama.cpp
that referenced
this pull request
Oct 28, 2026
* mtmd : lfm2 tiles stretch the image to the grid Lfm2VlImageProcessor.crop_image_to_patches resizes the image straight to (512 * rows, 512 * cols) before it cuts the tiles. llama.cpp kept the aspect ratio and added black bars for LFM2 (only d1-omni stretched), so the tiles of d1-3B and every LFM2-VL / LFM2.5-VL model differed from transformers unless the image had the aspect ratio of the grid. With PAD_NONE the tiles are the same as in transformers. * mtmd : lfm2 layout with Python rounding in double precision Lfm2VlImageProcessor.smart_resize rounds with Python's round (half to even) and computes beta and the tile grid aspect ratio in double. llama.cpp used std::round and float, so 31926 of the 16.7M sizes in [1,4096]^2 got another thumbnail size or grid than transformers (and d1-omni vision.py), e.g. 400x600 -> 416x608 instead of 384x608 and 720x1600 -> 2x5 tiles instead of 2x4. The lfm2 smart resize and grid choice now follow the Python code and match it on every size of that range. * fix --------- Co-authored-by: Xuan Son Nguyen <son@huggingface.co>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
LFM2-VL, LFM2.5-VL, d1-3B and d1-omni cut a large image into 512x512 tiles plus a small thumbnail. llama.cpp did this differently from the reference Python code (transformers
Lfm2VlImageProcessor, d1-omni'svision.py) in two ways:std::roundand float, so 31,926 of the 16.8M image sizes up to 4096x4096 got another grid or thumbnail, and another number of tokens. For example, a 720x1600 image got 2x5 tiles instead of 2x4. This PR rounds as Python does, like mtmd: fix LFM2 image tiling threshold #27057 already did forshould_tile.Only LFM2 code changes; other models are not affected.
Additional information
Reproduction (prompt tokens: master 2812, this PR 2298, transformers 2298):
With a 400x600 image: master 258, this PR 239, transformers 239.
Accuracy: the largest probability difference to the Python reference (fp32; d1: over the answer options, LFM2-VL: over transformers' top-20 next tokens), and the number of items with another prompt length. Master 50e3e3e, Apple M3 Ultra (Metal), F16 models:
clip.vision.image_resize_algo, so llama.cpp resizes it with bilinear while transformers uses bicubic. With the key set to bicubic: 0.444 -> 0.003.Speed follows the number of tokens. Whole-request time, this PR vs master 50e3e3e (F16,
-np 1), on an H100 GPU (CUDA) and a 30-core Xeon 8358 (CPU):Stretching costs nothing: preparing a 1024x768 image takes 25.9 ms instead of 31.0 ms (LFM2.5-VL, CUDA build).
Tests:
test-mtmd-implgets 4 thumbnail-size cases and 4 grid cases with the values transformers gives; master's code fails 6 of the 13 checks. The stretch is a loader setting and has no unit test. For a 1024x768 image on LFM2.5-VL-450M, master's tiles differ from transformers in 97.5% of the pixel values (up to 255, the black bars); this PR's tiles are identical.Requirements