Производительность скорости декодирования BitmapImage wpf

У меня есть 5 изображений одинаковой высоты и ширины в пикселях (2481 * 3508 в этом отношении). Но один в формате gif, один в формате jpeg, один в формате png и один в формате bmp. Теперь я визуализирую их в BitmapSource с (1) двумя третями исходной высоты пикселя для DecodePixelHeight и (2) исходной высотой пикселя для DecodePixelHeight.

Первый сценарий:

bitmapImage.BeginInit();
bitmapImage.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.DecodePixelHeight = 2/3 * originalHeight;
bitmapImage.StreamSource = streamWithTheFile;
bitmapImage.EndInit();
bitmapImage.Freeze();

BMP и Jpeg одинаково медленны. Png и Gif требуют меньше половины времени. Почему?

Второй сценарий:

bitmapImage.BeginInit();
bitmapImage.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = streamWithTheFile;
bitmapImage.EndInit();
bitmapImage.Freeze();

Png половину времени, необходимого раньше. Jpeg и BMP в пять раз меньше, чем раньше. Гифка в то же время, что и раньше.

Согласно документации я бы предположил, что Png и Производительность JPEG каким-то образом будет более независимой от фактического размера декодирования, чем в других форматах. В чем может быть причина, что это не так?


person user1182735    schedule 24.10.2012    source источник
comment
Хороший вопрос - кто-нибудь знает, почему BitmapImage ведет себя так?   -  person alexander.biskop    schedule 07.11.2012
comment
Может ли свойство RenderOptions.BitmapScalingMode (применяемое к любому элементу управления, фактически отображающему изображение, обычно это элемент XAML ‹Image›) помочь со скоростью? Я думаю, что по умолчанию для него установлено значение HighQuality в .NET 3.5 и LowQuality для .NET 4, но оба несут своего рода штраф в скорости рендеринга...   -  person Marko    schedule 21.08.2013
comment
Я работал над приложением, которое последовательно воспроизводило большое количество (тысячи) изображений - либо jpg, либо bmp, и я пытался оптимизировать скорость для более быстрой скорости воспроизведения. С одной стороны, растровые изображения больше по размеру и требуют больше времени для загрузки с диска или кэша памяти, но они не требуют декодирования. С jpg все наоборот - декодирование медленное, но загрузка с диска быстрая. В конце концов я выбрал исходный формат изображений (jpg), потому что это требует меньше начальной обработки. Дело в том, что кэширование и загрузка диска могут мешать синхронизации.   -  person Dean    schedule 18.09.2014


Ответы (1)


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

Попробуйте это и дайте мне знать, что вы думаете. Использование довольно простое: назначьте свойства зависимости, прикрепите поведение к изображению и покончите с этим. ваше здоровье.

Примечание. Пиксели могут быть IList, но вы также можете назначить массив, поскольку массив C# реализует IList.

Примечание 2. Не назначайте источник изображения, так как это переопределит назначение поведения, просто привяжите его к свойству зависимости Pixels поведения.

public class VideoBehavior : Behavior<Image>
{

    public static readonly DependencyProperty PixelsProperty = DependencyProperty.Register(
        "Pixels", typeof (IList<byte>), typeof (VideoBehavior), new PropertyMetadata(default(IList<byte>),OnPixelsChanged));

    private static void OnPixelsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var b = (VideoBehavior) d;
        var pixels = (IList<byte>) e.NewValue;


