RGB and YUV

Mentioned in the previous article on the JPEG format in the conversion between YUV and RGB, readers understand the background reply I said no, then start to talk about this, and will doping some recent video and audio learning content.

What is RGB, YUV

First mentioned in the YCbCr color space in an article on the JPEG codec in (do not go back and find the original, there will briefly summarize). YUV, RGB and YCbCr color space is a model, but we most often heard BMP, PNG, JPEG, GIF is a file storage format.

RGB is the three primary colors red green blue, any color can be mixed up by the three primary colors in different proportions. So very broad application.

YUV is the luminance Y (gradation values), the color difference signals U and V. We human eye color luminance information than color information would be sensitive, no UV information can be displayed as a complete picture, but black and white. By default the image and video compression standards.

YCbCr YUV actually scaled and offset replica. Wherein Y is consistent with the meaning of YUV Y, Cb, Cr color all referring to the same, but the method of representing different. Wherein Y refers to YCbCr luminance component, Cb refers to the blue chrominance components, and red chrominance component Cr means. JPEG, MPEG adopt this format.

The three color space model can be converted to each other by format.

RGB to YUV conversion

RGB to YUV conversion, is to convert an image of all pixels R, G, B component of the Y, U, V components:

YUV和RGB的转换:
      Y = 0.299 R + 0.587 G + 0.114 B
      U = -0.1687 R - 0.3313 G + 0.5 B + 128
      V = 0.5 R - 0.4187 G - 0.0813 B + 128

      R = Y + 1.402 (V-128)
      G= Y - 0.34414 (U-128) - 0.71414 (V-128)
      B= Y + 1.772 (U-128)

Since it can be converted, why do you need two color models RGB and YUV it? Since the image display, the image which is displayed by the RGB model. And when the image data is transmitted using the YUV model, because the YUV model may save bandwidth. Therefore, when it is necessary to capture the image RGB to YUV model into the model, the model is displayed and then converted to RGB YUV model.

Video sampling

From the perspective of the video acquisition and processing, the general video capture chip code output stream format YUV data stream are generally, from video processing (e.g., H.264, MPEG video codec) point of view, but also in original YUV encoding stream and parsing; if the RGB resource collection, but also needs to be converted into YUV.

We must restore the YUV value of each pixel from the code stream according to a sampling format, YUV format, there are two categories: planar and packed. For the planar YUV format, into continuous storage of all pixels Y, U subsequently stored in all pixels, followed by all the pixels of the V. For the packed YUV format, Y of each pixel, U, V continuous cross stored.

And closely related to the YUV format stored in its sample stream fact way, mainstream sampling in three ways, YUV4: 4: 4, YUV4: 2: 2, YUV4: 2: 0.

YUV sample format

YUV 4: 4: 4 sampling

It means that the Y, U, V same sampling ratio of three components, so that the generated image, the three components of information for each pixel is 8bit. (Y is represented by ×, UV ○ expressed)

Pictures .png

假如图像像素为:[Y0 U0 V0]、[Y1 U1 V1]、[Y2 U2 V2]、[Y3 U3 V3]

那么采样的码流为:Y0 U0 V0 Y1 U1 V1 Y2 U2 V2 Y3 U3 V3 

最后映射出的像素点依旧为 [Y0 U0 V0]、[Y1 U1 V1]、[Y2 U2 V2]、[Y3 U3 V3] 

This sampling method and the image size of the RGB color model is the same, and does not achieve the purpose of saving bandwidth.

YUV 4: 2: 2 sampling

UV component is generally Y component, the Y component, and UV component ratio of 2: 1 sampling, if there are 10 horizontal pixels, then the sampling of the Y component 10, to only five sampling UV component.

Pictures .png

假如图像像素为:[Y0 U0 V0]、[Y1 U1 V1]、[Y2 U2 V2]、[Y3 U3 V3]

 那么采样的码流为:Y0 U0 Y1 V1 Y2 U2 Y3 V3 

 其中,每采样过一个像素点,都会采样其 Y 分量,而 U、V 分量就会间隔一个采集一个。

 最后映射出的像素点为 [Y0 U0 V1]、[Y1 U0 V1]、[Y2 U2 V3]、[Y3 U2 V3]

This example can be found by a first pixel and the second pixel share the [U0, V1] component, the third pixels and fourth pixels share a [U2, V3] component, thus saving image space. For example, a 1280 * 720 size image, if stored in RGB mode, consuming:

