How to compress YUYV raw data to JPEG using libjpeg?

vdm picture vdm · May 6, 2013 · Viewed 8.7k times · Source

I'm looking for an example of how to save a YUYV format frame to a JPEG file using the libjpeg library.

Answer

Jon Watte picture Jon Watte · Jan 6, 2014

In typical computer APIs, "YUV" actually means YCbCr, and "YUYV" means "YCbCr 4:2:2" stored as Y0, Cb01, Y1, Cr01, Y2 ...

Thus, if you have a "YUV" image, you can save it to libjpeg using the JCS_YCbCr color space.

When you have a 422 image (YUYV) you have to duplicate the Cb/Cr values to the two pixels that need them before writing the scanline to libjpeg. Thus, this write loop will do it for you:

// "base" is an unsigned char const * with the YUYV data
// jrow is a libjpeg row of samples array of 1 row pointer
cinfo.image_width = width & -1; 
cinfo.image_height = height & -1; 
cinfo.input_components = 3; 
cinfo.in_color_space = JCS_YCbCr; 
jpeg_set_defaults(&cinfo); 
jpeg_set_quality(&cinfo, 92, TRUE); 
jpeg_start_compress(&cinfo, TRUE); 
unsigned char *buf = new unsigned char[width * 3]; 
while (cinfo.next_scanline < height) { 
    for (int i = 0; i < cinfo.image_width; i += 2) { 
        buf[i*3] = base[i*2]; 
        buf[i*3+1] = base[i*2+1]; 
        buf[i*3+2] = base[i*2+3]; 
        buf[i*3+3] = base[i*2+2]; 
        buf[i*3+4] = base[i*2+1]; 
        buf[i*3+5] = base[i*2+3]; 
    } 
    jrow[0] = buf; 
    base += width * 2; 
    jpeg_write_scanlines(&cinfo, jrow, 1); 
}
jpeg_finish_compress(&cinfo);
delete[] buf;

Use your favorite auto-ptr to avoid leaking "buf" if your error or write function can throw / longjmp.

Providing YCbCr to libjpeg directly is preferrable to converting to RGB, because it will store it directly in that format, thus saving a lot of conversion work. When the image comes from a webcam or other video source, it's also usually most efficient to get it in YCbCr of some sort (such as YUYV.)

Finally, "U" and "V" mean something slightly different in analog component video, so the naming of YUV in computer APIs that really mean YCbCr is highly confusing.