Can't get tfliteinference to negotiate with ssdtensordec

I am trying to do some object detection with the tfliteinference element, but I’m having trouble getting it to negotiate with the ssdtensordec element. I am using the GStreamer 1.28 packages from Debian Testing (version 1.28.1-2+b2) to get a recent enough version without having to compile the whole thing from source.

I’m using this ssdlite_mobiledet TFLite model for testing. Reading the docs I saw I need to create a modelinfo file, which I did with the helper script in gst devtools - I based it on the ssd_mobilenet example which AFAIK is for a similar ONNX model. The modelinfo file I came up with is this:

modelinfo file
[modelinfo]
version=1.0
group-id=ssd-mobilenet-v1-variant-1-out

[normalized_input_image_tensor]
id=image_tensor_0
type=uint8
dims=1,320,320,3
dir=input
ranges=0.0,255.0;0.0,255.0;0.0,255.0

[TFLite_Detection_PostProcess]
id=ssd-mobilenet-v1-variant-1-out-boxes
type=float32
dims=1,100,4
dir=output

[TFLite_Detection_PostProcess:1]
id=ssd-mobilenet-v1-variant-1-out-classes
type=float32
dims=1,100
dir=output

[TFLite_Detection_PostProcess:2]
id=ssd-mobilenet-v1-variant-1-out-scores
type=float32
dims=1,100
dir=output

[TFLite_Detection_PostProcess:3]
id=generic-variant-1-out-count
type=float32
dims=1
dir=output

Anyway, when trying to test it with this pipeline (I initially wanted to use the overlay to display bounding boxes on the detected objects, but replaced that with fakesink to isolate the problem)…

GST_DEBUG=basetransform:5,ssdtensordec:5,tfliteinference:5 gst-launch-1.0 filesrc location=bus.jpg ! jpegparse ! jpegdec ! videoconvertscale ! tfliteinference model-file=ssdlite_mobiledet_coco_qat_postprocess.tflite  ! ssdtensordec label-file=coco_labels.txt ! objectdetectionoverlay ! fakesink

…I’m getting a negotiation error between the tfliteinference and ssdtensordec elements. The full debug log is very long so I won’t paste it here but this seems to be the relevant part:

0:00:00.052508356       1960       1963 DEBUG          basetransform gstbasetransform.c:1280:gst_base_transform_acceptcaps_default:<ssdtensordec0> subset with pad template: video/x-raw, tensors=(structure)[tensorgroups, ssd-mobilenet-v1-variant-1-out=(/uniquelist){ (caps)"tensor/strided\,\ tensor-id\=\(string\)generic-variant-1-out-count\,\ dims\=\(int\)\<\ \[\ 0\,\ 2147483647\ \]\ \>\,\ dims-order\=\(string\)row-major\,\ type\=\(string\)float32", (caps)"tensor/strided\,\ tensor-id\=\(string\)ssd-mobilenet-v1-variant-1-out-scores\,\ dims\=\(int\)\<\ \[\ 0\,\ 2147483647\ \]\,\ 0\ \>\,\ dims-order\=\(string\)row-major\,\ type\=\(string\)float32", (caps)"tensor/strided\,\ tensor-id\=\(string\)ssd-mobilenet-v1-variant-1-out-boxes\,\ dims\=\(int\)\<\ \[\ 0\,\ 2147483647\ \]\,\ 0\,\ 4\ \>\,\ dims-order\=\(string\)row-major\,\ type\=\(string\)float32", (caps)"tensor/strided\,\ tensor-id\=\(string\)ssd-mobilenet-v1-variant-1-out-classes\,\ dims\=\(int\)\<\ \[\ 0\,\ 2147483647\ \]\,\ 0\ \>\,\ dims-order\=\(string\)row-major\,\ type\=\(string\)float32" };]
0:00:00.052518023       1960       1963 DEBUG          basetransform gstbasetransform.c:1303:gst_base_transform_acceptcaps_default:<ssdtensordec0> caps can't intersect with the template
0:00:00.052529565       1960       1963 DEBUG          basetransform gstbasetransform.c:1293:gst_base_transform_acceptcaps_default:<ssdtensordec0> accept-caps result: 0
0:00:00.052543231       1960       1963 DEBUG          basetransform gstbasetransform.c:1237:gst_base_transform_find_transform:<tfliteinference0> FAILED to get peer of <tfliteinference0:src> to accept video/x-raw, width=(int)320, height=(int)320, interlace-mode=(string)progressive, multiview-mode=(string)mono, multiview-flags=(GstVideoMultiviewFlagsSet)0:ffffffff:/right-view-first/left-flipped/left-flopped/right-flipped/right-flopped/half-aspect/mixed-mono, pixel-aspect-ratio=(fraction)3/4, framerate=(fraction)0/1, format=(string)RGB, colorimetry=(string)1:1:0:0, tensors=(structure)[tensorgroups, ssd-mobilenet-v1-variant-1-out=(/uniquelist){ (caps)"tensor/strided\,\ dims-order\=\(string\)row-major\,\ tensor-id\=\(string\)ssd-mobilenet-v1-variant-1-out-boxes\,\ dims\=\(int\)\<\ 1\,\ 100\,\ 4\ \>\,\ type\=\(string\)float32", (caps)"tensor/strided\,\ dims-order\=\(string\)row-major\,\ tensor-id\=\(string\)ssd-mobilenet-v1-variant-1-out-classes\,\ dims\=\(int\)\<\ 1\,\ 100\ \>\,\ type\=\(string\)float32", (caps)"tensor/strided\,\ dims-order\=\(string\)row-major\,\ tensor-id\=\(string\)ssd-mobilenet-v1-variant-1-out-scores\,\ dims\=\(int\)\<\ 1\,\ 100\ \>\,\ type\=\(string\)float32", (caps)"tensor/strided\,\ dims-order\=\(string\)row-major\,\ tensor-id\=\(string\)generic-variant-1-out-count\,\ dims\=\(int\)\<\ 1\ \>\,\ type\=\(string\)float32" };]

