blob: c4f8a9eaf1d1959323b48c5fe733037540d02f27 [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
Brian Silverman37b15b32019-03-10 13:30:18 -07005namespace aos {
6namespace vision {
Brian Silvermanefde9522019-03-23 22:02:40 -07007namespace {
Brian Silverman37b15b32019-03-10 13:30:18 -07008
Brian Silvermanefde9522019-03-23 22:02:40 -07009constexpr int kChunkSize = 8;
10
11} // namespace
Brian Silverman37b15b32019-03-10 13:30:18 -070012
Brian Silverman4482db52019-03-10 16:14:48 -070013// At a high level, the algorithm is the same as the slow thresholding, except
Brian Silvermanefde9522019-03-23 22:02:40 -070014// it operates in kChunkSize-pixel chunks.
Brian Silverman37b15b32019-03-10 13:30:18 -070015RangeImage FastYuyvYThreshold(ImageFormat fmt, const char *data,
16 uint8_t value) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070017 AOS_CHECK_EQ(0, fmt.w % kChunkSize);
Brian Silverman4482db52019-03-10 16:14:48 -070018 std::vector<std::vector<ImageRange>> result;
19 result.reserve(fmt.h);
20
21 // Iterate through each row.
Brian Silverman37b15b32019-03-10 13:30:18 -070022 for (int y = 0; y < fmt.h; ++y) {
Brian Silverman4482db52019-03-10 16:14:48 -070023 // The start of the data for the current row.
24 const char *const current_row = fmt.w * y * 2 + data;
25 bool in_range = false;
26 int current_range_start = -1;
27 std::vector<ImageRange> current_row_ranges;
Brian Silvermanefde9522019-03-23 22:02:40 -070028 // Iterate through each kChunkSize-pixel chunk
29 for (int x = 0; x < fmt.w / kChunkSize; ++x) {
Brian Silverman4482db52019-03-10 16:14:48 -070030 // The per-channel (YUYV) values in the current chunk.
Brian Silvermanefde9522019-03-23 22:02:40 -070031 uint8_t chunk_channels[2 * kChunkSize];
Austin Schuh60e77942022-05-16 17:48:24 -070032 memcpy(&chunk_channels[0], current_row + x * kChunkSize * 2,
33 2 * kChunkSize);
Brian Silverman20b57772019-03-23 22:02:49 -070034 __builtin_prefetch(current_row + (x + 1) * kChunkSize * 2);
Brian Silvermanefde9522019-03-23 22:02:40 -070035
36 for (int i = 0; i < kChunkSize; ++i) {
37 if ((chunk_channels[i * 2] > value) != in_range) {
38 const int here = x * kChunkSize + i;
39 if (in_range) {
Austin Schuh60e77942022-05-16 17:48:24 -070040 current_row_ranges.emplace_back(
41 ImageRange(current_range_start, here));
Brian Silvermanefde9522019-03-23 22:02:40 -070042 } else {
43 current_range_start = here;
44 }
45 in_range = !in_range;
46 }
Brian Silverman37b15b32019-03-10 13:30:18 -070047 }
Brian Silverman37b15b32019-03-10 13:30:18 -070048 }
Brian Silverman4482db52019-03-10 16:14:48 -070049 if (in_range) {
50 current_row_ranges.emplace_back(ImageRange(current_range_start, fmt.w));
Brian Silverman37b15b32019-03-10 13:30:18 -070051 }
Brian Silverman4482db52019-03-10 16:14:48 -070052 result.push_back(current_row_ranges);
Brian Silverman37b15b32019-03-10 13:30:18 -070053 }
Brian Silverman4482db52019-03-10 16:14:48 -070054 return RangeImage(0, std::move(result));
Brian Silverman37b15b32019-03-10 13:30:18 -070055}
56
Brian Silverman20b57772019-03-23 22:02:49 -070057FastYuyvYPooledThresholder::FastYuyvYPooledThresholder() {
58 states_.fill(ThreadState::kWaitingForInputData);
59 for (int i = 0; i < kThreads; ++i) {
60 threads_[i] = std::thread([this, i]() { RunThread(i); });
61 }
62}
63
64FastYuyvYPooledThresholder::~FastYuyvYPooledThresholder() {
65 {
66 std::unique_lock<std::mutex> locker(mutex_);
67 quit_ = true;
68 condition_variable_.notify_all();
69 }
70 for (int i = 0; i < kThreads; ++i) {
71 threads_[i].join();
72 }
73}
74
75RangeImage FastYuyvYPooledThresholder::Threshold(ImageFormat fmt,
76 const char *data,
77 uint8_t value) {
78 input_format_ = fmt;
79 input_data_ = data;
80 input_value_ = value;
81 {
82 std::unique_lock<std::mutex> locker(mutex_);
83 for (int i = 0; i < kThreads; ++i) {
84 states_[i] = ThreadState::kProcessing;
85 }
86 condition_variable_.notify_all();
87 while (!AllThreadsDone()) {
88 condition_variable_.wait(locker);
89 }
90 }
91 std::vector<std::vector<ImageRange>> result;
92 result.reserve(fmt.h);
93 for (int i = 0; i < kThreads; ++i) {
94 result.insert(result.end(), outputs_[i].begin(), outputs_[i].end());
95 }
96 return RangeImage(0, std::move(result));
97}
98
99void FastYuyvYPooledThresholder::RunThread(int i) {
100 while (true) {
101 {
102 std::unique_lock<std::mutex> locker(mutex_);
103 while (states_[i] == ThreadState::kWaitingForInputData) {
104 if (quit_) {
105 return;
106 }
107 condition_variable_.wait(locker);
108 }
109 }
110
111 ImageFormat shard_format = input_format_;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700112 AOS_CHECK_EQ(shard_format.h % kThreads, 0);
Brian Silverman20b57772019-03-23 22:02:49 -0700113 shard_format.h /= kThreads;
114
115 outputs_[i] = FastYuyvYThreshold(
116 shard_format, input_data_ + shard_format.w * 2 * shard_format.h * i,
117 input_value_);
118 {
119 std::unique_lock<std::mutex> locker(mutex_);
120 states_[i] = ThreadState::kWaitingForInputData;
121 condition_variable_.notify_all();
122 }
123 }
124}
125
Brian Silverman37b15b32019-03-10 13:30:18 -0700126} // namespace vision
127} // namespace aos