GStreamer Video Analytics (GVA) Plugin
video_frame.h
Go to the documentation of this file.
1 /*******************************************************************************
2  * Copyright (C) 2018-2021 Intel Corporation
3  *
4  * SPDX-License-Identifier: MIT
5  ******************************************************************************/
6 
13 #pragma once
14 
15 #include "region_of_interest.h"
16 
17 #include "metadata/gva_json_meta.h"
19 
20 #include <algorithm>
21 #include <assert.h>
22 #include <functional>
23 #include <memory>
24 #include <stdexcept>
25 #include <string>
26 #include <vector>
27 
28 #include <gst/gstbuffer.h>
29 #include <gst/video/gstvideometa.h>
30 #include <gst/video/video.h>
31 
32 #include <opencv2/opencv.hpp>
33 
34 namespace GVA {
35 
43 class VideoFrame {
44  protected:
49  GstBuffer *buffer;
50 
54  std::unique_ptr<GstVideoInfo, std::function<void(GstVideoInfo *)>> info;
55 
56  public:
63  VideoFrame(GstBuffer *buffer, GstVideoInfo *info)
64  : buffer(buffer), info(gst_video_info_copy(info), gst_video_info_free) {
65  if (not buffer or not info) {
66  throw std::invalid_argument("GVA::VideoFrame: buffer or info nullptr");
67  }
68  }
69 
75  VideoFrame(GstBuffer *buffer, const GstCaps *caps) : buffer(buffer) {
76  if (not buffer or not caps) {
77  throw std::invalid_argument("GVA::VideoFrame: buffer or caps nullptr");
78  }
79  info = std::unique_ptr<GstVideoInfo, std::function<void(GstVideoInfo *)>>(gst_video_info_new(),
80  gst_video_info_free);
81  if (!gst_video_info_from_caps(info.get(), caps)) {
82  throw std::runtime_error("GVA::VideoFrame: gst_video_info_from_caps failed");
83  }
84  }
85 
92  VideoFrame(GstBuffer *buffer) : buffer(buffer) {
93  if (not buffer)
94  throw std::invalid_argument("GVA::VideoFrame: buffer is nullptr");
95 
96  GstVideoMeta *meta = video_meta();
97  if (not meta)
98  throw std::logic_error("GVA::VideoFrame: video_meta() is nullptr");
99 
100  info = std::unique_ptr<GstVideoInfo, std::function<void(GstVideoInfo *)>>(gst_video_info_new(),
101  gst_video_info_free);
102  if (not info.get())
103  throw std::logic_error("GVA::VideoFrame: gst_video_info_new() failed");
104 
105  info->width = meta->width;
106  info->height = meta->height;
107 
108  // Perform secure assignment of buffer similar to memcpy_s
109  if (meta->stride == NULL || sizeof(info->stride) < sizeof(meta->stride)) {
110  memset(info->stride, 0, sizeof(info->stride));
111  throw std::logic_error("GVA::VideoFrame: stride copy failed");
112  }
113 
114  memcpy(info->stride, meta->stride, sizeof(meta->stride));
115  }
116 
121  GstVideoMeta *video_meta() {
122  return gst_buffer_get_video_meta(buffer);
123  }
124 
129  GstVideoInfo *video_info() {
130  return info.get();
131  }
132 
137  std::vector<RegionOfInterest> regions() {
138  return get_regions();
139  }
140 
145  const std::vector<RegionOfInterest> regions() const {
146  return get_regions();
147  }
148 
153  std::vector<Tensor> tensors() {
154  return get_tensors();
155  }
156 
161  const std::vector<Tensor> tensors() const {
162  return get_tensors();
163  }
164 
169  std::vector<std::string> messages() {
170  std::vector<std::string> json_messages;
171  GstGVAJSONMeta *meta = NULL;
172  gpointer state = NULL;
173  GType meta_api_type = g_type_from_name(GVA_JSON_META_API_NAME);
174  while ((meta = (GstGVAJSONMeta *)gst_buffer_iterate_meta_filtered(buffer, &state, meta_api_type))) {
175  json_messages.emplace_back(meta->message);
176  }
177  return json_messages;
178  }
179 
192  RegionOfInterest add_region(double x, double y, double w, double h, std::string label = std::string(),
193  double confidence = 0.0, bool normalized = false) {
194  if (!normalized) {
195  if (info->width == 0 or info->height == 0) {
196  throw std::logic_error("Failed to normalize coordinates width/height equal to 0");
197  }
198  x /= info->width;
199  y /= info->height;
200  w /= info->width;
201  h /= info->height;
202  }
203 
204  clip_normalized_rect(x, y, w, h);
205 
206  // absolute coordinates
207  double _x = x * info->width + 0.5;
208  double _y = y * info->height + 0.5;
209  double _w = w * info->width + 0.5;
210  double _h = h * info->height + 0.5;
211 
212  if (!gst_buffer_is_writable(buffer))
213  throw std::runtime_error("Buffer is not writable.");
214 
215  GstVideoRegionOfInterestMeta *meta = gst_buffer_add_video_region_of_interest_meta(
216  buffer, label.c_str(), double_to_uint(_x), double_to_uint(_y), double_to_uint(_w), double_to_uint(_h));
217 
218  // Add detection tensor
219  GstStructure *detection =
220  gst_structure_new("detection", "x_min", G_TYPE_DOUBLE, x, "x_max", G_TYPE_DOUBLE, x + w, "y_min",
221  G_TYPE_DOUBLE, y, "y_max", G_TYPE_DOUBLE, y + h, NULL);
222  if (confidence) {
223  gst_structure_set(detection, "confidence", G_TYPE_DOUBLE, confidence, NULL);
224  }
225  gst_video_region_of_interest_meta_add_param(meta, detection);
226 
227  return RegionOfInterest(meta);
228  }
229 
235  const GstMetaInfo *meta_info = gst_meta_get_info(GVA_TENSOR_META_IMPL_NAME);
236 
237  if (!gst_buffer_is_writable(buffer))
238  throw std::runtime_error("Buffer is not writable.");
239 
240  GstGVATensorMeta *tensor_meta = (GstGVATensorMeta *)gst_buffer_add_meta(buffer, meta_info, NULL);
241 
242  return Tensor(tensor_meta->data);
243  }
244 
249  void add_message(const std::string &message) {
250  const GstMetaInfo *meta_info = gst_meta_get_info(GVA_JSON_META_IMPL_NAME);
251 
252  if (!gst_buffer_is_writable(buffer))
253  throw std::runtime_error("Buffer is not writable.");
254 
255  GstGVAJSONMeta *json_meta = (GstGVAJSONMeta *)gst_buffer_add_meta(buffer, meta_info, NULL);
256  json_meta->message = g_strdup(message.c_str());
257  }
258 
263  void remove_region(const RegionOfInterest &roi) {
264  if (!gst_buffer_is_writable(buffer))
265  throw std::runtime_error("Buffer is not writable.");
266 
267  if (!gst_buffer_remove_meta(buffer, (GstMeta *)roi._meta())) {
268  throw std::out_of_range("GVA::VideoFrame: RegionOfInterest doesn't belong to this frame");
269  }
270  }
271 
276  void remove_tensor(const Tensor &tensor) {
277  GstGVATensorMeta *meta = NULL;
278  gpointer state = NULL;
279  while ((meta = GST_GVA_TENSOR_META_ITERATE(buffer, &state))) {
280  if (meta->data == tensor._structure) {
281  if (!gst_buffer_is_writable(buffer))
282  throw std::runtime_error("Buffer is not writable.");
283 
284  if (gst_buffer_remove_meta(buffer, (GstMeta *)meta))
285  return;
286  }
287  }
288  throw std::out_of_range("GVA::VideoFrame: Tensor doesn't belong to this frame");
289  }
290 
291  private:
292  void clip_normalized_rect(double &x, double &y, double &w, double &h) {
293  if (!((x >= 0) && (y >= 0) && (w >= 0) && (h >= 0) && (x + w <= 1) && (y + h <= 1))) {
294  GST_DEBUG("ROI coordinates x=[%.5f, %.5f], y=[%.5f, %.5f] are out of range [0,1] and will be clipped", x,
295  x + w, y, y + h);
296 
297  x = (x < 0) ? 0 : (x > 1) ? 1 : x;
298  y = (y < 0) ? 0 : (y > 1) ? 1 : y;
299  w = (w < 0) ? 0 : (w > 1 - x) ? 1 - x : w;
300  h = (h < 0) ? 0 : (h > 1 - y) ? 1 - y : h;
301  }
302  }
303 
304  unsigned int double_to_uint(double val) {
305  unsigned int max = std::numeric_limits<unsigned int>::max();
306  unsigned int min = std::numeric_limits<unsigned int>::min();
307  return (val < min) ? min : ((val > max) ? max : static_cast<unsigned int>(val));
308  }
309 
310  std::vector<RegionOfInterest> get_regions() const {
311  std::vector<RegionOfInterest> regions;
312  GstMeta *meta = NULL;
313  gpointer state = NULL;
314 
315  while ((meta = gst_buffer_iterate_meta_filtered(buffer, &state, GST_VIDEO_REGION_OF_INTEREST_META_API_TYPE)))
316  regions.emplace_back((GstVideoRegionOfInterestMeta *)meta);
317  return regions;
318  }
319 
320  std::vector<Tensor> get_tensors() const {
321  std::vector<Tensor> tensors;
322  GstGVATensorMeta *meta = NULL;
323  gpointer state = NULL;
324  GType meta_api_type = g_type_from_name("GstGVATensorMetaAPI");
325  while ((meta = (GstGVATensorMeta *)gst_buffer_iterate_meta_filtered(buffer, &state, meta_api_type)))
326  tensors.emplace_back(meta->data);
327  return tensors;
328  }
329 };
330 
331 } // namespace GVA
GVA::VideoFrame::buffer
GstBuffer * buffer
GstBuffer with inference results metadata attached (Gstreamer pipeline's GstBuffer,...
Definition: video_frame.h:49
GVA::VideoFrame::VideoFrame
VideoFrame(GstBuffer *buffer, GstVideoInfo *info)
Construct VideoFrame instance from GstBuffer and GstVideoInfo. This is preferred way of creating Vide...
Definition: video_frame.h:63
GVA::VideoFrame::regions
std::vector< RegionOfInterest > regions()
Get RegionOfInterest objects attached to VideoFrame.
Definition: video_frame.h:137
GVA::VideoFrame::remove_region
void remove_region(const RegionOfInterest &roi)
Remove RegionOfInterest.
Definition: video_frame.h:263
GVA::VideoFrame::add_region
RegionOfInterest add_region(double x, double y, double w, double h, std::string label=std::string(), double confidence=0.0, bool normalized=false)
Attach RegionOfInterest to this VideoFrame. This function takes ownership of region_tensor,...
Definition: video_frame.h:192
GVA::VideoFrame::add_message
void add_message(const std::string &message)
Attach message to this VideoFrame.
Definition: video_frame.h:249
GVA::VideoFrame::video_meta
GstVideoMeta * video_meta()
Get video metadata of buffer.
Definition: video_frame.h:121
GVA::VideoFrame::tensors
const std::vector< Tensor > tensors() const
Get Tensor objects attached to VideoFrame.
Definition: video_frame.h:161
region_of_interest.h
This file contains GVA::RegionOfInterest class to control region of interest for particular GVA::Vide...
GVA::VideoFrame::video_info
GstVideoInfo * video_info()
Get GstVideoInfo of this VideoFrame. This is preferrable way of getting any image information.
Definition: video_frame.h:129
GVA::VideoFrame::tensors
std::vector< Tensor > tensors()
Get Tensor objects attached to VideoFrame.
Definition: video_frame.h:153
GVA::VideoFrame::info
std::unique_ptr< GstVideoInfo, std::function< void(GstVideoInfo *)> > info
GstVideoInfo containing actual video information for this VideoFrame.
Definition: video_frame.h:54
GVA::VideoFrame::regions
const std::vector< RegionOfInterest > regions() const
Get RegionOfInterest objects attached to VideoFrame.
Definition: video_frame.h:145
GVA::VideoFrame::VideoFrame
VideoFrame(GstBuffer *buffer)
Construct VideoFrame instance from GstBuffer. Video information will be obtained from buffer....
Definition: video_frame.h:92
GVA::RegionOfInterest::_meta
GstVideoRegionOfInterestMeta * _meta() const
Internal function, don't use or use with caution.
Definition: region_of_interest.h:189
gva_tensor_meta.h
This file contains helper functions to control _GstGVATensorMeta instances.
_GstGVATensorMeta
This struct represents raw tensor metadata and contains instance of parent GstMeta and fields describ...
Definition: gva_tensor_meta.h:64
GVA::VideoFrame::messages
std::vector< std::string > messages()
Get messages attached to this VideoFrame.
Definition: video_frame.h:169
GVA::Tensor::_structure
GstStructure * _structure
ptr to GstStructure that contains all tensor (inference results) data & info.
Definition: tensor.h:358
GVA::RegionOfInterest
This class represents region of interest - object describing detection result (bounding box) and cont...
Definition: region_of_interest.h:43
gva_json_meta.h
This file contains helper functions to control _GstGVAJSONMeta instances.
GVA::VideoFrame::add_tensor
Tensor add_tensor()
Attach empty Tensor to this VideoFrame.
Definition: video_frame.h:234
GVA::Tensor
This class represents tensor - map-like storage for inference result information, such as output blob...
Definition: tensor.h:38
GVA::VideoFrame::VideoFrame
VideoFrame(GstBuffer *buffer, const GstCaps *caps)
Construct VideoFrame instance from GstBuffer and GstCaps.
Definition: video_frame.h:75
GVA::VideoFrame
This class represents video frame - object for working with RegionOfInterest and Tensor objects which...
Definition: video_frame.h:43
GVA::VideoFrame::remove_tensor
void remove_tensor(const Tensor &tensor)
Remove Tensor.
Definition: video_frame.h:276
_GstGVATensorMeta::data
GstStructure * data
Definition: gva_tensor_meta.h:66