Как отображать изображения, не занимая огромное количество оперативной памяти - PullRequest
13 голосов
/ 16 марта 2010

Я работаю над проектом Silverlight, где пользователи могут создавать свои собственные коллажи.

Проблема

При загрузке нескольких изображений с помощью класса BitmapImage Silverlight увеличивает объем неоправданно большого объема оперативной памяти. 150 снимков, где отдельные из них занимают не более 4,5 МБ, занимают около 1,6 ГБ ОЗУ, что приводит к исключениям памяти.

Я загружаю их через потоки, так как пользователь выбирает свои собственные фотографии.

Что я ищу

Класс, метод или какой-то процесс, чтобы устранить израсходованный огромный объем ОЗУ. Скорость - это проблема, поэтому я не хочу конвертировать между форматами изображений или чем-то в этом роде. Быстрое решение для изменения размера может работать.

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

Ответы [ 6 ]

6 голосов
/ 06 апреля 2010

Я бы попытался загрузить каждый поток и изменить его размер до миниатюры (скажем, 640x480) перед загрузкой следующего. Затем позвольте пользователю работать с небольшими изображениями. Когда вы будете готовы сгенерировать PDF-файл, перезагрузите JPEG-файлы из исходных потоков по одному, избавляясь от каждого растрового изображения перед загрузкой следующего.

4 голосов
/ 07 апреля 2010

Полагаю, вы делаете что-то вроде этого:

Bitmap bitmap = new Bitmap (filename of jpeg);

и затем делает:

OnPaint (...)
{
   Graphics g = ....;
   g.DrawImage (bitmap, ...);
}

Это будет изменять размер огромного изображения JPEG до размера, отображаемого на экране каждый раз, когда вы его рисуете. Я предполагаю, что ваш JPEG имеет размер около 2500x2000 пикселей. Когда вы загружаете JPEG в растровое изображение, код загрузки растрового изображения распаковывает данные и сохраняет их либо как данные RGB в формате, который будет легко отображать (т. Е. В том же формате пикселей, что и дисплей), либо как элемент, известный как Независимое от устройства растровое изображение (также известное как DIBitmap). Этим растровым изображениям требуется больше оперативной памяти для хранения, чем сжатому JPEG.

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

В идеале вы хотите загрузить изображение и уменьшить его. .Net имеет систему для этого:

Bitmap bitmap = new Bitmap (filename of JPEG);
Bitmap thumbnail = bitmap.GetThumbnailImage (width, height, ....);
bitmap.Dispose (); // this releases all the unmanged resources and makes the bitmap unusable - you may have been missing this step
bitmap = null; // let the GC know the object is no longer needed

где ширина и высота - размер требуемой миниатюры. Теперь это может привести к тому, что изображения будут выглядеть не так хорошо, как хотелось бы (но будут использоваться любые встроенные данные миниатюр, если они есть, поэтому они будут работать быстрее), в этом случае выполните изменение размера растрового изображения -> растрового изображения .

Когда вы создаете файл PDF, вам необходимо перезагрузить данные JPEG, но с точки зрения пользователя, все в порядке. Я уверен, что пользователь не будет возражать подождать некоторое время, чтобы экспортировать данные в PDF, если у вас есть какие-то отзывы, чтобы сообщить пользователю, что они обрабатываются. Вы также можете сделать это в фоновом потоке и позволить пользователю работать с другим коллажем.

1 голос
/ 06 апреля 2010

Это не полное решение, но если вы собираетесь конвертировать между растровыми изображениями и JPEG (и наоборот), вам нужно заглянуть в библиотеку изображений FJCore . Он достаточно прост в использовании и позволяет выполнять такие действия, как изменение размера изображений JPEG или перемещение их в другое качество. Если вы используете Silverlight для обработки изображений на стороне клиента, этой библиотеки, вероятно, будет недостаточно, но она, безусловно, необходима.

Вы также должны посмотреть, как вы представляете изображения пользователю. Если вы делаете коллажи с Silverlight, вероятно, вы не сможете использовать виртуализацию элементов управления, поскольку пользователи будут манипулировать всеми 150 изображениями одновременно. Но, как говорили другие люди, вы также должны убедиться, что вы не представляете растровые изображения на основе полноразмерных файлов JPEG. Сжатый JPEG размером 1 МБ, вероятно, расширится до растрового изображения размером 10 МБ, которое, вероятно, является источником многих проблем. Убедитесь, что изображения, которые вы предоставляете пользователю, основаны на файлах JPEG намного меньшего размера (более низкого качества и с измененным размером).

1 голос
/ 06 апреля 2010

То, что может случиться с вами, является малоизвестным фактом о сборке мусора, который меня тоже поразил. Если объект достаточно большой (я не помню, где именно находится линия), сборщик мусора решит, что, хотя ничто в настоящее время в области действия не связано с объектом (как в вашем, так и в моем объектах - изображения), он сохраняет изображение в памяти, потому что он решил, что в случае, если вам когда-нибудь понадобится это изображение, дешевле сохранить его, а не удалить и перезагрузить позже.

0 голосов
/ 09 апреля 2010

Еще один вариант уменьшения оперативной памяти с помощью: Не загружайте изображения, которые в данный момент невидимы, и загружайте их при прокрутке страницы пользователем. Этот метод используется веб-разработчиками для повышения скорости загрузки страницы. Для вас это способ не хранить количество отверстий в памяти.

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

0 голосов
/ 08 апреля 2010

Решение, которое наконец-то сработало для меня, использовало WriteableBitmapEX для выполнения следующих действий:

Конечно, я использую миниатюры, только если изображение недостаточно маленькое, чтобы сохранить его в памяти.

У меня было то, что у WritechBitmap нет конструктора без параметров, но инициализация его размером 0,0 и последующая загрузка исходного кода устанавливает их автоматически. Это не было естественно для меня.

Спасибо всем за помощь!

private WriteableBitmap getThumbnailFromBitmapStream(Stream bitmapStream, PhotoFrame photoFrame)
    {
        WriteableBitmap inputBitmap = new WriteableBitmap(0,0);
        inputBitmap.SetSource(bitmapStream);

        Size thumbnailSize = getThumbnailSizeFromWriteableBitmap(inputBitmap, photoFrame.size);

        WriteableBitmap thumbnail = new WriteableBitmap(0,0);
        thumbnail = inputBitmap.Resize((int)thumbnailSize.Width, (int)thumbnailSize.Height, WriteableBitmapExtensions.Interpolation.NearestNeighbor);

        return thumbnail;
    }
...