YUV420 stored as BMP and JPG images

YUV420 is stored as BMP and JPG images [personal test and valid] Most of the information about YUV420 on the Internet is about YUV420P, and there is very little about YUV420SP, because the UVs of YUV420SP are staggered, which is relatively troublesome to process, but YUV420SP is also a A common format, so here, I will summarize the processing of YUV420SP format data for the convenience of comrades in need. 1. Introduction to YUV420 format data YUV is divided into three components. “Y” represents brightness, which is the gray value; “U” and “V” represent chroma, which is used to describe shadows.

Most of the information about YUV420 on the Internet is about YUV420P, and there is very little about YUV420SP. Because the UVs of YUV420SP are stored in staggered ways, it is relatively troublesome to process. However, YUV420SP is also a common format, so here, I will summarize the processing of YUV420SP format data for the convenience of comrades in need.

Benefit of this article, you can receive free C++ audio and video learning materials package, technical video/code, including (audio and video development, interview questions, FFmpeg, webRTC, rtmp, hls, rtsp, ffplay, codec, push-pull streaming, srs) ↓↓↓↓↓↓See below↓↓Click at the bottom of the article to get it for free↓↓

1. Introduction to YUV420 format data

YUV is divided into three components. “Y” represents brightness, which is the gray value; “U” and “V” represent chroma, which is used to describe the color saturation of the image and is used to specify the color of the pixel. There are three mainstream YUV sampling methods: YUV4:4:4, YUV4:2:2, YUV4:2:0. Here we mainly introduce YUV420.

In YUV420, a pixel corresponds to a Y, and a 2X2 small square corresponds to a U and V. For all YUV420 images, their Y value arrangement is exactly the same, because the image with only Y is a grayscale image. YUV420 is divided into two types: YUV420SP and YUV420P. The Y distribution of these two formats is the same. The difference is UV: YUV420p stores U first and then V, which means that UV is continuous; and YUV420sp is stored alternately with UV and UV. (Their memory distribution diagram is as follows, the left is YUV420sp, the right is YUV420p)

2. Method of getting data group from YUV memory

Note that YUV corresponds to one UV for every four Y, and the UV storage formats of YUV420P and YUV420SP are different, and the acquisition methods are also different. Generally speaking, the acquisition method of YUV420P is simple, and the acquisition method of YUV420SP is relatively complicated.

1. YUV420SP

for(int j=0;j<DataHeight;j + + )
{
    for(int i=0;i<DataWidth;i + + )
    {
        y=ybase[i + j * DataWidth]; // Every four y corresponds to a uv
        u=ubase[j/2 * DataWidth + (i/2)*2];
        v=ubase[j/2 * DataWidth + (i/2)*2 + 1]; //Be sure to note that it is u + 1
    }
} 

2. YUV420P

for(int j=0;j<DataHeight;j + + )
{
    for(int i=0;i<DataWidth;i + + )
    {
        //yyyyyy...uuuu...vvv
        y=ybase[i + j * DataWidth];
        u=ubase[j/2 * DataWidth/2 + (i/2)];
        v=vbase[j/2 * DataWidth/2 + (i/2)];
    }
} 

In the above code, ybase is the starting address of Y in YUV, ubase is the starting address of u, and vbase is the starting address of v. In the YUV420SP format, V is the address of U plus one; in YUV420P, both U and V are consecutive. According to the above method, we can get each set of YUV data, and then we can save each set of data ourselves and then process it.

3. Convert YUV420 to RGB data

1. Conversion formula

R=Y + 1.4075*(V-128)
G=Y-0.3455*(U-128) – 0.7169*(V-128)
B=Y + 1.779*(U-128)

2. Conversion method

YUV420SP

for(int j=0;j<DataHeight;j + + )
{
    for(int i=0;i<DataWidth;i + + )
    {
        unsigned char r,g,b;
        y=ybase[i + j * DataWidth];
        u=ubase[j/2 * DataWidth + (i/2)*2];
        v=ubase[j/2 * DataWidth + (i/2)*2 + 1];

        b=(unsigned char)(y + 1.779*(u- 128));
        g=(unsigned char)(y-0.7169*(v - 128)-0.3455*(u - 128));
        r=(unsigned char)(y + 1.4075*(v - 128));*/
    }
} 

