blob: 74809d14bc4456fa679bee5f68ba82f5f282ad4c [file] [log] [blame]
#include "aos/vision/blob/threshold.h"
#include "aos/logging/logging.h"
namespace aos {
namespace vision {
// Expands to a unique value for each combination of values for 5 bools.
#define MASH(v0, v1, v2, v3, v4) \
((uint8_t(v0) << 4) | (uint8_t(v1) << 3) | (uint8_t(v2) << 2) | \
(uint8_t(v3) << 1) | (uint8_t(v4)))
// At a high level, the algorithm is the same as the slow thresholding, except
// it operates in 4-pixel chunks. The handling for each of these chunks is
// manually flattened (via codegen) into a 32-case switch statement. There are
// 2^4 cases for each pixel being in or out, along with another set of cases
// depending on whether the start of the chunk is in a range or not.
RangeImage FastYuyvYThreshold(ImageFormat fmt, const char *data,
uint8_t value) {
CHECK_EQ(0, fmt.w % 4);
std::vector<std::vector<ImageRange>> result;
result.reserve(fmt.h);
// Iterate through each row.
for (int y = 0; y < fmt.h; ++y) {
// The start of the data for the current row.
const char *const current_row = fmt.w * y * 2 + data;
bool in_range = false;
int current_range_start = -1;
std::vector<ImageRange> current_row_ranges;
// Iterate through each 4-pixel chunk
for (int x = 0; x < fmt.w / 4; ++x) {
// The per-channel (YUYV) values in the current chunk.
uint8_t chunk_channels[8];
memcpy(&chunk_channels[0], current_row + x * 4 * 2, 8);
const uint8_t pattern =
MASH(in_range, chunk_channels[0] > value, chunk_channels[2] > value,
chunk_channels[4] > value, chunk_channels[6] > value);
switch (pattern) {
// clang-format off
/*
# Ruby code to generate the below code:
32.times do |v|
puts "case MASH(#{[v[4], v[3], v[2], v[1], v[0]].join(", ")}):"
in_range = v[4]
current_range_start = "current_range_start"
4.times do |i|
if v[3 - i] != in_range
if (in_range == 1)
puts " current_row_ranges.emplace_back(ImageRange(#{current_range_start}, x * 4 + #{i}));"
else
current_range_start = "x * 4 + #{i}"
end
in_range = v[3 - i]
end
end
if (current_range_start != "current_range_start")
puts " current_range_start = #{current_range_start};"
end
if (in_range != v[4])
puts " in_range = #{["false", "true"][v[0]]};"
end
puts " break;"
end
*/
// clang-format on
case MASH(0, 0, 0, 0, 0):
break;
case MASH(0, 0, 0, 0, 1):
current_range_start = x * 4 + 3;
in_range = true;
break;
case MASH(0, 0, 0, 1, 0):
current_row_ranges.emplace_back(ImageRange(x * 4 + 2, x * 4 + 3));
current_range_start = x * 4 + 2;
break;
case MASH(0, 0, 0, 1, 1):
current_range_start = x * 4 + 2;
in_range = true;
break;
case MASH(0, 0, 1, 0, 0):
current_row_ranges.emplace_back(ImageRange(x * 4 + 1, x * 4 + 2));
current_range_start = x * 4 + 1;
break;
case MASH(0, 0, 1, 0, 1):
current_row_ranges.emplace_back(ImageRange(x * 4 + 1, x * 4 + 2));
current_range_start = x * 4 + 3;
in_range = true;
break;
case MASH(0, 0, 1, 1, 0):
current_row_ranges.emplace_back(ImageRange(x * 4 + 1, x * 4 + 3));
current_range_start = x * 4 + 1;
break;
case MASH(0, 0, 1, 1, 1):
current_range_start = x * 4 + 1;
in_range = true;
break;
case MASH(0, 1, 0, 0, 0):
current_row_ranges.emplace_back(ImageRange(x * 4 + 0, x * 4 + 1));
current_range_start = x * 4 + 0;
break;
case MASH(0, 1, 0, 0, 1):
current_row_ranges.emplace_back(ImageRange(x * 4 + 0, x * 4 + 1));
current_range_start = x * 4 + 3;
in_range = true;
break;
case MASH(0, 1, 0, 1, 0):
current_row_ranges.emplace_back(ImageRange(x * 4 + 0, x * 4 + 1));
current_row_ranges.emplace_back(ImageRange(x * 4 + 2, x * 4 + 3));
current_range_start = x * 4 + 2;
break;
case MASH(0, 1, 0, 1, 1):
current_row_ranges.emplace_back(ImageRange(x * 4 + 0, x * 4 + 1));
current_range_start = x * 4 + 2;
in_range = true;
break;
case MASH(0, 1, 1, 0, 0):
current_row_ranges.emplace_back(ImageRange(x * 4 + 0, x * 4 + 2));
current_range_start = x * 4 + 0;
break;
case MASH(0, 1, 1, 0, 1):
current_row_ranges.emplace_back(ImageRange(x * 4 + 0, x * 4 + 2));
current_range_start = x * 4 + 3;
in_range = true;
break;
case MASH(0, 1, 1, 1, 0):
current_row_ranges.emplace_back(ImageRange(x * 4 + 0, x * 4 + 3));
current_range_start = x * 4 + 0;
break;
case MASH(0, 1, 1, 1, 1):
current_range_start = x * 4 + 0;
in_range = true;
break;
case MASH(1, 0, 0, 0, 0):
current_row_ranges.emplace_back(
ImageRange(current_range_start, x * 4 + 0));
in_range = false;
break;
case MASH(1, 0, 0, 0, 1):
current_row_ranges.emplace_back(
ImageRange(current_range_start, x * 4 + 0));
current_range_start = x * 4 + 3;
break;
case MASH(1, 0, 0, 1, 0):
current_row_ranges.emplace_back(
ImageRange(current_range_start, x * 4 + 0));
current_row_ranges.emplace_back(ImageRange(x * 4 + 2, x * 4 + 3));
current_range_start = x * 4 + 2;
in_range = false;
break;
case MASH(1, 0, 0, 1, 1):
current_row_ranges.emplace_back(
ImageRange(current_range_start, x * 4 + 0));
current_range_start = x * 4 + 2;
break;
case MASH(1, 0, 1, 0, 0):
current_row_ranges.emplace_back(
ImageRange(current_range_start, x * 4 + 0));
current_row_ranges.emplace_back(ImageRange(x * 4 + 1, x * 4 + 2));
current_range_start = x * 4 + 1;
in_range = false;
break;
case MASH(1, 0, 1, 0, 1):
current_row_ranges.emplace_back(
ImageRange(current_range_start, x * 4 + 0));
current_row_ranges.emplace_back(ImageRange(x * 4 + 1, x * 4 + 2));
current_range_start = x * 4 + 3;
break;
case MASH(1, 0, 1, 1, 0):
current_row_ranges.emplace_back(
ImageRange(current_range_start, x * 4 + 0));
current_row_ranges.emplace_back(ImageRange(x * 4 + 1, x * 4 + 3));
current_range_start = x * 4 + 1;
in_range = false;
break;
case MASH(1, 0, 1, 1, 1):
current_row_ranges.emplace_back(
ImageRange(current_range_start, x * 4 + 0));
current_range_start = x * 4 + 1;
break;
case MASH(1, 1, 0, 0, 0):
current_row_ranges.emplace_back(
ImageRange(current_range_start, x * 4 + 1));
in_range = false;
break;
case MASH(1, 1, 0, 0, 1):
current_row_ranges.emplace_back(
ImageRange(current_range_start, x * 4 + 1));
current_range_start = x * 4 + 3;
break;
case MASH(1, 1, 0, 1, 0):
current_row_ranges.emplace_back(
ImageRange(current_range_start, x * 4 + 1));
current_row_ranges.emplace_back(ImageRange(x * 4 + 2, x * 4 + 3));
current_range_start = x * 4 + 2;
in_range = false;
break;
case MASH(1, 1, 0, 1, 1):
current_row_ranges.emplace_back(
ImageRange(current_range_start, x * 4 + 1));
current_range_start = x * 4 + 2;
break;
case MASH(1, 1, 1, 0, 0):
current_row_ranges.emplace_back(
ImageRange(current_range_start, x * 4 + 2));
in_range = false;
break;
case MASH(1, 1, 1, 0, 1):
current_row_ranges.emplace_back(
ImageRange(current_range_start, x * 4 + 2));
current_range_start = x * 4 + 3;
break;
case MASH(1, 1, 1, 1, 0):
current_row_ranges.emplace_back(
ImageRange(current_range_start, x * 4 + 3));
in_range = false;
break;
case MASH(1, 1, 1, 1, 1):
break;
}
}
if (in_range) {
current_row_ranges.emplace_back(ImageRange(current_range_start, fmt.w));
}
result.push_back(current_row_ranges);
}
return RangeImage(0, std::move(result));
}
#undef MASH
} // namespace vision
} // namespace aos