-
Notifications
You must be signed in to change notification settings - Fork 244
LPs on Halide #1814
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
pareenaverma
merged 13 commits into
ArmDeveloperEcosystem:main
from
dawidborycki:LP-Halide
Sep 22, 2025
Merged
LPs on Halide #1814
Changes from 1 commit
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
ae6be9d
Halide: Intro
dawidborycki 030985b
Lesson 2
dawidborycki 6808521
Create fusion.md
dawidborycki 7148683
AOT
dawidborycki 06d4a2d
Android
dawidborycki dd0a3ba
Addressing comments
dawidborycki 0a9355e
Addressing comments
dawidborycki 043ced9
Addressing comments
dawidborycki 92924b5
2nd round
dawidborycki a0c4524
Updates
dawidborycki 49fcc35
Updates
dawidborycki 5bb5e67
Update _index.md
pareenaverma 6704def
Update _index.md
pareenaverma File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
Binary file added
BIN
+294 KB
content/learning-paths/mobile-graphics-and-gaming/android_halide/Figures/03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
170 changes: 170 additions & 0 deletions
170
...learning-paths/mobile-graphics-and-gaming/android_halide/processing-workflow.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,170 @@ | ||
| --- | ||
| # User change | ||
| title: "Building a Simple Camera Image Processing Workflow" | ||
|
|
||
| weight: 3 | ||
|
|
||
| layout: "learningpathall" | ||
| --- | ||
|
|
||
| ## Objective | ||
| In this section, we’ll build a real-time camera processing pipeline using Halide. We’ll start by capturing video frames from a webcam using OpenCV, then implement a Gaussian blur to smooth the captured images, followed by thresholding to create a clear binary output highlighting the most prominent image features. After establishing this pipeline, we’ll explore how to optimize performance further by applying Halide’s tiling strategy, a technique for enhancing cache efficiency and execution speed, particularly beneficial for high-resolution or real-time applications | ||
|
|
||
| ## Gaussian Blur And Thresholding | ||
| Create a new camera-capture.cpp file and modify it as follows: | ||
| ```cpp | ||
| #include "Halide.h" | ||
| #include <opencv2/opencv.hpp> | ||
| #include <iostream> | ||
| #include <exception> | ||
|
|
||
| using namespace cv; | ||
| using namespace std; | ||
|
|
||
| // This function clamps the coordinate (coord) within [0, maxCoord - 1]. | ||
| static inline Halide::Expr clampCoord(Halide::Expr coord, int maxCoord) { | ||
| return Halide::clamp(coord, 0, maxCoord - 1); | ||
| } | ||
|
|
||
| int main() { | ||
| // Open the default camera with OpenCV. | ||
| VideoCapture cap(0); | ||
| if (!cap.isOpened()) { | ||
| cerr << "Error: Unable to open camera." << endl; | ||
| return -1; | ||
| } | ||
|
|
||
| while (true) { | ||
| // Capture a frame from the camera. | ||
| Mat frame; | ||
| cap >> frame; | ||
| if (frame.empty()) { | ||
| cerr << "Error: Received empty frame." << endl; | ||
| break; | ||
| } | ||
|
|
||
| // Convert the frame to grayscale. | ||
| Mat gray; | ||
| cvtColor(frame, gray, COLOR_BGR2GRAY); | ||
dawidborycki marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| // Ensure the grayscale image is continuous in memory. | ||
| if (!gray.isContinuous()) { | ||
| gray = gray.clone(); | ||
| } | ||
|
|
||
| int width = gray.cols; | ||
| int height = gray.rows; | ||
|
|
||
| // Create a simple 2D Halide buffer from the grayscale Mat. | ||
| Halide::Buffer<uint8_t> inputBuffer(gray.data, width, height); | ||
|
|
||
| // Create a Halide ImageParam for a 2D UInt(8) image. | ||
| Halide::ImageParam input(Halide::UInt(8), 2, "input"); | ||
| input.set(inputBuffer); | ||
|
|
||
| // Define variables for x (width) and y (height). | ||
| Halide::Var x("x"), y("y"); | ||
dawidborycki marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // Define a function that applies a 3x3 Gaussian blur. | ||
| Halide::Func blur("blur"); | ||
| Halide::RDom r(0, 3, 0, 3); | ||
dawidborycki marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| // Kernel layout: [1 2 1; 2 4 2; 1 2 1], sum = 16. | ||
dawidborycki marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Halide::Expr weight = Halide::select( | ||
dawidborycki marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| (r.x == 1 && r.y == 1), 4, | ||
| (r.x == 1 || r.y == 1), 2, | ||
| 1 | ||
| ); | ||
|
|
||
| Halide::Expr offsetX = x + (r.x - 1); | ||
dawidborycki marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Halide::Expr offsetY = y + (r.y - 1); | ||
|
|
||
| // Manually clamp offsets to avoid out-of-bounds. | ||
dawidborycki marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Halide::Expr clampedX = clampCoord(offsetX, width); | ||
| Halide::Expr clampedY = clampCoord(offsetY, height); | ||
|
|
||
| // Accumulate weighted sum in 32-bit int before normalization. | ||
| Halide::Expr val = Halide::cast<int>(input(clampedX, clampedY)) * weight; | ||
dawidborycki marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| blur(x, y) = Halide::cast<uint8_t>(Halide::sum(val) / 16); | ||
|
|
||
| // Add a thresholding stage on top of the blurred result. | ||
| // If blur(x,y) > 128 => 255, else 0 | ||
| Halide::Func thresholded("thresholded"); | ||
| thresholded(x, y) = Halide::cast<uint8_t>( | ||
| Halide::select(blur(x, y) > 128, 255, 0) | ||
| ); | ||
|
|
||
| // Realize the thresholded function. Wrap in try-catch for error reporting. | ||
| Halide::Buffer<uint8_t> outputBuffer; | ||
| try { | ||
| outputBuffer = thresholded.realize({ width, height }); | ||
dawidborycki marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } catch (const std::exception &e) { | ||
| cerr << "Halide pipeline error: " << e.what() << endl; | ||
| break; | ||
| } | ||
|
|
||
| // Wrap the Halide output in an OpenCV Mat and display. | ||
| Mat blurredThresholded(height, width, CV_8UC1, outputBuffer.data()); | ||
|
|
||
| imshow("Processed image", blurredThresholded); | ||
|
|
||
| // Exit the loop if a key is pressed. | ||
| if (waitKey(30) >= 0) { | ||
dawidborycki marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| break; | ||
| } | ||
| } | ||
|
|
||
| cap.release(); | ||
| destroyAllWindows(); | ||
| return 0; | ||
| } | ||
| ``` | ||
|
|
||
| This code demonstrates a simple real-time image processing pipeline using Halide and OpenCV. Initially, it opens the computer’s default camera to continuously capture video frames. Each captured frame, originally in color, is converted into a single-channel grayscale image using OpenCV. | ||
|
|
||
| The grayscale image data is then passed to Halide via a buffer to perform computations. Within Halide, the program implements a Gaussian blur using a 3x3 convolution kernel with weights specifically chosen to smooth the image (weights: [1 2 1; 2 4 2; 1 2 1]). To safely handle pixels near image borders, the coordinates are manually clamped, ensuring all pixel accesses remain valid within the image dimensions. | ||
|
|
||
| After the Gaussian blur stage, a thresholding operation is applied. This step converts the blurred grayscale image into a binary image, assigning a value of 255 to pixels with intensity greater than 128 and 0 otherwise, thus highlighting prominent features against the background. | ||
|
|
||
| Finally, the processed image is returned from Halide to an OpenCV matrix and displayed in real-time. The loop continues until a key is pressed, providing a smooth, interactive demonstration of Halide’s ability to accelerate and streamline real-time image processing tasks. | ||
|
|
||
| ## Compilation instructions | ||
| Compile the program as follows (replace /path/to/halide accordingly): | ||
| ```console | ||
| g++ -std=c++17 camera-capture.cpp -o camera-capture \ | ||
| -I/path/to/halide/include -L/path/to/halide/lib -lHalide \ | ||
| $(pkg-config --cflags --libs opencv4) -lpthread -ldl \ | ||
| -Wl,-rpath,/path/to/halide/lib | ||
| ``` | ||
|
|
||
| Run the executable: | ||
| ```console | ||
| ./camera-capture | ||
| ``` | ||
|
|
||
| The output should look as in the figure below: | ||
|  | ||
|
|
||
| ## Tiling | ||
dawidborycki marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
dawidborycki marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Tiling is a powerful scheduling optimization provided by Halide, allowing image computations to be executed efficiently by dividing the workload into smaller, cache-friendly blocks called tiles. By processing these smaller regions individually, we significantly improve data locality, reduce memory bandwidth usage, and better leverage CPU caches, ultimately boosting performance for real-time applications. | ||
dawidborycki marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| We can easily extend our Gaussian blur and thresholding pipeline to leverage Halide’s built-in tiling capabilities. Let’s apply a simple tiling schedule to our existing pipeline. Replace the code segment immediately after defining the thresholded function with the following: | ||
|
|
||
| ```cpp | ||
| // Apply a simple tiling schedule | ||
| Halide::Var x_outer, y_outer, x_inner, y_inner; | ||
| thresholded.tile(x, y, x_outer, y_outer, x_inner, y_inner, 64, 64) | ||
| .parallel(y_outer); | ||
| ``` | ||
|
|
||
| The .tile(x, y, x_outer, y_outer, x_inner, y_inner, 64, 64) statement divides the image into 64×64 pixel tiles, significantly improving cache locality. While the parallel(y_outer) executes each horizontal row of tiles in parallel across available CPU cores, boosting execution speed. | ||
|
|
||
| Recompile your application as before, then run: | ||
| ```console | ||
| ./camera-capture | ||
| ``` | ||
|
|
||
| ## Summary | ||
| In this section, we built a complete real-time image processing pipeline using Halide and OpenCV. Initially, we captured live video frames and applied Gaussian blur and thresholding to highlight image features clearly. By incorporating Halide’s tiling optimization, we also improved performance by enhancing cache efficiency and parallelizing computation. Through these steps, we demonstrated Halide’s capability to provide both concise, clear code and high performance, making it an ideal framework for demanding real-time image processing tasks. | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.