12

NV12 形式の未加工の YUV ファイルを表示できません。

選択したフレームを表示できますが、まだ主に白黒で、ピンクと緑の特定の色合いがあります。

ここに私の出力がどのように見えるかがあります 代替テキスト

とにかく、これが私のプログラムの仕組みです。(これは cocoa/objective-c で行われますが、構文ではなく、プログラムのアルゴリズムに関する専門家のアドバイスが必要です。)

プログラムの実行前に、YUV ファイルは「test.yuv」という名前のバイナリ ファイルに保存されます。ファイルは NV12 形式です。つまり、Y プランが最初に保存され、次に UV プランがインターレースされます。多くのテストを行ったため、ファイルの抽出に問題はありません。

開始時に、バイナリ/8 バイト/バイト/文字を Y、U、V の float 値に変換するルックアップ テーブルを作成します。

Y平面の場合、これは私のコードです

-(void)createLookupTableY //creates a lookup table for converting a single byte into a float between 0 and 1
{
    NSLog(@"YUVFrame: createLookupTableY");
    lookupTableY = new float [256];
    
    for (int i = 0; i < 256; i++)
    {
        lookupTableY[i] = (float) i /255;
        //NSLog(@"lookupTableY[%d]: %f",i,lookupTableY[i]);//prints out the value of each float
    }
}

U プレーン ルックアップ テーブル

-(void)createLookupTableU //creates a lookup table for converting a single byte into a float between 0 and 1
{
    NSLog(@"YUVFrame: createLookupTableU");
    lookupTableU = new float [256];
    
    for (int i = 0; i < 256; i++)
    {
        lookupTableU[i] =  -0.436 + (float) i / 255* (0.436*2);
        NSLog(@"lookupTableU[%d]: %f",i,lookupTableU[i]);//prints out the value of each float
    }
}

そしてVルックアップテーブル

-(void)createLookupTableV //creates a lookup table for converting a single byte into a float between 0 and 1
{
    NSLog(@"YUVFrame: createLookupTableV");
    lookupTableV = new float [256];
    
    for (int i = 0; i < 256; i++)
    {
        lookupTableV[i] =  -0.615 + (float) i /255 * (0.615*2);
        NSLog(@"lookupTableV[%d]: %f",i,lookupTableV[i]);//prints out the value of each float
    }
}

この時点の後、Y および UV プランを抽出し、それらを yBuffer および uvBuffer の 2 つのバッファーに格納します。

この時点で、YUV データを変換して、「frameImage」という RGB バッファ配列に格納しようとしています。

-(void)sortAndConvert//sort the extracted frame data into an array of float
{
    NSLog(@"YUVFrame: sortAndConvert");
    int frameImageCounter = 0;
    int pixelCounter = 0;
    for (int y = 0; y < YUV_HEIGHT; y++)//traverse through the frame's height
    {
        for ( int x = 0; x < YUV_WIDTH; x++)//traverse through the frame's width
        {
            
            float Y = lookupTableY [yBuffer [y*YUV_WIDTH + x] ];
            float U = lookupTableU [uvBuffer [ ((y / 2) * (YUV_WIDTH / 2) + (x/2)) * 2  ] ]; 
            float V = lookupTableV [uvBuffer [  ((y / 2) * (YUV_WIDTH / 2) + (x/2)) * 2 + 1] ];
        
            float RFormula = Y + 1.13983f * V;
            float GFormula = Y - 0.39465f * U - 0.58060f * V;
            float BFormula = Y + 2.03211f * U;
            
             frameImage [frameImageCounter++] = [self clampValue:RFormula];
             frameImage [frameImageCounter++] = [self clampValue:GFormula];
             frameImage [frameImageCounter++] = [self clampValue:BFormula];

        }
    }

}

次に、OpenGLで画像を描画しようとしました