From what I can understand, the dimensions of the output tensors don’t match, i.e. the ssd-mobilenet-v1-variant-1-out-boxes tensor has dimensions [1, 100, 4] while the ssdtensordec expects the second dimension (I guess number of detected objects?) there to be 0. I’m not sure how to get ssdtensordec to accept tensors with a dimension of 100 in that position, or if that’s even the right way to go about it. I also don’t know if the problem is with the modelinfo file I created, or if it’s something else. Can anyone give any pointers?

I believe I’ve hit the same issue yesterday. I notice your post while rebuilding with onnx support to try and reproduce the example. I’m also using tflite. I believe the template caps added to ssdtensordec many broke the element completely many month ago (which I’ll see if true once I have onnx demo). I believe it can be fixed with:

diff --git a/subprojects/gst-plugins-bad/gst/tensordecoders/gstssdtensordec.c b/subprojects/gst-plugins-bad/gst/tensordecoders/gstssdtensordec.c
index e9f967db3a..e9ab27d44b 100644
--- a/subprojects/gst-plugins-bad/gst/tensordecoders/gstssdtensordec.c
+++ b/subprojects/gst-plugins-bad/gst/tensordecoders/gstssdtensordec.c
@@ -127,19 +127,19 @@ GST_STATIC_PAD_TEMPLATE ("sink",
              "(GstCaps)["
               "tensor/strided,"
                 "tensor-id="GST_MODEL_OBJECT_DETECTOR_SCORES","
-                "dims=(int)<[0, max],0>,"
+                "dims=(int)<[0, max],[0, max]>,"
                 "dims-order=(string)row-major,"
                 "type=(string)float32;],"
             "(GstCaps)["
               "tensor/strided,"
                 "tensor-id="GST_MODEL_OBJECT_DETECTOR_BOXES","
-                "dims=(int)<[0, max],0,4>,"
+                "dims=(int)<[0, max],[0, max],4>,"
                 "dims-order=(string)row-major,"
                 "type=(string)float32;],"
             "(GstCaps)["
               "tensor/strided,"
                 "tensor-id="GST_MODEL_OBJECT_DETECTOR_CLASSES","
-                "dims=(int)<[0, max],0>,"
+                "dims=(int)<[0, max],[0, max]>,"
                 "dims-order=(string)row-major,"
                 "type=(string)float32;]"
         "}"

I’ll send a patch once I can confirm this. Note that while it made it negotiation and run, my model isn’t running right yet, but using onnx, I should be able to verify this. The offending patch would be this one from @dmorin .

An update on the subject, @ocrete applied this change and an MR is pending merge:

There was addition issues in the tfliteinference in conditions where the video is strided. Another MR is pending here:

I think the MR that @ndufresne mentioned should fix the problem but just to answer questions specifically. A value of 0 in tensor caps dimensions means dynamic, in other words the value is not fixated during. To make the analogy with pytorch it’s like a value of -1, but we followed the Gstreamer convention here. Now some inference frameworks don’t support dynamic dimensions, so the workaround is to set this dimension to a maximum value. Here 100 was chosen. On the tensor decoder caps we were constraining the dimension to be dynamic only. Latest changes fixed that by accepting fixed dimension value.

I hope it helps and thanks for sharing this.