        b.RenderPixels(pixels);
    }


    public IList<byte> Pixels
    {
        get { return (IList<byte>) GetValue(PixelsProperty); }
        set { SetValue(PixelsProperty, value); }
    }

    public static readonly DependencyProperty PixelFormatProperty = DependencyProperty.Register(
        "PixelFormat", typeof (PixelFormat), typeof (VideoBehavior), new PropertyMetadata(PixelFormats.Default,OnPixelFormatChanged));


    private static void OnPixelFormatChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var b = (VideoBehavior) d;
        var pixelFormat = (PixelFormat) e.NewValue;

        if(pixelFormat==PixelFormats.Default)
            return;

        b._pixelFormat = pixelFormat;

        b.InitializeBufferIfAttached();
    }

    public PixelFormat PixelFormat
    {
        get { return (PixelFormat) GetValue(PixelFormatProperty); }
        set { SetValue(PixelFormatProperty, value); }
    }

    public static readonly DependencyProperty PixelWidthProperty = DependencyProperty.Register(
        "PixelWidth", typeof (int), typeof (VideoBehavior), new PropertyMetadata(default(int),OnPixelWidthChanged));

    private static void OnPixelWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var b = (VideoBehavior)d;
        var value = (int)e.NewValue;

        if(value<=0)
            return;

        b._pixelWidth = value;

        b.InitializeBufferIfAttached();
    }

    public int PixelWidth
    {
        get { return (int) GetValue(PixelWidthProperty); }
        set { SetValue(PixelWidthProperty, value); }
    }


    public static readonly DependencyProperty PixelHeightProperty = DependencyProperty.Register(
        "PixelHeight", typeof (int), typeof (VideoBehavior), new PropertyMetadata(default(int),OnPixelHeightChanged));

    private static void OnPixelHeightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var b = (VideoBehavior)d;
        var value = (int)e.NewValue;

        if (value <= 0)
            return;


        b._pixelHeight = value;

        b.InitializeBufferIfAttached();
    }

    public int PixelHeight
    {
        get { return (int) GetValue(PixelHeightProperty); }
        set { SetValue(PixelHeightProperty, value); }
    }

    public static readonly DependencyProperty DpiXProperty = DependencyProperty.Register(
        "DpiX", typeof (int), typeof (VideoBehavior), new PropertyMetadata(96,OnDpiXChanged));

    private static void OnDpiXChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var b = (VideoBehavior)d;
        var value = (int)e.NewValue;

        if (value <= 0)
            return;


        b._dpiX = value;

        b.InitializeBufferIfAttached();
    }

    public int DpiX
    {
        get { return (int) GetValue(DpiXProperty); }
        set { SetValue(DpiXProperty, value); }
    }

    public static readonly DependencyProperty DpiYProperty = DependencyProperty.Register(
        "DpiY", typeof (int), typeof (VideoBehavior), new PropertyMetadata(96,OnDpiYChanged));

    private static void OnDpiYChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var b = (VideoBehavior)d;
        var value = (int)e.NewValue;

        if (value <= 0)
            return;


        b._dpiY = value;

        b.InitializeBufferIfAttached();
    }

    public int DpiY
    {
        get { return (int) GetValue(DpiYProperty); }
        set { SetValue(DpiYProperty, value); }
    }

    [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
    public static extern void CopyMemory(IntPtr destination, IntPtr source, uint length);

    private IntPtr _backBuffer = IntPtr.Zero;
    private int _bytesPerPixel;
    private const int BitsPerByte = 8;
    private int _pixelWidth;
    private int _pixelHeight;
    private int _dpiX;
    private int _dpiY;
    private PixelFormat _pixelFormat;
    private Int32Rect _rect;

    private uint _byteArraySize;
    private WriteableBitmap _bitMap;

    private bool _attached;

    protected override void OnAttached()
    {
        _attached = true;
        InitializeBufferIfAttached();
    }

    private void InitializeBufferIfAttached()
    {
        if(_attached==false)
            return;

        ReevaluateBitsPerPixel();

        RecomputeByteArraySize();

        ReinitializeImageSource();
    }

    private void ReevaluateBitsPerPixel()
    {
        if(_pixelFormat==PixelFormats.Default)
            return;

        _bytesPerPixel = _pixelFormat.BitsPerPixel/BitsPerByte;
    }

    private void ReinitializeImageSource()
    {
        if(_pixelHeight<=0|| _pixelHeight<=0)
            return;

        _bitMap = new WriteableBitmap(_pixelWidth, _pixelHeight, _dpiX, _dpiY, _pixelFormat, null);
        _backBuffer = _bitMap.BackBuffer;
        _rect = new Int32Rect(0, 0, _pixelWidth, _pixelHeight);
        AssociatedObject.Source = _bitMap;
    }

    private async void RenderPixels(IList<byte> pixels)
    {
        if (_backBuffer == IntPtr.Zero)
            return;

        if (pixels == null)
        {
            return;
        }

        await Task.Factory.StartNew(() =>
        {
            var h = new GCHandle();
            var allocated = false;

            try
            {
                h = GCHandle.Alloc(pixels, GCHandleType.Pinned);
                allocated = true;
                var ptr = h.AddrOfPinnedObject();
                CopyMemory(_backBuffer, ptr, _byteArraySize);
            }
            finally
            {
                if (allocated)
                    h.Free();
            }
        });

        _bitMap.Lock();

        _bitMap.AddDirtyRect(_rect);
        _bitMap.Unlock();
    }

    private void RecomputeByteArraySize()
    {
        _byteArraySize = (uint)(_pixelWidth * _pixelHeight * _bytesPerPixel);
    }
}
person Eyal Perry    schedule 23.06.2016