Add M9-M14: UI redesign, plugins, macros, 3D viz, binary/segmentation ops, Metal GPU, scientific LUTs
M9: TailwindCSS + shadcn/ui frontend redesign
M10: Dynamic plugin system with shared library loading
M11: Macro recording, playback, and batch processing
M12: 3D volume visualization, marching cubes, STL/OBJ export, rivol:// protocol
M13: Additional GPU shaders, enhanced auto-threshold, merge channels
M14: Binary image processing (EDT, watershed, skeleton, connected components,
voronoi), segmentation & analysis (particles, colocalization, find maxima),
math/noise/filter/transform ops (60+ total), scientific LUTs (Fire, Ice,
Spectrum, Jet, Phase, HiLo + .lut file I/O), Apple Metal GPU optimization
(metal-only feature, Apple Silicon detection, lower dispatch threshold),
native Metal compute crate (ri-metal with MSL shaders via objc2-metal),
7 new WGSL GPU shaders (bilateral, variance, mean, math_ops, outline,
minmax_filter, affine transform). 25 crates, 162 tests, 75+ IPC commands.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
1889715f3c
commit
0fd39d7fd3
@@ -0,0 +1,107 @@
|
||||
#include <metal_stdlib>
|
||||
using namespace metal;
|
||||
|
||||
/// Jump Flooding Algorithm (JFA) for Euclidean Distance Transform.
|
||||
/// Pass 1 (seed): Initialize seed coordinates from binary input.
|
||||
/// Pass 2..N (jump): Propagate nearest seed with decreasing step sizes.
|
||||
/// Final: Compute Euclidean distance from seed coordinates.
|
||||
|
||||
struct EdtParams {
|
||||
uint width;
|
||||
uint height;
|
||||
int step_size; // Current JFA step size; 0 for seed pass, -1 for final distance pass
|
||||
uint _pad;
|
||||
};
|
||||
|
||||
// Seed coordinate encoding: pack (x, y) into a uint2.
|
||||
// Use (0xFFFF, 0xFFFF) as sentinel for "no seed".
|
||||
constant uint NO_SEED = 0xFFFFu;
|
||||
|
||||
kernel void edt_seed(
|
||||
device const float* input [[buffer(0)]],
|
||||
device uint2* seeds [[buffer(1)]],
|
||||
constant EdtParams& params [[buffer(2)]],
|
||||
uint2 gid [[thread_position_in_grid]]
|
||||
) {
|
||||
uint w = params.width;
|
||||
uint h = params.height;
|
||||
if (gid.x >= w || gid.y >= h) return;
|
||||
|
||||
uint idx = gid.y * w + gid.x;
|
||||
// Foreground pixels (> 0.5) become seeds pointing to themselves
|
||||
if (input[idx] > 0.5f) {
|
||||
seeds[idx] = uint2(gid.x, gid.y);
|
||||
} else {
|
||||
seeds[idx] = uint2(NO_SEED, NO_SEED);
|
||||
}
|
||||
}
|
||||
|
||||
kernel void edt_jump(
|
||||
device uint2* seeds [[buffer(0)]],
|
||||
device uint2* seeds_out [[buffer(1)]],
|
||||
constant EdtParams& params [[buffer(2)]],
|
||||
uint2 gid [[thread_position_in_grid]]
|
||||
) {
|
||||
uint w = params.width;
|
||||
uint h = params.height;
|
||||
int step = params.step_size;
|
||||
if (gid.x >= w || gid.y >= h) return;
|
||||
|
||||
uint idx = gid.y * w + gid.x;
|
||||
uint2 best_seed = seeds[idx];
|
||||
float best_dist = 1e30f;
|
||||
|
||||
if (best_seed.x != NO_SEED) {
|
||||
float dx = (float)gid.x - (float)best_seed.x;
|
||||
float dy = (float)gid.y - (float)best_seed.y;
|
||||
best_dist = dx * dx + dy * dy;
|
||||
}
|
||||
|
||||
// Check 8 neighbors at current step distance
|
||||
for (int dy = -1; dy <= 1; dy++) {
|
||||
for (int dx = -1; dx <= 1; dx++) {
|
||||
if (dx == 0 && dy == 0) continue;
|
||||
|
||||
int nx = (int)gid.x + dx * step;
|
||||
int ny = (int)gid.y + dy * step;
|
||||
|
||||
if (nx < 0 || nx >= (int)w || ny < 0 || ny >= (int)h) continue;
|
||||
|
||||
uint2 neighbor_seed = seeds[(uint)ny * w + (uint)nx];
|
||||
if (neighbor_seed.x == NO_SEED) continue;
|
||||
|
||||
float ndx = (float)gid.x - (float)neighbor_seed.x;
|
||||
float ndy = (float)gid.y - (float)neighbor_seed.y;
|
||||
float dist = ndx * ndx + ndy * ndy;
|
||||
|
||||
if (dist < best_dist) {
|
||||
best_dist = dist;
|
||||
best_seed = neighbor_seed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
seeds_out[idx] = best_seed;
|
||||
}
|
||||
|
||||
kernel void edt_distance(
|
||||
device const uint2* seeds [[buffer(0)]],
|
||||
device float* output [[buffer(1)]],
|
||||
constant EdtParams& params [[buffer(2)]],
|
||||
uint2 gid [[thread_position_in_grid]]
|
||||
) {
|
||||
uint w = params.width;
|
||||
uint h = params.height;
|
||||
if (gid.x >= w || gid.y >= h) return;
|
||||
|
||||
uint idx = gid.y * w + gid.x;
|
||||
uint2 seed = seeds[idx];
|
||||
|
||||
if (seed.x == NO_SEED) {
|
||||
output[idx] = sqrt((float)(w * w + h * h)); // max possible distance
|
||||
} else {
|
||||
float dx = (float)gid.x - (float)seed.x;
|
||||
float dy = (float)gid.y - (float)seed.y;
|
||||
output[idx] = sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user