RGBとYUV

YUVとRGB間の変換では、JPEG形式の前回の記事で述べたように、読者は私がいないと、この話をし始めると、バックグラウンド応答を理解し、いくつかの最近のビデオとオーディオの学習をドーピングますコンテンツ。

RGB、YUVとは何ですか

まずにおけるJPEGコーデックの記事でYCbCr色空間で述べた(戻って、オリジナルを見つけることができませんが、簡単にそこに要約されます)。YUV、RGBとYCbCr色空間は、モデルですが、私たちが最も頻繁に聞いたBMP、PNG、JPEG、GIFファイルの保存形式です。

RGB青緑、赤の三原色である、任意の色が異なる割合で三原色処理機により混合することができます。だから、非常に広範なアプリケーション。

YUVは、輝度Y(階調値)であり、色差UおよびV信号を送ります 我々は、色情報よりも、人間の目の色の輝度情報に敏感になり、何UV情報は、完全な画像として表示されないが、黒と白することができます。デフォルトでは、画像およびビデオ圧縮規格。

YCbCrのYUVは、実際にスケーリングされ、レプリカを相殺しました。前記Yは、全て同じに言及するが、異なる表現方法YUV Y、Cb、Cr色の意味と一致しています。Yは、YCbCrの輝度成分をいい、ここで、Cbは、青の色差成分を指し、及び赤色差成分Crを意味します。JPEG、MPEGは、このフォーマットを採用します。

三色の空間モデルは、形式によって相互に変換することができます。

YUVへの変換RGB

YUVへの変換RGBは、全ての画素のR、G、Y、UのB成分、V成分の画像を変換することです。

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)

それは変換することができますので、なぜあなたは2つのカラーモデルのRGBを必要とし、それをYUVのですか?画像表示するので、画像のRGBモデルによって表示されます。YUVモデルは、帯域幅を節約する可能性があるためとされた画像データは、YUVモデルを使用して送信されたとき。それはモデルにYUVモデルに画像RGBをキャプチャする必要がある場合したがって、モデルを表示した後、RGB YUVモデルに変換されます。

ビデオサンプリング

映像取得及び処理の観点から、一般的なビデオキャプチャチップコード出力ストリームフォーマットYUVデータストリームビューのビデオ処理(例えば、H.264、MPEGビデオコーデック)点からだけでなく、中に、一般的に元のYUV符号化ストリームと解析は、RGBリソースコレクション場合だけでなく、YUVに変換する必要があります。

平面および充填:我々はサンプリングフォーマット、YUVフォーマットに従って符号ストリームから各画素のYUV値を復元する必要があり、2つのカテゴリがあります。平面YUV形式の場合、全ての画素の連続的なストレージにY、Uは、その後Vの全ての画素が続く、全ての画素に格納されています パックされたYUV形式の場合、各画素のYは、U、V連続クロス記憶されます。

4:4:4、YUV4:2:2、YUV4:0:2と密接にそのサンプルストリームの実際の方法に格納されたYUV形式、三つの方法、YUV4主流サンプリングに関する。

YUVサンプル形式

YUV 4:4:4のサンプリング

画素毎に生成された画像情報の3つの成分が8ビットであるので、それは、3つの成分のそのY、U、V同じサンプリング比を意味します。(Yは×で表され、UV○で表されます)

写真.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] 

このサンプリング方法およびRGBカラーモデルの画像サイズは同じであり、帯域幅を節約する目的を達成しません。

YUV 4:2:2サンプリング

10個の水平画素がある場合、5つだけサンプリングUV成分に対するY成分10の次にサンプリング、1サンプリング:UV成分は、一般に、Y成分、Y成分、及び2のUV成分比です。

写真.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]

この例では、第一の画素と第2画素共有[U0、V1]成分により求めることができる、第三の画素と第四の画素がこのように保存、[U2、V3]コンポーネントを共有します画像空間。例えば、1280×720サイズの画像、RGBモードに格納されている場合、消費:

\ [(1280 * 720 * 8 + 1280×720 * + 1280年8 * 720 * 8)、8/1024/1024 = 2.637メガバイト\]

画素数が1280×720。しかし、YUV4場合:2:サンプル・フォーマット:

