不安全边缘检测仍然很慢

时间:2018-10-17 08:46:22

标签: c# wpf image-processing

我正在尝试在WPF程序中实现图像边缘检测。 我已经可以使用它了,但是图像的转换非常慢。 该代码未使用慢速的GetPixel和SetPixel函数。但是,相反,我使用一些不安全的代码遍历了图像,以便可以使用指针直接访问该值。 在开始边缘检测之前,我还将图像转换为灰度图像,以提高边缘检测速度。

但是程序仍然需要大约1600ms的时间来转换大小为1920x1440像素的图像,我认为这可能会更快。

这是原始图像: Original Image

将转换为以下形式(应用程序的快照): enter image description here

这就是我转换图像的方式,我想知道如何才能实现其他一些速度改进?

加载图像并创建一个灰度WriteableBitmap:

    private void imageData_Loaded(object sender, RoutedEventArgs e)
    {
        if (imageData.Source != null)
        {
            BitmapSource BitmapSrc = new FormatConvertedBitmap(imageData.Source as BitmapSource, PixelFormats.Gray8 /* Convert to greyscale image */, null, 0);
            writeableOriginalBitmap = new WriteableBitmap(BitmapSrc);
            writeableBitmap = writeableOriginalBitmap.Clone();
            imageData.Source = writeableBitmap;
            EdgeDetection();
        }
    }

转换图像:

    private const int TOLERANCE = 20;

    private void EdgeDetection()
    {
        DateTime startTime = DateTime.Now;   //Save starting time
        writeableOriginalBitmap.Lock();
        writeableBitmap.Lock();
        unsafe
        {
            byte* pBuffer         = (byte*)writeableBitmap.BackBuffer.ToPointer();
            byte* pOriginalBuffer = (byte*)writeableOriginalBitmap.BackBuffer.ToPointer();

            for (int row = 0; row < writeableOriginalBitmap.PixelHeight; row++)
            {
                for (int column = 0; column < writeableOriginalBitmap.PixelWidth; column++)
                {
                    byte edgeColor = getEdgeColor(column, row, pOriginalBuffer); //Get pixel color based on edge value
                    pBuffer[column + (row * writeableBitmap.BackBufferStride)] = (byte)(255 - edgeColor);
                }
            }
        }

        //Refresh image
        writeableBitmap.AddDirtyRect(new Int32Rect(0, 0, writeableBitmap.PixelWidth, writeableBitmap.PixelHeight));
        writeableBitmap.Unlock();
        writeableOriginalBitmap.Unlock();

        //Calculate converting time
        TimeSpan diff = DateTime.Now - startTime;
        Debug.WriteLine("Loading Time: " + (int)diff.TotalMilliseconds);
    }

    private unsafe byte getEdgeColor(int xPos, int yPos, byte* pOriginalBuffer)
    {
        byte Color;
        byte maxColor = 0;
        byte minColor = 255;
        int difference;

        //Calculate max and min value of surrounding pixels
        for (int y = yPos - 1; y <= yPos + 1; y++)
        {
            for (int x = xPos - 1; x <= xPos + 1; x++)
            {
                if (x >= 0 && x < writeableOriginalBitmap.PixelWidth && y >= 0 && y < writeableOriginalBitmap.PixelHeight)
                {
                    Color = pOriginalBuffer[x + (y * writeableOriginalBitmap.BackBufferStride)];
                    if (Color > maxColor)            //If current pixel has higher value as previous max pixel
                        maxColor = Color;            //Save current pixel value as max
                    if (Color < minColor)            //If current pixel has lower value as previous min pixel
                        minColor = Color;            //Save current pixel value as min
                }
            }
        }

        //Difference of minimum and maximum pixel with tollerance
        difference = maxColor - minColor - TOLERANCE;
        if (difference < 0)
            difference = 0;

        return (byte)difference;
    }

控制台输出:

Loading Time: 1599

1 个答案:

答案 0 :(得分:4)

以下代码在字节数组而不是WriteableBitmap的BackBuffer上运行算法。它可以在不到300毫秒的时间内完成,并在我的PC上显示为1900x1200的图像。

private static BitmapSource EdgeDetection(BitmapSource source)
{
    var stopwatch = Stopwatch.StartNew();
    var bitmap = new FormatConvertedBitmap(source, PixelFormats.Gray8, null, 0);
    var width = bitmap.PixelWidth;
    var height = bitmap.PixelHeight;
    var originalBuffer = new byte[width * height];
    var buffer = new byte[width * height];

    bitmap.CopyPixels(originalBuffer, width, 0);

    for (var y = 0; y < height; y++)
    {
        for (var x = 0; x < width; x++)
        {
            byte edgeColor = GetEdgeColor(originalBuffer, width, height, x, y);
            buffer[width * y + x] = (byte)(255 - edgeColor);
        }
    }

    Debug.WriteLine(stopwatch.ElapsedMilliseconds);

    return BitmapSource.Create(
        width, height, 96, 96, PixelFormats.Gray8, null, buffer, width);
}

private static byte GetEdgeColor(byte[] buffer, int width, int height, int x, int y)
{
    const int tolerance = 20;
    byte minColor = 255;
    byte maxColor = 0;
    var xStart = Math.Max(0, x - 1);
    var xEnd = Math.Min(width - 1, x + 1);
    var yStart = Math.Max(0, y - 1);
    var yEnd = Math.Min(height - 1, y + 1);

    for (var j = yStart; j <= yEnd; j++)
    {
        for (var i = xStart; i <= xEnd; i++)
        {
            var color = buffer[width * j + i];
            minColor = Math.Min(minColor, color);
            maxColor = Math.Max(maxColor, color);
        }
    }

    return (byte)Math.Max(0, maxColor - minColor - tolerance);
}