mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-18 07:29:45 +00:00
refactor: infer YOLO layout from the model, drop per-detector options and the dxrtd placeholder
This commit is contained in:
parent
15ce8f83e4
commit
82dcdca8c0
@ -5,8 +5,10 @@
|
||||
# modules, so this runs outside the image; the driver creates the /dev/dxrt*
|
||||
# nodes and the daemon multiplexes the NPU across host and container.
|
||||
#
|
||||
# Driver, runtime (v3.4.0) and NPU firmware (v2.7.4) must agree or inference
|
||||
# hangs instead of failing at startup; 3.4.0 needs driver 2.5.0, firmware 2.7.0.
|
||||
# Driver, runtime and firmware versions must agree or inference hangs instead
|
||||
# of failing at startup. The set this script installs is pinned in
|
||||
# driver_version, runtime_version and firmware_version below; move them
|
||||
# together, never one at a time.
|
||||
#
|
||||
# DEEPX NPU support in Frigate is maintained by Sixfab (https://sixfab.com).
|
||||
|
||||
@ -28,11 +30,13 @@ if ! lspci -d 1ff4: | grep -q .; then
|
||||
fi
|
||||
|
||||
# fetch the pinned commit rather than cloning the tag, so a retag cannot swap
|
||||
# in different source
|
||||
mkdir dx_rt_npu_linux_driver
|
||||
# in different source. The build directory is reused so a second run after a
|
||||
# failure does not stop on the directory already being there
|
||||
mkdir -p dx_rt_npu_linux_driver
|
||||
cd dx_rt_npu_linux_driver
|
||||
git init -q
|
||||
git remote add origin https://github.com/DEEPX-AI/dx_rt_npu_linux_driver.git
|
||||
git remote get-url origin > /dev/null 2>&1 ||
|
||||
git remote add origin https://github.com/DEEPX-AI/dx_rt_npu_linux_driver.git
|
||||
git fetch --depth 1 origin "${driver_commit}"
|
||||
git checkout -q FETCH_HEAD
|
||||
|
||||
@ -93,8 +97,24 @@ sudo ldconfig
|
||||
rm -f "${deb_file}"
|
||||
|
||||
sudo cp /usr/share/libdxrt-bin/service/dxrt.service /etc/systemd/system/
|
||||
|
||||
# With an endpoint set, dxrtd binds that path only, so the socket goes in a
|
||||
# directory Frigate can mount (kept across restarts so the mount stays valid)
|
||||
# and a symlink at the default /tmp path keeps host tools that do not set the
|
||||
# variable working through their own fallback.
|
||||
sudo mkdir -p /etc/systemd/system/dxrt.service.d
|
||||
sudo tee /etc/systemd/system/dxrt.service.d/frigate.conf > /dev/null <<'UNIT'
|
||||
[Service]
|
||||
RuntimeDirectory=dxrt
|
||||
RuntimeDirectoryMode=0755
|
||||
RuntimeDirectoryPreserve=yes
|
||||
Environment=DXRT_DYNAMIC_IPC_ENDPOINT=/run/dxrt/dxrt_dynamic_ipc.sock
|
||||
ExecStartPost=/bin/ln -sfn /run/dxrt/dxrt_dynamic_ipc.sock /tmp/dxrt_dynamic_ipc.sock
|
||||
UNIT
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now dxrt.service
|
||||
sudo systemctl enable dxrt.service
|
||||
sudo systemctl restart dxrt.service
|
||||
|
||||
if ! sudo systemctl is-active --quiet dxrt.service; then
|
||||
echo "dxrt.service did not start. Check: sudo journalctl -u dxrt.service"
|
||||
|
||||
@ -988,37 +988,9 @@ memryx:
|
||||
deepx:
|
||||
title: DEEPX NPU
|
||||
models:
|
||||
- key: yolo
|
||||
label: YOLO (anchor / anchor-free / NMS-in-head)
|
||||
recommended: true
|
||||
download: No model is bundled with Frigate. Download a pre-compiled `.dxnn` model from the [DEEPX ModelZoo](https://developer.deepx.ai/modelzoo) or compile your own with DX-COM, then bind-mount it into the container and point the model's `path` at it.
|
||||
ui: |-
|
||||
Navigate to **Settings > System > Detection models** and select **DEEPX NPU** from the **Hardware** dropdown. Then, on the same model, open the **Custom Model** tab and configure:
|
||||
|
||||
| Field | Value |
|
||||
| ---------------------------------------- | -------------------------------------- |
|
||||
| **Custom object detector model path** | `/config/model_cache/deepx/model.dxnn` |
|
||||
| **Label map for custom object detector** | `/labelmap/coco-80.txt` |
|
||||
| **Object detection model input width** | `640` |
|
||||
| **Object detection model input height** | `640` |
|
||||
| **Model Input Pixel Color Format** | `rgb` (Frigate's default value) |
|
||||
| **Model Input Tensor Shape** | `nhwc` (Frigate's default value) |
|
||||
| **Model Input D Type** | `int` (Frigate's default value) |
|
||||
| **Object Detection Model Type** | `yolo-generic` |
|
||||
|
||||
The width and height must match the resolution the `.dxnn` file was compiled for. Quantization is baked into the compiled model, so no normalization is applied on the host and the input defaults do not need to be overridden.
|
||||
yaml: |-
|
||||
models:
|
||||
- devices:
|
||||
- deepx:PCIe:0
|
||||
path: /config/model_cache/deepx/model.dxnn
|
||||
labelmap_path: /labelmap/coco-80.txt
|
||||
model_type: yolo-generic
|
||||
width: 640
|
||||
height: 640
|
||||
- key: damoyolo
|
||||
label: DAMO-YOLO (TinyNAS-L20T / S / M / L)
|
||||
recommended: false
|
||||
recommended: true
|
||||
download: No model is bundled with Frigate. Download a pre-compiled DAMO-YOLO `.dxnn` model (TinyNAS-L20T, S, M, or L size) from the [DEEPX ModelZoo](https://developer.deepx.ai/modelzoo), then bind-mount it into the container and point the model's `path` at it. All four sizes share one detection head, so nothing else has to be told apart.
|
||||
ui: |-
|
||||
Navigate to **Settings > System > Detection models** and select **DEEPX NPU** from the **Hardware** dropdown. Then, on the same model, open the **Custom Model** tab and configure:
|
||||
@ -1044,6 +1016,34 @@ deepx:
|
||||
model_type: damo-yolo
|
||||
width: 640
|
||||
height: 640
|
||||
- key: yolo
|
||||
label: YOLO
|
||||
recommended: false
|
||||
download: No model is bundled with Frigate. Download a pre-compiled YOLO `.dxnn` model from the [DEEPX ModelZoo](https://developer.deepx.ai/modelzoo) or compile your own with DX-COM, then bind-mount it into the container and point the model's `path` at it. The output layout is read from the compiled model, so anchor-based, anchor-free, NMS-in-head and anchor-free PPU models need no extra configuration. PPU models compiled from an anchor-based head are not supported.
|
||||
ui: |-
|
||||
Navigate to **Settings > System > Detection models** and select **DEEPX NPU** from the **Hardware** dropdown. Then, on the same model, open the **Custom Model** tab and configure:
|
||||
|
||||
| Field | Value |
|
||||
| ---------------------------------------- | -------------------------------------- |
|
||||
| **Custom object detector model path** | `/config/model_cache/deepx/model.dxnn` |
|
||||
| **Label map for custom object detector** | `/labelmap/coco-80.txt` |
|
||||
| **Object detection model input width** | `640` |
|
||||
| **Object detection model input height** | `640` |
|
||||
| **Model Input Pixel Color Format** | `rgb` (Frigate's default value) |
|
||||
| **Model Input Tensor Shape** | `nhwc` (Frigate's default value) |
|
||||
| **Model Input D Type** | `int` (Frigate's default value) |
|
||||
| **Object Detection Model Type** | `yolo-generic` |
|
||||
|
||||
The width and height must match the resolution the `.dxnn` file was compiled for. Quantization is baked into the compiled model, so no normalization is applied on the host and the input defaults do not need to be overridden.
|
||||
yaml: |-
|
||||
models:
|
||||
- devices:
|
||||
- deepx:PCIe:0
|
||||
path: /config/model_cache/deepx/model.dxnn
|
||||
labelmap_path: /labelmap/coco-80.txt
|
||||
model_type: yolo-generic
|
||||
width: 640
|
||||
height: 640
|
||||
tensorrt:
|
||||
title: TensorRT
|
||||
models:
|
||||
|
||||
@ -655,64 +655,12 @@ The DX-RT Python bindings are not part of the Frigate image. They are downloaded
|
||||
|
||||
<ModelConfigDropdown detectorTitle="DEEPX" models={objectDetectorsModels.deepx.models} />
|
||||
|
||||
Frigate does not bundle a model for this detector. Models must be compiled to DEEPX's `.dxnn` format.
|
||||
Frigate does not bundle a model for this detector. Models must be compiled to DEEPX's `.dxnn` format. Two model types are supported:
|
||||
|
||||
The quickest way to get one is the [DEEPX ModelZoo](https://developer.deepx.ai/modelzoo), which publishes pre-compiled `.dxnn` files for a range of object detection models alongside a JSON file describing how each was compiled: YOLO variants (`model_type: yolo-generic`) and DAMO-YOLO (`model_type: damo-yolo`). Pick a model, download the `.dxnn`, bind-mount it into the container, and point the model's `path` at it. Note which one you picked, since it determines the `model_type` value below. `model_type` has to be set: Frigate defaults it to `ssd`, which no DEEPX decoder reads, so a model that leaves it unset is rejected at startup.
|
||||
- `damo-yolo` for DAMO-YOLO models, the recommended choice. All four sizes (TinyNAS-L20T, S, M, L) are supported.
|
||||
- `yolo-generic` for YOLO object detection models. The detector reads the model's output layout from the compiled file, so anchor-based, anchor-free and NMS-in-head models all work with the same configuration, as do anchor-free models compiled with DEEPX's Post-Processing Unit (PPU) support. PPU models compiled from an anchor-based head are not supported; compile those without PPU support.
|
||||
|
||||
```yaml
|
||||
models:
|
||||
- devices:
|
||||
- deepx:PCIe:0
|
||||
path: /config/model_cache/deepx/model.dxnn
|
||||
labelmap_path: /labelmap/coco-80.txt
|
||||
model_type: yolo-generic
|
||||
width: 640
|
||||
height: 640
|
||||
```
|
||||
|
||||
A DEEPX device is `PCIe:<index>`, as reported on the detector settings page. The NPU daemon multiplexes across processes, so the same device may be listed more than once to run additional inference processes against it:
|
||||
|
||||
```yaml
|
||||
models:
|
||||
- devices:
|
||||
- deepx:PCIe:0
|
||||
- deepx:PCIe:1
|
||||
```
|
||||
|
||||
Alternatively, compile your own model with the DX-COM compiler.
|
||||
|
||||
`width` and `height` must match the resolution the model was compiled for. Quantization parameters are baked into the `.dxnn` file at compile time, so no normalization is applied on the host and Frigate's default `input_tensor`, `input_pixel_format`, and `input_dtype` values do not need to be overridden.
|
||||
|
||||
#### Label maps
|
||||
|
||||
Every supported model from the DEEPX ModelZoo is trained on the standard 80-class COCO label set, so `labelmap_path` must be set to `/labelmap/coco-80.txt`. Frigate's default label map uses an extended 91-class COCO scheme, and leaving it in place will cause detections to be reported as the wrong object type.
|
||||
|
||||
#### Decoder options {#deepx-decoder-options}
|
||||
|
||||
`model_format`, `ppu`, and `anchors` belong to the DEEPX detector rather than to a model. A `models` entry carries no per-detector options, so there is currently no way to set them and every DEEPX detector runs with their defaults, `model_format: auto` and `ppu: false`. In practice a `.dxnn` file therefore has to be one the `auto` inference can read: an anchor-free `yolo-generic` head, or DAMO-YOLO, which takes no layout at all. The sections below describe what each option selects, for when the config grows a place to set them.
|
||||
|
||||
#### YOLO model formats {#deepx-yolo-model-formats}
|
||||
|
||||
For `model_type: yolo-generic`, DX-COM preserves the detection head of the model it compiles, so the head's tensor layout determines how the output has to be read. `model_format` names the layout the `.dxnn` file was compiled with, across three decode families:
|
||||
|
||||
| `model_format` | Output layout | PPU support |
|
||||
| -------------- | -------------- | ----------- |
|
||||
| `anchor` | Anchor-based, with a separate objectness score | Yes |
|
||||
| `anchor_free` | Anchor-free, no objectness, boxes already in pixels | Yes |
|
||||
| `nms_in_head` | NMS runs in the detection head, output is final corner boxes | No |
|
||||
|
||||
The `auto` default infers the layout from the output shape. That inference cannot distinguish an anchor-based head from an anchor-free one, so it is only reliable for anchor-free heads.
|
||||
|
||||
Two mistakes an explicit layout prevents:
|
||||
|
||||
- Decoding an anchor-free model with the anchor formula squares the box dimensions, producing boxes hundreds of thousands of pixels wide that clip to the entire frame.
|
||||
- Decoding an anchor-based model as anchor-free reads the objectness column as a class score, so confidences and class IDs are both wrong.
|
||||
|
||||
`nms_in_head` models run NMS inside the head, so the detector's `nms_threshold` does not apply to them.
|
||||
|
||||
#### DAMO-YOLO models
|
||||
|
||||
For `model_type: damo-yolo` no layout is needed. All four sizes (TinyNAS-L20T, S, M, L) share one detection head, differing only in backbone depth/width, and the decoder reads that one layout directly:
|
||||
The quickest way to get one is the [DEEPX ModelZoo](https://developer.deepx.ai/modelzoo), which publishes pre-compiled `.dxnn` files for a range of YOLO and DAMO-YOLO object detection models. Download the `.dxnn`, bind-mount it into the container, and point the model's `path` at it. Alternatively, compile your own model with the DX-COM compiler.
|
||||
|
||||
```yaml
|
||||
models:
|
||||
@ -725,21 +673,22 @@ models:
|
||||
height: 640
|
||||
```
|
||||
|
||||
#### PPU models
|
||||
`model_type` must be set to `damo-yolo` or `yolo-generic` to match the model. Frigate defaults it to `ssd`, which this detector does not support, so a model that leaves it unset is rejected at startup.
|
||||
|
||||
Models compiled with Post-Processing Unit (PPU) support perform candidate selection on the NPU itself and emit a list of surviving detections rather than raw feature maps. This leaves only the box decode and NMS for the host, which lowers CPU usage. PPU support is only available for `model_type: yolo-generic` — there is no confirmed PPU record layout for DAMO-YOLO yet, and combining the two is rejected at startup.
|
||||
`width` and `height` must match the resolution the model was compiled for. Quantization parameters are baked into the `.dxnn` file at compile time, so no normalization is applied on the host and Frigate's default `input_tensor`, `input_pixel_format`, and `input_dtype` values do not need to be overridden.
|
||||
|
||||
`ppu` and `model_format` are independent: `ppu` says whether candidate selection already happened on the NPU, and `model_format` says how to read the boxes that come back. Both must match how the model was compiled.
|
||||
A DEEPX device is `PCIe:<index>`, as reported on the detector settings page. The NPU daemon multiplexes across processes, so the same device may be listed more than once to run additional inference processes against it:
|
||||
|
||||
`model_format` is required whenever `ppu` is set. A PPU record has the same fixed-width layout for every yolo-generic variant, so unlike the raw output path there is no shape to infer the layout from — leaving it at `auto` is rejected at startup rather than risking a silent anchor-free/anchor-based mismatch. Since neither option can currently be set, PPU-compiled models cannot be used.
|
||||
```yaml
|
||||
models:
|
||||
- devices:
|
||||
- deepx:PCIe:0
|
||||
- deepx:PCIe:0
|
||||
```
|
||||
|
||||
DX-COM cannot compile `nms_in_head` models with PPU support, since their heads already produce final detections. Combining the two is rejected at startup.
|
||||
#### Label maps
|
||||
|
||||
##### Anchors {#deepx-ppu-anchors}
|
||||
|
||||
`model_format: anchor` with `ppu` also needs `anchors`. A PPU record names its anchor by index and says nothing about the table behind that index, and anchor tables differ between models that share this output layout, so there is nothing safe to default to — the wrong table rescales every box by the wrong amount, and the boxes still look plausible enough that nothing reports an error. It is required rather than defaulted for the same reason `model_format` is.
|
||||
|
||||
The table is copied out of the config the model was trained with. It is one group of `width,height` pairs per feature-map layer, groups separated by `;`, in stride order 8, 16, 32, for example `10,13,16,30,33,23;30,61,62,45,59,119;116,90,156,198,373,326`. The nested list form a model config usually declares them in is accepted too. Every group must hold the same number of pairs, since a record's anchor index is a single number that has to mean the same thing on every layer. `anchors` is rejected on any other combination, because no other decode path reads it: `anchor_free` and `nms_in_head` models carry their box geometry in the output, and the non-PPU `anchor` path receives boxes already in pixels.
|
||||
The object detection models in the DEEPX ModelZoo are trained on the standard 80-class COCO label set, so `labelmap_path` must be set to `/labelmap/coco-80.txt`. Frigate's default label map uses an extended 91-class COCO scheme, and leaving it in place will cause detections to be reported as the wrong object type. For `yolo-generic` models the label map is also what the detector uses to tell the output layout, so a label map with the wrong number of classes is reported as an error at startup.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -267,7 +267,7 @@ Frigate supports the DEEPX NPU in both of its form factors: the **DX-M1** M.2 mo
|
||||
|
||||
DEEPX NPU support in Frigate is developed and maintained by [Sixfab](https://sixfab.com).
|
||||
|
||||
The NPU runs models compiled to the `.dxnn` format with DEEPX's DX-COM compiler. Pre-compiled YOLO and DAMO-YOLO models can be downloaded from the [DEEPX ModelZoo](https://developer.deepx.ai/modelzoo), and YOLO models compiled with Post-Processing Unit (PPU) support move candidate selection onto the NPU, which reduces host CPU usage.
|
||||
The NPU runs models compiled to the `.dxnn` format with DEEPX's DX-COM compiler. Pre-compiled YOLO and DAMO-YOLO models can be downloaded from the [DEEPX ModelZoo](https://developer.deepx.ai/modelzoo). Anchor-free YOLO models compiled with Post-Processing Unit (PPU) support move candidate selection onto the NPU, which reduces host CPU usage; PPU models compiled from an anchor-based head are not supported.
|
||||
|
||||
The DEEPX kernel driver, the DX-RT runtime, and the `dxrtd` daemon all run on the Docker host rather than inside the Frigate container, and have to be installed there before the NPU can be used. Frigate connects to the daemon over its socket, so the NPU stays available to other programs on the host at the same time. See the [installation docs](installation.md#deepx-npu) for the setup steps.
|
||||
|
||||
|
||||
@ -426,10 +426,11 @@ Confirm the NPU is visible before continuing:
|
||||
ls /dev/dxrt*
|
||||
```
|
||||
|
||||
Then confirm the daemon is running:
|
||||
Then confirm the daemon is running and listening in `/run/dxrt`:
|
||||
|
||||
```bash
|
||||
systemctl is-active dxrt.service
|
||||
ls /run/dxrt/
|
||||
```
|
||||
|
||||
#### Setup
|
||||
@ -438,7 +439,7 @@ To set up Frigate, follow the default installation instructions, for example: `g
|
||||
|
||||
#### Docker configuration
|
||||
|
||||
Frigate needs the NPU device node and the daemon's socket:
|
||||
Frigate needs the NPU device node and the directory holding the daemon's socket:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
@ -446,24 +447,26 @@ services:
|
||||
devices:
|
||||
- /dev/dxrt0:/dev/dxrt0
|
||||
volumes:
|
||||
- /tmp/dxrt_dynamic_ipc.sock:/tmp/dxrt_dynamic_ipc.sock
|
||||
- /run/dxrt:/run/dxrt
|
||||
```
|
||||
|
||||
If you can't use Docker Compose, add `--device /dev/dxrt0:/dev/dxrt0 -v /tmp/dxrt_dynamic_ipc.sock:/tmp/dxrt_dynamic_ipc.sock` to your `docker run` command.
|
||||
If you can't use Docker Compose, add `--device /dev/dxrt0:/dev/dxrt0 -v /run/dxrt:/run/dxrt` to your `docker run` command.
|
||||
|
||||
Add one `--device` per NPU, contiguously from `/dev/dxrt0`, since the client stops enumerating at the first gap.
|
||||
|
||||
The installation script configures `dxrt.service` to place its socket in `/run/dxrt` through a systemd drop-in. Mounting the directory rather than the socket file means the container sees the new socket after `dxrt.service` is restarted, rather than holding on to a deleted one.
|
||||
|
||||
`dxrtd` listens on an abstract socket as well, but that one does not cross into a container, so Frigate names the filesystem socket through `DXRT_DYNAMIC_IPC_ENDPOINT` on your behalf. Set that variable on the container yourself only if the daemon listens somewhere else, which means you also set it for `dxrtd` through its own systemd drop-in. The script writes `/etc/systemd/system/dxrt.service.d/frigate.conf` and `/etc/profile.d/dxrt.sh` for exactly that, the second so the host's own `dxrt-cli` and `dxtop` look in the same place.
|
||||
|
||||
:::note
|
||||
|
||||
Restart `dxrt.service` before starting the container, not after. A bind-mounted socket pins the inode it saw at container start, so recreating the socket afterwards leaves the container holding a deleted one.
|
||||
The DX-RT client exits when `dxrt.service` stops, so restart the Frigate container after restarting `dxrt.service`.
|
||||
|
||||
:::
|
||||
|
||||
The device node is needed as well as the socket, because the client opens the NPU directly even though the daemon arbitrates access. Without it, inference fails with `Device not found`.
|
||||
|
||||
`/dev/shm` does not need sharing. The daemon publishes a shared memory segment for device monitoring, which `dxtop` and device status read, but inference does not.
|
||||
|
||||
`dxrtd` listens on both an abstract socket and `/tmp/dxrt_dynamic_ipc.sock`. Only the second crosses into a container, so Frigate names it through `DXRT_DYNAMIC_IPC_ENDPOINT` on your behalf. Set that variable yourself only if you moved the socket, in which case set it for the daemon too, through a systemd drop-in.
|
||||
`/dev/shm` does not need sharing.
|
||||
|
||||
The DX-RT python bindings are not shipped in the Frigate image. Frigate downloads them on first start when a DEEPX detector is configured, and caches them under `/config`.
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -81,21 +81,9 @@ class TestYoloNmsPostProcess(unittest.TestCase):
|
||||
self.assertEqual(len(detections), 1)
|
||||
np.testing.assert_allclose(detections[0], [0, 0.90, *A_ROW], atol=2e-3)
|
||||
|
||||
def test_honors_caller_supplied_thresholds(self):
|
||||
"""A detector with its own thresholds must not be held to the 0.4
|
||||
default, which would drop this 0.3 confidence box."""
|
||||
output = self._single_output([[A_CX, A_CY, A_W, A_H, 0.30, 0.0]])
|
||||
|
||||
self.assertEqual(len(kept(post_process_yolo(output, WIDTH, HEIGHT))), 0)
|
||||
|
||||
detections = kept(post_process_yolo(output, WIDTH, HEIGHT, 0.25, 0.45))
|
||||
|
||||
self.assertEqual(len(detections), 1)
|
||||
np.testing.assert_allclose(detections[0], [0, 0.30, *A_ROW], atol=2e-3)
|
||||
|
||||
|
||||
class TestMultipartYoloPostProcess(unittest.TestCase):
|
||||
def _multipart_output(self, a_conf=0.95, b_conf=0.90) -> list[np.ndarray]:
|
||||
def _multipart_output(self) -> list[np.ndarray]:
|
||||
"""Build a 3-scale anchor-based YOLO output containing boxes A and B,
|
||||
both decoded through anchor 0 of the stride-32 scale."""
|
||||
outputs = [
|
||||
@ -106,8 +94,8 @@ class TestMultipartYoloPostProcess(unittest.TestCase):
|
||||
stride, (anchor_w, anchor_h) = 32, (142, 110)
|
||||
|
||||
for cx, cy, w, h, conf, class_channel in [
|
||||
(A_CX, A_CY, A_W, A_H, a_conf, 5), # class 0
|
||||
(B_CX, B_CY, B_W, B_H, b_conf, 6), # class 1
|
||||
(A_CX, A_CY, A_W, A_H, 0.95, 5), # class 0
|
||||
(B_CX, B_CY, B_W, B_H, 0.90, 6), # class 1
|
||||
]:
|
||||
cell_x, cell_y = int(cx // stride), int(cy // stride)
|
||||
dx = (cx / stride - cell_x + 0.5) / 2
|
||||
@ -139,19 +127,6 @@ class TestMultipartYoloPostProcess(unittest.TestCase):
|
||||
|
||||
self.assertEqual(len(detections), 0)
|
||||
|
||||
def test_honors_caller_supplied_thresholds(self):
|
||||
"""Same as the single-tensor case: the multi-part branch must filter
|
||||
at the caller's threshold, not the 0.4 default."""
|
||||
outputs = self._multipart_output(a_conf=0.35, b_conf=0.30)
|
||||
|
||||
self.assertEqual(len(kept(post_process_yolo(outputs, WIDTH, HEIGHT))), 0)
|
||||
|
||||
detections = kept(post_process_yolo(outputs, WIDTH, HEIGHT, 0.25, 0.45))
|
||||
|
||||
self.assertEqual(len(detections), 2)
|
||||
np.testing.assert_allclose(detections[0], [0, 0.35, *A_ROW], atol=2e-3)
|
||||
np.testing.assert_allclose(detections[1], [1, 0.30, *B_ROW], atol=2e-3)
|
||||
|
||||
|
||||
class TestYoloxPostProcess(unittest.TestCase):
|
||||
def test_keeps_separated_objects_far_from_origin(self):
|
||||
|
||||
@ -134,8 +134,6 @@ def __post_process_multipart_yolo(
|
||||
output_list,
|
||||
width,
|
||||
height,
|
||||
score_threshold=0.4,
|
||||
nms_threshold=0.4,
|
||||
):
|
||||
anchors = [
|
||||
[(12, 16), (19, 36), (40, 28)],
|
||||
@ -168,7 +166,7 @@ def __post_process_multipart_yolo(
|
||||
class_conf = class_probs[class_id]
|
||||
conf = class_conf * pred[4]
|
||||
|
||||
if conf < score_threshold:
|
||||
if conf < 0.4:
|
||||
continue
|
||||
|
||||
dx = pred[0]
|
||||
@ -193,8 +191,8 @@ def __post_process_multipart_yolo(
|
||||
indices = cv2.dnn.NMSBoxes(
|
||||
bboxes=xyxy_to_xywh_for_nms(all_boxes),
|
||||
scores=all_scores,
|
||||
score_threshold=score_threshold,
|
||||
nms_threshold=nms_threshold,
|
||||
score_threshold=0.4,
|
||||
nms_threshold=0.4,
|
||||
)
|
||||
|
||||
results = np.zeros((20, 6), np.float32)
|
||||
@ -216,13 +214,7 @@ def __post_process_multipart_yolo(
|
||||
return results
|
||||
|
||||
|
||||
def __post_process_nms_yolo(
|
||||
predictions: np.ndarray,
|
||||
width,
|
||||
height,
|
||||
score_threshold=0.4,
|
||||
nms_threshold=0.4,
|
||||
) -> np.ndarray:
|
||||
def __post_process_nms_yolo(predictions: np.ndarray, width, height) -> np.ndarray:
|
||||
predictions = np.squeeze(predictions)
|
||||
|
||||
# transpose the output so it has order (inferences, class_ids)
|
||||
@ -230,8 +222,8 @@ def __post_process_nms_yolo(
|
||||
predictions = predictions.T
|
||||
|
||||
scores = np.max(predictions[:, 4:], axis=1)
|
||||
predictions = predictions[scores > score_threshold, :]
|
||||
scores = scores[scores > score_threshold]
|
||||
predictions = predictions[scores > 0.4, :]
|
||||
scores = scores[scores > 0.4]
|
||||
class_ids = np.argmax(predictions[:, 4:], axis=1)
|
||||
|
||||
# Rescale box
|
||||
@ -245,10 +237,7 @@ def __post_process_nms_yolo(
|
||||
|
||||
# run NMS
|
||||
indices = cv2.dnn.NMSBoxes(
|
||||
xyxy_to_xywh_for_nms(boxes),
|
||||
scores,
|
||||
score_threshold=score_threshold,
|
||||
nms_threshold=nms_threshold,
|
||||
xyxy_to_xywh_for_nms(boxes), scores, score_threshold=0.4, nms_threshold=0.4
|
||||
)
|
||||
detections = np.zeros((20, 6), np.float32)
|
||||
for i, (bbox, confidence, class_id) in enumerate(
|
||||
@ -269,21 +258,11 @@ def __post_process_nms_yolo(
|
||||
return detections
|
||||
|
||||
|
||||
def post_process_yolo(
|
||||
output: list[np.ndarray],
|
||||
width: int,
|
||||
height: int,
|
||||
score_threshold: float = 0.4,
|
||||
nms_threshold: float = 0.4,
|
||||
) -> np.ndarray:
|
||||
def post_process_yolo(output: list[np.ndarray], width: int, height: int) -> np.ndarray:
|
||||
if len(output) > 1:
|
||||
return __post_process_multipart_yolo(
|
||||
output, width, height, score_threshold, nms_threshold
|
||||
)
|
||||
return __post_process_multipart_yolo(output, width, height)
|
||||
else:
|
||||
return __post_process_nms_yolo(
|
||||
output[0], width, height, score_threshold, nms_threshold
|
||||
)
|
||||
return __post_process_nms_yolo(output[0], width, height)
|
||||
|
||||
|
||||
def post_process_yolox(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user