-(void)drawFrame:(int )x
{
    
    GLuint texture;
    
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, YUV_WIDTH, YUV_HEIGHT, 0, GL_RGB, GL_FLOAT, frameImage);
    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, texture);
    glRotatef( 180.0f, 1.0f, 0.0f, 0.0f );

    glBegin( GL_QUADS );
    glTexCoord2d(0.0,0.0); glVertex2d(-1.0,-1.0);
    glTexCoord2d(1.0,0.0); glVertex2d(+1.0,-1.0);
    glTexCoord2d(1.0,1.0); glVertex2d(+1.0,+1.0);
    glTexCoord2d(0.0,1.0); glVertex2d(-1.0,+1.0);
    glEnd();
    
    glFlush();
}

基本的に、これはナットシェルの私のプログラムです。基本的に、バイナリ YUV ファイルを読み取り、すべてのデータを char 配列バッファーに格納します。次に、これらの値を対応する YUV float 値に変換します。

ここでエラーが発生する可能性があります。Y ルックアップ テーブルでは Y 平面を [0,1] に標準化し、U 平面では [-0.435,0.436] の間の値を標準化し、V 平面では標準化しました。 [-0.615,0.615] の間です。ウィキペディアによると、これらはYUV値の範囲であるため、これを行いました。

また、YUV から RGB への式は、ウィキペディアと同じ式です。他にもいろいろな計算式を試しましたが、これがフレームの大まかな概観を示す唯一の計算式です。私のプログラムが YUV フレーム データを正しく表示しない理由を推測する人もいるかもしれません。私の標準化技術と関係があると思いますが、私には問題ないようです。

私は多くのテストを行いましたが、ルックアップ テーブルが原因でエラーが発生していることを 100% 確信しています。私の設定式は正しくないと思います。

これを読んでいるすべての人への注意。フレーム データを正しく抽出できなかったため、長い間、フレームが正しく表示されませんでした。

私が最初にプログラミングを始めたとき、たとえば 30 フレームのクリップでは、30 の Y 平面データすべてが最初にデータ ファイルに書き込まれ、次に UV 平面データが続くという印象を受けました。

試行錯誤して分かったのは、YUV データ ファイル、具体的には NV12 の場合、データ ファイルは次のように格納されるということでした。

Y(1) UV(1) Y(2) UV(2) ... ...

@nschmidt

コードをあなたが提案したものに変更しました

float U = lookupTableU [uvBuffer [ (y * (YUV_WIDTH / 4) + (x/4)) * 2 ] ]
float V = lookupTableU [uvBuffer [ (y * (YUV_WIDTH / 4) + (x/4)) * 2 + 1] ]

これが私が得た結果です 代替テキスト

ここにコンソールからの印刷行があります。変換されてframeImage配列に格納されているY、U、V、およびRGB値の値を出力しています

YUV:[0.658824,-0.022227,-0.045824] RGBFinal:[0.606593,0.694201,0.613655]

YUV:[0.643137,-0.022227,-0.045824] RGBFinal:[0.590906,0.678514,0.597969]

YUV:[0.607843,-0.022227,-0.045824] RGBFinal:[0.555612,0.643220,0.562675]

YUV:[0.592157,-0.022227,-0.045824] RGBFinal:[0.539926,0.627534,0.546988]

YUV:[0.643137,0.025647,0.151941] RGBFinal:[0.816324,0.544799,0.695255]

YUV:[0.662745,0.025647,0.151941] RGBFinal:[0.835932,0.564406,0.714863]

YUV:[0.690196,0.025647,0.151941] RGBFinal:[0.863383,0.591857,0.742314]

2009 年 7 月 13 日更新

nschmidt さんからのアドバイスのおかげで、問題は最終的に解決されました。私の YUV ファイルは実際には YUV 4:1:1 形式であることがわかりました。当初、YUV NV12 形式であると言われました。とにかく、私の結果をあなたと共有したいと思います。

ここに出力があります

代替テキスト

