From 5a0a6a69a822c00899e2ab8d9ccac2e444385a0a Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Tue, 5 Mar 2024 10:41:06 -0600 Subject: [PATCH] Add new "Display All Boxes" Object Detector property, since it can be difficult to turn off each individual box. Made "visible" property read-only on Tracker & Object Detector effects. Improve Keyframe::SetJsonValue method to accept an object or a float. --- src/KeyFrame.cpp | 27 +++++---- src/TrackedObjectBBox.cpp | 4 +- src/TrackedObjectBase.h | 3 + src/effects/ObjectDetection.cpp | 99 ++++++++++++++++++--------------- src/effects/ObjectDetection.h | 7 ++- 5 files changed, 81 insertions(+), 59 deletions(-) diff --git a/src/KeyFrame.cpp b/src/KeyFrame.cpp index a8364819..1df3f3c0 100644 --- a/src/KeyFrame.cpp +++ b/src/KeyFrame.cpp @@ -374,18 +374,25 @@ void Keyframe::SetJsonValue(const Json::Value root) { Points.clear(); Points.shrink_to_fit(); - if (!root["Points"].isNull()) - // loop through points - for (const auto existing_point : root["Points"]) { - // Create Point - Point p; + if (root.isObject() && !root["Points"].isNull()) { + // loop through points in JSON Object + for (const auto existing_point : root["Points"]) { + // Create Point + Point p; - // Load Json into Point - p.SetJsonValue(existing_point); + // Load Json into Point + p.SetJsonValue(existing_point); - // Add Point to Keyframe - AddPoint(p); - } + // Add Point to Keyframe + AddPoint(p); + } + } else if (root.isNumeric()) { + // Create Point from Numeric value + Point p(root.asFloat()); + + // Add Point to Keyframe + AddPoint(p); + } } // Get the change in Y value (from the previous Y value) diff --git a/src/TrackedObjectBBox.cpp b/src/TrackedObjectBBox.cpp index 6c81a41a..b520b054 100644 --- a/src/TrackedObjectBBox.cpp +++ b/src/TrackedObjectBBox.cpp @@ -431,9 +431,7 @@ Json::Value TrackedObjectBBox::PropertiesJSON(int64_t requested_frame) const root["scale_x"] = add_property_json("Scale (Width)", scale_x.GetValue(requested_frame), "float", "", &scale_x, 0.0, 1.0, false, requested_frame); root["scale_y"] = add_property_json("Scale (Height)", 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); - root["visible"] = add_property_json("Visible", visible.GetValue(requested_frame), "int", "", &visible, 0, 1, false, requested_frame); - root["visible"]["choices"].append(add_property_choice_json("Yes", true, visible.GetValue(requested_frame))); - root["visible"]["choices"].append(add_property_choice_json("No", false, visible.GetValue(requested_frame))); + root["visible"] = add_property_json("Visible", visible.GetValue(requested_frame), "int", "", &visible, 0, 1, true, requested_frame); root["draw_box"] = add_property_json("Draw Box", draw_box.GetValue(requested_frame), "int", "", &draw_box, 0, 1, false, requested_frame); root["draw_box"]["choices"].append(add_property_choice_json("Yes", true, draw_box.GetValue(requested_frame))); diff --git a/src/TrackedObjectBase.h b/src/TrackedObjectBase.h index 4922f1e6..58b312ed 100644 --- a/src/TrackedObjectBase.h +++ b/src/TrackedObjectBase.h @@ -39,7 +39,10 @@ namespace openshot { public: + /// Keyframe to track if a box is visible in the current frame (read-only) Keyframe visible; + + /// Keyframe to determine if a specific box is drawn (or hidden) Keyframe draw_box; /// Default constructor diff --git a/src/effects/ObjectDetection.cpp b/src/effects/ObjectDetection.cpp index 7bad798e..5d45992b 100644 --- a/src/effects/ObjectDetection.cpp +++ b/src/effects/ObjectDetection.cpp @@ -30,7 +30,8 @@ using namespace openshot; /// Blank constructor, useful when using Json to load the effect properties -ObjectDetection::ObjectDetection(std::string clipObDetectDataPath) +ObjectDetection::ObjectDetection(std::string clipObDetectDataPath) : +display_box_text(1.0), display_boxes(1.0) { // Init effect properties init_effect_details(); @@ -43,7 +44,8 @@ ObjectDetection::ObjectDetection(std::string clipObDetectDataPath) } // Default constructor -ObjectDetection::ObjectDetection() +ObjectDetection::ObjectDetection() : + display_box_text(1.0), display_boxes(1.0) { // Init effect properties init_effect_details(); @@ -104,55 +106,54 @@ std::shared_ptr ObjectDetection::GetFrame(std::shared_ptr frame, i trackedBox.width * frame_image->width(), trackedBox.height * frame_image->height()); - if (trackedObject->draw_box.GetValue(frame_number) == 1) { - // Draw bounding box - bool display_text = display_box_text.GetValue(frame_number); - std::vector stroke_rgba = trackedObject->stroke.GetColorRGBA(frame_number); - std::vector bg_rgba = trackedObject->background.GetColorRGBA(frame_number); - int stroke_width = trackedObject->stroke_width.GetValue(frame_number); - float stroke_alpha = trackedObject->stroke_alpha.GetValue(frame_number); - float bg_alpha = trackedObject->background_alpha.GetValue(frame_number); - float bg_corner = trackedObject->background_corner.GetValue(frame_number); + // Get properties of tracked object (i.e. colors, stroke width, etc...) + std::vector stroke_rgba = trackedObject->stroke.GetColorRGBA(frame_number); + std::vector bg_rgba = trackedObject->background.GetColorRGBA(frame_number); + int stroke_width = trackedObject->stroke_width.GetValue(frame_number); + float stroke_alpha = trackedObject->stroke_alpha.GetValue(frame_number); + float bg_alpha = trackedObject->background_alpha.GetValue(frame_number); + float bg_corner = trackedObject->background_corner.GetValue(frame_number); - // Set the pen for the border - QPen pen(QColor(stroke_rgba[0], stroke_rgba[1], stroke_rgba[2], 255 * stroke_alpha)); - pen.setWidth(stroke_width); - painter.setPen(pen); + // Set the pen for the border + QPen pen(QColor(stroke_rgba[0], stroke_rgba[1], stroke_rgba[2], 255 * stroke_alpha)); + pen.setWidth(stroke_width); + painter.setPen(pen); - // Set the brush for the background - QBrush brush(QColor(bg_rgba[0], bg_rgba[1], bg_rgba[2], 255 * bg_alpha)); - painter.setBrush(brush); + // Set the brush for the background + QBrush brush(QColor(bg_rgba[0], bg_rgba[1], bg_rgba[2], 255 * bg_alpha)); + painter.setBrush(brush); - // Draw the rounded rectangle + if (display_boxes.GetValue(frame_number) == 1 && trackedObject->draw_box.GetValue(frame_number) == 1) { + // Only draw boxes if both properties are set to YES (draw all boxes, and draw box of the selected box) painter.drawRoundedRect(boxRect, bg_corner, bg_corner); + } - if(display_text) { - // Draw text label above bounding box - // Get the confidence and classId for the current detection - int classId = detections.classIds.at(i); + if(display_box_text.GetValue(frame_number) == 1) { + // Draw text label above bounding box + // Get the confidence and classId for the current detection + int classId = detections.classIds.at(i); - // Get the label for the class name and its confidence - QString label = QString::number(objectId); - if (!classNames.empty()) { - label = QString::fromStdString(classNames[classId]) + ":" + label; - } - - // Set up the painter, font, and pen - QFont font; - font.setPixelSize(14); - painter.setFont(font); - - // Calculate the size of the text - QFontMetrics fontMetrics(font); - QSize labelSize = fontMetrics.size(Qt::TextSingleLine, label); - - // Define the top left point of the rectangle - double left = boxRect.center().x() - (labelSize.width() / 2.0); - double top = std::max(static_cast(boxRect.top()), labelSize.height()) - 4.0; - - // Draw the text - painter.drawText(QPointF(left, top), label); + // Get the label for the class name and its confidence + QString label = QString::number(objectId); + if (!classNames.empty()) { + label = QString::fromStdString(classNames[classId]) + ":" + label; } + + // Set up the painter, font, and pen + QFont font; + font.setPixelSize(14); + painter.setFont(font); + + // Calculate the size of the text + QFontMetrics fontMetrics(font); + QSize labelSize = fontMetrics.size(Qt::TextSingleLine, label); + + // Define the top left point of the rectangle + double left = boxRect.center().x() - (labelSize.width() / 2.0); + double top = std::max(static_cast(boxRect.top()), labelSize.height()) - 4.0; + + // Draw the text + painter.drawText(QPointF(left, top), label); } } } @@ -343,6 +344,7 @@ Json::Value ObjectDetection::JsonValue() const { root["selected_object_index"] = selectedObjectIndex; root["confidence_threshold"] = confidence_threshold; root["display_box_text"] = display_box_text.JsonValue(); + root["display_boxes"] = display_boxes.JsonValue(); // Add tracked object's IDs to root Json::Value objects; @@ -399,6 +401,9 @@ void ObjectDetection::SetJsonValue(const Json::Value root) { if (!root["display_box_text"].isNull()) display_box_text.SetJsonValue(root["display_box_text"]); + if (!root["display_boxes"].isNull()) + display_boxes.SetJsonValue(root["display_boxes"]); + if (!root["class_filter"].isNull()) { class_filter = root["class_filter"].asString(); @@ -458,10 +463,14 @@ std::string ObjectDetection::PropertiesJSON(int64_t requested_frame) const { root["confidence_threshold"] = add_property_json("Confidence Theshold", confidence_threshold, "float", "", NULL, 0, 1, false, requested_frame); root["class_filter"] = add_property_json("Class Filter", 0.0, "string", class_filter, NULL, -1, -1, false, requested_frame); - root["display_box_text"] = add_property_json("Draw Box Text", display_box_text.GetValue(requested_frame), "int", "", &display_box_text, 0, 1, false, requested_frame); + root["display_box_text"] = add_property_json("Draw All Text", display_box_text.GetValue(requested_frame), "int", "", &display_box_text, 0, 1, false, requested_frame); root["display_box_text"]["choices"].append(add_property_choice_json("Yes", true, display_box_text.GetValue(requested_frame))); root["display_box_text"]["choices"].append(add_property_choice_json("No", false, display_box_text.GetValue(requested_frame))); + root["display_boxes"] = add_property_json("Draw All Boxes", display_boxes.GetValue(requested_frame), "int", "", &display_boxes, 0, 1, false, requested_frame); + root["display_boxes"]["choices"].append(add_property_choice_json("Yes", true, display_boxes.GetValue(requested_frame))); + root["display_boxes"]["choices"].append(add_property_choice_json("No", false, display_boxes.GetValue(requested_frame))); + // Return formatted string return root.toStyledString(); } diff --git a/src/effects/ObjectDetection.h b/src/effects/ObjectDetection.h index 42f278d1..d56eca72 100644 --- a/src/effects/ObjectDetection.h +++ b/src/effects/ObjectDetection.h @@ -62,10 +62,15 @@ namespace openshot std::vector classNames; std::vector classesColor; - /// Draw class name and confidence score on top of the bounding box + /// Draw ALL class name and ID #'s on top of the bounding boxes (or hide all text) Keyframe display_box_text; + + /// Draw ALL tracked bounding boxes (or hide all boxes) + Keyframe display_boxes; + /// Minimum confidence value to display the detected objects float confidence_threshold = 0.5; + /// Contain the user selected classes for visualization std::vector display_classes; std::string class_filter;