You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Added
Box line width.ImageProcessorTrait::set_box_line_width(width) sets the thickness, in destination pixels, of the box outlines drawn by draw_decoded_masks and draw_proto_masks on the GL and CPU backends; the default stays 3 (DEFAULT_BOX_LINE_WIDTH) and 0 is Error::InvalidOptions. G2D returns NotImplemented, as it does for set_class_colors. Python: ImageProcessor.set_box_line_width(width) (ValueError for 0). C: ef_image_processor_set_box_line_width (EINVAL for 0).
Display pipeline examples for C and Python.crates/image-capi/examples/display_pipeline.c and crates/python-image/examples/display_pipeline.py build the display path on a device: the GPU renders the frame and boxes into a hardware-only DMA-BUF canvas for the compositor, a CPU-written overlay uses a write-only image, and the decoder turns a captured model's outputs into boxes. Each step prints PASS or FAIL, with decode and draw timings.
Changed
DmaBuf tensors declared CpuAccess::None or CpuAccess::Write are allocated from the uncached CMA heap when available (edgefirst-tensor). On Linux, Tensor::image, ImageDesc, image_with_stride and ImageProcessor::create_image (and the C and Python image constructors) try /dev/dma_heap/linux,cma-uncached first for such images, then the cached CMA heap and the system heap as before; Read and ReadWrite images are unchanged. Devices that map these buffers (G2D, a compositor, GPU import) no longer clean and invalidate the CPU cache over them on every access, and CPU writes to them are write-combined. The declaration is enforced on every CPU map, the HAL's own included, so these buffers are never read by the CPU. The heap that served an allocation is logged at debug level.
Box outlines take the palette's alpha. The alpha of each set_class_colors colour now sets the box outline's coverage as well as the mask's: boxes blend over the image with the colour's alpha scaled by the draw's opacity, so colours with alpha below 255 now draw translucent boxes where GL drew them opaque after set_class_colors. The CPU backend now blends boxes the same way, where it drew them opaque and ignored opacity; with the default palette (alpha 0.7) its boxes are 70% opaque, as GL's already were. Pass alpha 255 for opaque boxes. Masks are unchanged. ef_image_abi_version is 5.
The per-scale score prefilter runs for every level size (edgefirst-decoder). A level whose anchor count is not a multiple of the 16-anchor pack, such as the 60x34 and 30x17 levels of a 960x544 input, used to send the whole frame to the dense decode. The prefilter now packs the level's whole groups and decodes its trailing partial group whole, with the same detections as the dense decode.
8-bit DFL boxes decode through an exp table (edgefirst-decoder). Per-scale int8 and uint8 DFL box tensors with per-tensor quantization find each side's largest bin in the integer domain and weigh the bins through a 256-entry exp table built once per level, with the four sides in NEON lanes on aarch64. On an i.MX 95 (Cortex-A55) the box kernel takes about 120 ns per anchor, down from about 760 ns for the FP16 softmax kernel, and its boxes are within 4e-6 grid units of an f64 softmax where the FP16 kernel's were up to 8e-3 off. f16 decode output now uses the same kernel; 16-bit and per-channel quantized boxes are unchanged. A box scale that is not finite and positive, which the table cannot represent, is decoded by the max-subtracted softmax as before.
The dense per-scale decode spreads its work evenly over the rayon pool (edgefirst-decoder). It ran the first level on one thread and each other level on another, so the largest level set the wall time. Each input is now mapped once and the levels are cut into pieces of similar size on 16-anchor boundaries, with identical results for any thread count; frames under 2,048 anchors stay on the calling thread. On an i.MX 95 a dense 960x544 decode takes 0.47 ms (1.6 ms before) and a dense 640x640 80-class decode 1.7 ms (6.0 ms).
Per-scale integer scores take the fused f32 sigmoid on FP16 cores (edgefirst-decoder). CPUs with FP16 arithmetic dequantized scores and then ran an FP16 sigmoid in a second pass; they now use the single fused f32 pass that other NEON CPUs use, which is also more accurate. On an i.MX 95 a dense 640x640 80-class decode drops from 1.7 ms to 1.1 ms of wall time and from 9.3 ms to 6.2 ms of CPU time.
The score prefilter scans raw scores in blocks (edgefirst-decoder). It tests 64 raw scores at a time for one at the threshold's floor and examines only the rows of blocks that hold one; a prefiltered 640x640 80-class frame takes 0.24 ms on an i.MX 95 (1.7 ms before).
Small float candidate filters run on the calling thread (edgefirst-decoder).postprocess_boxes_float, postprocess_boxes_index_float and postprocess_boxes_multilabel_index_float (and their callers, including the per-scale decode) filter fewer than 65,536 score elements without rayon, whose workers spent several times the filter's own CPU time waking and spinning. On an i.MX 95 this removes about 440 µs of CPU time per prefiltered 960x544 decode, which now takes 60 µs of wall and CPU time (8.6 ms of wall and 12 ms of CPU in 0.37.0, with the other changes above).
Dependencies updated.argminmax moves from 0.6 to 0.7 and zip from 8 to 9 (the model-files feature of edgefirst-decoder; a ZIP trailer entry whose name is not valid UTF-8 is now skipped). Every lockfile, including the standalone C API crates', takes the latest semver-compatible releases; pyo3 stays on 0.29 to keep the Python 3.8 abi3 wheels.