blob: c4f8a9eaf1d1959323b48c5fe733037540d02f27 [file] [log] [blame]
#include "aos/vision/blob/threshold.h"
#include "aos/logging/logging.h"
namespace aos {
namespace vision {
namespace {
constexpr int kChunkSize = 8;
} // namespace
// At a high level, the algorithm is the same as the slow thresholding, except
// it operates in kChunkSize-pixel chunks.
RangeImage FastYuyvYThreshold(ImageFormat fmt, const char *data,
uint8_t value) {
AOS_CHECK_EQ(0, fmt.w % kChunkSize);
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 kChunkSize-pixel chunk
for (int x = 0; x < fmt.w / kChunkSize; ++x) {
// The per-channel (YUYV) values in the current chunk.
uint8_t chunk_channels[2 * kChunkSize];
memcpy(&chunk_channels[0], current_row + x * kChunkSize * 2,
2 * kChunkSize);
__builtin_prefetch(current_row + (x + 1) * kChunkSize * 2);
for (int i = 0; i < kChunkSize; ++i) {
if ((chunk_channels[i * 2] > value) != in_range) {
const int here = x * kChunkSize + i;
if (in_range) {
current_row_ranges.emplace_back(
ImageRange(current_range_start, here));
} else {
current_range_start = here;
}
in_range = !in_range;
}
}
}
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));
}
FastYuyvYPooledThresholder::FastYuyvYPooledThresholder() {
states_.fill(ThreadState::kWaitingForInputData);
for (int i = 0; i < kThreads; ++i) {
threads_[i] = std::thread([this, i]() { RunThread(i); });
}
}
FastYuyvYPooledThresholder::~FastYuyvYPooledThresholder() {
{
std::unique_lock<std::mutex> locker(mutex_);
quit_ = true;
condition_variable_.notify_all();
}
for (int i = 0; i < kThreads; ++i) {
threads_[i].join();
}
}
RangeImage FastYuyvYPooledThresholder::Threshold(ImageFormat fmt,
const char *data,
uint8_t value) {
input_format_ = fmt;
input_data_ = data;
input_value_ = value;
{
std::unique_lock<std::mutex> locker(mutex_);
for (int i = 0; i < kThreads; ++i) {
states_[i] = ThreadState::kProcessing;
}
condition_variable_.notify_all();
while (!AllThreadsDone()) {
condition_variable_.wait(locker);
}
}
std::vector<std::vector<ImageRange>> result;
result.reserve(fmt.h);
for (int i = 0; i < kThreads; ++i) {
result.insert(result.end(), outputs_[i].begin(), outputs_[i].end());
}
return RangeImage(0, std::move(result));
}
void FastYuyvYPooledThresholder::RunThread(int i) {
while (true) {
{
std::unique_lock<std::mutex> locker(mutex_);
while (states_[i] == ThreadState::kWaitingForInputData) {
if (quit_) {
return;
}
condition_variable_.wait(locker);
}
}
ImageFormat shard_format = input_format_;
AOS_CHECK_EQ(shard_format.h % kThreads, 0);
shard_format.h /= kThreads;
outputs_[i] = FastYuyvYThreshold(
shard_format, input_data_ + shard_format.w * 2 * shard_format.h * i,
input_value_);
{
std::unique_lock<std::mutex> locker(mutex_);
states_[i] = ThreadState::kWaitingForInputData;
condition_variable_.notify_all();
}
}
}
} // namespace vision
} // namespace aos