blob: 95330c57ede8bcef2170520390d16be32e7ba327 [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>
James Kuszmaul3ae42262019-11-08 12:33:41 -08006#include <functional>
Brian Silverman20b57772019-03-23 22:02:49 -07007#include <mutex>
8#include <thread>
9
Parker Schuh6691f192017-01-14 17:01:02 -080010#include "aos/vision/blob/range_image.h"
11#include "aos/vision/image/image_types.h"
12
13namespace aos {
14namespace vision {
Brian Silverman37b15b32019-03-10 13:30:18 -070015namespace threshold_internal {
Parker Schuh6691f192017-01-14 17:01:02 -080016
Brian Silverman37b15b32019-03-10 13:30:18 -070017// Performs thresholding in a given region using a function which determines
18// whether a given point is in or out of the region.
19//
20// fn must return a bool when called with two integers (x, y).
21template <typename PointTestFn>
22RangeImage ThresholdPointsWithFunction(ImageFormat fmt, PointTestFn &&fn) {
23 static_assert(
24 std::is_convertible<PointTestFn, std::function<bool(int, int)>>::value,
25 "Invalid threshold function");
Brian Silverman4482db52019-03-10 16:14:48 -070026 std::vector<std::vector<ImageRange>> result;
27 result.reserve(fmt.h);
28 // Iterate through each row.
Parker Schuh02f13f62019-02-16 16:42:41 -080029 for (int y = 0; y < fmt.h; ++y) {
Brian Silverman4482db52019-03-10 16:14:48 -070030 // Whether we're currently in a range.
31 bool in_range = false;
32 int current_range_start = -1;
33 std::vector<ImageRange> current_row_ranges;
34 // Iterate through each pixel.
Parker Schuh02f13f62019-02-16 16:42:41 -080035 for (int x = 0; x < fmt.w; ++x) {
Brian Silverman4482db52019-03-10 16:14:48 -070036 if (fn(x, y) != in_range) {
37 if (in_range) {
38 current_row_ranges.emplace_back(ImageRange(current_range_start, x));
Parker Schuh6691f192017-01-14 17:01:02 -080039 } else {
Brian Silverman4482db52019-03-10 16:14:48 -070040 current_range_start = x;
Parker Schuh6691f192017-01-14 17:01:02 -080041 }
Brian Silverman4482db52019-03-10 16:14:48 -070042 in_range = !in_range;
Parker Schuh6691f192017-01-14 17:01:02 -080043 }
44 }
Brian Silverman4482db52019-03-10 16:14:48 -070045 if (in_range) {
46 current_row_ranges.emplace_back(ImageRange(current_range_start, fmt.w));
Parker Schuh6691f192017-01-14 17:01:02 -080047 }
Brian Silverman4482db52019-03-10 16:14:48 -070048 result.push_back(current_row_ranges);
Parker Schuh6691f192017-01-14 17:01:02 -080049 }
Brian Silverman4482db52019-03-10 16:14:48 -070050 return RangeImage(0, std::move(result));
Parker Schuh6691f192017-01-14 17:01:02 -080051}
52
Brian Silverman37b15b32019-03-10 13:30:18 -070053} // namespace threshold_internal
54
55// Thresholds an image using a function which determines whether a given pixel
56// value is in or out of the region.
57//
58// fn must return a bool when called with a PixelRef.
Parker Schuh02f13f62019-02-16 16:42:41 -080059template <typename ThresholdFn>
Brian Silverman37b15b32019-03-10 13:30:18 -070060RangeImage ThresholdImageWithFunction(const ImagePtr &img, ThresholdFn &&fn) {
61 static_assert(
62 std::is_convertible<ThresholdFn, std::function<bool(PixelRef)>>::value,
63 "Invalid threshold function");
64 return threshold_internal::ThresholdPointsWithFunction(
65 img.fmt(), [&](int x, int y) { return fn(img.get_px(x, y)); });
Parker Schuh02f13f62019-02-16 16:42:41 -080066}
67
Brian Silverman37b15b32019-03-10 13:30:18 -070068// Thresholds an image in YUYV format, selecting pixels with a Y (luma) greater
69// than value.
70//
71// This is implemented via a simple function that pulls out the Y values and
72// compares them each. It mostly exists for tests to compare against
73// FastYuyvYThreshold, because it's obviously correct.
74inline RangeImage SlowYuyvYThreshold(ImageFormat fmt, const char *data,
75 uint8_t value) {
76 return threshold_internal::ThresholdPointsWithFunction(
77 fmt, [&](int x, int y) {
78 uint8_t v = data[x * 2 + y * fmt.w * 2];
79 return v > value;
80 });
Parker Schuh02f13f62019-02-16 16:42:41 -080081}
82
Brian Silverman37b15b32019-03-10 13:30:18 -070083// Thresholds an image in YUYV format, selecting pixels with a Y (luma) greater
Brian Silverman4482db52019-03-10 16:14:48 -070084// than value. The width must be a multiple of 4.
Brian Silverman37b15b32019-03-10 13:30:18 -070085//
86// This is implemented via some tricky bit shuffling that goes fast.
87RangeImage FastYuyvYThreshold(ImageFormat fmt, const char *data, uint8_t value);
88
Brian Silverman20b57772019-03-23 22:02:49 -070089// Manages a pool of threads which do sharded thresholding.
90class FastYuyvYPooledThresholder {
91 public:
92 // The number of threads we'll use.
93 static constexpr int kThreads = 4;
94
95 FastYuyvYPooledThresholder();
96 // Shuts down and joins the threads.
97 ~FastYuyvYPooledThresholder();
98
99 // Actually does a threshold, merges the result, and returns it.
100 RangeImage Threshold(ImageFormat fmt, const char *data, uint8_t value);
101
102 private:
103 enum class ThreadState {
104 // Each thread moves itself into this state once it's done processing the
105 // previous input data.
106 kWaitingForInputData,
107 // The main thread moves all the threads into this state once it has
108 // finished setting up new input data.
109 kProcessing,
110 };
111
112 // The main function for a thread.
113 void RunThread(int index);
114
115 // Returns true if all threads are currently done.
116 bool AllThreadsDone() const {
117 for (ThreadState state : states_) {
118 if (state != ThreadState::kWaitingForInputData) {
119 return false;
120 }
121 }
122 return true;
123 }
124
125 std::array<std::thread, kThreads> threads_;
126 // Protects access to the states_ and coordinates with condition_variable_.
127 std::mutex mutex_;
128 // Signals changes to states_ and quit_.
129 std::condition_variable condition_variable_;
130 bool quit_ = false;
131
132 std::array<ThreadState, kThreads> states_;
133
134 // Access to these is protected by coordination via states_.
135 ImageFormat input_format_;
136 const char *input_data_;
137 uint8_t input_value_;
138 std::array<RangeImage, kThreads> outputs_;
139};
140
Parker Schuh6691f192017-01-14 17:01:02 -0800141} // namespace vision
142} // namespace aos
143
Brian Silverman37b15b32019-03-10 13:30:18 -0700144#endif // AOS_VISION_BLOB_THRESHOLD_H_