Skip to content

Commit feef798

Browse files
committed
Add tests for OVYoloXTensorsToDetectionsCalculator and DetectionColorByIdCalculator
1 parent 9b39786 commit feef798

5 files changed

Lines changed: 294 additions & 1 deletion

File tree

src/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2075,6 +2075,8 @@ cc_test(
20752075
"test/http_openai_handler_test.cpp",
20762076
"test/multipart_calculator_test.cpp",
20772077
"test/llm/assisted_decoding_test.cpp",
2078+
"test/bytetrack/ov_yolox_tensors_to_detections_calculator_test.cpp",
2079+
"test/bytetrack/detection_color_by_id_calculator_test.cpp",
20782080
"test/llm/llmnode_test.cpp",
20792081
"test/llm/tokenize_endpoint_test.cpp",
20802082
"test/llm/max_model_length_test.cpp",

src/bytetrack/utils/detection_color_by_id_calculator.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ class DetectionColorByIdCalculator : public CalculatorBase {
4141
thickness_ = options.has_thickness() ? options.thickness() : 5.0f;
4242
saturation_ = options.has_saturation() ? options.saturation() : 0.85f;
4343
value_ = options.has_value() ? options.value() : 0.95f;
44+
RET_CHECK_GE(saturation_, 0);
45+
RET_CHECK_LE(saturation_, 1);
46+
RET_CHECK_GE(value_, 0);
47+
RET_CHECK_LE(value_, 1);
4448
return absl::OkStatus();
4549
}
4650
absl::Status Process(CalculatorContext* cc) override {
@@ -50,6 +54,10 @@ class DetectionColorByIdCalculator : public CalculatorBase {
5054
auto render_data = std::make_unique<RenderData>();
5155

5256
for (const auto& det : detections) {
57+
if (!det.has_detection_id()) {
58+
LOG(WARNING) << "Detection missing detection_id, skipping.";
59+
continue;
60+
}
5361
int id = det.detection_id();
5462
mediapipe::Color color = IdToColor(id);
5563

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
//*****************************************************************************
2+
// Copyright 2026 Intel Corporation
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
//*****************************************************************************
16+
17+
#include <memory>
18+
#include <string>
19+
#include <vector>
20+
21+
#include <gtest/gtest.h>
22+
23+
#include "absl/strings/str_format.h"
24+
#pragma GCC diagnostic push
25+
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
26+
#include "mediapipe/framework/calculator_framework.h"
27+
#include "mediapipe/framework/calculator_runner.h"
28+
#include "mediapipe/framework/formats/detection.pb.h"
29+
#include "mediapipe/framework/packet.h"
30+
#include "mediapipe/util/render_data.pb.h"
31+
#include "mediapipe/util/color.pb.h"
32+
#pragma GCC diagnostic pop
33+
34+
#include "src/bytetrack/utils/detection_color_by_id_calculator.pb.h"
35+
36+
namespace mediapipe {
37+
namespace {
38+
class DetectionColorByIdCalculatorTest : public ::testing::Test {
39+
protected:
40+
std::unique_ptr<CalculatorRunner> MakeRunner(float saturation, float value, float thickness = 5.0f) {
41+
std::string pbtxt = absl::StrFormat(R"pb(
42+
calculator: "DetectionColorByIdCalculator"
43+
input_stream: "DETECTIONS:detections"
44+
output_stream: "RENDER_DATA:render_data"
45+
node_options: {
46+
[type.googleapis.com/mediapipe.DetectionColorByIdCalculatorOptions]: {
47+
saturation: %f
48+
value: %f
49+
thickness: %f
50+
}
51+
}
52+
)pb",
53+
saturation, value, thickness);
54+
return std::make_unique<CalculatorRunner>(pbtxt);
55+
}
56+
57+
absl::Status RunWithDetections(CalculatorRunner& runner, std::vector<Detection> detections) {
58+
auto input = std::make_unique<std::vector<Detection>>(std::move(detections));
59+
runner.MutableInputs()->Tag("DETECTIONS").packets.push_back(Adopt(input.release()).At(Timestamp(0)));
60+
return runner.Run();
61+
}
62+
63+
Detection MakeDetection(int id, const std::string& label = "", float score = -1.0f) {
64+
Detection det;
65+
det.set_detection_id(id);
66+
auto* bbox = det.mutable_location_data()->mutable_relative_bounding_box();
67+
bbox->set_xmin(0.1f);
68+
bbox->set_ymin(0.1f);
69+
bbox->set_width(0.2f);
70+
bbox->set_height(0.2f);
71+
if (!label.empty()) {
72+
det.add_label(label);
73+
}
74+
if (score >= 0.0f) {
75+
det.add_score(score);
76+
}
77+
return det;
78+
}
79+
80+
Detection MakeDetectionWithoutId() {
81+
Detection det;
82+
auto* bbox = det.mutable_location_data()->mutable_relative_bounding_box();
83+
bbox->set_xmin(0.1f);
84+
bbox->set_ymin(0.1f);
85+
bbox->set_width(0.2f);
86+
bbox->set_height(0.2f);
87+
return det;
88+
}
89+
};
90+
91+
// These tests should fail because saturation and value go out of bounds
92+
93+
TEST_F(DetectionColorByIdCalculatorTest, SaturationBelowZeroFailsOpen) {
94+
auto runner = MakeRunner(-0.1f, 0.5f);
95+
EXPECT_FALSE(RunWithDetections(*runner, {}).ok());
96+
}
97+
98+
TEST_F(DetectionColorByIdCalculatorTest, SaturationAboveOneFailsOpen) {
99+
auto runner = MakeRunner(1.1f, 0.5f);
100+
EXPECT_FALSE(RunWithDetections(*runner, {}).ok());
101+
}
102+
103+
TEST_F(DetectionColorByIdCalculatorTest, ValueBelowZeroFailsOpen) {
104+
auto runner = MakeRunner(0.5f, -0.1f);
105+
EXPECT_FALSE(RunWithDetections(*runner, {}).ok());
106+
}
107+
108+
TEST_F(DetectionColorByIdCalculatorTest, ValueAboveOneFailsOpen) {
109+
auto runner = MakeRunner(0.5f, 1.1f);
110+
EXPECT_FALSE(RunWithDetections(*runner, {}).ok());
111+
}
112+
113+
// These tests should pass because saturation and value are within bounds
114+
115+
TEST_F(DetectionColorByIdCalculatorTest, SaturationAtZeroValid) {
116+
auto runner = MakeRunner(0.0f, 0.5f);
117+
EXPECT_TRUE(RunWithDetections(*runner, {}).ok());
118+
}
119+
120+
TEST_F(DetectionColorByIdCalculatorTest, SaturationAtOneValid) {
121+
auto runner = MakeRunner(1.0f, 0.5f);
122+
EXPECT_TRUE(RunWithDetections(*runner, {}).ok());
123+
}
124+
125+
TEST_F(DetectionColorByIdCalculatorTest, ValueAtZeroValid) {
126+
auto runner = MakeRunner(0.5f, 0.0f);
127+
EXPECT_TRUE(RunWithDetections(*runner, {}).ok());
128+
}
129+
130+
TEST_F(DetectionColorByIdCalculatorTest, ValueAtOneValid) {
131+
auto runner = MakeRunner(0.5f, 1.0f);
132+
EXPECT_TRUE(RunWithDetections(*runner, {}).ok());
133+
}
134+
135+
// test with passing a detection without id
136+
137+
TEST_F(DetectionColorByIdCalculatorTest, SkipsDetectionWithoutId) {
138+
auto runner = MakeRunner(0.85f, 0.95f);
139+
140+
std::vector<Detection> detections;
141+
detections.push_back(MakeDetection(1));
142+
detections.push_back(MakeDetectionWithoutId());
143+
144+
ASSERT_TRUE(RunWithDetections(*runner, detections).ok());
145+
146+
const auto& output = runner->Outputs().Tag("RENDER_DATA").packets;
147+
ASSERT_EQ(output.size(), 1);
148+
const auto& render_data = output[0].Get<RenderData>();
149+
150+
// 1 valid detection -> 2 annotations (box + label). The id-less one is skipped.
151+
EXPECT_EQ(render_data.render_annotations_size(), 2);
152+
}
153+
154+
TEST_F(DetectionColorByIdCalculatorTest, AllDetectionsMissingId_ProducesEmptyRenderData) {
155+
auto runner = MakeRunner(0.85f, 0.95f);
156+
157+
std::vector<Detection> detections;
158+
detections.push_back(MakeDetectionWithoutId());
159+
detections.push_back(MakeDetectionWithoutId());
160+
161+
ASSERT_TRUE(RunWithDetections(*runner, detections).ok());
162+
163+
const auto& output = runner->Outputs().Tag("RENDER_DATA").packets;
164+
ASSERT_EQ(output.size(), 1);
165+
const auto& render_data = output[0].Get<RenderData>();
166+
// output should be zero because all the non id detections are skipped
167+
EXPECT_EQ(render_data.render_annotations_size(), 0);
168+
}
169+
170+
} // namespace
171+
} // namespace mediapipe
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
//*****************************************************************************
2+
// Copyright 2026 Intel Corporation
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
//*****************************************************************************
16+
17+
#include <memory>
18+
#include <string>
19+
#include <vector>
20+
21+
#include <gtest/gtest.h>
22+
#include <openvino/openvino.hpp>
23+
24+
#include "absl/strings/str_format.h"
25+
26+
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
27+
#include "mediapipe/framework/calculator_framework.h"
28+
#include "mediapipe/framework/calculator_runner.h"
29+
#include "mediapipe/framework/formats/detection.pb.h"
30+
#include "mediapipe/framework/packet.h"
31+
#include "mediapipe/util/render_data.pb.h"
32+
#include "mediapipe/util/color.pb.h"
33+
#pragma GCC diagnostic pop
34+
35+
#include "src/yolox/ov_yolox_tensors_to_detections_calculator.pb.h"
36+
37+
namespace mediapipe {
38+
namespace {
39+
class OVYoloXTensorsToDetectionsCalculatorTest : public ::testing::Test {
40+
protected:
41+
std::unique_ptr<CalculatorRunner> MakeRunner(float conf_thresh, float input_size) {
42+
std::string pbtxt = absl::StrFormat(R"pb(
43+
node {
44+
calculator: "OVYoloXTensorsToDetectionsCalculator"
45+
input_stream: "TENSORS:detection_tensors"
46+
output_stream: "DETECTIONS:detections"
47+
48+
node_options: {
49+
[type.googleapis.com/mediapipe.OVYoloXTensorsToDetectionsCalculatorOptions] {
50+
conf_thresh: %f
51+
input_size: %f
52+
}
53+
}
54+
})pb",
55+
conf_thresh, input_size);
56+
return std::make_unique<CalculatorRunner>(pbtxt);
57+
}
58+
absl::Status RunOpenOnly(CalculatorRunner& runner) {
59+
auto tensors = std::make_unique<std::vector<ov::Tensor>>();
60+
runner.MutableInputs()->Tag("TENSORS").packets.push_back(
61+
Adopt(tensors.release()).At(Timestamp(0)));
62+
return runner.Run();
63+
}
64+
};
65+
66+
// confidence threshold out of bounds
67+
68+
TEST_F(OVYoloXTensorsToDetectionsCalculatorTest, ConfThreshBelowZeroFailsOpen) {
69+
auto runner = MakeRunner(-0.1f, 416.0f);
70+
EXPECT_FALSE(RunOpenOnly(*runner).ok());
71+
}
72+
73+
TEST_F(OVYoloXTensorsToDetectionsCalculatorTest, ConfThreshAboveOneFailsOpen) {
74+
auto runner = MakeRunner(1.1f, 416.0f);
75+
EXPECT_FALSE(RunOpenOnly(*runner).ok());
76+
}
77+
78+
// confidence threshold within bounds
79+
80+
TEST_F(OVYoloXTensorsToDetectionsCalculatorTest, ConfThreshAtZeroValid) {
81+
auto runner = MakeRunner(0.0f, 416.0f);
82+
EXPECT_TRUE(RunOpenOnly(*runner).ok());
83+
}
84+
85+
TEST_F(OVYoloXTensorsToDetectionsCalculatorTest, ConfThreshAtOneValid) {
86+
auto runner = MakeRunner(1.0f, 416.0f);
87+
EXPECT_TRUE(RunOpenOnly(*runner).ok());
88+
}
89+
90+
// input size out of bounds
91+
92+
TEST_F(OVYoloXTensorsToDetectionsCalculatorTest, InputSizeAtZeroFailsOpen) {
93+
auto runner = MakeRunner(0.5f, 0.0f);
94+
EXPECT_FALSE(RunOpenOnly(*runner).ok());
95+
}
96+
97+
TEST_F(OVYoloXTensorsToDetectionsCalculatorTest, InputSizeNegative_FailsOpen) {
98+
auto runner = MakeRunner(0.5f, -416.0f);
99+
EXPECT_FALSE(RunOpenOnly(*runner).ok());
100+
}
101+
102+
// this check passes because input size is in valid range (0,inf)
103+
104+
TEST_F(OVYoloXTensorsToDetectionsCalculatorTest, InputSizeValidPositive_Valid) {
105+
auto runner = MakeRunner(/*conf_thresh=*/0.5f, /*input_size=*/416.0f);
106+
EXPECT_TRUE(RunOpenOnly(*runner).ok());
107+
}
108+
109+
} // namespace
110+
} // namespace mediapipe

src/yolox/ov_yolox_tensors_to_detections_calculator.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ class OVYoloXTensorsToDetectionsCalculator : public CalculatorBase {
6161

6262
inputSize_ =
6363
options.has_input_size() ? options.input_size() : 416.0f;
64-
64+
RET_CHECK_GE(confidenceThreshold_, 0);
65+
RET_CHECK_LE(confidenceThreshold_, 1);
66+
RET_CHECK_GT(inputSize_, 0);
6567
return absl::OkStatus();
6668
}
6769

0 commit comments

Comments
 (0)