diff --git a/src/TrackedObjectBBox.cpp b/src/TrackedObjectBBox.cpp index 5ca98270..55400d2d 100644 --- a/src/TrackedObjectBBox.cpp +++ b/src/TrackedObjectBBox.cpp @@ -26,20 +26,19 @@ using namespace openshot; // Default Constructor, delegating TrackedObjectBBox::TrackedObjectBBox() - : TrackedObjectBBox::TrackedObjectBBox(0, 0, 255, 0) {} + : TrackedObjectBBox::TrackedObjectBBox(0, 0, 255, 255) {} // Constructor that takes RGBA values for stroke, and sets the bounding-box // displacement as 0 and the scales as 1 for the first frame TrackedObjectBBox::TrackedObjectBBox(int Red, int Green, int Blue, int Alfa) : delta_x(0.0), delta_y(0.0), scale_x(1.0), scale_y(1.0), rotation(0.0), - background_alpha(1.0), background_corner(0), - stroke_width(2) , stroke_alpha(0.0), + background_alpha(0.0), background_corner(12), + stroke_width(2) , stroke_alpha(0.5), stroke(Red, Green, Blue, Alfa), - background(0, 0, 255, 0) + background(0, 0, 255, Alfa) { this->TimeScale = 1.0; - return; } // Add a BBox to the BoxVec map @@ -442,10 +441,12 @@ Json::Value TrackedObjectBBox::PropertiesJSON(int64_t requested_frame) const 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["draw_box"] = add_property_json("Draw Box", draw_box.GetValue(requested_frame), "int", "", &draw_box, -1, 1.0, false, requested_frame); - root["draw_box"]["choices"].append(add_property_choice_json("Off", 0, draw_box.GetValue(requested_frame))); - root["draw_box"]["choices"].append(add_property_choice_json("On", 1, draw_box.GetValue(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))); + root["draw_box"]["choices"].append(add_property_choice_json("No", false, draw_box.GetValue(requested_frame))); root["stroke"] = add_property_json("Border", 0.0, "color", "", NULL, 0, 255, false, requested_frame); root["stroke"]["red"] = add_property_json("Red", stroke.red.GetValue(requested_frame), "float", "", &stroke.red, 0, 255, false, requested_frame); diff --git a/src/effects/Tracker.cpp b/src/effects/Tracker.cpp index 7a0a3df5..094b58d7 100644 --- a/src/effects/Tracker.cpp +++ b/src/effects/Tracker.cpp @@ -13,7 +13,6 @@ #include #include -#include #include #include "effects/Tracker.h" @@ -25,6 +24,8 @@ #include #include +#include +#include #include using namespace std; @@ -83,129 +84,100 @@ void Tracker::init_effect_details() // This method is required for all derived classes of EffectBase, and returns a // 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(); +std::shared_ptr Tracker::GetFrame(std::shared_ptr frame, int64_t frame_number) { + // Get the frame's QImage + std::shared_ptr frame_image = frame->GetImage(); - // Initialize the Qt rectangle that will hold the positions of the bounding-box - QRectF boxRect; - // Initialize the image of the TrackedObject child clip - std::shared_ptr childClipImage = nullptr; + // Check if frame isn't NULL + if(frame_image && !frame_image->isNull() && + trackedData->Contains(frame_number) && + trackedData->visible.GetValue(frame_number) == 1) { + QPainter painter(frame_image.get()); - // Check if frame isn't NULL - if(!frame_image.empty() && - trackedData->Contains(frame_number) && - trackedData->visible.GetValue(frame_number) == 1) - { - // Get the width and height of the image - float fw = frame_image.size().width; - float fh = frame_image.size().height; + // Get the bounding-box of the given frame + BBox fd = trackedData->GetBox(frame_number); - // Get the bounding-box of given frame - BBox fd = trackedData->GetBox(frame_number); + // Create a QRectF for the bounding box + QRectF boxRect((fd.cx - fd.width / 2) * frame_image->width(), + (fd.cy - fd.height / 2) * frame_image->height(), + fd.width * frame_image->width(), + fd.height * frame_image->height()); - // Check if track data exists for the requested frame - if (trackedData->draw_box.GetValue(frame_number) == 1) - { - std::vector stroke_rgba = trackedData->stroke.GetColorRGBA(frame_number); - int stroke_width = trackedData->stroke_width.GetValue(frame_number); - float stroke_alpha = trackedData->stroke_alpha.GetValue(frame_number); - std::vector bg_rgba = trackedData->background.GetColorRGBA(frame_number); - float bg_alpha = trackedData->background_alpha.GetValue(frame_number); + // Check if track data exists for the requested frame + if (trackedData->draw_box.GetValue(frame_number) == 1) { + painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); - // Create a rotated rectangle object that holds the bounding box - cv::RotatedRect box ( cv::Point2f( (int)(fd.cx*fw), (int)(fd.cy*fh) ), - cv::Size2f( (int)(fd.width*fw), (int)(fd.height*fh) ), - (int) (fd.angle) ); + // Get trackedObjectBox keyframes + std::vector stroke_rgba = trackedData->stroke.GetColorRGBA(frame_number); + int stroke_width = trackedData->stroke_width.GetValue(frame_number); + float stroke_alpha = trackedData->stroke_alpha.GetValue(frame_number); + std::vector bg_rgba = trackedData->background.GetColorRGBA(frame_number); + float bg_alpha = trackedData->background_alpha.GetValue(frame_number); + float bg_corner = trackedData->background_corner.GetValue(frame_number); - DrawRectangleRGBA(frame_image, box, bg_rgba, bg_alpha, 1, true); - DrawRectangleRGBA(frame_image, box, stroke_rgba, stroke_alpha, stroke_width, false); - } + // 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); - // Get the image of the Tracked Object' child clip - if (trackedData->ChildClipId() != ""){ - // Cast the parent timeline of this effect - Timeline* parentTimeline = static_cast(ParentTimeline()); - if (parentTimeline){ - // Get the Tracked Object's child clip - Clip* childClip = parentTimeline->GetClip(trackedData->ChildClipId()); - if (childClip){ - // Get the image of the child clip for this frame - std::shared_ptr childClipFrame = childClip->GetFrame(frame_number); - childClipImage = childClipFrame->GetImage(); + // 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 Qt rectangle with the bounding-box properties - boxRect.setRect((int)((fd.cx-fd.width/2)*fw), - (int)((fd.cy - fd.height/2)*fh), - (int)(fd.width*fw), - (int)(fd.height*fh) ); - } - } - } + // Draw the rounded rectangle + painter.drawRoundedRect(boxRect, bg_corner, bg_corner); + } - } + // Get the image of the Tracked Object' child clip + if (trackedData->ChildClipId() != ""){ + // Cast the parent timeline of this effect + Timeline* parentTimeline = static_cast(ParentTimeline()); + if (parentTimeline){ + // Get the Tracked Object's child clip + Clip* childClip = parentTimeline->GetClip(trackedData->ChildClipId()); + if (childClip){ + // Get the image of the child clip for this frame + std::shared_ptr childClipFrame = childClip->GetFrame(frame_number); + std::shared_ptr childClipImage = childClipFrame->GetImage(); - // Set image with drawn box to frame - // If the input image is NULL or doesn't have tracking data, it's returned as it came - frame->SetImageCV(frame_image); + // Scale the original bounding box to this image + QRectF scaledRect = scaleAndCenterRect(QRectF(childClipImage->rect()), boxRect); + QImage scaledImage = childClipImage->scaled(scaledRect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation); - // Set the bounding-box image with the Tracked Object's child clip image - if (childClipImage){ - // Get the frame image - QImage frameImage = *(frame->GetImage()); + // Draw the child clip image inside the bounding-box + painter.drawImage(scaledRect, *childClipImage); + } + } + } - // Set a Qt painter to the frame image - QPainter painter(&frameImage); + painter.end(); + } - // Draw the child clip image inside the bounding-box - painter.drawImage(boxRect, *childClipImage); - - // Set the frame image as the composed image - frame->AddImage(std::make_shared(frameImage)); - } - - return frame; + // No need to set the image back to the frame, as we directly modified the frame's QImage + return frame; } -void Tracker::DrawRectangleRGBA(cv::Mat &frame_image, cv::RotatedRect box, std::vector color, float alpha, int thickness, bool is_background){ - // Get the bouding box vertices - cv::Point2f vertices2f[4]; - box.points(vertices2f); +QRectF Tracker::scaleAndCenterRect(const QRectF& sourceRect, const QRectF& targetRect) { + float sourceAspectRatio = sourceRect.width() / sourceRect.height(); + float targetWidth = targetRect.width(); + float targetHeight = targetRect.height(); + float newWidth, newHeight; - // TODO: take a rectangle of frame_image by refencence and draw on top of that to improve speed - // select min enclosing rectangle to draw on a small portion of the image - // cv::Rect rect = box.boundingRect(); - // cv::Mat image = frame_image(rect) + if (sourceAspectRatio > targetRect.width() / targetRect.height()) { + // Source is wider relative to target, so it's constrained by target's width + newWidth = targetWidth; + newHeight = newWidth / sourceAspectRatio; + } else { + // Source is taller relative to target, so it's constrained by target's height + newHeight = targetHeight; + newWidth = newHeight * sourceAspectRatio; + } - if(is_background){ - cv::Mat overlayFrame; - frame_image.copyTo(overlayFrame); + // Center the new rectangle within the target rectangle + float newX = targetRect.left() + (targetWidth - newWidth) / 2.0; + float newY = targetRect.top() + (targetHeight - newHeight) / 2.0; - // draw bounding box background - cv::Point vertices[4]; - for(int i = 0; i < 4; ++i){ - vertices[i] = vertices2f[i];} - - cv::Rect rect = box.boundingRect(); - cv::fillConvexPoly(overlayFrame, vertices, 4, cv::Scalar(color[2],color[1],color[0]), cv::LINE_AA); - // add opacity - cv::addWeighted(overlayFrame, 1-alpha, frame_image, alpha, 0, frame_image); - } - else{ - cv::Mat overlayFrame; - frame_image.copyTo(overlayFrame); - - // Draw bounding box - for (int i = 0; i < 4; i++) - { - cv::line(overlayFrame, vertices2f[i], vertices2f[(i+1)%4], cv::Scalar(color[2],color[1],color[0]), - thickness, cv::LINE_AA); - } - - // add opacity - cv::addWeighted(overlayFrame, 1-alpha, frame_image, alpha, 0, frame_image); - } + return QRectF(newX, newY, newWidth, newHeight); } // Get the indexes and IDs of all visible objects in the given frame diff --git a/src/effects/Tracker.h b/src/effects/Tracker.h index dc9e718f..2b6b574e 100644 --- a/src/effects/Tracker.h +++ b/src/effects/Tracker.h @@ -44,6 +44,9 @@ namespace openshot /// Init effect settings void init_effect_details(); + /// Find a rectangle inside another (centered) + QRectF scaleAndCenterRect(const QRectF& sourceRect, const QRectF& targetRect); + Fraction BaseFPS; double TimeScale; @@ -71,8 +74,6 @@ namespace openshot /// Get the indexes and IDs of all visible objects in the given frame std::string GetVisibleObjects(int64_t frame_number) const override; - void DrawRectangleRGBA(cv::Mat &frame_image, cv::RotatedRect box, std::vector color, float alpha, int thickness, bool is_background); - // Get and Set JSON methods /// Generate JSON string of this object