Index into find_elements boxes of the element ranked first by focus, chosen on the GPU from the analyzer's own area, shape and nearest-neighbour results.
Empty without find_elements boxes.
302{
303 if (!analysis.find_elements || analysis.find_elements->boxes.empty())
304 return std::nullopt;
305
306 const auto& elements = *analysis.find_elements;
307 const auto count = elements.boxes.size();
308
309 std::vector<float> metrics;
310 metrics.reserve(
count * 6);
311 for (
size_t i = 0; i <
count; ++i) {
312 const auto& box = elements.boxes[i];
313 const uint32_t
label = box.label_id != 0 ? box.label_id :
static_cast<uint32_t
>(i + 1U);
314 const auto contour = std::ranges::find_if(elements.contours,
315 [
label](
const auto& c) { return c.label_id == label; });
316
317 metrics.push_back(contour != elements.contours.end() ? contour->area : box.w * box.
h);
318 metrics.push_back(i < elements.shapes.size() ? elements.shapes[i].compactness : 0.0F);
319 metrics.push_back(i < elements.shapes.size() ? elements.shapes[i].aspect_ratio : 0.0F);
320 metrics.push_back(i < elements.nearest_neighbor_distance_px.size() ? elements.nearest_neighbor_distance_px[i] : 0.0F);
321 metrics.push_back(box.x + box.w * 0.5F);
322 metrics.push_back(box.y + box.h * 0.5F);
323 }
324
326 use_shader(ctx, "element_select.comp.spv", k_wg_reduce, sizeof(SelectPC));
327 ctx.set_binding_data(3, std::span<const float>(metrics));
328 ctx.set_push_constants(SelectPC {
329 .count =
static_cast<uint32_t
>(
count),
330 .
criterion =
static_cast<uint32_t
>(focus.criterion),
331 .largest = focus.largest ? 1U : 0
U,
332 .px = focus.point.x,
333 .py = focus.point.y,
334 });
335
336 const auto raw = dispatch_reduce(ctx, 256, 2 * sizeof(float));
337 if (raw.empty())
338 return std::nullopt;
339
340 const auto index =
static_cast<size_t>(std::lround(raw[0]));
341 return index < count ? std::optional<size_t> {
index } : std::nullopt;
342}