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

StackOverflow https://stackoverflow.com/questions/1851361

Вопрос

изменение размера UIImage камеры, возвращаемого UIImagePickerController, занимает смехотворно много времени, если вы делаете это обычным способом, как в этот пост.

[обновить:последний призыв к творческим идеям здесь!я думаю, мой следующий вариант - пойти спросить Apple.]

Да, это много пикселей, но графическое оборудование iPhone вполне способно отображать на экране множество текстурированных квадратиков размером 1024x1024 за 1/60 секунды, так что действительно должен быть способ уменьшить размер изображения 2048x1536 до 640x480 за гораздо меньшее время, чем за 1,5 секунды.

Так почему же это происходит так медленно?Являются ли базовые данные изображения, которые ОС возвращает из средства выбора, каким-то образом не готовыми к отрисовке, так что их нужно каким-то образом обработать, с чем графический процессор не может помочь?

Мое лучшее предположение заключается в том, что его нужно преобразовать из RGBA в ABGR или что-то в этом роде;кто-нибудь может придумать способ, которым можно было бы убедить систему быстро предоставить мне данные, даже если они в неправильном формате, и я сам разберусь с этим позже?

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

Итак, вопрос:есть ли какой-нибудь альтернативный метод рисования, помимо простого использования CGBitmapContextCreate и CGContextDrawImage, который использует больше преимуществ графического процессора?

Что-то, что нужно расследовать:если я начну с UIImage того же размера, который не из средства выбора изображений, будет ли это так же медленно?По-видимому, нет...

Обновить:Мэтт Лонг обнаружил, что для изменения размера изображения, которое вы получаете из программы выбора, требуется всего 30 мс [info objectForKey:@"UIImagePickerControllerEditedImage"], если вы включили обрезку с помощью ручного управления камерой.Это не помогает в том случае, когда меня волнует, где я использую takePicture делать снимки программно.Я вижу, что отредактированное изображение является kCGImageAlphaPremultipliedFirst но исходное изображение - это kCGImageAlphaNoneSkipFirst.

Дальнейшее обновление:Предложил Джейсон Кроуфорд CGContextSetInterpolationQuality(context, kCGInterpolationLow), что фактически сокращает время примерно с 1,5 сек до 1,3 сек, снижая качество изображения - но это все еще далеко от скорости, на которую должен быть способен графический процессор!

Последнее обновление до истечения недели:пользователь refulgentis выполнил некоторое профилирование, которое, по-видимому, указывает на то, что 1,5 секунды тратятся на запись захваченного с камеры изображения на диск в формате JPEG, а затем на его повторное считывание.Если это правда, то очень странно.

Это было полезно?

Решение

Используйте Shark, составьте его профиль, выясните, что занимает так много времени.

Мне приходится много работать с MediaPlayer.framework, и когда вы получаете свойства для песен на iPod, первый запрос свойств безумно медленный по сравнению с последующими запросами, потому что в первом запросе свойств MobileMediaPlayer упаковывает словарь со всеми свойствами и передает его в мое приложение.

Я был бы готов поспорить, что здесь происходит аналогичная ситуация.

Редактировать:Мне удалось создать временной профиль в Shark как для ситуации UIImagePickerControllerEditedImage Мэтта Лонга, так и для общей ситуации UIImagePickerControllerOriginalImage.

В обоих случаях большую часть времени занимает CGContextDrawImage.В случае Мэтта Лонга UIImagePickerController заботится об этом в промежутке между захватом пользователем изображения и переходом изображения в режим редактирования.

Масштабирование процента времени, затраченного на CGContextDrawImage = 100%, CGContextDelegateDrawImage затем занимает 100%, затем ripc_DrawImage (из libRIP.A.dylib) занимает 100%, а затем ripc_AcquireImage (который, похоже, распаковывает JPEG и занимает большую часть времени в _cg_jpeg_idct_islow, vec_ycc_bgrx_convert, decompress_onepass, sep_upsample) занимает 93% времени.Только 7% времени фактически тратится на ripc_RenderImage, который, как я предполагаю, является фактическим рисунком.

Другие советы

Похоже, что вы сделали здесь несколько предположений, которые могут быть верными, а могут и не быть.Мой опыт отличается от вашего. Этот метод кажется, на моем 3Gs требуется всего 20-30 мс при масштабировании фотографии, сделанной с камеры, до 0,31 от исходного размера с помощью вызова:

CGImageRef scaled = CreateScaledCGImageFromCGImage([image CGImage], 0.31);

(Кстати, я получаю 0,31, взяв масштаб ширины, 640,0 / 2048,0)

Я проверил, чтобы убедиться, что изображение того же размера, с которым вы работаете.Вот мой вывод NSLog:

2009-12-07 16:32:12.941 ImagePickerThing[8709:207] Info: {
    UIImagePickerControllerCropRect = NSRect: {{0, 0}, {2048, 1536}};
    UIImagePickerControllerEditedImage = <UIImage: 0x16c1e0>;
    UIImagePickerControllerMediaType = "public.image";
    UIImagePickerControllerOriginalImage = <UIImage: 0x184ca0>;
}

Я не уверен почему разница есть, и я не могу ответить на ваш вопрос, поскольку он относится к графическому процессору, однако я бы счел 1,5 секунды и 30 мс очень существенной разницей.Может быть, сравнить код в этом сообщении в блоге с тем, что вы используете?

С наилучшими пожеланиями.

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

Когда вы устанавливаете allowsImageEditing=YES, возвращаемое изображение изменяется и обрезается примерно до 320x320.Это ускоряет процесс, но, вероятно, это не то, чего вы хотите.

Лучшее ускорение, которое я нашел, это:

CGContextSetInterpolationQuality(context, kCGInterpolationLow)

в контексте, который вы получаете обратно из CGBitmapContextCreate, перед выполнением CGContextDrawImage.

Проблема в том, что ваши уменьшенные изображения могут выглядеть не так хорошо.Однако, если вы уменьшаете масштаб на целое число - например, с 1600x1200 до 800x600, - тогда это выглядит нормально.

Вот проект git, который я использовал, и, похоже, он работает хорошо.Использование также довольно чистое - одна строка кода.

https://github.com/AliSoftware/UIImage-Resize

НЕ ИСПОЛЬЗУЙТЕ CGBitmapImageContextCreate в этом случае!Я провел почти неделю в той же ситуации, что и вы.Производительность будет абсолютно ужасной, и это будет безумно поглощать память.Вместо этого используйте UIGraphicsBeginImageContext:

// create a new CGImage of the desired size
UIGraphicsBeginImageContext(desiredImageSize);
CGContextRef c = UIGraphicsGetCurrentContext();

// clear the new image
CGContextClearRect(c, CGRectMake(0,0,desiredImageSize.width, desiredImageSize.height));

// draw in the image
CGContextDrawImage(c, rect, [image CGImage]);

// return the result to our parent controller
UIImage * result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

В приведенном выше примере (из моего собственного кода изменения размера изображения) "rect" значительно меньше, чем изображение.Приведенный выше код выполняется очень быстро и должен делать именно то, что вам нужно.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top