blob: f126b14a57fd37f84a53dac6d1757df6848a68ef [file] [log] [blame]
Brian Silverman37b15b32019-03-10 13:30:18 -07001#ifndef AOS_VISION_BLOB_THRESHOLD_H_
2#define AOS_VISION_BLOB_THRESHOLD_H_
Parker Schuh6691f192017-01-14 17:01:02 -08003
Alex Perrycb7da4b2019-08-28 19:35:56 -07004#include <array>
Brian Silverman20b57772019-03-23 22:02:49 -07005#include <condition_variable>
6#include <mutex>
7#include <thread>
8
Parker Schuh6691f192017-01-14 17:01:02 -08009#include "aos/vision/blob/range_image.h"
10#include "aos/vision/image/image_types.h"
11
12namespace aos {
13namespace vision {
Brian Silverman37b15b32019-03-10 13:30:18 -070014namespace threshold_internal {
Parker Schuh6691f192017-01-14 17:01:02 -080015
Brian Silverman37b15b32019-03-10 13:30:18 -070016// Performs thresholding in a given region using a function which determines
17// whether a given point is in or out of the region.
18//
19// fn must return a bool when called with two integers (x, y).
20template <typename PointTestFn>
21RangeImage ThresholdPointsWithFunction(ImageFormat fmt, PointTestFn &&fn) {
22 static_assert(
23 std::is_convertible<PointTestFn, std::function<bool(int, int)>>::value,
24 "Invalid threshold function");
Brian Silverman4482db52019-03-10 16:14:48 -070025 std::vector<std::vector<ImageRange>> result;
26 result.reserve(fmt.h);
27 // Iterate through each row.
Parker Schuh02f13f62019-02-16 16:42:41 -080028 for (int y = 0; y < fmt.h; ++y) {
Brian Silverman4482db52019-03-10 16:14:48 -070029 // Whether we're currently in a range.
30 bool in_range = false;
31 int current_range_start = -1;
32 std::vector<ImageRange> current_row_ranges;
33 // Iterate through each pixel.
Parker Schuh02f13f62019-02-16 16:42:41 -080034 for (int x = 0; x < fmt.w; ++x) {
Brian Silverman4482db52019-03-10 16:14:48 -070035 if (fn(x, y) != in_range) {
36 if (in_range) {
37 current_row_ranges.emplace_back(ImageRange(current_range_start, x));
Parker Schuh6691f192017-01-14 17:01:02 -080038 } else {
Brian Silverman4482db52019-03-10 16:14:48 -070039 current_range_start = x;
Parker Schuh6691f192017-01-14 17:01:02 -080040 }
Brian Silverman4482db52019-03-10 16:14:48 -070041 in_range = !in_range;
Parker Schuh6691f192017-01-14 17:01:02 -080042 }
43 }
Brian Silverman4482db52019-03-10 16:14:48 -070044 if (in_range) {
45 current_row_ranges.emplace_back(ImageRange(current_range_start, fmt.w));
Parker Schuh6691f192017-01-14 17:01:02 -080046 }
Brian Silverman4482db52019-03-10 16:14:48 -070047 result.push_back(current_row_ranges);
Parker Schuh6691f192017-01-14 17:01:02 -080048 }
Brian Silverman4482db52019-03-10 16:14:48 -070049 return RangeImage(0, std::move(result));
Parker Schuh6691f192017-01-14 17:01:02 -080050}
51
Brian Silverman37b15b32019-03-10 13:30:18 -070052} // namespace threshold_internal
53
54// Thresholds an image using a function which determines whether a given pixel
55// value is in or out of the region.
56//
57// fn must return a bool when called with a PixelRef.
Parker Schuh02f13f62019-02-16 16:42:41 -080058template <typename ThresholdFn>
Brian Silverman37b15b32019-03-10 13:30:18 -070059RangeImage ThresholdImageWithFunction(const ImagePtr &img, ThresholdFn &&fn) {
60 static_assert(
61 std::is_convertible<ThresholdFn, std::function<bool(PixelRef)>>::value,
62 "Invalid threshold function");
63 return threshold_internal::ThresholdPointsWithFunction(
64 img.fmt(), [&](int x, int y) { return fn(img.get_px(x, y)); });
Parker Schuh02f13f62019-02-16 16:42:41 -080065}
66
Brian Silverman37b15b32019-03-10 13:30:18 -070067// Thresholds an image in YUYV format, selecting pixels with a Y (luma) greater
68// than value.
69//
70// This is implemented via a simple function that pulls out the Y values and
71// compares them each. It mostly exists for tests to compare against
72// FastYuyvYThreshold, because it's obviously correct.
73inline RangeImage SlowYuyvYThreshold(ImageFormat fmt, const char *data,
74 uint8_t value) {
75 return threshold_internal::ThresholdPointsWithFunction(
76 fmt, [&](int x, int y) {
77 uint8_t v = data[x * 2 + y * fmt.w * 2];
78 return v > value;
79 });
Parker Schuh02f13f62019-02-16 16:42:41 -080080}
81
Brian Silverman37b15b32019-03-10 13:30:18 -070082// Thresholds an image in YUYV format, selecting pixels with a Y (luma) greater
Brian Silverman4482db52019-03-10 16:14:48 -070083// than value. The width must be a multiple of 4.
Brian Silverman37b15b32019-03-10 13:30:18 -070084//
85// This is implemented via some tricky bit shuffling that goes fast.
86RangeImage FastYuyvYThreshold(ImageFormat fmt, const char *data, uint8_t value);
87
Brian Silverman20b57772019-03-23 22:02:49 -070088// Manages a pool of threads which do sharded thresholding.
89class FastYuyvYPooledThresholder {
90 public:
91 // The number of threads we'll use.
92 static constexpr int kThreads = 4;
93
94 FastYuyvYPooledThresholder();
95 // Shuts down and joins the threads.
96 ~FastYuyvYPooledThresholder();
97
98 // Actually does a threshold, merges the result, and returns it.
99 RangeImage Threshold(ImageFormat fmt, const char *data, uint8_t value);
100
101 private:
102 enum class ThreadState {
103 // Each thread moves itself into this state once it's done processing the
104 // previous input data.
105 kWaitingForInputData,
106 // The main thread moves all the threads into this state once it has
107 // finished setting up new input data.
108 kProcessing,
109 };
110
111 // The main function for a thread.
112 void RunThread(int index);
113
114 // Returns true if all threads are currently done.
115 bool AllThreadsDone() const {
116 for (ThreadState state : states_) {
117 if (state != ThreadState::kWaitingForInputData) {
118 return false;
119 }
120 }
121 return true;
122 }
123
124 std::array<std::thread, kThreads> threads_;
125 // Protects access to the states_ and coordinates with condition_variable_.
126 std::mutex mutex_;
127 // Signals changes to states_ and quit_.
128 std::condition_variable condition_variable_;
129 bool quit_ = false;
130
131 std::array<ThreadState, kThreads> states_;
132
133 // Access to these is protected by coordination via states_.
134 ImageFormat input_format_;
135 const char *input_data_;
136 uint8_t input_value_;
137 std::array<RangeImage, kThreads> outputs_;
138};
139
Parker Schuh6691f192017-01-14 17:01:02 -0800140} // namespace vision
141} // namespace aos
142
Brian Silverman37b15b32019-03-10 13:30:18 -0700143#endif // AOS_VISION_BLOB_THRESHOLD_H_