\ [(1280 * 720 * 8 + 1280 * 720 * 8 + 1280 * 720 * 8), 8/1024/1024 = 2.637MB \]

1280 * 720 which is the number of pixels. However, if the YUV4: 2: Sample format:

\[(1280 * 720 * 8 + 1280 * 720 * 0.5 * 8 * 2)/ 8 / 1024 / 1024 = 1.76 MB \]

1/3 saves storage space, suitable for image transmission.

YUV 4: 2: 0 sampling

YUV 4: 2: 0 sampling, the sample does not mean only the V component samples without the U component. But rather, each line is scanned, scan only one kind of chrominance component (U or V), and Y components in a 2: 1 sampling pattern. For example, the first scan line, in accordance with YU 2: 1 sampling mode, the second scan line, YV component of a 2: 1 sampling pattern. For each chrominance component, it samples the horizontal direction and a vertical direction component and Y are compared to 2: 1.

Pictures .png

假设图像像素为:
 
[Y0 U0 V0]、[Y1 U1 V1]、 [Y2 U2 V2]、 [Y3 U3 V3]
[Y4 U4 V4]、[Y5 U5 V5]、[Y6 U6 V6]、 [Y7 U7 V7]
 
那么采样的码流为:Y0 U0 Y1 Y2 U2 Y3 Y4 V4 Y5 Y6 V6 Y7
 
其中,每采样过一个像素点,都会采样其 Y 分量,而 U、V 分量就会间隔一行按照 2 : 1 进行采样。
 
最后映射出的像素点为:

[Y0 U0 V5]、[Y1 U0 V5]、[Y2 U2 V7]、[Y3 U2 V7]
[Y5 U0 V5]、[Y6 U0 V5]、[Y7 U2 V7]、[Y8 U2 V7]

By YUV 4: 2: 0 sampled image size:

\[(1280 * 720 * 8 + 1280 * 720 * 0.25 * 8 * 2)/ 8 / 1024 / 1024 = 1.32 MB \]

Image sampling save half the storage space than RGB model image, and therefore more mainstream sampling method.

YUV storage format

I mentioned briefly the YUV format stored in two ways:

planar 平面格式
    指先连续存储所有像素点的 Y 分量,然后存储 U 分量,最后是 V 分量。
packed 打包模式
    指每个像素点的 Y、U、V 分量是连续交替存储的。

According to different sampling methods and storage format, there will be a variety of YUV format. These formats are mainly based on YUV 4: 2: 2 and YUV 4: 2: 0 sampling.

Based YUV 4: 2 sampling format: 2

YUV 4: 2: 2 sampling the Y and UV components of a predetermined ratio of 2: 1 sampling, a common set of two Y-component UV component.

YUYV format

Packed format YUYV format is employed for storing, it means that each pixel of the Y component are used, but the UV component of its sampling every other pixel arranged in the order as follows:

Y0 UO Y1 V0  Y2 U2 Y3 V2

Y1 and Y0 U0 V0 common components, Y2 and Y3 common component U2 V2.

Pictures .png

UYVY format

UYVY format packed format also uses memory, and the sequence YUYV its opposite, U-component of the first re-sampled Y component, are arranged in the following order:

U0 Y0 V0 Y1 U2 Y2 V2 Y3

Y1 and Y0 U0 V0 common components, Y2 and Y3 common component U2 V2.

Pictures .png

YUV 422P format

YUV 422P format, also called I422, plane format is used for storage, all the previously stored Y component, the U component and then stores all, then all of the storage components V

Based YUV 4: 2: 0 sampling format

基于 YUV 4:2:0 采样的格式主要有 YUV 420P 和 YUV 420SP 两种类型 。YUV 420P 和 YUV 420SP 都是基于 Planar 平面模式 进行存储的,先存储所有的 Y 分量后, YUV420P 类型就会先存储所有的 U 分量或者 V 分量,而 YUV420SP 则是按照 UV 或者 VU 的交替顺序进行存储 。所以针对这两种类型的格式就为YU12格式、YV112格式和NV12格式、NV21格式。

YU12和YV12格式

YU12 和 YV12 格式都属于 YUV 420P 类型,即先存储 Y 分量,再存储 U、V 分量,区别在于:YU12 是先 Y 再 U 后 V,而 YV12 是先 Y 再 V 后 U 。

