Skip to content

v1.9.4: Export & Aug Correctness Fixes

Choose a tag to compare

@Borda Borda released this 24 Aug 12:37
· 146 commits to release/latest since this release

RF-DETR 1.9.4 is a maintenance release: no public API removed or renamed, no new public API — the new arguments below sit on private ONNX/TFLite reference helpers. It fixes seven bugs — two in export (ONNX/TFLite background-logit-slot assumption, a keypoint tensor misidentified as a segmentation mask), two silent annotation-corruption bugs in augmentation (keypoint flip with empty flip-pairs, Albumentations TimeReverse/SquareSymmetry), one training-correctness bug (BestModelCallback scoring PyTorch Lightning's sanity-check pass as a real epoch), one non-square training resize bug, and one environment-dependent TFLite export failure.

Users exporting sparse-ID COCO checkpoints — including the official pretrained weights — or legacy background-first keypoint checkpoints, training keypoint models with the pydantic-default empty keypoint_flip_pairs, using custom Albumentations TimeReverse/SquareSymmetry configs, starting a new training run seeded from pretrain_weights, or training non-square models benefit most from this release. See the migration guide below — three behavior changes may need action on upgrade.

✨ Spotlights / highlights

Export: configurable background logit slot (#1397)

The ONNX/TFLite reference inference helpers assumed the background class always sits at the final logit index. Sparse-ID COCO checkpoints and legacy background-first keypoint checkpoints decoded wrong. A new background_class_id argument on the private _run_inference helpers makes the assumption explicit — -1 (default) preserves current behavior, None keeps every logit slot, 0 supports legacy background-first keypoint checkpoints. The -1 default is kept for backward compatibility; it still mis-decodes the checkpoints named above, including the official pretrained COCO weights (a real foreground category occupies the final slot there) — pass background_class_id=None explicitly for those. The same commit also replaces the TFLite helper's old guess-any-lone-rank-4-output-is-a-mask behavior with a new rank4_output argument, defaulting to None — a keypoint export's pred_keypoints tensor could previously get silently upsampled into Detections.mask.

Keypoint annotations no longer silently corrupted on flip (#1358)

RandomHorizontalFlip on the torchvision-native backend mirrored keypoint x-coordinates on every drawn flip, but only relabeled left/right joints if self.keypoint_flip_pairs:. With an empty list — the pydantic default — training samples got keypoints mirrored in position while keeping their original left/right label, with no warning. The flip is now dropped entirely for an empty-but-not-None keypoint_flip_pairs, matching the Albumentations backend's existing safety contract.

Albumentations TimeReverse/SquareSymmetry box and keypoint handling (#1386)

Custom Albumentations configs using TimeReverse had it treated as pixel-only — it flipped images while leaving boxes and keypoints unchanged. It now shares the same geometric-transform and replay-based keypoint handling as HorizontalFlip, and the no-pairs safety filter now also covers SquareSymmetry.

BestModelCallback no longer scores the PTL sanity check as a real epoch (#1357)

A positive validation score from PyTorch Lightning's pre-training sanity check — common when starting a new run initialized with pretrain_weights from a checkpoint pretrained on a different dataset — could get written out as the permanent "best" EMA checkpoint before a single real epoch ran, after which real training could never surpass it. EMA tracking now honors the same trainer.sanity_checking guard the regular checkpoint path already had. (This is distinct from PTL's own resume/ckpt_path restart, which PTL itself skips the sanity check for — resumed runs were never affected.)

🔄 Migration guide

No public API was removed or renamed. Three behavior changes may need action on upgrade:

  • TFLite segmentation inference. The _run_inference reference helper no longer treats an anonymous rank-4 output as a mask. Pass rank4_output="masks" for a name-stripped segmentation export.
  • Keypoint training with keypoint_flip_pairs=[] (the pydantic default). The default horizontal flip is now disabled instead of applied without relabeling. Provide left/right pairs to keep the augmentation.
  • Non-square training. The crop branch no longer resamples through a fixed 384x384 intermediate, so the augmented pixel distribution differs from 1.9.3. Square training (the default for every shipped model config) is unchanged.

📝 Notable changes

🔧 Fixed

  • ONNX and TFLite reference inference helpers now accept an explicit background_class_id; a new rank4_output argument replaces the old guess-any-lone-rank-4-output-is-a-mask behavior and defaults to None. (#1397)
  • Non-square training resize no longer double-resamples crop-branch outputs through a fixed 384x384 intermediate. (#1383)
  • Custom Albumentations TimeReverse no longer leaves boxes and keypoints unflipped while the image flips; the no-pairs keypoint safety filter now also covers SquareSymmetry. (#1386)
  • TFLite export no longer fails when onnx2tf can't resolve onnxsim from a non-activated virtualenv; RF-DETR now temporarily adds the running interpreter's script directory to PATH during conversion. (#1366, fixes #1365)
  • Torchvision-native training no longer silently mirrors keypoint positions without relabeling left/right joints when keypoint_flip_pairs=[]. (#1358)
  • BestModelCallback no longer treats PyTorch Lightning's sanity-check pass as a real epoch's result. (#1357, fixes #1348)

🏆 Contributors

  • Jesús Royeth (@JESUSROYETH) — export background-logit-slot fix, keypoint flip-pair safety fix, Albumentations TimeReverse/SquareSymmetry fix, BestModelCallback sanity-check fix.
  • Aman Harsh (@amanharshx, LinkedIn) — TFLite export onnxsim-on-PATH fix.
  • jirka (@Borda, LinkedIn) — non-square training resize double-resample fix.

Full changelog: 1.9.3...1.9.4