Optimization: VB.Net Loading an image from a file and resizing it on the fly; Target: FAST

I am going to post what was my project and how I got it. I am curious to see if there are ways I missed the mark or if there could be better optimizations.

[Tasks]

~ Add "fast" photo scrolling to your existing WinForms application.

~ "Like Picasa" - Flip the image from one image to another back and forth, keeping the lag and responsiveness of the application as small as possible.

~ Others not related to these tasks (renaming, tagging, exporting, etc.)

~ Resize the image after booting from disk to reduce memory usage and speed of paging from image to image; resize to the size of the control that displays the image.

[Restrictions]

~ Without converting the application to WPF

~ Maximum .NET Framework version: 4.0

[test platform]

Dell M6800 with SSD

Note. In all cases below most of the time was changed, not when booting from disk to memory. In addition, upload and resize operations were performed on a thread that queued up images. I didn't even bother using miniatures; the quality was too bad for this app, although it was fast. When the user "flips" from one image to another, the thread loads the next image into the directory in the queue and discards one of them at the end of the queue (conversely, if flipped back to the queue); seizure or dumping operations were minor.

[Test results: Image.FromFile]

54ms to load and resize using the following code. HMI response was low.

m_Image = Image.FromFile(Me.FullName)
m_Image = ResizeImage(m_Image, ImageSize, True)

Public Function ResizeImage(ByVal image As Image, ByVal size As Size, Optional ByVal preserveAspectRatio As Boolean = True) As Image
    Dim newWidth As Integer
    Dim newHeight As Integer
    If preserveAspectRatio Then
        Dim originalWidth As Integer = image.Width
        Dim originalHeight As Integer = image.Height
        Dim percentWidth As Single = CSng(size.Width) / CSng(originalWidth)
        Dim percentHeight As Single = CSng(size.Height) / CSng(originalHeight)
        Dim percent As Single = If(percentHeight < percentWidth, percentHeight, percentWidth)
        newWidth = CInt(originalWidth * percent)
        newHeight = CInt(originalHeight * percent)
    Else
        newWidth = size.Width
        newHeight = size.Height
    End If
    Dim newImage As Image = New Bitmap(newWidth, newHeight)
    Using graphicsHandle As Graphics = Graphics.FromImage(newImage)
        graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic
        graphicsHandle.DrawImage(image, 0, 0, newWidth, newHeight)
    End Using
    Return newImage
End Function

      

[Test results: ImageFast]

41ms to load and resize using the following code. HMI response has improved somewhat, although still not very good. Booting from disk is better than the traditional method.

Source for ImageFast: Fastest Image Resizing in .NET

m_Image = ImageFast.FromFile(Me.FullName)
m_Image = ResizeImage1(m_Image, ImageSize.Width, ImageSize.Height)

      

[Test Results: ImageProcessor]

122ms to load and resize using the following code. HMI response was low.

Source for ImageProcessor: http://www.hanselman.com/blog/NuGetPackageOfTheWeekImageProcessorLightweightImageManipulationInC.aspx

Private Sub UseImageProcessor(ByVal fileName As String)
        Dim photoBytes As Byte() = File.ReadAllBytes(fileName)
        Dim quality As Integer = 70
        Dim format As ImageFormat = ImageFormat.Jpeg
        Dim size As New Size(150, 0)
        Using inStream As New MemoryStream(photoBytes)
            Using outStream As New MemoryStream()
                    imageFactory.Load(inStream).Resize(size)  '// resizing takes about 127mS on Dell 
                    m_Image = imageFactory.Image
                    m_Image = Image.FromStream(inStream)
                End Using
            End Using
        End Using
End Sub

      

None of these results were very good. It is a flashy fast PC and I was unhappy especially when comparing the image performance to the image from the aforementioned Picasa.

So, I tried something different. I added a reference to PresentationCore version 4.0.0.0 to my WinForms application, which allowed me to use:

Imports System.Windows.Media.Imaging

      

Now I can do this for loading and resizing:

m_Image = GetBitMap(GetImageBitMap(Me.FullName))

Public Function GetImageBitMap(ByVal fullName As String) As BitmapImage
    Dim imageData = File.ReadAllBytes(fullName)
    Dim resizedImage As New BitmapImage()
    resizedImage.BeginInit()  ' Needed only so we can call EndInit()
    With resizedImage
        .StreamSource = New MemoryStream(imageData)
        .CreateOptions = BitmapCreateOptions.IgnoreColorProfile
        .DecodePixelHeight = ImageSize.Height
        .DecodePixelWidth = ImageSize.Width
    End With
    resizedImage.EndInit()    ' This does the actual loading and resizing
    Return resizedImage
End Function

      

For both operations (load and resize) it was a four letter word & = fast: average 3ms, total. Loading and resizing. Holy grail.

The obvious problem is that the return for the above function is a BitmapImage object that cannot be used in a WinForms application (which I am aware of). So I had to convert it to BitMap using the following code:

Private Function GetBitMap(bitmapImage As BitmapImage) As Bitmap
    Try
        '// https://stackoverflow.com/questions/6484357/converting-bitmapimage-to-bitmap-and-vice-versa
        Using outStream As New MemoryStream()
            Dim enc As BitmapEncoder = New BmpBitmapEncoder()
            enc.Frames.Add(BitmapFrame.Create(bitmapImage))
            enc.Save(outStream)
            Dim bitmap As New System.Drawing.Bitmap(outStream)
            Return New Bitmap(bitmap)
        End Using
    Catch ex As Exception
        Throw
    End Try
End Function

      

The result of using the WPF load and resize method and convert back to BitMap for our WinForms application was ~ 22ms to load, resize and convert (to BitMap from BitmapImage) on the mentioned platform. HMI's response was good, if not great. It was better than experienced using only mentioned WinForms methods.

I'm open to any further suggestions though (hence the question here). The current result is acceptable. I wish I should have spent the expense of redistributing from BitmapImage to BitMap, but this is a WinForms application after all.

+3


source to share


1 answer


It sounds like what you really want to use BitmapImage

from within your WinForms application. To do this, you just need to use ElementHost to host the WPF control Image

.



Image imageControl;
imageControl.Source = GetImageBitMap(filename);

      

+3


source







All Articles