Source browser
examples/shaders/evt3_flow.cpp
Read the complete file below or download its original bytes. Line numbers provide stable links to a declaration or processing step.
File fingerprint
1// Estimate normal optical flow by fitting a local plane to event timestamps.
2#ifdef __cplusplus // Include the runtime only when compiling the CPU adapter.
3#include "shdr.h" // Share the portable resource and math declarations.
4#endif // Finish the C++ declaration include guard.
5layout(local_size_x = 64) in; // Assign one decoded event to each flow-estimation invocation.
6layout(std430, binding = 0) readonly buffer Events { // Access decoded records in their original event order.
7 uvec4 events[]; // Use decoded pixel or trigger records with paired timestamp words.
8}; // Finish this shader storage resource declaration.
9layout(std430, binding = 1) readonly buffer Offsets { // Read each block's exclusive output interval and the final total.
10 uint offsets[]; // Read exclusive event offsets and the final total event count.
11}; // Finish this shader storage resource declaration.
12layout(std430, binding = 2) writeonly buffer Flow { // Write one normal-flow result per decoded event.
13 vec4 flow[]; // Store normal velocity in pixels per second, neighbor count and validity.
14}; // Finish this shader storage resource declaration.
15uniform uint blocks; // Receive the number of summarized input blocks.
16uniform float radius; // Receive the spatial neighborhood radius in pixels.
17uniform uint history; // Receive the maximum preceding records to inspect, capped at 128.
18uniform uint time_window; // Receive the maximum neighbor age in microseconds.
19
20void main() { // Fit a local timestamp plane and publish its observable normal velocity.
21 uint index = gl_GlobalInvocationID.x; // Select the decoded event whose flow is being estimated.
22 if (index >= offsets[blocks]) // Ignore invocations beyond the decoder's final compact event count.
23 return; // Stop dispatch padding at the compact event-count boundary.
24 uvec4 center = events[index]; // Load the current event's pixel metadata and complete timestamp.
25 flow[index] = vec4(0.0); // Default to an invalid zero result until a usable plane is fitted.
26 // Fit spatial neighborhoods around decoded CD pixel events.
27 if ((center.y & 2147483648u) != 0u) // Recognize a trigger record before selecting a pixel neighborhood.
28 return; // Retain the initialized zero flow for trigger records.
29 float sx = 0.0, sy = 0.0, st = 0.0, sxx = 0.0, sxy = 0.0, syy = 0.0, sxt = 0.0, syt = 0.0; // Initialize first and second moments for a centered least-squares plane.
30 uint neighbors = 0u, lookback = min(history, 128u); // Count accepted neighbors and bound the per-event search cost.
31 ivec2 reference_offset = ivec2(0); // Remember the first nonzero integer pixel direction from the center.
32 bool spatial_rank_two = false; // Require two independent directions before fitting a two-dimensional plane.
33 for (uint step = 1u; step <= lookback && step <= index; ++step) { // Inspect only available preceding events within the bounded history.
34 uvec4 previous = events[index - step]; // Load one candidate neighbor from the decoded stream.
35 if ((previous.y & 2147483648u) != 0u || (previous.y & 6144u) != (center.y & 6144u)) // Reject triggers and events with a different polarity or camera.
36 continue; // Exclude this candidate while preserving the accumulated accepted neighbors.
37 // Subtract the timestamp pair exactly, including a low-word borrow.
38 // Only differences fitting uint32 and the requested window become float.
39 if (previous.w > center.w) // Reject timestamps newer than the center in their high word.
40 continue; // Exclude this candidate while preserving the accumulated accepted neighbors.
41 uint high = center.w - previous.w, borrow = center.z < previous.z ? 1u : 0u; // Subtract high words and determine the low-word borrow exactly.
42 if (high < borrow) // Reject a future timestamp whose low-word borrow would underflow.
43 continue; // Exclude this candidate while preserving the accumulated accepted neighbors.
44 high -= borrow; // Apply the borrow to the remaining high-word difference.
45 uint elapsed = center.z - previous.z; // Compute the low-word age with the defined unsigned wrapping subtraction.
46 if (high != 0u || elapsed > time_window) // Reject ages beyond uint32 or the requested temporal neighborhood.
47 continue; // Exclude this candidate while preserving the accumulated accepted neighbors.
48 ivec2 offset = ivec2(int(previous.x) - int(center.x), int(previous.y & 2047u) - int(center.y & 2047u)); // Subtract the decoded eleven-bit pixel coordinates exactly as signed integers.
49 float dx = float(offset.x), dy = float(offset.y); // Convert the small integer offsets for the floating-point moment calculation.
50 if (dx * dx + dy * dy > radius * radius) // Reject candidates outside the circular pixel neighborhood.
51 continue; // Exclude this candidate while preserving the accumulated accepted neighbors.
52 if (!spatial_rank_two) { // Stop testing directions once the neighborhood is known to span a plane.
53 if (reference_offset.x == 0 && reference_offset.y == 0) { // Find the first nonzero pixel direction from the center.
54 reference_offset = offset; // Keep this direction for comparison with later accepted neighbors.
55 } else if (offset.x * reference_offset.y != offset.y * reference_offset.x) { // Test independence with exact integer products bounded by eleven-bit coordinates.
56 spatial_rank_two = true; // Record exact two-dimensional spatial rank from the integer cross product.
57 } // Finish the exact spatial direction test.
58 } // Skip later direction tests once two independent offsets are found.
59 float dt = -float(elapsed); // Express an accepted earlier timestamp relative to the center in microseconds.
60 sx += dx; // Accumulate the horizontal first moment.
61 sy += dy; // Accumulate the vertical first moment.
62 st += dt; // Accumulate the relative timestamp first moment.
63 sxx += dx * dx; // Accumulate the horizontal squared moment.
64 sxy += dx * dy; // Accumulate the horizontal and vertical cross moment.
65 syy += dy * dy; // Accumulate the vertical squared moment.
66 sxt += dx * dt; // Accumulate the horizontal and timestamp cross moment.
67 syt += dy * dt; // Accumulate the vertical and timestamp cross moment.
68 ++neighbors; // Count this accepted spatial and temporal neighbor.
69 } // Finish this decoder or fitting branch.
70 if (neighbors < 6u || !spatial_rank_two) // Require six neighbors and exact two-dimensional spatial rank.
71 return; // Return the initialized zero result for insufficient or collinear neighbors.
72 // Include the center sample and eliminate the intercept with count-scaled moments.
73 // Subtract count-scaled integer moments before the final slope divisions.
74 // The common sample-count factors cancel in the slope solve.
75 float n = float(neighbors + 1u); // Include the center event at zero offset and zero relative time.
76 float xx = n * sxx - sx * sx, xy = n * sxy - sx * sy, yy = n * syy - sy * sy; // Form spatial covariances with the common sample-count scale.
77 float xt = n * sxt - sx * st, yt = n * syt - sy * st; // Form matching count-scaled space-time covariances.
78 float determinant = xx * yy - xy * xy; // Measure invertibility with both covariance factors scaled by the sample count.
79 if (determinant <= 0.0001 * n * n) // Preserve the original determinant threshold under the squared count scaling.
80 return; // Return the initialized zero result for degenerate spatial covariance.
81 float a = (xt * yy - yt * xy) / determinant, b = (yt * xx - xt * xy) / determinant; // Solve the horizontal and vertical timestamp slopes in microseconds per pixel.
82 float gradient = a * a + b * b; // Compute the squared timestamp-gradient magnitude.
83 if (gradient <= 0.000001) // Reject a plane with too little temporal slope to define finite velocity.
84 return; // Return the initialized zero result for a timestamp slope below the velocity threshold.
85 // Timestamps are microseconds, publish pixels/second, neighbor count, validity.
86 flow[index] = vec4(vec2(a, b) * (1000000.0 / gradient), float(neighbors), 1.0); // Convert the observable normal velocity to pixels per second and mark this fit valid.
87} // Finish this decoder or fitting branch.