diff -Nru libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/debian/changelog libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/debian/changelog --- libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/debian/changelog 2024-02-18 17:20:08.000000000 +0000 +++ libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/debian/changelog 2024-03-26 19:02:34.000000000 +0000 @@ -1,8 +1,8 @@ -libopenshot (0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1) focal; urgency=low +libopenshot (0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1) focal; urgency=low * Auto build. - -- OpenShot Code Sun, 18 Feb 2024 17:20:08 +0000 + -- OpenShot Code Tue, 26 Mar 2024 19:02:34 +0000 libopenshot (0.3.2-1) UNRELEASED; urgency=medium diff -Nru libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/debian/git-build-recipe.manifest libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/debian/git-build-recipe.manifest --- libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/debian/git-build-recipe.manifest 2024-02-18 17:20:08.000000000 +0000 +++ libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/debian/git-build-recipe.manifest 2024-03-26 19:02:34.000000000 +0000 @@ -1,3 +1,3 @@ -# git-build-recipe format 0.4 deb-version {debupstream}+dfsg2+1183+202402181720+daily -lp:libopenshot git-commit:284904d41be0bf189171b016fb6620fd59c21b51 +# git-build-recipe format 0.4 deb-version {debupstream}+dfsg2+1184+202403261902+daily +lp:libopenshot git-commit:a9e34a9bce59fdb78123c0eb824a171948c6b9e8 nest-part packaging lp:openshot-packaging libopenshot/debian debian git-commit:68bf5b6c1a9c48fd0b8d99de9cd1a0a98e088ee8 diff -Nru libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/Clip.cpp libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/Clip.cpp --- libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/Clip.cpp 2024-02-18 17:19:58.000000000 +0000 +++ libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/Clip.cpp 2024-03-26 19:02:17.000000000 +0000 @@ -251,9 +251,11 @@ // Check for valid tracked object if (trackedObject){ SetAttachedObject(trackedObject); + parentClipObject = NULL; } else if (clipObject) { SetAttachedClip(clipObject); + parentTrackedObject = nullptr; } } } @@ -414,47 +416,49 @@ // Check cache frame = final_cache.GetFrame(clip_frame_number); - if (frame) { - // Debug output - ZmqLogger::Instance()->AppendDebugMethod( - "Clip::GetFrame (Cached frame found)", - "requested_frame", clip_frame_number); - - // Return cached frame - return frame; - } - - // Generate clip frame - frame = GetOrCreateFrame(clip_frame_number); - - if (!background_frame) { - // Create missing background_frame w/ transparent color (if needed) - background_frame = std::make_shared(clip_frame_number, frame->GetWidth(), frame->GetHeight(), - "#00000000", frame->GetAudioSamplesCount(), - frame->GetAudioChannelsCount()); - } - - // Get time mapped frame object (used to increase speed, change direction, etc...) - apply_timemapping(frame); - - // Apply waveform image (if any) - apply_waveform(frame, background_frame); - - // Apply effects BEFORE applying keyframes (if any local or global effects are used) - apply_effects(frame, background_frame, options, true); - - // Apply keyframe / transforms to current clip image - apply_keyframes(frame, background_frame); - - // Apply effects AFTER applying keyframes (if any local or global effects are used) - apply_effects(frame, background_frame, options, false); + if (!frame) { + // Generate clip frame + frame = GetOrCreateFrame(clip_frame_number); + + // Get frame size and frame # + int64_t timeline_frame_number = clip_frame_number; + QSize timeline_size(frame->GetWidth(), frame->GetHeight()); + if (background_frame) { + // If a background frame is provided, use it instead + timeline_frame_number = background_frame->number; + timeline_size.setWidth(background_frame->GetWidth()); + timeline_size.setHeight(background_frame->GetHeight()); + } + + // Get time mapped frame object (used to increase speed, change direction, etc...) + apply_timemapping(frame); + + // Apply waveform image (if any) + apply_waveform(frame, timeline_size); + + // Apply effects BEFORE applying keyframes (if any local or global effects are used) + apply_effects(frame, timeline_frame_number, options, true); + + // Apply keyframe / transforms to current clip image + apply_keyframes(frame, timeline_size); + + // Apply effects AFTER applying keyframes (if any local or global effects are used) + apply_effects(frame, timeline_frame_number, options, false); + + // Add final frame to cache (before flattening into background_frame) + final_cache.Add(frame); + } + + if (!background_frame) { + // Create missing background_frame w/ transparent color (if needed) + background_frame = std::make_shared(frame->number, frame->GetWidth(), frame->GetHeight(), + "#00000000", frame->GetAudioSamplesCount(), + frame->GetAudioChannelsCount()); + } // Apply background canvas (i.e. flatten this image onto previous layer image) apply_background(frame, background_frame); - // Add final frame to cache - final_cache.Add(frame); - // Return processed 'frame' return frame; } @@ -475,6 +479,24 @@ return nullptr; } +// Return the associated ParentClip (if any) +openshot::Clip* Clip::GetParentClip() { + if (!parentObjectId.empty() && (!parentClipObject && !parentTrackedObject)) { + // Attach parent clip OR object to this clip + AttachToObject(parentObjectId); + } + return parentClipObject; +} + +// Return the associated Parent Tracked Object (if any) +std::shared_ptr Clip::GetParentTrackedObject() { + if (!parentObjectId.empty() && (!parentClipObject && !parentTrackedObject)) { + // Attach parent clip OR object to this clip + AttachToObject(parentObjectId); + } + return parentTrackedObject; +} + // Get file extension std::string Clip::get_file_extension(std::string path) { @@ -482,33 +504,6 @@ return path.substr(path.find_last_of(".") + 1); } -// Reverse an audio buffer -void Clip::reverse_buffer(juce::AudioBuffer* buffer) -{ - int number_of_samples = buffer->getNumSamples(); - int channels = buffer->getNumChannels(); - - // Reverse array (create new buffer to hold the reversed version) - auto *reversed = new juce::AudioBuffer(channels, number_of_samples); - reversed->clear(); - - for (int channel = 0; channel < channels; channel++) - { - int n=0; - for (int s = number_of_samples - 1; s >= 0; s--, n++) - reversed->getWritePointer(channel)[n] = buffer->getWritePointer(channel)[s]; - } - - // Copy the samples back to the original array - buffer->clear(); - // Loop through channels, and get audio samples - for (int channel = 0; channel < channels; channel++) - // Get the audio samples for this channel - buffer->addFrom(channel, 0, reversed->getReadPointer(channel), number_of_samples, 1.0f); - - delete reversed; -} - // Adjust the audio and image of a time mapped frame void Clip::apply_timemapping(std::shared_ptr frame) { @@ -785,38 +780,8 @@ root["waveform"]["choices"].append(add_property_choice_json("Yes", true, waveform)); root["waveform"]["choices"].append(add_property_choice_json("No", false, waveform)); - // Add the parentTrackedObject's properties - if (parentTrackedObject && parentClipObject) - { - // Convert Clip's frame position to Timeline's frame position - long clip_start_position = round(Position() * info.fps.ToDouble()) + 1; - long clip_start_frame = (Start() * info.fps.ToDouble()) + 1; - double timeline_frame_number = requested_frame + clip_start_position - clip_start_frame; - - // Get attached object's parent clip properties - std::map< std::string, float > trackedObjectParentClipProperties = parentTrackedObject->GetParentClipProperties(timeline_frame_number); - double parentObject_frame_number = trackedObjectParentClipProperties["frame_number"]; - // Get attached object properties - std::map< std::string, float > trackedObjectProperties = parentTrackedObject->GetBoxValues(parentObject_frame_number); - - // Correct the parent Tracked Object properties by the clip's reference system - float parentObject_location_x = trackedObjectProperties["cx"] - 0.5 + trackedObjectParentClipProperties["cx"]; - float parentObject_location_y = trackedObjectProperties["cy"] - 0.5 + trackedObjectParentClipProperties["cy"]; - float parentObject_scale_x = trackedObjectProperties["w"]*trackedObjectProperties["sx"]; - float parentObject_scale_y = trackedObjectProperties["h"]*trackedObjectProperties["sy"]; - float parentObject_rotation = trackedObjectProperties["r"] + trackedObjectParentClipProperties["r"]; - - // Add the parent Tracked Object properties to JSON - root["location_x"] = add_property_json("Location X", parentObject_location_x, "float", "", &location_x, -1.0, 1.0, false, requested_frame); - root["location_y"] = add_property_json("Location Y", parentObject_location_y, "float", "", &location_y, -1.0, 1.0, false, requested_frame); - root["scale_x"] = add_property_json("Scale X", parentObject_scale_x, "float", "", &scale_x, 0.0, 1.0, false, requested_frame); - root["scale_y"] = add_property_json("Scale Y", parentObject_scale_y, "float", "", &scale_y, 0.0, 1.0, false, requested_frame); - root["rotation"] = add_property_json("Rotation", parentObject_rotation, "float", "", &rotation, -360, 360, false, requested_frame); - root["shear_x"] = add_property_json("Shear X", shear_x.GetValue(requested_frame), "float", "", &shear_x, -1.0, 1.0, false, requested_frame); - root["shear_y"] = add_property_json("Shear Y", shear_y.GetValue(requested_frame), "float", "", &shear_y, -1.0, 1.0, false, requested_frame); - } // Add the parentClipObject's properties - else if (parentClipObject) + if (parentClipObject) { // Convert Clip's frame position to Timeline's frame position long clip_start_position = round(Position() * info.fps.ToDouble()) + 1; @@ -1221,7 +1186,7 @@ } // Apply effects to the source frame (if any) -void Clip::apply_effects(std::shared_ptr frame, std::shared_ptr background_frame, TimelineInfoStruct* options, bool before_keyframes) +void Clip::apply_effects(std::shared_ptr frame, int64_t timeline_frame_number, TimelineInfoStruct* options, bool before_keyframes) { for (auto effect : effects) { @@ -1237,18 +1202,18 @@ // Apply global timeline effects (i.e. transitions & masks... if any) Timeline* timeline_instance = static_cast(timeline); options->is_before_clip_keyframes = before_keyframes; - timeline_instance->apply_effects(frame, background_frame->number, Layer(), options); + timeline_instance->apply_effects(frame, timeline_frame_number, Layer(), options); } } // Compare 2 floating point numbers for equality -bool Clip::isEqual(double a, double b) +bool Clip::isNear(double a, double b) { return fabs(a - b) < 0.000001; } // Apply keyframes to the source frame (if any) -void Clip::apply_keyframes(std::shared_ptr frame, std::shared_ptr background_frame) { +void Clip::apply_keyframes(std::shared_ptr frame, QSize timeline_size) { // Skip out if video was disabled or only an audio frame (no visualisation in use) if (!frame->has_image_data) { // Skip the rest of the image processing for performance reasons @@ -1257,8 +1222,8 @@ // Get image from clip, and create transparent background image std::shared_ptr source_image = frame->GetImage(); - std::shared_ptr background_canvas = std::make_shared(background_frame->GetImage()->width(), - background_frame->GetImage()->height(), + std::shared_ptr background_canvas = std::make_shared(timeline_size.width(), + timeline_size.height(), QImage::Format_RGBA8888_Premultiplied); background_canvas->fill(QColor(Qt::transparent)); @@ -1312,7 +1277,7 @@ } // Apply apply_waveform image to the source frame (if any) -void Clip::apply_waveform(std::shared_ptr frame, std::shared_ptr background_frame) { +void Clip::apply_waveform(std::shared_ptr frame, QSize timeline_size) { if (!Waveform()) { // Exit if no waveform is needed @@ -1321,15 +1286,13 @@ // Get image from clip std::shared_ptr source_image = frame->GetImage(); - std::shared_ptr background_canvas = background_frame->GetImage(); // Debug output - ZmqLogger::Instance()->AppendDebugMethod( - "Clip::apply_waveform (Generate Waveform Image)", + ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_waveform (Generate Waveform Image)", "frame->number", frame->number, "Waveform()", Waveform(), - "background_canvas->width()", background_canvas->width(), - "background_canvas->height()", background_canvas->height()); + "width", timeline_size.width(), + "height", timeline_size.height()); // Get the color of the waveform int red = wave_color.red.GetInt(frame->number); @@ -1338,11 +1301,32 @@ int alpha = wave_color.alpha.GetInt(frame->number); // Generate Waveform Dynamically (the size of the timeline) - source_image = frame->GetWaveform(background_canvas->width(), background_canvas->height(), red, green, blue, alpha); + source_image = frame->GetWaveform(timeline_size.width(), timeline_size.height(), red, green, blue, alpha); frame->AddImage(source_image); } -// Apply keyframes to the source frame (if any) +// Scale a source size to a target size (given a specific scale-type) +QSize Clip::scale_size(QSize source_size, ScaleType source_scale, int target_width, int target_height) { + switch (source_scale) + { + case (SCALE_FIT): { + source_size.scale(target_width, target_height, Qt::KeepAspectRatio); + break; + } + case (SCALE_STRETCH): { + source_size.scale(target_width, target_height, Qt::IgnoreAspectRatio); + break; + } + case (SCALE_CROP): { + source_size.scale(target_width, target_height, Qt::KeepAspectRatioByExpanding);; + break; + } + } + + return source_size; +} + +// Get QTransform from keyframes QTransform Clip::get_transform(std::shared_ptr frame, int width, int height) { // Get image from clip @@ -1368,68 +1352,13 @@ } // Debug output - ZmqLogger::Instance()->AppendDebugMethod( - "Clip::get_transform (Set Alpha & Opacity)", + ZmqLogger::Instance()->AppendDebugMethod("Clip::get_transform (Set Alpha & Opacity)", "alpha_value", alpha_value, "frame->number", frame->number); } /* RESIZE SOURCE IMAGE - based on scale type */ - QSize source_size = source_image->size(); - - // Apply stretch scale to correctly fit the bounding-box - if (parentTrackedObject){ - scale = SCALE_STRETCH; - } - - switch (scale) - { - case (SCALE_FIT): { - source_size.scale(width, height, Qt::KeepAspectRatio); - - // Debug output - ZmqLogger::Instance()->AppendDebugMethod( - "Clip::get_transform (Scale: SCALE_FIT)", - "frame->number", frame->number, - "source_width", source_size.width(), - "source_height", source_size.height()); - break; - } - case (SCALE_STRETCH): { - source_size.scale(width, height, Qt::IgnoreAspectRatio); - - // Debug output - ZmqLogger::Instance()->AppendDebugMethod( - "Clip::get_transform (Scale: SCALE_STRETCH)", - "frame->number", frame->number, - "source_width", source_size.width(), - "source_height", source_size.height()); - break; - } - case (SCALE_CROP): { - source_size.scale(width, height, Qt::KeepAspectRatioByExpanding); - - // Debug output - ZmqLogger::Instance()->AppendDebugMethod( - "Clip::get_transform (Scale: SCALE_CROP)", - "frame->number", frame->number, - "source_width", source_size.width(), - "source_height", source_size.height()); - break; - } - case (SCALE_NONE): { - // Image is already the original size (i.e. no scaling mode) relative - // to the preview window size (i.e. timeline / preview ratio). No further - // scaling is needed here. - // Debug output - ZmqLogger::Instance()->AppendDebugMethod( - "Clip::get_transform (Scale: SCALE_NONE)", - "frame->number", frame->number, - "source_width", source_size.width(), - "source_height", source_size.height()); - break; - } - } + QSize source_size = scale_size(source_image->size(), scale, width, height); // Initialize parent object's properties (Clip or Tracked Object) float parentObject_location_x = 0.0; @@ -1441,63 +1370,53 @@ float parentObject_rotation = 0.0; // Get the parentClipObject properties - if (parentClipObject){ - - // Convert Clip's frame position to Timeline's frame position - long clip_start_position = round(Position() * info.fps.ToDouble()) + 1; - long clip_start_frame = (Start() * info.fps.ToDouble()) + 1; - double timeline_frame_number = frame->number + clip_start_position - clip_start_frame; + if (GetParentClip()){ + // Get the start trim position of the parent clip + long parent_start_offset = parentClipObject->Start() * info.fps.ToDouble(); + long parent_frame_number = frame->number + parent_start_offset; // Get parent object's properties (Clip) - parentObject_location_x = parentClipObject->location_x.GetValue(timeline_frame_number); - parentObject_location_y = parentClipObject->location_y.GetValue(timeline_frame_number); - parentObject_scale_x = parentClipObject->scale_x.GetValue(timeline_frame_number); - parentObject_scale_y = parentClipObject->scale_y.GetValue(timeline_frame_number); - parentObject_shear_x = parentClipObject->shear_x.GetValue(timeline_frame_number); - parentObject_shear_y = parentClipObject->shear_y.GetValue(timeline_frame_number); - parentObject_rotation = parentClipObject->rotation.GetValue(timeline_frame_number); - } - - // Get the parentTrackedObject properties - if (parentTrackedObject){ - // Convert Clip's frame position to Timeline's frame position - long clip_start_position = round(Position() * info.fps.ToDouble()) + 1; - long clip_start_frame = (Start() * info.fps.ToDouble()) + 1; - double timeline_frame_number = frame->number + clip_start_position - clip_start_frame; - - // Get parentTrackedObject's parent clip's properties - std::map trackedObjectParentClipProperties = - parentTrackedObject->GetParentClipProperties(timeline_frame_number); - - // Get the attached object's parent clip's properties - if (!trackedObjectParentClipProperties.empty()) - { - // Get parent object's properties (Tracked Object) - float parentObject_frame_number = trackedObjectParentClipProperties["frame_number"]; - - // Access the parentTrackedObject's properties - std::map trackedObjectProperties = parentTrackedObject->GetBoxValues(parentObject_frame_number); - - // Get the Tracked Object's properties and correct them by the clip's reference system - parentObject_location_x = trackedObjectProperties["cx"] - 0.5 + trackedObjectParentClipProperties["location_x"]; - parentObject_location_y = trackedObjectProperties["cy"] - 0.5 + trackedObjectParentClipProperties["location_y"]; - parentObject_scale_x = trackedObjectProperties["w"]*trackedObjectProperties["sx"]; - parentObject_scale_y = trackedObjectProperties["h"]*trackedObjectProperties["sy"]; - parentObject_rotation = trackedObjectProperties["r"] + trackedObjectParentClipProperties["rotation"]; - } - else - { - // Access the parentTrackedObject's properties - std::map trackedObjectProperties = parentTrackedObject->GetBoxValues(timeline_frame_number); - - // Get the Tracked Object's properties and correct them by the clip's reference system - parentObject_location_x = trackedObjectProperties["cx"] - 0.5; - parentObject_location_y = trackedObjectProperties["cy"] - 0.5; - parentObject_scale_x = trackedObjectProperties["w"]*trackedObjectProperties["sx"]; - parentObject_scale_y = trackedObjectProperties["h"]*trackedObjectProperties["sy"]; - parentObject_rotation = trackedObjectProperties["r"]; - } - } + parentObject_location_x = parentClipObject->location_x.GetValue(parent_frame_number); + parentObject_location_y = parentClipObject->location_y.GetValue(parent_frame_number); + parentObject_scale_x = parentClipObject->scale_x.GetValue(parent_frame_number); + parentObject_scale_y = parentClipObject->scale_y.GetValue(parent_frame_number); + parentObject_shear_x = parentClipObject->shear_x.GetValue(parent_frame_number); + parentObject_shear_y = parentClipObject->shear_y.GetValue(parent_frame_number); + parentObject_rotation = parentClipObject->rotation.GetValue(parent_frame_number); + } + + // Get the parentTrackedObject properties + if (GetParentTrackedObject()){ + // Get the attached object's parent clip's properties + Clip* parentClip = (Clip*) parentTrackedObject->ParentClip(); + if (parentClip) + { + // Get the start trim position of the parent clip + long parent_start_offset = parentClip->Start() * info.fps.ToDouble(); + long parent_frame_number = frame->number + parent_start_offset; + + // Access the parentTrackedObject's properties + std::map trackedObjectProperties = parentTrackedObject->GetBoxValues(parent_frame_number); + + // Get actual scaled parent size + QSize parent_size = scale_size(QSize(parentClip->info.width, parentClip->info.height), + parentClip->scale, width, height); + + // Get actual scaled tracked object size + int trackedWidth = trackedObjectProperties["w"] * trackedObjectProperties["sx"] * parent_size.width() * + parentClip->scale_x.GetValue(parent_frame_number); + int trackedHeight = trackedObjectProperties["h"] * trackedObjectProperties["sy"] * parent_size.height() * + parentClip->scale_y.GetValue(parent_frame_number); + + // Scale the clip source_size based on the actual tracked object size + source_size = scale_size(source_size, scale, trackedWidth, trackedHeight); + + // Update parentObject's properties based on the tracked object's properties and parent clip's scale + parentObject_location_x = parentClip->location_x.GetValue(parent_frame_number) + ((trackedObjectProperties["cx"] - 0.5) * parentClip->scale_x.GetValue(parent_frame_number)); + parentObject_location_y = parentClip->location_y.GetValue(parent_frame_number) + ((trackedObjectProperties["cy"] - 0.5) * parentClip->scale_y.GetValue(parent_frame_number)); + parentObject_rotation = trackedObjectProperties["r"] + parentClip->rotation.GetValue(parent_frame_number); + } + } /* GRAVITY LOCATION - Initialize X & Y to the correct values (before applying location curves) */ float x = 0.0; // left @@ -1563,8 +1482,8 @@ /* LOCATION, ROTATION, AND SCALE */ float r = rotation.GetValue(frame->number) + parentObject_rotation; // rotate in degrees - x += (width * (location_x.GetValue(frame->number) + parentObject_location_x )); // move in percentage of final width - y += (height * (location_y.GetValue(frame->number) + parentObject_location_y )); // move in percentage of final height + x += width * (location_x.GetValue(frame->number) + parentObject_location_x); // move in percentage of final width + y += height * (location_y.GetValue(frame->number) + parentObject_location_y); // move in percentage of final height float shear_x_value = shear_x.GetValue(frame->number) + parentObject_shear_x; float shear_y_value = shear_y.GetValue(frame->number) + parentObject_shear_y; float origin_x_value = origin_x.GetValue(frame->number); @@ -1578,11 +1497,11 @@ "r", r, "sx", sx, "sy", sy); - if (!isEqual(x, 0) || !isEqual(y, 0)) { + if (!isNear(x, 0) || !isNear(y, 0)) { // TRANSLATE/MOVE CLIP transform.translate(x, y); } - if (!isEqual(r, 0) || !isEqual(shear_x_value, 0) || !isEqual(shear_y_value, 0)) { + if (!isNear(r, 0) || !isNear(shear_x_value, 0) || !isNear(shear_y_value, 0)) { // ROTATE CLIP (around origin_x, origin_y) float origin_x_offset = (scaled_source_width * origin_x_value); float origin_y_offset = (scaled_source_height * origin_y_value); @@ -1594,7 +1513,7 @@ // SCALE CLIP (if needed) float source_width_scale = (float(source_size.width()) / float(source_image->width())) * sx; float source_height_scale = (float(source_size.height()) / float(source_image->height())) * sy; - if (!isEqual(source_width_scale, 1.0) || !isEqual(source_height_scale, 1.0)) { + if (!isNear(source_width_scale, 1.0) || !isNear(source_height_scale, 1.0)) { transform.scale(source_width_scale, source_height_scale); } diff -Nru libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/Clip.h libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/Clip.h --- libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/Clip.h 2024-02-18 17:19:58.000000000 +0000 +++ libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/Clip.h 2024-03-26 19:02:17.000000000 +0000 @@ -131,13 +131,13 @@ void apply_background(std::shared_ptr frame, std::shared_ptr background_frame); /// Apply effects to the source frame (if any) - void apply_effects(std::shared_ptr frame, std::shared_ptr background_frame, TimelineInfoStruct* options, bool before_keyframes); + void apply_effects(std::shared_ptr frame, int64_t timeline_frame_number, TimelineInfoStruct* options, bool before_keyframes); /// Apply keyframes to an openshot::Frame and use an existing background frame (if any) - void apply_keyframes(std::shared_ptr frame, std::shared_ptr background_frame); + void apply_keyframes(std::shared_ptr frame, QSize timeline_size); /// Apply waveform image to an openshot::Frame and use an existing background frame (if any) - void apply_waveform(std::shared_ptr frame, std::shared_ptr background_frame); + void apply_waveform(std::shared_ptr frame, QSize timeline_size); /// Adjust frame number for Clip position and start (which can result in a different number) int64_t adjust_timeline_framenumber(int64_t clip_frame_number); @@ -154,15 +154,14 @@ /// Adjust the audio and image of a time mapped frame void apply_timemapping(std::shared_ptr frame); - /// Compare 2 floating point numbers - bool isEqual(double a, double b); + /// Compare 2 floating point numbers and return true if they are extremely close + bool isNear(double a, double b); /// Sort effects by order void sort_effects(); - /// Reverse an audio buffer - void reverse_buffer(juce::AudioBuffer* buffer); - + /// Scale a source size to a target size (given a specific scale-type) + QSize scale_size(QSize source_size, ScaleType source_scale, int target_width, int target_height); public: openshot::GravityType gravity; ///< The gravity of a clip determines where it snaps to its parent @@ -224,6 +223,12 @@ /// Close the internal reader void Close() override; + /// Return the associated ParentClip (if any) + openshot::Clip* GetParentClip(); + + /// Return the associated Parent Tracked Object (if any) + std::shared_ptr GetParentTrackedObject(); + /// Return the list of effects on the timeline std::list Effects() { return effects; }; diff -Nru libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/KeyFrame.cpp libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/KeyFrame.cpp --- libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/KeyFrame.cpp 2024-02-18 17:19:58.000000000 +0000 +++ libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/KeyFrame.cpp 2024-03-26 19:02:17.000000000 +0000 @@ -374,18 +374,25 @@ 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 -Nru libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/TrackedObjectBBox.cpp libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/TrackedObjectBBox.cpp --- libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/TrackedObjectBBox.cpp 2024-02-18 17:19:58.000000000 +0000 +++ libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/TrackedObjectBBox.cpp 2024-03-26 19:02:17.000000000 +0000 @@ -26,20 +26,19 @@ // 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.7), 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 @@ -315,7 +314,6 @@ root["BaseFPS"]["num"] = BaseFps.num; root["BaseFPS"]["den"] = BaseFps.den; root["TimeScale"] = TimeScale; - root["child_clip_id"] = ChildClipId(); // Keyframe's properties root["delta_x"] = delta_x.JsonValue(); @@ -380,11 +378,6 @@ if (!root["protobuf_data_path"].isNull()) protobufDataPath = root["protobuf_data_path"].asString(); - // Set the id of the child clip - if (!root["child_clip_id"].isNull() && root["child_clip_id"].asString() != Id()){ - ChildClipId(root["child_clip_id"].asString()); - } - // Set the Keyframes by the given JSON object if (!root["delta_x"].isNull()) delta_x.SetJsonValue(root["delta_x"]); @@ -426,14 +419,11 @@ // Add the ID of this object to the JSON object root["box_id"] = add_property_json("Box ID", 0.0, "string", Id(), NULL, -1, -1, true, requested_frame); - // Add the ID of this object's child clip to the JSON object - root["child_clip_id"] = add_property_json("Child Clip ID", 0.0, "string", ChildClipId(), NULL, -1, -1, false, requested_frame); - // Add the data of given frame bounding-box to the JSON object - root["x1"] = add_property_json("X1", box.cx-(box.width/2), "float", "", NULL, 0.0, 1.0, false, requested_frame); - root["y1"] = add_property_json("Y1", box.cy-(box.height/2), "float", "", NULL, 0.0, 1.0, false, requested_frame); - root["x2"] = add_property_json("X2", box.cx+(box.width/2), "float", "", NULL, 0.0, 1.0, false, requested_frame); - root["y2"] = add_property_json("Y2", box.cy+(box.height/2), "float", "", NULL, 0.0, 1.0, false, requested_frame); + root["x1"] = add_property_json("X1", box.cx-(box.width/2), "float", "", NULL, 0.0, 1.0, true, requested_frame); + root["y1"] = add_property_json("Y1", box.cy-(box.height/2), "float", "", NULL, 0.0, 1.0, true, requested_frame); + root["x2"] = add_property_json("X2", box.cx+(box.width/2), "float", "", NULL, 0.0, 1.0, true, requested_frame); + root["y2"] = add_property_json("Y2", box.cy+(box.height/2), "float", "", NULL, 0.0, 1.0, true, requested_frame); // Add the bounding-box Keyframes to the JSON object root["delta_x"] = add_property_json("Displacement X-axis", delta_x.GetValue(requested_frame), "float", "", &delta_x, -1.0, 1.0, false, requested_frame); @@ -441,11 +431,11 @@ 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"] = 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, -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); @@ -455,7 +445,7 @@ root["stroke_alpha"] = add_property_json("Stroke alpha", stroke_alpha.GetValue(requested_frame), "float", "", &stroke_alpha, 0.0, 1.0, false, requested_frame); root["background_alpha"] = add_property_json("Background Alpha", background_alpha.GetValue(requested_frame), "float", "", &background_alpha, 0.0, 1.0, false, requested_frame); - root["background_corner"] = add_property_json("Background Corner Radius", background_corner.GetValue(requested_frame), "int", "", &background_corner, 0.0, 60.0, false, requested_frame); + root["background_corner"] = add_property_json("Background Corner Radius", background_corner.GetValue(requested_frame), "int", "", &background_corner, 0.0, 150.0, false, requested_frame); root["background"] = add_property_json("Background", 0.0, "color", "", NULL, 0, 255, false, requested_frame); root["background"]["red"] = add_property_json("Red", background.red.GetValue(requested_frame), "float", "", &background.red, 0, 255, false, requested_frame); @@ -530,36 +520,3 @@ return boxValues; } - -// Return a map that contains the properties of this object's parent clip -std::map TrackedObjectBBox::GetParentClipProperties(int64_t frame_number) const { - - // Get the parent clip of this object as a Clip pointer - Clip* parentClip = (Clip *) ParentClip(); - - // Calculate parentClip's frame number - long parentClip_start_position = round( parentClip->Position() * parentClip->info.fps.ToDouble() ) + 1; - long parentClip_start_frame = ( parentClip->Start() * parentClip->info.fps.ToDouble() ) + 1; - float parentClip_frame_number = round(frame_number - parentClip_start_position) + parentClip_start_frame; - - // Get parentClip's Keyframes - float parentClip_location_x = parentClip->location_x.GetValue(parentClip_frame_number); - float parentClip_location_y = parentClip->location_y.GetValue(parentClip_frame_number); - float parentClip_scale_x = parentClip->scale_x.GetValue(parentClip_frame_number); - float parentClip_scale_y = parentClip->scale_y.GetValue(parentClip_frame_number); - float parentClip_rotation = parentClip->rotation.GetValue(parentClip_frame_number); - - // Initialize the parent clip properties map - std::map parentClipProperties; - - // Set the map properties - parentClipProperties["frame_number"] = parentClip_frame_number; - parentClipProperties["timeline_frame_number"] = frame_number; - parentClipProperties["location_x"] = parentClip_location_x; - parentClipProperties["location_y"] = parentClip_location_y; - parentClipProperties["scale_x"] = parentClip_scale_x; - parentClipProperties["scale_y"] = parentClip_scale_y; - parentClipProperties["rotation"] = parentClip_rotation; - - return parentClipProperties; -} diff -Nru libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/TrackedObjectBBox.h libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/TrackedObjectBBox.h --- libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/TrackedObjectBBox.h 2024-02-18 17:19:58.000000000 +0000 +++ libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/TrackedObjectBBox.h 2024-03-26 19:02:17.000000000 +0000 @@ -211,9 +211,6 @@ /// Return a map that contains the bounding box properties and it's keyframes indexed by their names std::map GetBoxValues(int64_t frame_number) const override; - /// Return a map that contains the properties of this object's parent clip - std::map GetParentClipProperties(int64_t frame_number) const override; - }; } // namespace openshot diff -Nru libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/TrackedObjectBase.cpp libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/TrackedObjectBase.cpp --- libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/TrackedObjectBase.cpp 2024-02-18 17:19:58.000000000 +0000 +++ libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/TrackedObjectBase.cpp 2024-03-26 19:02:17.000000000 +0000 @@ -23,7 +23,7 @@ // Constructor TrackedObjectBase::TrackedObjectBase(std::string _id) - : visible(1.0), draw_box(1), id(_id), childClipId("") {} + : visible(1.0), draw_box(1), id(_id) {} Json::Value TrackedObjectBase::add_property_choice_json( std::string name, int value, int selected_value) const diff -Nru libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/TrackedObjectBase.h libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/TrackedObjectBase.h --- libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/TrackedObjectBase.h 2024-02-18 17:19:58.000000000 +0000 +++ libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/TrackedObjectBase.h 2024-03-26 19:02:17.000000000 +0000 @@ -35,13 +35,14 @@ class TrackedObjectBase { protected: std::string id; - std::string childClipId; - ClipBase* parentClip; 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 @@ -60,9 +61,6 @@ /// Get and set the parentClip of this object ClipBase* ParentClip() const { return parentClip; } void ParentClip(ClipBase* clip) { parentClip = clip; } - /// Get and set the Id of the childClip of this object - std::string ChildClipId() const { return childClipId; }; - void ChildClipId(std::string _childClipId) { childClipId = _childClipId; }; /// Check if there is data for the exact frame number virtual bool ExactlyContains(int64_t frame_number) const { return {}; }; @@ -71,8 +69,6 @@ virtual void ScalePoints(double scale) { return; }; /// Return the main properties of a TrackedObjectBBox instance - such as position, size and rotation virtual std::map GetBoxValues(int64_t frame_number) const { std::map ret; return ret; }; - /// Return the main properties of the tracked object's parent clip - such as position, size and rotation - virtual std::map GetParentClipProperties(int64_t frame_number) const { std::map ret; return ret; } /// Add a bounding box to the tracked object's BoxVec map virtual void AddBox(int64_t _frame_num, float _cx, float _cy, float _width, float _height, float _angle) { return; }; diff -Nru libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/effects/ObjectDetection.cpp libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/effects/ObjectDetection.cpp --- libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/effects/ObjectDetection.cpp 2024-02-18 17:19:58.000000000 +0000 +++ libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/effects/ObjectDetection.cpp 2024-03-26 19:02:17.000000000 +0000 @@ -23,12 +23,15 @@ #include #include #include +#include +#include using namespace std; 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(); @@ -41,7 +44,8 @@ } // Default constructor -ObjectDetection::ObjectDetection() +ObjectDetection::ObjectDetection() : + display_box_text(1.0), display_boxes(1.0) { // Init effect properties init_effect_details(); @@ -67,213 +71,99 @@ // This method is required for all derived classes of EffectBase, and returns a // modified openshot::Frame object -std::shared_ptr ObjectDetection::GetFrame(std::shared_ptr frame, int64_t frame_number) -{ - // Get the frame's image - cv::Mat cv_image = frame->GetImageCV(); - - // Check if frame isn't NULL - if(cv_image.empty()){ - return frame; - } - - // Initialize the Qt rectangle that will hold the positions of the bounding-box - std::vector boxRects; - // Initialize the image of the TrackedObject child clip - std::vector> childClipImages; - - // Check if track data exists for the requested frame - if (detectionsData.find(frame_number) != detectionsData.end()) { - float fw = cv_image.size().width; - float fh = cv_image.size().height; - - DetectionData detections = detectionsData[frame_number]; - for(int i = 0; i 0 && - std::find(display_classes.begin(), display_classes.end(), classNames[detections.classIds.at(i)]) == display_classes.end()){ - continue; - } - - // Get the object id - int objectId = detections.objectIds.at(i); - - // Search for the object in the trackedObjects map - auto trackedObject_it = trackedObjects.find(objectId); - - // Cast the object as TrackedObjectBBox - std::shared_ptr trackedObject = std::static_pointer_cast(trackedObject_it->second); - - // Check if the tracked object has data for this frame - if (trackedObject->Contains(frame_number) && - trackedObject->visible.GetValue(frame_number) == 1) - { - // Get the bounding-box of given frame - BBox trackedBox = trackedObject->GetBox(frame_number); - bool draw_text = !display_box_text.GetValue(frame_number); - std::vector stroke_rgba = trackedObject->stroke.GetColorRGBA(frame_number); - int stroke_width = trackedObject->stroke_width.GetValue(frame_number); - float stroke_alpha = trackedObject->stroke_alpha.GetValue(frame_number); - std::vector bg_rgba = trackedObject->background.GetColorRGBA(frame_number); - float bg_alpha = trackedObject->background_alpha.GetValue(frame_number); - - cv::Rect2d box( - (int)( (trackedBox.cx-trackedBox.width/2)*fw), - (int)( (trackedBox.cy-trackedBox.height/2)*fh), - (int)( trackedBox.width*fw), - (int)( trackedBox.height*fh) - ); - - // If the Draw Box property is off, then make the box invisible - if (trackedObject->draw_box.GetValue(frame_number) == 0) - { - bg_alpha = 1.0; - stroke_alpha = 1.0; - } - - drawPred(detections.classIds.at(i), detections.confidences.at(i), - box, cv_image, detections.objectIds.at(i), bg_rgba, bg_alpha, 1, true, draw_text); - drawPred(detections.classIds.at(i), detections.confidences.at(i), - box, cv_image, detections.objectIds.at(i), stroke_rgba, stroke_alpha, stroke_width, false, draw_text); - - - // Get the Detected Object's child clip - if (trackedObject->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(trackedObject->ChildClipId()); - - if (childClip){ - // Get the image of the child clip for this frame - std::shared_ptr childClipFrame = childClip->GetFrame(frame_number); - childClipImages.push_back(childClipFrame->GetImage()); - - // Set the Qt rectangle with the bounding-box properties - QRectF boxRect; - boxRect.setRect((int)((trackedBox.cx-trackedBox.width/2)*fw), - (int)((trackedBox.cy - trackedBox.height/2)*fh), - (int)(trackedBox.width*fw), - (int)(trackedBox.height*fh)); - boxRects.push_back(boxRect); - } - } - } - } - } - } - - // Update Qt image with new Opencv frame - frame->SetImageCV(cv_image); - - // Set the bounding-box image with the Tracked Object's child clip image - if(boxRects.size() > 0){ - // Get the frame image - QImage frameImage = *(frame->GetImage()); - for(int i; i < boxRects.size();i++){ - // Set a Qt painter to the frame image - QPainter painter(&frameImage); - // Draw the child clip image inside the bounding-box - painter.drawImage(boxRects[i], *childClipImages[i]); - } - // Set the frame image as the composed image - frame->AddImage(std::make_shared(frameImage)); - } - - return frame; -} +std::shared_ptr ObjectDetection::GetFrame(std::shared_ptr frame, int64_t frame_number) { + // Get the frame's QImage + std::shared_ptr frame_image = frame->GetImage(); + + // Check if frame isn't NULL + if(!frame_image || frame_image->isNull()) { + return frame; + } + + QPainter painter(frame_image.get()); + painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + + if (detectionsData.find(frame_number) != detectionsData.end()) { + DetectionData detections = detectionsData[frame_number]; + for (int i = 0; i < detections.boxes.size(); i++) { + if (detections.confidences.at(i) < confidence_threshold || + (!display_classes.empty() && + std::find(display_classes.begin(), display_classes.end(), classNames[detections.classIds.at(i)]) == display_classes.end())) { + continue; + } + + int objectId = detections.objectIds.at(i); + auto trackedObject_it = trackedObjects.find(objectId); + + if (trackedObject_it != trackedObjects.end()) { + std::shared_ptr trackedObject = std::static_pointer_cast(trackedObject_it->second); + + Clip* parentClip = (Clip*) trackedObject->ParentClip(); + if (parentClip && trackedObject->Contains(frame_number) && trackedObject->visible.GetValue(frame_number) == 1) { + BBox trackedBox = trackedObject->GetBox(frame_number); + QRectF boxRect((trackedBox.cx - trackedBox.width / 2) * frame_image->width(), + (trackedBox.cy - trackedBox.height / 2) * frame_image->height(), + trackedBox.width * frame_image->width(), + trackedBox.height * frame_image->height()); + + // 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 brush for the background + QBrush brush(QColor(bg_rgba[0], bg_rgba[1], bg_rgba[2], 255 * bg_alpha)); + painter.setBrush(brush); + + 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_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); + } + } + } + } + } -void ObjectDetection::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); - - // 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(is_background){ - cv::Mat overlayFrame; - frame_image.copyTo(overlayFrame); - - // 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); + painter.end(); - // 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); - } -} - -void ObjectDetection::drawPred(int classId, float conf, cv::Rect2d box, cv::Mat& frame, int objectNumber, std::vector color, - float alpha, int thickness, bool is_background, bool display_text) -{ - - if(is_background){ - cv::Mat overlayFrame; - frame.copyTo(overlayFrame); - - //Draw a rectangle displaying the bounding box - cv::rectangle(overlayFrame, box, cv::Scalar(color[2],color[1],color[0]), cv::FILLED); - - // add opacity - cv::addWeighted(overlayFrame, 1-alpha, frame, alpha, 0, frame); - } - else{ - cv::Mat overlayFrame; - frame.copyTo(overlayFrame); - - //Draw a rectangle displaying the bounding box - cv::rectangle(overlayFrame, box, cv::Scalar(color[2],color[1],color[0]), thickness); - - if(display_text){ - //Get the label for the class name and its confidence - std::string label = cv::format("%.2f", conf); - if (!classNames.empty()) - { - CV_Assert(classId < (int)classNames.size()); - label = classNames[classId] + ":" + label; - } - - //Display the label at the top of the bounding box - int baseLine; - cv::Size labelSize = cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine); - - double left = box.x; - double top = std::max((int)box.y, labelSize.height); - - cv::rectangle(overlayFrame, cv::Point(left, top - round(1.025*labelSize.height)), cv::Point(left + round(1.025*labelSize.width), top + baseLine), - cv::Scalar(color[2],color[1],color[0]), cv::FILLED); - putText(overlayFrame, label, cv::Point(left+1, top), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0,0,0),1); - } - // add opacity - cv::addWeighted(overlayFrame, 1-alpha, frame, alpha, 0, frame); - } + // The frame's QImage has been modified in place, so we just return the original frame + return frame; } // Load protobuf data file @@ -348,6 +238,7 @@ { // There is no tracked object with that id, so insert a new one TrackedObjectBBox trackedObj((int)classesColor[classId](0), (int)classesColor[classId](1), (int)classesColor[classId](2), (int)0); + trackedObj.stroke_alpha = Keyframe(1.0); trackedObj.AddBox(id, x+(w/2), y+(h/2), w, h, 0.0); std::shared_ptr trackedObjPtr = std::make_shared(trackedObj); @@ -387,6 +278,7 @@ Json::Value root; root["visible_objects_index"] = Json::Value(Json::arrayValue); root["visible_objects_id"] = Json::Value(Json::arrayValue); + root["visible_class_names"] = Json::Value(Json::arrayValue); // Check if track data exists for the requested frame if (detectionsData.find(frame_number) == detectionsData.end()){ @@ -401,11 +293,21 @@ continue; } - // Just display selected classes - if( display_classes.size() > 0 && - std::find(display_classes.begin(), display_classes.end(), classNames[detections.classIds.at(i)]) == display_classes.end()){ - continue; - } + // Get class name of tracked object + auto className = classNames[detections.classIds.at(i)]; + + // If display_classes is not empty, check if className is in it + if (!display_classes.empty()) { + auto it = std::find(display_classes.begin(), display_classes.end(), className); + if (it == display_classes.end()) { + // If not in display_classes, skip this detection + continue; + } + root["visible_class_names"].append(className); + } else { + // include all class names + root["visible_class_names"].append(className); + } int objectId = detections.objectIds.at(i); // Search for the object in the trackedObjects map @@ -442,6 +344,7 @@ 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; @@ -498,18 +401,27 @@ if (!root["display_box_text"].isNull()) display_box_text.SetJsonValue(root["display_box_text"]); - if (!root["class_filter"].isNull()){ - class_filter = root["class_filter"].asString(); - std::stringstream ss(class_filter); - display_classes.clear(); - while( ss.good() ) - { - // Parse comma separated string - std::string substr; - std::getline( ss, substr, ',' ); - display_classes.push_back( substr ); - } - } + if (!root["display_boxes"].isNull()) + display_boxes.SetJsonValue(root["display_boxes"]); + + if (!root["class_filter"].isNull()) { + class_filter = root["class_filter"].asString(); + + // Convert the class_filter to a QString + QString qClassFilter = QString::fromStdString(root["class_filter"].asString()); + + // Split the QString by commas and automatically trim each resulting string + QStringList classList = qClassFilter.split(',', QString::SkipEmptyParts); + display_classes.clear(); + + // Iterate over the QStringList and add each trimmed, non-empty string + for (const QString &classItem : classList) { + QString trimmedItem = classItem.trimmed().toLower(); + if (!trimmedItem.isEmpty()) { + display_classes.push_back(trimmedItem.toStdString()); + } + } + } if (!root["objects"].isNull()){ for (auto const& trackedObject : trackedObjects){ @@ -551,9 +463,13 @@ 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.0, false, requested_frame); - root["display_box_text"]["choices"].append(add_property_choice_json("Off", 1, display_box_text.GetValue(requested_frame))); - root["display_box_text"]["choices"].append(add_property_choice_json("On", 0, display_box_text.GetValue(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 -Nru libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/effects/ObjectDetection.h libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/effects/ObjectDetection.h --- libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/effects/ObjectDetection.h 2024-02-18 17:19:58.000000000 +0000 +++ libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/effects/ObjectDetection.h 2024-03-26 19:02:17.000000000 +0000 @@ -60,25 +60,23 @@ std::string protobuf_data_path; std::map detectionsData; 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; /// Init effect settings void init_effect_details(); - /// Draw bounding box with class and score text - void drawPred(int classId, float conf, cv::Rect2d box, cv::Mat& frame, int objectNumber, std::vector color, float alpha, - int thickness, bool is_background, bool draw_text); - /// Draw rotated rectangle with alpha channel - void DrawRectangleRGBA(cv::Mat &frame_image, cv::RotatedRect box, std::vector color, float alpha, int thickness, bool is_background); - public: /// Index of the Tracked Object that was selected to modify it's properties diff -Nru libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/effects/Tracker.cpp libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/effects/Tracker.cpp --- libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/effects/Tracker.cpp 2024-02-18 17:19:58.000000000 +0000 +++ libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/effects/Tracker.cpp 2024-03-26 19:02:17.000000000 +0000 @@ -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,55 @@ // 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(); - - // 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.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 given frame - BBox fd = trackedData->GetBox(frame_number); - - // 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); - - // 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) ); - - DrawRectangleRGBA(frame_image, box, bg_rgba, bg_alpha, 1, true); - DrawRectangleRGBA(frame_image, box, stroke_rgba, stroke_alpha, stroke_width, false); - } - - // 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 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) ); - } - } - } - - } - - // 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); - - // Set the bounding-box image with the Tracked Object's child clip image - if (childClipImage){ - // Get the frame image - QImage frameImage = *(frame->GetImage()); - - // Set a Qt painter to the frame image - QPainter painter(&frameImage); - - // 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)); - } +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(); + + // 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()); + + // Get the bounding-box of the 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) { + painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + + // 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); + + // 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); + + // Draw the rounded rectangle + painter.drawRoundedRect(boxRect, bg_corner, bg_corner); + } - return frame; -} + painter.end(); + } -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); - - // 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(is_background){ - cv::Mat overlayFrame; - frame_image.copyTo(overlayFrame); - - // 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); - } + // No need to set the image back to the frame, as we directly modified the frame's QImage + return frame; } // Get the indexes and IDs of all visible objects in the given frame diff -Nru libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/effects/Tracker.h libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/effects/Tracker.h --- libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/src/effects/Tracker.h 2024-02-18 17:19:58.000000000 +0000 +++ libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/src/effects/Tracker.h 2024-03-26 19:02:17.000000000 +0000 @@ -71,8 +71,6 @@ /// 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 diff -Nru libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/tests/KeyFrame.cpp libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/tests/KeyFrame.cpp --- libopenshot-0.3.2+dfsg2+1183+202402181720+daily~ubuntu20.04.1/tests/KeyFrame.cpp 2024-02-18 17:19:58.000000000 +0000 +++ libopenshot-0.3.2+dfsg2+1184+202403261902+daily~ubuntu20.04.1/tests/KeyFrame.cpp 2024-03-26 19:02:17.000000000 +0000 @@ -583,10 +583,10 @@ CHECK(kfb.rotation.GetInt(1) == 0); CHECK(kfb.stroke_width.GetInt(1) == 2); - CHECK(kfb.stroke_alpha.GetInt(1) == 0); + CHECK(kfb.stroke_alpha.GetValue(1) == Approx(0.7f).margin(0.0001)); - CHECK(kfb.background_alpha .GetInt(1)== 1); - CHECK(kfb.background_corner.GetInt(1) == 0); + CHECK(kfb.background_alpha .GetInt(1) == 0); + CHECK(kfb.background_corner.GetInt(1) == 12); CHECK(kfb.stroke.red.GetInt(1) == 62); CHECK(kfb.stroke.green.GetInt(1) == 143); @@ -596,8 +596,7 @@ CHECK(kfb.background.red.GetInt(1) == 0); CHECK(kfb.background.green.GetInt(1) == 0); CHECK(kfb.background.blue.GetInt(1) == 255); - CHECK(kfb.background.alpha.GetInt(1) == 0); - + CHECK(kfb.background.alpha.GetInt(1) == 212); } TEST_CASE( "TrackedObjectBBox AddBox and RemoveBox", "[libopenshot][keyframe]" )