Skip to content

ultralytics-inference 0.0.56 Shape right-to-left and complex-script labels in native and web, with up to 2.3x faster label drawing - #604

Merged
onuralpszr merged 32 commits into
mainfrom
rtl-labels
Nov 3, 2026
Merged

onuralpszr merged 32 commits into
mainfrom
rtl-labels

Conversation

@onuralpszr

@onuralpszr onuralpszr commented Nov 3, 2026 •

Copy link
Copy Markdown
Member

✨ Highlights

  • 🌍 Right-to-left labels: Arabic, Persian, Urdu and Hebrew class names now draw correctly in native annotations, with Arabic letters joined, Hebrew points and Arabic vowels on their letter, ligatures and right-to-left reading order
  • 🎯 Matches Ultralytics Python: native labels are shaped with harfrust (the Rust port of HarfBuzz) and ordered with unicode-bidi, the same pictures as Python with Raqm
  • ⚡ Faster labels: up to 2.3x faster label drawing on busy frames, since each glyph is rasterized once per frame
  • 🌐 Browser: Chrome, Firefox and Safari draw right-to-left labels in the same order, including brackets in Safari
  • 📚 Docs: version 0.0.56 across the READMEs, CUDA and OpenVINO docs

📈 Speed

Native label drawing on a 1280x720 frame, release build, median of 41 runs:

Labels per frame COCO names main -> this PR Arabic names main -> this PR CJK names main -> this PR
5 0.19 -> 0.23 ms 0.18 -> 0.19 ms 0.17 -> 0.18 ms
20 0.52 -> 0.44 ms (1.2x faster) 0.39 -> 0.27 ms (1.4x faster) 0.34 -> 0.26 ms (1.3x faster)
50 0.93 -> 0.64 ms (1.5x faster) 0.76 -> 0.42 ms (1.8x faster) 0.66 -> 0.40 ms (1.7x faster)
300 5.12 -> 2.46 ms (2.1x faster) 3.99 -> 1.70 ms (2.3x faster) 3.45 -> 1.53 ms (2.3x faster)

Browser annotate() keeps main's speed for labels without right-to-left text (300 COCO labels: 0.53 ms in WebKit, 0.62 ms in Chromium, 1.5 ms in Firefox). Right-to-left labels are drawn as resolved runs with each run measured once, so 300 Arabic labels take 3.48, 1.57 and 5.83 ms (main 1.68, 0.55 and 5.0 ms), and 20 take 0.28, 0.22 and 0.72 ms.

✅ Checked

  • 20,021 generated labels mixing Arabic, Persian, Urdu, Hebrew, Syriac, Latin, European and Arabic-Indic digits, brackets, mirrored signs, %, currency signs, vowel marks, direction marks and isolates draw the same glyphs at the same positions as Pillow with Raqm (FriBiDi and HarfBuzz) on the Ultralytics Arial Unicode font, plus 57 focused cases such as shin and sin dots, lam-alef with a vowel, shadda with fatha, full-width digits and numbers grouped with no-break spaces.
  • COCO labels on Arial match Raqm too, including the ff kerning in traffic light, so Latin labels move by at most a pixel of rounding compared with main.
  • Chrome, Firefox and WebKit (Safari 27.2 engine) draw right-to-left labels, brackets, numbers, direction marks, isolates and RLO and LRO overrides the same as a whole label drawn in a conforming canvas, 0 differing pixels in Chromium and Firefox, and WebKit matches them where its own canvas breaks brackets and isolates.

🛠️ PR Summary

Made with ❤️ by Ultralytics Actions

🌟 Summary

Releases ultralytics-inference and @ultralytics/yolo 0.0.56 with correct right-to-left Arabic and Hebrew labels in native and browser annotations, and up to 2.3x faster label drawing.

📊 Key Changes

  • Lays out each native label once with unicode-bidi runs split by script and shaped by harfrust, and draws the shaped glyphs with ab_glyph, so measuring and drawing use the same layout.
  • Reuses one shaping plan per direction and script and rasterizes each glyph once per frame, reusing it across labels.
  • Sizes the classification panel from the measured text width, so long CJK names stay inside it and Arabic names no longer widen it.
  • Picks the Unicode font for classification and OBB labels with non-ASCII names, as detection already did, so names such as 猫 no longer draw as boxes.
  • Draws browser labels that may hold right-to-left text as the engine's unicode-bidi runs, each wrapped in an override for its resolved direction, since Safari does not pair brackets in right-to-left canvas text, and lets annotate() take a wasmUrl for pages that load the engine only in a worker.
  • Adds harfrust and unicode-bidi to the annotate feature and unicode-bidi to the web crate, and lists them in the READMEs.
  • Bumps the version to 0.0.56 in Cargo.toml, crates/web/Cargo.toml, web/package.json, both lockfiles and the docs.

