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↓↓