6

これは、私が見つけているよりも単純なはずです。

AVFoundation標準のデリゲート メソッドに戻ってくるフレームがあります。

- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
       fromConnection:(AVCaptureConnection *)connection

を使用してフレームをグレースケールに変換したい場所Accelerate.Framework

フレームワークには を含む変換メソッドのファミリがありvImageConvert_RGBA8888toPlanar8()、これは私が見たいもののように見えますが、それらの使用方法の例が見つかりません!

これまでのところ、私はコードを持っています:

- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
       fromConnection:(AVCaptureConnection *)connection
{

      @autoreleasepool {
            CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
            /*Lock the image buffer*/
            CVPixelBufferLockBaseAddress(imageBuffer,0);
            /*Get information about the image*/
            uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer);
            size_t width = CVPixelBufferGetWidth(imageBuffer);
            size_t height = CVPixelBufferGetHeight(imageBuffer);
            size_t stride = CVPixelBufferGetBytesPerRow(imageBuffer);

            // vImage In
            Pixel_8 *bitmap = (Pixel_8 *)malloc(width * height * sizeof(Pixel_8));
            const vImage_Buffer inImage = { bitmap, height, width, stride };

            //How can I take this inImage and convert it to greyscale?????
            //vImageConvert_RGBA8888toPlanar8()??? Is the correct starting format here??
      }    
}

2 つの質問があります: (1) 上記のコードでRBGA8888、正しい開始形式はありますか? Accelerate.Framework(2)実際にグレースケールに変換する呼び出しを行うにはどうすればよいですか?

4

5 に答える 5

5

ここにはもっと簡単なオプションがあります。カメラの取得形式を YUV に変更すると、グレースケール フレームが既に作成されており、好きなように使用できます。データ出力を設定するときは、次のようなものを使用します。

dataOutput.videoSettings = @{ (id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) };

次に、以下を使用して、キャプチャ コールバックで Y 平面にアクセスできます。

CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
uint8_t *yPlane = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);

... do stuff with your greyscale camera image ...

CVPixelBufferUnlockBaseAddress(pixelBuffer);
于 2014-01-19T11:10:30.747 に答える
4

vImage メソッドはvImageMatrixMultiply_Planar8、1x3 マトリックスを使用することです。 vImageConvert_RGBA8888toPlanar8RGBA8888 バッファーを 4 つの平面バッファーに変換するために使用する関数です。これらは によって使用されvImageMatrixMultiply_Planar8ます。 vImageMatrixMultiply_ARGB8888これも 1 回のパスで実行されますが、結果として灰色のチャネルが他の 3 つのチャネルとインターリーブされます。 vImageConvert_RGBA8888toPlanar8それ自体は計算を行いません。インターリーブされた画像を別々の画像平面に分離するだけです。

ガンマも調整する必要がある場合vImageConvert_AnyToAny()は、おそらく簡単な選択です。RGB 形式からグレースケール色空間への完全にカラー管理された変換を行います。vImage_Utilities.h を参照してください。

私はタークスの答えが好きです。輝度を手動で色管理しなければならないという立場に置かれるだけです (気になる場合)。

于 2014-01-21T00:33:54.000 に答える
3

Accelerate vImage を使用して BGRA イメージをグレースケールに変換する

vImageこのメソッドは、BGR イメージをグレースケールに変換する際に Accelerate を使用する方法を説明するためのものです。画像は RGBA 形式である可能性が高く、それに応じてマトリックスを調整する必要がありますが、カメラは BGRA を出力するため、ここではそれを使用しています。マトリックスの値は OpenCV でcvtColorに使用される値と同じですが、 luminosityのように操作できる他の値があります。結果に対して適切な量のメモリをmallocすると仮定します。グレースケールの場合、それは 1 チャネルまたは BGRA に使用されるメモリの 1/4 にすぎません。このコードに問題がある場合は、コメントを残してください。

パフォーマンスノート

この方法でのグレースケールへの変換は、最速ではない場合があります。環境内のメソッドのパフォーマンスを確認する必要があります。Brad Larson のGPUImageの方が速いか、OpenCV のcvtColor. いずれにせよ、中間バッファーの malloc と free の呼び出しを削除し、アプリのライフサイクルでそれらを管理する必要があります。そうしないと、関数呼び出しは malloc と free によって支配されます。Apple のドキュメントでは、可能であれば vImage_Buffer 全体を再利用することを推奨しています。

NEON 組み込み関数を使用して同じ問題を解決する方法についても読むことができます。

最後に、最速の方法はまったく変換しません。デバイス カメラから画像データを取得している場合、デバイス カメラはネイティブのkCVPixelFormatType_420YpCbCr8BiPlanarFullRange形式です。つまり、最初のプレーンのデータ (Y チャネル、輝度) を取得することが、グレースケールを取得する最速の方法です。

BGRA から グレースケール

