【问题标题】:WPF Image resizing and storing works on most images but fails to resize or save on othersWPF 图像大小调整和存储适用于大多数图像,但无法调整大小或保存其他图像
【发布时间】:2016-02-04 03:15:14
【问题描述】:

我正在尝试调整并保存 Page_load 事件中定义的 3 张图像。

在方法ResizeAndSave 中,我正在尝试两种方法:FastResizeSlowResize

当我取消注释 FastResize 代码行时:图像 1 和 2 已正确保存并调整大小。然而,IMAGE 3 以 625x441px 的尺寸保存,因此不尊重我希望它调整大小的 200x200 框。

当我改用 SlowResize 代码行时:图像 1 和 2 再次被正确保存和调整大小。但是,图像 3 根本没有保存。

我的代码中没有错误。我将从各种来源导入图像,因此我的代码适用于各种图像格式至关重要。显然 IMAGE 3 有一些特别之处,我不知道它是什么或如何处理它。

这是我的完整代码,您应该可以复制/粘贴并自己测试:

Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.IO
Imports System.Xml
Imports System.Data.SqlClient
Imports System.Net
Imports System.Windows.Media.Imaging
Imports System.Windows.Media

Partial Class importfeeds
    Inherits System.Web.UI.Page

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

    'IMAGE 1
    ResizeAndSave(200, 200, "https://upload.wikimedia.org/wikipedia/commons/8/82/Dell_Logo.png")    
    'IMAGE 2        
    ResizeAndSave(200, 200, "https://upload.wikimedia.org/wikipedia/commons/d/d8/Square-1_solved.jpg")  
    'IMAGE 3
    ResizeAndSave(200, 200, "http://cdn2.emobassets.eu/media/catalog/product/1/1/1116220.jpg")          

End Sub


Private Sub ResizeAndSave(ByVal maxWidth As Integer, ByVal maxHeight As Integer, ByVal imageURL As String)
    Dim imgRequest As WebRequest = WebRequest.Create(imageURL)
    Dim imgResponse As WebResponse = imgRequest.GetResponse()

    Dim streamPhoto As Stream = imgResponse.GetResponseStream()
    Dim memStream As New MemoryStream
    streamPhoto.CopyTo(memStream)
    memStream.Position = 0
    Dim bfPhoto As BitmapFrame = ReadBitmapFrame(memStream)

    Dim newWidth, newHeight As Integer
    Dim scaleFactor As Double

    If bfPhoto.Width > maxWidth Or bfPhoto.Height > maxHeight Then
        If bfPhoto.Width > maxWidth Then
            scaleFactor = maxWidth / bfPhoto.Width
            newWidth = Math.Round(bfPhoto.Width * scaleFactor, 0)
            newHeight = Math.Round(bfPhoto.Height * scaleFactor, 0)
        End If
        If newHeight > maxHeight Then
            scaleFactor = maxHeight / newHeight
            newWidth = Math.Round(newWidth * scaleFactor, 0)
            newHeight = Math.Round(newHeight * scaleFactor, 0)
        End If
    End If


    Dim bfResize As BitmapFrame = FastResize(bfPhoto, newWidth, newHeight)
    'Dim bfResize As BitmapFrame = SlowResize(bfPhoto, newWidth, newHeight, BitmapScalingMode.Linear)

    Dim baResize As Byte() = ToByteArray(bfResize)

    Dim strThumbnail As String = Guid.NewGuid.ToString() + ".png"
    Dim saveToPath As String = Server.MapPath(ConfigurationManager.AppSettings("products_photospath")) + "\49\" + strThumbnail

    File.WriteAllBytes(saveToPath, baResize)

End Sub

Private Shared Function FastResize(bfPhoto As BitmapFrame, nWidth As Integer, nHeight As Integer) As BitmapFrame
    Dim tbBitmap As New TransformedBitmap(bfPhoto, New ScaleTransform(nWidth / bfPhoto.PixelWidth, nHeight / bfPhoto.PixelHeight, 0, 0))
    Return BitmapFrame.Create(tbBitmap)
End Function