YUV420P

for(int j=0;j<DataHeight;j + + )
{
    for(int i=0;i<DataWidth;i + + )
    {
        unsigned char r,g,b;
      
        y=ybase[i + j * DataWidth];
        u=ubase[j/2 * DataWidth/2 + (i/2)];
        v=vbase[j/2 * DataWidth/2 + (i/2)];

        b=(unsigned char)(y + 1.779*(u- 128));
        g=(unsigned char)(y-0.7169*(v - 128)-0.3455*(u - 128));
        r=(unsigned char)(y + 1.4075*(v - 128));*/
    }
} 

4. RGB data is stored as image

Note the difference between when rgb data is stored as bmp and jpg. When RBG data is stored as bmp, the data is stored in reverse order, and it is not rgb, but bgr. When rgb data is stored as jpg, it is not used. There is no need to reverse the data, and the data is also Or rgb.

1. Store as BMP image

Please see another blog, RGB TO BMP

2. Save as JPG image

To store JPG images, you need to use a shipping library, libjpeg, or libjpeg-turbo. I use libjpeg. There is a lot of information about these two open source libraries on the Internet. You can download the compiled package from here, the LIBJPEG package.

The encapsulated storage method is as follows:

int rgb2jpeg(const char * filename, unsigned char* rgbData,int image_width,int image_height,int quality)
{
    struct jpeg_compress_struct jpeg; //identify a compress object
    struct jpeg_error_mgr jerr; //error information

    jpeg.err = jpeg_std_error( & amp;jerr);
    jpeg_create_compress( & amp;jpeg); //init compress object

    FILE* pFile;
    fopen_s( & amp;pFile,filename,"wb" );
    if( !pFile ) return 0;
    jpeg_stdio_dest( & amp;jpeg, pFile);

    //compress param set,i just did a simple param set
    jpeg.client_data=(void*) & amp;pFile;
    jpeg.image_width = image_width;
    jpeg.image_height = image_height;
    jpeg.input_components = 3;
    jpeg.in_color_space = JCS_RGB;
    jpeg_set_defaults( & amp;jpeg);
     Specify luminance and chroma quality
    jpeg.q_scale_factor[0] = jpeg_quality_scaling(100);
    jpeg.q_scale_factor[1] = jpeg_quality_scaling(100);
     Image sampling rate, default is 2 * 2
    jpeg.comp_info[0].v_samp_factor = 2;
    jpeg.comp_info[0].h_samp_factor = 2;
     set jpeg compress quality
    jpeg_set_quality( & amp;jpeg, quality, TRUE); //100 is the highest

    //start compress
    jpeg_start_compress( & amp;jpeg, TRUE);

    JSAMPROW row_pointer[1];

    //from up to down, set every pixel
    for( unsigned int i=0;i<jpeg.image_height;i + + )
    {
        row_pointer[0] = rgbData + i*jpeg.image_width*3;
        jpeg_write_scanlines( & amp;jpeg,row_pointer,1 );
    }
    //stop compress
    jpeg_finish_compress( & amp;jpeg);

    fclose( pFile );
    pFile = NULL;
    jpeg_destroy_compress( & amp;jpeg);
    return 0;
} 

5. YUV data is stored as JPG

There are a lot of codes and blogs on the Internet about storing YUV420 data as JPG, but when I use their code, it always fails. Either it cannot run or the effect is not good, but I still express my gratitude.

1. YUV420SP

