blob: effd321adb78803fa393c3a2c9d54ac92e7bae1a [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
Stephan Pleinescc500b92024-05-30 10:58:40 -07004#include <stdint.h>
5
Alex Perrycb7da4b2019-08-28 19:35:56 -07006#include <array>
Brian Silverman20b57772019-03-23 22:02:49 -07007#include <condition_variable>
James Kuszmaul3ae42262019-11-08 12:33:41 -08008#include <functional>
Brian Silverman20b57772019-03-23 22:02:49 -07009#include <mutex>
10#include <thread>
Stephan Pleinescc500b92024-05-30 10:58:40 -070011#include <type_traits>
12#include <utility>
13#include <vector>
Brian Silverman20b57772019-03-23 22:02:49 -070014
Parker Schuh6691f192017-01-14 17:01:02 -080015#include "aos/vision/blob/range_image.h"
16#include "aos/vision/image/image_types.h"
17
18namespace aos {
19namespace vision {
Brian Silverman37b15b32019-03-10 13:30:18 -070020namespace threshold_internal {
Parker Schuh6691f192017-01-14 17:01:02 -080021
Brian Silverman37b15b32019-03-10 13:30:18 -070022// Performs thresholding in a given region using a function which determines
23// whether a given point is in or out of the region.
24//
25// fn must return a bool when called with two integers (x, y).
26template <typename PointTestFn>
27RangeImage ThresholdPointsWithFunction(ImageFormat fmt, PointTestFn &&fn) {
28 static_assert(
29 std::is_convertible<PointTestFn, std::function<bool(int, int)>>::value,
30 "Invalid threshold function");
Brian Silverman4482db52019-03-10 16:14:48 -070031 std::vector<std::vector<ImageRange>> result;
32 result.reserve(fmt.h);
33 // Iterate through each row.
Parker Schuh02f13f62019-02-16 16:42:41 -080034 for (int y = 0; y < fmt.h; ++y) {
Brian Silverman4482db52019-03-10 16:14:48 -070035 // Whether we're currently in a range.
36 bool in_range = false;
37 int current_range_start = -1;
38 std::vector<ImageRange> current_row_ranges;
39 // Iterate through each pixel.
Parker Schuh02f13f62019-02-16 16:42:41 -080040 for (int x = 0; x < fmt.w; ++x) {
Brian Silverman4482db52019-03-10 16:14:48 -070041 if (fn(x, y) != in_range) {
42 if (in_range) {
43 current_row_ranges.emplace_back(ImageRange(current_range_start, x));
Parker Schuh6691f192017-01-14 17:01:02 -080044 } else {
Brian Silverman4482db52019-03-10 16:14:48 -070045 current_range_start = x;
Parker Schuh6691f192017-01-14 17:01:02 -080046 }
Brian Silverman4482db52019-03-10 16:14:48 -070047 in_range = !in_range;
Parker Schuh6691f192017-01-14 17:01:02 -080048 }
49 }
Brian Silverman4482db52019-03-10 16:14:48 -070050 if (in_range) {
51 current_row_ranges.emplace_back(ImageRange(current_range_start, fmt.w));
Parker Schuh6691f192017-01-14 17:01:02 -080052 }
Brian Silverman4482db52019-03-10 16:14:48 -070053 result.push_back(current_row_ranges);
Parker Schuh6691f192017-01-14 17:01:02 -080054 }
Brian Silverman4482db52019-03-10 16:14:48 -070055 return RangeImage(0, std::move(result));
Parker Schuh6691f192017-01-14 17:01:02 -080056}
57
Brian Silverman37b15b32019-03-10 13:30:18 -070058} // namespace threshold_internal
59
60// Thresholds an image using a function which determines whether a given pixel
61// value is in or out of the region.
62//
63// fn must return a bool when called with a PixelRef.
Parker Schuh02f13f62019-02-16 16:42:41 -080064template <typename ThresholdFn>
Brian Silverman37b15b32019-03-10 13:30:18 -070065RangeImage ThresholdImageWithFunction(const ImagePtr &img, ThresholdFn &&fn) {
66 static_assert(
67 std::is_convertible<ThresholdFn, std::function<bool(PixelRef)>>::value,
68 "Invalid threshold function");
69 return threshold_internal::ThresholdPointsWithFunction(
70 img.fmt(), [&](int x, int y) { return fn(img.get_px(x, y)); });
Parker Schuh02f13f62019-02-16 16:42:41 -080071}
72
Brian Silverman37b15b32019-03-10 13:30:18 -070073// Thresholds an image in YUYV format, selecting pixels with a Y (luma) greater
74// than value.
75//
76// This is implemented via a simple function that pulls out the Y values and
77// compares them each. It mostly exists for tests to compare against
78// FastYuyvYThreshold, because it's obviously correct.
79inline RangeImage SlowYuyvYThreshold(ImageFormat fmt, const char *data,
80 uint8_t value) {
81 return threshold_internal::ThresholdPointsWithFunction(
82 fmt, [&](int x, int y) {
83 uint8_t v = data[x * 2 + y * fmt.w * 2];
84 return v > value;
85 });
Parker Schuh02f13f62019-02-16 16:42:41 -080086}
87
Brian Silverman37b15b32019-03-10 13:30:18 -070088// Thresholds an image in YUYV format, selecting pixels with a Y (luma) greater
Brian Silverman4482db52019-03-10 16:14:48 -070089// than value. The width must be a multiple of 4.
Brian Silverman37b15b32019-03-10 13:30:18 -070090//
91// This is implemented via some tricky bit shuffling that goes fast.
92RangeImage FastYuyvYThreshold(ImageFormat fmt, const char *data, uint8_t value);
93
Brian Silverman20b57772019-03-23 22:02:49 -070094// Manages a pool of threads which do sharded thresholding.
95class FastYuyvYPooledThresholder {
96 public:
97 // The number of threads we'll use.
98 static constexpr int kThreads = 4;
99
100 FastYuyvYPooledThresholder();
101 // Shuts down and joins the threads.
102 ~FastYuyvYPooledThresholder();
103
104 // Actually does a threshold, merges the result, and returns it.
105 RangeImage Threshold(ImageFormat fmt, const char *data, uint8_t value);
106
107 private:
108 enum class ThreadState {
109 // Each thread moves itself into this state once it's done processing the
110 // previous input data.
111 kWaitingForInputData,
112 // The main thread moves all the threads into this state once it has
113 // finished setting up new input data.
114 kProcessing,
115 };
116
117 // The main function for a thread.
118 void RunThread(int index);
119
120 // Returns true if all threads are currently done.
121 bool AllThreadsDone() const {
122 for (ThreadState state : states_) {
123 if (state != ThreadState::kWaitingForInputData) {
124 return false;
125 }
126 }
127 return true;
128 }
129
130 std::array<std::thread, kThreads> threads_;
131 // Protects access to the states_ and coordinates with condition_variable_.
132 std::mutex mutex_;
133 // Signals changes to states_ and quit_.
134 std::condition_variable condition_variable_;
135 bool quit_ = false;
136
137 std::array<ThreadState, kThreads> states_;
138
139 // Access to these is protected by coordination via states_.
140 ImageFormat input_format_;
141 const char *input_data_;
142 uint8_t input_value_;
143 std::array<RangeImage, kThreads> outputs_;
144};
145
Parker Schuh6691f192017-01-14 17:01:02 -0800146} // namespace vision
147} // namespace aos
148
Brian Silverman37b15b32019-03-10 13:30:18 -0700149#endif // AOS_VISION_BLOB_THRESHOLD_H_