blob: 4b23f3e716a7df1a5709b123977c1b73cf1a3c10 [file] [log] [blame]
Brian Silverman5eec8b92019-03-10 15:14:31 -07001#include "aos/vision/blob/threshold.h"
2
Brian Silverman4482db52019-03-10 16:14:48 -07003#include <random>
Brian Silverman5eec8b92019-03-10 15:14:31 -07004#include <vector>
5
Brian Silverman5eec8b92019-03-10 15:14:31 -07006#include "gmock/gmock.h"
7#include "gtest/gtest.h"
8
Philipp Schrader790cb542023-07-05 21:06:52 -07009#include "aos/vision/blob/range_image.h"
10#include "aos/vision/image/image_types.h"
11
Stephan Pleinesf63bde82024-01-13 15:59:33 -080012namespace aos::vision::testing {
Brian Silverman5eec8b92019-03-10 15:14:31 -070013
14class YuyvYThresholdTest : public ::testing::Test {
Brian Silverman4482db52019-03-10 16:14:48 -070015 public:
16 std::vector<char> RandomImage(ImageFormat format) {
17 std::vector<char> result;
18 result.resize(format.w * format.h * 2);
19 std::uniform_int_distribution<char> distribution(
20 std::numeric_limits<char>::min(), std::numeric_limits<char>::max());
21 for (size_t i = 0; i < result.size(); ++i) {
22 result[i] = distribution(generator_);
23 }
24 return result;
25 }
26
27 private:
28 std::minstd_rand generator_;
Brian Silverman5eec8b92019-03-10 15:14:31 -070029};
30
31// Verifies that a simple image is thresholded correctly.
32//
33// Specifically, we want to get this result from the thresholding:
Brian Silverman4482db52019-03-10 16:14:48 -070034// --+-----
35// +------+
36// -++++++-
37// +++-++++
38// --------
39// ++++-+++
40// ++++++++
41// +-+-+--+
Brian Silverman5eec8b92019-03-10 15:14:31 -070042TEST_F(YuyvYThresholdTest, SimpleImage) {
43 ImageFormat format;
Brian Silverman4482db52019-03-10 16:14:48 -070044 format.w = 8;
Brian Silverman5eec8b92019-03-10 15:14:31 -070045 format.h = 8;
46
47 std::vector<std::vector<ImageRange>> expected_ranges;
48 std::vector<char> image;
Brian Silverman4482db52019-03-10 16:14:48 -070049 image.resize(8 * 8 * 2);
50 // --+-----
Alex Perrycb7da4b2019-08-28 19:35:56 -070051 image[0 * 2 + 0 * 16] = static_cast<char>(0);
52 image[1 * 2 + 0 * 16] = static_cast<char>(0);
53 image[2 * 2 + 0 * 16] = static_cast<char>(128);
54 image[3 * 2 + 0 * 16] = static_cast<char>(127);
55 image[4 * 2 + 0 * 16] = static_cast<char>(0);
56 image[5 * 2 + 0 * 16] = static_cast<char>(0);
57 image[6 * 2 + 0 * 16] = static_cast<char>(0);
58 image[7 * 2 + 0 * 16] = static_cast<char>(0);
Brian Silverman5eec8b92019-03-10 15:14:31 -070059 expected_ranges.push_back({{{2, 3}}});
Brian Silverman4482db52019-03-10 16:14:48 -070060 // +------+
Alex Perrycb7da4b2019-08-28 19:35:56 -070061 image[0 * 2 + 1 * 16] = static_cast<char>(128);
62 image[1 * 2 + 1 * 16] = static_cast<char>(0);
63 image[2 * 2 + 1 * 16] = static_cast<char>(0);
64 image[3 * 2 + 1 * 16] = static_cast<char>(10);
65 image[4 * 2 + 1 * 16] = static_cast<char>(30);
66 image[5 * 2 + 1 * 16] = static_cast<char>(50);
67 image[6 * 2 + 1 * 16] = static_cast<char>(70);
68 image[7 * 2 + 1 * 16] = static_cast<char>(255);
Brian Silverman4482db52019-03-10 16:14:48 -070069 expected_ranges.push_back({{{0, 1}, {7, 8}}});
70 // -++++++-
Alex Perrycb7da4b2019-08-28 19:35:56 -070071 image[0 * 2 + 2 * 16] = static_cast<char>(73);
72 image[1 * 2 + 2 * 16] = static_cast<char>(246);
73 image[2 * 2 + 2 * 16] = static_cast<char>(247);
74 image[3 * 2 + 2 * 16] = static_cast<char>(248);
75 image[4 * 2 + 2 * 16] = static_cast<char>(249);
76 image[5 * 2 + 2 * 16] = static_cast<char>(250);
77 image[6 * 2 + 2 * 16] = static_cast<char>(250);
78 image[7 * 2 + 2 * 16] = static_cast<char>(45);
Brian Silverman4482db52019-03-10 16:14:48 -070079 expected_ranges.push_back({{{1, 7}}});
80 // +++-++++
Alex Perrycb7da4b2019-08-28 19:35:56 -070081 image[0 * 2 + 3 * 16] = static_cast<char>(128);
82 image[1 * 2 + 3 * 16] = static_cast<char>(134);
83 image[2 * 2 + 3 * 16] = static_cast<char>(250);
84 image[3 * 2 + 3 * 16] = static_cast<char>(0);
85 image[4 * 2 + 3 * 16] = static_cast<char>(230);
86 image[5 * 2 + 3 * 16] = static_cast<char>(230);
87 image[6 * 2 + 3 * 16] = static_cast<char>(230);
88 image[7 * 2 + 3 * 16] = static_cast<char>(210);
Brian Silverman4482db52019-03-10 16:14:48 -070089 expected_ranges.push_back({{{0, 3}, {4, 8}}});
90 // --------
Alex Perrycb7da4b2019-08-28 19:35:56 -070091 image[0 * 2 + 4 * 16] = static_cast<char>(7);
92 image[1 * 2 + 4 * 16] = static_cast<char>(120);
93 image[2 * 2 + 4 * 16] = static_cast<char>(127);
94 image[3 * 2 + 4 * 16] = static_cast<char>(0);
95 image[4 * 2 + 4 * 16] = static_cast<char>(50);
96 image[5 * 2 + 4 * 16] = static_cast<char>(80);
97 image[6 * 2 + 4 * 16] = static_cast<char>(110);
98 image[7 * 2 + 4 * 16] = static_cast<char>(25);
Brian Silverman5eec8b92019-03-10 15:14:31 -070099 expected_ranges.push_back({{}});
Brian Silverman4482db52019-03-10 16:14:48 -0700100 // ++++-+++
Alex Perrycb7da4b2019-08-28 19:35:56 -0700101 image[0 * 2 + 5 * 16] = static_cast<char>(140);
102 image[1 * 2 + 5 * 16] = static_cast<char>(140);
103 image[2 * 2 + 5 * 16] = static_cast<char>(140);
104 image[3 * 2 + 5 * 16] = static_cast<char>(140);
105 image[4 * 2 + 5 * 16] = static_cast<char>(0);
106 image[5 * 2 + 5 * 16] = static_cast<char>(140);
107 image[6 * 2 + 5 * 16] = static_cast<char>(140);
108 image[7 * 2 + 5 * 16] = static_cast<char>(140);
Brian Silverman4482db52019-03-10 16:14:48 -0700109 expected_ranges.push_back({{{0, 4}, {5, 8}}});
110 // ++++++++
Alex Perrycb7da4b2019-08-28 19:35:56 -0700111 image[0 * 2 + 6 * 16] = static_cast<char>(128);
112 image[1 * 2 + 6 * 16] = static_cast<char>(128);
113 image[2 * 2 + 6 * 16] = static_cast<char>(128);
114 image[3 * 2 + 6 * 16] = static_cast<char>(128);
115 image[4 * 2 + 6 * 16] = static_cast<char>(128);
116 image[5 * 2 + 6 * 16] = static_cast<char>(128);
117 image[6 * 2 + 6 * 16] = static_cast<char>(128);
118 image[7 * 2 + 6 * 16] = static_cast<char>(128);
Brian Silverman4482db52019-03-10 16:14:48 -0700119 expected_ranges.push_back({{{0, 8}}});
120 // +-+-+--+
Alex Perrycb7da4b2019-08-28 19:35:56 -0700121 image[0 * 2 + 7 * 16] = static_cast<char>(200);
122 image[1 * 2 + 7 * 16] = static_cast<char>(0);
123 image[2 * 2 + 7 * 16] = static_cast<char>(200);
124 image[3 * 2 + 7 * 16] = static_cast<char>(0);
125 image[4 * 2 + 7 * 16] = static_cast<char>(200);
126 image[5 * 2 + 7 * 16] = static_cast<char>(0);
127 image[6 * 2 + 7 * 16] = static_cast<char>(0);
128 image[7 * 2 + 7 * 16] = static_cast<char>(200);
Brian Silverman4482db52019-03-10 16:14:48 -0700129 expected_ranges.push_back({{{0, 1}, {2, 3}, {4, 5}, {7, 8}}});
Brian Silverman5eec8b92019-03-10 15:14:31 -0700130 const RangeImage expected_result(0, std::move(expected_ranges));
131
132 const auto slow_result = SlowYuyvYThreshold(format, image.data(), 127);
133 ASSERT_EQ(expected_result, slow_result);
Brian Silverman4482db52019-03-10 16:14:48 -0700134 const auto fast_result = FastYuyvYThreshold(format, image.data(), 127);
135 ASSERT_EQ(expected_result, fast_result);
136}
137
138// Verifies that a couple of completely random images match.
139TEST_F(YuyvYThresholdTest, Random) {
140 for (int i = 0; i < 10; ++i) {
141 ImageFormat small_format;
142 small_format.w = 16;
143 small_format.h = 16;
144 const auto small_image = RandomImage(small_format);
145 const auto slow_result =
146 SlowYuyvYThreshold(small_format, small_image.data(), 127);
147 const auto fast_result =
148 FastYuyvYThreshold(small_format, small_image.data(), 127);
149 ASSERT_EQ(slow_result, fast_result);
150 }
151 for (int i = 0; i < 10; ++i) {
152 ImageFormat large_format;
153 large_format.w = 1024;
154 large_format.h = 512;
155 const auto large_image = RandomImage(large_format);
156 const auto slow_result =
157 SlowYuyvYThreshold(large_format, large_image.data(), 127);
158 const auto fast_result =
159 FastYuyvYThreshold(large_format, large_image.data(), 127);
160 ASSERT_EQ(slow_result, fast_result);
161 }
162}
163
164// Verifies that changing the U and V values doesn't affect the result.
165TEST_F(YuyvYThresholdTest, UVIgnored) {
166 ImageFormat format;
167 format.w = 32;
168 format.h = 20;
169 const auto baseline_image = RandomImage(format);
170 const auto baseline_result =
171 SlowYuyvYThreshold(format, baseline_image.data(), 127);
172 for (int i = 0; i < 5; ++i) {
173 auto tweaked_image = RandomImage(format);
174 for (int y = 0; y < format.h; ++y) {
175 for (int x = 0; x < format.w; ++x) {
176 tweaked_image[x * 2 + y * format.w * 2] =
177 baseline_image[x * 2 + y * format.w * 2];
178 }
179 }
180
181 const auto slow_result =
182 SlowYuyvYThreshold(format, tweaked_image.data(), 127);
183 ASSERT_EQ(baseline_result, slow_result);
184 const auto fast_result =
185 FastYuyvYThreshold(format, tweaked_image.data(), 127);
186 ASSERT_EQ(baseline_result, fast_result);
187 }
Brian Silverman5eec8b92019-03-10 15:14:31 -0700188}
189
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800190} // namespace aos::vision::testing