This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

The following commit(s) were added to refs/heads/master by this push:
     new 1ae4048218 avfilter/dnn: extend ONNX Runtime backend with dnn_detect 
and multi-output support
1ae4048218 is described below

commit 1ae40482188283a7f955d001abb08c5d865cdae0
Author:     younengxiao <[email protected]>
AuthorDate: Fri Jul 3 22:50:48 2026 -0400
Commit:     guoyejun <[email protected]>
CommitDate: Sat Aug 29 10:18:28 2026 +0000

    avfilter/dnn: extend ONNX Runtime backend with dnn_detect and multi-output 
support
    
    This patch extends the existing ONNX Runtime DNN backend with object
    detection support via dnn_detect and removes the single-output restriction
    in dnn_processing.
    
    Example usage:
      # SSD detection (single output, DirectML)
      ffmpeg -i input.mp4 -vf 
"scale=300:300,format=rgb24,dnn_detect=dnn_backend=onnx:model=ssd.onnx:model_type=ssd:device=dml:device_id=0"
 -f null -
    
      # YOLOv3 detection (two outputs, CPU)
      ffmpeg -i input.mp4 -vf 
"scale=416:416,format=rgb24,dnn_detect=dnn_backend=onnx:model=yolov3.onnx:output=yolo_13&yolo_26:model_type=yolov3:nb_classes=80:anchors=116&90&156&198&373&326&30&61&62&45&59&119"
 -f null -
    
    Signed-off-by: younengxiao <[email protected]>
---
 doc/filters.texi                   |  58 ++++++-
 libavfilter/dnn/dnn_backend_onnx.c | 322 +++++++++++++++++++++++++------------
 libavfilter/dnn/dnn_io_proc.c      | 155 +++++++++++++++---
 libavfilter/dnn_filter_common.c    |   8 +-
 libavfilter/vf_dnn_detect.c        | 272 +++++++++++++++++++++++++------
 5 files changed, 631 insertions(+), 184 deletions(-)

diff --git a/doc/filters.texi b/doc/filters.texi
index 488ecdf9c5..9f2c41bf44 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -12176,7 +12176,15 @@ The filter accepts the following options:
 @table @option
 @item dnn_backend
 Specify which DNN backend to use for model loading and execution. This option 
accepts
-only openvino now, tensorflow backends will be added.
+the following values:
+@table @samp
+@item tensorflow
+TensorFlow backend.
+@item openvino
+OpenVINO backend.
+@item onnx
+ONNX Runtime backend.
+@end table
 
 @item model
 Set path to model file specifying network architecture and its parameters.
@@ -12186,7 +12194,9 @@ Note that different backends use different file formats.
 Set the input name of the dnn network.
 
 @item output
-Set the output name of the dnn network.
+Set the output name of the dnn network. The TensorFlow and ONNX Runtime
+backends accept multiple output names separated by @samp{&}, e.g.
+@option{output=yolo_13&yolo_26}.
 
 @item confidence
 Set the confidence threshold (default: 0.5).
@@ -12198,12 +12208,51 @@ The first line is the name of label id 0 (usually it 
is 'background'),
 and the second line is the name of label id 1, etc.
 The label id is considered as name if the label file is not provided.
 
+@item model_type
+Set the detection model output format. The following values are accepted:
+
+@table @samp
+@item ssd
+Single-stage detector (default).
+@item yolo
+YOLO v1/v2.
+@item yolov3
+YOLOv3.
+@item yolov4
+YOLOv4.
+@end table
+@item anchors
+Anchor box dimensions, separated by @samp{&}. Required for YOLO-family models.
+The list is consumed in output order, so the anchors of the first output come
+first: with @option{output=yolo_13&yolo_26} the leading pairs belong to
+@code{yolo_13}.
+@item nb_classes
+Number of detection classes. Required for YOLO-family models.
 @item backend_configs
 Set the configs to be passed into backend. To use async execution, set async 
(default: set).
 Roll back to sync execution if the backend does not support async.
 
 @end table
 
+@subsection Examples
+
+@itemize
+@item
+Run SSD object detection with an ONNX model (single output, DirectML on 
Windows):
+@example
+ffmpeg -i input.mp4 -vf "scale=300:300,format=rgb24,dnn_detect=dnn_backend=onnx
+       :model=ssd.onnx:model_type=ssd:confidence=0.5:device=dml" -f null -
+@end example
+
+@item
+Run YOLOv3 object detection with an ONNX model (two outputs, CPU):
+@example
+ffmpeg -i input.mp4 -vf "scale=416:416,format=rgb24,dnn_detect=dnn_backend=onnx
+       
:model=yolov3.onnx:output=yolo_13&yolo_26:model_type=yolov3:nb_classes=80
+       :anchors=116&90&156&198&373&326&30&61&62&45&59&119" -f null -
+@end example
+@end itemize
+
 @anchor{dnn_processing}
 @section dnn_processing
 
@@ -12265,7 +12314,10 @@ exactly one input tensor when running the model.
 
 The @option{input} and @option{output} options are optional for the
 ONNX Runtime backend; when they are omitted the backend resolves the
-tensor names from the session.
+tensor names from the session.  Multiple output names may be supplied
+separated by @samp{&} (e.g. @option{output=out_a&out_b}); however for
+@code{dnn_processing} only the first output tensor is used for frame
+post-processing.
 
 The ONNX Runtime backend runs inference synchronously using a single
 inference request. The shared @option{async} and @option{nireq} options
diff --git a/libavfilter/dnn/dnn_backend_onnx.c 
b/libavfilter/dnn/dnn_backend_onnx.c
index 6c75d6eb24..16b95cc55c 100644
--- a/libavfilter/dnn/dnn_backend_onnx.c
+++ b/libavfilter/dnn/dnn_backend_onnx.c
@@ -25,6 +25,7 @@
 
 #include "libavutil/opt.h"
 #include "libavutil/avassert.h"
+#include "libavutil/imgutils.h"
 #include "libavutil/mem.h"
 #include "libavutil/avstring.h"
 #include "libavutil/thread.h"
@@ -55,9 +56,10 @@ typedef struct ONNXModel {
 } ONNXModel;
 
 typedef struct ONNXInferRequest {
-    OrtValue *input_tensor;
-    OrtValue *output_tensor;
-    void     *input_data;
+    OrtValue  *input_tensor;
+    OrtValue **output_tensors;
+    uint32_t   nb_outputs;
+    void      *input_data;
 } ONNXInferRequest;
 
 typedef struct ONNXRequestItem {
@@ -125,10 +127,16 @@ static void onnx_free_request(ONNXInferRequest *request)
         request->input_tensor = NULL;
     }
     av_freep(&request->input_data);
-    if (request->output_tensor) {
-        g_ort->ReleaseValue(request->output_tensor);
-        request->output_tensor = NULL;
+    if (request->output_tensors) {
+        for (uint32_t i = 0; i < request->nb_outputs; i++) {
+            if (request->output_tensors[i]) {
+                g_ort->ReleaseValue(request->output_tensors[i]);
+                request->output_tensors[i] = NULL;
+            }
+        }
+        av_freep(&request->output_tensors);
     }
+    request->nb_outputs = 0;
 }
 
 static inline void destroy_request_item(ONNXRequestItem **arg)
@@ -286,6 +294,17 @@ static int get_input_onnx(DNNModel *model, DNNData *input, 
const char *input_nam
         return AVERROR(ENOSYS);
     }
 
