Source browser
examples/vision.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#include "runner.hpp" // Include runner.hpp declarations.
2#include <vector> // Include vector declarations.
3
4namespace { // Group these helpers within their namespace.
5
6/// Index a row-major image with the same explicit clamp-to-edge border convention.
7std::size_t pixel(int x, int y, unsigned width, unsigned height) { // Implement pixel for the operations below.
8 const unsigned cx = unsigned(std::max(0, std::min(int(width) - 1, x))); // Set cx from this expression.
9 const unsigned cy = unsigned(std::max(0, std::min(int(height) - 1, y))); // Set cy from this expression.
10 return std::size_t(cy) * width + cx; // Return std::size t(cy) * width + cx.
11} // End this block.
12
13/// Independent reference: apply the full 25-tap Gaussian convolution directly.
14std::vector<float> // Continue the expression using std, vector, float.
15gaussian_reference(const shdr::buffer<float>& input, unsigned width, unsigned height) { // Apply gaussian reference to these arguments.
16 const int weights[5] = {1, 4, 6, 4, 1}; // Set weights[5] from this expression.
17 std::vector<float> result(std::size_t(width) * height); // Construct result with these initial values.
18 const float* source = input.data(); // Set source from this expression.
19 for (unsigned y = 0; y < height; ++y) // Iterate over y for this calculation.
20 for (unsigned x = 0; x < width; ++x) { // Iterate over x for this calculation.
21 double sum = 0.0; // Use this floating-point value or resource in the following calculation.
22 for (int dy = -2; dy <= 2; ++dy) // Iterate over dy for this calculation.
23 for (int dx = -2; dx <= 2; ++dx) // Iterate over dx for this calculation.
24 sum += double(source[pixel(int(x) + dx, int(y) + dy, width, height)]) * // Update sum with this contribution.
25 weights[dx + 2] * weights[dy + 2]; // Continue the expression using weights, dx, weights.
26 result[std::size_t(y) * width + x] = float(sum / 256.0); // Set result[std::size t(y) * width + x] from this expression.
27 } // End this block.
28 return result; // Return result.
29} // End this block.
30
31/// Apply the two Sobel coefficient tables independently of the shader's expanded formulas.
32std::vector<glsl::vec2> // Continue the expression using std, vector, glsl.
33gradient_reference(const std::vector<float>& input, unsigned width, unsigned height) { // Apply gradient reference to these arguments.
34 const int sx[3][3] = {{-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1}}; // Set sx[3][3] from this expression.
35 const int sy[3][3] = {{-1, -2, -1}, {0, 0, 0}, {1, 2, 1}}; // Set sy[3][3] from this expression.
36 std::vector<glsl::vec2> result(std::size_t(width) * height); // Construct result with these initial values.
37 for (unsigned y = 0; y < height; ++y) // Iterate over y for this calculation.
38 for (unsigned x = 0; x < width; ++x) { // Iterate over x for this calculation.
39 double gx = 0.0, gy = 0.0; // Use this floating-point value or resource in the following calculation.
40 for (int dy = -1; dy <= 1; ++dy) // Iterate over dy for this calculation.
41 for (int dx = -1; dx <= 1; ++dx) { // Iterate over dx for this calculation.
42 const double value = input[pixel(int(x) + dx, int(y) + dy, width, height)]; // Set value from this expression.
43 gx += value * sx[dy + 1][dx + 1]; // Update gx with this contribution.
44 gy += value * sy[dy + 1][dx + 1]; // Update gy with this contribution.
45 } // End this block.
46 result[std::size_t(y) * width + x] = glsl::vec2(float(gx / 8.0), float(gy / 8.0)); // Set result[std::size t(y) * width + x] from this expression.
47 } // End this block.
48 return result; // Return result.
49} // End this block.
50
51/// Form and smooth a symmetric 2x2 tensor, then use det(M)-k*trace(M)^2.
52std::vector<float> harris_reference(const std::vector<glsl::vec2>& gradients, // Declare harris reference with the parameters continued below.
53 unsigned width, // Declare width for the following calculation.
54 unsigned height, // Declare height for the following calculation.
55 float k) { // Declare k for the following calculation.
56 const int weights[3] = {1, 2, 1}; // Set weights[3] from this expression.
57 std::vector<float> result(std::size_t(width) * height); // Construct result with these initial values.
58 for (unsigned y = 0; y < height; ++y) // Iterate over y for this calculation.
59 for (unsigned x = 0; x < width; ++x) { // Iterate over x for this calculation.
60 double xx = 0.0, xy = 0.0, yy = 0.0; // Use this floating-point value or resource in the following calculation.
61 for (int dy = -1; dy <= 1; ++dy) // Iterate over dy for this calculation.
62 for (int dx = -1; dx <= 1; ++dx) { // Iterate over dx for this calculation.
63 const glsl::vec2& g = gradients[pixel(int(x) + dx, int(y) + dy, width, height)]; // Set g from this expression.
64 const double weight = double(weights[dx + 1] * weights[dy + 1]) / 16.0; // Set weight from this expression.
65 xx += double(g.x) * g.x * weight; // Update xx with this contribution.
66 xy += double(g.x) * g.y * weight; // Update xy with this contribution.
67 yy += double(g.y) * g.y * weight; // Update yy with this contribution.
68 } // End this block.
69 result[std::size_t(y) * width + x] = // Set result[std::size t(y) * width + x] from this expression.
70 float(xx * yy - xy * xy - double(k) * (xx + yy) * (xx + yy)); // Apply float to these arguments.
71 } // End this block.
72 return result; // Return result.
73} // End this block.
74
75/// Bind dimensions shared by the independently dispatched image-processing kernels.
76void dimensions(shdr::kernel& kernel, unsigned width, unsigned height) { // Implement dimensions for the operations below.
77 kernel.set("width", width).set("height", height); // Apply set to these arguments.
78} // End this block.
79
80} // namespace
81
82/// Gaussian smoothing -> Sobel -> structure tensor -> Harris -> nonmaximum suppression.
83int main(int argc, char** argv) { // Run this example and validate its results.
84 try { // Run the operation while retaining the error handling below.
85 shdr_examples::runner run(argc, argv); // Construct run with these initial values.
86 const unsigned width = 32, height = 32, count = width * height; // Set width from this expression.
87 const float harris_k = 0.04f; // Set harris k from this expression.
88 shdr::buffer<float> input(count), horizontal(count), blurred(count), response(count); // Construct input with these initial values.
89 shdr::buffer<glsl::vec2> gradients(count); // Construct gradients with these initial values.
90 shdr::buffer<glsl::vec4> tensors(count); // Construct tensors with these initial values.
91 shdr::buffer<unsigned> corners(count); // Construct corners with these initial values.
92 for (unsigned y = 0; y < height; ++y) // Iterate over y for this calculation.
93 for (unsigned x = 0; x < width; ++x) // Iterate over x for this calculation.
94 input[std::size_t(y) * width + x] = // Set input[std::size t(y) * width + x] from this expression.
95 x >= 8 && x < 24 && y >= 8 && y < 24 ? 1.0f : 0.0f; // Continue the expression using x, x, y.
96
97 auto gaussian_x = shdr::make_kernel("gaussian_horizontal_kernel"); // Create the linked gaussian horizontal kernel instance.
98 auto gaussian_y = shdr::make_kernel("gaussian_vertical_kernel"); // Create the linked gaussian vertical kernel instance.
99 auto sobel = shdr::make_kernel("sobel_kernel"); // Create the linked sobel kernel instance.
100 auto tensor = shdr::make_kernel("harris_tensor_kernel"); // Create the linked harris tensor kernel instance.
101 auto harris = shdr::make_kernel("harris_response_kernel"); // Create the linked harris response kernel instance.
102 auto nonmax = shdr::make_kernel("nonmax_kernel"); // Create the linked nonmax kernel instance.
103 gaussian_x->bind("input_values", input).bind("output_values", horizontal); // Connect this shader port to its application resource.
104 gaussian_y->bind("input_values", horizontal).bind("output_values", blurred); // Connect this shader port to its application resource.
105 sobel->bind("input_values", blurred).bind("output_values", gradients); // Connect this shader port to its application resource.
106 tensor->bind("input_values", gradients).bind("output_values", tensors).set("count", count); // Connect this shader port to its application resource.
107 harris->bind("input_values", tensors) // Connect this shader port to its application resource.
108 .bind("output_values", response) // Connect this shader port to its application resource.
109 .set("harris_k", harris_k); // Apply set to these arguments.
110 nonmax->bind("input_values", response) // Connect this shader port to its application resource.
111 .bind("output_values", corners) // Connect this shader port to its application resource.
112 .set("threshold", 0.00001f); // Apply set to these arguments.
113 dimensions(*gaussian_x, width, height); // Apply dimensions to these arguments.
114 dimensions(*gaussian_y, width, height); // Apply dimensions to these arguments.
115 dimensions(*sobel, width, height); // Apply dimensions to these arguments.
116 dimensions(*harris, width, height); // Apply dimensions to these arguments.
117 dimensions(*nonmax, width, height); // Apply dimensions to these arguments.
118
119 const shdr::extent groups((count + 63) / 64); // Construct groups with these initial values.
120 auto pipeline = shdr::dispatch(*gaussian_x, groups) >> // Add a shader dispatch with the required workgroup counts.
121 shdr::dispatch(*gaussian_y, groups) >> shdr::dispatch(*sobel, groups) >> // Add a shader dispatch with the required workgroup counts.
122 shdr::dispatch(*tensor, groups) >> shdr::dispatch(*harris, groups) >> // Add a shader dispatch with the required workgroup counts.
123 shdr::dispatch(*nonmax, groups); // Add a shader dispatch with the required workgroup counts.
124 run.run(pipeline); // Execute the configured pipeline on the requested backend.
125
126 const std::vector<float> expected_blur = gaussian_reference(input, width, height); // Set expected blur from this expression.
127 const std::vector<glsl::vec2> expected_gradients = // Set expected gradients from this expression.
128 gradient_reference(expected_blur, width, height); // Apply gradient reference to these arguments.
129 const std::vector<float> expected_response = // Set expected response from this expression.
130 harris_reference(expected_gradients, width, height, harris_k); // Apply harris reference to these arguments.
131 const shdr::buffer<float>& read_blur = blurred; // Use a constant buffer view so GPU writes synchronize before host reads.
132 const shdr::buffer<glsl::vec2>& read_gradients = gradients; // Use a constant buffer view so GPU writes synchronize before host reads.
133 const shdr::buffer<float>& read_response = response; // Use a constant buffer view so GPU writes synchronize before host reads.
134 const shdr::buffer<unsigned>& read_corners = corners; // Use a constant buffer view so GPU writes synchronize before host reads.
135 const float* blur_data = read_blur.data(); // Set blur data from this expression.
136 const glsl::vec2* gradient_data = read_gradients.data(); // Set gradient data from this expression.
137 const float* response_data = read_response.data(); // Set response data from this expression.
138 const unsigned* corner_data = read_corners.data(); // Set corner data from this expression.
139 unsigned corner_count = 0; // Set corner count from this expression.
140 for (unsigned y = 0; y < height; ++y) // Iterate over y for this calculation.
141 for (unsigned x = 0; x < width; ++x) { // Iterate over x for this calculation.
142 const unsigned i = y * width + x; // Set i from this expression.
143 shdr_examples::require_close( // Check the computed value against the independent reference.
144 blur_data[i], expected_blur[i], "Gaussian blur", 0.000001f); // Continue the expression using blur data, i, expected blur.
145 shdr_examples::require_close( // Check the computed value against the independent reference.
146 gradient_data[i].x, expected_gradients[i].x, "Sobel dx", 0.000001f); // Continue the expression using gradient data, i, x.
147 shdr_examples::require_close( // Check the computed value against the independent reference.
148 gradient_data[i].y, expected_gradients[i].y, "Sobel dy", 0.000001f); // Continue the expression using gradient data, i, y.
149 shdr_examples::require_close( // Check the computed value against the independent reference.
150 response_data[i], expected_response[i], "Harris response", 0.0000001f); // Continue the expression using response data, i, expected response.
151 // This smoothed square has one stable maximum one pixel inside each corner.
152 const unsigned expected_corner = // Set expected corner from this expression.
153 (x == 9 || x == 22) && (y == 9 || y == 22) ? 1u : 0u; // Continue the expression using x, x, y.
154 if (corner_data[i] != expected_corner) // Check this condition before entering the following branch.
155 throw std::runtime_error( // Report this failed operation.
156 "nonmaximum suppression produced an unexpected corner at " + // Provide this text argument for the operation above.
157 std::to_string(x) + "," + std::to_string(y)); // Apply to string to these arguments.
158 corner_count += corner_data[i]; // Update corner count with this contribution.
159 } // End this block.
160 std::cout << (run.gpu() ? "GPU" : "CPU") << " vision pipeline: " << corner_count // Write this part of the example result or diagnostic.
161 << " Harris corners verified\n"; // Write this part of the example result or diagnostic.
162 return 0; // Report successful completion.
163#ifdef SHDR_HAVE_EGL // Enable this branch when its feature macro is defined.
164 } catch (const shdr_examples::context_unavailable& error) { // Report the exception from the preceding operation.
165 std::cerr << "SKIP: " << error.what() << "\n"; // Write this part of the example result or diagnostic.
166 return 77; // Report an unavailable optional GPU backend.
167#endif // Finish this conditional compilation branch.
168 } catch (const std::exception& error) { // Report the exception from the preceding operation.
169 std::cerr << error.what() << "\n"; // Write this part of the example result or diagnostic.
170 return 1; // Report a failed check or operation.
171 } // End this block.
172} // End this block.