Каков наиболее эффективный для памяти способ уменьшения масштаба изображений на iOS?
В фоновом потоке мое приложение должно читать изображения с диска, уменьшать их до размера экрана (1024x768 или 2048x1536) и сохранять их обратно на диск. Исходные изображения в основном из Camera Roll, но некоторые из них могут иметь больший размер (например, 3000x3000).
Позже, в другом потоке, эти изображения будут часто уменьшаться до разных размеров около 500x500 и снова сохраняться на диске.
Это заставляет меня задаться вопросом: какой самый эффективный способ сделать это в iOS, производительности и памяти? Я использовал два разных API:
Оба работали для меня, но мне интересно, имеют ли они какую-либо разницу в производительности и использовании памяти. И если есть лучшие варианты, конечно.
Ответы
Ответ 1
Пока я не могу сказать, что это поможет, я думаю, что стоит попытаться подтолкнуть работу к графическому процессору. Вы можете сделать это самостоятельно, создав текстурированный квадрат с заданным размером или используя GPUImage и его возможности изменения размера. Хотя у него есть некоторые ограничения размера текстуры для старых устройств, он должен иметь гораздо лучшую производительность, чем решение на базе процессора
Ответ 2
С libjpeg-turbo вы можете использовать поля scale_num
и scale_denom
jpeg_decompress_struct
, и он будет декодировать только необходимые блоки изображения. Это дало мне 250 мс декодирования + время масштабирования в фоновом потоке на 4S с оригинальным изображением 3264x2448 (с камеры, данные изображения, размещенные в памяти) до разрешения экрана iPhone. Я предполагаю, что это нормально для изображения, большого, но все же не большого.
(И да, это эффективная память. Вы можете декодировать и хранить изображение почти по строкам)
Ответ 3
То, что вы сказали в твиттере, не соответствует вашему вопросу.
Если у вас есть всплески памяти, посмотрите на инструменты, чтобы выяснить, что потребляет память. Только данные для вашего изображения с высоким разрешением - 10 мегабайт, и ваши результирующие изображения будут около 750 тысяч, если они не содержат альфа-канала.
Первая проблема заключается в том, что использование памяти низкое, для этого убедитесь, что все загружаемые изображения удаляются, как только вы закончите использовать их, что гарантирует, что базовый API C/ Objective-C память немедленно, вместо того, чтобы ждать, пока GC будет работать, что-то вроде:
using (var img = UIImage.FromFile ("..."){
using (var scaled = Scaler (img)){
scaled.Save (...);
}
}
Что касается масштабирования, существует множество способов масштабирования изображений. Самый простой способ - создать контекст, затем нарисовать его, а затем получить изображение из контекста. Вот как реализован метод MonoTouch UIImage.Scale:
public UIImage Scale (SizeF newSize)
{
UIGraphics.BeginImageContext (newSize);
Draw (new RectangleF (0, 0, newSize.Width, newSize.Height));
var scaledImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return scaledImage;
}
Производительность будет определяться включенными контекстными функциями. Например, для более качественного масштабирования потребуется изменить качество интерполяции:
context.InterpolationQuality = CGInterpolationQuality.High
Другой вариант - запустить масштабирование не на CPU, а на графическом процессоре. Для этого вы должны использовать API CoreImage и использовать фильтр CIAffineTransform.
Что касается того, какой из них быстрее, для кого-то еще нужно что-то проверить
CGImage Scale (string file)
{
var ciimage = CIImage.FromCGImage (UIImage.FromFile (file));
// Create an AffineTransform that makes the image 1/5th of the size
var transform = CGAffineTransform.MakeScale (0.5f, 0.5f);
var affineTransform = new CIAffineTransform () {
Image = ciimage,
Transform = transform
};
var output = affineTransform.OutputImage;
var context = CIContext.FromOptions (null);
return context.CreateCGImage (output, output.Extent);
}
Ответ 4
Если один из них более эффективен, то он будет первым.
Когда вы создаете CGImageSource
, вы создаете именно то, что говорит это имя, - какую-то непрозрачную вещь, из которой можно получить изображение. В вашем случае это будет ссылка на вещь на диске. Когда вы запрашиваете ImageIO для создания эскиза, вы явно говорите ему "сделайте столько, сколько вам нужно, чтобы вывести это много пикселей".
И наоборот, если вы рисуете до CGBitmapContext
, то в какой-то момент вы явно вносили целое изображение в память.
Таким образом, второй подход определенно имеет все изображение в памяти сразу в какой-то момент. И наоборот, первое необязательно (на практике, несомненно, будет какая-то догадка в ImageIO, чтобы лучше всего продолжить). Таким образом, во всех возможных реализациях ОС либо первый будет выгодным, либо между ними не будет никакой разницы.
Ответ 5
Я бы попытался использовать библиотеку c-типа, например, leptonica. Я не уверен, что ios оптимизирует Core Graphics с относительно новой Accelerate Framework, но CoreGraphics, вероятно, имеет больше накладных расходов, чтобы просто изменить размер изображения. Наконец... Если вы хотите запустить собственную реализацию, попробуйте использовать vImageScale _?? format?? с некоторыми файлами с отображением памяти, я не вижу ничего быстрее.
http://developer.apple.com/library/ios/#documentation/Performance/Conceptual/vImage/Introduction/Introduction.html
PS. Также проверяйте флаги оптимизации компилятора.
Ответ 6
Я думаю, что если вы хотите сохранить память, вы можете прочитать исходное изображение из плитки в плитку и сжать плитку и сохранить ее на целевую плиту.
Есть пример из яблока. Это реализация пути.
https://developer.apple.com/library/ios/samplecode/LargeImageDownsizing/Introduction/Intro.html
Вы можете загрузить этот проект и запустить его. Это MRC, поэтому вы можете использовать его очень плавно.
Помогите.:)