2

(以下を更新...

10.8+ をターゲットとする OpenGL Cocoa/OSX デスクトップ ap

フレームごとに NSImage オブジェクトを受け取り、それを openGL テクスチャ (IPCamera からのビデオ フレーム) に変換したい状況があります。SO とインターネットから、NSImage を glTexture に変換するために見つけた最も役立つユーティリティ メソッドは、以下のコードです。

これは時折の (IE 読み込み) 状況では問題ありませんが、フレームごとに 1 回実行するとパフォーマンスが大幅に低下し、ひどいものになります。コードのプロファイルを作成し、ボトルネックを 2 つの呼び出しに絞り込みました。これらの呼び出しを合わせると、アプリケーション全体の実行時間のほぼ 3 分の 2 を占めます。

  1. ビットマップの作成

     NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithData:[inputImage TIFFRepresentation]];
    

    問題はおそらくビットマップを取得する方法にあると考えており、TIFFRepresentation に関するパフォーマンスに関して悪いことを聞いたので、代わりにこれを試しました。これは確かに高速ですが、少しだけであり、奇妙な色シフトも導入しました。 (すべてが赤く見えます):

    NSRect rect = NSMakeRect(0, 0, inputImage.size.width, inputImage.size.height);
    CGImageRef cgImage = [inputImage CGImageForProposedRect:&rect context:[NSGraphicsContext currentContext] hints:nil];
    NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithCGImage:cgImage];
    
  2. glTexImage2D 呼び出し (これを改善する方法がわかりません。)

以下の方法をより効率的にするにはどうすればよいですか?

または、私のやり方が間違っているので、他の場所を探すべきだと教えてください。フレームは MJPEG として入ってくるので、NSImage に変換される前に NSData を使用できる可能性があります。ただし、それは jpeg でエンコードされているので、それに対処する必要があります。また、実際には、アプリケーションの別の部分に NSImage オブジェクトが必要です。

-(void)loadNSImage:(NSImage*)inputImage intoTexture:(GLuint)glTexture{


    // If we are passed an empty image, just quit
    if (inputImage == nil){
        //NSLog(@"LOADTEXTUREFROMNSIMAGE: Error: you called me with an empty image!");
        return;
    }


    // We need to save and restore the pixel state
    [self GLpushPixelState];
    glEnable(GL_TEXTURE_2D);

    glBindTexture(GL_TEXTURE_2D, glTexture);


    //  Aquire and flip the data
    NSSize imageSize = inputImage.size;
    if (![inputImage isFlipped]) {
        NSImage *drawImage = [[NSImage alloc] initWithSize:imageSize];
        NSAffineTransform *transform = [NSAffineTransform transform];

        [drawImage lockFocus];

        [transform translateXBy:0 yBy:imageSize.height];
        [transform scaleXBy:1 yBy:-1];
        [transform concat];

        [inputImage drawAtPoint:NSZeroPoint
                                fromRect:(NSRect){NSZeroPoint, imageSize}
                               operation:NSCompositeCopy
                                fraction:1];

        [drawImage unlockFocus];

        inputImage = drawImage;
    }

    NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithData:[inputImage TIFFRepresentation]];


    //  Now make a texture out of the bitmap data
    // Set proper unpacking row length for bitmap.
    glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)[bitmap pixelsWide]);

    // Set byte aligned unpacking (needed for 3 byte per pixel bitmaps).
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    NSInteger samplesPerPixel = [bitmap samplesPerPixel];

    // Nonplanar, RGB 24 bit bitmap, or RGBA 32 bit bitmap.
    if(![bitmap isPlanar] && (samplesPerPixel == 3 || samplesPerPixel == 4)) {

        // Create one OpenGL texture
        // FIXME: Very slow
        glTexImage2D(GL_TEXTURE_2D, 0,
                     GL_RGBA,//samplesPerPixel == 4 ? GL_RGBA8 : GL_RGB8,
                     (GLint)[bitmap pixelsWide],
                     (GLint)[bitmap pixelsHigh],
                     0,
                     GL_RGBA,//samplesPerPixel == 4 ? GL_RGBA : GL_RGB,
                     GL_UNSIGNED_BYTE,
                     [bitmap bitmapData]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
    }else{
        [[NSException exceptionWithName:@"ImageFormat" reason:@"Unsupported image format" userInfo:nil] raise];
    }
    [self GLpopPixelState];
}

プロファイルのスクリーンショット:

ここに画像の説明を入力

アップデート

以下の Brad のコメントに基づいて、単純に NSImage をバイパスすることを検討することにしました。私の特定のケースでは、NSImage に変換される前に NSData オブジェクトとして JPG データにアクセスできたので、これはうまく機能しました。

  [NSBitmapImageRep imageRepWithData: imgData];

上記とほぼ同じ方法を使用しますが、ビットマップ表現から直接開始すると、CPU 使用率が 80% から 20% に低下し、速度に満足しています。私は自分のapの解決策を持っています。

元の質問に対する答えがあるかどうか、または避けるべきことのオブジェクトレッスンとしてこれを受け入れるのが最善かどうかを知りたい. 最後に、glTexImage2D 呼び出しの読み込み時間を改善できるかどうかまだ疑問に思っています。現在は妥当な範囲内ですが、メソッドの負荷の 99% を占めるようにプロファイルされています (ただし、おそらくそれで問題ありません)。

===========

4

1 に答える 1

2

データの受信方法に応じて、ピクセル バッファ オブジェクト (PBO) を使用できます。これは、テクスチャをアップロードするときに DMA を使用し (CPU を使用せずに)、memcpy を使用して (CPU を使用して) ランダム ポインタをコピーします。

Apple はここでこれを行う方法を説明しています: https://developer.apple.com/library/mac/documentation/graphicsimaging/conceptual/opengl-macprogguide/opengl_texturedata/opengl_texturedata.html

基本的に、PBO は OpenGL が DMA できるメモリのチャンクを提供するため、それを使用して解凍したフレームをコピーするメモリを割り当て、次にバッファをバインドし、nil をメモリ参照として glTexSubImage2D を呼び出して描画します。

これは、受信データを PBO に直接取り込めるかどうかに依存します。そうしないと、PBO に memcpy する必要があり、それがもたらす利点が失われます。

于 2014-04-01T07:15:48.330 に答える