7

ビデオフレームを適切に処理するコードを書き込もうとしています。フレームをとして受け取っていSystem.Windows.Media.Imaging.WriteableBitmapます。テストの目的で、BGRA形式の画像を処理し、BGRピクセルの平均に基づいて各ピクセルを黒または白に割り当てる単純なしきい値フィルターを適用しています。

これが私の「安全な」バージョンです:

public static void ApplyFilter(WriteableBitmap Bitmap, byte Threshold)
{
    // Let's just make this work for this format
    if (Bitmap.Format != PixelFormats.Bgr24
        && Bitmap.Format != PixelFormats.Bgr32)
    {
        return;
    }

    // Calculate the number of bytes per pixel (should be 4 for this format). 
    var bytesPerPixel = (Bitmap.Format.BitsPerPixel + 7) / 8;

    // Stride is bytes per pixel times the number of pixels.
    // Stride is the byte width of a single rectangle row.
    var stride = Bitmap.PixelWidth * bytesPerPixel;

    // Create a byte array for a the entire size of bitmap.
    var arraySize = stride * Bitmap.PixelHeight;
    var pixelArray = new byte[arraySize];

    // Copy all pixels into the array
    Bitmap.CopyPixels(pixelArray, stride, 0);

    // Loop through array and change pixels to black/white based on threshold
    for (int i = 0; i < pixelArray.Length; i += bytesPerPixel)
    {
        // i=B, i+1=G, i+2=R, i+3=A
        var brightness =
               (byte)((pixelArray[i] + pixelArray[i+1] + pixelArray[i+2]) / 3);

        var toColor = byte.MinValue; // Black

        if (brightness >= Threshold)
        {
            toColor = byte.MaxValue; // White
        }

        pixelArray[i] = toColor;
        pixelArray[i + 1] = toColor;
        pixelArray[i + 2] = toColor;
    }
    Bitmap.WritePixels(
        new Int32Rect(0, 0, Bitmap.PixelWidth, Bitmap.PixelHeight),
        pixelArray, stride, 0
    );
}

これが、安全でないコードブロックとフォアバッファーの代わりにWriteableBitmapバックバッファーを使用した直接変換であると私が思うものです。

public static void ApplyFilterUnsafe(WriteableBitmap Bitmap, byte Threshold)
{
    // Let's just make this work for this format
    if (Bitmap.Format != PixelFormats.Bgr24
        && Bitmap.Format != PixelFormats.Bgr32)
    {
        return;
    }

    var bytesPerPixel = (Bitmap.Format.BitsPerPixel + 7) / 8;

    Bitmap.Lock();

    unsafe
    {
        // Get a pointer to the back buffer.
        byte* pBackBuffer = (byte*)Bitmap.BackBuffer;

        for (int i = 0;
             i < Bitmap.BackBufferStride*Bitmap.PixelHeight;
             i+= bytesPerPixel)
        {
            var pCopy = pBackBuffer;
            var brightness = (byte)((*pBackBuffer
                                     + *++pBackBuffer
                                     + *++pBackBuffer) / 3);
            pBackBuffer++;

            var toColor =
                    brightness >= Threshold ? byte.MaxValue : byte.MinValue;

            *pCopy = toColor;
            *++pCopy = toColor;
            *++pCopy = toColor;                    
        }
    }

    // Bitmap.AddDirtyRect(
    //           new Int32Rect(0,0, Bitmap.PixelWidth, Bitmap.PixelHeight));
    Bitmap.Unlock();

}

これは、安全でないコードブロックとポインターへの私の最初の進出であるため、ロジックが最適ではない可能性があります。

以下を使用して、同じWriteableBitmapsで両方のコードブロックをテストしました。

var threshold = Convert.ToByte(op.Result);
var copy2 = copyFrame.Clone();
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
BinaryFilter.ApplyFilterUnsafe(copyFrame, threshold);
stopWatch.Stop();

var unsafesecs = stopWatch.ElapsedMilliseconds;
stopWatch.Reset();
stopWatch.Start();
BinaryFilter.ApplyFilter(copy2, threshold);
stopWatch.Stop();
Debug.WriteLine(string.Format("Unsafe: {1}, Safe: {0}",
                stopWatch.ElapsedMilliseconds, unsafesecs));

だから私は同じ画像を分析しています。ビデオフレームの着信ストリームのテスト実行:

Unsafe: 110, Safe: 53
Unsafe: 136, Safe: 42
Unsafe: 106, Safe: 36
Unsafe: 95, Safe: 43
Unsafe: 98, Safe: 41
Unsafe: 88, Safe: 36
Unsafe: 129, Safe: 65
Unsafe: 100, Safe: 47
Unsafe: 112, Safe: 50
Unsafe: 91, Safe: 33
Unsafe: 118, Safe: 42
Unsafe: 103, Safe: 80
Unsafe: 104, Safe: 34
Unsafe: 101, Safe: 36
Unsafe: 154, Safe: 83
Unsafe: 134, Safe: 46
Unsafe: 113, Safe: 76
Unsafe: 117, Safe: 57
Unsafe: 90, Safe: 41
Unsafe: 156, Safe: 35

安全でないバージョンが常に遅いのはなぜですか?バックバッファを使用しているためですか?それとも私は何か間違ったことをしていますか?

ありがとう

4

3 に答える 3

9

安全でないバージョンが乗算とプロパティアクセスを実行していることが原因である可能性があります。

Bitmap.BackBufferStride*Bitmap.PixelHeight

ループの反復ごと。結果を変数に格納します。

于 2010-05-03T19:16:50.817 に答える
5

安全なコードまたは安全でないコードのいずれかでのもう1つの最適化:ループ内で3で割るのをやめます。ループの外側で、しきい値に3を1回掛けます。以外のタイプを使用する必要がありますがbyte、それは問題にはなりません。実際、あなたはすでにbyte:)よりも大きなデータ型を使用しています

于 2010-05-03T19:37:33.653 に答える
0

コードのプロファイリングなしで言うのは難しいです、特にコードは非常に異なっているので(最初は似ていますが)いくつかの重要なポイント(そしてそれらはすべて単なる推測です)

ifが安全ではない安全でないバージョンで計算された場合の停止条件

  • 配列pixelArrayのインデックスは、2回使用されていても、1回しか計算されない場合があります。
  • それらが「キャッシュ」されていない場合でも、(++ pとは対照的に)数値を格納せずに加算すると、さらに高速になります(命令が少なくなり、メモリアクセスが少なくなります)
  • 安全なバージョンでビットマップをロックしていません
  • pixelArray [i]、pixelArray [i + 1]、pixelArray [i + 2]はローカルに格納される可能性があり、ポインタを再度繰り返すよりも2回目のアクセスが高速になる可能性があります。
  • 安全でないコードに追加の割り当て(pCOpy = pBackBuffer)と追加の増分(pBackBuffer ++;)があります

それが私が思いつくことができるすべてのアイデアです。それが役に立てば幸い

于 2010-05-03T19:37:33.950 に答える