Как справиться с ограничениями памяти в iOS 8 Photo Extensions?

Я добавил новое расширение iOS 8 Photo Extension в свое существующее приложение для редактирования фотографий. Мое приложение имеет довольно сложный конвейер фильтрации и должно одновременно хранить в памяти несколько текстур. Однако на устройствах с 1 ГБ ОЗУ я легко могу обрабатывать 8-мегапиксельные изображения.

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

Я провел несколько экспериментов. Я модифицировал приложение для тестирования бюджета памяти, чтобы оно работало в расширении, и получил следующие результаты (показывая количество объем ОЗУ в МБ, который можно выделить перед сбоем):

╔═══════════════════════╦═════╦═══════════╦══════════════════╗
║        Device         ║ App ║ Extension ║ Ext. (+Debugger) ║
╠═══════════════════════╬═════╬═══════════╬══════════════════╣
║ iPhone 6 Plus (8.0.2) ║ 646 ║       115 ║              645 ║
║ iPhone 5 (8.1 beta 2) ║ 647 ║        97 ║              646 ║
║ iPhone 4s (8.0.2)     ║ 305 ║        97 ║              246 ║
╚═══════════════════════╩═════╩═══════════╩══════════════════╝

Несколько наблюдений:

  • С подключенным отладчиком расширение ведет себя как «обычное» приложение.
  • Несмотря на то, что 4s имеет только половину общего объема памяти (512 МБ) по сравнению с другими устройствами, он получает те же ~ 100 МБ от системы для расширения.

Теперь мой вопрос: как я должен работать с этим небольшим объемом памяти в расширении для редактирования фотографий? Одна текстура, содержащая RGBA-изображение с разрешением 8 МП (разрешение камеры), занимает около 31 МБ. Какой смысл в этом механизме расширения, если я должен сообщить пользователю, что полноразмерное редактирование возможно только при использовании основного приложения?

Кто-то из вас тоже достиг этого барьера? Вы нашли решение обойти это ограничение?


person Frank Schlegel    schedule 16.10.2014    source источник
comment
Я должен указать, что здесь виноват UIImage; В документации Apple указано, что любая фотография или видео размером 1920 × 1080, на которые ссылается объект UIImage, неизбежно создадут проблемы с памятью для расширения для редактирования фотографий. Вы работаете не с ограничениями телефона в приложении «Фотографии», а с ограничениями, специфичными для приложения (я думаю, это как-то связано с защитой от вредоносных программ, вызывающих сбой приложения); указывая на объем памяти камеры или что-то еще бесполезно. К сожалению, вы должны уменьшить размер носителя до 1280 × 720 или меньше, говорят они. Плитка не вариант.   -  person James Bush    schedule 21.10.2015
comment
Любопытно, как это изменится с iOS 10. Согласно упоминанию @rickster о CIImageProcessorKernel ниже. В частности, при работе с другими технологиями обработки изображений, такими как GPUImage.   -  person brandonscript    schedule 11.01.2017


Ответы (3)


Я разрабатываю расширение для редактирования фотографий для своей компании, и мы столкнулись с той же проблемой. Нашему внутреннему движку обработки изображений требуется более 150 МБ для применения к изображению определенных эффектов. И это даже не считая панорамных изображений, которые занимают около 100 МБ памяти на копию.

Мы нашли только два обходных пути, но не фактическое решение.

  1. Уменьшение изображения и применение фильтра. Это потребует гораздо меньше памяти, но результат изображения ужасен. По крайней мере, расширение не будет падать.

or

  1. Используйте CoreImage или Metal для обработки изображений. Когда мы проанализировали пример расширения для редактирования фотографий от Apple , который использует CoreImage, может обрабатывать очень большие изображения и даже панорамы без потери качества или разрешения. На самом деле, нам не удалось сломать расширение, загрузив очень большие изображения. Пример кода может обрабатывать панорамы с 40-мегабайтным просмотром памяти, что довольно впечатляет.

Согласно Руководству по программированию расширений приложений от Apple. , стр. 55, глава «Обработка ограничений памяти», решение проблемы нехватки памяти в расширениях заключается в просмотре вашего кода обработки изображений. На данный момент мы портируем наш движок обработки изображений на CoreImage, и результаты намного лучше, чем у нашего предыдущего движка.

Я надеюсь, что смог немного помочь. Марко Пайва

person marcopaivaf    schedule 27.10.2014
comment
Спасибо, Марко. Я предполагаю, что CoreImage выполняет тайлинг под капотом. Я попытаюсь использовать инструменты, чтобы выяснить, что именно он делает. Моя проблема в том, что мне нужны текстуры, которые поддерживают хранение значений со знаком, и, насколько я знаю, я не могу сделать это с CoreImage прямо сейчас. Еще один момент заключается в том, что пользовательские фильтры CoreImage поддерживаются только в iOS 8, и я все еще хочу поддерживать iOS 7 в своем основном приложении. Думаю, я в конечном итоге реализую механизм тайлинга самостоятельно. Но хорошо знать, что CoreImage может обрабатывать большие изображения. Спасибо еще раз. - person Frank Schlegel; 27.10.2014
comment
Обратите внимание, что Core Image предоставляет несколько возможностей для вставки вашей собственной обработки изображения (при этом вы получаете преимущества управления памятью CI/GPU/цветом/и т. д.). Помимо возможности писать собственные ядра, которая существовала всегда, iOS 10 представляет CIImageProcessorKernel . , который позволяет вставлять технологии, отличные от CI, в цепочку обработки изображений CI. - person rickster; 10.01.2017

