c# - 不安全边缘检测仍然很慢

标签 c# wpf image-processing

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

但是程序仍然需要大约 1600 毫秒来转换尺寸为 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

最佳答案

以下代码在字节数组而不是 WriteableBitmap 的 BackBuffer 上运行算法。在我的电脑上,它在不到 300 毫秒的时间内完成了 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);
}

关于c# - 不安全边缘检测仍然很慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52850744/

相关文章:

c# - 我可以使用 BasePage 中的 Page.IsPostBack 吗?

c# - 当其他动画正在播放时,如何防止我的动画启动?

c# - 反编译使用预编译工具生成的aspx页面

java - 从 Java 读取 .mat 文件

algorithm - 两组点之间的转换

javascript - 触发 asp :TextBox via Javascript 的 ontextchanged() 事件

c# - 如何保持 DataGrid 的一行或多行即使在排序后也始终位于顶部

WPF 字体缩放

opencv - 如何防止我的对象检测程序检测到多个不同大小的对象?

c# - 将背景设置为图像控件