UIImage в необработанные NSData / избежать сжатия

У меня есть собственный класс загрузчика изображений, он содержит очередь и загружает изображения по одному (или определенное количество) за раз, записывает их в папку кеша и при необходимости извлекает из папки кеша. У меня также есть подкласс UIImageView, которому я могу передать URL-адрес, через класс загрузчика изображений он будет смотреть, существует ли изображение на устройстве, и показывать его, если оно есть, или загружать и показывать его после завершения.

После того, как изображение загрузится, я делаю следующее. Я создаю UIImage из загруженных NSData, сохраняю загруженные NSData на диск и возвращаю UIImage.

// This is executed in a background thread
downloadedImage = [UIImage imageWithData:downloadedData];
BOOL saved = [fileManager createFileAtPath:filePath contents:downloadedData attributes:attributes];
// Send downloadedImage to the main thread and do something with it

Я делаю это, чтобы получить существующее изображение.

// This is executed in a background thread
if ([fileManager fileExistsAtPath:filePath])
{
    NSData* imageData = [fileManager contentsAtPath:filePath];
    retrievedImage = [UIImage imageWithData:imageData];
    // Send retrievedImage to the main thread and do something with it
}

Как видите, я всегда создаю UIImage непосредственно из загруженных NSData, я никогда не создаю NSData с помощью UIImagePNGRepresentation, поэтому изображение никогда не сжимается. Когда вы создаете UIImage из сжатых NSData, UIImage распаковывает его прямо перед рендерингом в основном потоке и, таким образом, блокирует пользовательский интерфейс. Поскольку теперь у меня есть UITableView с множеством небольших изображений, которые необходимо загрузить или получить с диска, это было бы неприемлемо, так как это сильно замедлило бы мою прокрутку.

Теперь моя проблема. Пользователь также может выбрать фотографию из фотопленки, сохранить ее, и она также должна появиться в моем UITableView. Но я не могу найти способ превратить UIImage из камеры в NSData без использования UIImagePNGRepresentation. Итак, вот мой вопрос.

Как я могу преобразовать UIImage в несжатый NSData, чтобы я мог позже преобразовать его обратно в UIImage, используя imageWithData, чтобы его не нужно было распаковывать перед рендерингом?

or

Есть ли способ выполнить распаковку перед отправкой UIImage в основной поток и кэшировать его, чтобы его нужно было распаковать только один раз?

Заранее спасибо.


person David    schedule 28.03.2013    source источник


Ответы (3)


Как я могу преобразовать UIImage в несжатый NSData, чтобы я мог позже преобразовать его обратно в UIImage, используя imageWithData, чтобы его не нужно было распаковывать перед рендерингом?

Я так понимаю, что вы действительно спрашиваете здесь, как сохранить UIImage на диске таким образом, чтобы впоследствии вы могли читать UIImage с диска так быстро, как возможный. Вам все равно, хранится ли он как NSData; вы просто хотите, чтобы его можно было быстро прочитать. Я предлагаю вам использовать фреймворк ImageIO. Сохраните как место назначения изображения и получите позже через источник изображения.

http://developer.apple.com/library/ios/#documentation/GraphicsImaging/Conceptual/ImageIOGuide/ikpg_dest/ikpg_dest.html

Есть ли способ выполнить распаковку перед отправкой UIImage в основной поток и кэшировать его, чтобы его нужно было распаковать только один раз?

Да, хороший вопрос. Это должно было быть моим вторым предложением: использовать многопоточность. Это то, что люди постоянно делают с таблицами. Когда таблица запрашивает изображение, вы либо уже имеете изображение, либо его нет. Если вы этого не сделаете, вы предоставите изображение-заполнитель, а в фоновом режиме получите реальное изображение. Когда реальное изображение будет готово, вы договоритесь о получении уведомления. Вернувшись к основному потоку, вы говорите табличному представлению снова запросить данные для этой строки; на этот раз у вас есть изображение, и вы его поставляете. Таким образом, перед появлением изображения пользователь увидит небольшую задержку. Я уверен, что вы видели множество приложений, которые ведут себя подобным образом (хороший пример - New York Times).

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

ДОБАВЛЕНО ПОЗЖЕ: Конечно, вы понимаете, что многие из этих беспокойств были бы ненужными, если бы вы не сохраняли изображения на диск. Я не совсем понимаю, зачем вам это нужно. Я надеюсь, что у вас есть для этого веская причина; но это, очевидно, намного быстрее, если вы просто держите изображения готовыми в памяти.

person matt    schedule 29.03.2013
comment
Привет спасибо за ответ Я уже использую фоновый поток для загрузки изображений. Изображения имеют размер, отображаемый в tableView, поэтому они настолько малы, насколько это возможно. И когда они загружаются с диска, я храню их в памяти как можно дольше. Но всего этого все равно было недостаточно: при прокрутке моего tableView декомпрессия занимала примерно 1/3 времени. Я использовал ваше предложение использовать ImageIO, в CGImageSourceCreateImageAtIndex я установил для kCGImageSourceShouldCache значение YES. Теперь он хранит несжатую версию в памяти и обрабатывается намного быстрее. Спасибо. - person David; 29.03.2013
comment
Очень хорошо! Хотя я все еще немного обеспокоен; Надеюсь, вы проверите с инструментами и убедитесь, что все в порядке, когда пользователь прокручивает представление таблицы на реальном устройстве. - person matt; 29.03.2013
comment
Я использовал профилировщик времени, именно так я обнаружил, что именно распаковка вызывает такие огромные задержки. copyImageBlockSetPNG и png_read_now были функциями, которые занимали 1/3 времени. Я тестирую iPhone 3GS и iPod Touch 4G, два самых медленных устройства, которые поддерживает наше приложение. Даже если я отключу кеширование и загружаю изображения с диска каждый раз, когда они становятся видимыми, я могу лишь очень немного видеть пустой imageView перед отображением изображения, но пользовательский интерфейс не зависает. А при включенном кешировании это совершенно незаметно. Это сработало отлично, так что еще раз спасибо. - person David; 30.03.2013
comment
Отлично, спасибо за этот отчет. Интересная проблема, и я рад, что фреймворк ImageIO помог; это действительно хорошее дополнение к iOS, и слишком малоизвестное. - person matt; 30.03.2013

Я нашел решение:

CGImageRef downloadedImageRef = downloadedImage.CGImage;
CGDataProviderRef provider = CGImageGetDataProvider(downloadedImageRef);
NSData *data = CFBridgingRelease(CGDataProviderCopyData(provider));
// Then you can save the data
person kelin    schedule 24.04.2015

ЕСЛИ вы загружаете данные и сохраняете их на диск, данные сжимаются в формате PNG, JPEG или GIF. Вы не собираетесь загружать несжатые данные изображений. Итак, корень вашего вопроса о выполнении распаковки необходимо решить, прежде чем сохранять файл на диск. Распаковка перед сохранением увеличит размер файла, но это означает, что распаковка не требуется, прежде чем данные будут считаны обратно в CGImageRef или UIImage. Это загрузка, а затем распаковка кучи изображений, которая замедляет ваш процессор и замедляет прокрутку. Но это не решение просто хранить все в памяти в уже распакованном виде, потому что это израсходует всю память вашего приложения и вскоре приведет к сбою вашего телефона. Возможно, вам удастся обойтись без небольшого количества изображений, но это основной недостаток дизайна, который вам необходимо устранить при первом написании кода. Если хотите, можете посмотреть мое сообщение в блоге по этой теме video-and-memory-usage-on-ios-devices, сообщение касается видео, но у вас точно такая же проблема при работе с большим количеством разных изображений. Я бы посоветовал вам записывать свои небольшие изображения на диск в несжатом формате, таком как TIFF или BMP, чтобы их было легко прочитать, если ImageIO поддерживает этот конкретный формат.

person MoDJ    schedule 18.06.2013