Если вы используете «рецепт» Core Image, вам вообще не нужно беспокоиться о памяти, как и сказал Марко. Никакое изображение, к которому применяются фильтры Core Image, не визуализируется до тех пор, пока объект изображения не будет возвращен в представление.

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

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

Для этого достаточно документации Apple, знакомящей с программированием фильтров Core Image; если вам нужны конкретные ссылки на части документации, которые, по моему мнению, относятся именно к вашим проблемам, просто спросите.

person James Bush    schedule 23.04.2015
comment
Извините, это было просто бесполезно. Я знаю, как работает Core Image и на что он способен. Он может оптимизировать некоторые последующие шаги фильтра, скомпилировав их в один, но не всегда может это сделать. Например, два последовательных ядра свертки не могут быть скомпилированы в одно — вам нужен промежуточный результат. И, как я сказал Марко, Core Image не поддерживает все функции, которые мне нужны для моего конвейера. Я уже оптимизировал свой конвейер, чтобы использовать только 4 текстуры одновременно, но это все еще слишком много памяти для расширения... - person Frank Schlegel; 20.05.2015
comment
Четыре текстуры одновременно? Как вы используете хотя бы одну текстуру в Core Image? Это не имеет смысла... - person James Bush; 21.05.2015
comment
Как я уже сказал, я не могу использовать Core Image, потому что он не предлагает всего, что мне нужно. Я написал свой собственный конвейер обработки изображений, используя OpenGL. - person Frank Schlegel; 21.05.2015
comment
У меня должно быть подмножество OpenGL, glslang. Вы, должно быть, говорите об OpenGL во всей его полноте, а это не то, что вы, вероятно, будете использовать, если пишете расширение для редактирования фотографий. Легче запустить код ядра OpenGL, написанный на glslang, чем нет; вы просто загружаете его как ресурс пакета в настраиваемый фильтр Core Image. Core Image не ограничивается встроенными фильтрами, которые — вы правы — не будут работать для всего; Я не рекомендовал его вместо OpenGL. Ядра OpenGL (glslang) могут быть загружены в пользовательский фильтр Core Image; затем вы можете комбинировать фильтры Core Image с вашим ядром, если это необходимо. - person James Bush; 21.10.2015
comment
Извини, Фрэнк; Я не переводил текстуру в сэмплер. В то время, когда вы сделали комментарий, вы были правы; iOS не позволяла передавать более одного объекта сэмплера в класс CIKernel (и вы также не могли создавать объекты CISampler). С iOS 9 все изменилось; точно так же, как MacOS X, объекты CISampler могут быть созданы и переданы кратно классу CIKernel. Еще раз извините; Я должен был потратить больше времени, чтобы понять, что вы говорите (и мне было бы очень интересно увидеть, как вы успешно передаете несколько текстур, используя собственный фильтр Core Image в приложении для iOS. - person James Bush; 21.10.2015

Вот как вы применяете два последовательных ядра свертки в Core Image с «промежуточным результатом» между ними:

- (CIImage *)outputImage { 

const double g = self.inputIntensity.doubleValue;
const CGFloat weights_v[] = { -1*g, 0*g, 1*g,
                              -1*g, 0*g, 1*g,
                              -1*g, 0*g, 1*g};

CIImage *result = [CIFilter filterWithName:@"CIConvolution3X3" keysAndValues:
          @"inputImage", self.inputImage,
          @"inputWeights", [CIVector vectorWithValues:weights_v count:9],
          @"inputBias", [NSNumber numberWithFloat:1.0],
          nil].outputImage;

CGRect rect = [self.inputImage extent];
rect.origin = CGPointZero;

CGRect cropRectLeft = CGRectMake(0, 0, rect.size.width, rect.size.height);
CIVector *cropRect = [CIVector vectorWithX:rect.origin.x Y:rect.origin.y Z:rect.size.width W:rect.size.height];
result = [result imageByCroppingToRect:cropRectLeft];

result = [CIFilter filterWithName:@"CICrop" keysAndValues:@"inputImage", result, @"inputRectangle", cropRect, nil].outputImage;


const CGFloat weights_h[] = {-1*g, -1*g, -1*g,
    0*g,   0*g,   0*g,
    1*g,   1*g,     1*g};


result = [CIFilter filterWithName:@"CIConvolution3X3" keysAndValues:
          @"inputImage", result,
          @"inputWeights", [CIVector vectorWithValues:weights_h count:9],
          @"inputBias", [NSNumber numberWithFloat:1.0],
          nil].outputImage;

result = [result imageByCroppingToRect:cropRectLeft];

result = [CIFilter filterWithName:@"CICrop" keysAndValues:@"inputImage", result, @"inputRectangle", cropRect, nil].outputImage;

result = [CIFilter filterWithName:@"CIColorInvert" keysAndValues:kCIInputImageKey, result, nil].outputImage;

return result;

}

person James Bush    schedule 21.05.2015
comment
Как я уже сказал, я знаю, как работает Core Image и как с ним применять (очень простые) сверточные фильтры. В другом вашем ответе я пытался сказать вам, что а) даже Core Image необходимо выделять несколько буферов (текстур) для некоторых сценариев и б) что я не могу делать все, что мне нужно, с помощью Core Image. Мне кажется, что Core Image сияет, когда дело доходит до объема памяти — я думаю, это потому, что он поддерживает тайлинг под капотом. Однако это также делает его сравнительно медленным, как указывает Брэд Ларсон в своей структуре GPUImage. - person Frank Schlegel; 21.05.2015