Skip to content

fix: prevent zero division in xcycsr_to_xyxy converter - #485

Merged
Borda merged 12 commits into
roboflow:developfrom
pranaysb:fix/converters-zero-division-clean
Jul 1, 2026
Merged

fix: prevent zero division in xcycsr_to_xyxy converter#485
Borda merged 12 commits into
roboflow:developfrom
pranaysb:fix/converters-zero-division-clean

Conversation

@pranaysb

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes a bug in xcycsr_to_xyxy (src/trackers/utils/converters.py) where bounding boxes with zero scale or zero aspect ratio cause a division by zero, producing NaN values that propagate into downstream tracking state.

The inverse function, xyxy_to_xcycsr, already guards this exact division pattern with w / (h + 1e-6). This protection was missing from the reverse conversion. This PR adds the same 1e-6 epsilon to xcycsr_to_xyxy for both the single-box and batch paths.

Related Issue(s): Fixes #484

Type of Change

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

Testing

  • I have tested this change locally
  • I have added/updated tests for this change

Test details:
Updated test_xcycsr_to_xyxy_zero_scale and test_xcycsr_to_xyxy_zero_aspect to assert finite output instead of NaN, and added a new test test_xcycsr_to_xyxy_batch_mixed_degenerate covering a mix of normal and degenerate boxes in one batch call.

uv run pytest tests/ -v → 711 passed, 2 skipped

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code where necessary, particularly in hard-to-understand areas
  • My changes generate no new warnings or errors
  • I have updated the documentation accordingly (if applicable)

Additional Context

Minimal reproducible example showing the bug on unpatched develop:

import numpy as np
from trackers.utils.converters import xcycsr_to_xyxy

box = np.array([10.0, 20.0, 0.0, 1.0])
result = xcycsr_to_xyxy(box)
print(result)
RuntimeWarning: invalid value encountered in scalar divide
[10. nan 10. nan]
(Screenshot attached)

Verified locally: uv run ruff check — all checks passed.
uv run ruff format --check — clean.

Screenshot of the ISSUE:-
image

Issue resolved :
image

Screenshot— Full test suite passing
'uv run pytest tests/ -v'
Screenshot the final summary line at the bottom (711 passed, 2 skipped).
image

@pranaysb
pranaysb requested a review from SkalskiP as a code owner June 30, 2026 15:22
@CLAassistant

CLAassistant commented Jun 30, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@Borda Borda added the bug Something isn't working label Jul 1, 2026
@Borda
Borda requested a review from Copilot July 1, 2026 09:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes a numerical stability bug in xcycsr_to_xyxy (center/scale/ratio → xyxy) so degenerate boxes (zero scale or zero aspect ratio) no longer trigger division-by-zero and propagate NaN values into downstream tracking state.

Changes:

  • Add an epsilon guard to the h = scale / w computation in xcycsr_to_xyxy for both 1-D and batch inputs.
  • Update converter tests to assert finite outputs for degenerate inputs and add a mixed-degenerate batch test case.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/trackers/utils/converters.py Adds division guard in xcycsr_to_xyxy to prevent NaN/Inf outputs on degenerate boxes.
tests/utils/test_converters.py Updates/extends tests to validate finite outputs for degenerate and mixed batch inputs.

Comment thread src/trackers/utils/converters.py Outdated
Comment thread tests/utils/test_converters.py Outdated
Comment thread src/trackers/utils/converters.py Outdated
@Borda Borda self-assigned this Jul 1, 2026
@Borda Borda changed the title fix: prevent zero division in xcycsr_to_xyxy converter fix: prevent zero division in xcycsr_to_xyxy converter Jul 1, 2026
Borda and others added 3 commits July 1, 2026 15:24
…additive guard

Replace `(w + 1e-6)` with `max(w, 1e-6)` (single-box) and `np.maximum(w, 1e-6)`
(batch) so the guard only fires when w=0, leaving normal-box arithmetic exact.
Additive form perturbed every decoded height, breaking the existing doctest and
causing all 12 CI matrix jobs to fail.

- [resolve roboflow#1] Copilot (gh): apply clamped epsilon on single-box path (line 152)
- [resolve roboflow#1] Copilot (gh): apply clamped epsilon on batch path (line 165)

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
…ove hardcoded epsilon comment

Replace exact equality with np.testing.assert_allclose (rtol=1e-6) in
test_xcycsr_to_xyxy_zero_aspect so the assertion stays robust across
platforms and future epsilon changes. Remove inline comment that
hardcoded the 1e-6 constant value.

- [resolve roboflow#2] Copilot (gh): approx comparison in zero_aspect assertions
- [resolve roboflow#3] /review finding (foundry:qa-specialist): remove epsilon-coupling comment

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
@pranaysb
pranaysb force-pushed the fix/converters-zero-division-clean branch from 1c66cce to 50ec31a Compare July 1, 2026 13:41
pre-commit-ci Bot and others added 5 commits July 1, 2026 13:41
…ar type

- Add `r1 != 0` guard to `_unfreeze_xcycsr` in ocsort/tracklet.py so
  zero-aspect boxes yield h=0 (not h=inf), consistent with xcycsr_to_xyxy
- Change `else 0.0` to `else np.float64(0.0)` in scalar decode path to
  eliminate Python float / numpy.float64 type asymmetry

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
- Parametrize zero_scale + zero_aspect tests into single
  test_xcycsr_to_xyxy_degenerate_collapses_to_point with ids
- Add coordinate value assertions (assert_array_almost_equal) to
  test_xcycsr_to_xyxy_batch_mixed_degenerate for all 3 rows
- Add test_xcycsr_to_xyxy_batch_negative_scale covering NaN propagation
  in the batch code path
- Add test_roundtrip_degenerate_is_lossy asserting known-lossy result
  for zero-area boxes (width cannot be recovered after scale=0 encoding)
- Add pytest.param IDs to all parametrize blocks across both test classes

---
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
@Borda
Borda merged commit c56b77e into roboflow:develop Jul 1, 2026
18 checks passed
@Borda Borda mentioned this pull request Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: xcycsr_to_xyxy divides by zero for degenerate boxes (zero scale or aspect ratio)

4 participants