blob: 67f71163ab806be63d5779f3b2731e89f6386972 [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
Brian Silverman5eec8b92019-03-10 15:14:31 -070012namespace aos {
13namespace vision {
14namespace testing {
15
16class YuyvYThresholdTest : public ::testing::Test {
Brian Silverman4482db52019-03-10 16:14:48 -070017 public:
18 std::vector<char> RandomImage(ImageFormat format) {
19 std::vector<char> result;
20 result.resize(format.w * format.h * 2);
21 std::uniform_int_distribution<char> distribution(
22 std::numeric_limits<char>::min(), std::numeric_limits<char>::max());
23 for (size_t i = 0; i < result.size(); ++i) {
24 result[i] = distribution(generator_);
25 }
26 return result;
27 }
28
29 private:
30 std::minstd_rand generator_;
Brian Silverman5eec8b92019-03-10 15:14:31 -070031};
32
33// Verifies that a simple image is thresholded correctly.
34//
35// Specifically, we want to get this result from the thresholding:
Brian Silverman4482db52019-03-10 16:14:48 -070036// --+-----
37// +------+
38// -++++++-
39// +++-++++
40// --------
41// ++++-+++
42// ++++++++
43// +-+-+--+
Brian Silverman5eec8b92019-03-10 15:14:31 -070044TEST_F(YuyvYThresholdTest, SimpleImage) {
45 ImageFormat format;
Brian Silverman4482db52019-03-10 16:14:48 -070046 format.w = 8;
Brian Silverman5eec8b92019-03-10 15:14:31 -070047 format.h = 8;
48
49 std::vector<std::vector<ImageRange>> expected_ranges;
50 std::vector<char> image;
Brian Silverman4482db52019-03-10 16:14:48 -070051 image.resize(8 * 8 * 2);
52 // --+-----
Alex Perrycb7da4b2019-08-28 19:35:56 -070053 image[0 * 2 + 0 * 16] = static_cast<char>(0);
54 image[1 * 2 + 0 * 16] = static_cast<char>(0);
55 image[2 * 2 + 0 * 16] = static_cast<char>(128);
56 image[3 * 2 + 0 * 16] = static_cast<char>(127);
57 image[4 * 2 + 0 * 16] = static_cast<char>(0);
58 image[5 * 2 + 0 * 16] = static_cast<char>(0);
59 image[6 * 2 + 0 * 16] = static_cast<char>(0);
60 image[7 * 2 + 0 * 16] = static_cast<char>(0);
Brian Silverman5eec8b92019-03-10 15:14:31 -070061 expected_ranges.push_back({{{2, 3}}});
Brian Silverman4482db52019-03-10 16:14:48 -070062 // +------+
Alex Perrycb7da4b2019-08-28 19:35:56 -070063 image[0 * 2 + 1 * 16] = static_cast<char>(128);
64 image[1 * 2 + 1 * 16] = static_cast<char>(0);
65 image[2 * 2 + 1 * 16] = static_cast<char>(0);
66 image[3 * 2 + 1 * 16] = static_cast<char>(10);
67 image[4 * 2 + 1 * 16] = static_cast<char>(30);
68 image[5 * 2 + 1 * 16] = static_cast<char>(50);
69 image[6 * 2 + 1 * 16] = static_cast<char>(70);
70 image[7 * 2 + 1 * 16] = static_cast<char>(255);
Brian Silverman4482db52019-03-10 16:14:48 -070071 expected_ranges.push_back({{{0, 1}, {7, 8}}});
72 // -++++++-
Alex Perrycb7da4b2019-08-28 19:35:56 -070073 image[0 * 2 + 2 * 16] = static_cast<char>(73);
74 image[1 * 2 + 2 * 16] = static_cast<char>(246);
75 image[2 * 2 + 2 * 16] = static_cast<char>(247);
76 image[3 * 2 + 2 * 16] = static_cast<char>(248);
77 image[4 * 2 + 2 * 16] = static_cast<char>(249);
78 image[5 * 2 + 2 * 16] = static_cast<char>(250);
79 image[6 * 2 + 2 * 16] = static_cast<char>(250);
80 image[7 * 2 + 2 * 16] = static_cast<char>(45);
Brian Silverman4482db52019-03-10 16:14:48 -070081 expected_ranges.push_back({{{1, 7}}});
82 // +++-++++
Alex Perrycb7da4b2019-08-28 19:35:56 -070083 image[0 * 2 + 3 * 16] = static_cast<char>(128);
84 image[1 * 2 + 3 * 16] = static_cast<char>(134);
85 image[2 * 2 + 3 * 16] = static_cast<char>(250);
86 image[3 * 2 + 3 * 16] = static_cast<char>(0);
87 image[4 * 2 + 3 * 16] = static_cast<char>(230);
88 image[5 * 2 + 3 * 16] = static_cast<char>(230);
89 image[6 * 2 + 3 * 16] = static_cast<char>(230);
90 image[7 * 2 + 3 * 16] = static_cast<char>(210);
Brian Silverman4482db52019-03-10 16:14:48 -070091 expected_ranges.push_back({{{0, 3}, {4, 8}}});
92 // --------
Alex Perrycb7da4b2019-08-28 19:35:56 -070093 image[0 * 2 + 4 * 16] = static_cast<char>(7);
94 image[1 * 2 + 4 * 16] = static_cast<char>(120);
95 image[2 * 2 + 4 * 16] = static_cast<char>(127);
96 image[3 * 2 + 4 * 16] = static_cast<char>(0);
97 image[4 * 2 + 4 * 16] = static_cast<char>(50);
98 image[5 * 2 + 4 * 16] = static_cast<char>(80);
99 image[6 * 2 + 4 * 16] = static_cast<char>(110);
100 image[7 * 2 + 4 * 16] = static_cast<char>(25);
Brian Silverman5eec8b92019-03-10 15:14:31 -0700101 expected_ranges.push_back({{}});
Brian Silverman4482db52019-03-10 16:14:48 -0700102 // ++++-+++
Alex Perrycb7da4b2019-08-28 19:35:56 -0700103 image[0 * 2 + 5 * 16] = static_cast<char>(140);
104 image[1 * 2 + 5 * 16] = static_cast<char>(140);
105 image[2 * 2 + 5 * 16] = static_cast<char>(140);
106 image[3 * 2 + 5 * 16] = static_cast<char>(140);
107 image[4 * 2 + 5 * 16] = static_cast<char>(0);
108 image[5 * 2 + 5 * 16] = static_cast<char>(140);
109 image[6 * 2 + 5 * 16] = static_cast<char>(140);
110 image[7 * 2 + 5 * 16] = static_cast<char>(140);
Brian Silverman4482db52019-03-10 16:14:48 -0700111 expected_ranges.push_back({{{0, 4}, {5, 8}}});
112 // ++++++++
Alex Perrycb7da4b2019-08-28 19:35:56 -0700113 image[0 * 2 + 6 * 16] = static_cast<char>(128);
114 image[1 * 2 + 6 * 16] = static_cast<char>(128);
115 image[2 * 2 + 6 * 16] = static_cast<char>(128);
116 image[3 * 2 + 6 * 16] = static_cast<char>(128);
117 image[4 * 2 + 6 * 16] = static_cast<char>(128);
118 image[5 * 2 + 6 * 16] = static_cast<char>(128);
119 image[6 * 2 + 6 * 16] = static_cast<char>(128);
120 image[7 * 2 + 6 * 16] = static_cast<char>(128);
Brian Silverman4482db52019-03-10 16:14:48 -0700121 expected_ranges.push_back({{{0, 8}}});
122 // +-+-+--+
Alex Perrycb7da4b2019-08-28 19:35:56 -0700123 image[0 * 2 + 7 * 16] = static_cast<char>(200);
124 image[1 * 2 + 7 * 16] = static_cast<char>(0);
125 image[2 * 2 + 7 * 16] = static_cast<char>(200);
126 image[3 * 2 + 7 * 16] = static_cast<char>(0);
127 image[4 * 2 + 7 * 16] = static_cast<char>(200);
128 image[5 * 2 + 7 * 16] = static_cast<char>(0);
129 image[6 * 2 + 7 * 16] = static_cast<char>(0);
130 image[7 * 2 + 7 * 16] = static_cast<char>(200);
Brian Silverman4482db52019-03-10 16:14:48 -0700131 expected_ranges.push_back({{{0, 1}, {2, 3}, {4, 5}, {7, 8}}});
Brian Silverman5eec8b92019-03-10 15:14:31 -0700132 const RangeImage expected_result(0, std::move(expected_ranges));
133
134 const auto slow_result = SlowYuyvYThreshold(format, image.data(), 127);
135 ASSERT_EQ(expected_result, slow_result);
Brian Silverman4482db52019-03-10 16:14:48 -0700136 const auto fast_result = FastYuyvYThreshold(format, image.data(), 127);
137 ASSERT_EQ(expected_result, fast_result);
138}
139
140// Verifies that a couple of completely random images match.
141TEST_F(YuyvYThresholdTest, Random) {
142 for (int i = 0; i < 10; ++i) {
143 ImageFormat small_format;
144 small_format.w = 16;
145 small_format.h = 16;
146 const auto small_image = RandomImage(small_format);
147 const auto slow_result =
148 SlowYuyvYThreshold(small_format, small_image.data(), 127);
149 const auto fast_result =
150 FastYuyvYThreshold(small_format, small_image.data(), 127);
151 ASSERT_EQ(slow_result, fast_result);
152 }
153 for (int i = 0; i < 10; ++i) {
154 ImageFormat large_format;
155 large_format.w = 1024;
156 large_format.h = 512;
157 const auto large_image = RandomImage(large_format);
158 const auto slow_result =
159 SlowYuyvYThreshold(large_format, large_image.data(), 127);
160 const auto fast_result =
161 FastYuyvYThreshold(large_format, large_image.data(), 127);
162 ASSERT_EQ(slow_result, fast_result);
163 }
164}
165
166// Verifies that changing the U and V values doesn't affect the result.
167TEST_F(YuyvYThresholdTest, UVIgnored) {
168 ImageFormat format;
169 format.w = 32;
170 format.h = 20;
171 const auto baseline_image = RandomImage(format);
172 const auto baseline_result =
173 SlowYuyvYThreshold(format, baseline_image.data(), 127);
174 for (int i = 0; i < 5; ++i) {
175 auto tweaked_image = RandomImage(format);
176 for (int y = 0; y < format.h; ++y) {
177 for (int x = 0; x < format.w; ++x) {
178 tweaked_image[x * 2 + y * format.w * 2] =
179 baseline_image[x * 2 + y * format.w * 2];
180 }
181 }
182
183 const auto slow_result =
184 SlowYuyvYThreshold(format, tweaked_image.data(), 127);
185 ASSERT_EQ(baseline_result, slow_result);
186 const auto fast_result =
187 FastYuyvYThreshold(format, tweaked_image.data(), 127);
188 ASSERT_EQ(baseline_result, fast_result);
189 }
Brian Silverman5eec8b92019-03-10 15:14:31 -0700190}
191
192} // namespace testing
193} // namespace vision
194} // namespace aos