我要发布我的设计目标,以及我是如何获得它的。我很想知道是否有任何方法可以让我错过标记或者可以更好地进行优化。
[目标]
〜添加'快速'照片滚动到现有的WinForms应用程序。
〜"类似于Picasa" - 从一个图像翻转到另一个图像,前进和后退,尽可能减少延迟,尽可能保持应用程序的响应性。
〜其他与此问题目标无关(重命名;标记;导出等)
〜从磁盘加载后调整图像大小以减少内存使用和速度'翻转'从图像到图像;调整大小以显示图像的控件的尺寸。
[约束]
〜没有将应用程序转换为WPF
〜最大.NET Framework版本:4.0
[测试平台]
带SSD的Dell M6800
注意:在下面的所有情况下,消耗的大部分时间都是调整大小,而不是从磁盘加载到内存。此外,加载和调整大小操作是在排队图像的线程中执行的。我甚至不打扰使用缩略图;尽管速度很快,但这个应用的质量太差了。 当用户'翻转'从一个图像到下一个图像,一个线程将目录中的下一个图像加载到队列中,并在队列的后面丢弃一个(反之亦然,如果通过队列翻回);从队列中排队或退出的操作可以忽略不计。
[测试结果:Image.FromFile]
使用以下代码加载和调整大小平均54ms。 HMI响应很差。
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
[测试结果:ImageFast]
使用以下代码加载和调整大小平均41ms。 HMI响应有所改善,但仍然不好。从磁盘加载比传统的更好。方法
ImageFast的来源:Fastest Image Resizing in .NET
m_Image = ImageFast.FromFile(Me.FullName)
m_Image = ResizeImage1(m_Image, ImageSize.Width, ImageSize.Height)
[测试结果:ImageProcessor]
使用以下代码加载和调整大小的平均值为122毫秒。 HMI响应很差。
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
这些结果都不是很好。这是一台尖叫的快速PC,我不知所措,特别是当我将图像到图像的性能与上述Picasa的性能进行比较时。
所以,我尝试了不同的东西。我在我的WinForms应用程序中添加了PresentationCore版本4.0.0.0的引用,允许我使用:
Imports System.Windows.Media.Imaging
现在,我可以为加载和调整大小执行此操作:
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
对于这两个操作(加载和调整大小),这是一个四个字母的单词&amp; = ing fast:3ms average,total。加载和调整大小。圣杯。
这里显而易见的问题是上面函数的返回是 BitmapImage 对象,在WinForms应用程序(我知道)中不可用。因此,我必须使用以下代码将其转换为BitMap:
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
使用WPF加载的结果&amp;调整大小的方法,加上我们的WinForms应用程序转换回BitMap,在上述平台上加载,调整大小和转换(从BitmapImage到BitMap)约22ms。 HMI响应很好,如果不是很好的话。它比仅使用提到的WinForms方法更好。
我对任何进一步的建议持开放态度(因此这里的问题)。目前的结果是可以接受的。我希望我不必花费从BitmapImage转换到BitMap的开销,但它毕竟是一个WinForms应用程序。
答案 0 :(得分:3)
您真正想要做的就是在WinForms应用中使用BitmapImage
。为此,您只需使用ElementHost来托管WPF Image
控件。
Image imageControl;
imageControl.Source = GetImageBitMap(filename);