Completed: I2I source aspect-ratio policy¶
Metadata¶
- Created: 2026-06-04
- Status: Completed
- Completed: 2026-06-04
ADR status¶
- Governing ADRs: ADR 0002
- ADR impact: None
Context¶
Wan image-to-video now resolves the generation canvas from the source image ratio before
conditioning the model, which prevents portrait or square sources from being silently stretched
into a mismatched requested video canvas. Ordinary image-to-image has the same user-facing risk:
the model may resize a source image into an explicit --width and --height even when that canvas
does not match the source ratio.
This item covers ordinary image-to-image geometry only. True outpainting/reframing remains tracked by 0019.
Current code reality¶
src/mflux/utils/dimension_resolver.pyresolvesauto/scale factors against a reference image but returns explicit integerwidth/heightas-is.src/mflux/models/common/config/config.pyfloors image dimensions to 16-pixel multiples and resolvesNonedimensions from a reference image, but it has no source-aspect canvas policy.src/mflux/models/common/latent_creator/latent_creator.pyencodes latent img2img sources by resizing the source image toconfig.widthbyconfig.height.src/mflux/models/flux2/variants/edit/flux2_klein_edit.pyandsrc/mflux/models/qwen/variants/edit/qwen_image_edit.pyuse one or more reference images but do not centrally define how output canvas ratio should relate to the primary source image.- CLI image routes that use
supports_dimension_scale_factor=Truedefault toauto, so the common CLI path often preserves the source size by default, but explicit mismatched dimensions can still stretch/recompose ordinary I2I. - Direct Python calls usually have
width=1024andheight=1024defaults, so a rectangular source can be unintentionally forced into a square output unless the caller knows to pass matching dimensions.
Problem¶
Ordinary I2I does not have a consistent geometry contract. Users can ask for a source-conditioned edit or variation, pass a rectangular/square source image, and get a generated canvas that silently uses a different aspect ratio. This makes visual validation confusing and can turn a style/edit request into an unintended reframing/remix.
What we want to do¶
Add a shared source-aspect canvas resolver for ordinary I2I. When an I2I mode has a primary source
image, width and height should be treated as a size target by default, while the final output
canvas preserves the primary source image aspect ratio and model multiple.
Why¶
This gives users and application integrations predictable geometry. It also keeps ordinary I2I separate from explicit canvas-changing workflows such as outpainting, reframing, fill/inpaint, and upscale.
Requirements¶
- Preserve the primary source image aspect ratio for latent img2img, edit/reference I2I, and multi-reference I2I.
- Use the first image as the geometry anchor for multi-reference I2I.
- Treat explicit
widthandheightas an approximate size target, not as permission to stretch the source into a different ratio. - Keep output dimensions on the model's required multiple, starting with 16-pixel multiples for
image generation routes that use the shared
Config. - Keep text-to-image behavior unchanged.
- Do not change masked fill/outpaint/upscale semantics in this item.
- Preserve clean early failures for unsupported task/mode/options.
Suggested implementation¶
- Add a shared image canvas resolver near
DimensionResolver. - Add an opt-in
preserve_image_aspect_ratioflag toConfig. - Enable the flag in latent img2img and edit/reference model paths that use an ordinary source image, but leave fill/outpaint/upscale paths exact-canvas.
- Update Qwen edit dimension computation to use the same resolver with the first input image.
- Adjust Python defaults where needed so direct
generate_image(image_path=...)calls can preserve source geometry without requiring callers to pass source dimensions manually.
Scope¶
- Shared resolver implementation.
- Focused unit tests for canvas resolution and Config behavior.
- Router or model-path tests proving ordinary I2I forwards to the expected backend and preserves dimensions before generation.
- At least one low-cost real image-generation validation asset with command, prompt, source, and output paths for manual inspection.
- Core documentation updates.
Non-goals¶
- Do not implement first-class outpainting/reframing; keep that in item 0019.
- Do not promise ordinary I2I preserves original pixels in place.
- Do not change video canvas policy.
- Do not run heavyweight model validations unless a lightweight local model cannot prove the contract.
Dependencies and related tasks¶
- 0018: taskless generation routing.
- 0020: capability contract and I2I modes.
- 0021: analogous I2V source-ratio behavior.
- 0019: future outpaint/reframe UX.
Expected outcomes¶
- Ordinary I2I no longer silently stretches a source image into a mismatched output ratio.
- CLI and Python direct calls use the same source-aspect policy for ordinary I2I routes.
- Users can reproduce a small validation image and inspect source/output dimensions.
- FAQ/API/getting-started/LLM docs explain the behavior without claiming outpainting support.
Validation¶
- Unit tests for source-aspect canvas resolution across square, portrait, and landscape sources.
- Config tests showing I2I preserves source ratio and T2I remains unchanged.
- Focused model-path tests for at least Flux2 latent/edit and Qwen edit dimension behavior.
- One real low-cost I2I generation command with source, prompt, output, and image-size proof.
uv run ruff check .- Focused pytest target for new tests, plus no doc whitespace errors.
Progress checklist¶
- [x] Create shared resolver and Config integration.
- [x] Enable ordinary latent/edit/multi-reference I2I paths.
- [x] Add focused tests.
- [x] Generate validation source/output assets and record exact commands.
- [x] Update core documentation.
- [x] Move this item to completed with completion evidence.
Guidance for the implementing agent¶
Re-check current code before editing. Keep the implementation explicit and mode-aware: ordinary I2I preserves aspect by default, while fill/outpaint/upscale remain exact-canvas workflows.
Adversarial design reports¶
- API contract review: keep public tasks as media directions and expose canvas behavior through
capability fields. The default for ordinary I2I should be
source-aspect;exact-resizemust be an explicit opt-in. - Code-path review: centralize ordinary I2I geometry in
Configand the shared dimension resolver. Backend CLIs should passautoand explicit dimensions through instead of resolving them before the model config can apply source-aspect policy. - Validation review: prove behavior with a mismatched target canvas, image metadata, and a side-by-side exact-resize comparison. Keep edit/reference and multi-reference route behavior in focused unit/router tests when no local prepared edit model is available.
- Documentation review: state the current contract neutrally. Do not claim ordinary I2I preserves original pixels in place or provides outpainting/reframing.
Completion report¶
Implemented shared ordinary I2I canvas policies:
source-aspect: default for latent img2img, edit/reference I2I, and multi-reference I2I.exact-resize: explicit opt-in for exact requested dimensions.
The capability contract now exposes canvas_policies, default_canvas_policy,
primary_image_index, and dimension_multiple. Generated image metadata records final dimensions,
requested dimensions, source-image dimensions, and canvas_policy.
The implementation routes CLI and Python direct calls through the same config resolver. A final
audit removed backend CLI pre-resolution from ordinary image routes so auto dimensions remain
visible to Config.
Validation evidence¶
Focused tests:
uv run pytest tests/test_task_inference.py tests/cli/test_mlx_gen_router.py tests/cli/test_backend_canvas_policy.py tests/common/config/test_config_dimensions.py tests/utils/test_dimension_resolver.py tests/arg_parser/test_cli_argparser.py tests/callbacks/test_progress_callbacks.py tests/image_generation/test_qwen_edit_dimensions.py tests/metadata/test_generated_image.py tests/ernie_image/test_mistral3_text_encoder.py::test_ernie_img2img_latent_helper_patchifies_and_normalizes -q
Result: 172 passed in 1.11s.
Static check:
uv run ruff check .
Result: All checks passed!.
Post-fix local validation source:
validation_outputs/i2i_matrix_2026_06_04/source_spaceship_432x240.png
Source size: 432x240.
Base FIBO was removed from ordinary I2I capability because the prior generated proof was not a valid I2I/edit quality validation. The route now fails before model load:
uv run mlxgen generate --model ./models/fibo-8bit --image validation_outputs/i2i_matrix_2026_06_04/source_spaceship_432x240.png --i2i-mode latent --image-strength 0.4 --width 320 --height 320 --prompt '{"short_description":"A graphite pencil sketch of the same spaceship in snow."}' --steps 2 --guidance 1 --seed 9107 --metadata --replace --output validation_outputs/i2i_matrix_2026_06_04/final_after_fix/fibo_base_should_reject.png
Post-fix generated compatibility outputs:
validation_outputs/i2i_matrix_2026_06_04/final_after_fix/flux2_9b_8bit_edit_pencil.pngvalidation_outputs/i2i_matrix_2026_06_04/final_after_fix/flux2_9b_8bit_latent_pencil_strength035.pngvalidation_outputs/i2i_matrix_2026_06_04/final_after_fix/flux2_9b_8bit_multi_reference_sketch_crash.pngvalidation_outputs/i2i_matrix_2026_06_04/final_after_fix/flux2_4b_4bit_edit_pencil.pngvalidation_outputs/i2i_matrix_2026_06_04/final_after_fix/flux2_9b_4bit_edit_pencil.pngvalidation_outputs/i2i_matrix_2026_06_04/final_after_fix/qwen_edit_2511_4bit_crash.pngvalidation_outputs/i2i_matrix_2026_06_04/final_after_fix/qwen_image_2512_8bit_latent_bluehour_strength05.pngvalidation_outputs/i2i_matrix_2026_06_04/final_after_fix/qwen_image_2512_4bit_latent_bluehour_strength05.pngvalidation_outputs/i2i_matrix_2026_06_04/final_after_fix/z_image_turbo_8bit_latent_pencil_strength05.pngvalidation_outputs/i2i_matrix_2026_06_04/final_after_fix/z_image_turbo_4bit_latent_pencil_strength05.pngvalidation_outputs/i2i_matrix_2026_06_04/final_after_fix/ernie_turbo_8bit_latent_pencil_strength035.pngvalidation_outputs/i2i_matrix_2026_06_04/final_after_fix/ernie_turbo_4bit_latent_pencil_strength035.png
All generated outputs in this matrix resolve to 432x240, record canvas_policy:
source-aspect, and write metadata sidecars as JSON objects.
Contact sheet:
validation_outputs/i2i_matrix_2026_06_04/final_after_fix/i2i_compatibility_contact_sheet_after_fix.png
Local cache checks that failed before model load because the cached package was incomplete:
FIBO Edit, Qwen Edit 2509 q4/q8, Qwen Edit 2511 q8, FLUX.2 4B q8, FLUX.2 base 9B q8, and
non-Turbo Z-Image q4/q8. Base Qwen with one image and no --image-strength now fails closed
instead of silently swapping to an edit checkpoint.
Documentation updated¶
docs/api.mddocs/faq.mddocs/getting-started.mddocs/python-integration.mddocs/troubleshooting.mddocs/README.mdllms.txtllms-full.txt
Residual work¶
First-class reframe and outpaint follow-up work is tracked in 0019.