YU12 format

NV12和NV21格式

NV12 和 NV21 格式都属于 YUV420SP 类型。它也是先存储了 Y 分量,但接下来并不是再存储所有的 U 或者 V 分量,而是把 UV 分量交替连续存储。

NV12 是 IOS 中有的模式,它的存储顺序是先存 Y 分量,再 UV 进行交替存储。

NV12 format

RGB格式

RGB565 每个像素用16位表示,RGB分量分别使用5位、6位、5位

RGB555 每个像素用16位表示,RGB分量都使用5位(剩下1位不用)

RGB24 每个像素用24位表示,RGB分量各使用8位

RGB32 每个像素用32位表示,RGB分量各使用8位(剩下8位不用)

ARGB32 每个像素用32位表示,RGB分量各使用8位(剩下的8位用于表示Alpha通道值)

在这里介绍一下常用的格式:

RGB565,使用16位表示一个像素,5位用于R,6位用于G,5位用于B。

RGB24,使用24位表示一个像素,RGB分量都用8位表示,取值范围为0-255,排列顺序为:BGR

typedef struct tagRGBTRIPLE {
    BYTE rgbtBlue; // 蓝色分量
    BYTE rgbtGreen; // 绿色分量
    BYTE rgbtRed; // 红色分量
} RGBTRIPLE;

一些应用

这里参考了雷神的博客。

分离YUV420P像素数据中的Y、U、V分量

首先建立一个函数能够将根据格式将YUV420P格式的文件分离成三个分量的文件:

output_420_y.y:纯Y数据,分辨率为256x256。
output_420_u.y:纯U数据,分辨率为128x128。
output_420_v.y:纯V数据,分辨率为128x128。

函数如下:

int yuv420_split(const char *path, int w, int h, int num)
{
    FILE *fp = fopen(path, "r");
    if (fp < 0)
        cerr << "File doesn't exist.";

    FILE *fy, *fu, *fv;
    fy = fopen("output_420_y.y", "w");
    fu = fopen("output_420_u.y", "w");
    fv = fopen("output_420_v.y", "w");
    if (fv < 0 || fy < 0 || fu < 0)
        cerr << "Cannot create file.";

    //YUV420:Y->w*h + U->w*h*1/4 + V->w*h*1/4
    unsigned char *pic = (unsigned char*)malloc(w * h * 3 / 2);
    for (int i = 0; i < num; ++i)
    {
        fread(pic, 1, w * h * 3 / 2, fp);
        fwrite(pic, 1, w*h, fy);
        fwrite(pic + w*h, 1, w*h * 1 / 4, fu);
        fwrite(pic + w * h * 5 / 4,1, w*h * 1 / 4, fv);
    }

    free(pic);
    fclose(fp);
    fclose(fy);
    fclose(fu);
    fclose(fv);
    
    return 0;
}

得到的效果图就是下图:

Pictures .png

分离YUV444P像素数据中的Y、U、V分量

这就相当于提取一个根据RGB相同大小的像素数据转换而来的Y、U、V三个分量,稍微更改一下读取的位置就行了,函数如下:

int yuv444_split(const char *path, int w, int h, int num)
{
    FILE *fp = fopen(path, "r");
    if (fp < 0)
        cerr << "File doesn't exist.";

    FILE *fy, *fu, *fv;
    fy = fopen("output_444_y.y", "w");
    fu = fopen("output_444_u.y", "w");
    fv = fopen("output_444_v.y", "w");
    if (fv < 0 || fy < 0 || fu < 0)
        cerr << "Cannot create file.";

    //YUV420:Y->w*h + U->w*h + V->w*h
    unsigned char *pic = (unsigned char*)malloc(w * h * 3);
    for (int i = 0; i < num; ++i)
    {
        int size = w * h;
        fread(pic, 1, size * 3 , fp);
        fwrite(pic, 1, size, fy);
        fwrite(pic +size, 1, size, fu);
        fwrite(pic + 2*size, 1, size, fv);
    }

    free(pic);
    fclose(fp);
    fclose(fy);
    fclose(fu);
    fclose(fv);

    return 0;
}

将YUV420P像素数据变为灰度图

这个只需要保存Y分量就可以了,最后保存的文件更改为.yuv格式即可。但是在生成.yuv文件的时候是需要uv分量的,这个只需要将U、V分量设置成128即可。