+    for (size_t i = 1; i < num_dims; i++) {
+        if (dims[i] > INT_MAX) {
+            av_log(ctx, AV_LOG_ERROR,
+                   "ONNX model input dimension %zu (%"PRId64") is too large to 
be represented\n",
+                   i, dims[i]);
+            av_free(dims);
+            g_ort->ReleaseTypeInfo(type_info);
+            return AVERROR(ENOSYS);
+        }
+    }
+
     /*
      * The ONNX backend assumes a 4-D NCHW input tensor (the rank check
      * above already rejects anything else).
@@ -351,16 +370,44 @@ static int fill_model_input_onnx(ONNXModel *onnx_model, 
ONNXRequestItem *request
     height_idx  = dnn_get_height_idx_by_layout(input.layout);
     channel_idx = dnn_get_channel_idx_by_layout(input.layout);
 
-    input.dims[height_idx] = task->in_frame->height;
-    input.dims[width_idx]  = task->in_frame->width;
+    if (input.dims[height_idx] < 0)
+        input.dims[height_idx] = task->in_frame->height;
+    if (input.dims[width_idx] < 0)
+        input.dims[width_idx] = task->in_frame->width;
+
+    if (input.dims[0] <= 0 || input.dims[channel_idx] <= 0 ||
+        input.dims[height_idx] <= 0 || input.dims[width_idx] <= 0) {
+        av_log(ctx, AV_LOG_ERROR, "ONNX input tensor has a non-positive 
dimension\n");
+        ret = AVERROR(EINVAL);
+        goto err;
+    }
+
+    ret = av_image_check_size((unsigned)input.dims[width_idx],
+                              (unsigned)input.dims[height_idx], 0, ctx);
+    if (ret < 0) {
+        av_log(ctx, AV_LOG_ERROR, "ONNX input image dimensions %dx%d are not 
supported\n",
+               input.dims[width_idx], input.dims[height_idx]);
+        goto err;
+    }
 
     input_shape[0] = input.dims[0];
     input_shape[1] = input.dims[channel_idx];
     input_shape[2] = input.dims[height_idx];
     input_shape[3] = input.dims[width_idx];
 
-    input_tensor_size = input_shape[0] * input_shape[1] * input_shape[2] * 
input_shape[3];
-    input_tensor_size *= sizeof(float);
+    /*
+     * Build the byte count with checked size_t multiplications instead of
+     * multiplying four int64_t shape values in one expression.
+     */
+    input_tensor_size = sizeof(float);
+    if (av_size_mult(input_tensor_size, (size_t)input_shape[0], 
&input_tensor_size) < 0 ||
+        av_size_mult(input_tensor_size, (size_t)input_shape[1], 
&input_tensor_size) < 0 ||
+        av_size_mult(input_tensor_size, (size_t)input_shape[2], 
&input_tensor_size) < 0 ||
+        av_size_mult(input_tensor_size, (size_t)input_shape[3], 
&input_tensor_size) < 0) {
+        av_log(ctx, AV_LOG_ERROR, "ONNX input tensor size overflows\n");
+        ret = AVERROR(EINVAL);
+        goto err;
+    }
 
     input.data = av_malloc(input_tensor_size);
     if (!input.data) {
@@ -374,14 +421,19 @@ static int fill_model_input_onnx(ONNXModel *onnx_model, 
ONNXRequestItem *request
         input.scale = 255;
         if (task->do_ioproc) {
             if (onnx_model->model.frame_pre_proc != NULL) {
-                onnx_model->model.frame_pre_proc(task->in_frame, &input, 
onnx_model->model.filter_ctx);
+                ret = onnx_model->model.frame_pre_proc(task->in_frame, &input,
+                                                       
onnx_model->model.filter_ctx);
             } else {
-                ff_proc_from_frame_to_dnn(task->in_frame, &input, ctx);
+                ret = ff_proc_from_frame_to_dnn(task->in_frame, &input, ctx);
             }
+            if (ret < 0)
+                goto err;
         }
         break;
     case DFT_ANALYTICS_DETECT:
-        ff_frame_to_dnn_detect(task->in_frame, &input, ctx);
+        ret = ff_frame_to_dnn_detect(task->in_frame, &input, ctx);
+        if (ret < 0)
+            goto err;
         break;
     default:
         avpriv_report_missing_feature(ctx, "model function type %d", 
onnx_model->model.func_type);
@@ -426,8 +478,8 @@ static int onnx_start_inference(void *args)
     ONNXModel           *onnx_model = NULL;
     DnnContext                 *ctx = NULL;
     OrtStatus *status;
-    const char  *input_names[1];
-    const char *output_names[1];
+    const char *input_names[1];
+    int ret = DNN_GENERIC_ERROR;
 
     if (!request) {
         av_log(NULL, AV_LOG_ERROR, "ONNXRequestItem is NULL\n");
@@ -440,12 +492,6 @@ static int onnx_start_inference(void *args)
     onnx_model = (ONNXModel *)task->model;
     ctx = onnx_model->ctx;
 
-    if (task->nb_output > 1) {
-        avpriv_report_missing_feature(ctx,
-            "Multiple output tensors (%u) for ONNX backend", task->nb_output);
-        return AVERROR(ENOSYS);
-    }
-
     if (!task->input_name || !task->output_names || !task->output_names[0]) {
         av_log(ctx, AV_LOG_ERROR,
                "ONNX backend: input/output tensor name was not resolved at 
load time\n");
@@ -469,46 +515,63 @@ static int onnx_start_inference(void *args)
             return AVERROR(EINVAL);
         }
 
-        for (size_t i = 0; i < output_count; i++) {
-            char *name = NULL;
-            status = g_ort->SessionGetOutputName(onnx_model->session, i,
-                                                 onnx_model->allocator, &name);
-            if (status != NULL) {
-                g_ort->ReleaseStatus(status);
-                continue;
+        for (uint32_t req = 0; req < task->nb_output; req++) {
+            found_output = 0;
+            for (size_t i = 0; i < output_count; i++) {
+                char *name = NULL;
+                status = g_ort->SessionGetOutputName(onnx_model->session, i,
+                                                     onnx_model->allocator, 
&name);
+                if (status != NULL) {
+                    g_ort->ReleaseStatus(status);
+                    continue;
+                }
+                if (!strcmp(name, task->output_names[req]))
+                    found_output = 1;
+                onnx_model->allocator->Free(onnx_model->allocator, name);
+                if (found_output)
+                    break;
+            }
+            if (!found_output) {
+                av_log(ctx, AV_LOG_ERROR,
+                       "Output name '%s' not found in ONNX model\n",
+                       task->output_names[req]);
+                return AVERROR(EINVAL);
             }
-            if (!strcmp(name, task->output_names[0]))
-                found_output = 1;
-            onnx_model->allocator->Free(onnx_model->allocator, name);
-            if (found_output)
-                break;
-        }
-
-        if (!found_output) {
-            av_log(ctx, AV_LOG_ERROR,
-                   "Output name '%s' not found in ONNX model\n",
-                   task->output_names[0]);
-            return AVERROR(EINVAL);
         }
 
         onnx_model->output_resolved = 1;
     }
 
-    input_names[0]  = task->input_name;
-    output_names[0] = task->output_names[0];
+    input_names[0] = task->input_name;
+
+    /* ORT writes task->nb_output result handles into this array; it must be
+     * allocated (and NULL-initialised) before Run() so ORT owns each slot. */
+    av_freep(&infer_request->output_tensors);
+    infer_request->output_tensors = av_calloc(task->nb_output,
+                                              
sizeof(*infer_request->output_tensors));
+    if (!infer_request->output_tensors) {
+        infer_request->nb_outputs = 0;
+        return AVERROR(ENOMEM);
+    }
+    infer_request->nb_outputs = task->nb_output;
 
     status = g_ort->Run(onnx_model->session, NULL,
-                        input_names, (const OrtValue *const 
*)&infer_request->input_tensor, 1,
-                        output_names, 1, &infer_request->output_tensor);
+        input_names, (const OrtValue *const *)&infer_request->input_tensor, 1,
+        task->output_names, task->nb_output, infer_request->output_tensors);
 
     if (status != NULL) {
         const char *msg = g_ort->GetErrorMessage(status);
         av_log(ctx, AV_LOG_ERROR, "ONNX inference failed: %s\n", msg);
         g_ort->ReleaseStatus(status);
-        return DNN_GENERIC_ERROR;
+        goto err;
     }
 
     return 0;
+
+err:
+    av_freep(&infer_request->output_tensors);
+    infer_request->nb_outputs = 0;
+    return ret;
 }
 
 static void infer_completion_callback(void *args)
@@ -516,7 +579,7 @@ static void infer_completion_callback(void *args)
     ONNXRequestItem  *request = (ONNXRequestItem *)args;
     LastLevelTaskItem *lltask = request->lltask;
     TaskItem            *task = lltask->task;
-    DNNData           outputs = { 0 };
+    DNNData          *outputs = NULL;
     ONNXInferRequest *infer_request = request->infer_request;
     ONNXModel           *onnx_model = (ONNXModel *)task->model;
     DnnContext                 *ctx = onnx_model->ctx;
@@ -524,93 +587,143 @@ static void infer_completion_callback(void *args)
     ONNXTensorElementDataType tensor_type;
     size_t num_dims;
     int64_t *dims;
-    void *output_data;
     OrtStatus *status;
+    int ret;
 
-    if (!infer_request->output_tensor) {
-        av_log(ctx, AV_LOG_ERROR, "Output tensor is NULL\n");
+    outputs = av_calloc(infer_request->nb_outputs, sizeof(*outputs));
+    if (!outputs) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to allocate output DNNData array\n");
         goto err;
     }
 
-    status = g_ort->GetTensorTypeAndShape(infer_request->output_tensor, 
&tensor_info);
-    if (status != NULL) {
-        av_log(ctx, AV_LOG_ERROR, "Failed to get output tensor info\n");
-        g_ort->ReleaseStatus(status);
-        goto err;
-    }
+    for (uint32_t i = 0; i < infer_request->nb_outputs; i++) {
+        status = g_ort->GetTensorTypeAndShape(infer_request->output_tensors[i],
+                                              &tensor_info);
+        if (status != NULL) {
+            av_log(ctx, AV_LOG_ERROR, "Failed to get output tensor[%u] 
type/shape\n", i);
+            g_ort->ReleaseStatus(status);
+            goto err;
+        }
 
-    g_ort->GetDimensionsCount(tensor_info, &num_dims);
-    dims = av_malloc(num_dims * sizeof(int64_t));
-    if (!dims) {
-        av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for 
dimensions\n");
-        g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
-        goto err;
-    }
-    g_ort->GetDimensions(tensor_info, dims, num_dims);
+        status = g_ort->GetDimensionsCount(tensor_info, &num_dims);
+        if (status != NULL) {
+            av_log(ctx, AV_LOG_ERROR, "Failed to get output tensor[%u] 
dimension count\n", i);
+            g_ort->ReleaseStatus(status);
+            g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
+            goto err;
+        }
 
-    /* Output is interpreted as NCHW, matching the input assumption. */
-    outputs.layout = DL_NCHW;
-    outputs.order = DCO_RGB;
+        dims = av_malloc(num_dims * sizeof(int64_t));
+        if (!dims) {
+            av_log(ctx, AV_LOG_ERROR, "Failed to allocate dims array\n");
+            g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
+            goto err;
+        }
 
-    g_ort->GetTensorElementType(tensor_info, &tensor_type);
-    if (tensor_type == ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT) {
-        outputs.dt = DNN_FLOAT;
-    } else {
-        av_log(ctx, AV_LOG_ERROR, "Unsupported output tensor data type, only 
float is supported\n");
-        av_free(dims);
-        g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
-        goto err;
-    }
+        status = g_ort->GetDimensions(tensor_info, dims, num_dims);
+        if (status != NULL) {
+            av_log(ctx, AV_LOG_ERROR, "Failed to get output tensor[%u] 
dimensions\n", i);
+            g_ort->ReleaseStatus(status);
+            av_free(dims);
+            g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
+            goto err;
+        }
 
-    if (num_dims == 4) {
-        outputs.dims[0] = dims[0];
-        outputs.dims[1] = dims[1];
-        outputs.dims[2] = dims[2];
-        outputs.dims[3] = dims[3];
-    } else {
-        avpriv_report_missing_feature(ctx, "Support for %zu dimensional 
output", num_dims);
-        av_free(dims);
-        g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
-        goto err;
-    }
+        for (size_t d = 0; d < num_dims; d++) {
+            if (dims[d] < 0 || dims[d] > INT_MAX) {
+                av_log(ctx, AV_LOG_ERROR,
+                       "Output tensor[%u] dimension %zu (%"PRId64") is out of 
representable range\n",
+                       i, d, dims[d]);
+                av_free(dims);
+                g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
+                goto err;
+            }
+        }
+
+        status = g_ort->GetTensorElementType(tensor_info, &tensor_type);
+        if (status != NULL) {
+            av_log(ctx, AV_LOG_ERROR, "Failed to get output tensor[%u] element 
type\n", i);
+            g_ort->ReleaseStatus(status);
+            av_free(dims);
+            g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
+            goto err;
+        }
+        if (tensor_type == ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT) {
+            outputs[i].dt = DNN_FLOAT;
+        } else {
+            av_log(ctx, AV_LOG_ERROR,
+                   "Unsupported output tensor[%u] data type, only float 
supported\n", i);
+            av_free(dims);
+            g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
+            goto err;
+        }
+
+        /* Output is interpreted as NCHW, matching the input assumption. */
+        outputs[i].layout = DL_NCHW;
+        outputs[i].order  = DCO_RGB;
+
+        if (num_dims == 4) {
+            outputs[i].dims[0] = dims[0];
+            outputs[i].dims[1] = dims[1];
+            outputs[i].dims[2] = dims[2];
+            outputs[i].dims[3] = dims[3];
+        } else if (num_dims == 3) {
+            /* Some detection models output [1, N, D]; promote it to [1, 1, N, 
D]. */
+            outputs[i].dims[0] = dims[0];
+            outputs[i].dims[1] = 1;
+            outputs[i].dims[2] = dims[1];
+            outputs[i].dims[3] = dims[2];
+        } else {
+            avpriv_report_missing_feature(ctx,
+                "Support for %zu-dimensional output (tensor[%u])", num_dims, 
i);
+            av_free(dims);
+            g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
+            goto err;
+        }
+
+        status = g_ort->GetTensorMutableData(infer_request->output_tensors[i], 
&outputs[i].data);
+        if (status != NULL) {
+            av_log(ctx, AV_LOG_ERROR, "Failed to get tensor[%u] data 
pointer\n", i);
+            g_ort->ReleaseStatus(status);
+            av_free(dims);
+            g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
+            goto err;
+        }
 
-    status = g_ort->GetTensorMutableData(infer_request->output_tensor, 
&output_data);
-    if (status != NULL) {
-        av_log(ctx, AV_LOG_ERROR, "Failed to get tensor data\n");
-        g_ort->ReleaseStatus(status);
         av_free(dims);
         g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
-        goto err;
     }
 
-    outputs.data = output_data;
-
     switch (onnx_model->model.func_type) {
     case DFT_PROCESS_FRAME:
         if (task->do_ioproc) {
-            outputs.scale = 255;
+            outputs[0].scale = 255;
             if (onnx_model->model.frame_post_proc != NULL) {
-                onnx_model->model.frame_post_proc(task->out_frame, &outputs, 
onnx_model->model.filter_ctx);
+                onnx_model->model.frame_post_proc(task->out_frame, outputs, 
onnx_model->model.filter_ctx);
             } else {
-                ff_proc_from_dnn_to_frame(task->out_frame, &outputs, ctx);
+                ff_proc_from_dnn_to_frame(task->out_frame, outputs, ctx);
             }
         } else {
-            task->out_frame->width = 
outputs.dims[dnn_get_width_idx_by_layout(outputs.layout)];
-            task->out_frame->height = 
outputs.dims[dnn_get_height_idx_by_layout(outputs.layout)];
+            task->out_frame->width  = 
outputs[0].dims[dnn_get_width_idx_by_layout(outputs[0].layout)];
+            task->out_frame->height = 
outputs[0].dims[dnn_get_height_idx_by_layout(outputs[0].layout)];
         }
         break;
+    case DFT_ANALYTICS_DETECT:
+        ret = onnx_model->model.detect_post_proc(task->in_frame, outputs,
+                                                 infer_request->nb_outputs,
+                                                 onnx_model->model.filter_ctx);
+        if (ret < 0)
+            goto err;
+        break;
     default:
         avpriv_report_missing_feature(ctx, "model function type %d", 
onnx_model->model.func_type);
-        av_free(dims);
-        g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
         goto err;
     }
 
-    av_free(dims);
-    g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
     task->inference_done++;
 
 err:
+    av_freep(&outputs);
     av_freep(&request->lltask);
     onnx_free_request(infer_request);
     if (ff_safe_queue_push_back(onnx_model->request_queue, request) < 0) {
@@ -713,12 +826,9 @@ err:
 
 static ONNXInferRequest *onnx_create_inference_request(void)
 {
-    ONNXInferRequest *request = av_malloc(sizeof(ONNXInferRequest));
+    ONNXInferRequest *request = av_mallocz(sizeof(ONNXInferRequest));
     if (!request)
         return NULL;
-    request->input_tensor  = NULL;
-    request->output_tensor = NULL;
-    request->input_data    = NULL;
     return request;
 }
 
diff --git a/libavfilter/dnn/dnn_io_proc.c b/libavfilter/dnn/dnn_io_proc.c
index 826110dab0..0a18c56b84 100644
--- a/libavfilter/dnn/dnn_io_proc.c
+++ b/libavfilter/dnn/dnn_io_proc.c
@@ -420,12 +420,60 @@ int ff_frame_to_dnn_classify(AVFrame *frame, DNNData 
*input, uint32_t bbox_index
     return ret;
 }
 
+/*
+ * Write packed 24bit RGB/BGR samples into the tensor type/layout the model 
expects.
+ */
+static void detect_write_tensor(DNNData *input, const uint8_t *src,
+                                int src_linesize, int w, int h)
+{
+    const size_t plane_size = (size_t)w * h;
+
+    if (input->dt == DNN_FLOAT) {
+        float *dst = input->data;
+        if (input->layout == DL_NCHW) {
+            /* FLOAT + NCHW: deinterleave into planes while widening uint8 -> 
float. */
+            for (int c = 0; c < 3; c++)
+                for (int y = 0; y < h; y++) {
+                    const uint8_t *row = src + (ptrdiff_t)y * src_linesize;
+                    for (int x = 0; x < w; x++)
+                        dst[c * plane_size + (size_t)y * w + x] = row[x * 3 + 
c];
+                }
+        } else {
+            /* FLOAT + NHWC : keep it packed, only widen uint8 -> float byte 
by byte. */
+            for (int y = 0; y < h; y++) {
+                const uint8_t *row = src + (ptrdiff_t)y * src_linesize;
+                for (int x = 0; x < w * 3; x++)
+                    dst[(size_t)y * w * 3 + x] = row[x];
+            }
+        }
+    } else {
+        /* UINT8 + NCHW: deinterleave into 3 uint8 planes. */
+        uint8_t *dst = input->data;
+        for (int c = 0; c < 3; c++)
+            for (int y = 0; y < h; y++) {
+                const uint8_t *row = src + (ptrdiff_t)y * src_linesize;
+                for (int x = 0; x < w; x++)
+                    dst[c * plane_size + (size_t)y * w + x] = row[x * 3 + c];
+            }
+    }
+}
+
 int ff_frame_to_dnn_detect(AVFrame *frame, DNNData *input, void *log_ctx)
 {
-    struct SwsContext *sws_ctx;
+    struct SwsContext *sws_ctx = NULL;
+    uint8_t *tmp_buf = NULL;
+    const uint8_t *src;
+    int src_linesize;
     int linesizes[4];
-    int ret = 0, width_idx, height_idx;
-    enum AVPixelFormat fmt = get_pixel_format(input);
+    int ret = 0, width_idx, height_idx, channel_idx;
+    int packed_u8, w, h;
+    enum AVPixelFormat fmt;
+
+    switch (input->order) {
+        case DCO_BGR: fmt = AV_PIX_FMT_BGR24; break;
+        case DCO_RGB: fmt = AV_PIX_FMT_RGB24; break;
+        default:      fmt = AV_PIX_FMT_NONE;  break;
+    }
 
     /* (scale != 1 and scale != 0) or mean != 0 */
     if ((fabsf(input->scale - 1) > 1e-6f && fabsf(input->scale) > 1e-6f) ||
@@ -435,37 +483,100 @@ int ff_frame_to_dnn_detect(AVFrame *frame, DNNData 
*input, void *log_ctx)
         return AVERROR(ENOSYS);
     }
 
-    if (input->layout == DL_NCHW) {
-        av_log(log_ctx, AV_LOG_ERROR, "dnn_detect input data doesn't support 
layout: NCHW\n");
+    if (fmt == AV_PIX_FMT_NONE) {
+        av_log(log_ctx, AV_LOG_ERROR, "dnn_detect input data doesn't support "
+                                      "color order: %d\n", input->order);
         return AVERROR(ENOSYS);
     }
 
-    width_idx = dnn_get_width_idx_by_layout(input->layout);
-    height_idx = dnn_get_height_idx_by_layout(input->layout);
+    if (input->dt != DNN_UINT8 && input->dt != DNN_FLOAT) {
+        av_log(log_ctx, AV_LOG_ERROR, "dnn_detect input data doesn't support "
+                                      "data type: %d\n", input->dt);
+        return AVERROR(ENOSYS);
+    }
 
-    sws_ctx = sws_getContext(frame->width, frame->height, frame->format,
-                             input->dims[width_idx],
-                             input->dims[height_idx], fmt,
-                             SWS_FAST_BILINEAR, NULL, NULL, NULL);
-    if (!sws_ctx) {
-        av_log(log_ctx, AV_LOG_ERROR, "Impossible to create scale context for 
the conversion "
-            "fmt:%s s:%dx%d -> fmt:%s s:%dx%d\n",
-            av_get_pix_fmt_name(frame->format), frame->width, frame->height,
-            av_get_pix_fmt_name(fmt), input->dims[width_idx],
-            input->dims[height_idx]);
+    width_idx   = dnn_get_width_idx_by_layout(input->layout);
+    height_idx  = dnn_get_height_idx_by_layout(input->layout);
+    channel_idx = dnn_get_channel_idx_by_layout(input->layout);
+
+    w = input->dims[width_idx];
+    h = input->dims[height_idx];
+    if (w <= 0 || h <= 0) {
+        av_log(log_ctx, AV_LOG_ERROR, "dnn_detect input data has invalid "
+                                      "dimensions: %dx%d\n", w, h);
         return AVERROR(EINVAL);
     }
 
-    ret = av_image_fill_linesizes(linesizes, fmt, input->dims[width_idx]);
+    packed_u8 = (input->dt == DNN_UINT8) && (input->layout != DL_NCHW);
+    if (!packed_u8 && input->dims[channel_idx] != 3) {
+        av_log(log_ctx, AV_LOG_ERROR, "dnn_detect requires a 3-channel input, "
+                                      "but the model has %d channels\n",
+                                      input->dims[channel_idx]);
+        return AVERROR(ENOSYS);
+    }
+
+    ret = av_image_fill_linesizes(linesizes, fmt, w);
     if (ret < 0) {
         av_log(log_ctx, AV_LOG_ERROR, "unable to get linesizes with 
av_image_fill_linesizes");
-        sws_freeContext(sws_ctx);
         return ret;
     }
 
-    sws_scale(sws_ctx, (const uint8_t *const *)frame->data, frame->linesize, 
0, frame->height,
-                       (uint8_t *const [4]){input->data, 0, 0, 0}, linesizes);
+    if (packed_u8) {
+        /* UINT8 + NHWC: sws_scale() writes straight into input->data in one 
pass. */
+        sws_ctx = sws_getContext(frame->width, frame->height, frame->format,
+                                 w, h, fmt, SWS_FAST_BILINEAR, NULL, NULL, 
NULL);
+        if (!sws_ctx) {
+            av_log(log_ctx, AV_LOG_ERROR, "Impossible to create scale context 
for the conversion "
+                "fmt:%s s:%dx%d -> fmt:%s s:%dx%d\n",
+                av_get_pix_fmt_name(frame->format), frame->width, 
frame->height,
+                av_get_pix_fmt_name(fmt), w, h);
+            return AVERROR(EINVAL);
+        }
 
-    sws_freeContext(sws_ctx);
+        sws_scale(sws_ctx, (const uint8_t *const *)frame->data, 
frame->linesize, 0, frame->height,
+                           (uint8_t *const [4]){input->data, 0, 0, 0}, 
linesizes);
+        sws_freeContext(sws_ctx);
+        return 0;
+    }
+
+    if (frame->format == fmt && frame->width == w && frame->height == h) {
+        src = frame->data[0];
+        src_linesize = frame->linesize[0];
+    } else {
+        size_t tmp_size;
+
+        if (av_size_mult((size_t)linesizes[0], (size_t)h, &tmp_size) < 0) {
+            av_log(log_ctx, AV_LOG_ERROR, "dnn_detect temporary buffer size 
overflows\n");
+            return AVERROR(EINVAL);
+        }
+
+        tmp_buf = av_malloc(tmp_size);
+        if (!tmp_buf)
+            return AVERROR(ENOMEM);
+
+        sws_ctx = sws_getContext(frame->width, frame->height, frame->format,
+                                 w, h, fmt, SWS_FAST_BILINEAR, NULL, NULL, 
NULL);
+        if (!sws_ctx) {
+            av_log(log_ctx, AV_LOG_ERROR, "Impossible to create scale context 
for the conversion "
+                "fmt:%s s:%dx%d -> fmt:%s s:%dx%d\n",
+                av_get_pix_fmt_name(frame->format), frame->width, 
frame->height,
+                av_get_pix_fmt_name(fmt), w, h);
+            ret = AVERROR(EINVAL);
+            goto end;
+        }
+
+        sws_scale(sws_ctx, (const uint8_t *const *)frame->data, 
frame->linesize, 0, frame->height,
+                           (uint8_t *const [4]){tmp_buf, 0, 0, 0}, linesizes);
+        sws_freeContext(sws_ctx);
+        sws_ctx = NULL;
+
+        src = tmp_buf;
+        src_linesize = linesizes[0];
+    }
+
+    detect_write_tensor(input, src, src_linesize, w, h);
+
+end:
+    av_freep(&tmp_buf);
     return ret;
 }
diff --git a/libavfilter/dnn_filter_common.c b/libavfilter/dnn_filter_common.c
index 1e2bbd776e..392cca4d38 100644
--- a/libavfilter/dnn_filter_common.c
+++ b/libavfilter/dnn_filter_common.c
@@ -109,18 +109,14 @@ int ff_dnn_init(DnnContext *ctx, DNNFunctionType 
func_type, AVFilterContext *fil
             return AVERROR(EINVAL);
         }
     } else if (backend == DNN_ONNX) {
-        /* ONNX: input and output tensor names are optional.*/
+        /* ONNX: input and output tensor names are optional.
+         * Multiple output names may be specified separated by '&'. */
         if (ctx->model_outputnames_string) {
             ctx->model_outputnames = 
separate_output_names(ctx->model_outputnames_string, "&", &ctx->nb_outputs);
             if (!ctx->model_outputnames) {
                 av_log(filter_ctx, AV_LOG_ERROR, "could not parse model output 
names\n");
                 return AVERROR(EINVAL);
             }
-            if (ctx->nb_outputs != 1) {
-                av_log(filter_ctx, AV_LOG_ERROR,
-                       "ONNX backend supports a single output name only\n");
-                return AVERROR(EINVAL);
-            }
         }
     }
 
diff --git a/libavfilter/vf_dnn_detect.c b/libavfilter/vf_dnn_detect.c
index 8ce51bf07d..99dca8546c 100644
--- a/libavfilter/vf_dnn_detect.c
+++ b/libavfilter/vf_dnn_detect.c
@@ -21,6 +21,7 @@
  * implementing an object detecting filter using deep learning networks.
  */
 
+#include "libavutil/common.h"
 #include "libavutil/file_open.h"
 #include "libavutil/mem.h"
 #include "libavutil/opt.h"
@@ -70,6 +71,9 @@ static const AVOption dnn_detect_options[] = {
 #endif
 #if (CONFIG_LIBOPENVINO == 1)
     { "openvino",    "openvino backend flag",      0,                        
AV_OPT_TYPE_CONST,     { .i64 = DNN_OV },    0, 0, FLAGS, .unit = "backend" },
+#endif
+#if (CONFIG_LIBONNXRUNTIME == 1)
+    { "onnx",        "ONNX Runtime backend flag",  0,                        
AV_OPT_TYPE_CONST,     { .i64 = DNN_ONNX },  0, 0, FLAGS, .unit = "backend" },
 #endif
     { "confidence",  "threshold of confidence",    OFFSET2(confidence),      
AV_OPT_TYPE_FLOAT,     { .dbl = 0.5 },  0, 1, FLAGS},
     { "labels",      "path to labels file",        OFFSET2(labels_filename), 
AV_OPT_TYPE_STRING,    { .str = NULL }, 0, 0, FLAGS },
@@ -85,7 +89,7 @@ static const AVOption dnn_detect_options[] = {
     { NULL }
 };
 
-AVFILTER_DNN_DEFINE_CLASS(dnn_detect, DNN_TF | DNN_OV);
+AVFILTER_DNN_DEFINE_CLASS(dnn_detect, DNN_TF | DNN_OV | DNN_ONNX);
 
 static inline float sigmoid(float x) {
     return 1.f / (1.f + exp(-x));
@@ -108,19 +112,80 @@ static int dnn_detect_get_label_id(int nb_classes, int 
cell_size, float *label_d
     return label_id;
 }
 
+static int dnn_detect_label_id_from_float(float f)
+{
+    if (!isfinite(f) || f < 0 || (double)f > INT_MAX)
+        return -1;
+    return (int)f;
+}
+
+static int dnn_detect_float_to_coord(double v)
+{
+    if (!isfinite(v))
+        return 0;
+    if (v > INT_MAX)
+        return INT_MAX;
+    if (v < INT_MIN)
+        return INT_MIN;
+    return (int)v;
+}
+
+static int dnn_detect_confidence_num(double conf)
+{
+    double scaled = conf * 10000.0;
+    if (!isfinite(scaled))
+        return 0;
+    if (scaled > INT_MAX)
+        return INT_MAX;
+    if (scaled < 0)
+        return 0;
+    return (int)scaled;
+}
+
+static void dnn_detect_set_bbox_edges(AVDetectionBBox *bbox, double x0, double 
y0,
+                                       double x1, double y1, int frame_w, int 
frame_h)
+{
+    double lo_x = -1.0 * frame_w, hi_x = 2.0 * frame_w;
+    double lo_y = -1.0 * frame_h, hi_y = 2.0 * frame_h;
+
+    x0 = av_clipd(x0, lo_x, hi_x);
+    x1 = av_clipd(x1, lo_x, hi_x);
+    y0 = av_clipd(y0, lo_y, hi_y);
+    y1 = av_clipd(y1, lo_y, hi_y);
+
+    if (x1 < x0)
+        x1 = x0;
+    if (y1 < y0)
+        y1 = y0;
+
+    bbox->x = dnn_detect_float_to_coord(x0);
+    bbox->y = dnn_detect_float_to_coord(y0);
+    bbox->w = dnn_detect_float_to_coord(x1 - x0);
+    bbox->h = dnn_detect_float_to_coord(y1 - y0);
+}
+
 /* Calculate Intersection Over Union */
 static float dnn_detect_IOU(AVDetectionBBox *bbox1, AVDetectionBBox *bbox2)
 {
-    float overlapping_width = FFMIN(bbox1->x + bbox1->w, bbox2->x + bbox2->w) 
- FFMAX(bbox1->x, bbox2->x);
-    float overlapping_height = FFMIN(bbox1->y + bbox1->h, bbox2->y + bbox2->h) 
- FFMAX(bbox1->y, bbox2->y);
-    float intersection_area =
+    double x1_min = bbox1->x, y1_min = bbox1->y;
+    double x1_max = (double)bbox1->x + bbox1->w, y1_max = (double)bbox1->y + 
bbox1->h;
+    double x2_min = bbox2->x, y2_min = bbox2->y;
+    double x2_max = (double)bbox2->x + bbox2->w, y2_max = (double)bbox2->y + 
bbox2->h;
+    double overlapping_width = FFMIN(x1_max, x2_max) - FFMAX(x1_min, x2_min);
+    double overlapping_height = FFMIN(y1_max, y2_max) - FFMAX(y1_min, y2_min);
+    double intersection_area =
         (overlapping_width < 0 || overlapping_height < 0) ? 0 : 
overlapping_height * overlapping_width;
-    float union_area = bbox1->w * bbox1->h + bbox2->w * bbox2->h - 
intersection_area;
-    return intersection_area / union_area;
+    double area1 = (double)bbox1->w * bbox1->h;
+    double area2 = (double)bbox2->w * bbox2->h;
+    double union_area = area1 + area2 - intersection_area;
+
+    if (area1 <= 0 || area2 <= 0 || union_area <= 0)
+        return 0.f;
+    return (float)(intersection_area / union_area);
 }
 
 static int dnn_detect_parse_yolo_output(AVFrame *frame, DNNData *output, int 
output_index,
-                                      AVFilterContext *filter_ctx)
+                                      AVFilterContext *filter_ctx, int64_t 
*anchor_used)
 {
     DnnDetectContext *ctx = filter_ctx->priv;
     float conf_threshold = ctx->confidence;
@@ -128,11 +193,19 @@ static int dnn_detect_parse_yolo_output(AVFrame *frame, 
DNNData *output, int out
     int cell_w = 0, cell_h = 0, scale_w = 0, scale_h = 0;
     int nb_classes = ctx->nb_classes;
     float *output_data = output[output_index].data;
-    float *anchors = ctx->anchors;
+    float *anchors;
+    int64_t anchors_needed;
     AVDetectionBBox *bbox;
     float (*post_process_raw_data)(float x) = linear;
     int is_NHWC = 0;
 
+    if (output[output_index].dims[0] != 1) {
+        av_log(filter_ctx, AV_LOG_ERROR,
+               "YOLO output batch dimension must be 1, got %d\n",
+               output[output_index].dims[0]);
+        return AVERROR_INVALIDDATA;
+    }
+
     if (ctx->model_type == DDMT_YOLOV1V2) {
         cell_w = ctx->cell_w;
         cell_h = ctx->cell_h;
@@ -151,8 +224,6 @@ static int dnn_detect_parse_yolo_output(AVFrame *frame, 
DNNData *output, int out
         scale_w = ctx->scale_width;
         scale_h = ctx->scale_height;
     }
-    box_size = nb_classes + 5;
-
     switch (ctx->model_type) {
     case DDMT_YOLOV1V2:
     case DDMT_YOLOV3:
@@ -163,31 +234,66 @@ static int dnn_detect_parse_yolo_output(AVFrame *frame, 
DNNData *output, int out
          break;
     }
 
-    if (!cell_h || !cell_w) {
+    if (cell_h <= 0 || cell_w <= 0) {
         av_log(filter_ctx, AV_LOG_ERROR, "cell_w and cell_h are detected\n");
         return AVERROR(EINVAL);
     }
 
-    if (!nb_classes) {
+    if (nb_classes <= 0) {
         av_log(filter_ctx, AV_LOG_ERROR, "nb_classes is not set\n");
         return AVERROR(EINVAL);
     }
 
-    if (output[output_index].dims[1] * output[output_index].dims[2] *
-            output[output_index].dims[3] % (box_size * cell_w * cell_h)) {
+    if (output[output_index].dims[1] <= 0 || output[output_index].dims[2] <= 0 
||
+        output[output_index].dims[3] <= 0) {
+        av_log(filter_ctx, AV_LOG_ERROR, "invalid output tensor dimensions\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    size_t box_size_sz = (size_t)nb_classes + 5;
+    size_t cell_area, elems_per_box, tmp, total_elems, detection_boxes_sz;
+
+    if (av_size_mult((size_t)cell_w, (size_t)cell_h, &cell_area) < 0 ||
+        av_size_mult(box_size_sz, cell_area, &elems_per_box) < 0 ||
+        elems_per_box == 0 || elems_per_box > INT_MAX) {
+        av_log(filter_ctx, AV_LOG_ERROR, "wrong cell_w, cell_h or 
nb_classes\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (av_size_mult((size_t)output[output_index].dims[1],
+                        (size_t)output[output_index].dims[2], &tmp) < 0 ||
+        av_size_mult(tmp, (size_t)output[output_index].dims[3], &total_elems) 
< 0 ||
+        total_elems > INT_MAX) {
+        av_log(filter_ctx, AV_LOG_ERROR, "output tensor is too large\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    if (total_elems % elems_per_box) {
+        av_log(filter_ctx, AV_LOG_ERROR, "wrong cell_w, cell_h or 
nb_classes\n");
+        return AVERROR(EINVAL);
+    }
+
+    detection_boxes_sz = total_elems / elems_per_box;
+    if (detection_boxes_sz == 0 || detection_boxes_sz > INT_MAX) {
         av_log(filter_ctx, AV_LOG_ERROR, "wrong cell_w, cell_h or 
nb_classes\n");
         return AVERROR(EINVAL);
     }
-    detection_boxes = output[output_index].dims[1] *
-                      output[output_index].dims[2] *
-                      output[output_index].dims[3] / box_size / cell_w / 
cell_h;
 
-    anchors = anchors + (detection_boxes * output_index * 2);
-    /**
-     * find all candidate bbox
-     * yolo output can be reshaped to [B, N*D, Cx, Cy]
-     * Detection box 'D' has format [`x`, `y`, `h`, `w`, `box_score`, 
`class_no_1`, ...,]
-     **/
+    box_size = (int)box_size_sz;
+    detection_boxes = (int)detection_boxes_sz;
+
+    anchors_needed = (int64_t)detection_boxes * 2;
+    if (anchors_needed < 0 || *anchor_used < 0 ||
+        *anchor_used + anchors_needed > ctx->nb_anchor) {
+        av_log(filter_ctx, AV_LOG_ERROR,
+               "anchors array (%d floats) is too small for %d detection 
box(es) "
+               "in output %d (needs %"PRId64" floats starting at offset 
%"PRId64")\n",
+               ctx->nb_anchor, detection_boxes, output_index, anchors_needed, 
*anchor_used);
+        return AVERROR_INVALIDDATA;
+    }
+    anchors = ctx->anchors + *anchor_used;
+    *anchor_used += anchors_needed;
+
     for (int box_id = 0; box_id < detection_boxes; box_id++) {
         for (int cx = 0; cx < cell_w; cx++)
             for (int cy = 0; cy < cell_h; cy++) {
@@ -222,7 +328,7 @@ static int dnn_detect_parse_yolo_output(AVFrame *frame, 
DNNData *output, int out
                     conf = conf * post_process_raw_data(
                                 detection_boxes_data[cy * cell_w + cx + 
(label_id + 5) * cell_w * cell_h]);
                 }
-                if (conf < conf_threshold) {
+                if (!isfinite(conf) || conf < conf_threshold) {
                     continue;
                 }
 
@@ -230,11 +336,15 @@ static int dnn_detect_parse_yolo_output(AVFrame *frame, 
DNNData *output, int out
                 if (!bbox)
                     return AVERROR(ENOMEM);
 
-                bbox->w = exp(w) * anchors[box_id * 2] * frame->width / 
scale_w;
-                bbox->h = exp(h) * anchors[box_id * 2 + 1] * frame->height / 
scale_h;
-                bbox->x = (cx + x) / cell_w * frame->width - bbox->w / 2;
-                bbox->y = (cy + y) / cell_h * frame->height - bbox->h / 2;
-                bbox->detect_confidence = av_make_q((int)(conf * 10000), 
10000);
+                double w_px = exp((double)w) * anchors[box_id * 2] * 
frame->width / scale_w;
+                double h_px = exp((double)h) * anchors[box_id * 2 + 1] * 
frame->height / scale_h;
+                double x_px = (cx + (double)x) / cell_w * frame->width - w_px 
/ 2;
+                double y_px = (cy + (double)y) / cell_h * frame->height - h_px 
/ 2;
+
+                dnn_detect_set_bbox_edges(bbox, x_px, y_px, x_px + w_px, y_px 
+ h_px,
+                                           frame->width, frame->height);
+
+                bbox->detect_confidence = 
av_make_q(dnn_detect_confidence_num(conf), 10000);
                 if (ctx->labels && label_id < ctx->label_count) {
                     av_strlcpy(bbox->detect_label, ctx->labels[label_id], 
sizeof(bbox->detect_label));
                 } else {
@@ -303,7 +413,8 @@ static int dnn_detect_fill_side_data(AVFrame *frame, 
AVFilterContext *filter_ctx
 static int dnn_detect_post_proc_yolo(AVFrame *frame, DNNData *output, 
AVFilterContext *filter_ctx)
 {
     int ret = 0;
-    ret = dnn_detect_parse_yolo_output(frame, output, 0, filter_ctx);
+    int64_t anchor_used = 0;
+    ret = dnn_detect_parse_yolo_output(frame, output, 0, filter_ctx, 
&anchor_used);
     if (ret < 0)
         return ret;
     ret = dnn_detect_fill_side_data(frame, filter_ctx);
@@ -316,8 +427,9 @@ static int dnn_detect_post_proc_yolov3(AVFrame *frame, 
DNNData *output,
                                        AVFilterContext *filter_ctx, int 
nb_outputs)
 {
     int ret = 0;
+    int64_t anchor_used = 0;
     for (int i = 0; i < nb_outputs; i++) {
-        ret = dnn_detect_parse_yolo_output(frame, output, i, filter_ctx);
+        ret = dnn_detect_parse_yolo_output(frame, output, i, filter_ctx, 
&anchor_used);
         if (ret < 0)
             return ret;
     }
@@ -334,42 +446,98 @@ static int dnn_detect_post_proc_ssd(AVFrame *frame, 
DNNData *output, int nb_outp
     float conf_threshold = ctx->confidence;
     int proposal_count = 0;
     int detect_size = 0;
+    int detect_output_idx = 0;
+    int label_output_idx = -1;
     float *detections = NULL, *labels = NULL;
     int nb_bboxes = 0;
     AVDetectionBBoxHeader *header;
     AVDetectionBBox *bbox;
     int scale_w = ctx->scale_width;
     int scale_h = ctx->scale_height;
+    size_t detect_elems, needed_elems;
 
     if (nb_outputs == 1 && output->dims[3] == 7) {
+        detect_output_idx = 0;
         proposal_count = output->dims[2];
         detect_size = output->dims[3];
         detections = output->data;
     } else if (nb_outputs == 2 && output[0].dims[3] == 5) {
+        detect_output_idx = 0;
         proposal_count = output[0].dims[2];
         detect_size = output[0].dims[3];
         detections = output[0].data;
         labels = output[1].data;
+        label_output_idx = 1;
     } else if (nb_outputs == 2 && output[1].dims[3] == 5) {
+        detect_output_idx = 1;
         proposal_count = output[1].dims[2];
         detect_size = output[1].dims[3];
         detections = output[1].data;
         labels = output[0].data;
+        label_output_idx = 0;
     } else {
         av_log(filter_ctx, AV_LOG_ERROR, "Model output shape doesn't match ssd 
requirement.\n");
         return AVERROR(EINVAL);
     }
 
+    if (proposal_count < 0) {
+        av_log(filter_ctx, AV_LOG_ERROR, "Invalid negative proposal count 
%d.\n", proposal_count);
+        return AVERROR_INVALIDDATA;
+    }
+
     if (proposal_count == 0)
         return 0;
 
+    if (av_size_mult((size_t)proposal_count, (size_t)detect_size,
+                      &needed_elems) < 0) {
+        av_log(filter_ctx, AV_LOG_ERROR, "detection tensor element count 
overflows\n");
+        return AVERROR_INVALIDDATA;
+    }
+    if (needed_elems > INT_MAX) {
+        av_log(filter_ctx, AV_LOG_ERROR,
+               "detection tensor has %zu elements, more than the supported 
maximum\n",
+               needed_elems);
+        return AVERROR_INVALIDDATA;
+    }
+
+    if (av_size_mult((size_t)output[detect_output_idx].dims[0],
+                      (size_t)output[detect_output_idx].dims[1], 
&detect_elems) < 0 ||
+        av_size_mult(detect_elems, (size_t)output[detect_output_idx].dims[2], 
&detect_elems) < 0 ||
+        av_size_mult(detect_elems, (size_t)output[detect_output_idx].dims[3], 
&detect_elems) < 0) {
+        av_log(filter_ctx, AV_LOG_ERROR, "detection tensor element count 
overflows\n");
+        return AVERROR_INVALIDDATA;
+    }
+    if (detect_elems != needed_elems) {
+        av_log(filter_ctx, AV_LOG_ERROR,
+               "detection tensor has %zu elements, expected %zu\n",
+               detect_elems, needed_elems);
+        return AVERROR_INVALIDDATA;
+    }
+
+    if (label_output_idx >= 0) {
+        size_t label_count;
+        if (av_size_mult((size_t)output[label_output_idx].dims[0],
+                          (size_t)output[label_output_idx].dims[1], 
&label_count) < 0 ||
+            av_size_mult(label_count, 
(size_t)output[label_output_idx].dims[2], &label_count) < 0 ||
+            av_size_mult(label_count, 
(size_t)output[label_output_idx].dims[3], &label_count) < 0) {
+            av_log(filter_ctx, AV_LOG_ERROR, "labels tensor element count 
overflows\n");
+            return AVERROR_INVALIDDATA;
+        }
+        if (label_count < (size_t)proposal_count) {
+            av_log(filter_ctx, AV_LOG_ERROR,
+                   "labels tensor has %zu element(s), too small for %d 
proposal(s)\n",
+                   label_count, proposal_count);
+            return AVERROR_INVALIDDATA;
+        }
+    }
+
     for (int i = 0; i < proposal_count; ++i) {
         float conf;
         if (nb_outputs == 1)
             conf = detections[i * detect_size + 2];
         else
             conf = detections[i * detect_size + 4];
-        if (conf < conf_threshold) {
+        if (!isfinite(conf) || conf < conf_threshold) {
             continue;
         }
         nb_bboxes++;
@@ -389,19 +557,19 @@ static int dnn_detect_post_proc_ssd(AVFrame *frame, 
DNNData *output, int nb_outp
     av_strlcpy(header->source, ctx->dnnctx.model_filename, 
sizeof(header->source));
 
     for (int i = 0; i < proposal_count; ++i) {
-        av_unused int image_id = (int)detections[i * detect_size + 0];
         int label_id;
         float conf, x0, y0, x1, y1;
+        double x0_px, y0_px, x1_px, y1_px;
 
         if (nb_outputs == 1) {
-            label_id = (int)detections[i * detect_size + 1];
+            label_id = dnn_detect_label_id_from_float(detections[i * 
detect_size + 1]);
             conf = detections[i * detect_size + 2];
             x0   = detections[i * detect_size + 3];
             y0   = detections[i * detect_size + 4];
             x1   = detections[i * detect_size + 5];
             y1   = detections[i * detect_size + 6];
         } else {
-            label_id = (int)labels[i];
+            label_id = dnn_detect_label_id_from_float(labels[i]);
             x0     =      detections[i * detect_size] / scale_w;
             y0     =      detections[i * detect_size + 1] / scale_h;
             x1     =      detections[i * detect_size + 2] / scale_w;
@@ -409,20 +577,24 @@ static int dnn_detect_post_proc_ssd(AVFrame *frame, 
DNNData *output, int nb_outp
             conf   =      detections[i * detect_size + 4];
         }
 
-        if (conf < conf_threshold) {
+        if (!isfinite(conf) || conf < conf_threshold) {
             continue;
         }
 
         bbox = av_get_detection_bbox(header, header->nb_bboxes - nb_bboxes);
-        bbox->x = (int)(x0 * frame->width);
-        bbox->w = (int)(x1 * frame->width) - bbox->x;
-        bbox->y = (int)(y0 * frame->height);
-        bbox->h = (int)(y1 * frame->height) - bbox->y;
 
-        bbox->detect_confidence = av_make_q((int)(conf * 10000), 10000);
+        x0_px = (double)x0 * frame->width;
+        y0_px = (double)y0 * frame->height;
+        x1_px = (double)x1 * frame->width;
+        y1_px = (double)y1 * frame->height;
+
+        dnn_detect_set_bbox_edges(bbox, x0_px, y0_px, x1_px, y1_px,
+                                   frame->width, frame->height);
+
+        bbox->detect_confidence = av_make_q(dnn_detect_confidence_num(conf), 
10000);
         bbox->classify_count = 0;
 
-        if (ctx->labels && label_id < ctx->label_count) {
+        if (ctx->labels && label_id >= 0 && label_id < ctx->label_count) {
             av_strlcpy(bbox->detect_label, ctx->labels[label_id], 
sizeof(bbox->detect_label));
         } else {
             snprintf(bbox->detect_label, sizeof(bbox->detect_label), "%d", 
label_id);
@@ -436,7 +608,7 @@ static int dnn_detect_post_proc_ssd(AVFrame *frame, DNNData 
*output, int nb_outp
     return 0;
 }
 
-static int dnn_detect_post_proc_ov(AVFrame *frame, DNNData *output, int 
nb_outputs,
+static int dnn_detect_post_proc_anchored(AVFrame *frame, DNNData *output, int 
nb_outputs,
                                    AVFilterContext *filter_ctx)
 {
     AVFrameSideData *sd;
@@ -512,6 +684,7 @@ static int dnn_detect_post_proc_tf(AVFrame *frame, DNNData 
*output, AVFilterCont
     av_strlcpy(header->source, ctx->dnnctx.model_filename, 
sizeof(header->source));
 
     for (int i = 0; i < proposal_count; ++i) {
+        int label_id_i;
         y0 = position[i * 4];
         x0 = position[i * 4 + 1];
         y1 = position[i * 4 + 2];
@@ -531,10 +704,11 @@ static int dnn_detect_post_proc_tf(AVFrame *frame, 
DNNData *output, AVFilterCont
         bbox->detect_confidence = av_make_q((int)(conf[i] * 10000), 10000);
         bbox->classify_count = 0;
 
-        if (ctx->labels && label_id[i] < ctx->label_count) {
-            av_strlcpy(bbox->detect_label, ctx->labels[(int)label_id[i]], 
sizeof(bbox->detect_label));
+        label_id_i = dnn_detect_label_id_from_float(label_id[i]);
+        if (ctx->labels && label_id_i >= 0 && label_id_i < ctx->label_count) {
+            av_strlcpy(bbox->detect_label, ctx->labels[label_id_i], 
sizeof(bbox->detect_label));
         } else {
-            snprintf(bbox->detect_label, sizeof(bbox->detect_label), "%d", 
(int)label_id[i]);
+            snprintf(bbox->detect_label, sizeof(bbox->detect_label), "%d", 
label_id_i);
         }
 
         nb_bboxes--;
@@ -551,9 +725,11 @@ static int dnn_detect_post_proc(AVFrame *frame, DNNData 
*output, uint32_t nb, AV
     DnnContext *dnn_ctx = &ctx->dnnctx;
     switch (dnn_ctx->backend_type) {
     case DNN_OV:
-        return dnn_detect_post_proc_ov(frame, output, nb, filter_ctx);
+        return dnn_detect_post_proc_anchored(frame, output, nb, filter_ctx);
     case DNN_TF:
         return dnn_detect_post_proc_tf(frame, output, filter_ctx);
+    case DNN_ONNX:
+        return dnn_detect_post_proc_anchored(frame, output, nb, filter_ctx);
     default:
         avpriv_report_missing_feature(filter_ctx, "Current dnn backend does 
not support detect filter\n");
         return AVERROR(EINVAL);
@@ -639,6 +815,8 @@ static int check_output_nb(DnnDetectContext *ctx, 
DNNBackendType backend_type, i
         return 0;
     case DNN_OV:
         return 0;
+    case DNN_ONNX:
+        return 0;
     default:
         avpriv_report_missing_feature(ctx, "Dnn detect filter does not support 
current backend\n");
         return AVERROR(EINVAL);

-- 
To stop receiving notification emails like this one, please contact
[email protected].
_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to