41 #include <google/protobuf/util/time_util.h>
49 using google::protobuf::util::TimeUtil;
52 Tracker::Tracker(std::string clipTrackerDataPath)
55 init_effect_details();
58 trackedData = std::make_shared<TrackedObjectBBox>(trackedDataObject);
60 trackedData->LoadBoxData(clipTrackerDataPath);
61 ClipBase* parentClip = this->ParentClip();
62 trackedData->ParentClip(parentClip);
63 trackedData->Id(std::to_string(0));
65 trackedObjects.insert({0, trackedData});
72 init_effect_details();
75 trackedData = std::make_shared<TrackedObjectBBox>(trackedDataObject);
76 ClipBase* parentClip = this->ParentClip();
77 trackedData->ParentClip(parentClip);
78 trackedData->Id(std::to_string(0));
80 trackedObjects.insert({0, trackedData});
85 void Tracker::init_effect_details()
91 info.class_name =
"Tracker";
92 info.name =
"Tracker";
93 info.description =
"Track the selected bounding box through the video.";
94 info.has_audio =
false;
95 info.has_video =
true;
96 info.has_tracked_object =
true;
98 this->TimeScale = 1.0;
103 std::shared_ptr<Frame> Tracker::GetFrame(std::shared_ptr<Frame> frame, int64_t frame_number)
106 cv::Mat frame_image = frame->GetImageCV();
111 std::shared_ptr<QImage> childClipImage =
nullptr;
114 if(!frame_image.empty() &&
115 trackedData->Contains(frame_number) &&
116 trackedData->visible.GetValue(frame_number) == 1)
119 float fw = frame_image.size().width;
120 float fh = frame_image.size().height;
123 BBox fd = trackedData->GetBox(frame_number);
126 if (trackedData->draw_box.GetValue(frame_number) == 1)
128 std::vector<int> stroke_rgba = trackedData->stroke.GetColorRGBA(frame_number);
129 int stroke_width = trackedData->stroke_width.GetValue(frame_number);
130 float stroke_alpha = trackedData->stroke_alpha.GetValue(frame_number);
131 std::vector<int> bg_rgba = trackedData->background.GetColorRGBA(frame_number);
132 float bg_alpha = trackedData->background_alpha.GetValue(frame_number);
135 cv::RotatedRect box ( cv::Point2f( (
int)(fd.
cx*fw), (
int)(fd.
cy*fh) ),
136 cv::Size2f( (
int)(fd.
width*fw), (
int)(fd.
height*fh) ),
139 DrawRectangleRGBA(frame_image, box, bg_rgba, bg_alpha, 1,
true);
140 DrawRectangleRGBA(frame_image, box, stroke_rgba, stroke_alpha, stroke_width,
false);
144 if (trackedData->ChildClipId() !=
""){
149 Clip* childClip = parentTimeline->
GetClip(trackedData->ChildClipId());
152 std::shared_ptr<Frame> f(
new Frame(1, frame->GetWidth(), frame->GetHeight(),
"#00000000"));
153 std::shared_ptr<Frame> childClipFrame = childClip->
GetFrame(f, frame_number);
154 childClipImage = childClipFrame->GetImage();
157 boxRect.setRect((
int)((fd.
cx-fd.
width/2)*fw),
169 frame->SetImageCV(frame_image);
174 QImage frameImage = *(frame->GetImage());
177 QPainter painter(&frameImage);
180 painter.drawImage(boxRect, *childClipImage, QRectF(0, 0, frameImage.size().width(), frameImage.size().height()));
183 frame->AddImage(std::make_shared<QImage>(frameImage));
189 void Tracker::DrawRectangleRGBA(cv::Mat &frame_image, cv::RotatedRect box, std::vector<int> color,
float alpha,
int thickness,
bool is_background){
191 cv::Point2f vertices2f[4];
192 box.points(vertices2f);
200 cv::Mat overlayFrame;
201 frame_image.copyTo(overlayFrame);
204 cv::Point vertices[4];
205 for(
int i = 0; i < 4; ++i){
206 vertices[i] = vertices2f[i];}
208 cv::Rect rect = box.boundingRect();
209 cv::fillConvexPoly(overlayFrame, vertices, 4, cv::Scalar(color[2],color[1],color[0]), cv::LINE_AA);
211 cv::addWeighted(overlayFrame, 1-alpha, frame_image, alpha, 0, frame_image);
214 cv::Mat overlayFrame;
215 frame_image.copyTo(overlayFrame);
218 for (
int i = 0; i < 4; i++)
220 cv::line(overlayFrame, vertices2f[i], vertices2f[(i+1)%4], cv::Scalar(color[2],color[1],color[0]),
221 thickness, cv::LINE_AA);
225 cv::addWeighted(overlayFrame, 1-alpha, frame_image, alpha, 0, frame_image);
230 std::string Tracker::GetVisibleObjects(int64_t frame_number)
const{
234 root[
"visible_objects_index"] = Json::Value(Json::arrayValue);
235 root[
"visible_objects_id"] = Json::Value(Json::arrayValue);
238 for (
const auto& trackedObject : trackedObjects){
240 Json::Value trackedObjectJSON = trackedObject.second->PropertiesJSON(frame_number);
241 if (trackedObjectJSON[
"visible"][
"value"].asBool()){
243 root[
"visible_objects_index"].append(trackedObject.first);
244 root[
"visible_objects_id"].append(trackedObject.second->Id());
248 return root.toStyledString();
252 std::string Tracker::Json()
const {
255 return JsonValue().toStyledString();
259 Json::Value Tracker::JsonValue()
const {
262 Json::Value root = EffectBase::JsonValue();
265 root[
"type"] = info.class_name;
266 root[
"protobuf_data_path"] = protobuf_data_path;
267 root[
"BaseFPS"][
"num"] = BaseFPS.num;
268 root[
"BaseFPS"][
"den"] = BaseFPS.den;
269 root[
"TimeScale"] = this->TimeScale;
273 for (
auto const& trackedObject : trackedObjects){
274 Json::Value trackedObjectJSON = trackedObject.second->JsonValue();
276 objects[trackedObject.second->Id()] = trackedObjectJSON;
278 root[
"objects"] = objects;
285 void Tracker::SetJson(
const std::string value) {
294 catch (
const std::exception& e)
297 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
303 void Tracker::SetJsonValue(
const Json::Value root) {
306 EffectBase::SetJsonValue(root);
308 if(!root[
"type"].isNull())
309 info.class_name = root[
"type"].asString();
311 if (!root[
"BaseFPS"].isNull() && root[
"BaseFPS"].isObject())
313 if (!root[
"BaseFPS"][
"num"].isNull())
315 BaseFPS.num = (int) root[
"BaseFPS"][
"num"].asInt();
317 if (!root[
"BaseFPS"][
"den"].isNull())
319 BaseFPS.den = (int) root[
"BaseFPS"][
"den"].asInt();
323 if (!root[
"TimeScale"].isNull())
324 TimeScale = (
double) root[
"TimeScale"].asDouble();
327 if (!root[
"protobuf_data_path"].isNull() && protobuf_data_path.size() <= 1)
329 protobuf_data_path = root[
"protobuf_data_path"].asString();
330 if(!trackedData->LoadBoxData(protobuf_data_path))
332 std::clog <<
"Invalid protobuf data path " << protobuf_data_path <<
'\n';
333 protobuf_data_path =
"";
337 if (!root[
"objects"].isNull()){
338 for (
auto const& trackedObject : trackedObjects){
339 std::string obj_id = std::to_string(trackedObject.first);
340 if(!root[
"objects"][obj_id].isNull()){
341 trackedObject.second->SetJsonValue(root[
"objects"][obj_id]);
347 if (!root[
"objects_id"].isNull()){
348 for (
auto const& trackedObject : trackedObjects){
349 Json::Value trackedObjectJSON;
350 trackedObjectJSON[
"box_id"] = root[
"objects_id"][trackedObject.first].asString();
351 trackedObject.second->SetJsonValue(trackedObjectJSON);
359 std::string Tracker::PropertiesJSON(int64_t requested_frame)
const {
366 for (
auto const& trackedObject : trackedObjects){
367 Json::Value trackedObjectJSON = trackedObject.second->PropertiesJSON(requested_frame);
369 objects[trackedObject.second->Id()] = trackedObjectJSON;
371 root[
"objects"] = objects;
374 root[
"id"] = add_property_json(
"ID", 0.0,
"string", Id(), NULL, -1, -1,
true, requested_frame);
375 root[
"position"] = add_property_json(
"Position", Position(),
"float",
"", NULL, 0, 1000 * 60 * 30,
false, requested_frame);
376 root[
"layer"] = add_property_json(
"Track", Layer(),
"int",
"", NULL, 0, 20,
false, requested_frame);
377 root[
"start"] = add_property_json(
"Start", Start(),
"float",
"", NULL, 0, 1000 * 60 * 30,
false, requested_frame);
378 root[
"end"] = add_property_json(
"End", End(),
"float",
"", NULL, 0, 1000 * 60 * 30,
false, requested_frame);
379 root[
"duration"] = add_property_json(
"Duration", Duration(),
"float",
"", NULL, 0, 1000 * 60 * 30,
true, requested_frame);
382 return root.toStyledString();
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.
This class represents a clip (used to arrange readers on the timeline)
std::shared_ptr< openshot::Frame > GetFrame(int64_t frame_number) override
Get an openshot::Frame object for a specific frame number of this clip. The image size and number of ...
This class represents a single frame of video (i.e. image & audio data)
Exception for invalid JSON.
This class represents a timeline.
openshot::Clip * GetClip(const std::string &id)
Look up a single clip by ID.
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.
const Json::Value stringToJson(const std::string value)
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
float angle
bounding box rotation angle [degrees]