【问题标题】:BitmapSource vs Bitmap位图源与位图
【发布时间】:2014-09-13 04:34:51
【问题描述】:

7 个月前,我们开始学习 C# 和 WPF,作为所有想要做一些图像处理的新手,我们遇到了这个问题:

为什么会有 Bitmap 和 BitmapSource ?各有什么优势?

在我们的项目中,我们必须从数据中生成位图。速度对我们来说非常重要。

我们从 Bitmap 开始,因为它更容易(特别是方法:get/setpixel),并且有很多示例。但后来我们发现了 WPF 中打印位图的转换问题。

所以我们尝试使用 BitmapSource,由于像素格式不同,这并不容易。但我们最终成功了。

我们比较了每一代的速度。使用 SetPixel (Bitmap) 比使用字节数组 (BitmapSource) 慢得多,但使用字节数组意味着复杂性:步幅、像素格式 ...

所以我们肯定选择了 BitmapSource。但后来我们想序列化一些 BitmapSource。 BitmapSource 不可序列化。所以通过[OnSerializing] [OnDeserialized]我们将BitmapSource转换成Bitmap(可序列化)。

我们的结论是:

位图优势:

  1. 简单
  2. 可序列化

BitmapSource 优势:

  1. 生成速度
  2. WPF 的继承 (ImageSource)

你看到其他几点了吗?

为了说明和像我们这样的新手,这里有一些我们需要的有用方法:

转化率:

public static System.Windows.Media.Imaging.BitmapSource BitmapToBitmapSource(System.Drawing.Bitmap source)
{
    using (MemoryStream memory = new MemoryStream())
    {
        source.Save(memory, ImageFormat.Png);
        memory.Position = 0;
        BitmapImage bitmapImage = new BitmapImage();
        bitmapImage.BeginInit();
        bitmapImage.StreamSource = memory;
        bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
        bitmapImage.EndInit();
        return bitmapImage;
    }
}

public static System.Drawing.Bitmap BitmapFromSource(BitmapSource source)
{
    using (MemoryStream outStream = new MemoryStream())
    {
        BitmapEncoder enc = new PngBitmapEncoder();
        enc.Frames.Add(BitmapFrame.Create(source));
        enc.Save(outStream);
        System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(outStream);

        // return bitmap; <-- leads to problems, stream is closed/closing ...
        return new Bitmap(bitmap);
    }
}

无锁图片打开:

    public static BitmapImage LoadImage(string uri)
    {
        BitmapImage monImage = null;
        if (uri != null)
        {
            BitmapImage image = new BitmapImage();
            using (FileStream stream = File.OpenRead(uri))
            {
                image.BeginInit();
                image.CacheOption = BitmapCacheOption.OnLoad;
                image.StreamSource = stream;
                image.EndInit();
            }
            monImage = image;
        }
        return monImage;
    }

调整 BitmapSource 大小:

    public static BitmapImage BitmapImageFromBitmapSourceResized(BitmapSource bitmapSource, int newWidth)
    {
        BmpBitmapEncoder encoder = new BmpBitmapEncoder();
        MemoryStream memoryStream = new MemoryStream();
        BitmapImage bImg = new BitmapImage();

        encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
        encoder.Save(memoryStream);

        bImg.BeginInit();
        bImg.StreamSource = new MemoryStream(memoryStream.ToArray());
        bImg.DecodePixelWidth = newWidth;
        bImg.EndInit();
        memoryStream.Close();
        return bImg;
    }

