blob: 3bbef1d1abb8e7f54435e2e9679bce62132e57a9 [file] [log] [blame]
#include "aos/vision/blob/threshold.h"
#include <random>
#include <vector>
#include "aos/vision/blob/range_image.h"
#include "aos/vision/image/image_types.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace aos {
namespace vision {
namespace testing {
class YuyvYThresholdTest : public ::testing::Test {
public:
std::vector<char> RandomImage(ImageFormat format) {
std::vector<char> result;
result.resize(format.w * format.h * 2);
std::uniform_int_distribution<char> distribution(
std::numeric_limits<char>::min(), std::numeric_limits<char>::max());
for (size_t i = 0; i < result.size(); ++i) {
result[i] = distribution(generator_);
}
return result;
}
private:
std::minstd_rand generator_;
};
// Verifies that a simple image is thresholded correctly.
//
// Specifically, we want to get this result from the thresholding:
// --+-----
// +------+
// -++++++-
// +++-++++
// --------
// ++++-+++
// ++++++++
// +-+-+--+
TEST_F(YuyvYThresholdTest, SimpleImage) {
ImageFormat format;
format.w = 8;
format.h = 8;
std::vector<std::vector<ImageRange>> expected_ranges;
std::vector<char> image;
image.resize(8 * 8 * 2);
// --+-----
image[0 * 2 + 0 * 16] = static_cast<char>(0);
image[1 * 2 + 0 * 16] = static_cast<char>(0);
image[2 * 2 + 0 * 16] = static_cast<char>(128);
image[3 * 2 + 0 * 16] = static_cast<char>(127);
image[4 * 2 + 0 * 16] = static_cast<char>(0);
image[5 * 2 + 0 * 16] = static_cast<char>(0);
image[6 * 2 + 0 * 16] = static_cast<char>(0);
image[7 * 2 + 0 * 16] = static_cast<char>(0);
expected_ranges.push_back({{{2, 3}}});
// +------+
image[0 * 2 + 1 * 16] = static_cast<char>(128);
image[1 * 2 + 1 * 16] = static_cast<char>(0);
image[2 * 2 + 1 * 16] = static_cast<char>(0);
image[3 * 2 + 1 * 16] = static_cast<char>(10);
image[4 * 2 + 1 * 16] = static_cast<char>(30);
image[5 * 2 + 1 * 16] = static_cast<char>(50);
image[6 * 2 + 1 * 16] = static_cast<char>(70);
image[7 * 2 + 1 * 16] = static_cast<char>(255);
expected_ranges.push_back({{{0, 1}, {7, 8}}});
// -++++++-
image[0 * 2 + 2 * 16] = static_cast<char>(73);
image[1 * 2 + 2 * 16] = static_cast<char>(246);
image[2 * 2 + 2 * 16] = static_cast<char>(247);
image[3 * 2 + 2 * 16] = static_cast<char>(248);
image[4 * 2 + 2 * 16] = static_cast<char>(249);
image[5 * 2 + 2 * 16] = static_cast<char>(250);
image[6 * 2 + 2 * 16] = static_cast<char>(250);
image[7 * 2 + 2 * 16] = static_cast<char>(45);
expected_ranges.push_back({{{1, 7}}});
// +++-++++
image[0 * 2 + 3 * 16] = static_cast<char>(128);
image[1 * 2 + 3 * 16] = static_cast<char>(134);
image[2 * 2 + 3 * 16] = static_cast<char>(250);
image[3 * 2 + 3 * 16] = static_cast<char>(0);
image[4 * 2 + 3 * 16] = static_cast<char>(230);
image[5 * 2 + 3 * 16] = static_cast<char>(230);
image[6 * 2 + 3 * 16] = static_cast<char>(230);
image[7 * 2 + 3 * 16] = static_cast<char>(210);
expected_ranges.push_back({{{0, 3}, {4, 8}}});
// --------
image[0 * 2 + 4 * 16] = static_cast<char>(7);
image[1 * 2 + 4 * 16] = static_cast<char>(120);
image[2 * 2 + 4 * 16] = static_cast<char>(127);
image[3 * 2 + 4 * 16] = static_cast<char>(0);
image[4 * 2 + 4 * 16] = static_cast<char>(50);
image[5 * 2 + 4 * 16] = static_cast<char>(80);
image[6 * 2 + 4 * 16] = static_cast<char>(110);
image[7 * 2 + 4 * 16] = static_cast<char>(25);
expected_ranges.push_back({{}});
// ++++-+++
image[0 * 2 + 5 * 16] = static_cast<char>(140);
image[1 * 2 + 5 * 16] = static_cast<char>(140);
image[2 * 2 + 5 * 16] = static_cast<char>(140);
image[3 * 2 + 5 * 16] = static_cast<char>(140);
image[4 * 2 + 5 * 16] = static_cast<char>(0);
image[5 * 2 + 5 * 16] = static_cast<char>(140);
image[6 * 2 + 5 * 16] = static_cast<char>(140);
image[7 * 2 + 5 * 16] = static_cast<char>(140);
expected_ranges.push_back({{{0, 4}, {5, 8}}});
// ++++++++
image[0 * 2 + 6 * 16] = static_cast<char>(128);
image[1 * 2 + 6 * 16] = static_cast<char>(128);
image[2 * 2 + 6 * 16] = static_cast<char>(128);
image[3 * 2 + 6 * 16] = static_cast<char>(128);
image[4 * 2 + 6 * 16] = static_cast<char>(128);
image[5 * 2 + 6 * 16] = static_cast<char>(128);
image[6 * 2 + 6 * 16] = static_cast<char>(128);
image[7 * 2 + 6 * 16] = static_cast<char>(128);
expected_ranges.push_back({{{0, 8}}});
// +-+-+--+
image[0 * 2 + 7 * 16] = static_cast<char>(200);
image[1 * 2 + 7 * 16] = static_cast<char>(0);
image[2 * 2 + 7 * 16] = static_cast<char>(200);
image[3 * 2 + 7 * 16] = static_cast<char>(0);
image[4 * 2 + 7 * 16] = static_cast<char>(200);
image[5 * 2 + 7 * 16] = static_cast<char>(0);
image[6 * 2 + 7 * 16] = static_cast<char>(0);
image[7 * 2 + 7 * 16] = static_cast<char>(200);
expected_ranges.push_back({{{0, 1}, {2, 3}, {4, 5}, {7, 8}}});
const RangeImage expected_result(0, std::move(expected_ranges));
const auto slow_result = SlowYuyvYThreshold(format, image.data(), 127);
ASSERT_EQ(expected_result, slow_result);
const auto fast_result = FastYuyvYThreshold(format, image.data(), 127);
ASSERT_EQ(expected_result, fast_result);
}
// Verifies that a couple of completely random images match.
TEST_F(YuyvYThresholdTest, Random) {
for (int i = 0; i < 10; ++i) {
ImageFormat small_format;
small_format.w = 16;
small_format.h = 16;
const auto small_image = RandomImage(small_format);
const auto slow_result =
SlowYuyvYThreshold(small_format, small_image.data(), 127);
const auto fast_result =
FastYuyvYThreshold(small_format, small_image.data(), 127);
ASSERT_EQ(slow_result, fast_result);
}
for (int i = 0; i < 10; ++i) {
ImageFormat large_format;
large_format.w = 1024;
large_format.h = 512;
const auto large_image = RandomImage(large_format);
const auto slow_result =
SlowYuyvYThreshold(large_format, large_image.data(), 127);
const auto fast_result =
FastYuyvYThreshold(large_format, large_image.data(), 127);
ASSERT_EQ(slow_result, fast_result);
}
}
// Verifies that changing the U and V values doesn't affect the result.
TEST_F(YuyvYThresholdTest, UVIgnored) {
ImageFormat format;
format.w = 32;
format.h = 20;
const auto baseline_image = RandomImage(format);
const auto baseline_result =
SlowYuyvYThreshold(format, baseline_image.data(), 127);
for (int i = 0; i < 5; ++i) {
auto tweaked_image = RandomImage(format);
for (int y = 0; y < format.h; ++y) {
for (int x = 0; x < format.w; ++x) {
tweaked_image[x * 2 + y * format.w * 2] =
baseline_image[x * 2 + y * format.w * 2];
}
}
const auto slow_result =
SlowYuyvYThreshold(format, tweaked_image.data(), 127);
ASSERT_EQ(baseline_result, slow_result);
const auto fast_result =
FastYuyvYThreshold(format, tweaked_image.data(), 127);
ASSERT_EQ(baseline_result, fast_result);
}
}
} // namespace testing
} // namespace vision
} // namespace aos