Extracting Color Space from Decoded Images in libavif

This article explains how to retrieve color space properties, such as CICP (Color Primaries, Transfer Characteristics, and Matrix Coefficients) and ICC profiles, from an AVIF image using the libavif C library. It covers the specific fields within the avifImage structure populated during decoding and the helper functions available to interpret this metadata.

In libavif, image decoding is handled by the avifDecoder object. Once an image is parsed and decoded successfully using avifDecoderParse() and avifDecoderNextImage(), the decoded image metadata and pixel data are stored in an avifImage structure (accessible via decoder->image).

Because libavif is a C library, color space properties are exposed directly as public fields within the avifImage struct, supplemented by utility functions to convert these properties into human-readable formats.


1. Extracting CICP (Color Parameter) Values

CICP values define the color properties of the image. You can extract these values directly from the fields of the decoded avifImage structure:

To convert these enum values into human-readable string representations, libavif provides the following helper functions:

const char * avifColorPrimariesToString(avifColorPrimaries val);
const char * avifTransferCharacteristicsToString(avifTransferCharacteristics val);
const char * avifMatrixCoefficientsToString(avifMatrixCoefficients val);

2. Extracting ICC Profiles

If the AVIF image contains an embedded ICC profile, it is stored in the icc field of the avifImage structure. This field is of type avifRWData, which holds raw binary data.

If image->icc.size is greater than zero, an ICC profile is present and can be extracted or copied directly from the memory buffer.


Example Implementation

The following C code demonstrates how to decode an AVIF file and extract its color space properties using the fields and utility functions described above:

#include "avif/avif.h"
#include <stdio.h>

void print_color_space_properties(const char* filename) {
    avifDecoder * decoder = avifDecoderCreate();
    
    // Read and parse the AVIF file
    avifResult result = avifDecoderSetIOFile(decoder, filename);
    if (result != AVIF_RESULT_OK) {
        printf("Failed to open file: %s\n", avifResultToString(result));
        avifDecoderDestroy(decoder);
        return;
    }

    result = avifDecoderParse(decoder);
    if (result != AVIF_RESULT_OK) {
        printf("Failed to parse AVIF: %s\n", avifResultToString(result));
        avifDecoderDestroy(decoder);
        return;
    }

    result = avifDecoderNextImage(decoder);
    if (result != AVIF_RESULT_OK) {
        printf("Failed to decode image: %s\n", avifResultToString(result));
        avifDecoderDestroy(decoder);
        return;
    }

    // Access the decoded image container
    avifImage * image = decoder->image;

    // 1. Extract CICP properties using helper functions
    const char* primaries = avifColorPrimariesToString(image->colorPrimaries);
    const char* transfer = avifTransferCharacteristicsToString(image->transferCharacteristics);
    const char* matrix = avifMatrixCoefficientsToString(image->matrixCoefficients);

    printf("Color Primaries: %s (%d)\n", primaries, image->colorPrimaries);
    printf("Transfer Characteristics: %s (%d)\n", transfer, image->transferCharacteristics);
    printf("Matrix Coefficients: %s (%d)\n", matrix, image->matrixCoefficients);

    // 2. Extract ICC Profile
    if (image->icc.size > 0) {
        printf("ICC Profile found: %zu bytes\n", image->icc.size);
        // Process raw ICC bytes at image->icc.data here if needed
    } else {
        printf("No ICC profile embedded in the image.\n");
    }

    avifDecoderDestroy(decoder);
}