From d28f11998deadc8036bb63aa5bdc80beb1a9fbe4 Mon Sep 17 00:00:00 2001 From: Vivek R <123vivekr@gmail.com> Date: Sun, 24 May 2020 23:37:25 +0530 Subject: opencv: add cvtracker plugin This adds an object tracker plugin. Tracker implementations from https://docs.opencv.org/3.4/d0/d0a/classcv_1_1Tracker.html Part-of: --- docs/plugins/gst_plugins_cache.json | 151 ++++++++ ext/opencv/gstcvtracker.cpp | 393 +++++++++++++++++++++ ext/opencv/gstcvtracker.h | 114 ++++++ ext/opencv/gstopencv.cpp | 2 + ext/opencv/meson.build | 7 +- tests/meson.build | 1 + tests/validate/meson.build | 29 ++ tests/validate/opencv/cvtracker.validatetest | 8 + .../flow-expectations/log-tracker-src-expected | 94 +++++ 9 files changed, 797 insertions(+), 2 deletions(-) create mode 100644 ext/opencv/gstcvtracker.cpp create mode 100644 ext/opencv/gstcvtracker.h create mode 100644 tests/validate/meson.build create mode 100644 tests/validate/opencv/cvtracker.validatetest create mode 100644 tests/validate/opencv/cvtracker/flow-expectations/log-tracker-src-expected diff --git a/docs/plugins/gst_plugins_cache.json b/docs/plugins/gst_plugins_cache.json index c7776a2d9..e3ad9e352 100644 --- a/docs/plugins/gst_plugins_cache.json +++ b/docs/plugins/gst_plugins_cache.json @@ -215782,6 +215782,117 @@ }, "rank": "none" }, + "cvtracker": { + "author": "Vivek R <123vivekr@gmail.com>", + "description": "Performs object tracking on videos and stores it in video buffer metadata.", + "hierarchy": [ + "GstCVTracker", + "GstOpencvVideoFilter", + "GstVideoFilter", + "GstBaseTransform", + "GstElement", + "GstObject", + "GInitiallyUnowned", + "GObject" + ], + "klass": "Filter/Effect/Video", + "long-name": "cvtracker", + "pad-templates": { + "sink": { + "caps": "video/x-raw:\n format: RGB\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\n framerate: [ 0/1, 2147483647/1 ]\n", + "direction": "sink", + "presence": "always" + }, + "src": { + "caps": "video/x-raw:\n format: RGB\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\n framerate: [ 0/1, 2147483647/1 ]\n", + "direction": "src", + "presence": "always" + } + }, + "properties": { + "algorithm": { + "blurb": "Algorithm for tracking objects", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "MedianFlow (3)", + "mutable": "null", + "readable": true, + "type": "GstOpenCVTrackerAlgorithm", + "writable": true + }, + "draw-rect": { + "blurb": "Draw rectangle around tracked object", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "true", + "mutable": "null", + "readable": true, + "type": "gboolean", + "writable": true + }, + "object-initial-height": { + "blurb": "Track object box's initial height", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "50", + "max": "-1", + "min": "0", + "mutable": "null", + "readable": true, + "type": "guint", + "writable": true + }, + "object-initial-width": { + "blurb": "Track object box's initial width", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "50", + "max": "-1", + "min": "0", + "mutable": "null", + "readable": true, + "type": "guint", + "writable": true + }, + "object-initial-x": { + "blurb": "Track object box's initial X coordinate", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "50", + "max": "-1", + "min": "0", + "mutable": "null", + "readable": true, + "type": "guint", + "writable": true + }, + "object-initial-y": { + "blurb": "Track object box's initial Y coordinate", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "50", + "max": "-1", + "min": "0", + "mutable": "null", + "readable": true, + "type": "guint", + "writable": true + } + }, + "rank": "none" + }, "dewarp": { "author": "Nicola Murino ", "description": "Dewarp fisheye images", @@ -217398,6 +217509,46 @@ } ] }, + "GstOpenCVTrackerAlgorithm": { + "kind": "enum", + "values": [ + { + "desc": "the Boosting tracker", + "name": "Boosting", + "value": "0" + }, + { + "desc": "the CSRT tracker", + "name": "CSRT", + "value": "1" + }, + { + "desc": "the KCF (Kernelized Correlation Filter) tracker", + "name": "KCF", + "value": "2" + }, + { + "desc": "the Median Flow tracker", + "name": "MedianFlow", + "value": "3" + }, + { + "desc": "the MIL tracker", + "name": "MIL", + "value": "4" + }, + { + "desc": "the MOSSE (Minimum Output Sum of Squared Error) tracker", + "name": "MOSSE", + "value": "5" + }, + { + "desc": "the TLD (Tracking, learning and detection) tracker", + "name": "TLD", + "value": "6" + } + ] + }, "GstOpencvFaceBlurFlags": { "kind": "flags", "values": [ diff --git a/ext/opencv/gstcvtracker.cpp b/ext/opencv/gstcvtracker.cpp new file mode 100644 index 000000000..829d9a2c6 --- /dev/null +++ b/ext/opencv/gstcvtracker.cpp @@ -0,0 +1,393 @@ +/* + * GStreamer + * Copyright (C) 2020 Vivek R <123vivekr@gmail.com> + * Copyright (C) 2021 Cesar Fabian Orccon Chipana + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-cvtracker + * + * Performs object tracking on videos and stores it in video buffer metadata. + * + * ## Example launch line + * + * ``` + * gst-launch-1.0 v4l2src ! videoconvert ! cvtracker box-x=50 box-y=50 box-wdith=50 box-height=50 ! videoconvert ! xvimagesink + * ``` + * + * Since: 1.20 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gstcvtracker.h" + +GST_DEBUG_CATEGORY_STATIC (gst_cvtracker_debug); +#define GST_CAT_DEFAULT gst_cvtracker_debug + +#define DEFAULT_PROP_INITIAL_X 50 +#define DEFAULT_PROP_INITIAL_Y 50 +#define DEFAULT_PROP_INITIAL_WIDTH 50 +#define DEFAULT_PROP_INITIAL_HEIGHT 50 + +enum +{ + PROP_0, + PROP_INITIAL_X, + PROP_INITIAL_Y, + PROP_INITIAL_WIDTH, + PROP_INITIAL_HEIGHT, + PROP_ALGORITHM, +}; + +#define GST_OPENCV_TRACKER_ALGORITHM (tracker_algorithm_get_type ()) + +/** + * GstOpenCVTrackerAlgorithm: + * + * Since: 1.20 + */ +static GType +tracker_algorithm_get_type (void) +{ + static GType algorithm = 0; + static const GEnumValue algorithms[] = { + {GST_OPENCV_TRACKER_ALGORITHM_BOOSTING, "the Boosting tracker", "Boosting"}, + {GST_OPENCV_TRACKER_ALGORITHM_CSRT, "the CSRT tracker", "CSRT"}, + {GST_OPENCV_TRACKER_ALGORITHM_KCF, + "the KCF (Kernelized Correlation Filter) tracker", + "KCF"}, + {GST_OPENCV_TRACKER_ALGORITHM_MEDIANFLOW, "the Median Flow tracker", + "MedianFlow"}, + {GST_OPENCV_TRACKER_ALGORITHM_MIL, "the MIL tracker", "MIL"}, + {GST_OPENCV_TRACKER_ALGORITHM_MOSSE, + "the MOSSE (Minimum Output Sum of Squared Error) tracker", "MOSSE"}, + {GST_OPENCV_TRACKER_ALGORITHM_TLD, + "the TLD (Tracking, learning and detection) tracker", + "TLD"}, + {0, NULL, NULL}, + }; + + if (!algorithm) { + algorithm = + g_enum_register_static ("GstOpenCVTrackerAlgorithm", algorithms); + } + return algorithm; +} + +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGB")) + ); + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGB")) + ); + +G_DEFINE_TYPE_WITH_CODE (GstCVTracker, gst_cvtracker, + GST_TYPE_OPENCV_VIDEO_FILTER, + GST_DEBUG_CATEGORY_INIT (gst_cvtracker_debug, "cvtracker", 0, + "Performs object tracking on videos and stores it in video buffer " + "metadata")); +GST_ELEMENT_REGISTER_DEFINE (cvtracker, "cvtracker", GST_RANK_NONE, + GST_TYPE_OPENCV_TRACKER); + +static void gst_cvtracker_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_cvtracker_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +static GstFlowReturn gst_cvtracker_transform_ip (GstOpencvVideoFilter + * filter, GstBuffer * buf, cv::Mat img); + +static void +gst_cvtracker_finalize (GObject * obj) +{ + GstCVTracker *filter = GST_OPENCV_TRACKER (obj); + + filter->tracker.release (); + filter->roi.release (); + + G_OBJECT_CLASS (gst_cvtracker_parent_class)->finalize (obj); +} + +static void +gst_cvtracker_class_init (GstCVTrackerClass * klass) +{ + GObjectClass *gobject_class; + GstOpencvVideoFilterClass *gstopencvbasefilter_class; + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gobject_class = (GObjectClass *) klass; + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_cvtracker_finalize); + gstopencvbasefilter_class = (GstOpencvVideoFilterClass *) klass; + + gstopencvbasefilter_class->cv_trans_ip_func = gst_cvtracker_transform_ip; + + gobject_class->set_property = gst_cvtracker_set_property; + gobject_class->get_property = gst_cvtracker_get_property; + + /* + * Tracker API in versions older than OpenCV 4.5.1 worked with a ROI based + * on Rect. However newer versions use Rect. Running the same + * tracker type on different versions may lead to round up errors. + * To avoid inconsistencies from the GStreamer side depending on the OpenCV + * version, use integer properties independently on the OpenCV. + **/ + g_object_class_install_property (gobject_class, PROP_INITIAL_X, + g_param_spec_uint ("object-initial-x", "Initial X coordinate", + "Track object box's initial X coordinate", 0, G_MAXUINT, + DEFAULT_PROP_INITIAL_X, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_INITIAL_Y, + g_param_spec_uint ("object-initial-y", "Initial Y coordinate", + "Track object box's initial Y coordinate", 0, G_MAXUINT, + DEFAULT_PROP_INITIAL_Y, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_INITIAL_WIDTH, + g_param_spec_uint ("object-initial-width", "Object Initial Width", + "Track object box's initial width", 0, G_MAXUINT, + DEFAULT_PROP_INITIAL_WIDTH, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_INITIAL_HEIGHT, + g_param_spec_uint ("object-initial-height", "Object Initial Height", + "Track object box's initial height", 0, G_MAXUINT, + DEFAULT_PROP_INITIAL_HEIGHT, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_ALGORITHM, + g_param_spec_enum ("algorithm", "Algorithm", + "Algorithm for tracking objects", GST_OPENCV_TRACKER_ALGORITHM, + GST_OPENCV_TRACKER_ALGORITHM_MEDIANFLOW, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + gst_element_class_set_static_metadata (element_class, + "cvtracker", + "Filter/Effect/Video", + "Performs object tracking on videos and stores it in video buffer metadata.", + "Vivek R <123vivekr@gmail.com>"); + + gst_element_class_add_static_pad_template (element_class, &src_factory); + gst_element_class_add_static_pad_template (element_class, &sink_factory); + + gst_type_mark_as_plugin_api (GST_OPENCV_TRACKER_ALGORITHM, + (GstPluginAPIFlags) 0); +} + +static void +gst_cvtracker_init (GstCVTracker * filter) +{ + filter->x = DEFAULT_PROP_INITIAL_X; + filter->y = DEFAULT_PROP_INITIAL_Y; + filter->width = DEFAULT_PROP_INITIAL_WIDTH; + filter->height = DEFAULT_PROP_INITIAL_HEIGHT; +#if CV_VERSION_MAJOR == 4 && CV_VERSION_MINOR >= 5 && CV_VERSION_REVISION >= 1 + filter->tracker = cv::legacy::upgradeTrackingAPI( + cv::legacy::TrackerMedianFlow::create()); +#else + filter->tracker = cv::TrackerMedianFlow::create(); +#endif + filter->post_debug_info = TRUE; + + gst_opencv_video_filter_set_in_place (GST_OPENCV_VIDEO_FILTER_CAST (filter), + TRUE); + filter->algorithm = GST_OPENCV_TRACKER_ALGORITHM_MEDIANFLOW; +} + +static void +gst_cvtracker_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstCVTracker *filter = GST_OPENCV_TRACKER (object); + + switch (prop_id) { + case PROP_INITIAL_X: + filter->x = g_value_get_uint (value); + break; + case PROP_INITIAL_Y: + filter->y = g_value_get_uint (value); + break; + case PROP_INITIAL_WIDTH: + filter->width = g_value_get_uint (value); + break; + case PROP_INITIAL_HEIGHT: + filter->height = g_value_get_uint (value); + break; + case PROP_ALGORITHM: + filter->algorithm = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +create_cvtracker (GstCVTracker * filter) +{ + switch (filter->algorithm) { + case GST_OPENCV_TRACKER_ALGORITHM_BOOSTING: +#if CV_VERSION_MAJOR == 4 && CV_VERSION_MINOR >= 5 && CV_VERSION_REVISION >= 1 + filter->tracker = cv::legacy::upgradeTrackingAPI( + cv::legacy::TrackerBoosting::create()); +#else + filter->tracker = cv::TrackerBoosting::create(); +#endif + break; + case GST_OPENCV_TRACKER_ALGORITHM_CSRT: + filter->tracker = cv::TrackerCSRT::create (); + break; + case GST_OPENCV_TRACKER_ALGORITHM_KCF: + filter->tracker = cv::TrackerKCF::create (); + break; + case GST_OPENCV_TRACKER_ALGORITHM_MEDIANFLOW: +#if CV_VERSION_MAJOR == 4 && CV_VERSION_MINOR >= 5 && CV_VERSION_REVISION >= 1 + filter->tracker = cv::legacy::upgradeTrackingAPI( + cv::legacy::TrackerMedianFlow::create()); +#else + filter->tracker = cv::TrackerMedianFlow::create(); +#endif + break; + case GST_OPENCV_TRACKER_ALGORITHM_MIL: + filter->tracker = cv::TrackerMIL::create (); + break; + case GST_OPENCV_TRACKER_ALGORITHM_MOSSE: +#if CV_VERSION_MAJOR == 4 && CV_VERSION_MINOR >= 5 && CV_VERSION_REVISION >= 1 + filter->tracker = cv::legacy::upgradeTrackingAPI( + cv::legacy::TrackerMOSSE::create()); +#else + filter->tracker = cv::TrackerMOSSE::create (); +#endif + break; + case GST_OPENCV_TRACKER_ALGORITHM_TLD: +#if CV_VERSION_MAJOR == 4 && CV_VERSION_MINOR >= 5 && CV_VERSION_REVISION >= 1 + filter->tracker = cv::legacy::upgradeTrackingAPI( + cv::legacy::TrackerTLD::create()); +#else + filter->tracker = cv::TrackerTLD::create(); +#endif + break; + } +} + +static void +gst_cvtracker_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstCVTracker *filter = GST_OPENCV_TRACKER (object); + + switch (prop_id) { + case PROP_INITIAL_X: + g_value_set_uint (value, filter->x); + break; + case PROP_INITIAL_Y: + g_value_set_uint (value, filter->y); + break; + case PROP_INITIAL_WIDTH: + g_value_set_uint (value, filter->width); + break; + case PROP_INITIAL_HEIGHT: + g_value_set_uint (value, filter->height); + break; + case PROP_ALGORITHM: + g_value_set_enum (value, filter->algorithm); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstFlowReturn +gst_cvtracker_transform_ip (GstOpencvVideoFilter * base, + GstBuffer * buf, cv::Mat img) +{ + GstCVTracker *filter = GST_OPENCV_TRACKER (base); + GstStructure *s; + GstMessage *msg; + + if (filter->roi.empty ()) { +#if CV_VERSION_MAJOR == 4 && CV_VERSION_MINOR >= 5 && CV_VERSION_REVISION >= 1 + filter->roi = new (cv::Rect); +#else + filter->roi = new (cv::Rect2d); +#endif + filter->roi->x = filter->x; + filter->roi->y = filter->y; + filter->roi->width = filter->width; + filter->roi->height = filter->height; + create_cvtracker (filter); + filter->tracker->init (img, *filter->roi); + } else if (filter->tracker->update (img, *filter->roi)) { +#if (!(CV_VERSION_MAJOR == 4 && CV_VERSION_MINOR >= 5 && CV_VERSION_REVISION >= 1)) + /* Round values to avoid inconsistencies depending on the OpenCV version. */ + filter->roi->x = cvRound (filter->roi->x); + filter->roi->y = cvRound (filter->roi->y); + filter->roi->width = cvRound (filter->roi->width); + filter->roi->height = cvRound (filter->roi->height); +#endif + s = gst_structure_new ("object", + "x", G_TYPE_UINT, (guint) filter->roi->x, + "y", G_TYPE_UINT, (guint) filter->roi->y, + "width", G_TYPE_UINT, (guint) filter->roi->width, + "height", G_TYPE_UINT, (guint) filter->roi->height, NULL); + msg = gst_message_new_element (GST_OBJECT (filter), s); + gst_buffer_add_video_region_of_interest_meta (buf, "object", + filter->roi->x, filter->roi->y, filter->roi->width, + filter->roi->height); + gst_element_post_message (GST_ELEMENT (filter), msg); + if (!(filter->post_debug_info)) + filter->post_debug_info = TRUE; + } else if (filter->post_debug_info) { + GST_DEBUG_OBJECT (filter, "tracker lost"); + filter->post_debug_info = FALSE; + } + + return GST_FLOW_OK; +} diff --git a/ext/opencv/gstcvtracker.h b/ext/opencv/gstcvtracker.h new file mode 100644 index 000000000..b817c86b8 --- /dev/null +++ b/ext/opencv/gstcvtracker.h @@ -0,0 +1,114 @@ +/* + * GStreamer + * Copyright (C) 2020 Vivek R <123vivekr@gmail.com> + * Copyright (C) 2021 Cesar Fabian Orccon Chipana + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_CVTRACKER_H__ +#define __GST_CVTRACKER_H__ + +#include +#include +#include +#include +#include +#if CV_VERSION_MAJOR == 4 && CV_VERSION_MINOR >= 5 && CV_VERSION_REVISION >= 1 +#include +#endif + +G_BEGIN_DECLS + +/* #defines don't like whitespacey bits */ +#define GST_TYPE_OPENCV_TRACKER \ + (gst_cvtracker_get_type()) +#define GST_OPENCV_TRACKER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OPENCV_TRACKER,GstCVTracker)) +#define GST_OPENCV_TRACKER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OPENCV_TRACKER,GstCVTrackerClass)) +#define GST_IS_OPENCV_TRACKER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OPENCV_TRACKER)) +#define GST_IS_OPENCV_TRACKER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OPENCV_TRACKER)) + +typedef struct _GstCVTracker GstCVTracker; +typedef struct _GstCVTrackerClass GstCVTrackerClass; + +struct _GstCVTracker +{ + GstOpencvVideoFilter element; + + guint x; + guint y; + guint width; + guint height; + gint algorithm; + gboolean post_debug_info; + + cv::Ptr tracker; +#if CV_VERSION_MAJOR == 4 && CV_VERSION_MINOR >= 5 && CV_VERSION_REVISION >= 1 + cv::Ptr roi; +#else + cv::Ptr roi; +#endif +}; + +typedef enum { + GST_OPENCV_TRACKER_ALGORITHM_BOOSTING, + GST_OPENCV_TRACKER_ALGORITHM_CSRT, + GST_OPENCV_TRACKER_ALGORITHM_KCF, + GST_OPENCV_TRACKER_ALGORITHM_MEDIANFLOW, + GST_OPENCV_TRACKER_ALGORITHM_MIL, + GST_OPENCV_TRACKER_ALGORITHM_MOSSE, + GST_OPENCV_TRACKER_ALGORITHM_TLD, +} GstOpenCVTrackerAlgorithm; + +struct _GstCVTrackerClass +{ + GstOpencvVideoFilterClass parent_class; +}; + +GType gst_cvtracker_get_type (void); + +GST_ELEMENT_REGISTER_DECLARE (cvtracker); + +G_END_DECLS + +#endif /* __GST_CVTRACKER_H__ */ diff --git a/ext/opencv/gstopencv.cpp b/ext/opencv/gstopencv.cpp index aee54c35b..5407a5a99 100644 --- a/ext/opencv/gstopencv.cpp +++ b/ext/opencv/gstopencv.cpp @@ -44,6 +44,7 @@ #include "gstdewarp.h" #include "gstcameracalibrate.h" #include "gstcameraundistort.h" +#include "gstcvtracker.h" static gboolean @@ -72,6 +73,7 @@ plugin_init (GstPlugin * plugin) ret |= GST_ELEMENT_REGISTER (dewarp, plugin); ret |= GST_ELEMENT_REGISTER (cameracalibrate, plugin); ret |= GST_ELEMENT_REGISTER (cameraundistort, plugin); + ret |= GST_ELEMENT_REGISTER (cvtracker, plugin); return ret; } diff --git a/ext/opencv/meson.build b/ext/opencv/meson.build index f69ea5966..b0bd54e80 100644 --- a/ext/opencv/meson.build +++ b/ext/opencv/meson.build @@ -30,7 +30,8 @@ gstopencv_sources = [ 'camerautils.cpp', 'cameraevent.cpp', 'gstcameracalibrate.cpp', - 'gstcameraundistort.cpp' + 'gstcameraundistort.cpp', + 'gstcvtracker.cpp' ] libopencv_headers = [ @@ -41,6 +42,7 @@ libopencv_headers = [ 'opencv2/objdetect.hpp', 'opencv2/opencv.hpp', 'opencv2/video.hpp', + 'opencv2/tracking.hpp', ] libopencv4_headers = [ @@ -51,6 +53,7 @@ libopencv4_headers = [ 'opencv4/opencv2/objdetect.hpp', 'opencv4/opencv2/opencv.hpp', 'opencv4/opencv2/video.hpp', + 'opencv4/opencv2/tracking.hpp', ] gstopencv_cargs = ['-DGST_HAAR_CASCADES_DIR="@0@"'] @@ -115,7 +118,7 @@ if opencv_found gstopencv = library('gstopencv', gstopencv_sources, cpp_args : gst_plugins_bad_args + gstopencv_cargs + [ '-DGST_USE_UNSTABLE_API' ], - link_args : noseh_link_args, + link_args : [noseh_link_args, '-lopencv_tracking'], include_directories : [configinc, libsinc], dependencies : [gstbase_dep, gstvideo_dep, opencv_dep, gstopencv_dep], install : true, diff --git a/tests/meson.build b/tests/meson.build index 145551993..3364f7ba6 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,6 +1,7 @@ if not get_option('tests').disabled() and gstcheck_dep.found() subdir('check') subdir('icles') + subdir('validate') endif if not get_option('examples').disabled() subdir('examples') diff --git a/tests/validate/meson.build b/tests/validate/meson.build new file mode 100644 index 000000000..fd154e3ea --- /dev/null +++ b/tests/validate/meson.build @@ -0,0 +1,29 @@ +gst_tester = find_program('gst-tester-@0@'.format(api_version), required: get_option('tests')) +if not gst_tester.found() + subdir_done() +endif + +tests = [ + 'opencv/cvtracker' +] + +env = environment() +env.set('GST_PLUGIN_PATH_1_0', meson.build_root(), pluginsdirs) +env.set('GST_PLUGIN_SYSTEM_PATH_1_0', '') +env.set('GST_REGISTRY', '@0@/@1@.registry'.format(meson.current_build_dir(), 'validate')) +env.set('GST_PLUGIN_SCANNER_1_0', gst_plugin_scanner_path) +env.set('GST_PLUGIN_LOADING_WHITELIST', 'gstreamer', 'gst-validate', 'gst-plugins-base', + 'gst-plugins-bad@' + meson.build_root()) + +foreach t: tests + test_dir_name = t.split('/') + test_name = 'validate' + foreach c: test_dir_name + test_name += '.' + c + endforeach + test_env = env + test_env.set('GST_VALIDATE_LOGSDIR', join_paths(meson.current_build_dir(), test_name)) + test_file = join_paths(meson.current_source_dir(), t + '.validatetest') + test(test_name, gst_tester, args: [test_file, '--use-fakesinks'], + env: test_env, timeout : 3 * 60, protocol: 'tap') +endforeach diff --git a/tests/validate/opencv/cvtracker.validatetest b/tests/validate/opencv/cvtracker.validatetest new file mode 100644 index 000000000..7005200fc --- /dev/null +++ b/tests/validate/opencv/cvtracker.validatetest @@ -0,0 +1,8 @@ +meta, + args = { + "videotestsrc pattern=ball animation-mode=frames num-buffers=90 ! video/x-raw,framerate=30/1,width=320,height=240 ! cvtracker name=tracker object-initial-x=135 object-initial-y=95 object-initial-width=50 object-initial-height=50 algorithm=1 ! videoconvert ! $(videosink) sync=true", + }, + configs = { + "$(validateflow), pad=tracker:src, record-buffers=true", + } +crank-clock, repeat=91 \ No newline at end of file diff --git a/tests/validate/opencv/cvtracker/flow-expectations/log-tracker-src-expected b/tests/validate/opencv/cvtracker/flow-expectations/log-tracker-src-expected new file mode 100644 index 000000000..4c8a4dab0 --- /dev/null +++ b/tests/validate/opencv/cvtracker/flow-expectations/log-tracker-src-expected @@ -0,0 +1,94 @@ +event stream-start: GstEventStreamStart, flags=(GstStreamFlags)GST_STREAM_FLAG_NONE, group-id=(uint)1; +event caps: video/x-raw, format=(string)RGB, framerate=(fraction)30/1, height=(int)240, interlace-mode=(string)progressive, multiview-mode=(string)mono, pixel-aspect-ratio=(fraction)1/1, width=(int)320; +event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=none, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:00.000000000 +buffer: pts=0:00:00.000000000, dur=0:00:00.033333333, flags=discont, meta=GstVideoMeta +buffer: pts=0:00:00.033333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=139, y=99, width=50, height=50] +buffer: pts=0:00:00.066666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=144, y=104, width=49, height=49] +buffer: pts=0:00:00.100000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=149, y=108, width=50, height=50] +buffer: pts=0:00:00.133333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=153, y=113, width=49, height=49] +buffer: pts=0:00:00.166666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=157, y=117, width=50, height=50] +buffer: pts=0:00:00.200000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=161, y=121, width=50, height=50] +buffer: pts=0:00:00.233333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=166, y=126, width=49, height=49] +buffer: pts=0:00:00.266666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=171, y=130, width=49, height=49] +buffer: pts=0:00:00.300000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=175, y=135, width=48, height=48] +buffer: pts=0:00:00.333333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=179, y=138, width=49, height=49] +buffer: pts=0:00:00.366666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=183, y=142, width=50, height=50] +buffer: pts=0:00:00.400000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=187, y=146, width=49, height=49] +buffer: pts=0:00:00.433333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=191, y=150, width=50, height=50] +buffer: pts=0:00:00.466666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=194, y=153, width=50, height=50] +buffer: pts=0:00:00.500000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=199, y=157, width=49, height=49] +buffer: pts=0:00:00.533333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=202, y=160, width=50, height=50] +buffer: pts=0:00:00.566666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=206, y=163, width=50, height=50] +buffer: pts=0:00:00.600000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=210, y=166, width=50, height=50] +buffer: pts=0:00:00.633333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=214, y=170, width=49, height=49] +buffer: pts=0:00:00.666666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=218, y=173, width=49, height=49] +buffer: pts=0:00:00.700000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=221, y=176, width=50, height=50] +buffer: pts=0:00:00.733333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=223, y=178, width=51, height=51] +buffer: pts=0:00:00.766666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=228, y=179, width=50, height=50] +buffer: pts=0:00:00.800000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=231, y=183, width=49, height=49] +buffer: pts=0:00:00.833333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=235, y=185, width=50, height=50] +buffer: pts=0:00:00.866666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=237, y=186, width=50, height=50] +buffer: pts=0:00:00.900000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=240, y=188, width=49, height=49] +buffer: pts=0:00:00.933333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=243, y=190, width=50, height=50] +buffer: pts=0:00:00.966666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=246, y=191, width=49, height=49] +buffer: pts=0:00:01.000000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=249, y=192, width=50, height=50] +buffer: pts=0:00:01.033333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=251, y=193, width=49, height=49] +buffer: pts=0:00:01.066666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=254, y=194, width=49, height=49] +buffer: pts=0:00:01.100000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=257, y=195, width=49, height=49] +buffer: pts=0:00:01.133333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=258, y=195, width=50, height=50] +buffer: pts=0:00:01.166666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=260, y=194, width=50, height=50] +buffer: pts=0:00:01.200000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=261, y=195, width=49, height=49] +buffer: pts=0:00:01.233333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=265, y=195, width=49, height=49] +buffer: pts=0:00:01.266666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=265, y=195, width=50, height=50] +buffer: pts=0:00:01.300000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=267, y=194, width=49, height=49] +buffer: pts=0:00:01.333333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=268, y=193, width=50, height=50] +buffer: pts=0:00:01.366666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=269, y=191, width=50, height=50] +buffer: pts=0:00:01.400000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=271, y=191, width=49, height=49] +buffer: pts=0:00:01.433333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=272, y=190, width=49, height=49] +buffer: pts=0:00:01.466666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=274, y=188, width=49, height=49] +buffer: pts=0:00:01.500000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=273, y=186, width=50, height=50] +buffer: pts=0:00:01.533333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=274, y=184, width=49, height=49] +buffer: pts=0:00:01.566666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=274, y=182, width=50, height=50] +buffer: pts=0:00:01.600000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=275, y=179, width=49, height=49] +buffer: pts=0:00:01.633333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=275, y=178, width=49, height=49] +buffer: pts=0:00:01.666666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=275, y=175, width=50, height=50] +buffer: pts=0:00:01.700000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=275, y=172, width=49, height=49] +buffer: pts=0:00:01.733333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=275, y=169, width=49, height=49] +buffer: pts=0:00:01.766666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=276, y=166, width=49, height=49] +buffer: pts=0:00:01.800000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=274, y=164, width=49, height=49] +buffer: pts=0:00:01.833333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=274, y=159, width=50, height=50] +buffer: pts=0:00:01.866666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=273, y=155, width=50, height=50] +buffer: pts=0:00:01.900000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=272, y=152, width=50, height=50] +buffer: pts=0:00:01.933333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=271, y=148, width=50, height=50] +buffer: pts=0:00:01.966666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=270, y=145, width=49, height=49] +buffer: pts=0:00:02.000000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=269, y=141, width=49, height=49] +buffer: pts=0:00:02.033333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=267, y=137, width=50, height=50] +buffer: pts=0:00:02.066666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=265, y=133, width=49, height=49] +buffer: pts=0:00:02.100000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=265, y=129, width=49, height=49] +buffer: pts=0:00:02.133333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=262, y=125, width=49, height=49] +buffer: pts=0:00:02.166666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=260, y=120, width=50, height=50] +buffer: pts=0:00:02.200000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=258, y=116, width=49, height=49] +buffer: pts=0:00:02.233333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=255, y=111, width=50, height=50] +buffer: pts=0:00:02.266666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=253, y=107, width=49, height=49] +buffer: pts=0:00:02.300000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=252, y=103, width=50, height=50] +buffer: pts=0:00:02.333333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=248, y=98, width=49, height=49] +buffer: pts=0:00:02.366666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=246, y=94, width=50, height=50] +buffer: pts=0:00:02.400000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=243, y=89, width=49, height=49] +buffer: pts=0:00:02.433333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=241, y=85, width=49, height=49] +buffer: pts=0:00:02.466666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=237, y=81, width=49, height=49] +buffer: pts=0:00:02.500000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=235, y=77, width=50, height=50] +buffer: pts=0:00:02.533333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=231, y=72, width=49, height=49] +buffer: pts=0:00:02.566666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=228, y=68, width=49, height=49] +buffer: pts=0:00:02.600000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=225, y=63, width=50, height=50] +buffer: pts=0:00:02.633333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=221, y=59, width=49, height=49] +buffer: pts=0:00:02.666666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=218, y=55, width=49, height=49] +buffer: pts=0:00:02.700000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=214, y=51, width=50, height=50] +buffer: pts=0:00:02.733333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=210, y=47, width=49, height=49] +buffer: pts=0:00:02.766666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=207, y=43, width=49, height=49] +buffer: pts=0:00:02.800000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=203, y=40, width=50, height=50] +buffer: pts=0:00:02.833333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=199, y=36, width=49, height=49] +buffer: pts=0:00:02.866666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=195, y=33, width=49, height=49] +buffer: pts=0:00:02.900000000, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=191, y=29, width=49, height=49] +buffer: pts=0:00:02.933333333, dur=0:00:00.033333333, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=188, y=26, width=49, height=49] +buffer: pts=0:00:02.966666666, dur=0:00:00.033333334, meta=GstVideoMeta, GstVideoRegionOfInterestMeta[x=182, y=22, width=50, height=50] +event eos: (no structure) -- cgit v1.2.3