'http://weblogs.asp.net/bleroy/resizing-images-from-the-server-using-wpf-wic-instead-of-gdi
Public Shared Function SlowResize(photo As BitmapFrame, width As Integer, height As Integer, scalingMode As BitmapScalingMode) As BitmapFrame

    Dim group = New DrawingGroup()
    RenderOptions.SetBitmapScalingMode(group, scalingMode)
    group.Children.Add(New ImageDrawing(photo, New Windows.Rect(0, 0, width, height)))
    Dim targetVisual = New DrawingVisual()
    Dim targetContext = targetVisual.RenderOpen()
    targetContext.DrawDrawing(group)
    Dim target = New RenderTargetBitmap(width, height, 96, 96, PixelFormats.[Default])
    targetContext.Close()
    target.Render(targetVisual)
    Dim targetFrame = BitmapFrame.Create(target)
    Return targetFrame
End Function

Private Shared Function ToByteArray(bfResize As BitmapFrame) As Byte()
    Using msStream As New MemoryStream()
        Dim pbdDecoder As New PngBitmapEncoder()
        pbdDecoder.Frames.Add(bfResize)
        pbdDecoder.Save(msStream)
        Return msStream.ToArray()
    End Using
End Function

Private Shared Function ReadBitmapFrame(streamPhoto As Stream) As BitmapFrame
    Dim bdDecoder As BitmapDecoder = BitmapDecoder.Create(streamPhoto, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None)
    Return bdDecoder.Frames(0)
End Function

End Class

更新 1

@Hans Passant:您对文件命名和 pixelWidth 使用的建议都很准确,并帮助我在 Page_load 事件中的 3 张图像上成功运行了此代码。 我更新了我的原始代码。
但是,当我将此代码作为实际应用程序的一部分运行时,我会从提要中导入大约 100 张图像。新代码在尝试处理 IMAGE 3 时因内存不足异常而失败。FastResizeSlowResize 方法都会发生这种情况。我的代码或相关图像中是否有某些内容会导致内存使用量增加,可能是某处泄漏或我使用的调整大小方法效率低?

我的机器上有很多可用内存,所以如果这是问题所在,我会感到非常惊讶,尽管我确实看到我的 Windows 任务管理器中的 System and compressed memory(至 1.1GB)任务大幅增加。尽管如此,如此多的内存使用会让我相信我的代码有问题。

会是什么?

【问题讨论】:

    标签: wpf vb.net image-processing image-resizing


    【解决方案1】:

    然而,图像 3 以 625x441px 的尺寸保存

    这是因为图像与其他图像略有不同,它的 DPI(每英寸点数)是 300 而不是 96。它的大小以像素为单位是 3071 x 2172,但您使用的是宽度和 Height 属性,尺寸以英寸为单位,单位为 1/96",即此图像的 982.72 x 695.04。改用 PixelWidth 和 PixelHeight 属性解决此问题:

    Dim tbBitmap As New TransformedBitmap(bfPhoto, 
       New ScaleTransform(nWidth / bfPhoto.PixelWidth, nHeight / bfPhoto.PixelHeight, 0, 0))
    

    但是,图像 3 根本没有保存

    这并没有完全加起来,但你在这个陈述中确实有一个严重的错误:

    Dim strThumbnail As String = "success" + Date.Now.Second.ToString + ".png"
    

    此名称的唯一性不足以确保您不会覆盖现有文件。如果代码“快”,则 Date.Now.Second 将具有相同的值,并且您的代码将覆盖以前写入的图像文件。请注意,当您调试时,此错误不会重现,这会人为地使代码变慢,并且第二个会有所不同。

    您将需要一种更好的方法来命名文件,例如 Guid.NewGuid.ToString() 是一种非常好的方法,可以保证是唯一的。或者使用为每个图像递增的简单计数器。您确实需要专注于清理工作。

    【讨论】:

    • 谢谢!现在更进一步,但仍然遇到一些问题,请查看我的更新 1。非常感谢!
    • 唉,这和原来的问题没有任何关系,你真的需要再次点击按钮。看完this Q+A。随机:尝试每隔 20 张图像向它扔一个 GC.Collect()。
    猜你喜欢
    • 2011-10-24
    • 1970-01-01
    • 2011-02-18
    • 2010-10-12
    • 1970-01-01
    • 2012-01-06
    • 2013-07-26
    • 2013-07-20
    • 2017-08-10
    相关资源
    最近更新 更多