デコード用の私のコードは次のとおりです

        float Y = (float) yBuffer [y*YUV_WIDTH + x] ;
        float U = (float) uvBuffer [ ((y / 2) * (YUV_WIDTH  / 2) + (x/2))   ] ; 
        float V = (float) uvBuffer [  ((y / 2) * (YUV_WIDTH  / 2) + (x/2))  + UOffset] ;

        float RFormula = (1.164*(Y-16) + (1.596* (V - 128) ));
        float GFormula = ((1.164 * (Y - 16)) - (0.813 * ((V) - 128)) - (0.391 * ((U) - 128)));
        float BFormula = ((1.164 * (Y - 16)) + (2.018 * ((U) - 128)));


        frameImage [frameImageCounter] = (unsigned char)( (int)[self clampValue:RFormula]);
        frameImageCounter ++;
        frameImage [frameImageCounter] =  (unsigned char)((int)[self clampValue:GFormula]);
        frameImageCounter++;
        frameImage [frameImageCounter] = (unsigned char)((int) [self clampValue:BFormula]);
        frameImageCounter++;



GLuint texture;

glGenTextures(1, &texture);
glEnable(GL_TEXTURE_2D);

glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, YUV_WIDTH, YUV_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, frameImage);

//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE_SGIS);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE_SGIS);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);

glRotatef( 180.0f, 1.0f, 0.0f, 0.0f );

glBegin( GL_QUADS );
glTexCoord2d(0.0,0.0); glVertex2d(-1.0,-1.0);
glTexCoord2d(1.0,0.0); glVertex2d(+1.0,-1.0);
glTexCoord2d(1.0,1.0); glVertex2d(+1.0,+1.0);
glTexCoord2d(0.0,1.0); glVertex2d(-1.0,+1.0);
glEnd();

NSLog(@"YUVFrameView: drawRect complete");
glFlush();

基本的に、生ファイルの抽出には NSData を使用しました。文字配列バッファに格納されます。YUV から RGB への変換には、上記の式を使用し、その後、値を [0:255] にクランプしました。次に、表示用に OpenGL で 2DTexture を使用しました。

したがって、将来 YUV を RGB に変換する場合は、上記の式を使用してください。以前の投稿の YUV から RGB への変換式を使用している場合は、[0:1] の間でクランプされる RGB の値から GL_Float にテクスチャを表示する必要があります。

4

3 に答える 3

8

次に試してみてください:)あなたはUVバッファがインターリーブされていないと思います。U値が最初に来て、次にV値の配列が来るように見えます。行をに変更する

unsigned int voffset = YUV_HEIGHT * YUV_WIDTH / 2;
float U = lookupTableU [uvBuffer [ y * (YUV_WIDTH / 2) + x/2] ]; 
float V = lookupTableV [uvBuffer [ voffset + y * (YUV_WIDTH / 2) + x/2] ];

これが実際に当てはまるかどうかを示す場合があります。

于 2009-07-11T12:12:56.843 に答える
3

UとVの値を間違って扱っていると思います。それよりも:

float U = lookupTableU [uvBuffer [ ((y / 2) * (x / 2) + (x/2)) * 2  ] ]; 
float V = lookupTableV [uvBuffer [  ((y / 2) * (x / 2) + (x/2)) * 2 + 1] ];

それはの線に沿ったものでなければなりません

float U = lookupTableU [uvBuffer [ ((y / 2) * (YUV_WIDTH / 2) + (x/2)) * 2  ] ]; 
float V = lookupTableV [uvBuffer [  ((y / 2) * (YUV_WIDTH / 2) + (x/2)) * 2 + 1] ];
于 2009-07-10T11:47:01.620 に答える
0

写真は 4:1:1 形式のようです。行を次のように変更する必要があります

float U = lookupTableU [uvBuffer [ (y * (YUV_WIDTH / 4) + (x/4)) * 2 ] ]
float V = lookupTableU [uvBuffer [ (y * (YUV_WIDTH / 4) + (x/4)) * 2 + 1] ]

たぶん、結果を投稿して、他に何が間違っているかを確認できます。それについて考えるのはいつも難しいと思います。これに繰り返しアプローチする方がはるかに簡単です。

于 2009-07-10T16:17:10.280 に答える