🎯 Purpose & Impact

  • Arabic, Persian, Urdu, Hebrew and mixed-script class names read correctly in native and browser outputs and match Ultralytics Python.
  • Frames with many labels annotate faster, which helps video and crowded scenes.
  • The CLI binary grows by about 1 MB (3%) for the shaping engine, and the npm package's wasm by about 47 KB for the bidi runs; right-to-left browser labels take a little longer to draw, about 0.3 ms for 20 labels.
📋 Skipped 2 files (lock files, generated, images, etc.)
  • Cargo.lock
  • web/package-lock.json

onuralpszr and others added 4 commits November 3, 2026 14:20
…notator

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
Signed-off-by: Onuralp SEZER <onuralp@ultralytics.com>
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
@UltralyticsAssistant UltralyticsAssistant added the enhancement New feature or request label Nov 3, 2026
@UltralyticsAssistant

Copy link
Copy Markdown
Member

👋 Hello @onuralpszr, thank you for submitting a ultralytics/inference 🚀 PR! This automated message confirms your contribution was received, and an Ultralytics engineer will assist with the review. To ensure a seamless integration of your work, please review the following checklist:

  • ✅ Define a Purpose: Clearly explain the purpose of your fix or feature in your PR description, and link to any relevant issues. Ensure your commit messages are clear, concise, and adhere to the project's conventions.
  • ✅ Synchronize with Source: Confirm your PR is synchronized with the ultralytics/inference main branch. If it's behind, update it by clicking the 'Update branch' button or by running git pull and git merge main locally.
  • ✅ Ensure CI Checks Pass: Verify all Ultralytics Continuous Integration (CI) checks are passing. If any checks fail, please address the issues.
  • ✅ Update Documentation: Update the relevant documentation for any new or modified features.
  • ✅ Add Tests: If applicable, include or update tests to cover your changes, and confirm that all tests are passing.
  • ✅ Sign the CLA: Please ensure you have signed our Contributor License Agreement if this is your first Ultralytics PR; the CLA check will comment with signing instructions if needed.
  • ✅ Minimize Changes: Limit your changes to the minimum necessary for your bug fix or feature addition. "It is not daily increase but daily decrease, hack away the unessential. The closer to the source, the less wastage there is." — Bruce Lee

For more guidance, please refer to our Contributing Guide. Don't hesitate to leave a comment if you have any questions. Thank you for contributing to Ultralytics! 🚀

@codecov

codecov Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.83721% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/annotate.rs 98.83% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@UltralyticsAssistant UltralyticsAssistant left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🔍 PR Review

Made with ❤️ by Ultralytics Actions

Reviewed the native Arabic joining and bidi implementation, web canvas direction changes, and version/documentation updates. The main RTL paths are updated, but native labels with paired punctuation around embedded LTR text still render incorrectly.