这是因为U、V是图像中的经过偏置处理的色度分量。色度分量在偏置处理前的取值范围是-128至127,这时候的无色对应的是“0”值。经过偏置后色度分量取值变成了0至255,因而此时的无色对应的就是128了。

程序如下:

int yuv420_gray(const char *path, int w, int h, int num)
{
    FILE *fp = fopen(path, "r");
    if (fp < 0)
        cerr << "File doesn't exist.";

    FILE *fgray;
    fgray = fopen("output_420_gray.yuv", "w");
    if (fgray < 0)
        cerr << "Cannot create file.";

    unsigned char *pic = (unsigned char*)malloc(w * h * 3);
    for (int i = 0; i < num; ++i)
    {
        fread(pic, 1, w*h * 3 / 2, fp);
        fwrite(pic, 1, w*h, fgray);
        memset(pic + w * h, 128, w*h/2);
        fwrite(pic + w * h, 1, w*h * 1 / 2, fgray);
    }

    free(pic);
    fclose(fp);
    fclose(fgray);

    return 0;
}

效果图如下:

Pictures .png

将YUV420P像素数据的亮度减半

只需将亮度分量Y的数值减半就可以了,程序实现如下:

int yuv420_halfy(const char *path, int w, int h, int num)
{
    FILE *fp = fopen(path, "r");
    if (fp < 0)
        cerr << "File doesn't exist.";

    FILE *fhalfy;
    fhalfy = fopen("output_420_halfy.yuv", "w");
    if (fhalfy < 0)
        cerr << "Cannot create file.";

    unsigned char *pic = (unsigned char*)malloc(w * h * 3);
    for (int i = 0; i < num; ++i)
    {
        fread(pic, 1, w*h * 3 / 2, fp);
        for (int j = 0; j < w*h; ++j)
            pic[j] /= 2;
        fwrite(pic, 1, w*h, fhalfy);
        fwrite(pic + w * h, 1, w*h * 1 / 2, fhalfy);
    }

    free(pic);
    fclose(fp);
    fclose(fhalfy);

    return 0;
}

Pictures .png

在YUV420P像素数据的周围加上边框

修改YUV数据中特定位置(边框)的分量Y数值,给图像添加一个边框效果,程序如下:

int yuv420_border(const char *path, int w, int h, int border, int num)
{
    FILE *fp = fopen(path, "r");
    if (fp < 0)
        cerr << "File doesn't exist.";

    FILE *fborder;
    fborder = fopen("output_420_border.yuv", "w");
    if (fborder < 0)
        cerr << "Cannot create file.";

    unsigned char *pic = (unsigned char*)malloc(w * h * 3);
    for (int i = 0; i < num; ++i)
    {
        fread(pic, 1, w*h * 3 / 2, fp);
        for (int cy = 0; cy < h; ++cy)
        {
            for (int cx = 0; cx < w; ++cx)
            {
                if (cx<border || cx>w - border || cy<border || cy>h - border)
                    pic[cx + cy * w] = 255; //二维数组的线性表示
            }
        }
        fwrite(pic, 1, w*h*3/2, fborder);
    }

    free(pic);
    fclose(fp);
    fclose(fborder);

    return 0;
}

Pictures .png

生成YUV420P格式的灰阶测试图

首先得确定灰阶测试图的亮度最小值和最大值、灰阶数量,然后生成相应的图,不妨设置成这样的取值:

Pictures .png

int yuv420_graybar(int w, int h, int ymin, int ymax, int barnum)
{
    int barwidth = w/barnum;
    unsigned lum;
    float dlum = (float)(ymax - ymin) / (float)(barnum - 1);
    unsigned char *data_y, *data_u, *data_v;

    int uv_width = w / 2;
    int uv_height = h / 2;

    data_y = (unsigned char *)malloc(w*h);
    data_u = (unsigned char *)malloc(uv_width*uv_height);
    data_v = (unsigned char *)malloc(uv_width*uv_height);

    FILE *fp;
    fp = fopen("output_yun420_graybar.yuv", "w");
    if (fp < 0)
        cerr << "Cannot create file.";

    //for (int i = 0; i < w / barwidth; ++i)
    //{
    //    lum = ymin + (char)(i*dlum);
    //}

    for (int i = 0; i < h; ++i)
        for (int j = 0; j < w; ++j)
            data_y[i*w + j] = ymin + (char)(j / barwidth * dlum);
    for (int i = 0; i < uv_height; ++i)
        for (int j = 0; j < uv_width; ++j)
        {
            data_u[i*uv_width + j] = 128;
            data_v[i*uv_width + j] = 128;
        }

    fwrite(data_y, 1, w*h, fp);
    fwrite(data_u, 1, uv_width*uv_height, fp);
    fwrite(data_v, 1, uv_width*uv_height, fp);
    fclose(fp);
    free(data_y);
    free(data_u);
    free(data_v);
    return 0;  
}

