tensormeta: Check dimensions when retrieving tensor

Modify the API to retrieve the tensor meta to check for the dimensions
as well.

Also fix an API mistake, the buffer whose dimensions should be checheck
is the one inside the GstTensor, not another buffer some outside.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9411>
This commit is contained in:
Olivier Crête 2025-07-16 15:58:27 -04:00 committed by GStreamer Marge Bot
parent 5f52eaae83
commit 2c3f2747d6
7 changed files with 74 additions and 54 deletions

View File

@ -11,6 +11,6 @@ variables:
CHECKS_TAG: '2025-05-24.0'
ABI_CHECK_TAG: '2025-07-15.0'
ABI_CHECK_TAG: '2025-07-16.0'
WINDOWS_TAG: '2025-07-03.0'

View File

@ -1513,8 +1513,7 @@ Validate whether the #GstBuffer has enough size to hold the tensor data.</doc>
<source-position filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensor.h"/>
<return-value transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensor.c">TRUE if the #GstTensor has the reading order from the memory matching @order,
dimensions matching @num_dims, data type matching @data_type and the #GstBuffer mathcing @data
has enough size to hold the tensor data.
dimensions matching @num_dims, data type matching @data_type
Otherwise FALSE will be returned.</doc>
<type name="gboolean" c:type="gboolean"/>
</return-value>
@ -1523,6 +1522,10 @@ Otherwise FALSE will be returned.</doc>
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensor.c">A #GstTensor</doc>
<type name="Tensor" c:type="const GstTensor*"/>
</instance-parameter>
<parameter name="data_type" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensor.c">The data type of the tensor</doc>
<type name="TensorDataType" c:type="GstTensorDataType"/>
</parameter>
<parameter name="order" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensor.c">The order of the tensor to read from the memory</doc>
<type name="TensorDimOrder" c:type="GstTensorDimOrder"/>
@ -1531,13 +1534,11 @@ Otherwise FALSE will be returned.</doc>
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensor.c">The number of dimensions that the tensor can have</doc>
<type name="gsize" c:type="gsize"/>
</parameter>
<parameter name="data_type" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensor.c">The data type of the tensor</doc>
<type name="TensorDataType" c:type="GstTensorDataType"/>
</parameter>
<parameter name="data" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensor.c">#GstBuffer holding tensor data</doc>
<type name="Gst.Buffer" c:type="GstBuffer*"/>
<parameter name="dims" transfer-ownership="none" nullable="1" allow-none="1">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensor.c">An optional array of dimensions, where G_MAXSIZE means ANY.</doc>
<array length="2" zero-terminated="0" c:type="const gsize*">
<type name="gsize" c:type="gsize"/>
</array>
</parameter>
</parameters>
</method>
@ -1765,16 +1766,14 @@ Otherwise NULL will be returned.</doc>
</parameters>
</method>
<method name="get_typed_tensor" c:identifier="gst_tensor_meta_get_typed_tensor" version="1.28">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensormeta.c">Get the first tensor from the #GstTensorMeta identified by @tensor_id, mathcing
the reading order, dimensions and the data type.
Validate whether the #GstBuffer has enough size to hold the tensor data.</doc>
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensormeta.c">Get the first tensor from the #GstTensorMeta identified by
@tensor_id, matching the reading order, dimensions and the data
type and optionally the dimensions. Validate whether the
#GstBuffer has enough size to hold the tensor data.</doc>
<source-position filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensormeta.h"/>
<return-value transfer-ownership="none" nullable="1">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensormeta.c">a GstTensor with id matching @tensor_id,
reading order from the memory matching @order, dimensions matching @num_dims,
data type matching @data_type. The #GstBuffer mathcing @data should
have enough size to hold the tensor data.
Otherwise NULL will be returned.</doc>
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensormeta.c">a matching #GstTensor,
otherwise NULL</doc>
<type name="Tensor" c:type="const GstTensor*"/>
</return-value>
<parameters>
@ -1786,6 +1785,10 @@ Otherwise NULL will be returned.</doc>
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensormeta.c">A #GQuark identifying the tensor-encoding</doc>
<type name="GLib.Quark" c:type="GQuark"/>
</parameter>
<parameter name="data_type" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensormeta.c">The data type of the tensor</doc>
<type name="TensorDataType" c:type="GstTensorDataType"/>
</parameter>
<parameter name="order" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensormeta.c">The order of the tensor to read from the memory</doc>
<type name="TensorDimOrder" c:type="GstTensorDimOrder"/>
@ -1794,13 +1797,11 @@ Otherwise NULL will be returned.</doc>
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensormeta.c">The number of dimensions that the tensor can have</doc>
<type name="gsize" c:type="gsize"/>
</parameter>
<parameter name="data_type" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensormeta.c">The data type of the tensor</doc>
<type name="TensorDataType" c:type="GstTensorDataType"/>
</parameter>
<parameter name="data" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensormeta.c">#GstBuffer holding tensor data</doc>
<type name="Gst.Buffer" c:type="GstBuffer*"/>
<parameter name="dims" transfer-ownership="none" nullable="1" allow-none="1">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/analytics/gsttensormeta.c">An optional array of dimensions, where G_MAXSIZE means ANY.</doc>
<array length="3" zero-terminated="0" c:type="const gsize*">
<type name="gsize" c:type="gsize"/>
</array>
</parameter>
</parameters>
</method>

