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