\ [(1280 * 720 * 8 + 1280×720 * 0.5 * 8 * 2)8/1024分の1024 / = 1.76メガバイト\]

1/3画像伝送に適した記憶空間を保存します。

YUV 4:2:0サンプリング

YUV 4:2:0サンプリング、サンプルは、U成分なしにのみV成分サンプルを意味するものではありません。むしろ、各ラインはクロミナンス成分(UまたはV)の一種のみをスキャンし、スキャンし、そして2におけるY成分:1サンプリングパターン。例えば、第1の走査線、YU 2に従って:1のサンプリングモード、第2の走査線、2のYV成分:1サンプリングパターン。1:各色差成分の場合、試料を水平方向と垂直方向成分とY 2と比較されます。

写真.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]

YUV 4によって:2:0サンプリングされた画像サイズ:

\ [(1280 * 720 * 8 + 1280×720 * 0.25 * 8 * 2)/ 1024/8/1024 = 1.32メガバイト\]

画像のサンプリングは半分RGBモデル画像よりも収納スペース、したがってより多くの主流のサンプリング方法を保存します。

YUVストレージフォーマット

私は簡単に二つの方法で保存されたYUVフォーマットを述べました:

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

異なるサンプリング方法及び記憶フォーマットによれば、YUVフォーマットの種々存在します。2:2とYUV 4:2:0サンプリングこれらのフォーマットは、主に、YUV 4に基づいています。

ベースYUV 4:2サンプリングフォーマット:2

YUV 4:2:2 2の所定の割合のY及びUV成分サンプリング:1つのサンプリング、2つのY成分、UV成分の共通セットを。

YUYVフォーマット

パックされたフォーマットYUYVフォーマットを記憶するために使用され、それがY成分の各画素が使用されることを意味するが、それは次のように順に配置され、他のすべてのピクセルをサンプリングするUV成分:

Y0 UO Y1 V0  Y2 U2 Y3 V2

Y1とY0のU0 V0共通のコンポーネント、Y2及びY3共通部品U2のV2。

写真.PNG

UYVYフォーマット

UYVYフォーマットパック形式は、メモリを使用し、配列は以下の順序で配置され、その反対側の、第1の再サンプリングされたY成分のU成分を、YUYV。

U0 Y0 V0 Y1 U2 Y2 V2 Y3

Y1とY0のU0 V0共通のコンポーネント、Y2及びY3共通部品U2のV2。

写真.PNG

YUV 422Pフォーマット

また、I422と呼ばれるYUV 422Pフォーマットは、平面フォーマットは、記憶のために使用される、すべての以前に格納されたY成分、U成分、次いで格納すべて、次いで貯蔵成分Vの全て

ベースYUV 4:2:0サンプリングフォーマット

基于 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フ​​ォーマット

NV12和NV21格式

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

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

NV12フォーマット

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;
}

得到的效果图就是下图:

写真.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;
}

效果图如下:

写真.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;
}

写真.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;
}

写真.PNG

生成YUV420P格式的灰阶测试图

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

写真.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;  
}

写真.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;
}

写真.PNG

写真.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

カラードットマトリクスデータ

配列順に左から右へ上方自己に従って、全ての画素のビットマップ、。

RGBデータは、逆方向順序B、Gに生データを読み出して、Rが配置されています。

BMPファイルのプログラム構造

// 位图文件头
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は、次の手順にRGBを実現しました:

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;
}

写真.PNG

次のようにBMPファイルには、長い時間のために、生成されたが、調査はエラー(カーン-_- ||)、色は問題がある場合にRGBを交換することができる。このように表示されますが見つかりませんでしたが、考え方は間違って行きませんでした。その後、ここに穴を残して、それを修正する理由を見つけました......

画素データのRGB24フォーマットは、ピクセルデータ形式に変換され、YUV420P

次のように使用RGBとYUV変換式、ストレージがRGB24パックされたときに注意を払う必要が、ストレージパックプログラムのYUV420Pは以下のとおりです。

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;
}

生成されたYUVファイルのサイズが多くを削減されます。

写真.PNG

もちろん、それだけでイメージファイルとYUV420P RGB24フォーマットを必要とするので、フォーマットが同じでない場合は、コードを変更する必要があります。

おすすめ

転載: www.cnblogs.com/yunlambert/p/11234971.html