From 60cec478d32547fab065ec6667ef243d1163d565 Mon Sep 17 00:00:00 2001 From: Brenno Date: Sat, 12 Dec 2020 20:23:34 -0300 Subject: [PATCH] Refactored KeyframeBBox -Moved the methods that handles protobuf data from Tracker to KeyframeBBox -Displacement and scale keyframes are now properties of KeyframeBBox -Changed interface that updates the KeyframeBBox properties. --- src/EffectBase.h | 8 + src/KeyFrameBBox.cpp | 651 ++++++++++++++++++++------------------- src/KeyFrameBBox.h | 183 ++++++----- src/effects/Tracker.cpp | 221 ++++++------- src/effects/Tracker.h | 52 +--- tests/KeyFrame_Tests.cpp | 53 ++-- 6 files changed, 574 insertions(+), 594 deletions(-) diff --git a/src/EffectBase.h b/src/EffectBase.h index 353e1817..090637e7 100644 --- a/src/EffectBase.h +++ b/src/EffectBase.h @@ -97,6 +97,14 @@ namespace openshot virtual void SetJson(const std::string value) = 0; ///< Load JSON string into this object virtual Json::Value JsonValue() const = 0; ///< Generate Json::Value for this object virtual void SetJsonValue(const Json::Value root) = 0; ///< Load Json::Value into this object + + virtual std::string Json(int64_t requested_frame) const{ + return {}; + }; + virtual void SetJson(int64_t requested_frame, const std::string value) { + return; + }; + Json::Value JsonInfo() const; ///< Generate JSON object of meta data / info /// Get the order that this effect should be executed. diff --git a/src/KeyFrameBBox.cpp b/src/KeyFrameBBox.cpp index 870506a8..2a861014 100644 --- a/src/KeyFrameBBox.cpp +++ b/src/KeyFrameBBox.cpp @@ -30,441 +30,456 @@ #include "KeyFrameBBox.h" #include +#include +#include #include -//#include "Point.h" -//#include + +//#define PI 3.14159265 using namespace std; using namespace openshot; -namespace { - bool IsPointBeforeX(Point const & p, double const x) { - return p.co.X < x; - } +namespace openshot +{ + bool IsPointBeforeX(Point const &p, double const x) + { + return p.co.X < x; + } - double InterpolateLinearCurve(Point const & left, Point const & right, double const target) { - double const diff_Y = right.co.Y - left.co.Y; - double const diff_X = right.co.X - left.co.X; - double const slope = diff_Y / diff_X; - return left.co.Y + slope * (target - left.co.X); - } + double InterpolateLinearCurve(Point const &left, Point const &right, double const target) + { + double const diff_Y = right.co.Y - left.co.Y; + double const diff_X = right.co.X - left.co.X; + double const slope = diff_Y / diff_X; + return left.co.Y + slope * (target - left.co.X); + } - double InterpolateBezierCurve(Point const & left, Point const & right, double const target, double const allowed_error) { - double const X_diff = right.co.X - left.co.X; - double const Y_diff = right.co.Y - left.co.Y; - Coordinate const p0 = left.co; - Coordinate const p1 = Coordinate(p0.X + left.handle_right.X * X_diff, p0.Y + left.handle_right.Y * Y_diff); - Coordinate const p2 = Coordinate(p0.X + right.handle_left.X * X_diff, p0.Y + right.handle_left.Y * Y_diff); - Coordinate const p3 = right.co; + double InterpolateBezierCurve(Point const &left, Point const &right, double const target, double const allowed_error) + { + double const X_diff = right.co.X - left.co.X; + double const Y_diff = right.co.Y - left.co.Y; + Coordinate const p0 = left.co; + Coordinate const p1 = Coordinate(p0.X + left.handle_right.X * X_diff, p0.Y + left.handle_right.Y * Y_diff); + Coordinate const p2 = Coordinate(p0.X + right.handle_left.X * X_diff, p0.Y + right.handle_left.Y * Y_diff); + Coordinate const p3 = right.co; - double t = 0.5; - double t_step = 0.25; - do { - // Bernstein polynoms - double B[4] = {1, 3, 3, 1}; - double oneMinTExp = 1; - double tExp = 1; - for (int i = 0; i < 4; ++i, tExp *= t) { - B[i] *= tExp; - } - for (int i = 0; i < 4; ++i, oneMinTExp *= 1 - t) { - B[4 - i - 1] *= oneMinTExp; - } - double const x = p0.X * B[0] + p1.X * B[1] + p2.X * B[2] + p3.X * B[3]; - double const y = p0.Y * B[0] + p1.Y * B[1] + p2.Y * B[2] + p3.Y * B[3]; - if (fabs(target - x) < allowed_error) { - return y; - } - if (x > target) { - t -= t_step; - } - else { - t += t_step; - } - t_step /= 2; - } while (true); - } + double t = 0.5; + double t_step = 0.25; + do + { + // Bernstein polynoms + double B[4] = {1, 3, 3, 1}; + double oneMinTExp = 1; + double tExp = 1; + for (int i = 0; i < 4; ++i, tExp *= t) + { + B[i] *= tExp; + } + for (int i = 0; i < 4; ++i, oneMinTExp *= 1 - t) + { + B[4 - i - 1] *= oneMinTExp; + } + double const x = p0.X * B[0] + p1.X * B[1] + p2.X * B[2] + p3.X * B[3]; + double const y = p0.Y * B[0] + p1.Y * B[1] + p2.Y * B[2] + p3.Y * B[3]; + if (fabs(target - x) < allowed_error) + { + return y; + } + if (x > target) + { + t -= t_step; + } + else + { + t += t_step; + } + t_step /= 2; + } while (true); + } + double InterpolateBetween(Point const &left, Point const &right, double target, double allowed_error) + { + assert(left.co.X < target); + assert(target <= right.co.X); + switch (right.interpolation) + { + case CONSTANT: + return left.co.Y; + case LINEAR: + return InterpolateLinearCurve(left, right, target); + case BEZIER: + return InterpolateBezierCurve(left, right, target, allowed_error); + } + } - double InterpolateBetween(Point const & left, Point const & right, double target, double allowed_error) { - assert(left.co.X < target); - assert(target <= right.co.X); - switch (right.interpolation) { - case CONSTANT: return left.co.Y; - case LINEAR: return InterpolateLinearCurve(left, right, target); - case BEZIER: return InterpolateBezierCurve(left, right, target, allowed_error); - } - } + template + int64_t SearchBetweenPoints(Point const &left, Point const &right, int64_t const current, Check check) + { + int64_t start = left.co.X; + int64_t stop = right.co.X; + while (start < stop) + { + int64_t const mid = (start + stop + 1) / 2; + double const value = InterpolateBetween(left, right, mid, 0.01); + if (check(round(value), current)) + { + start = mid; + } + else + { + stop = mid - 1; + } + } + return start; + } +} // namespace openshot - - template - int64_t SearchBetweenPoints(Point const & left, Point const & right, int64_t const current, Check check) { - int64_t start = left.co.X; - int64_t stop = right.co.X; - while (start < stop) { - int64_t const mid = (start + stop + 1) / 2; - double const value = InterpolateBetween(left, right, mid, 0.01); - if (check(round(value), current)) { - start = mid; - } else { - stop = mid - 1; - } - } - return start; - } -} - -/* -KeyFrameBBox::KeyFrameBBox(): delta_x(0.0), delta_y(0.0), scale_x(0.0), scale_y(0.0), rotation(0.0) { +// Default Constructor that sets the bounding-box displacement as 0 and the scales as 1 for the first frame +KeyFrameBBox::KeyFrameBBox() : delta_x(0.0), delta_y(0.0), scale_x(1.0), scale_y(1.0), rotation(0.0) +{ this->TimeScale = 1.0; return; } -*/ - -KeyFrameBBox::KeyFrameBBox(){ - this->TimeScale = 1.0; - return; -} -/* -void KeyFrameBBox::AddDisplacement(int64_t frame_num, double _delta_x, double _delta_y){ - if (!this->Contains((int64_t) frame_num)) - return; - - double time = this->FrameNToTime(frame_num, 1.0); - - if (_delta_x != 0.0) - delta_x.AddPoint(time, _delta_x, openshot::InterpolationType::LINEAR); - if (_delta_y != 0.0) - delta_y.AddPoint(time, _delta_y, openshot::InterpolationType::LINEAR); - - return; -} - -void KeyFrameBBox::AddScale(int64_t frame_num, double _scale_x, double _scale_y){ - if (!this->Contains((double) frame_num)) - return; - - double time = this->FrameNToTime(frame_num, 1.0); - - if (_scale_x != 0.0) - scale_x.AddPoint(time, _scale_x, openshot::InterpolationType::LINEAR); - if (_scale_y != 0.0) - scale_y.AddPoint(time, _scale_y, openshot::InterpolationType::LINEAR); - - return; -} - -void KeyFrameBBox::AddRotation(int64_t _frame_num, double rot){ - if (!this->Contains((double) _frame_num)) - return; - - double time = this->FrameNToTime(_frame_num, 1.0); - - rotation.AddPoint(time, rot, openshot::InterpolationType::LINEAR); -} -*/ -void KeyFrameBBox::AddBox(int64_t _frame_num , float _x1, float _y1, float _width, float _height){ - +// Add a BBox to the BoxVec map +void KeyFrameBBox::AddBox(int64_t _frame_num, float _x1, float _y1, float _width, float _height) +{ + // Check if the given frame number is valid if (_frame_num < 0) return; - BBox box = BBox(_x1, _y1, _width, _height); + // Instantiate a new bounding-box + BBox newBBox = BBox(_x1, _y1, _width, _height); + // Get the time of given frame double time = this->FrameNToTime(_frame_num, 1.0); - - auto it = BoxVec.find(time); - if (it != BoxVec.end()) - it->second = box; + // Create an iterator that points to the BoxVec pair indexed by the time of given frame + auto BBoxIterator = BoxVec.find(time); + + if (BBoxIterator != BoxVec.end()) + { + // There is a bounding-box indexed by the time of given frame, update-it + BBoxIterator->second = newBBox; + } else - BoxVec.insert({time, box}); + { + // There isn't a bounding-box indexed by the time of given frame, insert a new one + BoxVec.insert({time, newBBox}); + } } -int64_t KeyFrameBBox::GetLength() const{ - - if (BoxVec.empty()) +// Get the size of BoxVec map +int64_t KeyFrameBBox::GetLength() const +{ + if (BoxVec.empty()) return 0; - if (BoxVec.size() == 1) + if (BoxVec.size() == 1) return 1; - return BoxVec.size(); } -bool KeyFrameBBox::Contains(int64_t frame_num) { - +// Check if there is a bounding-box in the given frame +bool KeyFrameBBox::Contains(int64_t frame_num) +{ + // Get the time of given frame double time = this->FrameNToTime(frame_num, 1.0); - + // Create an iterator that points to the BoxVec pair indexed by the time of given frame (or the closest time) auto it = BoxVec.lower_bound(time); if (it == BoxVec.end()) + // BoxVec pair not found return false; - return true; } -void KeyFrameBBox::RemovePoint(int64_t frame_number){ - +// Remove a bounding-box from the BoxVec map +void KeyFrameBBox::RemoveBox(int64_t frame_number) +{ + // Get the time of given frame double time = this->FrameNToTime(frame_number, 1.0); - + // Create an iterator that points to the BoxVec pair indexed by the time of given frame auto it = BoxVec.find(time); - if (it != BoxVec.end()){ + if (it != BoxVec.end()) + { + // The BoxVec pair exists, so remove it BoxVec.erase(time); - - //RemoveDelta(time); - //RemoveScale(time); } return; } -/* -void KeyFrameBBox::RemoveDelta(int64_t frame_number) { - double time = this->FrameNToTime(frame_number, 1.0); - - double attr_x = this->delta_x.GetValue(time); - Point point_x = this->delta_x.GetClosestPoint(Point((double) time, attr_x)); - if (point_x.co.X == (double) time) - this->delta_x.RemovePoint(point_x); - - double attr_y = this->delta_y.GetValue(time); - Point point_y = this->delta_y.GetClosestPoint(Point((double) time, attr_y)); - if (point_y.co.X == (double) time) - this->delta_y.RemovePoint(point_y); - - - return; -} - -void KeyFrameBBox::RemoveRotation(int64_t frame_number) { - - double time = this->FrameNToTime(frame_number, 1.0); - - double rot = this->rotation.GetValue(time); - Point point_rot = this->rotation.GetClosestPoint(Point((double) time, rot)); - if (point_rot.co.X == (double) time) - this->rotation.RemovePoint(point_rot); - - return; -} - - - -void KeyFrameBBox::PrintParams() { - std::cout << "delta_x "; - this->delta_x.PrintPoints(); - - std::cout << "delta_y "; - this->delta_y.PrintPoints(); - - std::cout << "scale_x "; - this->scale_x.PrintPoints(); - - std::cout << "scale_y "; - this->scale_y.PrintPoints(); - - std::cout << "rotation "; - this->rotation.PrintPoints(); - -} - - -void KeyFrameBBox::RemoveScale(int64_t frame_number) { - - double time = this->FrameNToTime(frame_number, 1.0); - - double attr_x = this->scale_x.GetValue(time); - Point point_x = this->scale_x.GetClosestPoint(Point((double) time, attr_x)); - if (point_x.co.X == (double) time) - this->scale_x.RemovePoint(point_x); - - double attr_y = this->scale_y.GetValue(time); - Point point_y = this->scale_y.GetClosestPoint(Point((double) time, attr_y)); - if (point_y.co.X == (double) time) - this->scale_y.RemovePoint(point_y); - - - return; -} -*/ - -BBox KeyFrameBBox::GetValue(int64_t frame_number){ +// Return a bounding-box from BoxVec with it's properties adjusted by the Keyframes +BBox KeyFrameBBox::GetValue(int64_t frame_number) +{ + // Get the time position of the given frame. double time = this->FrameNToTime(frame_number, this->TimeScale); - auto it = BoxVec.lower_bound(time); - - if (it == BoxVec.end()){ - BBox resp; - return resp; + + // Return a iterator pointing to the BoxVec pair indexed by time or to the pair indexed + // by the closest upper time value. + auto currentBBoxIterator = BoxVec.lower_bound(time); + + // Check if there is a pair indexed by time, returns an empty bbox if there isn't. + if (currentBBoxIterator == BoxVec.end()) + { + // Create and return an empty bounding-box object + BBox emptyBBox; + return emptyBBox; } + + // Check if the iterator matches a BBox indexed by time or points to the first element of BoxVec + if ((currentBBoxIterator->first == time) || (currentBBoxIterator == BoxVec.begin())) + { + // Get the BBox indexed by time + BBox currentBBox = currentBBoxIterator->second; + + // Adjust the BBox properties by the Keyframes values + currentBBox.x1 += this->delta_x.GetValue(frame_number); + currentBBox.y1 += this->delta_y.GetValue(frame_number); + currentBBox.width *= this->scale_x.GetValue(frame_number); + currentBBox.height *= this->scale_y.GetValue(frame_number); - if ((it->first == time) || (it == BoxVec.begin())){ - BBox res = it->second; - /*res.x1 += this->delta_x.GetValue(it->first); - res.y1 += this->delta_y.GetValue(it->first); - res.height += this->scale_y.GetValue(it->first); - res.width += this->scale_x.GetValue(it->first); + /* TODO - Add rotation + (x1,y1) -> current point + (x1',y1') -> transformed point + (xc, yc) -> center of the BBox + (xc, yc) = (x1 + w/2, y1 + h/2) + rot -> rotation angle [radians] + x1' = xc + (x1 - xc)*cos(rot) - (y1-yc)*sin(rot) + y1' = yc + (x1 - xc)*sin(rot) + (y1-yc)*cos(rot) + *** + x1' = (x1 + w/2) + (-w/2)*cos(rot) - (-h/2)*sin(rot) + y1' = (y1 + h/2) + (-w/2)*sin(rot) + (-h/2)*cos(rot) + currentBBox.x1 += currentBBox.width/2 - (currentBBox.width/2)*cos(rot*PI/180) + (currentBBox.height)*sin(rot*PI/180); + currentBBox.y1 += currentBBox.height/2 - (currentBBox.width/2)*sin(rot*PI/180) - (currentBBox.height)*cos(rot*PI/180); */ - return res; + return currentBBox; } - - BBox second_ref = it->second; - BBox first_ref = prev(it, 1)->second; - BBox res = InterpolateBoxes(prev(it, 1)->first, it->first, first_ref, second_ref, time); - - /*later add rotation transform to these points*/ - /* - res.x1 += this->delta_x.GetValue(time); - res.y1 += this->delta_y.GetValue(time); - res.height += this->scale_y.GetValue(time); - res.width += this->scale_x.GetValue(time); - */ - return res; - + // BBox indexed by the closest upper time + BBox currentBBox = currentBBoxIterator->second; + // BBox indexed by the closet lower time + BBox previousBBox = prev(currentBBoxIterator, 1)->second; + // Interpolate a BBox in the middle of previousBBox and currentBBox + BBox interpolatedBBox = InterpolateBoxes(prev(currentBBoxIterator, 1)->first, currentBBoxIterator->first, + previousBBox, currentBBox, time); + + // Adjust the BBox properties by the Keyframes values + interpolatedBBox.x1 += this->delta_x.GetValue(frame_number); + interpolatedBBox.y1 += this->delta_y.GetValue(frame_number); + interpolatedBBox.width *= this->scale_x.GetValue(frame_number); + interpolatedBBox.height *= this->scale_y.GetValue(frame_number); + + return interpolatedBBox; } -BBox KeyFrameBBox::InterpolateBoxes(double t1, double t2, BBox left, BBox right, double target){ +// Interpolate the bouding-boxes properties +BBox KeyFrameBBox::InterpolateBoxes(double t1, double t2, BBox left, BBox right, double target) +{ Point p1_left(t1, left.x1, openshot::InterpolationType::LINEAR); Point p1_right(t2, right.x1, openshot::InterpolationType::LINEAR); - Point p1 = InterpolateBetween(p1_left, p1_right, target, 0.01); - + Point p2_left(t1, left.y1, openshot::InterpolationType::LINEAR); Point p2_right(t2, right.y1, openshot::InterpolationType::LINEAR); - Point p2 = InterpolateBetween(p2_left, p2_right, target, 0.01); - + Point p3_left(t1, left.height, openshot::InterpolationType::LINEAR); Point p3_right(t2, right.height, openshot::InterpolationType::LINEAR); - Point p3 = InterpolateBetween(p3_left, p3_right, target, 0.01); Point p4_left(t1, left.width, openshot::InterpolationType::LINEAR); Point p4_right(t2, right.width, openshot::InterpolationType::LINEAR); - Point p4 = InterpolateBetween(p4_left, p4_right, target, 0.01); - + BBox ans(p1.co.Y, p2.co.Y, p4.co.Y, p3.co.Y); return ans; } - +// Update object's BaseFps void KeyFrameBBox::SetBaseFPS(Fraction fps){ this->BaseFps = fps; return; } +// Return the object's BaseFps Fraction KeyFrameBBox::GetBaseFPS(){ return BaseFps; } +// Get the time of the given frame double KeyFrameBBox::FrameNToTime(int64_t frame_number, double time_scale){ - double time = ((double) frame_number) * this->BaseFps.Reciprocal().ToDouble() * (1.0 / time_scale); + double time = ((double)frame_number) * this->BaseFps.Reciprocal().ToDouble() * (1.0 / time_scale); return time; } +// Update the TimeScale member variable void KeyFrameBBox::ScalePoints(double time_scale){ this->TimeScale = time_scale; } +// Load the bounding-boxes information from the protobuf file +bool KeyFrameBBox::LoadBoxData(std::string inputFilePath) +{ + // Variable to hold the loaded data + libopenshottracker::Tracker bboxMessage; + + // Read the existing tracker message. + fstream input(inputFilePath, ios::in | ios::binary); + + //Check if it was able to read the protobuf data + if (!bboxMessage.ParseFromIstream(&input)) + { + cerr << "Failed to parse protobuf message." << endl; + return false; + } + + this->clear(); + + // Iterate over all frames of the saved message + for (size_t i = 0; i < bboxMessage.frame_size(); i++) + { + // Get data of the i-th frame + const libopenshottracker::Frame &pbFrameData = bboxMessage.frame(i); + + // Get frame number + size_t frame_number = pbFrameData.id(); + + // Get bounding box data from current frame + const libopenshottracker::Frame::Box &box = pbFrameData.bounding_box(); + + float x1 = box.x1(); + float y1 = box.y1(); + float x2 = box.x2(); + float y2 = box.y2(); + + if ( (x1 >= 0.0) && (y1 >= 0.0) && (x2 >= 0.0) && (y2 >= 0.0) ) + { + // The bounding-box properties are valid, so add it to the BoxVec map + this->AddBox(frame_number, x1, y1, (x2 - x1), (y2 - y1)); + } + } + + // Show the time stamp from the last update in tracker data file + if (bboxMessage.has_last_updated()) + { + cout << " Loaded Data. Saved Time Stamp: " << TimeUtil::ToString(bboxMessage.last_updated()) << endl; + } + + // Delete all global objects allocated by libprotobuf. + google::protobuf::ShutdownProtobufLibrary(); + + return true; +} // Generate JSON string of this object -std::string KeyFrameBBox::Json() { - - // Return formatted string - return JsonValue().toStyledString(); +std::string KeyFrameBBox::Json() +{ + // Return formatted string + return JsonValue().toStyledString(); } // Generate Json::Value for this object -Json::Value KeyFrameBBox::JsonValue() { - - // Create root json object - Json::Value root; - //root["Points"] = Json::Value(Json::arrayValue); +Json::Value KeyFrameBBox::JsonValue() +{ + // Create root json object + Json::Value root; root["BaseFPS"]["num"] = BaseFps.num; - root["BaseFPS"]["den"] = BaseFps.den; + root["BaseFPS"]["den"] = BaseFps.den; root["TimeScale"] = this->TimeScale; - root["boxes"] = Json::Value(Json::arrayValue); + root["Boxes"] = Json::Value(Json::arrayValue); - // loop through points - for (auto const& x : BoxVec){ - Json::Value elem; - elem["key"] = x.first; - elem["val"] = x.second.JsonValue(); - root["boxes"].append(elem); + // Loop through the BoxVec map and save the BBox data + for (auto const &x : BoxVec) + { + Json::Value box; + box["time"] = x.first; + box["data"] = x.second.JsonValue(); + root["Boxes"].append(box); } - -/* + // Get the Keyframe's Json strings root["delta_x"] = delta_x.JsonValue(); root["delta_y"] = delta_y.JsonValue(); root["scale_x"] = scale_x.JsonValue(); root["scale_y"] = scale_y.JsonValue(); root["rotation"] = rotation.JsonValue(); -*/ - // return JsonValue - return root; + + // return JsonValue + return root; } // Load JSON string into this object -void KeyFrameBBox::SetJson(const std::string value) { - - // Parse JSON string into JSON objects - try - { - const Json::Value root = openshot::stringToJson(value); - // Set all values that match - SetJsonValue(root); - } - catch (const std::exception& e) - { - // Error parsing JSON (or missing keys) - throw InvalidJSON("JSON is invalid (missing keys or invalid data types)"); - } - - return; +void KeyFrameBBox::SetJson(const std::string value) +{ + // Parse JSON string into JSON objects + try + { + const Json::Value root = openshot::stringToJson(value); + // Set all values that match + SetJsonValue(root); + } + catch (const std::exception &e) + { + // Error parsing JSON (or missing keys) + throw InvalidJSON("JSON is invalid (missing keys or invalid data types)"); + } + return; } -void KeyFrameBBox::clear(){ - BoxVec.clear(); +// Clear the BoxVec map +void KeyFrameBBox::clear() +{ + BoxVec.clear(); } - - // Load Json::Value into this object -void KeyFrameBBox::SetJsonValue(const Json::Value root) { - // Clear existing points - BoxVec.clear(); -/* - delta_x.SetJsonValue(root["delta_x"]); - delta_y.SetJsonValue(root["delta_y"]); - scale_x.SetJsonValue(root["scale_x"]); - scale_y.SetJsonValue(root["scale_y"]); - rotation.SetJsonValue(root["rotation"]); -*/ +void KeyFrameBBox::SetJsonValue(const Json::Value root) +{ + // Clear BoxVec + this->clear(); - if (!root["BaseFPS"].isNull() && root["BaseFPS"].isObject()) { + if (!root["BaseFPS"].isNull() && root["BaseFPS"].isObject()) + { + // Set the BaseFps by the given JSON object if (!root["BaseFPS"]["num"].isNull()) - BaseFps.num = (int) root["BaseFPS"]["num"].asInt(); + BaseFps.num = (int)root["BaseFPS"]["num"].asInt(); if (!root["BaseFPS"]["den"].isNull()) - BaseFps.den = (int) root["BaseFPS"]["den"].asInt(); - } - if (!root["TimeScale"].isNull()) { - double scale = (double) root["TimeScale"].asDouble(); + BaseFps.den = (int)root["BaseFPS"]["den"].asInt(); + } + + if (!root["TimeScale"].isNull()) + { + // Set the TimeScale by the given JSON object + double scale = (double)root["TimeScale"].asDouble(); this->ScalePoints(scale); - } - - if (!root["boxes"].isNull()){ - // loop through points - for (const auto existing_point : root["boxes"]) { - // Create Point + } + + if (!root["Boxes"].isNull()) + { + // Loop through the BBoxes data + for (const auto existing_point : root["Boxes"]) + { + // Insert BBox into the BoxVec map BBox box; - box.SetJsonValue(existing_point["val"]); - - BoxVec.insert({existing_point["key"].asDouble(), box}); + box.SetJsonValue(existing_point["data"]); + //BoxVec.insert({existing_point["time"].asDouble(), box}); + BoxVec[existing_point["time"].asDouble()] = box; } } - + + // Set the Keyframes by the given JSON object + if (!root["delta_x"].isNull()) + delta_x.SetJsonValue(root["delta_x"]); + if (!root["delta_y"].isNull()) + delta_y.SetJsonValue(root["delta_y"]); + if (!root["scale_x"].isNull()) + scale_x.SetJsonValue(root["scale_x"]); + if (!root["scale_y"].isNull()) + scale_y.SetJsonValue(root["scale_y"]); + if (!root["rotation"].isNull()) + rotation.SetJsonValue(root["rotation"]); + return; } \ No newline at end of file diff --git a/src/KeyFrameBBox.h b/src/KeyFrameBBox.h index 9828812f..fb7fc16a 100644 --- a/src/KeyFrameBBox.h +++ b/src/KeyFrameBBox.h @@ -6,7 +6,6 @@ * @ref License */ - /* LICENSE * * Copyright (c) 2008-2019 OpenShot Studios, LLC @@ -44,53 +43,56 @@ #include "Json.h" #include "IKeyFrame.h" #include "KeyFrame.h" +#include "trackerdata.pb.h" +#include +using google::protobuf::util::TimeUtil; - -namespace openshot { - /** - * @brief A Keyframe is a collection of Point instances, which is used to vary a number or property over time. +namespace openshot +{ + /** + * @brief This struct holds the information of a bounding-box: a rectangular shape that enclosures an object or a + * desired set of pixels in a digital image. * - * Keyframes are used to animate and interpolate values of properties over time. For example, a single property - * can use a Keyframe instead of a constant value. Assume you want to slide an image (from left to right) over - * a video. You can create a Keyframe which will adjust the X value of the image over 100 frames (or however many - * frames the animation needs to last) from the value of 0 to 640. - * - * \endcode + * The bounding-box structure holds four floating-point properties: the x and y coordinates of the rectangle's + * top left corner (x1, y1), the rectangle's width and the rectangle's height. */ + struct BBox + { + float x1 = -1; ///< x-coordinate of the top left corner + float y1 = -1; ///< y-coordinate of the top left corner + float width = -1; ///< bounding box width + float height = -1; ///< bounding box height - - struct BBox{ - float x1 = -1; - float y1 = -1; - float width = -1; - float height = -1; - - // Constructors - BBox(){ + /// Blank constructor + BBox() + { return; } - BBox(float _x1, float _y1, float _width, float _height){ - //frame_num = _frame_num; + /// Default constructor, which takes the bounding box top-left corner coordinates, width and height. + /// @param _x1 X-coordinate of the top left corner + /// @param _y1 Y-coordinate of the top left corner + /// @param _width Bounding box width + /// @param _height Bouding box height + BBox(float _x1, float _y1, float _width, float _height) + { x1 = _x1; y1 = _y1; width = _width; height = _height; } - - - std::string Json() const { - // Return formatted string + /// Generate JSON string of this object + std::string Json() const + { return JsonValue().toStyledString(); } - // Generate Json::Value for this object - Json::Value JsonValue() const { - - // Create root json object + /// Generate Json::Value for this object + Json::Value JsonValue() const + { Json::Value root; root["x1"] = x1; root["y1"] = y1; @@ -100,9 +102,9 @@ namespace openshot { return root; } - // Load JSON string into this object - void SetJson(const std::string value) { - + /// Load JSON string into this object + void SetJson(const std::string value) + { // Parse JSON string into JSON objects try { @@ -110,15 +112,16 @@ namespace openshot { // Set all values that match SetJsonValue(root); } - catch (const std::exception& e) + catch (const std::exception &e) { // Error parsing JSON (or missing keys) throw InvalidJSON("JSON is invalid (missing keys or invalid data types)"); } } - // Load Json::Value into this object - void SetJsonValue(const Json::Value root) { + /// Load Json::Value into this object + void SetJsonValue(const Json::Value root) + { // Set data from Json (if key is found) if (!root["x1"].isNull()) @@ -132,69 +135,83 @@ namespace openshot { } }; + /** + * @brief This class holds the information of a bounding-box (mapped by time) over the frames that contain + * the object enclosured by it. + * + * The bounding-box displacement in X and Y directions and it's width and height variation over the frames + * are set as openshot::Keyframe objects + * + * The bounding-box information over the clip's frames are saved into a protobuf file and loaded into an + * object of this class. + */ - class KeyFrameBBox { - private: - bool visible; - Fraction BaseFps; - double TimeScale; - std::map BoxVec; - public: - //Keyframe delta_x; - //Keyframe delta_y; - //Keyframe scale_x; - //Keyframe scale_y; - //Keyframe rotation; - + class KeyFrameBBox + { + private: + bool visible; + Fraction BaseFps; + double TimeScale; + public: + std::map BoxVec; ///< Index the bounding-box by time of each frame + Keyframe delta_x; ///< X-direction displacement Keyframe + Keyframe delta_y; ///< Y-direction displacement Keyframe + Keyframe scale_x; ///< X-direction scale Keyframe + Keyframe scale_y; ///< Y-direction scale Keyframe + Keyframe rotation; ///< Rotation Keyframe + std::string protobufDataPath; ///< Path to the protobuf file that holds the bbox points across the frames - KeyFrameBBox(); - - //void AddDisplacement(int64_t _frame_num, double _delta_x, double _delta_y); - //void AddScale(int64_t _frame_num, double _delta_x, double _delta_y); - void AddBox(int64_t _frame_num, float _x1, float _y1, float _width, float _height); - //void AddRotation(int64_t _frame_num, double rot); + /// Default Constructor + KeyFrameBBox(); - void SetBaseFPS(Fraction fps); - Fraction GetBaseFPS(); + /// Add a BBox to the BoxVec map + void AddBox(int64_t _frame_num, float _x1, float _y1, float _width, float _height); + + /// Update object's BaseFps + void SetBaseFPS(Fraction fps); - void ScalePoints(double scale); + /// Return the object's BaseFps + Fraction GetBaseFPS(); - bool Contains(int64_t frame_number); - //double GetDelta(int64_t index) const ; - int64_t GetLength() const; + /// Update the TimeScale member variable + void ScalePoints(double scale); - /// Remove a points by frame_number - void RemovePoint(int64_t frame_number); - //void RemoveDelta(int64_t frame_number); - //void RemoveScale(int64_t frame_number); - //void RemoveRotation(int64_t frame_number); + /// Check if there is a bounding-box in the given frame + bool Contains(int64_t frame_number); - BBox GetValue(int64_t frame_number) const - { - return const_cast(this)->GetValue(frame_number); - } + /// Get the size of BoxVec map + int64_t GetLength() const; - BBox GetValue(int64_t frame_number); - + /// Remove a bounding-box from the BoxVec map + void RemoveBox(int64_t frame_number); - /// Print collection of points - //void PrintParams(); + /// Return a bounding-box from BoxVec with it's properties adjusted by the Keyframes + BBox GetValue(int64_t frame_number) const + { + return const_cast(this)->GetValue(frame_number); + } + BBox GetValue(int64_t frame_number); - double FrameNToTime(int64_t frame_number, double time_scale); - BBox InterpolateBoxes(double t1, double t2, BBox left, BBox right, double target); + /// Load the bounding-boxes information from the protobuf file + bool LoadBoxData(std::string inputFilePath); - /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - Json::Value JsonValue(); ///< Generate Json::Value for this object - void SetJson(const std::string value); ///< Load JSON string into this object - void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object + /// Get the time of the given frame + double FrameNToTime(int64_t frame_number, double time_scale); - void clear(); //clear all fields + /// Interpolate the bouding-boxes properties + BBox InterpolateBoxes(double t1, double t2, BBox left, BBox right, double target); + /// Get and Set JSON methods + std::string Json(); ///< Generate JSON string of this object + Json::Value JsonValue(); ///< Generate Json::Value for this object + void SetJson(const std::string value); ///< Load JSON string into this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object + /// Clear the BoxVec map + void clear(); }; -} +} // namespace openshot #endif \ No newline at end of file diff --git a/src/effects/Tracker.cpp b/src/effects/Tracker.cpp index 17e23461..d3965ed6 100644 --- a/src/effects/Tracker.cpp +++ b/src/effects/Tracker.cpp @@ -33,20 +33,19 @@ using namespace openshot; /// Blank constructor, useful when using Json to load the effect properties -Tracker::Tracker(std::string clipTrackerDataPath): delta_x(0.0), delta_y(0.0), scale_x(1.0), scale_y(1.0), rotation(0.0) +Tracker::Tracker(std::string clipTrackerDataPath) { // Init effect properties init_effect_details(); - // Tries to load the tracker data from protobuf - LoadTrackedData(clipTrackerDataPath); + // Tries to load the tracked object's data from protobuf file + trackedData.LoadBoxData(clipTrackerDataPath); } // Default constructor -Tracker::Tracker(): delta_x(0.0), delta_y(0.0), scale_x(1.0), scale_y(1.0), rotation(0.0) +Tracker::Tracker() { // Init effect properties init_effect_details(); - } @@ -70,7 +69,6 @@ void Tracker::init_effect_details() // modified openshot::Frame object std::shared_ptr Tracker::GetFrame(std::shared_ptr frame, int64_t frame_number) { - // Get the frame's image cv::Mat frame_image = frame->GetImageCV(); @@ -80,27 +78,18 @@ std::shared_ptr Tracker::GetFrame(std::shared_ptr frame, int64_t f // Check if track data exists for the requested frame if (trackedData.Contains(frame_number)) { + // Get the width and height of the image float fw = frame_image.size().width; float fh = frame_image.size().height; - double scale_x = this->scale_x.GetValue(frame_number); - double scale_y = this->scale_y.GetValue(frame_number); - double delta_x = this->delta_x.GetValue(frame_number); - double delta_y = this->delta_y.GetValue(frame_number); - - // convert to [cx, cy, width, height]. Apply scale and translation + // Get the bounding-box of given frame BBox fd = this->trackedData.GetValue(frame_number); - float cx = fd.x1 + (fd.width/2) + delta_x; - float cy = fd.y1 + (fd.height/2) + delta_y; - float width = fd.width * scale_x; - float height = fd.height * scale_y; - - // Draw box on image - cv::Rect2d box((int)( (cx - (width/2) ) * fw ), - (int)( (cy - (height/2) ) * fh ), - (int)( (width) * fw), - (int)( (height) * fh) ); + // Draw the bounding-box on the image + cv::Rect2d box((int)( (fd.x1 ) * fw ), + (int)( (fd.y1 ) * fh ), + (int)( (fd.width) * fw), + (int)( (fd.height) * fh) ); cv::rectangle(frame_image, box, cv::Scalar( 255, 0, 0 ), 2, 1 ); } } @@ -112,64 +101,6 @@ std::shared_ptr Tracker::GetFrame(std::shared_ptr frame, int64_t f return frame; } -// Load protobuf data file -bool Tracker::LoadTrackedData(std::string inputFilePath){ - // Create tracker message - libopenshottracker::Tracker trackerMessage; - - { - // Read the existing tracker message. - fstream input(inputFilePath, ios::in | ios::binary); - if (!trackerMessage.ParseFromIstream(&input)) { - cerr << "Failed to parse protobuf message." << endl; - return false; - } - } - - // Make sure the trackedData is empty - //trackedDataById.clear(); - trackedData.clear(); - - - // Iterate over all frames of the saved message - for (size_t i = 0; i < trackerMessage.frame_size(); i++) { - const libopenshottracker::Frame& pbFrameData = trackerMessage.frame(i); - - // Load frame and rotation data - size_t id = pbFrameData.id(); - float rotation = pbFrameData.rotation(); - - // Load bounding box data - const libopenshottracker::Frame::Box& box = pbFrameData.bounding_box(); - float x1 = box.x1(); - float y1 = box.y1(); - float x2 = box.x2(); - float y2 = box.y2(); - - // Assign data to tracker map - //trackedDataById[id] = EffectFrameData(id, rotation, x1, y1, x2, y2); - if ((x1 >= 0.0) && (y1 >= 0.0) && (x2 >= 0.0) && (y2 >= 0.0)){ - trackedData.AddBox(id, x1, y1, (x2-x1), (y2-y1)); - //trackedData.AddRotation(id, rotation); - } - } - - // Show the time stamp from the last update in tracker data file - if (trackerMessage.has_last_updated()) { - cout << " Loaded Data. Saved Time Stamp: " << TimeUtil::ToString(trackerMessage.last_updated()) << endl; - } - - // Delete all global objects allocated by libprotobuf. - google::protobuf::ShutdownProtobufLibrary(); - - return true; -} - -// Get tracker info for the desired frame -BBox Tracker::GetTrackedData(size_t frameId){ - return this->trackedData.GetValue(frameId); -} - // Generate JSON string of this object std::string Tracker::Json() const { @@ -187,13 +118,6 @@ Json::Value Tracker::JsonValue() const { root["BaseFPS"]["num"] = BaseFPS.num; root["BaseFPS"]["den"] = BaseFPS.den; root["TimeScale"] = this->TimeScale; - - root["delta_x"] = delta_x.JsonValue(); - root["delta_y"] = delta_y.JsonValue(); - root["scale_x"] = scale_x.JsonValue(); - root["scale_y"] = scale_y.JsonValue(); - root["rotation"] = rotation.JsonValue(); - // return JsonValue return root; } @@ -221,50 +145,45 @@ void Tracker::SetJsonValue(const Json::Value root) { // Set parent data EffectBase::SetJsonValue(root); - if (!root["BaseFPS"].isNull() && root["BaseFPS"].isObject()) { + if (!root["BaseFPS"].isNull() && root["BaseFPS"].isObject()) + { if (!root["BaseFPS"]["num"].isNull()) + { BaseFPS.num = (int) root["BaseFPS"]["num"].asInt(); + } if (!root["BaseFPS"]["den"].isNull()) - BaseFPS.den = (int) root["BaseFPS"]["den"].asInt(); + { + BaseFPS.den = (int) root["BaseFPS"]["den"].asInt(); + } } - if (!root["TimeScale"].isNull()){ + if (!root["TimeScale"].isNull()) TimeScale = (double) root["TimeScale"].asDouble(); - } trackedData.SetBaseFPS(this->BaseFPS); trackedData.ScalePoints(TimeScale); // Set data from Json (if key is found) - if (!root["protobuf_data_path"].isNull()){ + if (!root["protobuf_data_path"].isNull()) + { protobuf_data_path = (root["protobuf_data_path"].asString()); - - if(!LoadTrackedData(protobuf_data_path)){ + if(!trackedData.LoadBoxData(protobuf_data_path)) + { std::cout<<"Invalid protobuf data path"; protobuf_data_path = ""; } } - - if (!root["delta_x"].isNull()) - delta_x.SetJsonValue(root["delta_x"]); - if (!root["delta_y"].isNull()) - delta_y.SetJsonValue(root["delta_y"]); - if (!root["scale_x"].isNull()) - scale_x.SetJsonValue(root["scale_x"]); - if (!root["scale_y"].isNull()) - scale_y.SetJsonValue(root["scale_y"]); - if (!root["rotation"].isNull()) - rotation.SetJsonValue(root["rotation"]); - - } // Get all properties for a specific frame std::string Tracker::PropertiesJSON(int64_t requested_frame) const { - + // Generate JSON properties list Json::Value root; + + // Effect's properties + root["name"] = add_property_json("Tracker", 0.0, "string", "", NULL, -1, -1, true, requested_frame); root["id"] = add_property_json("ID", 0.0, "string", Id(), NULL, -1, -1, true, requested_frame); root["position"] = add_property_json("Position", Position(), "float", "", NULL, 0, 1000 * 60 * 30, false, requested_frame); root["layer"] = add_property_json("Track", Layer(), "int", "", NULL, 0, 20, false, requested_frame); @@ -272,29 +191,73 @@ std::string Tracker::PropertiesJSON(int64_t requested_frame) const { root["end"] = add_property_json("End", End(), "float", "", NULL, 0, 1000 * 60 * 30, false, requested_frame); root["duration"] = add_property_json("Duration", Duration(), "float", "", NULL, 0, 1000 * 60 * 30, true, requested_frame); - // Keyframes - float scale_x_value = this->scale_x.GetValue(requested_frame); - float scale_y_value = this->scale_y.GetValue(requested_frame); - float delta_x_value = this->delta_x.GetValue(requested_frame); - float delta_y_value = this->delta_y.GetValue(requested_frame); - root["delta_x"] = add_property_json("Displacement X-axis", this->delta_x.GetValue(requested_frame), "float", "", &delta_x, -1.0, 1.0, false, requested_frame); - root["delta_y"] = add_property_json("Displacement Y-axis", this->delta_y.GetValue(requested_frame), "float", "", &delta_y, -1.0, 1.0, false, requested_frame); - root["scale_x"] = add_property_json("Scale (Width)", this->scale_x.GetValue(requested_frame), "float", "", &scale_x, 0.0, 1.0, false, requested_frame); - root["scale_y"] = add_property_json("Scale (Height)", this->scale_y.GetValue(requested_frame), "float", "", &scale_y, 0.0, 1.0, false, requested_frame); - root["rotation"] = add_property_json("Rotation", rotation.GetValue(requested_frame), "float", "", &rotation, 0, 360, false, requested_frame); + // Get the bounding-box for the given-frame + BBox fd = trackedData.GetValue(requested_frame); + // Add the data of given frame bounding-box to the JSON object + root["x1"] = add_property_json("X1", fd.x1, "float", "", NULL, 0.0, 1.0, false, requested_frame); + root["y1"] = add_property_json("Y1", fd.y1, "float", "", NULL, 0.0, 1.0, false, requested_frame); + root["x2"] = add_property_json("X2", fd.x1+fd.width, "float", "", NULL, 0.0, 1.0, false, requested_frame); + root["y2"] = add_property_json("Y2", fd.y1+fd.height, "float", "", NULL, 0.0, 1.0, false, requested_frame); - // TODO: use p1, p2 convention instead of [x1, y1, width, height] - BBox fd = this->trackedData.GetValue(requested_frame); - float cx = fd.x1 + (fd.width/2) + delta_x_value; - float cy = fd.y1 + (fd.height/2) + delta_y_value; - float width = fd.width * scale_x_value; - float height = fd.height * scale_y_value; - - root["x1"] = add_property_json("X1", cx-(width/2), "float", "", NULL, 0.0, 1.0, false, requested_frame); - root["y1"] = add_property_json("Y1", cy-(height/2), "float", "", NULL, 0.0, 1.0, false, requested_frame); - root["x2"] = add_property_json("X2", cx+(width/2), "float", "", NULL, 0.0, 1.0, false, requested_frame); - root["y2"] = add_property_json("Y2", cy+(height/2), "float", "", NULL, 0.0, 1.0, false, requested_frame); + // Add the bounding-box Keyframes to the JSON object + root["delta_x"] = add_property_json("Displacement X-axis", trackedData.delta_x.GetValue(requested_frame), "float", "", &trackedData.delta_x, -1.0, 1.0, false, requested_frame); + root["delta_y"] = add_property_json("Displacement Y-axis", trackedData.delta_y.GetValue(requested_frame), "float", "", &trackedData.delta_y, -1.0, 1.0, false, requested_frame); + root["scale_x"] = add_property_json("Scale (Width)", trackedData.scale_x.GetValue(requested_frame), "float", "", &trackedData.scale_x, -1.0, 1.0, false, requested_frame); + root["scale_y"] = add_property_json("Scale (Height)", trackedData.scale_y.GetValue(requested_frame), "float", "", &trackedData.scale_y, -1.0, 1.0, false, requested_frame); + root["rotation"] = add_property_json("Rotation", trackedData.rotation.GetValue(requested_frame), "float", "", &trackedData.rotation, 0, 360, false, requested_frame); // Return formatted string return root.toStyledString(); } + +// Generate JSON string of the trackedData object passing the frame number +std::string Tracker::Json(int64_t requested_frame) const { + + // Generate JSON properties list + Json::Value root; + + // Add the KeyframeBBox class properties to the JSON object + root["type"] = info.class_name; + root["protobuf_data_path"] = protobuf_data_path; + root["BaseFPS"]["num"] = BaseFPS.num; + root["BaseFPS"]["den"] = BaseFPS.den; + root["TimeScale"] = this->TimeScale; + + // Add the bounding-box Keyframes to the JSON object + root["delta_x"] = trackedData.delta_x.JsonValue(); + root["delta_y"] = trackedData.delta_y.JsonValue(); + root["scale_x"] = trackedData.scale_x.JsonValue(); + root["scale_y"] = trackedData.scale_y.JsonValue(); + root["rotation"] = trackedData.rotation.JsonValue(); + + return root.toStyledString(); +} + +// Set the tracketData object properties by a JSON string +void Tracker::SetJson(int64_t requested_frame, const std::string value) +{ + // Parse JSON string into JSON objects + try + { + const Json::Value root = openshot::stringToJson(value); + + // Set all values that match + if (!root["delta_x"].isNull()) + trackedData.delta_x.SetJsonValue(root["delta_x"]); + if (!root["delta_y"].isNull()) + trackedData.delta_y.SetJsonValue(root["delta_y"]); + if (!root["scale_x"].isNull()) + trackedData.scale_x.SetJsonValue(root["scale_x"]); + if (!root["scale_y"].isNull()) + trackedData.scale_y.SetJsonValue(root["scale_y"]); + if (!root["rotation"].isNull()) + trackedData.rotation.SetJsonValue(root["rotation"]); + } + + catch (const std::exception& e) + { + // Error parsing JSON (or missing keys) + throw InvalidJSON("JSON is invalid (missing keys or invalid data types)"); + } + return; +} \ No newline at end of file diff --git a/src/effects/Tracker.h b/src/effects/Tracker.h index 34408a7a..57a8b8b1 100644 --- a/src/effects/Tracker.h +++ b/src/effects/Tracker.h @@ -49,35 +49,6 @@ using namespace std; using google::protobuf::util::TimeUtil; - -// Tracking info struct -struct EffectFrameData{ - size_t frame_id = -1; - float rotation = 0; - float x1 = -1; - float y1 = -1; - float x2 = -1; - float y2 = -1; - - // Constructors - EffectFrameData() - {} - - EffectFrameData( int _frame_id) - {frame_id = _frame_id;} - - EffectFrameData( int _frame_id , float _rotation, float _x1, float _y1, float _x2, float _y2) - { - frame_id = _frame_id; - rotation = _rotation; - x1 = _x1; - y1 = _y1; - x2 = _x2; - y2 = _y2; - } -}; - - namespace openshot { /** @@ -90,20 +61,13 @@ namespace openshot private: /// Init effect settings void init_effect_details(); - std::string protobuf_data_path; + Fraction BaseFPS; double TimeScale; public: - Keyframe delta_x; - Keyframe delta_y; - Keyframe scale_x; - Keyframe scale_y; - Keyframe rotation; - - std::map trackedDataById; // Save object tracking box data - - KeyFrameBBox trackedData; + std::string protobuf_data_path; ///< Path to the protobuf file that holds the bounding-box data + KeyFrameBBox trackedData; ///< Object that holds the bounding-box data and it's Keyframes /// Blank constructor, useful when using Json to load the effect properties Tracker(std::string clipTrackerDataPath); @@ -122,12 +86,6 @@ namespace openshot /// @param frame_number The frame number (starting at 1) of the effect on the timeline. std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } - - // Load protobuf data file - bool LoadTrackedData(std::string inputFilePath); - - // Get tracker info for the desired frame - BBox GetTrackedData(size_t frameId); /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object @@ -138,8 +96,10 @@ namespace openshot /// Get all properties for a specific frame (perfect for a UI to display the current state /// of all properties at any time) std::string PropertiesJSON(int64_t requested_frame) const override; - }; + std::string Json(int64_t requested_frame) const override; ///< Generate JSON string of the trackedData object passing the frame number + void SetJson(int64_t requested_frame, const std::string value) override; ///< Set the tracketData object properties by a JSON string + }; } #endif diff --git a/tests/KeyFrame_Tests.cpp b/tests/KeyFrame_Tests.cpp index c5d5b6da..50ce041b 100644 --- a/tests/KeyFrame_Tests.cpp +++ b/tests/KeyFrame_Tests.cpp @@ -510,9 +510,7 @@ TEST(KeyFrameBBox_addBox_test) { CHECK_EQUAL(true, kfb.Contains(1)); CHECK_EQUAL(1, kfb.GetLength()); - //kfb.PrintParams(); - - kfb.RemovePoint(1); + kfb.RemoveBox(1); CHECK_EQUAL(false, kfb.Contains(1)); CHECK_EQUAL(0, kfb.GetLength()); @@ -524,15 +522,12 @@ TEST(KeyFrameBBox_GetVal_test) { kfb.AddBox(1, 10.0, 10.0, 100.0, 100.0); - //kfb.AddDisplacement(1, 20.0, 20.0); - //kfb.AddScale(1, 30, 30); - BBox val = kfb.GetValue(1); CHECK_EQUAL(10.0, val.x1); CHECK_EQUAL(10.0, val.y1); CHECK_EQUAL(100.0,val.width); - CHECK_EQUAL(100.0, val.height); + CHECK_EQUAL(100.0,val.height); } @@ -543,10 +538,6 @@ TEST(KeyFrameBBox_GetVal_Interpolation) { kfb.AddBox(11, 20.0, 20.0, 100.0, 100.0); kfb.AddBox(21, 30.0, 30.0, 100.0, 100.0); kfb.AddBox(31, 40.0, 40.0, 100.0, 100.0); - - - //kfb.AddDisplacement(1, 20.0, 20.0); - //kfb.AddScale(1, 30, 30); BBox val = kfb.GetValue(5); @@ -575,20 +566,46 @@ TEST(KeyFrameBBox_GetVal_Interpolation) { TEST(KeyFrameBBox_Json_set) { KeyFrameBBox kfb; - kfb.AddBox(0, 10.0, 10.0, 100.0, 100.0); + kfb.AddBox(1, 10.0, 10.0, 100.0, 100.0); kfb.AddBox(10, 20.0, 20.0, 100.0, 100.0); kfb.AddBox(20, 30.0, 30.0, 100.0, 100.0); kfb.AddBox(30, 40.0, 40.0, 100.0, 100.0); + kfb.scale_x.AddPoint(1, 2.0); + kfb.scale_x.AddPoint(10, 3.0); + kfb.SetBaseFPS(Fraction(24.0, 1.0)); - kfb.ScalePoints(1.2); - auto data = kfb.Json(); + + auto dataJSON = kfb.Json(); + KeyFrameBBox fromJSON_kfb; + fromJSON_kfb.SetJson(dataJSON); + + std::cout << fromJSON_kfb.Json() << std::endl; + + CHECK_EQUAL(kfb.GetBaseFPS().num, fromJSON_kfb.GetBaseFPS().num); - KeyFrameBBox from_json; - from_json.SetJson(data); + double time_kfb = kfb.FrameNToTime(1, 1.0); + double time_fromJSON_kfb = fromJSON_kfb.FrameNToTime(1, 1.0); + CHECK_EQUAL(time_kfb, time_fromJSON_kfb); - std::cout << from_json.Json() << std::endl; + BBox kfb_bbox = kfb.BoxVec[time_kfb]; + BBox fromJSON_bbox = fromJSON_kfb.BoxVec[time_fromJSON_kfb]; - CHECK_EQUAL(kfb.GetBaseFPS().num, from_json.GetBaseFPS().num); + CHECK_EQUAL(kfb_bbox.x1, fromJSON_bbox.x1); + CHECK_EQUAL(kfb_bbox.y1, fromJSON_bbox.y1); + CHECK_EQUAL(kfb_bbox.width, fromJSON_bbox.width); + CHECK_EQUAL(kfb_bbox.height, fromJSON_bbox.height); } +TEST(KeyFrameBBox_Scale_test){ + KeyFrameBBox kfb; + + kfb.AddBox(1, 10.0, 10.0, 10.0, 10.0); + kfb.scale_x.AddPoint(1.0, 2.0); + kfb.scale_y.AddPoint(1.0, 3.0); + + BBox bbox = kfb.GetValue(1); + + CHECK_EQUAL(20.0, bbox.width); + CHECK_EQUAL(30.0, bbox.height); +} \ No newline at end of file