Pictures .png

计算两个YUV420P像素数据的PSNR

PSNR通常用于图像质量评价,即计算受损图像与原始图像之间的区别,以此来评价受损图像的质量,其计算公式为:

\[PSNR = 10*log_{10}(\frac{255^{2}}{MSE})\]

其中MSE的定义为:

\[MSE = \frac{1}{M*N}\sum{\sum(x_{ij}-y_{ij})^{2}}\]

M,N分别为图像的宽高,xij和yij分别为两张图像的每一个像素值。 用程序来直接计算两者之间的区别:

int yuv420_psnr(const char *path1, const char *path2, int w, int h, int num)
{
    FILE *f1, *f2;
    f1 = fopen(path1, "r");
    f2 = fopen(path2, "r");
    if (f1 < 0 || f2 < 0) cerr << "Error when opening files";
    unsigned char *pic1 = (unsigned char *)malloc(w*h);
    unsigned char *pic2 = (unsigned char *)malloc(w*h);

    for (int i = 0; i < num; i++) 
    {
        if (fread(pic1, 1, w*h, f1)<0) cerr<<"Error when reading file1";
        if (fread(pic2, 1, w*h, f2)<0) cerr<<"Error when reading file2";

        double mse_sum = 0, mse = 0, psnr = 0;
        for (int j = 0; j < w*h; j++) {
            mse_sum += pow((double)(pic1[j] - pic2[j]), 2);
        }
        mse = mse_sum / (w*h);
        psnr = 10 * log10(255.0*255.0 / mse);
        cout << psnr << endl;
    }
    free(pic1);
    free(pic2);
    fclose(f1);
    fclose(f2);
    return 0;
}

分离RGB24像素数据中的RGB

和分离YUV文件中的Y、U、V分量一样,不同的是RGB的排列顺序为R-G-B-R-G-B-...RGB24格式的每个像素的三个分量是连续存储的。一帧宽高分别为w、h的RGB24图像一共占用wh3 Byte的存储空间

所以程序为:

int rgb24_split(const char *path, int w, int h, int num) {
    FILE *fp = fopen(path, "r");
    FILE *fp1 = fopen("output_r.y", "w");
    FILE *fp2 = fopen("output_g.y", "w");
    FILE *fp3 = fopen("output_b.y", "w");

    unsigned char *pic = (unsigned char *)malloc(w*h * 3);

    for (int i = 0; i < num; i++) {

        fread(pic, 1, w*h * 3, fp);

        for (int j = 0; j < w*h * 3; j = j + 3) {
            //R
            fwrite(pic + j, 1, 1, fp1);
            //G
            fwrite(pic + j + 1, 1, 1, fp2);
            //B
            fwrite(pic + j + 2, 1, 1, fp3);
        }
    }

    free(pic);
    fclose(fp);
    fclose(fp1);
    fclose(fp2);
    fclose(fp3);

    return 0;
}

Pictures .png

Pictures .png

将RGB24格式像素数据封装为BMP图像

BMP图像实际上存的就是RGB数据,所以需要程序实现对RGB像素数据的封装处理,具体实现起来就是在RGB数据前面加上文件头,同时将RGB数据中每个像素的"B"和"R"的位置互换,这是因为BMP文件是小端存储,在内存中存储的先后顺序为B-G-R。

BMP文件结构

BMP文件由4部分组成:

  1. 位图文件头(bitmap-file header)
  2. 位图信息头(bitmap-informationheader)
  3. 颜色表(color table)
  4. 颜色点阵数据(bits data)

24位真彩色位图没有颜色表,所以只有1、2、4这三部分。

位图文件头分4部分,共14字节:

