为什么我的不安全代码阻塞比我的安全代码慢?

时间:2010-05-03 19:14:51

标签: c# unsafe writeablebitmap

我正在尝试编写一些能够方便地处理视频帧的代码。我收到的帧数为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 Back Buffer而不是forebuffer的直接翻译:

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

为什么我的不安全版本总是较慢?是因为使用了后台缓冲区吗?或者我做错了什么?

由于

3 个答案:

答案 0 :(得分:9)

可能是因为您的不安全版本正在进行乘法和属性访问:

Bitmap.BackBufferStride*Bitmap.PixelHeight

在每次循环迭代中。将结果存储在变量中。

答案 1 :(得分:5)

进一步优化,安全或不安全的代码: 在循环内停止除以3。在循环外将阈值乘以3。您需要使用byte以外的某种类型,但这应该不是问题。实际上,您已经使用了比byte更大的数据类型:)

答案 2 :(得分:0)

很难说没有对代码进行分析,特别是因为代码非常不同(虽然它起初看起来很相似)一些关键点(它们都只是推测)

如果在不安全版本中计算if而不在安全

中,则停止条件
  • 数组pixelArray的索引 可能只计算一次 虽然它们被使用了两次。
  • 即使他们没有“缓存”添加 数字一起没有存储 他们(而不是++ p)仍然会 更快(更少的指令和 内存访问量减少)
  • 你没有锁定位图 安全版
  • pixelArray [I],pixelArray第[i + 1],pixelArray第[i + 2] 可能存储在当地人制作 第二次访问它们 可能比迭代更快 再次指针。
  • 你有额外的任务 不安全的代码(pCOpy = pBackBuffer)和 额外增量(pBackBuffer ++;)

这就是我能提出的所有想法。希望它有所帮助