Подтвердить что ты не робот

Каков наиболее эффективный для памяти способ уменьшения масштаба изображений на iOS?

В фоновом потоке мое приложение должно читать изображения с диска, уменьшать их до размера экрана (1024x768 или 2048x1536) и сохранять их обратно на диск. Исходные изображения в основном из Camera Roll, но некоторые из них могут иметь больший размер (например, 3000x3000).

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

Это заставляет меня задаться вопросом: какой самый эффективный способ сделать это в iOS, производительности и памяти? Я использовал два разных API:

Оба работали для меня, но мне интересно, имеют ли они какую-либо разницу в производительности и использовании памяти. И если есть лучшие варианты, конечно.

4b9b3361

Ответ 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, поэтому вы можете использовать его очень плавно.

Помогите.:)