View File

@ -263,24 +263,23 @@ gst_tensor_data_type_get_name (GstTensorDataType data_type)
/**
* gst_tensor_check_type:
* @tensor: A #GstTensor
* @data_type: The data type of the tensor
* @order: The order of the tensor to read from the memory
* @num_dims: The number of dimensions that the tensor can have
* @data_type: The data type of the tensor
* @data: #GstBuffer holding tensor data
* @dims: (array length=num_dims)(nullable): An optional array of dimensions, where G_MAXSIZE means ANY.
*
* Validate the tensor whether it mathces the reading order, dimensions and the data type.
* Validate whether the #GstBuffer has enough size to hold the tensor data.
*
* Returns: TRUE if the #GstTensor has the reading order from the memory matching @order,
* dimensions matching @num_dims, data type matching @data_type and the #GstBuffer mathcing @data
* has enough size to hold the tensor data.
* dimensions matching @num_dims, data type matching @data_type
* Otherwise FALSE will be returned.
*
* Since: 1.28
*/
gboolean
gst_tensor_check_type (const GstTensor * tensor, GstTensorDimOrder order,
gsize num_dims, GstTensorDataType data_type, GstBuffer * data)
gst_tensor_check_type (const GstTensor * tensor, GstTensorDataType data_type,
GstTensorDimOrder order, gsize num_dims, const gsize * dims)
{
gsize num_elements = 1, tensor_size, i;
@ -304,16 +303,25 @@ gst_tensor_check_type (const GstTensor * tensor, GstTensorDimOrder order,
for (i = 0; i < tensor->num_dims; i++) {
num_elements *= tensor->dims[i];
if (dims) {
if (dims[i] != G_MAXSIZE && dims[i] != tensor->dims[i]) {
GST_DEBUG ("Tensor has dim[%zu]=%zu but expect dim[%zu]=%zu",
i, tensor->dims[i], i, dims[i]);
return FALSE;
}
}
}
tensor_size = size_for_elements (tensor->data_type, num_elements);
if (gst_buffer_get_size (data) < tensor_size) {
GST_DEBUG ("Expected buffer of size %zu (%zu elements),"
if (gst_buffer_get_size (tensor->data) < tensor_size) {
GST_ERROR ("Expected buffer of size %zu (%zu elements),"
" but buffer has size %zu", tensor_size, num_elements,
gst_buffer_get_size (data));
gst_buffer_get_size (tensor->data));
return FALSE;
}
return TRUE;
}

View File

@ -221,8 +221,9 @@ GST_ANALYTICS_META_API
const gchar *gst_tensor_data_type_get_name (GstTensorDataType data_type);
GST_ANALYTICS_META_API
gboolean gst_tensor_check_type(const GstTensor * tensor, GstTensorDimOrder order,
gsize num_dims, GstTensorDataType data_type, GstBuffer * data);
gboolean gst_tensor_check_type(const GstTensor * tensor,
GstTensorDataType data_type, GstTensorDimOrder order, gsize num_dims,
const gsize *dims);
G_END_DECLS

View File

@ -210,27 +210,25 @@ gst_tensor_meta_get_by_id (GstTensorMeta * tmeta, GQuark id)
* gst_tensor_meta_get_typed_tensor:
* @tmeta: A #GstTensorMeta
* @tensor_id: A #GQuark identifying the tensor-encoding
* @data_type: The data type of the tensor
* @order: The order of the tensor to read from the memory
* @num_dims: The number of dimensions that the tensor can have
* @data_type: The data type of the tensor
* @data: #GstBuffer holding tensor data
* @dims: (array length=num_dims)(nullable): An optional array of dimensions, where G_MAXSIZE means ANY.
*
* Get the first tensor from the #GstTensorMeta identified by @tensor_id, mathcing
* the reading order, dimensions and the data type.
* Validate whether the #GstBuffer has enough size to hold the tensor data.
* Get the first tensor from the #GstTensorMeta identified by
* @tensor_id, matching the reading order, dimensions and the data
* type and optionally the dimensions. Validate whether the
* #GstBuffer has enough size to hold the tensor data.
*
* Return: (nullable) (transfer none): a GstTensor with id matching @tensor_id,
* reading order from the memory matching @order, dimensions matching @num_dims,
* data type matching @data_type. The #GstBuffer mathcing @data should
* have enough size to hold the tensor data.
* Otherwise NULL will be returned.
* Return: (nullable) (transfer none): a matching #GstTensor,
* otherwise NULL
*
* Since: 1.28
*/
const GstTensor *
gst_tensor_meta_get_typed_tensor (GstTensorMeta * tmeta,
GQuark tensor_id, GstTensorDimOrder order, gsize num_dims,
GstTensorDataType data_type, GstBuffer * data)
GQuark tensor_id, GstTensorDataType data_type, GstTensorDimOrder order,
gsize num_dims, const gsize * dims)
{
const GstTensor *tensor;
@ -239,7 +237,7 @@ gst_tensor_meta_get_typed_tensor (GstTensorMeta * tmeta,
if (tensor == NULL)
return NULL;
if (!gst_tensor_check_type (tensor, order, num_dims, data_type, data))
if (!gst_tensor_check_type (tensor, data_type, order, num_dims, dims))
return NULL;
return tensor;

View File

@ -84,7 +84,8 @@ const GstTensor *gst_tensor_meta_get_by_id (GstTensorMeta *tmeta, GQuark id);
GST_ANALYTICS_META_API
const GstTensor *gst_tensor_meta_get_typed_tensor (GstTensorMeta * tmeta,
GQuark tensor_id, GstTensorDimOrder order, gsize num_dims, GstTensorDataType data_type, GstBuffer * data);
GQuark tensor_id, GstTensorDataType data_type, GstTensorDimOrder order,
gsize num_dims, const gsize * dims);
GST_ANALYTICS_META_API
const GstTensor *gst_tensor_meta_get (GstTensorMeta *tmeta, gsize index);

View File

@ -308,6 +308,8 @@ gst_face_detector_tensor_decoder_get_tensor_meta (GstFaceDetectorTensorDecoder
const GstTensor ** scores_tensor)
{
GstTensorMeta *tensor_meta;
static const gsize BOXES_DIMS[] = { 1, G_MAXSIZE, 4 };
static const gsize SCORES_DIMS[] = { 1, G_MAXSIZE, 2 };
g_return_val_if_fail (boxes_tensor != NULL, FALSE);
g_return_val_if_fail (scores_tensor != NULL, FALSE);
@ -330,7 +332,13 @@ gst_face_detector_tensor_decoder_get_tensor_meta (GstFaceDetectorTensorDecoder
* 3 dimensions and the data type matching with GST_TENSOR_DATA_TYPE_FLOAT32 */
*boxes_tensor =
gst_tensor_meta_get_typed_tensor (tensor_meta, BOXES_TENSOR_ID_QUARK,
GST_TENSOR_DIM_ORDER_ROW_MAJOR, 3, GST_TENSOR_DATA_TYPE_FLOAT32, buf);
GST_TENSOR_DATA_TYPE_FLOAT32, GST_TENSOR_DIM_ORDER_ROW_MAJOR, 3,
BOXES_DIMS);
if (*boxes_tensor == NULL) {
GST_WARNING_OBJECT (self, "Can't retrieve boxes tensor");
return FALSE;
}
/* Retrieve the tensor that has a tensor-id matching
* SCORES_TENSOR_ID_QUARK in the GstTensorMeta along with
@ -338,10 +346,13 @@ gst_face_detector_tensor_decoder_get_tensor_meta (GstFaceDetectorTensorDecoder
* 3 dimensions and the data type matching with GST_TENSOR_DATA_TYPE_FLOAT32 */
*scores_tensor =
gst_tensor_meta_get_typed_tensor (tensor_meta, SCORES_TENSOR_ID_QUARK,
GST_TENSOR_DIM_ORDER_ROW_MAJOR, 3, GST_TENSOR_DATA_TYPE_FLOAT32, buf);
GST_TENSOR_DATA_TYPE_FLOAT32, GST_TENSOR_DIM_ORDER_ROW_MAJOR, 3,
SCORES_DIMS);
if (*boxes_tensor == NULL || *scores_tensor == NULL)
if (*scores_tensor == NULL) {
GST_WARNING_OBJECT (self, "Can't retrieve boxes tensor");
return FALSE;
}
return TRUE;
}
@ -644,7 +655,7 @@ gst_face_detector_tensor_decoder_transform_ip (GstBaseTransform * trans,
if (!gst_face_detector_tensor_decoder_get_tensor_meta (self, buf,
&boxes_tensor, &scores_tensor)) {
GST_ELEMENT_ERROR (self, STREAM, DECODE, (NULL),
("Tensor doens't have the expected data type or shape."));
("Tensor doesn't have the expected data type or shape."));
return GST_FLOW_ERROR;
}