【问题标题】:OutOfMemory Exception when loading lots of Images from Isolated storage从独立存储中加载大量图像时出现 OutOfMemory 异常
【发布时间】:2012-04-20 21:02:06
【问题描述】:

编辑:我不断收到 OutOfMemoryException 未处理, 我认为这就是我将图像保存到隔离存储的方式,我认为这是我可以解决问题的地方,如何在保存之前减小图像的大小? (在我保存图片的地方添加了代码)

我从独立存储中打开图像,有时超过 100 张图像,我想循环播放这些图像,但是当故事板中加载了大约 100 到 150 张图像时,我得到了 OutOfMemory Exception。我该如何处理这个异常,我已经降低了图像的分辨率。如何处理此异常并阻止我的应用崩溃?

我在这行得到了异常

image.SetSource(isStoreTwo.OpenFile(projectFolder + "\\MyImage" + i + ".jpg", FileMode.Open, FileAccess.Read));//images from isolated storage

这是我的代码

private void OnLoaded(object sender, RoutedEventArgs e)
    {


        IsolatedStorageFile isStoreTwo = IsolatedStorageFile.GetUserStoreForApplication();



        try
        {
            storyboard = new Storyboard
            {
                //RepeatBehavior = RepeatBehavior.Forever
            };

            var animation = new ObjectAnimationUsingKeyFrames();

            Storyboard.SetTarget(animation, projectImage);
            Storyboard.SetTargetProperty(animation, new PropertyPath("Source"));


            storyboard.Children.Add(animation);
            for (int i = 1; i <= savedCounter; i++)
            {
                BitmapImage image = new BitmapImage();

                image.SetSource(isStoreTwo.OpenFile(projectFolder + "\\MyImage" + i + ".jpg", FileMode.Open, FileAccess.Read));//images from isolated storage

                var keyframe = new DiscreteObjectKeyFrame
                {

                    KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(100 * i)),
                    Value = image
                };

                animation.KeyFrames.Add(keyframe);
            }
        }
        catch (OutOfMemoryException exc)
        {

            //throw;

        }



        Resources.Add("ProjectStoryBoard", storyboard);
        storyboard.Begin();
    }

