OpenShot Library | libopenshot 1.1.1
Loading...
Searching...
No Matches
ObjectDetection.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 <fstream>
15#include <iostream>
16#include <algorithm>
17
19#include "effects/Tracker.h"
20#include "Exceptions.h"
21#include "Timeline.h"
22#include "objdetectdata.pb.h"
23
24#include <QImage>
25#include <QPainter>
26#include <QBrush>
27#include <QColor>
28#include <QRectF>
29#include <QString>
30#include <QStringList>
31using namespace std;
32using namespace openshot;
33
34namespace {
35bool is_all_objects_key(const std::string& name)
36{
37 const QString normalized = QString::fromStdString(name).trimmed().toLower();
38 return normalized == "all" || normalized == "*" || normalized == "-1";
39}
40
41std::shared_ptr<TrackedObjectBBox> make_all_objects_properties(
42 const std::shared_ptr<TrackedObjectBase>& source,
43 bool has_mask_data = false)
44{
45 auto properties = std::make_shared<TrackedObjectBBox>();
46 if (source) {
47 properties->SetJsonValue(source->JsonValue());
48 auto source_bbox = std::dynamic_pointer_cast<TrackedObjectBBox>(source);
49 has_mask_data = has_mask_data || (source_bbox && source_bbox->HasMaskData());
50 }
51 if (has_mask_data) {
52 properties->AddMask(0, ObjectMaskData{1, 1, {0, 1}});
53 }
54 properties->Id("All Objects");
55 return properties;
56}
57
58bool has_tracked_object_mask_data(
59 const std::map<int, std::shared_ptr<TrackedObjectBase>>& tracked_objects)
60{
61 for (const auto& tracked_object : tracked_objects) {
62 auto bbox = std::dynamic_pointer_cast<TrackedObjectBBox>(tracked_object.second);
63 if (bbox && bbox->HasMaskData())
64 return true;
65 }
66 return false;
67}
68
69cv::Scalar default_class_color(const std::string& class_name, int index)
70{
71 const QString normalized = QString::fromStdString(class_name).trimmed().toLower();
72
73 // RGB values. Keep common object-detection classes on clear, saturated colors
74 // instead of the previous deterministic random palette.
75 if (normalized == "person") return cv::Scalar(83, 160, 237);
76 if (normalized == "car") return cv::Scalar(42, 200, 185);
77 if (normalized == "truck") return cv::Scalar(239, 126, 92);
78 if (normalized == "bus") return cv::Scalar(250, 196, 72);
79 if (normalized == "bicycle") return cv::Scalar(122, 201, 67);
80 if (normalized == "motorbike" || normalized == "motorcycle") return cv::Scalar(180, 126, 235);
81 if (normalized == "dog") return cv::Scalar(237, 92, 140);
82 if (normalized == "cat") return cv::Scalar(101, 214, 128);
83
84 static const cv::Scalar palette[] = {
85 cv::Scalar(83, 160, 237),
86 cv::Scalar(42, 200, 185),
87 cv::Scalar(239, 126, 92),
88 cv::Scalar(250, 196, 72),
89 cv::Scalar(122, 201, 67),
90 cv::Scalar(180, 126, 235),
91 cv::Scalar(237, 92, 140),
92 cv::Scalar(72, 190, 230),
93 };
94 return palette[index % (sizeof(palette) / sizeof(palette[0]))];
95}
96
97QImage alpha_mask_image_from_rle(const ObjectMaskData& mask)
98{
99 QImage image(mask.width, mask.height, QImage::Format_ARGB32_Premultiplied);
100 image.fill(Qt::transparent);
101 if (!mask.HasData())
102 return image;
103
104 QRgb* data = reinterpret_cast<QRgb*>(image.bits());
105 const int total = mask.width * mask.height;
106 int offset = 0;
107 bool value = false;
108 for (uint32_t count : mask.rle) {
109 const int end = std::min(total, offset + static_cast<int>(count));
110 if (value) {
111 std::fill(data + offset, data + end, qRgba(255, 255, 255, 255));
112 }
113 offset = end;
114 value = !value;
115 if (offset >= total)
116 break;
117 }
118 return image;
119}
120}
121
122
123// Default constructor
125 : display_box_text(1.0)
126 , display_boxes(1.0)
127{
128 // Init effect metadata
129 init_effect_details();
130
131 // We haven’t loaded any protobuf yet, so there's nothing to pick.
133}
134
135// Init effect settings
136void ObjectDetection::init_effect_details()
137{
140
142 info.class_name = "ObjectDetection";
143 info.name = "Object Detector";
144 info.description = "Detect objects through the video.";
145 info.has_audio = false;
146 info.has_video = true;
148}
149
150// This method is required for all derived classes of EffectBase, and returns a
151// modified openshot::Frame object
152std::shared_ptr<Frame> ObjectDetection::GetFrame(std::shared_ptr<Frame> frame, int64_t frame_number) {
153 // Get the frame's QImage
154 std::shared_ptr<QImage> frame_image = frame->GetImage();
155
156 // Check if frame isn't NULL
157 if(!frame_image || frame_image->isNull()) {
158 return frame;
159 }
160
161 QPainter painter(frame_image.get());
162 painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
163
164 if (detectionsData.find(frame_number) != detectionsData.end()) {
165 DetectionData detections = detectionsData[frame_number];
166 for (int i = 0; i < detections.boxes.size(); i++) {
167 if (detections.confidences.at(i) < confidence_threshold ||
168 (!display_classes.empty() &&
169 std::find(display_classes.begin(), display_classes.end(), classNames[detections.classIds.at(i)]) == display_classes.end())) {
170 continue;
171 }
172
173 int objectId = detections.objectIds.at(i);
174 auto trackedObject_it = trackedObjects.find(objectId);
175
176 if (trackedObject_it != trackedObjects.end()) {
177 std::shared_ptr<TrackedObjectBBox> trackedObject = std::static_pointer_cast<TrackedObjectBBox>(trackedObject_it->second);
178
179 Clip* parentClip = (Clip*) trackedObject->ParentClip();
180 if (parentClip && trackedObject->Contains(frame_number) && trackedObject->visible.GetValue(frame_number) == 1) {
181 BBox trackedBox = trackedObject->GetBox(frame_number);
182 QRectF boxRect((trackedBox.cx - trackedBox.width / 2) * frame_image->width(),
183 (trackedBox.cy - trackedBox.height / 2) * frame_image->height(),
184 trackedBox.width * frame_image->width(),
185 trackedBox.height * frame_image->height());
186
187 // Get properties of tracked object (i.e. colors, stroke width, etc...)
188 std::vector<int> stroke_rgba = trackedObject->stroke.GetColorRGBA(frame_number);
189 std::vector<int> bg_rgba = trackedObject->background.GetColorRGBA(frame_number);
190 float stroke_alpha = trackedObject->stroke_alpha.GetValue(frame_number);
191 float bg_alpha = trackedObject->background_alpha.GetValue(frame_number);
192 float bg_corner = trackedObject->background_corner.GetValue(frame_number);
193
194 // Set the pen for the border
195 QPen pen(QColor(stroke_rgba[0], stroke_rgba[1], stroke_rgba[2], 255 * stroke_alpha));
196 pen.setWidthF(trackedObject->ScaledStrokeWidth(
197 frame_number, frame_image->width(), frame_image->height()));
198 painter.setPen(pen);
199
200 // Set the brush for the background
201 QBrush brush(QColor(bg_rgba[0], bg_rgba[1], bg_rgba[2], 255 * bg_alpha));
202 painter.setBrush(brush);
203
204 if (display_boxes.GetValue(frame_number) == 1 && trackedObject->draw_box.GetValue(frame_number) == 1) {
205 // Only draw boxes if both properties are set to YES (draw all boxes, and draw box of the selected box)
206 painter.drawRoundedRect(boxRect, bg_corner, bg_corner);
207 }
208
209 ObjectMaskData object_mask = trackedObject->GetMask(frame_number, 5);
210 if (object_mask.HasData() && trackedObject->draw_mask.GetValue(frame_number) == 1) {
211 QImage mask = alpha_mask_image_from_rle(object_mask)
212 .scaled(frame_image->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
213 std::vector<int> mask_rgba = trackedObject->mask_color.GetColorRGBA(frame_number);
214 float mask_alpha = trackedObject->mask_alpha.GetValue(frame_number);
215 QColor mask_color(mask_rgba[0], mask_rgba[1], mask_rgba[2], 255 * mask_alpha);
216 QImage overlay(frame_image->size(), QImage::Format_ARGB32_Premultiplied);
217 overlay.fill(Qt::transparent);
218 QPainter overlay_painter(&overlay);
219 overlay_painter.setCompositionMode(QPainter::CompositionMode_Source);
220 overlay_painter.fillRect(overlay.rect(), mask_color);
221 overlay_painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
222 overlay_painter.drawImage(0, 0, mask);
223 overlay_painter.end();
224 painter.drawImage(0, 0, overlay);
225 }
226
227 if(display_box_text.GetValue(frame_number) == 1 && trackedObject->draw_text.GetValue(frame_number) == 1) {
228 // Draw text label above bounding box
229 // Get the confidence and classId for the current detection
230 int classId = detections.classIds.at(i);
231
232 // Get the label for the class name and its confidence
233 QString label = QString::number(objectId);
234 if (!classNames.empty()) {
235 label = QString::fromStdString(classNames[classId]) + ":" + label;
236 }
237
238 // Set up the painter, font, and pen
239 QFont font;
240 font.setPixelSize(14);
241 painter.setFont(font);
242
243 // Calculate the size of the text
244 QFontMetrics fontMetrics(font);
245 QSize labelSize = fontMetrics.size(Qt::TextSingleLine, label);
246
247 // Define the top left point of the rectangle
248 double left = boxRect.center().x() - (labelSize.width() / 2.0);
249 double top = std::max(static_cast<int>(boxRect.top()), labelSize.height()) - 4.0;
250
251 // Draw the text
252 painter.drawText(QPointF(left, top), label);
253 }
254 }
255 }
256 }
257 }
258
259 painter.end();
260
261 // The frame's QImage has been modified in place, so we just return the original frame
262 return frame;
263}
264
265// Load protobuf data file
266bool ObjectDetection::LoadObjDetectdData(std::string inputFilePath)
267{
268 // Parse the file
269 pb_objdetect::ObjDetect objMessage;
270 std::fstream input(inputFilePath, std::ios::in | std::ios::binary);
271 if (!objMessage.ParseFromIstream(&input)) {
272 std::cerr << "Failed to parse protobuf message." << std::endl;
273 return false;
274 }
275
276 // Clear out any old state
277 classNames.clear();
278 classesColor.clear();
279 detectionsData.clear();
280 trackedObjects.clear();
281
282 // Seed colors for each class
283 for (int i = 0; i < objMessage.classnames_size(); ++i) {
284 const std::string class_name = objMessage.classnames(i);
285 classNames.push_back(class_name);
286 classesColor.push_back(default_class_color(class_name, i));
287 }
288
289 // Walk every frame in the protobuf
290 for (size_t fi = 0; fi < objMessage.frame_size(); ++fi) {
291 const auto &pbFrame = objMessage.frame(fi);
292 size_t frameId = pbFrame.id();
293
294 // Buffers for DetectionData
295 std::vector<int> classIds;
296 std::vector<float> confidences;
297 std::vector<cv::Rect_<float>> boxes;
298 std::vector<int> objectIds;
299 std::vector<ObjectMaskData> masks;
300
301 // For each bounding box in this frame
302 for (int di = 0; di < pbFrame.bounding_box_size(); ++di) {
303 const auto &b = pbFrame.bounding_box(di);
304 float x = b.x(), y = b.y(), w = b.w(), h = b.h();
305 int classId = b.classid();
306 float confidence= b.confidence();
307 int objectId = b.objectid();
308 ObjectMaskData mask;
309 if (b.has_mask()) {
310 mask.width = b.mask().width();
311 mask.height = b.mask().height();
312 for (int rleIndex = 0; rleIndex < b.mask().rle_size(); ++rleIndex) {
313 mask.rle.push_back(b.mask().rle(rleIndex));
314 }
315 }
316
317 // Record for DetectionData
318 classIds.push_back(classId);
319 confidences.push_back(confidence);
320 boxes.emplace_back(x, y, w, h);
321 objectIds.push_back(objectId);
322 masks.push_back(mask);
323
324 // Either append to an existing TrackedObjectBBox…
325 auto it = trackedObjects.find(objectId);
326 if (it != trackedObjects.end()) {
327 it->second->AddBox(frameId, x + w/2, y + h/2, w, h, 0.0);
328 auto bbox = std::dynamic_pointer_cast<TrackedObjectBBox>(it->second);
329 if (bbox && mask.HasData())
330 bbox->AddMask(frameId, mask);
331 }
332 else {
333 // …or create a brand-new one
334 TrackedObjectBBox tmpObj(
335 (int)classesColor[classId][0],
336 (int)classesColor[classId][1],
337 (int)classesColor[classId][2],
338 /*alpha=*/0
339 );
340 tmpObj.stroke_alpha = Keyframe(1.0);
341 tmpObj.background_alpha = Keyframe(0.15);
342 tmpObj.AddBox(frameId, x + w/2, y + h/2, w, h, 0.0);
343 if (mask.HasData())
344 tmpObj.AddMask(frameId, mask);
345
346 auto ptr = std::make_shared<TrackedObjectBBox>(tmpObj);
347 ptr->ParentClip(this->ParentClip());
348
349 // Prefix with effect UUID for a unique string ID
350 std::string prefix = this->Id();
351 if (!prefix.empty())
352 prefix += "-";
353 ptr->Id(prefix + std::to_string(objectId));
354 trackedObjects.emplace(objectId, ptr);
355 }
356 }
357
358 // Save the DetectionData for this frame
359 detectionsData[frameId] = DetectionData(
360 classIds, confidences, boxes, frameId, objectIds, masks
361 );
362 }
363
364 google::protobuf::ShutdownProtobufLibrary();
365
366 // Default to the pseudo-selection that edits every tracked object.
367 if (!trackedObjects.empty()) {
369 }
370
371 return true;
372}
373
374// Get the indexes and IDs of all visible objects in the given frame
375std::string ObjectDetection::GetVisibleObjects(int64_t frame_number) const{
376
377 // Initialize the JSON objects
378 Json::Value root;
379 root["visible_objects_index"] = Json::Value(Json::arrayValue);
380 root["visible_objects_id"] = Json::Value(Json::arrayValue);
381 root["visible_class_names"] = Json::Value(Json::arrayValue);
382
383 // Check if track data exists for the requested frame
384 if (detectionsData.find(frame_number) == detectionsData.end()){
385 return root.toStyledString();
386 }
387 DetectionData detections = detectionsData.at(frame_number);
388
389 if (!trackedObjects.empty()) {
390 root["visible_objects_index"].append(-1);
391 root["visible_objects_id"].append("All Objects");
392 root["visible_class_names"].append("All Objects");
393 }
394
395 // Iterate through the tracked objects
396 for(int i = 0; i<detections.boxes.size(); i++){
397 // Does not show boxes with confidence below the threshold
398 if(detections.confidences.at(i) < confidence_threshold){
399 continue;
400 }
401
402 // Get class name of tracked object
403 auto className = classNames[detections.classIds.at(i)];
404
405 // If display_classes is not empty, check if className is in it
406 if (!display_classes.empty()) {
407 auto it = std::find(display_classes.begin(), display_classes.end(), className);
408 if (it == display_classes.end()) {
409 // If not in display_classes, skip this detection
410 continue;
411 }
412 root["visible_class_names"].append(className);
413 } else {
414 // include all class names
415 root["visible_class_names"].append(className);
416 }
417
418 int objectId = detections.objectIds.at(i);
419 // Search for the object in the trackedObjects map
420 auto trackedObject = trackedObjects.find(objectId);
421
422 // Get the tracked object JSON properties for this frame
423 Json::Value trackedObjectJSON = trackedObject->second->PropertiesJSON(frame_number);
424
425 if (trackedObjectJSON["visible"]["value"].asBool() &&
426 trackedObject->second->ExactlyContains(frame_number)){
427 // Save the object's index and ID if it's visible in this frame
428 root["visible_objects_index"].append(trackedObject->first);
429 root["visible_objects_id"].append(trackedObject->second->Id());
430 }
431 }
432
433 return root.toStyledString();
434}
435
436std::shared_ptr<QImage> ObjectDetection::TrackedObjectMask(std::shared_ptr<QImage> target_image, int64_t frame_number) const {
437 if (!target_image || target_image->isNull())
438 return {};
439
440 auto detections_it = detectionsData.find(frame_number);
441 if (detections_it == detectionsData.end())
442 return {};
443
444 auto mask_image = std::make_shared<QImage>(
445 target_image->width(), target_image->height(), QImage::Format_RGBA8888_Premultiplied);
446 mask_image->fill(QColor(0, 0, 0, 255));
447
448 QPainter painter(mask_image.get());
449 painter.setRenderHint(QPainter::Antialiasing, true);
450 painter.setPen(Qt::NoPen);
451 painter.setBrush(QBrush(QColor(255, 255, 255, 255)));
452
453 bool drew_any_box = false;
454 const DetectionData& detections = detections_it->second;
455 for (int i = 0; i < detections.boxes.size(); i++) {
456 if (detections.confidences.at(i) < confidence_threshold)
457 continue;
458
459 const int class_id = detections.classIds.at(i);
460 if (class_id < 0 || class_id >= classNames.size())
461 continue;
462
463 const std::string class_name = classNames[class_id];
464 if (!display_classes.empty() &&
465 std::find(display_classes.begin(), display_classes.end(), class_name) == display_classes.end()) {
466 continue;
467 }
468
469 int object_id = detections.objectIds.at(i);
470 auto tracked_object_it = trackedObjects.find(object_id);
471 if (tracked_object_it == trackedObjects.end() || !tracked_object_it->second)
472 continue;
473
474 auto tracked_object = std::static_pointer_cast<TrackedObjectBBox>(tracked_object_it->second);
475 if (!tracked_object->ExactlyContains(frame_number) ||
476 tracked_object->visible.GetValue(frame_number) != 1) {
477 continue;
478 }
479
480 BBox box = tracked_object->GetBox(frame_number);
481 if (box.width <= 0.0f || box.height <= 0.0f || box.cx < 0.0f || box.cy < 0.0f)
482 continue;
483
484 ObjectMaskData object_mask = tracked_object->GetMask(frame_number, 5);
485 if (object_mask.HasData() && tracked_object->draw_mask.GetValue(frame_number) == 1) {
486 QImage mask = alpha_mask_image_from_rle(object_mask)
487 .scaled(target_image->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
488 painter.drawImage(0, 0, mask);
489 drew_any_box = true;
490 continue;
491 }
492
493 const double x = (box.cx - box.width / 2.0) * target_image->width();
494 const double y = (box.cy - box.height / 2.0) * target_image->height();
495 const double w = box.width * target_image->width();
496 const double h = box.height * target_image->height();
497 const double corner = tracked_object->background_corner.GetValue(frame_number);
498 painter.drawRoundedRect(QRectF(x, y, w, h), corner, corner);
499 drew_any_box = true;
500 }
501
502 painter.end();
503 if (!drew_any_box)
504 return {};
505 return mask_image;
506}
507
508// Generate JSON string of this object
509std::string ObjectDetection::Json() const {
510
511 // Return formatted string
512 return JsonValue().toStyledString();
513}
514
515// Generate Json::Value for this object
516Json::Value ObjectDetection::JsonValue() const {
517
518 // Create root json object
519 Json::Value root = EffectBase::JsonValue(); // get parent properties
520 root["type"] = info.class_name;
521 root["protobuf_data_path"] = protobuf_data_path;
522 root["selected_object_index"] = selectedObjectIndex;
523 root["confidence_threshold"] = confidence_threshold;
524 root["display_box_text"] = display_box_text.JsonValue();
525 root["display_boxes"] = display_boxes.JsonValue();
526
527 // Add tracked object's IDs to root
528 Json::Value objects;
529 for (auto const& trackedObject : trackedObjects){
530 Json::Value trackedObjectJSON = trackedObject.second->JsonValue();
531 // add object json
532 objects[trackedObject.second->Id()] = trackedObjectJSON;
533 }
534 root["objects"] = objects;
535
536 // return JsonValue
537 return root;
538}
539
540// Load JSON string into this object
541void ObjectDetection::SetJson(const std::string value) {
542
543 // Parse JSON string into JSON objects
544 try
545 {
546 const Json::Value root = openshot::stringToJson(value);
547 // Set all values that match
548 SetJsonValue(root);
549 }
550 catch (const std::exception& e)
551 {
552 // Error parsing JSON (or missing keys)
553 throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
554 }
555}
556
557// Load Json::Value into this object
558void ObjectDetection::SetJsonValue(const Json::Value root)
559{
560 // Parent properties
562
563 // If a protobuf path is provided, load & prefix IDs
564 if (!root["protobuf_data_path"].isNull()) {
565 std::string new_path = root["protobuf_data_path"].asString();
566 if (protobuf_data_path != new_path || trackedObjects.empty()) {
567 protobuf_data_path = new_path;
568 allObjectsProperties.reset();
569 if (!LoadObjDetectdData(protobuf_data_path)) {
570 throw InvalidFile("Invalid protobuf data path", "");
571 }
572 }
573 }
574
575 // Selected index, thresholds, UI flags, filters, etc.
576 if (!root["selected_object_index"].isNull())
577 selectedObjectIndex = root["selected_object_index"].asInt();
578 if (!root["confidence_threshold"].isNull())
579 confidence_threshold = root["confidence_threshold"].asFloat();
580 if (!root["display_box_text"].isNull())
581 display_box_text.SetJsonValue(root["display_box_text"]);
582 if (!root["display_boxes"].isNull())
583 display_boxes.SetJsonValue(root["display_boxes"]);
584
585 if (!root["class_filter"].isNull()) {
586 class_filter = root["class_filter"].asString();
587 QStringList parts = QString::fromStdString(class_filter).split(',');
588 display_classes.clear();
589 for (auto &p : parts) {
590 auto s = p.trimmed().toLower();
591 if (!s.isEmpty()) {
592 display_classes.push_back(s.toStdString());
593 }
594 }
595 }
596
597 // Apply any per-object overrides
598 if (!root["objects"].isNull()) {
599 // Iterate over the supplied objects (indexed by id or position)
600 const auto memberNames = root["objects"].getMemberNames();
601 for (const auto& name : memberNames)
602 {
603 if (is_all_objects_key(name)) {
604 if (!allObjectsProperties) {
605 std::shared_ptr<TrackedObjectBase> firstObject;
606 if (!trackedObjects.empty())
607 firstObject = trackedObjects.begin()->second;
608 allObjectsProperties = make_all_objects_properties(
609 firstObject, has_tracked_object_mask_data(trackedObjects));
610 }
611 allObjectsProperties->SetJsonValue(root["objects"][name]);
612 for (auto& trackedObject : trackedObjects) {
613 if (trackedObject.second)
614 trackedObject.second->SetJsonValue(root["objects"][name]);
615 }
616 }
617 }
618
619 for (const auto& name : memberNames)
620 {
621 if (is_all_objects_key(name))
622 continue;
623 // Determine the numeric index of this object
624 int index = -1;
625 bool numeric_key = std::all_of(name.begin(), name.end(), ::isdigit);
626 if (numeric_key) {
627 index = std::stoi(name);
628 }
629 else
630 {
631 size_t pos = name.find_last_of('-');
632 if (pos != std::string::npos) {
633 try {
634 index = std::stoi(name.substr(pos + 1));
635 } catch (...) {
636 index = -1;
637 }
638 }
639 }
640
641 auto obj_it = trackedObjects.find(index);
642 if (obj_it != trackedObjects.end() && obj_it->second) {
643 // Update object id if provided as a non-numeric key
644 if (!numeric_key)
645 obj_it->second->Id(name);
646 obj_it->second->SetJsonValue(root["objects"][name]);
647 }
648 }
649 }
650 // Set the tracked object's ids (legacy format)
651 if (!root["objects_id"].isNull()) {
652 for (auto& kv : trackedObjects) {
653 if (!root["objects_id"][kv.first].isNull())
654 kv.second->Id(root["objects_id"][kv.first].asString());
655 }
656 }
657}
658
659// Get all properties for a specific frame
660std::string ObjectDetection::PropertiesJSON(int64_t requested_frame) const {
661
662 // Generate JSON properties list
663 Json::Value root = BasePropertiesJSON(requested_frame);
664
665 Json::Value objects;
666 if(selectedObjectIndex == -1 && !trackedObjects.empty()){
667 auto selectedObject = allObjectsProperties
668 ? allObjectsProperties
669 : make_all_objects_properties(trackedObjects.begin()->second, has_tracked_object_mask_data(trackedObjects));
670 if (selectedObject){
671 Json::Value trackedObjectJSON = selectedObject->PropertiesJSON(requested_frame);
672 trackedObjectJSON["box_id"]["memo"] = "All Objects";
673 trackedObjectJSON.removeMember("x1");
674 trackedObjectJSON.removeMember("y1");
675 trackedObjectJSON.removeMember("x2");
676 trackedObjectJSON.removeMember("y2");
677 objects["all"] = trackedObjectJSON;
678 }
679 }
680 else if(trackedObjects.count(selectedObjectIndex) != 0){
681 auto selectedObject = trackedObjects.at(selectedObjectIndex);
682 if (selectedObject){
683 Json::Value trackedObjectJSON = selectedObject->PropertiesJSON(requested_frame);
684 // add object json
685 objects[selectedObject->Id()] = trackedObjectJSON;
686 }
687 }
688 root["objects"] = objects;
689
690 root["selected_object_index"] = add_property_json("Selected Object", selectedObjectIndex, "int", "", NULL, -1, 200, false, requested_frame);
691 root["confidence_threshold"] = add_property_json("Confidence Threshold", confidence_threshold, "float", "", NULL, 0, 1, false, requested_frame);
692 root["class_filter"] = add_property_json("Class Filter", 0.0, "string", class_filter, NULL, -1, -1, false, requested_frame);
693
694 // Return formatted string
695 return root.toStyledString();
696}
Header file for all Exception classes.
Header file for Object Detection effect class.
Header file for Timeline class.
Header file for Tracker effect class.
std::string Id() const
Get the Id of this clip object.
Definition ClipBase.h:88
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
Generate JSON for a property.
Definition ClipBase.cpp:100
This class represents a clip (used to arrange readers on the timeline)
Definition Clip.h:89
virtual Json::Value JsonValue() const
Generate Json::Value for this object.
openshot::ClipBase * ParentClip()
Parent clip object of this effect (which can be unparented and NULL)
Json::Value BasePropertiesJSON(int64_t requested_frame) const
Generate JSON object of base properties (recommended to be used by all effects)
virtual void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
EffectInfoStruct info
Information about the current effect.
Definition EffectBase.h:114
std::map< int, std::shared_ptr< openshot::TrackedObjectBase > > trackedObjects
Map of Tracked Object's by their indices (used by Effects that track objects on clips)
Definition EffectBase.h:111
Exception for files that can not be found or opened.
Definition Exceptions.h:194
Exception for invalid JSON.
Definition Exceptions.h:224
A Keyframe is a collection of Point instances, which is used to vary a number or property over time.
Definition KeyFrame.h:53
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition KeyFrame.cpp:372
double GetValue(int64_t index) const
Get the value at a specific index.
Definition KeyFrame.cpp:258
Json::Value JsonValue() const
Generate Json::Value for this object.
Definition KeyFrame.cpp:339
Json::Value JsonValue() const override
Generate Json::Value for this object.
int selectedObjectIndex
Index of the Tracked Object that was selected to modify it's properties.
std::shared_ptr< Frame > GetFrame(std::shared_ptr< Frame > frame, int64_t frame_number) override
This method is required for all derived classes of EffectBase, and returns a modified openshot::Frame...
ObjectDetection()
Default constructor.
bool LoadObjDetectdData(std::string inputFilePath)
Load protobuf data file.
std::string GetVisibleObjects(int64_t frame_number) const override
Get the indexes and IDs of all visible objects in the given frame.
std::string Json() const override
Generate JSON string of this object.
std::string PropertiesJSON(int64_t requested_frame) const override
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
void SetJson(const std::string value) override
Load JSON string into this object.
std::shared_ptr< QImage > TrackedObjectMask(std::shared_ptr< QImage > target_image, int64_t frame_number) const override
Generate a black/white mask from visible detected bounding boxes.
openshot::ClipBase * ParentClip()
Parent clip object of this reader (which can be unparented and NULL)
This class contains the properties of a tracked object and functions to manipulate it.
void AddBox(int64_t _frame_num, float _cx, float _cy, float _width, float _height, float _angle) override
Add a BBox to the BoxVec map.
void AddMask(int64_t frame_num, const ObjectMaskData &mask)
Keyframe background_alpha
Background box opacity.
Keyframe stroke_alpha
Stroke box opacity.
This namespace is the default namespace for all code in the openshot library.
const Json::Value stringToJson(const std::string value)
Definition Json.cpp:16
std::vector< cv::Rect_< float > > boxes
std::vector< float > confidences
std::vector< int > classIds
std::vector< int > objectIds
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
bool has_video
Determines if this effect manipulates the image of a frame.
Definition EffectBase.h:43
bool has_audio
Determines if this effect manipulates the audio of a frame.
Definition EffectBase.h:44
std::string class_name
The class name of the effect.
Definition EffectBase.h:39
std::string name
The name of the effect.
Definition EffectBase.h:40
std::string description
The description of this effect and what it does.
Definition EffectBase.h:41
bool has_tracked_object
Determines if this effect track objects through the clip.
Definition EffectBase.h:45
std::vector< uint32_t > rle