Skip to main content

YOLO raw and combined heads

YOLO26 and RawLTRB​

  • YOLO26 with explicit RawLTRB regression in grid-cell units; detection by default.
  • One to three explicitly mapped box/score head pairs; every input has one role.
  • INT8 or BF16; each pair has matching precision and spatial geometry.
  • Logical trailing HWC axes, rank 3–6. Extra leading dimensions must be one. This is batch one, not arbitrary batched/time-axis decoding.
  • Four logical coordinate channels; 1–256 logical score channels/classes.
  • Positive spatial extents up to 32,767, at most 1,048,576 cells per head.
  • Model/original image extents 1–1,048,576; output capacity 1–65,536.
  • Explicit Identity or Sigmoid score activation. Detection threshold [0,1); NMS threshold [0,1]. Zero score threshold is an inclusive numerical cutoff, not a default sentinel. YOLO26 skips NMS at zero and applies it at positive thresholds; ordinary YOLO NMS remains active at zero. Unsupported profiles fail at preparation.
  • No FP16/FP32 input. Pose and segmentation are explicit tasks.

Dense single-class BF16 scores​

A physical dense or padded BF16 score tensor with one logical class and one anchor uses the single-class scanner. It reads only that class, skips the unnecessary class reduction, and advances by the validated physical pixel stride. It does not read padding or cache input values between executions.

The prepared sortable-key cutoff is evaluated as an exact raw-word interval; signed zeros, subnormals, infinities and NaN encodings retain their existing admission behavior. Identity or Sigmoid is selected once per head, with the same float expression and final finite/threshold checks. Logical cell order, class index zero, selection and output clearing are unchanged. Objectness, combined heads and family-specific scanners keep their existing routes. See scanning.h (A65/boxdecode/detail/scanning.h), bf16_cutoff.h (A65/boxdecode/detail/bf16_cutoff.h), and exhaustive and guarded tests (A65/boxdecode/tests/dense_single_test.inc).

Packed single-class BF16 scores​

For one logical BF16 class and one anchor without objectness, the packed scanner reads the low and high bytes of channel zero directly. Every admitted C tile has one real channel in this case, including odd C tiles and tiles larger than the logical channel count. Row runs preserve logical cell order across partial spatial tiles; padding never contributes a score.

This route needs no per-cell class reduction, key/class scratch, or second emission scan. It retains the packed scanner's explicit mapping of both signed zeros to positive zero, the exact prepared cutoff, configured activation and final score checks. Geometry, selection, ties and output clearing are unchanged. See scanning.h (A65/boxdecode/detail/scanning.h) and exhaustive/guarded single-class tests (A65/boxdecode/tests/dense_single_test.inc).

Packed BF16 class-stripe rejection​

The single-anchor packed scanner can reject a BF16 class stripe before reconstructing its words when every logical high byte is provably below the prepared score cutoff. For cutoff_high = cutoff >> 8, the negative-byte bound is max(0x81u, 0x100u - cutoff_high): a raw high byte at or above this bound has a complemented sortable high byte strictly below cutoff_high, regardless of its low byte. The 0x80 bucket, which includes negative zero and small negative subnormals, is never rejected by this shortcut.

Inactive tail lanes are masked out of the test. Any stripe not rejected by the bound retains the ordinary key reduction, full-word reconstruction and score checks. The cutoff comes from the configured activation's actual float results; this test neither approximates sigmoid nor changes the exact threshold or first-class tie rule. See scanning.h (A65/boxdecode/detail/scanning.h) and boundary tests (A65/boxdecode/tests/blocked_scan_test.inc).

For INT8 tensors containing only full C16 stripes, the signed maximum is compared directly with the prepared raw cutoff. The +128 sortable key is formed only for admitted stripes: adding 128 to both operands preserves the comparison, including admit-all -128 and reject-all 128 cutoffs. Surviving stripes keep the same key update, first-class tie behavior. Tensors with partial stripes retain their existing key comparison and padding masks. See cutoff and guarded-route tests (A65/boxdecode/tests/int8_cutoff_test.inc).

The packed scanner clears its per-cell best keys on every call. For a positive cutoff, every class index that can be emitted is written when that cell first replaces its zero key, so unused class-index scratch need not be cleared. A zero cutoff still clears default class indices because an untouched zero key can be admitted. This affects scratch initialization only: unused output rows and mask pixels retain the full zeroing contract on every call. See context-reuse/minimum-key tests (A65/boxdecode/tests/blocked_scan_test.inc).

For full-stripe INT8/BF16 tensors (both logical channels and channel-tile size are multiples of 16), the streaming scanner binds the class-scratch data pointer once per head. Context creation sizes this storage; scanning and candidate emission never resize it. Partial-stripe tensors retain their existing vector access. Both paths keep the same conditional clearing, strict best-key updates and first-class ties. See scanning.h (A65/boxdecode/detail/scanning.h).

Partial INT8 heads whose physical channel tiles contain a single active lane in their final C16 stripe select a singleton-capable scanner once per head. For that stripe, the signed byte supplies the key directly and the class is the stripe's first channel; no class reduction or first-index search is needed. The cutoff and strict best-key update are unchanged, including key zero and earlier-class ties. Heads without such a stripe use the direct partial scanner; BF16 and full-stripe policies are unchanged. Full channel tiles and the final partial channel tile are both considered, including when the logical channel count is smaller than the channel tile. See the guarded policy tests (A65/boxdecode/tests/int8_cutoff_test.inc).

