Почему масштабирование UIImage с камеры происходит так медленно? - PullRequest
4 голосов
/ 05 декабря 2009

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

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

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

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

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

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

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

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

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

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

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

Ответы [ 5 ]

3 голосов
/ 08 декабря 2009

Кажется, что вы сделали здесь несколько предположений, которые могут быть или не быть правдой. Мой опыт отличается от вашего. Этот метод , кажется, занимает только 20-30 мс на моих 3Gs при масштабировании фотографии, снятой с камеры, до 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 мс очень существенной разницей. Может быть, сравнить код в этом сообщении с тем, что вы используете?

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

2 голосов
/ 14 декабря 2009

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

Мне приходится много работать с 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, depress_onepass, sep_upsample) занимает 93% времени. Только 7% времени фактически затрачивается на ripc_RenderImage, который, как я полагаю, является фактическим рисунком.

2 голосов
/ 09 декабря 2009

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

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

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

CGContextSetInterpolationQuality(context, kCGInterpolationLow)

в контексте, который вы возвращаете из CGBitmapContextCreate, до того, как вы сделаете CGContextDrawImage.

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

1 голос
/ 21 июня 2011

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

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

0 голосов
/ 14 декабря 2009

НЕ ИСПОЛЬЗУЙТЕ 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();

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

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

...