编辑这就是我将图像保存到独立存储的方式,我认为这是我可以解决问题的地方,如何在将图像保存到独立存储时减小图像的大小?

    void cam_CaptureImageAvailable(object sender, Microsoft.Devices.ContentReadyEventArgs e)
    {

        string fileName = folderName+"\\MyImage" + savedCounter + ".jpg";

        try
        {  

            // Save picture to the library camera roll.
            //library.SavePictureToCameraRoll(fileName, e.ImageStream);



            // Set the position of the stream back to start
            e.ImageStream.Seek(0, SeekOrigin.Begin);

            // Save picture as JPEG to isolated storage.
            using (IsolatedStorageFile isStore = IsolatedStorageFile.GetUserStoreForApplication())
            {
                using (IsolatedStorageFileStream targetStream = isStore.OpenFile(fileName, FileMode.Create, FileAccess.Write))
                {

                    // Initialize the buffer for 4KB disk pages.
                    byte[] readBuffer = new byte[4096];
                    int bytesRead = -1;

                    // Copy the image to isolated storage. 
                    while ((bytesRead = e.ImageStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
                    {
                        targetStream.Write(readBuffer, 0, bytesRead);
                    }

                }

            }




        }
        finally
        {
            // Close image stream
            e.ImageStream.Close();
        }

    }

如果您能帮助我,我将不胜感激。

【问题讨论】:

    标签: c# silverlight windows-phone-7


    【解决方案1】:

    图像在磁盘上的大小并不重要,因为当您将它们加载到内存中时,它们将被解压缩。图像所需的内存大约为(stride * height)stridewidth * bitsPerPixel)/8,然后向上舍入到下一个 4 字节的倍数。因此,1024x768 和每像素 24 位的图像将占用大约 2.25 MB。

    您应该计算出未压缩的图像有多大,并使用该数字来确定内存要求。

    【讨论】:

    • 我的图片是 640x480,位深 24,所以这甚至不是 1mb,但我打开隔离存储超过 100 次。
    • 每像素 24 位的 640x480 图像正好是 900 KB。其中 100 个将为您提供 87 兆字节。最低限度。也会有一些其他的开销。你知道在出现内存不足异常之前添加了多少张图片吗?
    • 是的,我刚刚检查了 3 次计数器是 107
    • 未压缩的图像更有可能以每像素 32 位的格式存储。在这种情况下,107 张图像将占用 >125MB。任务管理对进程使用的内存有什么影响?
    • 我不知道如何使用任务管理器,我想如果我将图像的大小减少到 100kb 或其他东西,那么它可能会停止内存不足。保存图像时如何减小图像的大小。我在上面添加了代码。
    【解决方案2】:

    您收到 OutOfMemory 异常是因为您同时将所有图像存储在内存中以创建 StoryBoard。我认为您将无法克服图像需要在屏幕上显示的未压缩位图大小。

    因此,要克服这个问题,我们必须考虑您的目标,而不是试图修复错误。如果您的目标是每 X 毫秒按顺序显示一张新图像,那么您有几个选择。

    1. 继续使用 StoryBoard,但使用 OnCompleted 事件链接它们。这样您就不必一次创建它们,而只需生成接下来的几个。如果您每 100 毫秒更改一次图像,它可能不够快。

    2. 使用我的 answer here 中提到的 CompositionTarget.Rendering。如果您只是预加载下一个(而不是像您当前的解决方案那样将它们全部预加载),这可能会占用最少的内存。不过,您需要手动检查经过的时间。

    3. 重新思考你在做什么。如果你陈述你的目标,人们可能会有更多的选择。

    【讨论】:

    • 同意。您不必一次加载 100 张图片。仅加载您需要显示的内容,如果您遇到延迟问题,您可以预加载一些内容。
    • 嗨,奥斯汀,我正在尝试创建一个延时摄影应用程序,它每秒或每隔几秒钟拍摄一张照片,持续一定的时间,所以我想如果延时可以持续超过 3 分钟,因此要加载大量图像,我已经压缩了图像,但它们的质量不是很好。我会给你的建议去谢谢。
    • @M_K 好的,让我知道进展如何。另一种选择可能是使用计时器,例如 DispatcherTimer 来更改图像。这可能比链接 StoryBoard 或使用 CompositionTarget.Rendering 更简单。这是一个非常相似的例子:stackoverflow.com/questions/7429728/…
    • @AustinThompson 你帮了我很大的忙我已经抛弃了故事板并随线程而去,现在内存似乎很大,将不得不对大量照片进行一些测试。非常感谢您的帮助,如果一切顺利,我将奖励您。附带说明一下,Visual Studio Marketplace Testkit 是可靠的,这是应用程序中心用来认证的测试工具包吗?
    • @M_K 很高兴为您提供帮助。至于 Testkit,根据我的经验,自动化测试肯定已经捕获了一些东西,这让我不必重新提交。但是手动的是你会在测试人员之间获得差异的地方。当然值得做,然后至少看看手动测试。
    【解决方案3】:

    要回复帖子顶部的编辑,请尝试ImageResizer。有一个 NuGet 包和一个 HanselBlog 插曲。显然,这是基于 Asp.Net 的,但我相信您可以将其用于您的场景。

    【讨论】:

      【解决方案4】:

      在设计层解决这类问题通常效果更好。

      通过一些配置使应用程序智能地了解运行环境,使您的应用程序更加健壮。例如,您可以根据可用内存定义一些变量,如图像大小、图像计数、图像质量......并在运行时在应用程序中设置这些变量。所以你的应用程序总是有效的;在高内存机器上快,在低内存机器上慢;但永远不会崩溃。 (不要相信在托管环境中工作意味着不用担心环境......设计总是很重要)

      还有一些已知的设计模式,例如 Lazy Loading,您可以从中受益。

      【讨论】:

        【解决方案5】:

        我不特别了解 windows phone,但是在 .net winforms 中,您需要在执行长时间运行的任务时使用单独的线程。您使用的是 BackgroundWorker 还是等效的? finalizer thread can become blocked,这将阻止图像资源被释放。使用与 UI 线程不同的线程将允许 Dispose 方法自动运行。

        【讨论】:

          【解决方案6】:

          好的,图像 (1024x768) 的 memsize 至少为 3 mb (argb)

          不知道 ObjectAnimationUsingKeyFrames 如何在内部工作。也许您可以通过销毁 BitmapImage(和 KeyFrames)的实例来强制 gc,而不会丢失动画中的数据。 (不可能,见 cmets!)

          【讨论】:

          • 您好,如何使用垃圾收集器?
          • @ck0ne 你的计算不准确,很多东西决定了.jpg图片的大小。 OP,您可以在保存(或预处理)时将文件格式更改为 .png 吗?这通常会产生较小的图片。
          • 当我探索隔离存储时,两种格式之间没有区别,两者的大小都在 5.73 KB 左右
          • 只是 image.finalize() 在迭代块的末尾。 @Pedro:如果你确定,好吧!但是内部表示只是BitmapImage中的一种,不管是从bmp、png还是jpg创建的。如果将 BitmapImage 内容保存为 jpg 数据,则对任何像素的每个操作都需要对数据进行解码/编码。
          • image.finalize() 给我一个错误?无扩展方法 finalize()
          【解决方案7】:

          根据您的一个 cmets,您正在构建一个 Time Lapse 应用程序。适用于 WP7 的商业延时应用程序将图像压缩为视频,而不是静止图像。例如Time Lapse Pro

          视频播放的重点是将相似或与时间相关的图像减少为高度压缩的流播放不需要大量内存

          如果您可以在您的应用中添加对视频进行编码的功能,您将避免尝试模拟视频播放器的问题(使用 100 多个单个全分辨率帧作为电影书)。

          将图像处理到视频服务器端可能是另一种选择(但不如相机内友好)。

          【讨论】:

          • 这个应用是把图片压缩成视频吗??因为我认为不会,当您阅读该链接中的 cmets 时,您发布的用户会问为什么您不能保存为视频并分享到 Facebook。我试图找到一种方法来做到这一点,但找不到方法,这就是为什么我暂时必须这样做。你知道将图像压缩成视频的方法吗?
          • 他们很可能使用自定义库进行压缩,因为您不能在 C# 中进行真正快速的压缩(只是不够咕噜)。开始寻找可以使用的用 C# 编写的视频压缩/播放库。除非您想共享文件,否则您不关心编解码器。另一种方法(打开许多共享选项)是将文件发送到您的服务器并使用许多压缩库之一创建视频。您的单帧方法的基本问题是您正在移动/存储的数据的绝对重量。在 WP7 上,您需要在任何给定时间在内存中保存更少的数据
          猜你喜欢
          • 2011-05-05
          • 1970-01-01
          • 2014-08-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-12
          • 1970-01-01
          相关资源
          最近更新 更多