MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches

◆ canny() [2/2]

MAYAFLUX_API std::vector< float > MayaFlux::Kinesis::Vision::canny ( std::span< const float >  gray,
uint32_t  w,
uint32_t  h,
float  sigma,
float  low_threshold,
float  high_threshold 
)

Canny edge detector.

Pipeline: gaussian_blur(sigma) -> sobel -> non-maximum suppression -> double threshold -> hysteresis. Output is binary: 1.0f on edges, 0.0f elsewhere.

Parameters
graySingle-channel float span, size must be w * h.
wImage width in pixels.
hImage height in pixels.
sigmaGaussian pre-blur sigma. 0.0f skips blur.
low_thresholdHysteresis low threshold in [0, 1].
high_thresholdHysteresis high threshold in [0, 1].
Returns
Binary float vector of length w * h.

Definition at line 132 of file Gradient.cpp.

135{
136 const size_t n = static_cast<size_t>(w) * h;
137
138 std::vector<float> blurred;
139 std::span<const float> src = gray;
140 if (sigma > 0.0F) {
141 blurred = gaussian_blur(gray, w, h, sigma);
142 src = blurred;
143 }
144
145 auto grad = sobel(src, w, h);
146
147 std::vector<float> nms(n, 0.0F);
148 P::for_each(P::par_unseq,
149 std::views::iota(size_t { 0 }, n).begin(),
150 std::views::iota(size_t { 0 }, n).end(),
151 [&](size_t idx) {
152 const auto px = static_cast<int32_t>(idx % w);
153 const auto py = static_cast<int32_t>(idx / w);
154
155 if (px == 0 || py == 0
156 || px == static_cast<int32_t>(w) - 1
157 || py == static_cast<int32_t>(h) - 1)
158 return;
159
160 const float angle = grad.angle[idx];
161 const float pi = std::numbers::pi_v<float>;
162
163 float norm_angle = angle < 0.0F ? angle + pi : angle;
164 norm_angle = norm_angle / pi * 4.0F;
165
166 int32_t ox = 0, oy = 0;
167 if (norm_angle < 0.5F || norm_angle >= 3.5F) {
168 ox = 1;
169 } else if (norm_angle < 1.5F) {
170 ox = 1;
171 oy = 1;
172 } else if (norm_angle < 2.5F) {
173 oy = 1;
174 } else {
175 ox = -1;
176 oy = 1;
177 }
178
179 const float m = grad.magnitude[idx];
180 const float m_pos = grad.magnitude[static_cast<size_t>(py + oy) * w + static_cast<size_t>(px + ox)];
181 const float m_neg = grad.magnitude[static_cast<size_t>(py - oy) * w + static_cast<size_t>(px - ox)];
182
183 if (m >= m_pos && m >= m_neg)
184 nms[idx] = m;
185 });
186
187 std::vector<float> out(n, 0.0F);
188 std::vector<uint8_t> strong(n, 0);
189 std::vector<uint8_t> weak(n, 0);
190
191 for (size_t i = 0; i < n; ++i) {
192 if (nms[i] >= high_threshold) {
193 strong[i] = 1;
194 } else if (nms[i] >= low_threshold) {
195 weak[i] = 1;
196 }
197 }
198
199 std::vector<size_t> stack;
200 for (size_t i = 0; i < n; ++i) {
201 if (strong[i])
202 stack.push_back(i);
203 }
204
205 while (!stack.empty()) {
206 const size_t idx = stack.back();
207 stack.pop_back();
208 out[idx] = 1.0F;
209
210 const auto px = static_cast<int32_t>(idx % w);
211 const auto py = static_cast<int32_t>(idx / w);
212
213 for (int32_t dy = -1; dy <= 1; ++dy) {
214 for (int32_t dx = -1; dx <= 1; ++dx) {
215 if (dx == 0 && dy == 0)
216 continue;
217 const int32_t nx = px + dx;
218 const int32_t ny = py + dy;
219 if (nx < 0 || ny < 0
220 || nx >= static_cast<int32_t>(w)
221 || ny >= static_cast<int32_t>(h))
222 continue;
223 const size_t ni = static_cast<size_t>(ny) * w + nx;
224 if (weak[ni] && out[ni] == 0.0F)
225 stack.push_back(ni);
226 }
227 }
228 }
229
230 return out;
231}
uint32_t h
Definition InkPress.cpp:28
float sigma
std::vector< float > gaussian_blur(std::span< const float > src, uint32_t w, uint32_t h, float sigma)
Separable Gaussian blur.

References gaussian_blur(), h, sigma, and sobel().

Referenced by canny(), and MayaFlux::Kinesis::Vision::VisionExecutor::run().

+ Here is the call graph for this function:
+ Here is the caller graph for this function: