Apply an Otsu threshold to the current image.
Computes the histogram and threshold on the GPU, then replaces the working image with the binary result and publishes it as VisionResult::images.threshold_otsu.
38{
39 auto& pixel_ctx = contexts.pixel;
40 auto w = contexts.pass.w;
41 auto h = contexts.pass.h;
43
44 const auto otsu_input = contexts.pass.current;
45
50
51 const std::array<uint32_t, 768> histogram_reset {};
52 pixel_ctx.upload_shared_raw(0, 3, reinterpret_cast<const uint8_t*>(histogram_reset.data()), sizeof(histogram_reset));
53
54 const auto channels =
static_cast<uint32_t
>(p.channels);
55 const OtsuHistPC hist_pc { .width = w, .height =
h, .channels =
channels };
56 const OtsuApplyPC apply_pc { .channels =
channels };
57 const uint32_t select_groups =
channels == 0
U ? 1U : 3U;
58 const auto otsu_hazard = [](uint32_t binding) {
59 return [binding](GpuDispatchCore& ctx) {
60 return std::vector<HazardResource> {
62 };
63 };
64 };
66
67 std::shared_ptr<Core::VKImage> thresholded;
68 std::vector<DependencyStage> otsu_stages;
69 otsu_stages.reserve(3);
70 otsu_stages.push_back({
71 .config = { .shader_path =
"otsu_histogram.comp.spv", .workgroup_size =
k_wg2d, .push_constant_size =
sizeof(OtsuHistPC) },
72 .stage_fn = [&pixel_ctx, otsu_input, hist_pc](GpuDispatchCore& ctx) {
73 pixel_ctx.stage_image(otsu_input);
74 ctx.set_push_constants(hist_pc); },
75 .hazard_fn = otsu_hazard(3),
76 .explicit_groups = image_groups,
77 });
78 otsu_stages.push_back({
79 .config = { .shader_path = "otsu_select.comp.spv", .workgroup_size = { 256, 1, 1 } },
80 .stage_fn = [&pixel_ctx, otsu_input](GpuDispatchCore&) { pixel_ctx.stage_image(otsu_input); },
81 .hazard_fn = otsu_hazard(4),
82 .explicit_groups = std::array<uint32_t, 3> { select_groups, 1U, 1U },
83 });
84 otsu_stages.push_back({
85 .config = { .shader_path =
"otsu_apply.comp.spv", .workgroup_size =
k_wg2d, .push_constant_size =
sizeof(OtsuApplyPC) },
86 .stage_fn = [&pixel_ctx, &thresholded, otsu_input, w,
h, apply_pc](GpuDispatchCore& ctx) {
87 pixel_ctx.stage_image(otsu_input);
88 ctx.set_push_constants(apply_pc);
89 pixel_ctx.prepare_output_image(w,
h);
90 thresholded = pixel_ctx.get_output_image(0); },
91 .explicit_groups = image_groups,
92 });
93
94 {
95 ExecutionContext otsu_ctx;
97 DependencyParams otsu_params;
98 otsu_params.stages = otsu_stages;
99 otsu_params.async = true;
100 otsu_ctx.parameters = otsu_params;
101 const auto result = pixel_ctx.execute(Datum<> {}, otsu_ctx);
103 foundry.wait_for_fence(f);
104 foundry.release_fence(f);
105 }
106
107 contexts.pass.result.images.threshold_otsu = thresholded;
108 contexts.pass.current = thresholded;
109 contexts.pass.result.structured = std::monostate {};
110
111 return { .output = thresholded, .input = otsu_input };
112}
constexpr FenceID INVALID_FENCE
MAYAFLUX_API ShaderFoundry & get_shader_foundry()
Get the global shader compiler instance.
@ COMPUTE
Storage buffer for compute shaders (device-local)
constexpr std::array< uint32_t, 3 > k_wg2d
Standard 2D workgroup used by all pixel-to-pixel vision shaders.
@ DEPENDENCY
Part of dependency graph.