Libavif Chroma Subsampling: 4:4:4, 4:2:2, and 4:2:0

This article explains how the libavif library manages chroma subsampling formats, specifically 4:4:4, 4:2:2, and 4:2:0, during the image encoding and decoding processes. It covers how libavif maps RGB data to different YUV configurations, the downsampling and upsampling algorithms utilized to preserve image quality, and how developers can configure these formats within the API to balance file size and visual fidelity.

Understanding Chroma Subsampling in AVIF

Chroma subsampling is a compression technique that reduces color resolution (chrominance) while preserving brightness resolution (luminance). Because the human eye is more sensitive to variations in light than color, this technique significantly reduces file sizes with minimal perceived loss in quality.

The AV1 video codec, which underlies the AVIF image format, natively supports several YUV color spaces. The libavif library handles these spaces using specific pixel format identifiers:


How libavif Manages the Encoding Process (RGB to YUV)

To encode an input image (typically in RGB format) into an AVIF file, libavif must convert the RGB pixels into YUV. This process is handled via the avifImageRGBToYUV() function.

During this conversion, libavif manages subsampling through the following mechanisms:

1. Format Selection

The developer specifies the target subsampling format by setting the yuvFormat member of the avifImage struct to one of the library’s predefined enums (e.g., AVIF_PIXEL_FORMAT_YUV420).

2. Downsampling Filters

When converting to 4:2:2 or 4:2:0, libavif must discard chroma pixels. To do this without introducing aliasing or color artifacts, it uses downsampling filters. * Standard Bilinear/Box Filtering: By default, average neighboring pixels are calculated to create the lower-resolution chroma maps. * Sharp YUV (libsharpyuv): libavif integrates Google’s Sharp YUV algorithm (originally developed for WebP). Sharp YUV analyzes the luminance channel to adjust the downsampled chroma values, drastically reducing color bleeding and keeping sharp text boundaries legible even at 4:2:0 subsampling.


How libavif Manages the Decoding Process (YUV to RGB)

When opening and rendering an AVIF image, libavif reverses the process by decoding the compressed AV1 bitstream back into YUV, and then converting YUV back to RGB using avifImageYUVToRGB().

For subsampled formats like 4:2:2 and 4:2:0, the chroma channels must be upscaled (upsampled) to match the resolution of the luma channel before RGB conversion can take place.

1. Upsampling Algorithms

To reconstruct the missing color data, libavif utilizes upsampling filters: * Nearest Neighbor: Fast but can result in blocky, pixelated color edges. * Bilinear Filtering: Blends adjacent chroma pixels to create a smoother, more natural transition. This is the default behavior for most decoding paths to ensure high visual quality.

2. Chrominance Positioning (Chroma Siting)

Different video standards define where subsampled chroma samples sit relative to luma samples. libavif reads the chroma sample position metadata (vertical/horizontal offsets) from the AV1 sequence header. It respects configurations like “Center” or “Left” (colocated) to ensure that colors align perfectly with their corresponding brightness boundaries during reconstruction, avoiding alignment offsets or “color shifting.”


API Implementation and Range Configurations

Developers interacting with the libavif API control these behaviors through two main structures: avifImage and avifRGBImage.

// Example configuration for encoding 4:2:0
avifImage * image = avifImageCreate(width, height, depth, AVIF_PIXEL_FORMAT_YUV420);
image->yuvRange = AVIF_RANGE_FULL; // Or AVIF_RANGE_LIMITED