blob: 047d8db20ed5f007da20cc3903f04daef75ca7b9 [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];
32 memcpy(&chunk_channels[0], current_row + x * kChunkSize * 2, 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) {
39 current_row_ranges.emplace_back(ImageRange(current_range_start, here));
40 } else {
41 current_range_start = here;
42 }
43 in_range = !in_range;
44 }
Brian Silverman37b15b32019-03-10 13:30:18 -070045 }
Brian Silverman37b15b32019-03-10 13:30:18 -070046 }
Brian Silverman4482db52019-03-10 16:14:48 -070047 if (in_range) {
48 current_row_ranges.emplace_back(ImageRange(current_range_start, fmt.w));
Brian Silverman37b15b32019-03-10 13:30:18 -070049 }
Brian Silverman4482db52019-03-10 16:14:48 -070050 result.push_back(current_row_ranges);
Brian Silverman37b15b32019-03-10 13:30:18 -070051 }
Brian Silverman4482db52019-03-10 16:14:48 -070052 return RangeImage(0, std::move(result));
Brian Silverman37b15b32019-03-10 13:30:18 -070053}
54
Brian Silverman20b57772019-03-23 22:02:49 -070055FastYuyvYPooledThresholder::FastYuyvYPooledThresholder() {
56 states_.fill(ThreadState::kWaitingForInputData);
57 for (int i = 0; i < kThreads; ++i) {
58 threads_[i] = std::thread([this, i]() { RunThread(i); });
59 }
60}
61
62FastYuyvYPooledThresholder::~FastYuyvYPooledThresholder() {
63 {
64 std::unique_lock<std::mutex> locker(mutex_);
65 quit_ = true;
66 condition_variable_.notify_all();
67 }
68 for (int i = 0; i < kThreads; ++i) {
69 threads_[i].join();
70 }
71}
72
73RangeImage FastYuyvYPooledThresholder::Threshold(ImageFormat fmt,
74 const char *data,
75 uint8_t value) {
76 input_format_ = fmt;
77 input_data_ = data;
78 input_value_ = value;
79 {
80 std::unique_lock<std::mutex> locker(mutex_);
81 for (int i = 0; i < kThreads; ++i) {
82 states_[i] = ThreadState::kProcessing;
83 }
84 condition_variable_.notify_all();
85 while (!AllThreadsDone()) {
86 condition_variable_.wait(locker);
87 }
88 }
89 std::vector<std::vector<ImageRange>> result;
90 result.reserve(fmt.h);
91 for (int i = 0; i < kThreads; ++i) {
92 result.insert(result.end(), outputs_[i].begin(), outputs_[i].end());
93 }
94 return RangeImage(0, std::move(result));
95}
96
97void FastYuyvYPooledThresholder::RunThread(int i) {
98 while (true) {
99 {
100 std::unique_lock<std::mutex> locker(mutex_);
101 while (states_[i] == ThreadState::kWaitingForInputData) {
102 if (quit_) {
103 return;
104 }
105 condition_variable_.wait(locker);
106 }
107 }
108
109 ImageFormat shard_format = input_format_;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700110 AOS_CHECK_EQ(shard_format.h % kThreads, 0);
Brian Silverman20b57772019-03-23 22:02:49 -0700111 shard_format.h /= kThreads;
112
113 outputs_[i] = FastYuyvYThreshold(
114 shard_format, input_data_ + shard_format.w * 2 * shard_format.h * i,
115 input_value_);
116 {
117 std::unique_lock<std::mutex> locker(mutex_);
118 states_[i] = ThreadState::kWaitingForInputData;
119 condition_variable_.notify_all();
120 }
121 }
122}
123
Brian Silverman37b15b32019-03-10 13:30:18 -0700124} // namespace vision
125} // namespace aos