Standard YOLOv5​

  • BoxDecodeYoloV5V1, recipe-derived raw encoding and sigmoid activation; fixed anchors. Segmentation adds explicit coefficients and a basis tensor; arbitrary anchors are not supported.
  • Exactly three combined inputs, with Head::combined(port) in P3/P4/P5 order. Ports may be permuted, but each is mapped exactly once. Head order selects the anchor/stride level, not the input port number.
  • Trailing HWC, rank 3–6 with singleton outer axes; logical C is 3 * (num_classes + 5). Each anchor contains raw x/y/w/h logits, objectness, then class logits. Input is not decoded boxes or post-sigmoid probabilities.
  • Model width/height must be multiples of 32; head dimensions are exactly model dimensions divided by 8, 16 and 32. Common spatial/image/capacity bounds above also apply. Preparation validates this three-level profile explicitly.
  • INT8/BF16 only. At most 256 classes if any head is INT8; otherwise at most 336. Integer quantization is explicit per input. FP16/FP32 inputs are rejected.
  • Dense physical channels must be padded to a multiple of 16, at most 1,024. Compact Contiguous works only when logical C already satisfies that alignment. MlaCBlocked supports combined heads; its C tile must be a multiple of 16, at most 1,024, for both precisions. Partial tiles are accounted for in storage.
  • Fixed standard anchor pairs: P3 (10,13), (16,30), (33,23); P4 (30,61), (62,45), (59,119); P5 (116,90), (156,198), (373,326).
  • Decode uses (2*sigmoid(txy)-0.5+cell)*stride for centers and (2*sigmoid(twh))²*anchor for sizes. Confidence is sigmoid(objectness)*sigmoid(best_class_logit).

YOLOv5 preparation fragment​

This constructs the same plan type for a 640×640, 80-class BF16 model. Logical C is 255; each pixel reserves 256 physical channels. All six shape axes are explicit. The returned plan reports required buffer sizes; allocation, binding and execution follow the BoxDecode call, with three inputs.

#include <simaai/kernels/ops/boxdecode.h>
#include <array>

simaai::kernels::Result<simaai::kernels::ops::boxdecode::Plan>
prepare_yolov5() {
namespace k = simaai::kernels;
namespace b = k::ops::boxdecode;
constexpr int K = 16;
auto head = [](int hw) {
const std::int64_t pixel = 256 * 2;
const std::int64_t row = hw * pixel, frame = hw * row;
return k::TensorSpec{{1,1,1,hw,hw,255}, k::DType::BFloat16,
k::Strided{{frame,frame,frame,row,pixel,2}}};
};
std::array inputs{head(80), head(40), head(20)};
std::array outputs{
k::TensorSpec{{K,4}, k::DType::Int32},
k::TensorSpec{{K}, k::DType::Float32},
k::TensorSpec{{K}, k::DType::Int32},
k::TensorSpec{{1}, k::DType::Int32}};
b::Params params;
params.heads = {b::Head::combined(0), b::Head::combined(1),
b::Head::combined(2)};

params.model_input_size = {.width = 640, .height = 640};
params.original_image_size = {.width = 640, .height = 640};
params.num_classes = 80;
params.max_detections = K;
params.detection_threshold = 0.5f;
params.nms_iou_threshold = 0.5f;

// Sigmoid is derived from the YOLOv5 raw recipe.
params.profile = b::Profile::YoloV5V1;
return b::check_inference({inputs, outputs, params});
}

See the common BoxDecode contract for errors, memory, outputs and qualification.

Background objectness rejection​

The combined YOLO5/YOLO7 scanner reads the three anchors' objectness values for a cell before reading class values. If their exact raw-domain maximum is below the prepared objectness cutoff, the whole cell is rejected. Otherwise the counted anchor loop retains its per-anchor threshold, activation, finite check, class argmax and final score product. This is not a product-threshold approximation, and it does not change tied candidate order.

For packed inputs, objectness addresses advance through bounded physical row runs. Channel metadata is reused at each run boundary, including partial spatial tiles, instead of reconstructing the same metadata for every anchor and cell. Logical row/cell/anchor encounter order remains unchanged. Dense input keeps its ordinary element stride. See scanner implementation (A65/boxdecode/detail/scanning.h) and independent boundary tests (A65/boxdecode/tests/background_gate_test.inc).

Separate YOLOX objectness​

YOLOX prepares an address reader for its extra_input objectness tensor, independently of the class-score reader. Execution uses that tensor's dtype, quantization and tile geometry. The existing within-head dtype agreement still applies; objectness layout and INT8 quantization parameters may differ from those of the class scores. Prepared pixel/channel metadata replaces repeated packed-coordinate divisions. No objectness values are cached: each call reads the current binding.

Early rejection keeps the existing activation and score-product rules. With identity activation, positive objectness below detection_threshold can still produce an accepted product when the class score exceeds one. It must not be rejected using the sigmoid-only bound. See the objectness tests (A65/boxdecode/tests/yolox_objectness_test.inc).

Blocked INT8 score reductions​

The eligible packed full-C16 blocked scanner retains its local best score as a signed raw INT8 value. The initial value -128 corresponds exactly to the previous sortable key zero. Admission compares against the prepared raw cutoff; a strict improvement updates the class, preserving the first equal-score class. When every raw value is -128, class zero remains the default, including the minimum-cutoff case. Activation and final threshold checks are unchanged. This policy does not alter the partial-C16 scanner.

See packed_scan.h (A65/boxdecode/detail/packed_scan.h) and the blocked scanner tests (A65/boxdecode/tests/blocked_scan_test.inc).