如何更快地对图片进行二值化

How to binarize pictures faster

我目前正在尝试对图像进行二值化,以下是我使用的代码:

private static Bitmap PBinary(Bitmap src, int v)
{
    int w = src.Width;
    int h = src.Height;

    Bitmap dstBitmap = new Bitmap(src.Width, src.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
    System.Drawing.Imaging.BitmapData srcData = src.LockBits(new Rectangle(0, 0, w, h), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
    System.Drawing.Imaging.BitmapData dstData = dstBitmap.LockBits(new Rectangle(0, 0, w, h), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
    unsafe
    {
        byte* pIn = (byte*)srcData.Scan0.ToPointer();
        byte* pOut = (byte*)dstData.Scan0.ToPointer();
        byte* p;
        int stride = srcData.Stride;
        int r, g, b;
        for (int y = 0; y < h; y++)
        {
            for (int x = 0; x < w; x++)
            {
                p = pIn;
                r = p[2];
                g = p[1];
                b = p[0];
                pOut[0] = pOut[1] = pOut[2] = (byte)(((byte)(0.2125 * r + 0.7154 * g + 0.0721 * b) >= v) ? 255 : 0);
                pIn += 3;
                pOut += 3;
            }
            pIn += srcData.Stride - w * 3;
            pOut += srcData.Stride - w * 3;
        }
        src.UnlockBits(srcData);
        dstBitmap.UnlockBits(dstData);
        return dstBitmap;
    }
}

我可以成功将图片二值化,但是当我添加一个滑块动态调整参数时,我发现图片显示的时候很卡

    private void BinarySlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        if (originalImg!=null)
        {
            BinaryPara = (int)e.NewValue;

            PreviewImg = PBinary(originalImg, BinaryPara);

            this.Dispatcher.Invoke(() =>
            {
                img.Source = null;

                img.Source = BitmapToBitmapSource(PreviewImg);

            });

        }

    }

    [System.Runtime.InteropServices.DllImport("gdi32.dll")]
    public static extern bool DeleteObject(IntPtr hObject);

    public static BitmapSource BitmapToBitmapSource(Bitmap bitmap)
    {
        IntPtr hBitmap = bitmap.GetHbitmap();

        BitmapSource retval;

        try
        {
            retval = Imaging.CreateBitmapSourceFromHBitmap(
                         hBitmap,
                         IntPtr.Zero,
                         Int32Rect.Empty,
                         BitmapSizeOptions.FromEmptyOptions());
        }
        finally
        {
            DeleteObject(hBitmap);
        }

        return retval;

    }

再次尝试使用opencvsharp,发现使用opencvsharp动态调整二值化的参数,画面显示非常流畅,没有卡顿。

但是目前我不想使用第三方库。有什么方法可以更快地二值化图像吗?

谢谢大家!

这可能不完全您所需要的,但它应该让您基本了解如何从相同的、未修改的原始像素缓冲区创建不同的二值化位图。

第一步,它从原始 BitmapSource 创建一个每像素 8 位的灰度 BitmapSource,并将其缓冲区复制到一个字节数组。缓冲区中的每个字节都是 0 到 255 范围内的灰度值。诀窍是将其重新用作具有 256 个条目的调色板的索引。

现在每次阈值(即范围为 0 到 255 的滑块的值)发生变化时,都会创建一个包含 256 个条目的调色板,但它只包含 Black 低于阈值的索引, White以上指数。

private int pixelWidth;
private int pixelHeight;
private byte[] pixelBuffer;

public MainWindow()
{
    var source = new BitmapImage(...);

    // create grayscale bitmap from original

    var grayscale = new FormatConvertedBitmap(
        source, PixelFormats.Gray8, null, 0d);

    pixelWidth = grayscale.PixelWidth;
    pixelHeight = grayscale.PixelHeight;
    pixelBuffer = new byte[pixelWidth * pixelHeight];

    // get raw pixel buffer

    grayscale.CopyPixels(pixelBuffer, pixelWidth, 0);

    InitializeComponent();
}

private void UpdateImage(byte threshold)
{
    var colors = new Color[256];

    for (int i = 0; i < threshold; i++)
    {
        colors[i] = Colors.Black;
    }

    for (int i = threshold; i < 256; i++)
    {
        colors[i] = Colors.White;
    }

    // set the Source of an Image element declared in XAML
    // pixelBuffer is now used as an array of indices into a color palette

    image.Source = BitmapSource.Create(
        pixelWidth, pixelHeight, 96, 96,
        PixelFormats.Indexed8, new BitmapPalette(colors),
        pixelBuffer, pixelWidth);
}

private void Slider_ValueChanged(
    object sender, RoutedPropertyChangedEventArgs<double> e)
{
    UpdateImage((byte)e.NewValue);
}