- (void)convertBGRAFrame:(const CLPBasicVideoFrame &)bgraFrame toGrayscale:(CLPBasicVideoFrame &)grayscaleFrame
{
    vImage_Buffer bgraImageBuffer = {
        .width = bgraFrame.width,
        .height = bgraFrame.height,
        .rowBytes = bgraFrame.bytesPerRow,
        .data = bgraFrame.rawPixelData
    };

    void *intermediateBuffer = malloc(bgraFrame.totalBytes);
    vImage_Buffer intermediateImageBuffer = {
        .width = bgraFrame.width,
        .height = bgraFrame.height,
        .rowBytes = bgraFrame.bytesPerRow,
        .data = intermediateBuffer
    };

    int32_t divisor = 256;
//    int16_t a = (int16_t)roundf(1.0f * divisor);
    int16_t r = (int16_t)roundf(0.299f * divisor);
    int16_t g = (int16_t)roundf(0.587f * divisor);
    int16_t b = (int16_t)roundf(0.114f * divisor);
    const int16_t bgrToGray[4 * 4] = { b, 0, 0, 0,
                                       g, 0, 0, 0,
                                       r, 0, 0, 0,
                                       0, 0, 0, 0 };

    vImage_Error error;
    error = vImageMatrixMultiply_ARGB8888(&bgraImageBuffer, &intermediateImageBuffer, bgrToGray, divisor, NULL, NULL, kvImageNoFlags);
    if (error != kvImageNoError) {
        NSLog(@"%s, vImage error %zd", __PRETTY_FUNCTION__, error);
    }

    vImage_Buffer grayscaleImageBuffer = {
        .width = grayscaleFrame.width,
        .height = grayscaleFrame.height,
        .rowBytes = grayscaleFrame.bytesPerRow,
        .data = grayscaleFrame.rawPixelData
    };

    void *scratchBuffer = malloc(grayscaleFrame.totalBytes);
    vImage_Buffer scratchImageBuffer = {
        .width = grayscaleFrame.width,
        .height = grayscaleFrame.height,
        .rowBytes = grayscaleFrame.bytesPerRow,
        .data = scratchBuffer
    };

    error = vImageConvert_ARGB8888toPlanar8(&intermediateImageBuffer, &grayscaleImageBuffer, &scratchImageBuffer, &scratchImageBuffer, &scratchImageBuffer, kvImageNoFlags);
    if (error != kvImageNoError) {
        NSLog(@"%s, vImage error %zd", __PRETTY_FUNCTION__, error);
    }
    free(intermediateBuffer);
    free(scratchBuffer);
}

CLPBasicVideoFrame.h - 参照用

typedef struct
{
    size_t width;
    size_t height;
    size_t bytesPerRow;
    size_t totalBytes;
    unsigned long pixelFormat;
    void *rawPixelData;
} CLPBasicVideoFrame;

グレースケール変換はできたのですが、品質に問題があり、Web でInstant OpenCV for iOSという本を見つけました。私は個人的にコピーを手に入れましたが、コードは少し混乱していますが、多くの宝石があります. 明るい面では、非常に手頃な価格の電子書籍です。

私はそのマトリックスに非常に興味があります。私は何時間もそれをいじって、アレンジがどうあるべきかを理解しようとしました. 値は対角線上にあるはずだと思っていたでしょうが、Instant OpenCV の担当者は上記のように設定しました。

于 2015-02-24T09:52:48.663 に答える
0

(1) iOS カメラ フレームワークでの私の経験はkCMPixelFormat_32BGRA、ARGB8888 ファミリの関数と互換性のある形式の画像でした。(他の形式も使用できる場合があります。)

(2) iOS で BGR からグレースケールに変換する最も簡単な方法は、httpsvImageMatrixMultiply_ARGB8888ToPlanar8() : //developer.apple.com/documentation/accelerate/1546979-vimagematrixmultiply_argb8888top を使用することです。

これは、Swift で書かれたかなり完全な例です。Objective-C のコードも似ていると思います。

        guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
            // TODO: report error
            return
        }
        
        // Lock the image buffer
        if (kCVReturnSuccess != CVPixelBufferLockBaseAddress(imageBuffer, CVPixelBufferLockFlags.readOnly)) {
            // TODO: report error
            return
        }
        defer {
            CVPixelBufferUnlockBaseAddress(imageBuffer, CVPixelBufferLockFlags.readOnly)
        }
        
        // Create input vImage_Buffer
        let baseAddress = CVPixelBufferGetBaseAddress(imageBuffer)
        let width = CVPixelBufferGetWidth(imageBuffer)
        let height = CVPixelBufferGetHeight(imageBuffer)
        let stride = CVPixelBufferGetBytesPerRow(imageBuffer)
        var inImage = vImage_Buffer(data: baseAddress, height: UInt(height), width: UInt(width), rowBytes: stride)
        
        // Create output vImage_Buffer
        let bitmap = malloc(width * height)
        var outImage = vImage_Buffer(data: bitmap, height: UInt(height), width: UInt(width), rowBytes: width)
        defer {
            // Make sure to free unless the caller is responsible for this
            free(bitmap)
        }

        // Arbitrary divisor to scale coefficients to integer values
        let divisor: Int32 = 0x1000
        let fDivisor = Float(divisor)
        
        // Rec.709 coefficients
        var coefficientsMatrix = [
            Int16(0.0722 * fDivisor),  // blue
            Int16(0.7152 * fDivisor),  // green
            Int16(0.2126 * fDivisor),  // red
            0  // alpha
        ]

        // Convert to greyscale
        if (kvImageNoError != vImageMatrixMultiply_ARGB8888ToPlanar8(
            &inImage, &outImage, &coefficientsMatrix, divisor, nil, 0, vImage_Flags(kvImageNoFlags))) {
            // TODO: report error
            return
        }

上記のコードは、次のリンクにある Apple のグレースケール変換に関するチュートリアルに触発されたものです。必要に応じて if への変換も含まれCGImageます。BGR ではなく RGB 順序を想定しており、4 ではなく 3 つの係数しか提供していないことに注意してください (間違い?) https://developer.apple.com/documentation/accelerate/vimage/converting_color_images_to_grayscale

于 2021-10-12T21:02:19.850 に答える