世代:

    public static int GetBytesPerPixel(BitmapSource bmp)
    {
        return (bmp.Format.BitsPerPixel + 7) / 8;
    }

    public static int GetStrideFromeBitmapSource(BitmapSource bmp)
    {
        return 4 * ((bmp.PixelWidth * GetBytesPerPixel(bmp) + 3) / 4);
    }

    public static byte[] GetBytesFromBitmapSource(BitmapSource bmp)
    {
        int height = bmp.PixelHeight;
        int stride = GetStrideFromeBitmapSource(bmp);

        byte[] pixels = new byte[height * stride];

        bmp.CopyPixels(pixels, stride, 0);

        return pixels;
    }

    public static int GetWidth(int stride, int bytesPerPixel)
    {
        int width = (int)(
                            (float)stride
                            / (float)bytesPerPixel
                        );
        return width;
    }

    public static int GetHeight(byte[] bits, int stride)
    {
        int height = (int)(
                            (float)bits.Length
                            / (float)stride
                        );
        return height;
    }

    public static void SetPixelRgb24(ref byte[] bits, int x, int y, int stride, Color c)
    {
        bits[x * 3 + y * stride] = c.R;
        bits[x * 3 + y * stride + 1] = c.G;
        bits[x * 3 + y * stride + 2] = c.B;
    }

    public static void SetPixelBgra32(ref byte[] bits, int x, int y, int stride, Couleur c)
    {
        bits[x * 4 + y * stride + 0] = c.B;
        bits[x * 4 + y * stride + 1] = c.G;
        bits[x * 4 + y * stride + 2] = c.R;
        bits[x * 4 + y * stride + 3] = c.A;
    }

    public static int GetAverageValueOfPixel(ref byte[] bits, int x, int y, int stride, int bytesPerPixel)
    {
        int sum = 0;
        for (var i = 0; i < bytesPerPixel; i++)
            sum += bits[x * bytesPerPixel + y * stride + i];
        return (int)
            (
                sum
                * (255f / (255f * bytesPerPixel))
            );
    }

到 BitmapSource 的快照:

    public static BitmapSource SnapShotToBitmap(this UIElement source, double zoomX, double zoomY)
    {
        try
        {
            DataObject dataObject = new DataObject();

            double actualHeight = source.RenderSize.Height;
            double actualWidth = source.RenderSize.Width;

            if (actualHeight == 0)
                actualHeight = 1;
            if (actualWidth == 0)
                actualWidth = 1;

            double renderHeight = actualHeight * zoomY;
            double renderWidth = actualWidth * zoomX;

            RenderTargetBitmap renderTarget = new RenderTargetBitmap((int)renderWidth, (int)renderHeight, 96, 96, PixelFormats.Pbgra32);
            VisualBrush sourceBrush = new VisualBrush(source);

            DrawingVisual drawingVisual = new DrawingVisual();
            DrawingContext drawingContext = drawingVisual.RenderOpen();

            using (drawingContext)
            {
                drawingContext.PushTransform(new ScaleTransform(zoomX, zoomY));
                drawingContext.DrawRectangle(sourceBrush, null, new Rect(new Point(0, 0), new Point(actualWidth, actualHeight)));
            }
            renderTarget.Render(drawingVisual);

            return renderTarget;
        }
        catch (Exception e)
        {
            throw new Exception(e);
        }
    }

【问题讨论】:

  • 主要区别在 MSDN 上的Imaging Overview 文章中进行了说明。
  • 上面的代码中有很多从简单到严重的错误和问题,因此任何人将其作为示例代码阅读,请谨慎使用代码,并最好对其进行修复,例如正确处理位图等。

标签: c# wpf image-processing


【解决方案1】:

我只想说 Bitmap 实际上提供了一种通过LockBits method of Bitmap 进行像素操作的超快速方法。如果您想通过手动设置像素来​​创建位图,这是创建位图的最快方法之一。请注意,BitmapSource 使用 WIC,而 Bitmap 使用 GDI+。因此,加载或复制像素数据数组不应有任何差异(或充其量是边际差异),这不是 Bitmapsource 或 Bitmap 的好处。

我还会添加对位图方面的支持,因为它是一个非常古老的结构,很多库都接受位图进行编辑。

我看到 BitmapSource 的唯一好处是它是 WPF 中图像的来源,并且可以轻松使用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-15
    • 1970-01-01
    • 2012-09-12
    • 1970-01-01
    • 1970-01-01
    • 2015-08-23
    • 2010-10-31
    • 1970-01-01
    相关资源
    最近更新 更多