blob: 8251b3ab025671ef8efd69e5d7337e589390d844 [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
Brian Silverman20b57772019-03-23 22:02:49 -07004#include <condition_variable>
5#include <mutex>
6#include <thread>
7
Parker Schuh6691f192017-01-14 17:01:02 -08008#include "aos/vision/blob/range_image.h"
9#include "aos/vision/image/image_types.h"
10
11namespace aos {
12namespace vision {
Brian Silverman37b15b32019-03-10 13:30:18 -070013namespace threshold_internal {
Parker Schuh6691f192017-01-14 17:01:02 -080014
Brian Silverman37b15b32019-03-10 13:30:18 -070015// Performs thresholding in a given region using a function which determines
16// whether a given point is in or out of the region.
17//
18// fn must return a bool when called with two integers (x, y).
19template <typename PointTestFn>
20RangeImage ThresholdPointsWithFunction(ImageFormat fmt, PointTestFn &&fn) {
21 static_assert(
22 std::is_convertible<PointTestFn, std::function<bool(int, int)>>::value,
23 "Invalid threshold function");
Brian Silverman4482db52019-03-10 16:14:48 -070024 std::vector<std::vector<ImageRange>> result;
25 result.reserve(fmt.h);
26 // Iterate through each row.
Parker Schuh02f13f62019-02-16 16:42:41 -080027 for (int y = 0; y < fmt.h; ++y) {
Brian Silverman4482db52019-03-10 16:14:48 -070028 // Whether we're currently in a range.
29 bool in_range = false;
30 int current_range_start = -1;
31 std::vector<ImageRange> current_row_ranges;
32 // Iterate through each pixel.
Parker Schuh02f13f62019-02-16 16:42:41 -080033 for (int x = 0; x < fmt.w; ++x) {
Brian Silverman4482db52019-03-10 16:14:48 -070034 if (fn(x, y) != in_range) {
35 if (in_range) {
36 current_row_ranges.emplace_back(ImageRange(current_range_start, x));
Parker Schuh6691f192017-01-14 17:01:02 -080037 } else {
Brian Silverman4482db52019-03-10 16:14:48 -070038 current_range_start = x;
Parker Schuh6691f192017-01-14 17:01:02 -080039 }
Brian Silverman4482db52019-03-10 16:14:48 -070040 in_range = !in_range;
Parker Schuh6691f192017-01-14 17:01:02 -080041 }
42 }
Brian Silverman4482db52019-03-10 16:14:48 -070043 if (in_range) {
44 current_row_ranges.emplace_back(ImageRange(current_range_start, fmt.w));
Parker Schuh6691f192017-01-14 17:01:02 -080045 }
Brian Silverman4482db52019-03-10 16:14:48 -070046 result.push_back(current_row_ranges);
Parker Schuh6691f192017-01-14 17:01:02 -080047 }
Brian Silverman4482db52019-03-10 16:14:48 -070048 return RangeImage(0, std::move(result));
Parker Schuh6691f192017-01-14 17:01:02 -080049}
50
Brian Silverman37b15b32019-03-10 13:30:18 -070051} // namespace threshold_internal
52
53// Thresholds an image using a function which determines whether a given pixel
54// value is in or out of the region.
55//
56// fn must return a bool when called with a PixelRef.
Parker Schuh02f13f62019-02-16 16:42:41 -080057template <typename ThresholdFn>
Brian Silverman37b15b32019-03-10 13:30:18 -070058RangeImage ThresholdImageWithFunction(const ImagePtr &img, ThresholdFn &&fn) {
59 static_assert(
60 std::is_convertible<ThresholdFn, std::function<bool(PixelRef)>>::value,
61 "Invalid threshold function");
62 return threshold_internal::ThresholdPointsWithFunction(
63 img.fmt(), [&](int x, int y) { return fn(img.get_px(x, y)); });
Parker Schuh02f13f62019-02-16 16:42:41 -080064}
65
Brian Silverman37b15b32019-03-10 13:30:18 -070066// Thresholds an image in YUYV format, selecting pixels with a Y (luma) greater
67// than value.
68//
69// This is implemented via a simple function that pulls out the Y values and
70// compares them each. It mostly exists for tests to compare against
71// FastYuyvYThreshold, because it's obviously correct.
72inline RangeImage SlowYuyvYThreshold(ImageFormat fmt, const char *data,
73 uint8_t value) {
74 return threshold_internal::ThresholdPointsWithFunction(
75 fmt, [&](int x, int y) {
76 uint8_t v = data[x * 2 + y * fmt.w * 2];
77 return v > value;
78 });
Parker Schuh02f13f62019-02-16 16:42:41 -080079}
80
Brian Silverman37b15b32019-03-10 13:30:18 -070081// Thresholds an image in YUYV format, selecting pixels with a Y (luma) greater
Brian Silverman4482db52019-03-10 16:14:48 -070082// than value. The width must be a multiple of 4.
Brian Silverman37b15b32019-03-10 13:30:18 -070083//
84// This is implemented via some tricky bit shuffling that goes fast.
85RangeImage FastYuyvYThreshold(ImageFormat fmt, const char *data, uint8_t value);
86
Brian Silverman20b57772019-03-23 22:02:49 -070087// Manages a pool of threads which do sharded thresholding.
88class FastYuyvYPooledThresholder {
89 public:
90 // The number of threads we'll use.
91 static constexpr int kThreads = 4;
92
93 FastYuyvYPooledThresholder();
94 // Shuts down and joins the threads.
95 ~FastYuyvYPooledThresholder();
96
97 // Actually does a threshold, merges the result, and returns it.
98 RangeImage Threshold(ImageFormat fmt, const char *data, uint8_t value);
99
100 private:
101 enum class ThreadState {
102 // Each thread moves itself into this state once it's done processing the
103 // previous input data.
104 kWaitingForInputData,
105 // The main thread moves all the threads into this state once it has
106 // finished setting up new input data.
107 kProcessing,
108 };
109
110 // The main function for a thread.
111 void RunThread(int index);
112
113 // Returns true if all threads are currently done.
114 bool AllThreadsDone() const {
115 for (ThreadState state : states_) {
116 if (state != ThreadState::kWaitingForInputData) {
117 return false;
118 }
119 }
120 return true;
121 }
122
123 std::array<std::thread, kThreads> threads_;
124 // Protects access to the states_ and coordinates with condition_variable_.
125 std::mutex mutex_;
126 // Signals changes to states_ and quit_.
127 std::condition_variable condition_variable_;
128 bool quit_ = false;
129
130 std::array<ThreadState, kThreads> states_;
131
132 // Access to these is protected by coordination via states_.
133 ImageFormat input_format_;
134 const char *input_data_;
135 uint8_t input_value_;
136 std::array<RangeImage, kThreads> outputs_;
137};
138
Parker Schuh6691f192017-01-14 17:01:02 -0800139} // namespace vision
140} // namespace aos
141
Brian Silverman37b15b32019-03-10 13:30:18 -0700142#endif // AOS_VISION_BLOB_THRESHOLD_H_