Skip to content

fix: pass an explicit factor to smart_resize for Qwen2.5-VL - #241

Open
roshaninfordham wants to merge 2 commits into
roboflow:developfrom
roshaninfordham:fix/qwen-smart-resize-factor
Open

fix: pass an explicit factor to smart_resize for Qwen2.5-VL#241
roshaninfordham wants to merge 2 commits into
roboflow:developfrom
roshaninfordham:fix/qwen-smart-resize-factor

Conversation

@roshaninfordham

Copy link
Copy Markdown

Description

Closes #227.

qwen-vl-utils 0.0.13 made factor a required argument of smart_resize (QwenLM/Qwen3-VL@0dcc180). detections_to_suffix_formatter calls it without one, so every COCO detection dataset fails:

File "maestro/trainer/models/qwen_2_5_vl/detection.py", line 14, in detections_to_suffix_formatter
    input_h, input_w = smart_resize(height=image_h, width=image_w, min_pixels=min_pixels, max_pixels=max_pixels)
TypeError: smart_resize() missing 1 required positional argument: 'factor'

That is the path the Qwen2.5-VL object detection cookbook takes, and pyproject.toml pins qwen-vl-utils>=0.0.8 with no upper bound, so a fresh install resolves to a broken combination.

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)

Motivation and Context

I passed the argument explicitly rather than capping the dependency, for three reasons.

factor has existed in every released version of qwen-vl-utils — only its default was removed — so an explicit call works on both sides of 0.0.13 and needs no version bound.

The value is a property of the model, not of the helper library: Qwen2.5-VL's vision encoder uses a 14px patch with a 2×2 spatial merge, so resized side lengths must be multiples of 28. The repository already assumes this everywhere — min_pixels and max_pixels default to 256 * 28 * 28 and 1280 * 28 * 28.

The issue suggests importing the old IMAGE_FACTOR constant, but 0.0.13 removed it too. On 0.0.14:

>>> import qwen_vl_utils.vision_process as vp
>>> [k for k in dir(vp) if k.isupper()]
['FORCE_QWENVL_VIDEO_READER', 'FPS', 'FPS_MAX_FRAMES', 'FPS_MIN_FRAMES', 'FRAME_FACTOR',
 'IMAGE_MAX_TOKEN_NUM', 'IMAGE_MIN_TOKEN_NUM', 'MAX_NUM_WORKERS_FETCH_VIDEO', 'MAX_RATIO',
 'MODEL_SEQ_LEN', 'SPATIAL_MERGE_SIZE', 'VIDEO_MAX_TOKEN_NUM', 'VIDEO_MIN_TOKEN_NUM',
 'VIDEO_READER_BACKENDS']

So the constant has to be defined locally. It is exposed as an image_factor parameter so a variant with different vision-encoder geometry can override it without editing the module.

Changes Made

  • maestro/trainer/models/qwen_2_5_vl/detection.py — add QWEN_2_5_VL_IMAGE_FACTOR = 28, take an image_factor parameter defaulting to it, and pass it to smart_resize. Add a Google-style docstring covering the new parameter.
  • test/meastro/trainer/models/qwen_2_5_vl/test_detection.py — new, 6 cases: coordinate scaling at two source resolutions, multi-detection label resolution, a non-default image_factor, the empty-detections case, and a check that the resized side lengths really are multiples of the factor.

Testing

  • I have tested this code locally
  • I have added unit tests that prove my fix is effective
  • All new and existing tests pass

With the defect present the new tests reproduce the reported error exactly:

E       TypeError: smart_resize() missing 1 required positional argument: 'factor'
maestro/trainer/models/qwen_2_5_vl/detection.py:40: TypeError

Commands run, on macOS with qwen-vl-utils 0.0.14 installed:

pytest test/ -q --ignore=test/meastro/trainer/models/qwen_2_5_vl   27 passed   (baseline)
pytest test/ -q                                                    33 passed   (+6 new)
ruff check maestro/ test/                                          All checks passed!
pre-commit run --all-files                                         11 hooks, all passed (mypy included)

The expected coordinates in the tests were derived rather than recorded from the implementation. For a 640×480 source, smart_resize returns (h=476, w=644) — 17×28 and 23×28 — so a box at y=20 maps to 20 × 476/480 = 19.83, truncating to 19, which is what the test asserts.

Google Colab (optional)

Not applicable; the failure is a TypeError on import-time-visible signature drift, covered by the unit tests above and reproducible without a GPU or a model download.

qwen-vl-utils 0.0.13 made `factor` a required argument of `smart_resize`,
so `detections_to_suffix_formatter` raised

    TypeError: smart_resize() missing 1 required positional argument: 'factor'

on any COCO detection dataset, which is the path the Qwen2.5-VL fine-tuning
cookbook takes.

Pass the value explicitly rather than bounding the dependency: the argument
exists in every released version of qwen-vl-utils, so an explicit call works
on both sides of the change, and the constant it needs -- the vision encoder's
14px patch size times its 2x2 spatial merge -- is a property of the model
rather than of the helper library. 0.0.13 also removed the IMAGE_FACTOR
constant, so it cannot be imported from qwen-vl-utils any more.

Exposed as an `image_factor` parameter so a variant with different vision
encoder geometry can override it.
@CLAassistant

CLAassistant commented Aug 27, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 11d080d847

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +4 to +7
from maestro.trainer.models.qwen_2_5_vl.detection import (
QWEN_2_5_VL_IMAGE_FACTOR,
detections_to_suffix_formatter,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip qwen tests when the optional extra is absent

In the default test environment this new test is collected unconditionally, but tox.ini only installs pytest plus the package’s normal dependencies, while qwen-vl-utils lives under the optional qwen_2_5_vl extra in pyproject.toml. On a standard tox/dev run without that extra, importing maestro.trainer.models.qwen_2_5_vl.detection fails during collection before any tests can run; gate this with pytest.importorskip("qwen_vl_utils") or install the qwen extra in the test env.

Useful? React with 👍 / 👎.

The module under test imports qwen_vl_utils, which ships with the optional
qwen_2_5_vl extra. Collected unguarded, that import aborts the entire test
run rather than only its own file: in an environment with transformers but
without qwen-vl-utils, the florence_2 suite collects 15 tests today and
collected none with this file present.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Qwen2.5 VL fail to train due to qwen-vl-utils

2 participants