int yuv420p_to_jpeg(const char * filename, const char* pdata,int image_width,int image_height, int quality)
{
    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr jerr;
    cinfo.err = jpeg_std_error( & amp;jerr);
    jpeg_create_compress( & amp;cinfo);

    FILE * outfile; // target file
    if ((outfile = fopen(filename, "wb")) == NULL) {
        fprintf(stderr, "can't open %s\
", filename);
        exit(1);
    }
    jpeg_stdio_dest( & amp;cinfo, outfile);

    cinfo.image_width = image_width; // image width and height, in pixels
    cinfo.image_height = image_height;
    cinfo.input_components = 3; // # of color components per pixel
    cinfo.in_color_space = JCS_YCbCr; //colorspace of input image
    jpeg_set_defaults( & amp;cinfo);
    jpeg_set_quality( & amp;cinfo, quality, TRUE );

    //
    // cinfo.raw_data_in = TRUE;
    cinfo.jpeg_color_space = JCS_YCbCr;
    cinfo.comp_info[0].h_samp_factor = 2;
    cinfo.comp_info[0].v_samp_factor = 2;
    /

    jpeg_start_compress( & amp;cinfo, TRUE);

    JSAMPROW row_pointer[1];

    unsigned char *yuvbuf;
    if((yuvbuf=(unsigned char *)malloc(image_width*3))!=NULL)
        memset(yuvbuf,0,image_width*3);

    unsigned char *ybase,*ubase;
    ybase=pdata;
    ubase=pdata + image_width*image_height;
    int j=0;
    while (cinfo.next_scanline < cinfo.image_height)
    {
        int idx=0;
        for(int i=0;i<image_width;i + + )
        {
            yuvbuf[idx + + ]=ybase[i + j * image_width];
            yuvbuf[idx + + ]=ubase[j/2 * image_width + (i/2)*2];
            yuvbuf[idx + + ]=ubase[j/2 * image_width + (i/2)*2 + 1];
        }
        row_pointer[0] = yuvbuf;
        jpeg_write_scanlines( & amp;cinfo, row_pointer, 1);
        j + + ;
    }
    jpeg_finish_compress( & amp;cinfo);
    jpeg_destroy_compress( & amp;cinfo);
    fclose(outfile);
    return 0;
}

2. YUV420P

In fact, the main difference between YUV420P and YUV420SP is the way to retrieve data. It has been explained very clearly how YUV420P retrieves data. To store YUV420P as JPG, you only need to change the method of retrieving data based on the above method of storing YUV420SP as JPG.

Effect:

This is a 1280X720 picture with a size of 385kb. Because it is a USB camera, the picture quality is not very high. Overall, the effect is good!

6. Conversion between YUV420SP and YUV420P

After knowing the memory formats of YUV420SP and YUV420P, it is not difficult to convert between them.

1. YUV420SP TO YUV420P

int yuv420sp_to_yuv420p(unsigned char * yuv420sp, unsigned char* yuv420p, int width, int height)
{
    if(yuv420sp==NULL)
        return;
    int i=0,j=0;
    //Y
    for(i=0;i<width*height;i + + )
    {
        yuv420p[i]=yuv420sp[i];
    }
    //U
    for(int j=0,i=0;j<width*height/2;j + =2,i + + )
    {
        yuv420p[i + width*height] = yuv420sp[j + width*height];
    }
    
    //V
    for(i=0,j=1,j<width*height/2;j + =2,i + + )
    {
        yuv420p[i + width*height*5/4] = yuv420sp[j + width*height];
    }
}

2. YUV420P TO YUV420SP

int yuv420p_to_yuv420sp(unsigned char * yuv420p, unsigned char* yuv420sp, int width, int height)
{
    if(yuv420p==NULL)
        return;
    int i=0,j=0;
    //Y
    for(i=0;i<width*height;i + + )
    {
        yuv420sp[i]=yuv420p[i];
    }

    int m=0,n=0;
    for(int j=0;j<width*height/2;j + + )
    {
        if(j%2==0)
           yuv420sp[j + width*height]=yuv420p[m + + ];
        else
           yuv420sp[j + width*height]=yuv420p[n + + ];
    }
}

Benefit of this article, you can receive free C++ audio and video learning materials package, technical video/code, including (audio and video development, interview questions, FFmpeg, webRTC, rtmp, hls, rtsp, ffplay, codec, push-pull streaming, srs) ↓↓↓↓↓↓See below↓↓Click at the bottom of the article to get it for free↓↓