Memory-friendly monochrome WebP encoding

Hi, just a quick thing that may be useful if you’re trying to save memory while using libwebp to encode a monochrome image.

We assume that you already have the monochrome data, and you just want to encode it. Your data size is width*height*1, and is 100% equivalent to the Y channel in YUV.

Allocating a bunch of memory (width*height/2) for the UV part would be silly, but looks like it’s required, right?

Well, we can actually get away with just width/2, by doing this:

    picture.y = y_data; // straightforward
    picture.y_stride = width; // straightforward
    picture.u = dummy_uv_data; // array of length width/2 full of 0x80 bytes
    picture.v = dummy_uv_data; // it's the same array!
    picture.uv_stride = 0; // stride is 0! this means we'll always read UV from the same location for every single pixel row

BTW, you can generate your y_data with the following ImageMagick command:

convert test.jpg y:test.y

Below is the full code demonstrating the use of this trick. Note that the code is about 90% generated by ChatGPT, and I haven’t cleaned it up beyond the minimum necessary to get it to work. Don’t forget to adjust the width and height variables to match your input image.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <webp/encode.h>

int main() {
    int width = 3024;
    int height = 4032;
    int stride = width;
    uint8_t *y_data = malloc(width*height);
    uint8_t *dummy_uv_data = malloc(width/2);
    memset(dummy_uv_data, 0x80, width/2);

    FILE *infile = fopen("test.y", "r");
    if (!infile) {
        fprintf(stderr, "Error opening output file.\n");
        return 1;
    }
    fread(y_data, width*height, 1, infile);
    fclose(infile);

    FILE* outfile = fopen("output.webp", "wb");
    if (!outfile) {
        fprintf(stderr, "Error opening output file.\n");
        return 1;
    }

    WebPConfig config;
    WebPPicture picture;
    WebPMemoryWriter writer;

    WebPConfigInit(&config);
    config.lossless = 0;  // Set to 1 for lossless encoding
    config.quality = 75;  // Set the desired quality value (0-100)

    WebPMemoryWriterInit(&writer);

    if (!WebPPictureInit(&picture)) {
        fprintf(stderr, "Error initializing WebP structures.\n");
        return 1;
    }

    picture.width = width;
    picture.height = height;
    picture.use_argb = 0;
    picture.y = y_data;
    picture.u = dummy_uv_data;
    picture.v = dummy_uv_data;
    picture.y_stride = width;
    picture.uv_stride = 0;
    picture.writer = WebPMemoryWrite;
    picture.custom_ptr = &writer;

    if (!WebPEncode(&config, &picture)) {
        fprintf(stderr, "Error encoding WebP.\n");
        return 1;
    }

    fwrite(writer.mem, 1, writer.size, outfile);

    free(y_data);
    WebPMemoryWriterClear(&writer);
    WebPPictureFree(&picture);
    fclose(outfile);

    return 0;
}

Compile with: cc -o encode_webp encode_webp.c -lwebp

Execute with: ./encode_webp

Leave a Reply

Your email address will not be published. Required fields are marked *