73 lines
3.3 KiB
Diff
73 lines
3.3 KiB
Diff
# UNDF: UNDF-2026-000001118
|
|
# UNDF: (leave blank)
|
|
# CWE-407: Algorithmic Complexity (list membership inside per-frame loop)
|
|
# libopenshot ObjectDetection effect: display_classes filter uses std::find on a
|
|
# std::vector<std::string> inside the per-detection loop that runs every rendered frame.
|
|
#
|
|
# Defective path (src/effects/ObjectDetection.cpp, two sites):
|
|
#
|
|
# Site 1 (line 76-79, GetFrame hot path):
|
|
# for (int i = 0; i < detections.boxes.size(); i++) {
|
|
# if (!display_classes.empty() &&
|
|
# std::find(display_classes.begin(), display_classes.end(),
|
|
# classNames[detections.classIds.at(i)]) == display_classes.end())
|
|
# continue;
|
|
# }
|
|
#
|
|
# Site 2 (line 269-284, GetPropertiesJSON):
|
|
# for (int i = 0; i < detections.boxes.size(); i++) {
|
|
# auto it = std::find(display_classes.begin(), display_classes.end(), className);
|
|
# }
|
|
#
|
|
# Complexity: O(D * C) per frame, where D = detections per frame, C = |display_classes|.
|
|
# With D=50 detections and C=20 filter classes this is 1000 string comparisons per frame.
|
|
# At 30 fps over a 1-minute video: 1,800,000 comparisons vs 90,000 with a hash set (20x).
|
|
#
|
|
# Fix: replace display_classes std::vector<std::string> with
|
|
# std::unordered_set<std::string>. Membership test drops from O(C) to O(1).
|
|
# SetJsonValue still populates the set with the same string values.
|
|
#
|
|
# Severity: MEDIUM. Hot path (every rendered frame), constant-factor overhead per
|
|
# detection scales with both frame rate and filter list length.
|
|
--- a/src/effects/ObjectDetection.h
|
|
+++ b/src/effects/ObjectDetection.h
|
|
@@ -72,7 +72,7 @@ namespace openshot {
|
|
float confidence_threshold;
|
|
bool display_box;
|
|
bool display_label;
|
|
- std::vector<std::string> display_classes;
|
|
+ std::unordered_set<std::string> display_classes;
|
|
|
|
/// @brief Default constructor
|
|
ObjectDetection(std::string clipObDetectDataPath="");
|
|
@@ -1,6 +1,7 @@
|
|
#include <string>
|
|
+#include <unordered_set>
|
|
|
|
--- a/src/effects/ObjectDetection.cpp
|
|
+++ b/src/effects/ObjectDetection.cpp
|
|
@@ -76,7 +76,7 @@ std::shared_ptr<Frame> ObjectDetection::GetFrame(std::shared_ptr<Frame> frame,
|
|
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())) {
|
|
+ display_classes.find(classNames[detections.classIds.at(i)]) == display_classes.end())) {
|
|
continue;
|
|
}
|
|
|
|
@@ -279,7 +279,7 @@ std::string ObjectDetection::GetPropertiesJSON(int64_t requested_frame) const {
|
|
if (!display_classes.empty()) {
|
|
- auto it = std::find(display_classes.begin(), display_classes.end(), className);
|
|
- if (it == display_classes.end()) {
|
|
+ if (display_classes.find(className) == display_classes.end()) {
|
|
continue;
|
|
}
|
|
|
|
@@ -388,7 +388,6 @@ void ObjectDetection::SetJsonValue(const Json::Value root) {
|
|
display_classes.clear();
|
|
if (!root["display_classes"].isNull()) {
|
|
for (const auto &s : root["display_classes"]) {
|
|
- display_classes.push_back(s.toStyledString());
|
|
+ display_classes.insert(s.toStyledString());
|
|
}
|
|
}
|