💬 Posted 1 inline comment
  • 💡 MEDIUM src/annotate.rs:710 This only reorders clusters; it never mirrors characters at odd RTL levels. For an RTL label containing an LTR word in parentheses, such as Hebrew or Arabic text followed by (cat), the rendered parentheses face outward as )cat( instead of enclosing the word. draw_text_mut receives only this reordered string, so no later bidi-aware renderer corrects it. Apply Unicode bidi mirroring to odd-level characters before collecting the output. (unicode.org)
📋 Skipped 2 files (lock files, generated, images, etc.)
  • Cargo.lock
  • web/package-lock.json

…percent handling

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>

@UltralyticsAssistant UltralyticsAssistant left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🔍 PR Review 2

Made with ❤️ by Ultralytics Actions

Since review 1, odd-level bracket mirroring was added, addressing the earlier punctuation finding. The version and web changes look consistent, but native bidi still misorders RTL labels with a trailing percent sign.

💬 Posted 1 inline comment
  • 💡 MEDIUM src/annotate.rs:664 For an RTL label ending in a percentage, e.g. عربي 50%, % is classified as Neutral here. The neutral pass gives it level 1, so reordering puts it before the digits (%50). Unicode bidi rule W5 treats a percent sign adjacent to European digits as part of the number run; resolve ET characters before the neutral pass so the output remains 50%. (unicode.org)
📋 Skipped 2 files (lock files, generated, images, etc.)
  • Cargo.lock
  • web/package-lock.json

… each label glyph once per frame

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>

@UltralyticsAssistant UltralyticsAssistant left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🔍 PR Review 3

Made with ❤️ by Ultralytics Actions

Since review 2, the percent resolver distinguishes European from Arabic numbers. I withdraw review 2's requested 50% visual order for Arabic-context digits: UAX #9 applies W2 before W5, converting EN after AL to AN, while W5 attaches terminators to EN. The previous parenthesis-mirroring fix remains; one bounded native mirroring gap remains for other Unicode mirrored characters. Reviewed the native annotator, web canvas direction, and version/documentation updates. (unicode.org)

💬 Posted 1 inline comment
  • 📝 LOW src/annotate.rs:831 For an RTL class name containing ≤, this fallback leaves U+2264 unchanged, although Unicode's bidi mirroring data maps it to ≥ (U+2265), so the operator still faces the wrong way. Since odd-level clusters reach this helper, use the Unicode mirroring mapping or cover the supported mirrored operators instead of returning every unlisted character unchanged. (unicode.org)
📋 Skipped 2 files (lock files, generated, images, etc.)
  • Cargo.lock
  • web/package-lock.json

@UltralyticsAssistant UltralyticsAssistant left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🔍 PR Review 4

Made with ❤️ by Ultralytics Actions

Since review 3, the added mirror-pair table addresses the earlier ≤ example. Native RTL mirroring still misses Unicode pairs such as ∈/∋; the rest of the reviewed native, web, and version/documentation changes show no new actionable issue.

💬 Posted 1 inline comment
  • 📝 LOW src/annotate.rs:807 Since review 3, this table adds the ≤/≥ pair, fixing that example, but other mirrored characters still pass through unchanged. For an RTL label such as עברית ∈ שלום, U+2208 (∈) should mirror to U+220B (∋), but that pair is absent here, so the operator faces the wrong way. Use the Unicode mirroring mapping or add the supported pairs.
📋 Skipped 2 files (lock files, generated, images, etc.)
  • Cargo.lock
  • web/package-lock.json

… only Arabic-Indic digits

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>

@UltralyticsAssistant UltralyticsAssistant left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🔍 PR Review 5

Made with ❤️ by Ultralytics Actions

Since review 4, the mirror table adds the ∈/∋ pair and resolves the prior finding. The native RTL formatting, glyph caching, browser direction, and version updates otherwise look consistent, but classification-only and OBB-only RTL labels can still use the non-Unicode font. Reviewed scope: native annotations, browser canvas changes, and release/version documentation; this font-selection gap remains.

💬 Posted 1 inline comment
  • 💡 MEDIUM src/annotate.rs:1464 For classification-only results (probs set, boxes absent), the font-selection flag in annotate_image_with_options checks only result.boxes, so this new RTL path still loads Arial.ttf instead of the Unicode font and Arabic/Hebrew names render as missing glyphs. The same gap affects OBB-only results routed through draw_label. Include names from every rendered task, including classification and OBB, when selecting the Unicode font.
📋 Skipped 2 files (lock files, generated, images, etc.)
  • Cargo.lock
  • web/package-lock.json

…I names too

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>

@UltralyticsAssistant UltralyticsAssistant left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🔍 PR Review 6

Made with ❤️ by Ultralytics Actions

Since review 5, the Unicode-font selection gap for classification and OBB labels is addressed. The native RTL, browser direction, and version/documentation changes otherwise look consistent. One rendering issue remains: combining marks are reordered with their base but are not positioned over it.

💬 Posted 1 inline comment
  • 💡 MEDIUM src/annotate.rs:901 For labels containing Hebrew points or Arabic harakat, visual_order keeps each mark with its base character, but this rasterizer still places the mark at the current caret—after the base glyph’s advance—and does not apply mark-to-base positioning. The marks therefore render beside or after the base instead of over it. Position combining-mark glyphs relative to their base before rasterizing so vowelized RTL labels render correctly.
📋 Skipped 2 files (lock files, generated, images, etc.)
  • Cargo.lock
  • web/package-lock.json

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>

@UltralyticsAssistant UltralyticsAssistant left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🔍 PR Review 7

Made with ❤️ by Ultralytics Actions

Since review 6, combining marks now receive explicit horizontal placement over their base. I reviewed the native RTL rendering, browser direction handling, and version/documentation updates; one bounded rendering issue remains for Arabic Extended-A combining marks, which can break joining and detach from their base.

💬 Posted 1 inline comment
  • 📝 LOW src/annotate.rs:535 Arabic Extended-A combining marks such as U+08D3 (ARABIC SMALL LOW WAW) are omitted from is_mark. In a label such as ب࣓ت, join_arabic treats the mark as a boundary, so the adjacent letters do not join; visual_order can also separate the mark from its base. Include the relevant Arabic Extended-A combining-mark code points here. (unicode.org)
📋 Skipped 2 files (lock files, generated, images, etc.)
  • Cargo.lock
  • web/package-lock.json

…luding Arabic Extended-A

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
Comment thread src/annotate.rs Resolved
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
…dth digits and grouped numbers keep their order

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
…o-left labels

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
…d sin dots sit on the right side

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>

@UltralyticsAssistant UltralyticsAssistant left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🔍 PR Review 9

Made with ❤️ by Ultralytics Actions

Since review 8, the author’s FriBiDi verification settles the Arabic-Indic digit-order concern. This pass found a native kerning regression for labels containing kerning pairs; the other reviewed RTL, web, and version changes show no new actionable issues.

💬 Posted 1 inline comment
  • 📝 LOW src/annotate.rs:1065 When a label contains a kerning pair, this applies kern(id, prev) after the current glyph's position and advance have already been computed. The pair is therefore drawn without kerning, and the adjustment is applied to the following glyph instead, changing native label spacing from the previous renderer. Apply kerning before computing the current glyph's position and advance.
📋 Skipped 2 files (lock files, generated, images, etc.)
  • Cargo.lock
  • web/package-lock.json

Comment thread src/annotate.rs Resolved
Comment thread src/annotate.rs Resolved
…rabic and bidi tables

Signed-off-by: Onuralp SEZER <onuralp@ultralytics.com>

@UltralyticsAssistant UltralyticsAssistant left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🔍 PR Review 10

Made with ❤️ by Ultralytics Actions

Since review 9, the author clarified that kerning behavior matches imageproc, and the earlier font-selection, combining-mark, mirroring, and digit-order concerns are resolved. I reviewed the current native annotation, browser direction, and version/documentation changes; no new actionable issue remains. LGTM.

📋 Skipped 2 files (lock files, generated, images, etc.)
  • Cargo.lock
  • web/package-lock.json

onuralpszr and others added 13 commits November 3, 2026 17:07
Signed-off-by: Onuralp SEZER <onuralp@ultralytics.com>
… text

Signed-off-by: Onuralp SEZER <onuralp@ultralytics.com>
Signed-off-by: Onuralp SEZER <onuralp@ultralytics.com>
…ure the classification panel from that layout

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
…es shaping as fast as the hand-written layout

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
…ts and measure each run once

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
… READMEs

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
…bracket runs

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
…has loaded it in this thread

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
… in its resolved direction

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
…s for the label background

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
…web READMEs

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
@onuralpszr onuralpszr changed the title ultralytics-inference 0.0.56 Draw Arabic and Hebrew labels right to left in native and web ultralytics-inference 0.0.56 Shape right-to-left and complex-script labels in native and web for up to 2.3x faster label drawing Nov 3, 2026
@onuralpszr onuralpszr changed the title ultralytics-inference 0.0.56 Shape right-to-left and complex-script labels in native and web for up to 2.3x faster label drawing ultralytics-inference 0.0.56 Shape right-to-left and complex-script labels in native and web, with up to 2.3x faster label drawing Nov 3, 2026
@onuralpszr
onuralpszr merged commit eb55d93 into main Nov 3, 2026
19 checks passed
@onuralpszr
onuralpszr deleted the rtl-labels branch November 3, 2026 16:02
@UltralyticsAssistant

Copy link
Copy Markdown
Member

🎉 PR #604 has been merged—thank you, @onuralpszr, for improving right-to-left label rendering and speeding up annotation in ultralytics-inference and @ultralytics/yolo 0.0.56!

“The details are not the details. They make the design.” — Charles Eames

That attention to shaping, measurement, and bidi handling helps Arabic, Hebrew, and mixed-script labels render correctly across native and browser annotations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants