Как отображать изображения, не занимая огромных объемов ОЗУ

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

Проблема

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

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

Что я ищу

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

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

Ответ 1

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

Ответ 2

Я предполагаю, что вы что-то делаете:

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

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

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

Ответ 3

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

Ответ 4

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

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

Ответ 5

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

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

У меня было то, что WriteableBitmap не имеет конструктора без параметров, но инициализирует его размером 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;
    }

Ответ 6

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

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