名称 占用空间 内容 实际数据
bfType 2字节 标识,就是“BM”二字 BM
bfSize 4字节 整个BMP文件的大小 0x000C0036(786486)【与右键查看图片属性里面的大小值一样】
bfReserved1/2 4字节 保留字,没用 0
bfOffBits 4字节 偏移数,即 位图文件头+位图信息头+调色板 的大小 0x36(54)

位图信息头共40字节:

名称 占用空间 内容 实际数据
biSize 4字节 位图信息头的大小,为40 0x28(40)
biWidth 4字节 位图的宽度,单位是像素 0x200(512)
biHeight 4字节 位图的高度,单位是像素 0x200(512)
biPlanes 2字节 固定值1 1
biBitCount 2字节 每个像素的位数 1-黑白图,4-16色,8-256色,24-真彩色 0x18(24)
biCompression 4字节 压缩方式,BI_RGB(0)为不压缩 0
biSizeImage 4字节 位图全部像素占用的字节数,BI_RGB时可设为0 0x0C
biXPelsPerMeter 4字节 水平分辨率(像素/米) 0
biYPelsPerMeter 4字节 垂直分辨率(像素/米) 0
biClrUsed 4字节 位图使用的颜色数 如果为0,则颜色数为2的biBitCount次方 0
biClrImportant 4字节 重要的颜色数,0代表所有颜色都重要 0

Color dot matrix data

All pixel bitmap, in accordance with the self upward from left to right in the order of arrangement.

RGB data is read backwards, the raw data in order B, G, R arranged.

BMP file program structure

// 位图文件头
typedef  struct  tagBITMAPFILEHEADER
{ 
    unsigned short int  bfType;       //位图文件的类型,必须为BM 
    unsigned int       bfSize;       //文件大小,以字节为单位
    unsigned short int  bfReserverd1; //位图文件保留字,必须为0 
    unsigned short int  bfReserverd2; //位图文件保留字,必须为0 
    unsigned int       bfbfOffBits;  //位图文件头到数据的偏移量,以字节为单位
}BITMAPFILEHEADER; 

// 位图信息头
typedef  struct  tagBITMAPINFOHEADER 
{ 
    unsigned int biSize;                        //该结构大小,字节为单位
    int  biWidth;                     //图形宽度以象素为单位
    int  biHeight;                     //图形高度以象素为单位
    unsigned short biPlanes;               //目标设备的级别,必须为1 
    unsigned short  biBitcount;             //颜色深度,每个象素所需要的位数
    unsigned int  biCompression;        //位图的压缩类型
    unsigned int  biSizeImage;              //位图的大小,以字节为单位
    int  biXPelsPermeter;       //位图水平分辨率,每米像素数
    int  biYPelsPermeter;       //位图垂直分辨率,每米像素数
    unsigned int  biClrUsed;            //位图实际使用的颜色表中的颜色数
    unsigned int  biClrImportant;       //位图显示过程中重要的颜色数
}BITMAPINFOHEADER;

// RGB数据
...

RGB24->BMP

BMP realized RGB to the following procedure:

