fix: match Python Ultralytics detection output for YOLO ONNX inference#97
Conversation
- Use uniform gain for coordinate back-projection in letterbox params, matching Python's scale_boxes() which applies a single gain to both axes instead of per-axis scales that diverge after rounding - Fix bilinear interpolation to match OpenCV's INTER_LINEAR exactly: use single-pass fixed-point with rounding bias (ROUND_BIAS = 1 << 21) and OpenCV-matching weight computation (saturate_cast<short>((1-fx)*2048)) - Replace video-rs Decoder with custom BilinearVideoDecoder using raw ffmpeg-next API with SWS_BILINEAR flag for YUV→RGB conversion, avoiding video-rs's hardcoded SWS_AREA which can drift pixel values - Remove unused video_frame_to_image function Signed-off-by: Onuralp SEZER <onuralp@ultralytics.com>
|
👋 Hello @onuralpszr, thank you for submitting a To ensure a seamless integration of your work, please review the following checklist:
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! 🚀 |
Signed-off-by: Onuralp SEZER <onuralp@ultralytics.com>
UltralyticsAssistant
left a comment
There was a problem hiding this comment.
🔍 PR Review
Made with ❤️ by Ultralytics Actions
The preprocessing changes look coherent and the intent to match Python/OpenCV behavior is clear. The main issue is in the custom FFmpeg decoder: packet-send failures are currently swallowed in a way that can drop frames, and the new total_frames assignment regresses infinite stream handling by treating live streams as finite.
💬 Posted 2 inline comments
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
Amazing to see this merged — thank you @onuralpszr, with contributions from @picsalex! 🎉 As Leonardo da Vinci said, “Simplicity is the ultimate sophistication.” This PR reflects that beautifully: by simplifying preprocessing and video decoding paths while improving correctness, you’ve helped Your work strengthens alignment with Ultralytics and OpenCV behavior, reduces hard-to-debug prediction differences, and makes the Rust inference experience more predictable for everyone. Really appreciate the care and craftsmanship behind this contribution — it will make a meaningful difference for users. 🙌 |
Use uniform gain for coordinate back-projection in letterbox params, matching Python's scale_boxes() which applies a single gain to both axes instead of per-axis scales that diverge after rounding
Fix bilinear interpolation to match OpenCV's INTER_LINEAR exactly: use single-pass fixed-point with rounding bias (ROUND_BIAS = 1 << 21) and OpenCV-matching weight computation (saturate_cast((1-fx)*2048))
Replace video-rs Decoder with custom BilinearVideoDecoder using raw ffmpeg-next API with SWS_BILINEAR flag for YUV→RGB conversion, avoiding video-rs's hardcoded SWS_AREA which can drift pixel values
Remove unused video_frame_to_image function
🛠️ PR Summary
Made with ❤️ by Ultralytics Actions
🌟 Summary
This PR improves preprocessing and video decoding consistency in
ultralytics/inference🎯, helping Rust inference outputs align more closely with OpenCV/Ultralytics behavior and reducing small prediction differences across images and videos.📊 Key Changes
INTER_LINEARbehavior 🖼️video-rsdecoder path for videos with the new custom decoder, while preserving frame metadata like FPS and estimated total frames 📦🎯 Purpose & Impact