¿Cuál es la forma más eficiente de networkingucir el tamaño de las imágenes en iOS?

En el hilo de background, mi aplicación necesita leer imágenes del disco, networkingucirlas al tamaño de la pantalla (1024×768 o 2048×1536) y savelas en el disco. Las imágenes originales son en su mayoría de Camera Roll, pero algunas de ellas pueden tener tamaños más grandes (por ejemplo, 3000×3000).

Más tarde, en un hilo diferente, estas imágenes se networkingucirán con frecuencia a diferentes tamaños en torno a 500×500 y se saveán en el disco nuevamente.

Esto me lleva a preguntarme: ¿cuál es la forma más eficiente de hacer esto en iOS, performance y memory? He usado dos API diferentes:

  • usando CGImageSource y CGImageSourceCreateThumbnailAtIndex de CGImageSourceCreateThumbnailAtIndex ;
  • dibujando en CGBitmapContext y guardando los resultados en el disco con CGImageDestination .

Ambos funcionaron para mí, pero me pregunto si tienen alguna diferencia en el performance y el uso de memory. Y si hay mejores opciones, por supuesto.

Si bien definitivamente no puedo decir que será de ayuda, creo que vale la pena intentar impulsar el trabajo a la GPU. Puede hacerlo usted mismo al renderizar un quad texturado en un tamaño determinado, o al usar GPUImage y sus capacidades de cambio de tamaño. Si bien tiene algunas limitaciones de tamaño de textura en los dispositivos más antiguos, debería tener un performance mucho mejor que la solución basada en CPU

Con libjpeg-turbo puede usar los campos scale_num y scale_denom de jpeg_decompress_struct , y decodificará solo los bloques necesarios de una image. Me dio 250 ms deencoding + time de escalado en hilo de background en 4S con una image original de 3264×2448 (desde la camera, datos de image colocados en la memory) hasta la resolución de la pantalla del iPhone. Supongo que está bien para una image tan grande, pero aún así no es genial.

(Y sí, eso es memory eficiente. Puedes decodificar y almacenar la image casi línea por línea)

Lo que dijiste en Twitter no coincide con tu pregunta.

Si tiene picos de memory, mire Instruments para averiguar qué está consumiendo la memory. Solo los datos solo para su image de alta resolución son 10 megas, y las imágenes resultantes serán de aproximadamente 750k, si no contienen ningún canal alfa.

El primer problema es mantener el uso de la memory bajo, para eso, asegúrese de que todas las imágenes que cargue estén dispuestas tan pronto como haya terminado de usarlas, esto asegurará que la API C / Objective-C subyacente disponga la memory de inmediato , en lugar de esperar a que se ejecute el GC, algo así como:

  using (var img = UIImage.FromFile ("..."){ using (var scaled = Scaler (img)){ scaled.Save (...); } } 

En cuanto a la escala, hay una variedad de forms de escalar las imágenes. La forma más simple es crear un context, luego dibujarlo y luego sacar la image del context. Así se implementa el método UIImage.Scale de MonoTouch:

 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; } 

El performance se regirá por las características de context que habilite. Por ejemplo, una escala de mayor calidad requeriría cambiar la calidad de la interpolación:

  context.InterpolationQuality = CGInterpolationQuality.High 

La otra opción es ejecutar su escala no en la CPU, sino en la GPU. Para hacer eso, usaría la API de CoreImage y usaría el filter CIAffineTransform.

En cuanto a cuál es más rápido, es algo que le queda a otra persona para comparar

 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); } 

Si cualquiera de los dos es más eficiente, entonces será el primero.

Cuando creas una CGImageSource , creas exactamente lo que dice el nombre: una especie de elemento opaco del que se puede get una image. En tu caso será una reference a una cosa en el disco. Cuando le pide a ImageIO que cree una miniatura, explícale que "haga todo lo que necesite para generar tantos píxeles".

Por el contrario, si CGBitmapContext en un CGBitmapContext entonces en algún momento debes llevar explícitamente toda la image a la memory.

Entonces, el segundo enfoque definitivamente tiene la image completa en la memory al mismo time en algún momento. Por el contrario, los primeros no necesariamente (en la práctica, sin duda, habrá algún tipo de conjetura dentro de ImageIO en cuanto a la mejor manera de proceder). Entonces, en todas las implementaciones posibles del sistema operativo, la primera será ventajosa o no habrá diferencia entre las dos.

Intentaría usar una biblioteca basada en c como leptonica. No estoy seguro de si ios optimiza Core Graphics con el relativamente nuevo Accelerate Framework, pero CoreGraphics probablemente tenga más gastos indirectos involucrados solo para cambiar el tamaño de una image. Finalmente … Si quieres implementar tu propia implementación, testing con vImageScale _ ?? format? respaldado con algunos files asignados de memory, no veo nada más rápido.

http://developer.apple.com/library/ios/#documentation/Performance/Conceptual/vImage/Introduction/Introduction.html

PD. También asegúrese de verificar las banderas de optimization del comstackdor.

Creo que si quieres save la memory, puedes leer la image de origen de mosaico a mosaico y comprimir el mosaico y save en el mosaico de destino.

Hay un ejemplo de manzana. Es la implementación del path.

https://developer.apple.com/library/ios/samplecode/LargeImageDownsizing/Introduction/Intro.html

Puede download este proyecto y ejecutarlo. Es MRC por lo que puede usarlo muy bien.

Que te ayude 🙂