int rgb24_to_bmp(const char* rgb24path, int width, int height, const char* bmppath)
{
    BmpHead m_header = { 0 };
    InfoHead m_infoHeader = { 0 };
    char bfType[2] = { 'B', 'M' };
    int header_size = sizeof(bfType) + sizeof(BmpHead) + sizeof(InfoHead);
    unsigned char* rgb24_buffer;
    FILE *f_rgb, *f_bmp;
    f_rgb = fopen(rgb24path, "r");
    if (f_rgb < 0) cerr << "Cannot open RGB file";
    f_bmp = fopen(bmppath, "w");
    if (f_bmp < 0) cerr << "Cannot create BMP file";

    rgb24_buffer = (unsigned char *)malloc(width*height * 3);
    if (fread(rgb24_buffer, 1, width*height * 3, f_rgb) < 0)
        cerr << "Error when reading RGB";
    m_header.imageSize = header_size + width * height *3; //1像素=8bit,1byte=8bit
    m_header.startPosition = header_size;
    m_infoHeader.biSize = sizeof(InfoHead);
    m_infoHeader.width = width;
    m_infoHeader.height = -height; // BMP存储像素是从下至上的
    m_infoHeader.colorPlane = 1;
    m_infoHeader.bitColor = 24; //24位真彩
    m_infoHeader.realSize = 3 * width*height;
    cout << sizeof(BmpHead) << endl;
    cout << sizeof(InfoHead) << endl;

    //for (int i = 0; i < height; ++i)
    //{
    //    for (int j = 0; j < width; ++j)
    //    {
    //        char temp = rgb24_buffer[(i*width + j) * 3 + 2];
    //        rgb24_buffer[(i*width + j) * 3 + 2] = rgb24_buffer[(i*width + j) * 3];
    //        rgb24_buffer[(i*width + j) * 3] = temp;
    //    }
    //}

    for (int i = 0; i < height*width; ++i)
    {
        unsigned char temp = *(rgb24_buffer+i*3);
        *(rgb24_buffer + i * 3) = *(rgb24_buffer + i * 3 + 2);
        *(rgb24_buffer + i * 3 + 2) = temp;
    }

    fwrite(bfType, 1, sizeof(bfType), f_bmp);
    fwrite(&m_header, 1, sizeof(m_header), f_bmp);
    fwrite(&m_infoHeader, 1, sizeof(m_infoHeader), f_bmp);
    //fwrite(rgb24_buffer, 1, sizeof(rgb24_buffer), f_bmp);
    fwrite(rgb24_buffer, 3*width*height, 1, f_bmp);


    fclose(f_rgb);
    fclose(f_bmp);
    free(rgb24_buffer);
    return 0;
}

Pictures .png

BMP files generated as follows, for a long time but the investigation found no errors (Khan -_- ||), color appears like this that may be interchanged RGB when there is a problem, but the idea did not go wrong. Later found reason to fix it, leaving a hole here ......

RGB24 format of the pixel data is converted into pixel data format YUV420P

Use RGB and YUV conversion formula, the need to pay attention when storage is RGB24 Packed, YUV420P of storage Packed program is as follows:

unsigned char clip_value(unsigned char x, unsigned char min_val, unsigned char  max_val) {
    if (x>max_val) {
        return max_val;
    }
    else if (x<min_val) {
        return min_val;
    }
    else {
        return x;
    }
}

//RGB to YUV420
bool RGB24_TO_YUV420(unsigned char *RgbBuf, int w, int h, unsigned char *yuvBuf)
{
    unsigned char *ptrY, *ptrU, *ptrV, *ptrRGB;
    memset(yuvBuf, 0, w*h * 3 / 2);
    ptrY = yuvBuf;
    ptrU = yuvBuf + w * h;
    ptrV = ptrU + (w*h * 1 / 4);
    unsigned char y, u, v, r, g, b;
    for (int j = 0; j<h; j++) {
        ptrRGB = RgbBuf + w * j * 3; //从RgbBuf的每一行的开头开始
        for (int i = 0; i<w; i++) {

            r = *(ptrRGB++);
            g = *(ptrRGB++);
            b = *(ptrRGB++);
            y = (unsigned char)((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
            u = (unsigned char)((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
            v = (unsigned char)((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;
            *(ptrY++) = clip_value(y, 0, 255);
            if (j % 2 == 0 && i % 2 == 0) {
                *(ptrU++) = clip_value(u, 0, 255);
            }
            else {
                if (i % 2 == 0) {
                    *(ptrV++) = clip_value(v, 0, 255);
                }
            }
        }
    }
    return true;
}


int rgb24_to_yuv420(const char *path1, int w, int h, int num, const char *path2) {
    FILE *fp = fopen(path1, "r");
    FILE *fp1 = fopen(path2, "w");

    unsigned char *pic_rgb24 = (unsigned char *)malloc(w*h * 3);
    unsigned char *pic_yuv420 = (unsigned char *)malloc(w*h * 3 / 2);

    for (int i = 0; i<num; i++) {
        fread(pic_rgb24, 1, w*h * 3, fp);
        RGB24_TO_YUV420(pic_rgb24, w, h, pic_yuv420);
        fwrite(pic_yuv420, 1, w*h * 3 / 2, fp1);
    }

    free(pic_rgb24);
    free(pic_yuv420);
    fclose(fp);
    fclose(fp1);

    return 0;
}

Generated YUV file size will be reduced a lot:

Pictures .png

Of course, it involves only the image file and YUV420P RGB24 format, so if the format is not the same, you need to change the code.

Guess you like

Origin www.cnblogs.com/yunlambert/p/11234971.html