OpenShot Library | libopenshot  0.7.0
TrackedObjectBBox.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 <algorithm>
15 #include <cmath>
16 #include <fstream>
17 
18 #include "TrackedObjectBBox.h"
19 
20 #include "Clip.h"
21 #include "TimelineBase.h"
22 
23 #include "trackerdata.pb.h"
24 #include <google/protobuf/util/time_util.h>
25 
26 using google::protobuf::util::TimeUtil;
27 
28 using namespace openshot;
29 
30 namespace {
31 std::vector<uint32_t> encode_object_mask_rle(const std::vector<uint8_t>& mask)
32 {
33  std::vector<uint32_t> rle;
34  uint8_t current = 0;
35  uint32_t count = 0;
36  for (uint8_t value : mask) {
37  value = value ? 1 : 0;
38  if (value == current) {
39  ++count;
40  } else {
41  rle.push_back(count);
42  current = value;
43  count = 1;
44  }
45  }
46  rle.push_back(count);
47  return rle;
48 }
49 
50 std::vector<uint8_t> decode_object_mask_rle(const ObjectMaskData& mask)
51 {
52  std::vector<uint8_t> decoded(static_cast<size_t>(mask.width * mask.height), 0);
53  int offset = 0;
54  bool value = false;
55  for (uint32_t count : mask.rle) {
56  const int end = std::min(mask.width * mask.height, offset + static_cast<int>(count));
57  if (value)
58  std::fill(decoded.begin() + offset, decoded.begin() + end, static_cast<uint8_t>(1));
59  offset = end;
60  value = !value;
61  if (offset >= mask.width * mask.height)
62  break;
63  }
64  return decoded;
65 }
66 
67 ObjectMaskData transform_mask_between_boxes(
68  const ObjectMaskData& source_mask,
69  const BBox& source_box,
70  const BBox& target_box)
71 {
72  ObjectMaskData result;
73  if (!source_mask.HasData() ||
74  source_box.width <= 0.0f || source_box.height <= 0.0f ||
75  target_box.width <= 0.0f || target_box.height <= 0.0f)
76  return result;
77 
78  const float source_left = (source_box.cx - source_box.width / 2.0f) * source_mask.width;
79  const float source_top = (source_box.cy - source_box.height / 2.0f) * source_mask.height;
80  const float source_width = source_box.width * source_mask.width;
81  const float source_height = source_box.height * source_mask.height;
82  const float target_left = (target_box.cx - target_box.width / 2.0f) * source_mask.width;
83  const float target_top = (target_box.cy - target_box.height / 2.0f) * source_mask.height;
84  const float target_width = target_box.width * source_mask.width;
85  const float target_height = target_box.height * source_mask.height;
86  if (source_width <= 0.0f || source_height <= 0.0f || target_width <= 0.0f || target_height <= 0.0f)
87  return result;
88 
89  const std::vector<uint8_t> source = decode_object_mask_rle(source_mask);
90  std::vector<uint8_t> transformed(static_cast<size_t>(source_mask.width * source_mask.height), 0);
91  const int min_x = std::max(0, static_cast<int>(std::floor(target_left)));
92  const int min_y = std::max(0, static_cast<int>(std::floor(target_top)));
93  const int max_x = std::min(source_mask.width, static_cast<int>(std::ceil(target_left + target_width)));
94  const int max_y = std::min(source_mask.height, static_cast<int>(std::ceil(target_top + target_height)));
95  for (int y = min_y; y < max_y; ++y) {
96  for (int x = min_x; x < max_x; ++x) {
97  const float source_x = source_left + (static_cast<float>(x) - target_left) * source_width / target_width;
98  const float source_y = source_top + (static_cast<float>(y) - target_top) * source_height / target_height;
99  const int sx = static_cast<int>(std::round(source_x));
100  const int sy = static_cast<int>(std::round(source_y));
101  if (sx < 0 || sx >= source_mask.width || sy < 0 || sy >= source_mask.height)
102  continue;
103  if (source[static_cast<size_t>(sy * source_mask.width + sx)])
104  transformed[static_cast<size_t>(y * source_mask.width + x)] = 1;
105  }
106  }
107 
108  if (std::none_of(transformed.begin(), transformed.end(), [](uint8_t value) { return value != 0; }))
109  return result;
110  result.width = source_mask.width;
111  result.height = source_mask.height;
112  result.rle = encode_object_mask_rle(transformed);
113  return result;
114 }
115 }
116 
117 // Default Constructor, delegating
119  : TrackedObjectBBox::TrackedObjectBBox(0, 0, 255, 255) {}
120 
121 // Constructor that takes RGBA values for stroke, and sets the bounding-box
122 // displacement as 0 and the scales as 1 for the first frame
123 TrackedObjectBBox::TrackedObjectBBox(int Red, int Green, int Blue, int Alfa)
124  : delta_x(0.0), delta_y(0.0),
125  scale_x(1.0), scale_y(1.0),
126  background_alpha(0.0), background_corner(12),
127  mask_alpha(120.0 / 255.0),
128  stroke_width(2) , stroke_alpha(0.7),
129  stroke(Red, Green, Blue, Alfa),
130  background(Red, Green, Blue, Alfa),
131  mask_color(Red, Green, Blue, Alfa)
132 {
133  this->TimeScale = 1.0;
134 }
135 
136 // Add a BBox to the BoxVec map
137 void TrackedObjectBBox::AddBox(int64_t _frame_num, float _cx, float _cy, float _width, float _height, float _angle)
138 {
139  // Check if the given frame number is valid
140  if (_frame_num < 0)
141  return;
142 
143  // Instantiate a new bounding-box
144  BBox newBBox = BBox(_cx, _cy, _width, _height, _angle);
145 
146  // Get the time of given frame
147  double time = this->FrameNToTime(_frame_num, 1.0);
148  // Create an iterator that points to the BoxVec pair indexed by the time of given frame
149  auto BBoxIterator = BoxVec.find(time);
150 
151  if (BBoxIterator != BoxVec.end())
152  {
153  // There is a bounding-box indexed by the time of given frame, update-it
154  BBoxIterator->second = newBBox;
155  }
156  else
157  {
158  // There isn't a bounding-box indexed by the time of given frame, insert a new one
159  BoxVec.insert({time, newBBox});
160  }
161 }
162 
163 void TrackedObjectBBox::AddMask(int64_t frame_num, const ObjectMaskData& mask)
164 {
165  if (frame_num < 0 || !mask.HasData())
166  return;
167 
168  double time = FrameNToTime(frame_num, 1.0);
169  MaskVec[time] = mask;
170 }
171 
172 bool TrackedObjectBBox::HasMask(int64_t frame_num, int64_t max_frame_gap) const
173 {
174  return GetMask(frame_num, max_frame_gap).HasData();
175 }
176 
178 {
179  return !MaskVec.empty();
180 }
181 
182 ObjectMaskData TrackedObjectBBox::GetMask(int64_t frame_num, int64_t max_frame_gap) const
183 {
184  double time = FrameNToTime(frame_num, 1.0);
185  auto it = MaskVec.find(time);
186  if (it != MaskVec.end())
187  return it->second;
188  if (max_frame_gap <= 0 || MaskVec.empty())
189  return {};
190 
191  auto after = MaskVec.lower_bound(time);
192  if (after == MaskVec.begin())
193  return {};
194 
195  auto before = std::prev(after);
196  double max_gap_time = FrameNToTime(max_frame_gap, 1.0) - FrameNToTime(0, 1.0);
197  if (time - before->first <= max_gap_time + 0.000001) {
198  auto source_box = BoxVec.find(before->first);
199  auto target_box = BoxVec.find(time);
200  if (source_box != BoxVec.end() && target_box != BoxVec.end())
201  return transform_mask_between_boxes(before->second, source_box->second, target_box->second);
202  return before->second;
203  }
204  return {};
205 }
206 
207 // Get the size of BoxVec map
209 {
210  if (BoxVec.empty())
211  return 0;
212  if (BoxVec.size() == 1)
213  return 1;
214  return BoxVec.size();
215 }
216 
217 // Check if there is a bounding-box in the given frame
218 bool TrackedObjectBBox::Contains(int64_t frame_num) const
219 {
220  // Get the time of given frame
221  double time = this->FrameNToTime(frame_num, 1.0);
222  // Create an iterator that points to the BoxVec pair indexed by the time of given frame (or the closest time)
223  auto it = BoxVec.lower_bound(time);
224  if (it == BoxVec.end()){
225  // BoxVec pair not found
226  return false;
227  }
228  return true;
229 }
230 
231 // Check if there is a bounding-box in the exact frame number
232 bool TrackedObjectBBox::ExactlyContains(int64_t frame_number) const
233 {
234  // Get the time of given frame
235  double time = FrameNToTime(frame_number, 1.0);
236  // Create an iterator that points to the BoxVec pair indexed by the exact time of given frame
237  auto it = BoxVec.find(time);
238  if (it == BoxVec.end()){
239  // BoxVec pair not found
240  return false;
241  }
242  return true;
243 }
244 
245 // Remove a bounding-box from the BoxVec map
246 void TrackedObjectBBox::RemoveBox(int64_t frame_number)
247 {
248  // Get the time of given frame
249  double time = this->FrameNToTime(frame_number, 1.0);
250  // Create an iterator that points to the BoxVec pair indexed by the time of given frame
251  auto it = BoxVec.find(time);
252  if (it != BoxVec.end())
253  {
254  // The BoxVec pair exists, so remove it
255  BoxVec.erase(time);
256  }
257  return;
258 }
259 
260 // Return a bounding-box from BoxVec with it's properties adjusted by the Keyframes
261 BBox TrackedObjectBBox::GetBox(int64_t frame_number)
262 {
263  // Get the time position of the given frame.
264  double time = this->FrameNToTime(frame_number, this->TimeScale);
265 
266  // Return a iterator pointing to the BoxVec pair indexed by time or to the pair indexed
267  // by the closest upper time value.
268  auto currentBBoxIterator = BoxVec.lower_bound(time);
269 
270  // Check if there is a pair indexed by time, returns an empty bbox if there isn't.
271  if (currentBBoxIterator == BoxVec.end())
272  {
273  // Create and return an empty bounding-box object
274  BBox emptyBBox;
275  return emptyBBox;
276  }
277 
278  // Check if the iterator matches a BBox indexed by time or points to the first element of BoxVec
279  if ((currentBBoxIterator->first == time) || (currentBBoxIterator == BoxVec.begin()))
280  {
281  // Get the BBox indexed by time
282  BBox currentBBox = currentBBoxIterator->second;
283 
284  // Adjust the BBox properties by the Keyframes values
285  currentBBox.cx += this->delta_x.GetValue(frame_number);
286  currentBBox.cy += this->delta_y.GetValue(frame_number);
287  currentBBox.width *= this->scale_x.GetValue(frame_number);
288  currentBBox.height *= this->scale_y.GetValue(frame_number);
289  return currentBBox;
290  }
291 
292  // BBox indexed by the closest upper time
293  BBox currentBBox = currentBBoxIterator->second;
294  // BBox indexed by the closet lower time
295  BBox previousBBox = prev(currentBBoxIterator, 1)->second;
296 
297  // Interpolate a BBox in the middle of previousBBox and currentBBox
298  BBox interpolatedBBox = InterpolateBoxes(prev(currentBBoxIterator, 1)->first, currentBBoxIterator->first,
299  previousBBox, currentBBox, time);
300 
301  // Adjust the BBox properties by the Keyframes values
302  interpolatedBBox.cx += this->delta_x.GetValue(frame_number);
303  interpolatedBBox.cy += this->delta_y.GetValue(frame_number);
304  interpolatedBBox.width *= this->scale_x.GetValue(frame_number);
305  interpolatedBBox.height *= this->scale_y.GetValue(frame_number);
306  return interpolatedBBox;
307 }
308 
309 double TrackedObjectBBox::ScaledStrokeWidth(int64_t frame_number, int image_width, int image_height) const
310 {
311  const double base_width = std::max(0.0, static_cast<double>(stroke_width.GetValue(frame_number)));
312  Clip* parent_clip = static_cast<Clip*>(ParentClip());
313  if (!parent_clip || image_width <= 0 || image_height <= 0)
314  return base_width;
315 
316  int target_width = image_width;
317  int target_height = image_height;
318  if (parent_clip->ParentTimeline()) {
319  TimelineBase* timeline = static_cast<TimelineBase*>(parent_clip->ParentTimeline());
320  if (timeline->preview_width > 0)
321  target_width = timeline->preview_width;
322  if (timeline->preview_height > 0)
323  target_height = timeline->preview_height;
324  }
325 
326  QSize output_size(image_width, image_height);
327  switch (parent_clip->scale) {
328  case SCALE_FIT:
329  output_size.scale(target_width, target_height, Qt::KeepAspectRatio);
330  break;
331  case SCALE_STRETCH:
332  output_size.scale(target_width, target_height, Qt::IgnoreAspectRatio);
333  break;
334  case SCALE_CROP:
335  output_size.scale(target_width, target_height, Qt::KeepAspectRatioByExpanding);
336  break;
337  case SCALE_NONE:
338  default:
339  break;
340  }
341  if (output_size.width() <= 0 || output_size.height() <= 0)
342  return base_width;
343 
344  const double raster_scale_x = static_cast<double>(output_size.width()) / image_width;
345  const double raster_scale_y = static_cast<double>(output_size.height()) / image_height;
346  const double raster_scale = std::sqrt(raster_scale_x * raster_scale_y);
347  return base_width * std::max(raster_scale, 1.0 / std::max(raster_scale, 0.000001));
348 }
349 
350 // Interpolate the bouding-boxes properties
351 BBox TrackedObjectBBox::InterpolateBoxes(double t1, double t2, BBox left, BBox right, double target)
352 {
353  // Interpolate the x-coordinate of the center point
354  Point cx_left(t1, left.cx, openshot::InterpolationType::LINEAR);
355  Point cx_right(t2, right.cx, openshot::InterpolationType::LINEAR);
356  Point cx = InterpolateBetween(cx_left, cx_right, target, 0.01);
357 
358  // Interpolate de y-coordinate of the center point
359  Point cy_left(t1, left.cy, openshot::InterpolationType::LINEAR);
360  Point cy_right(t2, right.cy, openshot::InterpolationType::LINEAR);
361  Point cy = InterpolateBetween(cy_left, cy_right, target, 0.01);
362 
363  // Interpolate the width
364  Point width_left(t1, left.width, openshot::InterpolationType::LINEAR);
365  Point width_right(t2, right.width, openshot::InterpolationType::LINEAR);
366  Point width = InterpolateBetween(width_left, width_right, target, 0.01);
367 
368  // Interpolate the height
369  Point height_left(t1, left.height, openshot::InterpolationType::LINEAR);
370  Point height_right(t2, right.height, openshot::InterpolationType::LINEAR);
371  Point height = InterpolateBetween(height_left, height_right, target, 0.01);
372 
373  // Interpolate the source bounding-box angle
374  Point angle_left(t1, left.angle, openshot::InterpolationType::LINEAR);
375  Point angle_right(t1, right.angle, openshot::InterpolationType::LINEAR);
376  Point angle = InterpolateBetween(angle_left, angle_right, target, 0.01);
377 
378  // Create a bounding box with the interpolated points
379  BBox interpolatedBox(cx.co.Y, cy.co.Y, width.co.Y, height.co.Y, angle.co.Y);
380 
381  return interpolatedBox;
382 }
383 
384 // Update object's BaseFps
386  this->BaseFps = fps;
387  return;
388 }
389 
390 // Return the object's BaseFps
392  return BaseFps;
393 }
394 
395 // Get the time of the given frame
396 double TrackedObjectBBox::FrameNToTime(int64_t frame_number, double time_scale) const{
397  double time = ((double)frame_number) * this->BaseFps.Reciprocal().ToDouble() * (1.0 / time_scale);
398 
399  return time;
400 }
401 
402 // Update the TimeScale member variable
403 void TrackedObjectBBox::ScalePoints(double time_scale){
404  this->TimeScale = time_scale;
405 }
406 
407 // Load the bounding-boxes information from the protobuf file
408 bool TrackedObjectBBox::LoadBoxData(std::string inputFilePath)
409 {
410  using std::ios;
411 
412  // Variable to hold the loaded data
413  pb_tracker::Tracker bboxMessage;
414 
415  // Read the existing tracker message.
416  std::fstream input(inputFilePath, ios::in | ios::binary);
417 
418  // Check if it was able to read the protobuf data
419  if (!bboxMessage.ParseFromIstream(&input))
420  {
421  std::cerr << "Failed to parse protobuf message." << std::endl;
422  return false;
423  }
424 
425  this->clear();
426 
427  // Iterate over all frames of the saved message
428  for (size_t i = 0; i < bboxMessage.frame_size(); i++)
429  {
430  // Get data of the i-th frame
431  const pb_tracker::Frame &pbFrameData = bboxMessage.frame(i);
432 
433  // Get frame number
434  size_t frame_number = pbFrameData.id();
435 
436  // Get bounding box data from current frame
437  const pb_tracker::Frame::Box &box = pbFrameData.bounding_box();
438 
439  float width = box.x2() - box.x1();
440  float height = box.y2() - box.y1();
441  float cx = box.x1() + width/2;
442  float cy = box.y1() + height/2;
443  float angle = 0.0;
444 
445 
446  if ( (cx >= 0.0) && (cy >= 0.0) && (width >= 0.0) && (height >= 0.0) )
447  {
448  // The bounding-box properties are valid, so add it to the BoxVec map
449  this->AddBox(frame_number, cx, cy, width, height, angle);
450  }
451  }
452 
453  // Show the time stamp from the last update in tracker data file
454  if (bboxMessage.has_last_updated())
455  {
456  std::cout << " Loaded Data. Saved Time Stamp: "
457  << TimeUtil::ToString(bboxMessage.last_updated()) << std::endl;
458  }
459 
460  // Delete all global objects allocated by libprotobuf.
461  google::protobuf::ShutdownProtobufLibrary();
462 
463  return true;
464 }
465 
466 // Clear the BoxVec map
468 {
469  BoxVec.clear();
470  MaskVec.clear();
471 }
472 
473 // Generate JSON string of this object
474 std::string TrackedObjectBBox::Json() const
475 {
476  // Return formatted string
477  return JsonValue().toStyledString();
478 }
479 
480 // Generate Json::Value for this object
481 Json::Value TrackedObjectBBox::JsonValue() const
482 {
483  // Create root json object
484  Json::Value root;
485 
486  // Object's properties
487  root["box_id"] = Id();
488  root["BaseFPS"]["num"] = BaseFps.num;
489  root["BaseFPS"]["den"] = BaseFps.den;
490  root["TimeScale"] = TimeScale;
491 
492  // Keyframe's properties
493  root["delta_x"] = delta_x.JsonValue();
494  root["delta_y"] = delta_y.JsonValue();
495  root["scale_x"] = scale_x.JsonValue();
496  root["scale_y"] = scale_y.JsonValue();
497  root["visible"] = visible.JsonValue();
498  root["draw_box"] = draw_box.JsonValue();
499  root["draw_text"] = draw_text.JsonValue();
500  if (!MaskVec.empty()) {
501  root["draw_mask"] = draw_mask.JsonValue();
502  root["mask_alpha"] = mask_alpha.JsonValue();
503  root["mask_color"] = mask_color.JsonValue();
504  }
505  root["stroke"] = stroke.JsonValue();
506  root["background_alpha"] = background_alpha.JsonValue();
507  root["background_corner"] = background_corner.JsonValue();
508  root["background"] = background.JsonValue();
509  root["stroke_width"] = stroke_width.JsonValue();
510  root["stroke_alpha"] = stroke_alpha.JsonValue();
511 
512  // return JsonValue
513  return root;
514 }
515 
516 // Load JSON string into this object
517 void TrackedObjectBBox::SetJson(const std::string value)
518 {
519  // Parse JSON string into JSON objects
520  try
521  {
522  const Json::Value root = openshot::stringToJson(value);
523  // Set all values that match
524  SetJsonValue(root);
525  }
526  catch (const std::exception &e)
527  {
528  // Error parsing JSON (or missing keys)
529  throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
530  }
531  return;
532 }
533 
534 // Load Json::Value into this object
535 void TrackedObjectBBox::SetJsonValue(const Json::Value root)
536 {
537 
538  // Set the Id by the given JSON object
539  if (!root["box_id"].isNull() && root["box_id"].asString() != "")
540  Id(root["box_id"].asString());
541 
542  // Set the BaseFps by the given JSON object
543  if (!root["BaseFPS"].isNull() && root["BaseFPS"].isObject())
544  {
545  if (!root["BaseFPS"]["num"].isNull())
546  BaseFps.num = (int)root["BaseFPS"]["num"].asInt();
547  if (!root["BaseFPS"]["den"].isNull())
548  BaseFps.den = (int)root["BaseFPS"]["den"].asInt();
549  }
550  // Set the TimeScale by the given JSON object
551  if (!root["TimeScale"].isNull())
552  {
553  double scale = (double)root["TimeScale"].asDouble();
554  this->ScalePoints(scale);
555  }
556  // Set the protobuf data path by the given JSON object
557  if (!root["protobuf_data_path"].isNull())
558  protobufDataPath = root["protobuf_data_path"].asString();
559 
560  // Set the Keyframes by the given JSON object
561  if (!root["delta_x"].isNull())
562  delta_x.SetJsonValue(root["delta_x"]);
563  if (!root["delta_y"].isNull())
564  delta_y.SetJsonValue(root["delta_y"]);
565  if (!root["scale_x"].isNull())
566  scale_x.SetJsonValue(root["scale_x"]);
567  if (!root["scale_y"].isNull())
568  scale_y.SetJsonValue(root["scale_y"]);
569  if (!root["visible"].isNull())
570  visible.SetJsonValue(root["visible"]);
571  if (!root["draw_box"].isNull())
572  draw_box.SetJsonValue(root["draw_box"]);
573  if (!root["draw_text"].isNull())
574  draw_text.SetJsonValue(root["draw_text"]);
575  if (!root["draw_mask"].isNull())
576  draw_mask.SetJsonValue(root["draw_mask"]);
577  if (!root["mask_alpha"].isNull())
578  mask_alpha.SetJsonValue(root["mask_alpha"]);
579  if (!root["mask_color"].isNull())
580  mask_color.SetJsonValue(root["mask_color"]);
581  if (!root["stroke"].isNull())
582  stroke.SetJsonValue(root["stroke"]);
583  if (!root["background_alpha"].isNull())
584  background_alpha.SetJsonValue(root["background_alpha"]);
585  if (!root["background_corner"].isNull())
586  background_corner.SetJsonValue(root["background_corner"]);
587  if (!root["background"].isNull())
588  background.SetJsonValue(root["background"]);
589  if (!root["stroke_width"].isNull())
590  stroke_width.SetJsonValue(root["stroke_width"]);
591  if (!root["stroke_alpha"].isNull())
592  stroke_alpha.SetJsonValue(root["stroke_alpha"]);
593  return;
594 }
595 
596 // Get all properties for a specific frame (perfect for a UI to display the current state
597 // of all properties at any time)
598 Json::Value TrackedObjectBBox::PropertiesJSON(int64_t requested_frame) const
599 {
600  Json::Value root;
601 
602  BBox box = GetBox(requested_frame);
603 
604  // Add the ID of this object to the JSON object
605  root["box_id"] = add_property_json("Box ID", 0.0, "string", Id(), NULL, -1, -1, true, requested_frame);
606 
607  // Add the data of given frame bounding-box to the JSON object
608  root["x1"] = add_property_json("X1", box.cx-(box.width/2), "float", "", NULL, 0.0, 1.0, true, requested_frame);
609  root["y1"] = add_property_json("Y1", box.cy-(box.height/2), "float", "", NULL, 0.0, 1.0, true, requested_frame);
610  root["x2"] = add_property_json("X2", box.cx+(box.width/2), "float", "", NULL, 0.0, 1.0, true, requested_frame);
611  root["y2"] = add_property_json("Y2", box.cy+(box.height/2), "float", "", NULL, 0.0, 1.0, true, requested_frame);
612 
613  // Add the bounding-box Keyframes to the JSON object
614  root["delta_x"] = add_property_json("Displacement X-axis", delta_x.GetValue(requested_frame), "float", "", &delta_x, -1.0, 1.0, false, requested_frame);
615  root["delta_y"] = add_property_json("Displacement Y-axis", delta_y.GetValue(requested_frame), "float", "", &delta_y, -1.0, 1.0, false, requested_frame);
616  root["scale_x"] = add_property_json("Scale (Width)", scale_x.GetValue(requested_frame), "float", "", &scale_x, 0.0, 1.0, false, requested_frame);
617  root["scale_y"] = add_property_json("Scale (Height)", scale_y.GetValue(requested_frame), "float", "", &scale_y, 0.0, 1.0, false, requested_frame);
618  root["visible"] = add_property_json("Visible", visible.GetValue(requested_frame), "int", "", &visible, 0, 1, true, requested_frame);
619 
620  root["draw_box"] = add_property_json("Draw Box", draw_box.GetValue(requested_frame), "int", "", &draw_box, 0, 1, false, requested_frame);
621  root["draw_box"]["choices"].append(add_property_choice_json("Yes", true, draw_box.GetValue(requested_frame)));
622  root["draw_box"]["choices"].append(add_property_choice_json("No", false, draw_box.GetValue(requested_frame)));
623 
624  root["draw_text"] = add_property_json("Draw Text", draw_text.GetValue(requested_frame), "int", "", &draw_text, 0, 1, false, requested_frame);
625  root["draw_text"]["choices"].append(add_property_choice_json("Yes", true, draw_text.GetValue(requested_frame)));
626  root["draw_text"]["choices"].append(add_property_choice_json("No", false, draw_text.GetValue(requested_frame)));
627 
628  if (HasMaskData()) {
629  root["draw_mask"] = add_property_json("Draw Mask", draw_mask.GetValue(requested_frame), "int", "", &draw_mask, 0, 1, false, requested_frame);
630  root["draw_mask"]["choices"].append(add_property_choice_json("Yes", true, draw_mask.GetValue(requested_frame)));
631  root["draw_mask"]["choices"].append(add_property_choice_json("No", false, draw_mask.GetValue(requested_frame)));
632 
633  root["mask_color"] = add_property_json("Mask Color", 0.0, "color", "", NULL, 0, 255, false, requested_frame);
634  root["mask_color"]["red"] = add_property_json("Red", mask_color.red.GetValue(requested_frame), "float", "", &mask_color.red, 0, 255, false, requested_frame);
635  root["mask_color"]["blue"] = add_property_json("Blue", mask_color.blue.GetValue(requested_frame), "float", "", &mask_color.blue, 0, 255, false, requested_frame);
636  root["mask_color"]["green"] = add_property_json("Green", mask_color.green.GetValue(requested_frame), "float", "", &mask_color.green, 0, 255, false, requested_frame);
637  root["mask_alpha"] = add_property_json("Mask Alpha", mask_alpha.GetValue(requested_frame), "float", "", &mask_alpha, 0.0, 1.0, false, requested_frame);
638  }
639 
640  root["stroke"] = add_property_json("Border", 0.0, "color", "", NULL, 0, 255, false, requested_frame);
641  root["stroke"]["red"] = add_property_json("Red", stroke.red.GetValue(requested_frame), "float", "", &stroke.red, 0, 255, false, requested_frame);
642  root["stroke"]["blue"] = add_property_json("Blue", stroke.blue.GetValue(requested_frame), "float", "", &stroke.blue, 0, 255, false, requested_frame);
643  root["stroke"]["green"] = add_property_json("Green", stroke.green.GetValue(requested_frame), "float", "", &stroke.green, 0, 255, false, requested_frame);
644  root["stroke_width"] = add_property_json("Stroke Width", stroke_width.GetValue(requested_frame), "int", "", &stroke_width, 1, 10, false, requested_frame);
645  root["stroke_alpha"] = add_property_json("Stroke alpha", stroke_alpha.GetValue(requested_frame), "float", "", &stroke_alpha, 0.0, 1.0, false, requested_frame);
646 
647  root["background_alpha"] = add_property_json("Background Alpha", background_alpha.GetValue(requested_frame), "float", "", &background_alpha, 0.0, 1.0, false, requested_frame);
648  root["background_corner"] = add_property_json("Background Corner Radius", background_corner.GetValue(requested_frame), "int", "", &background_corner, 0.0, 150.0, false, requested_frame);
649 
650  root["background"] = add_property_json("Background", 0.0, "color", "", NULL, 0, 255, false, requested_frame);
651  root["background"]["red"] = add_property_json("Red", background.red.GetValue(requested_frame), "float", "", &background.red, 0, 255, false, requested_frame);
652  root["background"]["blue"] = add_property_json("Blue", background.blue.GetValue(requested_frame), "float", "", &background.blue, 0, 255, false, requested_frame);
653  root["background"]["green"] = add_property_json("Green", background.green.GetValue(requested_frame), "float", "", &background.green, 0, 255, false, requested_frame);
654 
655  // Return formatted string
656  return root;
657 }
658 
659 
660 // Generate JSON for a property
661 Json::Value TrackedObjectBBox::add_property_json(std::string name, float value, std::string type, std::string memo, const Keyframe* keyframe, float min_value, float max_value, bool readonly, int64_t requested_frame) const {
662 
663  // Requested Point
664  const Point requested_point(requested_frame, requested_frame);
665 
666  // Create JSON Object
667  Json::Value prop = Json::Value(Json::objectValue);
668  prop["name"] = name;
669  prop["value"] = value;
670  prop["memo"] = memo;
671  prop["type"] = type;
672  prop["min"] = min_value;
673  prop["max"] = max_value;
674  if (keyframe) {
675  prop["keyframe"] = keyframe->Contains(requested_point);
676  prop["points"] = int(keyframe->GetCount());
677  Point closest_point = keyframe->GetClosestPoint(requested_point);
678  prop["interpolation"] = closest_point.interpolation;
679  prop["closest_point_x"] = closest_point.co.X;
680  prop["previous_point_x"] = keyframe->GetPreviousPoint(closest_point).co.X;
681  }
682  else {
683  prop["keyframe"] = false;
684  prop["points"] = 0;
685  prop["interpolation"] = CONSTANT;
686  prop["closest_point_x"] = -1;
687  prop["previous_point_x"] = -1;
688  }
689 
690  prop["readonly"] = readonly;
691  prop["choices"] = Json::Value(Json::arrayValue);
692 
693  // return JsonValue
694  return prop;
695 }
696 
697 // Return a map that contains the bounding box properties and it's keyframes indexed by their names
698 std::map<std::string, float> TrackedObjectBBox::GetBoxValues(int64_t frame_number) const {
699 
700  // Create the map
701  std::map<std::string, float> boxValues;
702 
703  // Get bounding box of the current frame
704  BBox box = GetBox(frame_number);
705 
706  // Save the bounding box properties
707  boxValues["cx"] = box.cx;
708  boxValues["cy"] = box.cy;
709  boxValues["w"] = box.width;
710  boxValues["h"] = box.height;
711  boxValues["ang"] = box.angle;
712 
713  // Save the keyframes values
714  boxValues["sx"] = this->scale_x.GetValue(frame_number);
715  boxValues["sy"] = this->scale_y.GetValue(frame_number);
716  boxValues["dx"] = this->delta_x.GetValue(frame_number);
717  boxValues["dy"] = this->delta_y.GetValue(frame_number);
718  return boxValues;
719 }
openshot::stringToJson
const Json::Value stringToJson(const std::string value)
Definition: Json.cpp:16
openshot::TrackedObjectBBox::PropertiesJSON
Json::Value PropertiesJSON(int64_t requested_frame) const override
Definition: TrackedObjectBBox.cpp:598
TimelineBase.h
Header file for Timeline class.
openshot::TrackedObjectBBox::stroke_alpha
Keyframe stroke_alpha
Stroke box opacity.
Definition: TrackedObjectBBox.h:156
openshot::ObjectMaskData::height
int height
Definition: TrackedObjectBBox.h:30
openshot::ObjectMaskData::width
int width
Definition: TrackedObjectBBox.h:29
openshot::TrackedObjectBBox::AddBox
void AddBox(int64_t _frame_num, float _cx, float _cy, float _width, float _height, float _angle) override
Add a BBox to the BoxVec map.
Definition: TrackedObjectBBox.cpp:137
openshot::TrackedObjectBBox::HasMask
bool HasMask(int64_t frame_number, int64_t max_frame_gap=0) const
Definition: TrackedObjectBBox.cpp:172
openshot::Coordinate::Y
double Y
The Y value of the coordinate (usually representing the value of the property being animated)
Definition: Coordinate.h:41
openshot::TimelineBase::preview_width
int preview_width
Optional preview width of timeline image. If your preview window is smaller than the timeline,...
Definition: TimelineBase.h:44
Clip.h
Header file for Clip class.
openshot::BBox::height
float height
bounding box height
Definition: TrackedObjectBBox.h:51
openshot::TrackedObjectBBox::Contains
bool Contains(int64_t frame_number) const
Check if there is a bounding-box in the given frame.
Definition: TrackedObjectBBox.cpp:218
openshot::TrackedObjectBBox::JsonValue
Json::Value JsonValue() const override
Generate Json::Value for this object.
Definition: TrackedObjectBBox.cpp:481
openshot::TrackedObjectBBox::background
Color background
Background fill color.
Definition: TrackedObjectBBox.h:158
openshot::TrackedObjectBBox::SetJsonValue
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
Definition: TrackedObjectBBox.cpp:535
openshot::Point::interpolation
InterpolationType interpolation
This is the interpolation mode.
Definition: Point.h:69
openshot
This namespace is the default namespace for all code in the openshot library.
Definition: AnimatedCurve.h:24
openshot::TimelineBase::preview_height
int preview_height
Optional preview width of timeline image. If your preview window is smaller than the timeline,...
Definition: TimelineBase.h:45
openshot::Point::co
Coordinate co
This is the primary coordinate.
Definition: Point.h:66
openshot::TrackedObjectBBox::add_property_json
Json::Value add_property_json(std::string name, float value, std::string type, std::string memo, const Keyframe *keyframe, float min_value, float max_value, bool readonly, int64_t requested_frame) const
Definition: TrackedObjectBBox.cpp:661
openshot::TrackedObjectBBox::FrameNToTime
double FrameNToTime(int64_t frame_number, double time_scale) const
Get the time of the given frame.
Definition: TrackedObjectBBox.cpp:396
openshot::Clip
This class represents a clip (used to arrange readers on the timeline)
Definition: Clip.h:89
openshot::Fraction
This class represents a fraction.
Definition: Fraction.h:30
openshot::BBox::cy
float cy
y-coordinate of the bounding box center
Definition: TrackedObjectBBox.h:49
openshot::TrackedObjectBBox::ScalePoints
void ScalePoints(double scale) override
Update the TimeScale member variable.
Definition: TrackedObjectBBox.cpp:403
openshot::TrackedObjectBase::draw_mask
Keyframe draw_mask
Keyframe to determine if a specific object mask is drawn (when mask data exists)
Definition: TrackedObjectBase.h:52
openshot::Clip::ParentTimeline
void ParentTimeline(openshot::TimelineBase *new_timeline) override
Set associated Timeline pointer.
Definition: Clip.cpp:450
openshot::Keyframe::SetJsonValue
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: KeyFrame.cpp:372
openshot::ObjectMaskData::HasData
bool HasData() const
Definition: TrackedObjectBBox.h:33
openshot::TrackedObjectBBox::Json
std::string Json() const override
Get and Set JSON methods.
Definition: TrackedObjectBBox.cpp:474
openshot::TrackedObjectBBox::GetBaseFPS
Fraction GetBaseFPS()
Return the object's BaseFps.
Definition: TrackedObjectBBox.cpp:391
openshot::TrackedObjectBase::add_property_choice_json
Json::Value add_property_choice_json(std::string name, int value, int selected_value) const
Generate JSON choice for a property (dropdown properties)
Definition: TrackedObjectBase.cpp:28
openshot::Keyframe::Contains
bool Contains(Point p) const
Does this keyframe contain a specific point.
Definition: KeyFrame.cpp:184
openshot::TrackedObjectBBox::MaskVec
std::map< double, ObjectMaskData > MaskVec
Index optional object masks by time of each frame.
Definition: TrackedObjectBBox.h:147
openshot::TrackedObjectBase::draw_box
Keyframe draw_box
Keyframe to determine if a specific box is drawn (or hidden)
Definition: TrackedObjectBase.h:46
openshot::TrackedObjectBase::Id
std::string Id() const
Get the id of this object.
Definition: TrackedObjectBase.h:64
openshot::Fraction::ToDouble
double ToDouble() const
Return this fraction as a double (i.e. 1/2 = 0.5)
Definition: Fraction.cpp:40
openshot::TrackedObjectBBox::scale_y
Keyframe scale_y
Y-direction scale Keyframe.
Definition: TrackedObjectBBox.h:151
openshot::Keyframe::JsonValue
Json::Value JsonValue() const
Generate Json::Value for this object.
Definition: KeyFrame.cpp:339
openshot::ObjectMaskData
Definition: TrackedObjectBBox.h:27
openshot::BBox::angle
float angle
bounding box rotation angle [degrees]
Definition: TrackedObjectBBox.h:52
openshot::Fraction::num
int num
Numerator for the fraction.
Definition: Fraction.h:32
openshot::TrackedObjectBase::visible
Keyframe visible
Keyframe to track if a box is visible in the current frame (read-only)
Definition: TrackedObjectBase.h:43
openshot::TrackedObjectBBox
This class contains the properties of a tracked object and functions to manipulate it.
Definition: TrackedObjectBBox.h:139
openshot::Fraction::den
int den
Denominator for the fraction.
Definition: Fraction.h:33
openshot::Keyframe
A Keyframe is a collection of Point instances, which is used to vary a number or property over time.
Definition: KeyFrame.h:53
openshot::Color::SetJsonValue
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: Color.cpp:117
openshot::Fraction::Reciprocal
Fraction Reciprocal() const
Return the reciprocal as a Fraction.
Definition: Fraction.cpp:78
openshot::TrackedObjectBBox::AddMask
void AddMask(int64_t frame_num, const ObjectMaskData &mask)
Definition: TrackedObjectBBox.cpp:163
openshot::InterpolateBetween
double InterpolateBetween(Point const &left, Point const &right, double target, double allowed_error)
Interpolate two points using the right Point's interpolation method.
Definition: KeyFrame.cpp:80
openshot::TrackedObjectBBox::scale_x
Keyframe scale_x
X-direction scale Keyframe.
Definition: TrackedObjectBBox.h:150
openshot::InvalidJSON
Exception for invalid JSON.
Definition: Exceptions.h:223
openshot::TrackedObjectBBox::GetBox
BBox GetBox(int64_t frame_number)
Return a bounding-box from BoxVec with it's properties adjusted by the Keyframes.
Definition: TrackedObjectBBox.cpp:261
openshot::TrackedObjectBBox::TrackedObjectBBox
TrackedObjectBBox()
Default Constructor.
Definition: TrackedObjectBBox.cpp:118
openshot::BBox::width
float width
bounding box width
Definition: TrackedObjectBBox.h:50
openshot::SCALE_CROP
@ SCALE_CROP
Scale the clip until both height and width fill the canvas (cropping the overlap)
Definition: Enums.h:37
openshot::Color::green
openshot::Keyframe green
Curve representing the green value (0 - 255)
Definition: Color.h:31
openshot::TrackedObjectBase::ParentClip
ClipBase * ParentClip() const
Get and set the parentClip of this object.
Definition: TrackedObjectBase.h:68
openshot::TrackedObjectBBox::GetMask
ObjectMaskData GetMask(int64_t frame_number, int64_t max_frame_gap=0) const
Definition: TrackedObjectBBox.cpp:182
openshot::TrackedObjectBBox::InterpolateBoxes
BBox InterpolateBoxes(double t1, double t2, BBox left, BBox right, double target)
Interpolate the bouding-boxes properties.
Definition: TrackedObjectBBox.cpp:351
openshot::TrackedObjectBBox::delta_y
Keyframe delta_y
Y-direction displacement Keyframe.
Definition: TrackedObjectBBox.h:149
openshot::TrackedObjectBBox::RemoveBox
void RemoveBox(int64_t frame_number)
Remove a bounding-box from the BoxVec map.
Definition: TrackedObjectBBox.cpp:246
openshot::TrackedObjectBBox::BoxVec
std::map< double, BBox > BoxVec
Index the bounding-box by time of each frame.
Definition: TrackedObjectBBox.h:146
openshot::TrackedObjectBBox::GetBoxValues
std::map< std::string, float > GetBoxValues(int64_t frame_number) const override
Return a map that contains the bounding box properties and it's keyframes indexed by their names.
Definition: TrackedObjectBBox.cpp:698
openshot::SCALE_FIT
@ SCALE_FIT
Scale the clip until either height or width fills the canvas (with no cropping)
Definition: Enums.h:38
openshot::LINEAR
@ LINEAR
Linear curves are angular, straight lines between two points.
Definition: Point.h:30
openshot::TrackedObjectBBox::mask_color
Color mask_color
Object mask overlay color.
Definition: TrackedObjectBBox.h:159
openshot::Color::JsonValue
Json::Value JsonValue() const
Generate Json::Value for this object.
Definition: Color.cpp:86
openshot::Keyframe::GetClosestPoint
Point GetClosestPoint(Point p) const
Get current point (or closest point to the right) from the X coordinate (i.e. the frame number)
Definition: KeyFrame.cpp:221
openshot::TrackedObjectBBox::delta_x
Keyframe delta_x
X-direction displacement Keyframe.
Definition: TrackedObjectBBox.h:148
openshot::BBox
This struct holds the information of a bounding-box.
Definition: TrackedObjectBBox.h:46
openshot::TrackedObjectBBox::LoadBoxData
bool LoadBoxData(std::string inputFilePath)
Load the bounding-boxes information from the protobuf file.
Definition: TrackedObjectBBox.cpp:408
openshot::TrackedObjectBBox::clear
void clear()
Clear the BoxVec map.
Definition: TrackedObjectBBox.cpp:467
openshot::TrackedObjectBBox::SetBaseFPS
void SetBaseFPS(Fraction fps)
Update object's BaseFps.
Definition: TrackedObjectBBox.cpp:385
openshot::TrackedObjectBBox::SetJson
void SetJson(const std::string value) override
Load JSON string into this object.
Definition: TrackedObjectBBox.cpp:517
openshot::TrackedObjectBBox::stroke
Color stroke
Border line color.
Definition: TrackedObjectBBox.h:157
openshot::TrackedObjectBBox::ExactlyContains
bool ExactlyContains(int64_t frame_number) const override
Check if there is a bounding-box in the exact frame number.
Definition: TrackedObjectBBox.cpp:232
openshot::TimelineBase
This class represents a timeline (used for building generic timeline implementations)
Definition: TimelineBase.h:41
openshot::TrackedObjectBBox::protobufDataPath
std::string protobufDataPath
Path to the protobuf file that holds the bounding box points across the frames.
Definition: TrackedObjectBBox.h:161
openshot::Keyframe::GetCount
int64_t GetCount() const
Get the number of points (i.e. # of points)
Definition: KeyFrame.cpp:424
openshot::CONSTANT
@ CONSTANT
Constant curves jump from their previous position to a new one (with no interpolation).
Definition: Point.h:31
openshot::Clip::scale
openshot::ScaleType scale
The scale determines how a clip should be resized to fit its parent.
Definition: Clip.h:186
openshot::Keyframe::GetPreviousPoint
Point GetPreviousPoint(Point p) const
Get previous point (.
Definition: KeyFrame.cpp:226
TrackedObjectBBox.h
Header file for the TrackedObjectBBox class.
openshot::SCALE_NONE
@ SCALE_NONE
Do not scale the clip.
Definition: Enums.h:40
openshot::TrackedObjectBBox::mask_alpha
Keyframe mask_alpha
Object mask overlay opacity.
Definition: TrackedObjectBBox.h:154
openshot::TrackedObjectBBox::ScaledStrokeWidth
double ScaledStrokeWidth(int64_t frame_number, int image_width, int image_height) const
Return stroke width adjusted for source-to-output raster scaling.
Definition: TrackedObjectBBox.cpp:309
openshot::BBox::cx
float cx
x-coordinate of the bounding box center
Definition: TrackedObjectBBox.h:48
openshot::TrackedObjectBBox::stroke_width
Keyframe stroke_width
Thickness of border line.
Definition: TrackedObjectBBox.h:155
openshot::TrackedObjectBase::draw_text
Keyframe draw_text
Keyframe to determine if a specific object label is drawn (or hidden)
Definition: TrackedObjectBase.h:49
openshot::Color::red
openshot::Keyframe red
Curve representing the red value (0 - 255)
Definition: Color.h:30
openshot::SCALE_STRETCH
@ SCALE_STRETCH
Scale the clip until both height and width fill the canvas (distort to fit)
Definition: Enums.h:39
openshot::TrackedObjectBBox::background_corner
Keyframe background_corner
Radius of rounded corners.
Definition: TrackedObjectBBox.h:153
openshot::ObjectMaskData::rle
std::vector< uint32_t > rle
Definition: TrackedObjectBBox.h:31
openshot::Point
A Point is the basic building block of a key-frame curve.
Definition: Point.h:64
openshot::Color::blue
openshot::Keyframe blue
Curve representing the red value (0 - 255)
Definition: Color.h:32
openshot::TrackedObjectBBox::HasMaskData
bool HasMaskData() const
Definition: TrackedObjectBBox.cpp:177
openshot::Coordinate::X
double X
The X value of the coordinate (usually representing the frame #)
Definition: Coordinate.h:40
openshot::Keyframe::GetValue
double GetValue(int64_t index) const
Get the value at a specific index.
Definition: KeyFrame.cpp:258
openshot::TrackedObjectBBox::background_alpha
Keyframe background_alpha
Background box opacity.
Definition: TrackedObjectBBox.h:152
openshot::TrackedObjectBBox::GetLength
int64_t GetLength() const
Get the size of BoxVec map.
Definition: TrackedObjectBBox.cpp:208