Otsu threshold writing into caller-supplied buffer.
Finds the optimal global threshold by maximising inter-class variance, then applies it.
300{
301 constexpr int32_t bins = 256;
302 std::array<float, bins> hist {};
303
304 for (float v : gray)
305 hist[static_cast<int32_t>(
std::clamp(v, 0.0F, 1.0F) * (bins - 1))]++;
306
307 const auto total = static_cast<float>(gray.size());
310
311 float sum_all = 0.0F;
312 for (int32_t i = 0; i < bins; ++i)
313 sum_all += static_cast<float>(i) * hist[i];
314
315 float sum_bg = 0.0F;
316 float w_bg = 0.0F;
317 float best_var = 0.0F;
318 int32_t best_t = 0;
319
320 for (int32_t t = 0; t < bins; ++t) {
321 w_bg += hist[t];
322 if (w_bg == 0.0F)
323 continue;
324
325 const float w_fg = 1.0F - w_bg;
326 if (w_fg == 0.0F)
327 break;
328
329 sum_bg += static_cast<float>(t) * hist[t];
330
331 const float mean_bg = sum_bg / w_bg;
332 const float mean_fg = (sum_all - sum_bg) / w_fg;
333 const float diff = mean_fg - mean_bg;
334 const float var = w_bg * w_fg * diff * diff;
335
336 if (var > best_var) {
337 best_var = var;
338 best_t = t;
339 }
340 }
341
342 const float t_norm = static_cast<float>(best_t) / static_cast<float>(bins - 1);
344}