6

stackoverflow で見つけたコードをコピーして貼り付け、デフォルトのカメラ プレビュー YUV を RGB 形式に変換し、それを OpenGL にアップロードして処理しました。問題は、YUV 画像を RGB 形式に変換する際に CPU のほとんどがビジー状態になり、ボトルネックになったことです。

YUV 画像を GPU にアップロードしてから、フラグメント シェーダーで RGB に変換したいと考えています。CPUで動作するJava YUV to RGB関数を見つけて、GPUで動作させようとしました。

Java と GPU での計算の実行にはいくつかの違いがあるため、これはかなりの悪夢になりました。まず、Java ではプレビュー画像は byte[] で表示されますが、バイトは署名されているため、負の値が含まれている可能性があります。

さらに、フラグメント シェーダーは通常、バイトの代わりに [0..1] の浮動値を処理します。

これは解決可能であると確信しており、ほとんど解決しました。しかし、何が間違っているのかを理解するのに数時間を費やしましたが、うまくいきませんでした。

結論として、誰かにこのシェーダー関数を書いて、できればテストしてもらいたいと思います。私にとっては、この変換がこのように機能する理由がよくわからないため、退屈なサルの仕事であり、GPU で同じ機能を模倣しようとしています。

これは、私が Java で使用したものと非常によく似た関数です: Displaying YUV Image in Android

次のように、1.5*w h バイトの YUV 形式を aw h*YUV に変換するなど、CPU でいくつかの作業を行いました。

static public void decodeYUV420SP(int[] rgba, byte[] yuv420sp, int width,
        int height) {
    final int frameSize = width * height;

    for (int j = 0, yp = 0; j < height; j++) {
        int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
        for (int i = 0; i < width; i++, yp++) {
            int y = (int) yuv420sp[yp]+127;
            if ((i & 1) == 0) {
                v = (int)yuv420sp[uvp++]+127;
                u = (int)yuv420sp[uvp++]+127;
            }
            rgba[yp] = 0xFF000000+(y<<16) | (u<<8) | v;
        }
    }
}

バイトが署名されているため、127 を追加しました。次に、rgba を OpenGL テクスチャにロードし、残りの計算を GPU で実行しようとしました。

どんな助けでも感謝されます...

4

3 に答える 3

1

CPU での変換は簡単に聞こえますが、問題は GPU での変換方法だと思います。

私は最近、コードが印刷されている表面に対してカメラの角度が 45 度の場合でも、非常に高速な QR コード検出を取得する必要がある私のプロジェクトでそれを行いましたが、優れたパフォーマンスで動作しました。

(以下のコードは重要な行を含むようにトリミングされています。Java と OpenGLES の両方を十分に理解していることを前提としています)

  1. 保存されたカメラ イメージを含む GL テクスチャを作成します。

    int[] txt = new int[1];
    GLES20.glGenTextures(1,txt,0);
    GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,txt[0]);
    GLES20.glTextParameterf(... set min filter to GL_LINEAR );
    GLES20.glTextParameterf(... set mag filter to GL_LINEAR );
    GLES20.glTextParameteri(... set wrap_s to GL_CLAMP_TO_EDGE );
    GLES20.glTextParameteri(... set wrap_t to GL_CLAMP_TO_EDGE );
    

テクスチャ タイプが GL_TEXTURE_2D ではないことに注意してください。次のステップで使用される SurfaceTexture オブジェクトでサポートされるのは GL_TEXTURE_EXTERNAL_OES タイプのみであるため、これは重要です。

  1. SurfaceTexture のセットアップ:

    SurfaceTexture surfTex = new SurfaceTeture(txt[0]);
    surfTex.setOnFrameAvailableListener(this); 
    

上記では、「this」が「onFrameAvailable」機能を実装するオブジェクトであると想定しています。

    public void onFrameAvailable(SurfaceTexture st)
    {
            surfTexNeedUpdate = true;
            // this flag will be read in GL render pipeline
    }
  1. カメラのセットアップ:

    Camera cam = Camera.open();
    cam.setPreviewTexture(surfTex);
    

Android 5.0 を対象とする場合、この Camera API は廃止されるため、そうである場合は、新しい CameraDevice API を使用する必要があります。

  1. レンダリング パイプラインで、次のブロックを使用して、カメラに使用可能なフレームがあるかどうかを確認し、それを使用して表面テクスチャを更新します。表面テクスチャが更新されると、それにリンクされている GL テクスチャが塗りつぶされます。

    if( surfTexNeedUpdate )
    {
            surfTex.updateTexImage();
            surfTexNeedUpdate = false;
    }
    
  2. Camera -> SurfaceTeture リンクを持つ GL テクスチャをバインドするには、レンダリング パイプで次のようにします。

    GLES20.glBindTexture(GLES20.GL_TEXTURE_EXTERNAL_OS, txt[0]);
    

言うまでもなく、現在アクティブなテクスチャを設定する必要があります。

  1. フラグメント部分で上記のテクスチャを使用する GL シェーダー プログラムでは、最初の行が必要です。

    #extension GL_OES_EGL_imiage_external : require
    

上は必携です。

テクスチャ ユニフォームは samplerExternalOES タイプである必要があります:

    uniform samplerExternalOES u_Texture0;

そこからピクセルを読み取るのは GL_TEXTURE_2D タイプと同じで、UV 座標は同じ範囲 (0.0 から 1.0) にあります。

    vec4 px = texture2D(u_Texture0, v_UV);
  1. 上記のテクスチャとシェーダーを使用してクワッドをレンダリングするレンダリング パイプラインの準備が整ったら、カメラを起動します。

    cam.startPreview();
    
  2. GL 画面にクワッドが表示され、ライブ カメラ フィードが表示されます。glReadPixels で画像を取得する必要があります。

    GLES20.glReadPixels(0,0,width,height,GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, bytes);
    

上記の行は、FBO が RGBA であり、バイトが既に適切なサイズに初期化されている byte[] 配列であり、幅と高さが FBO のサイズであることを前提としています。

そして出来上がり!onPreviewFrame コールバックで受信した YUV バイトを変換する代わりに、カメラから RGBA ピクセルをキャプチャしました...

RGB フレームバッファ オブジェクトを使用して、必要がなければアルファを避けることもできます。

カメラは、GL レンダー パイプライン スレッドではない独自のスレッドで onFrameAvailable を呼び出すため、その関数で GL 呼び出しを実行しないでください。

于 2015-10-23T06:41:12.003 に答える
1

ウィキペディアのこのコードを使用して、GPU での YUV から RGB への変換を計算しました。

private static int convertYUVtoRGB(int y, int u, int v) {
    int r,g,b;

    r = y + (int)1.402f*v;
    g = y - (int)(0.344f*u +0.714f*v);
    b = y + (int)1.772f*u;
    r = r>255? 255 : r<0 ? 0 : r;
    g = g>255? 255 : g<0 ? 0 : g;
    b = b>255? 255 : b<0 ? 0 : b;
    return 0xff000000 | (b<<16) | (g<<8) | r;
}

float を 0.0..255.0 に変換してから、上記のコードを使用します。CPU の一部は、元の YUV ピクセルを YUV マトリックスに再配置することでした (wikipdia にも示されています)。基本的に、私はウィキペディアのコードを使用し、最も単純な float<->byte conersions を実行してそれを機能させました。Y に 16 を追加したり、U と V に 128 を追加しないなどの小さな間違いは、望ましくない結果をもたらします。だからあなたはそれの世話をする必要があります。しかし、ウィキペディアのコードをベースとして使用すると、たいした作業ではありませんでした。

于 2013-01-03T21:28:00.700 に答える