BitmapSource vs Bitmap

7 месяцев назад мы начали изучать C# и WPF, и, как все новички, которые хотят сделать некоторую обработку изображений, мы столкнулись с этим вопросом:

почему есть растровое изображение и BitmapSource ? И в чем преимущества каждого ?

в нашем проекте мы должны были создать растровое изображение из данных. Скорость была очень важна для нас.

мы начали с растрового изображения, потому что это проще (expecialy методы: get / setpixel), хорошо документировано с большим количеством исключения. Но затем мы обнаружили проблемы преобразования в WPF для печати растрового изображения.

поэтому мы попытались с BitmapSource, это было непросто из-за различных форматов пикселей. Но мы в конце концов уступили.

мы сравнили скорость каждого поколения. Работа с SetPixel (Bitmap) намного медленнее, чем работа с массивом байтов (BitmapSource), но работа с массивом байтов означает осложнения : шаг, формат пикселя ...

поэтому мы точно выбрали BitmapSource. Но потом мы хотел сериализовать некоторый BitmapSource. BitmapSource не сериализуется. Таким образом, с [OnSerializing] [OnDeserialized] мы преобразовали BitmapSource в Bitmap (сериализуемый).

наши выводы :

растровое изображение плюсы :

  1. простота
  2. Сериализуемость

преимущества BitmapSource:

  1. Скорость
  2. наследование (ImageSource) для В WPF

вы видите некоторые другие пункты ?

чтобы проиллюстрировать и для новичков, таких как мы, вот некоторые полезные методы, которые нам нужны:

преобразования :

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);
        }
    }

1 ответов


Я просто хочу сказать, что Bitmap на самом деле обеспечивает супер-быстрые средства для обработки пикселей через LockBits метод растрового изображения. Это один из самых быстрых способов создания растрового изображения, если вы хотите сделать это, установив пиксели вручную. Обратите внимание, что BitmapSource использует WIC, а Bitmap использует GDI+. Из-за этого не должно быть никакой разницы (или маргинальной в лучшем случае) для загрузки или копирования массивов пиксельных данных, и это не является преимуществом Bitmapsource или Растровый.

Я бы также добавил поддержку растровых изображений, так как это очень старая структура, с большим количеством библиотек, принимающих растровое изображение для редактирования.

единственное преимущество, которое я вижу для BitmapSource, заключается в том, что он является источником изображений в WPF и может быть легко использован.