Following on from my previous post covering decoding and rendering HDMI input on the RK3588, I encountered a new requirement: implementing a real-time vectorscope to visualize chrominance information from the video stream. This proved to be a challenging task due to the need for efficient frame processing and rendering without impacting video playback performance.
Extracting UV Data from Video Frames
The challenges to overcome were:
- Accessing U and V (chrominance) values for every pixel in the video stream.
- For RGB-formatted frames, a costly color space conversion (RGB to YUV) is required.
- The processing overhead increases significantly with higher-resolution frames.
To minimize CPU overhead and offload the color space conversion, I utilized RGA3, which efficiently converts each RGB frame into NV12 or NV16 format. This step significantly reduces the processing time needed to access UV data.
Once converted, the UV plane needed to be imported into an OpenGL ES texture for further processing and visualization. To preserve performance and avoid unnecessary memory copies, the goal was to directly bind the UV data as a texture.
Computing the UV Histogram
The primary processing here
-
Computing a UV histogram in real time based on pixel data from the video frame.
-
Normalizing the histogram values so it scales properly regardless of resolution or luminance.
Building a real-time UV histogram from each frame requires scanning and processing a large volume of pixel data efficiently. Traditional OpenGL fragment shaders are not well-suited for this type of arbitrary data accumulation, especially when dealing with high-resolution video.
Given that the Mali G-610 GPU is compliant with OpenGL ES 3.1, I explored the use of compute shaders, a more flexible approach for performing general-purpose GPU (GPGPU) operations like histogram generation.
Compute shaders for OpenGL ES is sparsely documented, and practical usage examples, especially for embedded platforms are limited. As a result, much of the development involved experimentation and iterative debugging, making it feel like a bit of a black art. Finally I managed to chain to together 3 compute shaders each perform one step in the pipeline.
Rendering the Vectorscope Output
The final step involved visualizing the processed chroma data in a way that mirrors a traditional vectorscope. This required overlaying the normalized UV histogram along with reference markers without disrupting video playback.
I drew inspiration from the OBS monitor plugin, which includes a vectorscope feature. Its rendering approach informed how I structured the visualization pipeline, particularly around how histogram data is mapped to screen coordinates.
The demo video shows the Vectorscope is capable of processing a 1080p@60 video stream.
Work was carried out on a ROCK 5B board running a tailored Ubuntu image maintained by Joshua Riek.