blob: 5403bbda50a1f635fcdaa0e57455d6a654012e67 [file] [log] [blame]
Brian Silverman37b15b32019-03-10 13:30:18 -07001#include "aos/vision/blob/threshold.h"
2
Brian Silverman4482db52019-03-10 16:14:48 -07003#include "aos/logging/logging.h"
4
Stephan Pleinesf63bde82024-01-13 15:59:33 -08005namespace aos::vision {
Brian Silvermanefde9522019-03-23 22:02:40 -07006namespace {
Brian Silverman37b15b32019-03-10 13:30:18 -07007
Brian Silvermanefde9522019-03-23 22:02:40 -07008constexpr int kChunkSize = 8;
9
10} // namespace
Brian Silverman37b15b32019-03-10 13:30:18 -070011
Brian Silverman4482db52019-03-10 16:14:48 -070012// At a high level, the algorithm is the same as the slow thresholding, except
Brian Silvermanefde9522019-03-23 22:02:40 -070013// it operates in kChunkSize-pixel chunks.
Brian Silverman37b15b32019-03-10 13:30:18 -070014RangeImage FastYuyvYThreshold(ImageFormat fmt, const char *data,
15 uint8_t value) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070016 AOS_CHECK_EQ(0, fmt.w % kChunkSize);
Brian Silverman4482db52019-03-10 16:14:48 -070017 std::vector<std::vector<ImageRange>> result;
18 result.reserve(fmt.h);
19
20 // Iterate through each row.
Brian Silverman37b15b32019-03-10 13:30:18 -070021 for (int y = 0; y < fmt.h; ++y) {
Brian Silverman4482db52019-03-10 16:14:48 -070022 // The start of the data for the current row.
23 const char *const current_row = fmt.w * y * 2 + data;
24 bool in_range = false;
25 int current_range_start = -1;
26 std::vector<ImageRange> current_row_ranges;
Brian Silvermanefde9522019-03-23 22:02:40 -070027 // Iterate through each kChunkSize-pixel chunk
28 for (int x = 0; x < fmt.w / kChunkSize; ++x) {
Brian Silverman4482db52019-03-10 16:14:48 -070029 // The per-channel (YUYV) values in the current chunk.
Brian Silvermanefde9522019-03-23 22:02:40 -070030 uint8_t chunk_channels[2 * kChunkSize];
Austin Schuh60e77942022-05-16 17:48:24 -070031 memcpy(&chunk_channels[0], current_row + x * kChunkSize * 2,
32 2 * kChunkSize);
Brian Silverman20b57772019-03-23 22:02:49 -070033 __builtin_prefetch(current_row + (x + 1) * kChunkSize * 2);
Brian Silvermanefde9522019-03-23 22:02:40 -070034
35 for (int i = 0; i < kChunkSize; ++i) {
36 if ((chunk_channels[i * 2] > value) != in_range) {
37 const int here = x * kChunkSize + i;
38 if (in_range) {
Austin Schuh60e77942022-05-16 17:48:24 -070039 current_row_ranges.emplace_back(
40 ImageRange(current_range_start, here));
Brian Silvermanefde9522019-03-23 22:02:40 -070041 } else {
42 current_range_start = here;
43 }
44 in_range = !in_range;
45 }
Brian Silverman37b15b32019-03-10 13:30:18 -070046 }
Brian Silverman37b15b32019-03-10 13:30:18 -070047 }
Brian Silverman4482db52019-03-10 16:14:48 -070048 if (in_range) {
49 current_row_ranges.emplace_back(ImageRange(current_range_start, fmt.w));
Brian Silverman37b15b32019-03-10 13:30:18 -070050 }
Brian Silverman4482db52019-03-10 16:14:48 -070051 result.push_back(current_row_ranges);
Brian Silverman37b15b32019-03-10 13:30:18 -070052 }
Brian Silverman4482db52019-03-10 16:14:48 -070053 return RangeImage(0, std::move(result));
Brian Silverman37b15b32019-03-10 13:30:18 -070054}
55
Brian Silverman20b57772019-03-23 22:02:49 -070056FastYuyvYPooledThresholder::FastYuyvYPooledThresholder() {
57 states_.fill(ThreadState::kWaitingForInputData);
58 for (int i = 0; i < kThreads; ++i) {
59 threads_[i] = std::thread([this, i]() { RunThread(i); });
60 }
61}
62
63FastYuyvYPooledThresholder::~FastYuyvYPooledThresholder() {
64 {
65 std::unique_lock<std::mutex> locker(mutex_);
66 quit_ = true;
67 condition_variable_.notify_all();
68 }
69 for (int i = 0; i < kThreads; ++i) {
70 threads_[i].join();
71 }
72}
73
74RangeImage FastYuyvYPooledThresholder::Threshold(ImageFormat fmt,
75 const char *data,
76 uint8_t value) {
77 input_format_ = fmt;
78 input_data_ = data;
79 input_value_ = value;
80 {
81 std::unique_lock<std::mutex> locker(mutex_);
82 for (int i = 0; i < kThreads; ++i) {
83 states_[i] = ThreadState::kProcessing;
84 }
85 condition_variable_.notify_all();
86 while (!AllThreadsDone()) {
87 condition_variable_.wait(locker);
88 }
89 }
90 std::vector<std::vector<ImageRange>> result;
91 result.reserve(fmt.h);
92 for (int i = 0; i < kThreads; ++i) {
93 result.insert(result.end(), outputs_[i].begin(), outputs_[i].end());
94 }
95 return RangeImage(0, std::move(result));
96}
97
98void FastYuyvYPooledThresholder::RunThread(int i) {
99 while (true) {
100 {
101 std::unique_lock<std::mutex> locker(mutex_);
102 while (states_[i] == ThreadState::kWaitingForInputData) {
103 if (quit_) {
104 return;
105 }
106 condition_variable_.wait(locker);
107 }
108 }
109
110 ImageFormat shard_format = input_format_;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700111 AOS_CHECK_EQ(shard_format.h % kThreads, 0);
Brian Silverman20b57772019-03-23 22:02:49 -0700112 shard_format.h /= kThreads;
113
114 outputs_[i] = FastYuyvYThreshold(
115 shard_format, input_data_ + shard_format.w * 2 * shard_format.h * i,
116 input_value_);
117 {
118 std::unique_lock<std::mutex> locker(mutex_);
119 states_[i] = ThreadState::kWaitingForInputData;
120 condition_variable_.notify_all();
121 }
122 }
123}
124
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800125} // namespace aos::vision