OpenShot Library | libopenshot  0.3.3
Tracker.cpp
Go to the documentation of this file.
1 
10 // Copyright (c) 2008-2019 OpenShot Studios, LLC
11 //
12 // SPDX-License-Identifier: LGPL-3.0-or-later
13 
14 #include <string>
15 #include <memory>
16 #include <iostream>
17 
18 #include "effects/Tracker.h"
19 #include "Exceptions.h"
20 #include "Timeline.h"
21 #include "trackerdata.pb.h"
22 
23 #include <google/protobuf/util/time_util.h>
24 
25 #include <QImage>
26 #include <QPainter>
27 #include <QPen>
28 #include <QBrush>
29 #include <QRectF>
30 
31 using namespace std;
32 using namespace openshot;
33 using google::protobuf::util::TimeUtil;
34 
36 Tracker::Tracker(std::string clipTrackerDataPath)
37 {
38  // Init effect properties
39  init_effect_details();
40  // Instantiate a TrackedObjectBBox object and point to it
41  TrackedObjectBBox trackedDataObject;
42  trackedData = std::make_shared<TrackedObjectBBox>(trackedDataObject);
43  // Tries to load the tracked object's data from protobuf file
44  trackedData->LoadBoxData(clipTrackerDataPath);
45  ClipBase* parentClip = this->ParentClip();
46  trackedData->ParentClip(parentClip);
47  trackedData->Id(std::to_string(0));
48  // Insert TrackedObject with index 0 to the trackedObjects map
49  trackedObjects.insert({0, trackedData});
50 }
51 
52 // Default constructor
53 Tracker::Tracker()
54 {
55  // Init effect properties
56  init_effect_details();
57  // Instantiate a TrackedObjectBBox object and point to it
58  TrackedObjectBBox trackedDataObject;
59  trackedData = std::make_shared<TrackedObjectBBox>(trackedDataObject);
60  ClipBase* parentClip = this->ParentClip();
61  trackedData->ParentClip(parentClip);
62  trackedData->Id(std::to_string(0));
63  // Insert TrackedObject with index 0 to the trackedObjects map
64  trackedObjects.insert({0, trackedData});
65 }
66 
67 
68 // Init effect settings
69 void Tracker::init_effect_details()
70 {
72  InitEffectInfo();
73 
75  info.class_name = "Tracker";
76  info.name = "Tracker";
77  info.description = "Track the selected bounding box through the video.";
78  info.has_audio = false;
79  info.has_video = true;
80  info.has_tracked_object = true;
81 
82  this->TimeScale = 1.0;
83 }
84 
85 // This method is required for all derived classes of EffectBase, and returns a
86 // modified openshot::Frame object
87 std::shared_ptr<Frame> Tracker::GetFrame(std::shared_ptr<Frame> frame, int64_t frame_number) {
88  // Get the frame's QImage
89  std::shared_ptr<QImage> frame_image = frame->GetImage();
90 
91  // Check if frame isn't NULL
92  if(frame_image && !frame_image->isNull() &&
93  trackedData->Contains(frame_number) &&
94  trackedData->visible.GetValue(frame_number) == 1) {
95  QPainter painter(frame_image.get());
96 
97  // Get the bounding-box of the given frame
98  BBox fd = trackedData->GetBox(frame_number);
99 
100  // Create a QRectF for the bounding box
101  QRectF boxRect((fd.cx - fd.width / 2) * frame_image->width(),
102  (fd.cy - fd.height / 2) * frame_image->height(),
103  fd.width * frame_image->width(),
104  fd.height * frame_image->height());
105 
106  // Check if track data exists for the requested frame
107  if (trackedData->draw_box.GetValue(frame_number) == 1) {
108  painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
109 
110  // Get trackedObjectBox keyframes
111  std::vector<int> stroke_rgba = trackedData->stroke.GetColorRGBA(frame_number);
112  int stroke_width = trackedData->stroke_width.GetValue(frame_number);
113  float stroke_alpha = trackedData->stroke_alpha.GetValue(frame_number);
114  std::vector<int> bg_rgba = trackedData->background.GetColorRGBA(frame_number);
115  float bg_alpha = trackedData->background_alpha.GetValue(frame_number);
116  float bg_corner = trackedData->background_corner.GetValue(frame_number);
117 
118  // Set the pen for the border
119  QPen pen(QColor(stroke_rgba[0], stroke_rgba[1], stroke_rgba[2], 255 * stroke_alpha));
120  pen.setWidth(stroke_width);
121  painter.setPen(pen);
122 
123  // Set the brush for the background
124  QBrush brush(QColor(bg_rgba[0], bg_rgba[1], bg_rgba[2], 255 * bg_alpha));
125  painter.setBrush(brush);
126 
127  // Draw the rounded rectangle
128  painter.drawRoundedRect(boxRect, bg_corner, bg_corner);
129  }
130 
131  painter.end();
132  }
133 
134  // No need to set the image back to the frame, as we directly modified the frame's QImage
135  return frame;
136 }
137 
138 // Get the indexes and IDs of all visible objects in the given frame
139 std::string Tracker::GetVisibleObjects(int64_t frame_number) const{
140 
141  // Initialize the JSON objects
142  Json::Value root;
143  root["visible_objects_index"] = Json::Value(Json::arrayValue);
144  root["visible_objects_id"] = Json::Value(Json::arrayValue);
145 
146  // Iterate through the tracked objects
147  for (const auto& trackedObject : trackedObjects){
148  // Get the tracked object JSON properties for this frame
149  Json::Value trackedObjectJSON = trackedObject.second->PropertiesJSON(frame_number);
150  if (trackedObjectJSON["visible"]["value"].asBool()){
151  // Save the object's index and ID if it's visible in this frame
152  root["visible_objects_index"].append(trackedObject.first);
153  root["visible_objects_id"].append(trackedObject.second->Id());
154  }
155  }
156 
157  return root.toStyledString();
158 }
159 
160 // Generate JSON string of this object
161 std::string Tracker::Json() const {
162 
163  // Return formatted string
164  return JsonValue().toStyledString();
165 }
166 
167 // Generate Json::Value for this object
168 Json::Value Tracker::JsonValue() const {
169 
170  // Create root json object
171  Json::Value root = EffectBase::JsonValue(); // get parent properties
172 
173  // Save the effect's properties on root
174  root["type"] = info.class_name;
175  root["protobuf_data_path"] = protobuf_data_path;
176  root["BaseFPS"]["num"] = BaseFPS.num;
177  root["BaseFPS"]["den"] = BaseFPS.den;
178  root["TimeScale"] = this->TimeScale;
179 
180  // Add trackedObjects IDs to JSON
181  Json::Value objects;
182  for (auto const& trackedObject : trackedObjects){
183  Json::Value trackedObjectJSON = trackedObject.second->JsonValue();
184  // add object json
185  objects[trackedObject.second->Id()] = trackedObjectJSON;
186  }
187  root["objects"] = objects;
188 
189  // return JsonValue
190  return root;
191 }
192 
193 // Load JSON string into this object
194 void Tracker::SetJson(const std::string value) {
195 
196  // Parse JSON string into JSON objects
197  try
198  {
199  const Json::Value root = openshot::stringToJson(value);
200  // Set all values that match
201  SetJsonValue(root);
202  }
203  catch (const std::exception& e)
204  {
205  // Error parsing JSON (or missing keys)
206  throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
207  }
208  return;
209 }
210 
211 // Load Json::Value into this object
212 void Tracker::SetJsonValue(const Json::Value root) {
213 
214  // Set parent data
215  EffectBase::SetJsonValue(root);
216 
217  if (!root["BaseFPS"].isNull() && root["BaseFPS"].isObject())
218  {
219  if (!root["BaseFPS"]["num"].isNull())
220  {
221  BaseFPS.num = (int) root["BaseFPS"]["num"].asInt();
222  }
223  if (!root["BaseFPS"]["den"].isNull())
224  {
225  BaseFPS.den = (int) root["BaseFPS"]["den"].asInt();
226  }
227  }
228 
229  if (!root["TimeScale"].isNull())
230  TimeScale = (double) root["TimeScale"].asDouble();
231 
232  // Set data from Json (if key is found)
233  if (!root["protobuf_data_path"].isNull() && protobuf_data_path.size() <= 1)
234  {
235  protobuf_data_path = root["protobuf_data_path"].asString();
236  if(!trackedData->LoadBoxData(protobuf_data_path))
237  {
238  std::clog << "Invalid protobuf data path " << protobuf_data_path << '\n';
239  protobuf_data_path = "";
240  }
241  }
242 
243  if (!root["objects"].isNull()){
244  for (auto const& trackedObject : trackedObjects){
245  std::string obj_id = std::to_string(trackedObject.first);
246  if(!root["objects"][obj_id].isNull()){
247  trackedObject.second->SetJsonValue(root["objects"][obj_id]);
248  }
249  }
250  }
251 
252  // Set the tracked object's ids
253  if (!root["objects_id"].isNull()){
254  for (auto const& trackedObject : trackedObjects){
255  Json::Value trackedObjectJSON;
256  trackedObjectJSON["box_id"] = root["objects_id"][trackedObject.first].asString();
257  trackedObject.second->SetJsonValue(trackedObjectJSON);
258  }
259  }
260 
261  return;
262 }
263 
264 // Get all properties for a specific frame
265 std::string Tracker::PropertiesJSON(int64_t requested_frame) const {
266 
267  // Generate JSON properties list
268  Json::Value root = BasePropertiesJSON(requested_frame);
269 
270  // Add trackedObject properties to JSON
271  Json::Value objects;
272  for (auto const& trackedObject : trackedObjects){
273  Json::Value trackedObjectJSON = trackedObject.second->PropertiesJSON(requested_frame);
274  // add object json
275  objects[trackedObject.second->Id()] = trackedObjectJSON;
276  }
277  root["objects"] = objects;
278 
279  // Return formatted string
280  return root.toStyledString();
281 }
Header file for all Exception classes.
Header file for Timeline class.
Header file for Tracker effect class.
This abstract class is the base class, used by all clips in libopenshot.
Definition: ClipBase.h:33
Exception for invalid JSON.
Definition: Exceptions.h:218
This class contains the properties of a tracked object and functions to manipulate it.
This namespace is the default namespace for all code in the openshot library.
Definition: Compressor.h:29
const Json::Value stringToJson(const std::string value)
Definition: Json.cpp:16
This struct holds the information of a bounding-box.
float cy
y-coordinate of the bounding box center
float height
bounding box height
float cx
x-coordinate of the bounding box center
float width
bounding box width