Quel est le moyen le plus efficace sur le plan de la memory de réduire les images sur iOS?

En arrière-plan, mon application doit lire les images à partir du disque, les réduire à la taille de l'écran (1024×768 ou 2048×1536) et les sauvegarder sur le disque. Les images originales proviennent principalement du rouleau de camera, mais certaines d'entre elles peuvent avoir des tailles plus grandes (par exemple 3000×3000).

Plus tard, dans un thread différent, ces images seront fréquemment redimensionnées à différentes tailles autour de 500×500 et sauvegardées sur le disque à nouveau.

Cela m'amène à me requestr: quel est le moyen le plus efficace de le faire dans iOS, la performance et la memory? J'ai utilisé deux API différentes:

  • en utilisant CGImageSource et CGImageSourceCreateThumbnailAtIndex partir d'ImageIO;
  • dessiner à CGBitmapContext et save les résultats sur disque avec CGImageDestination .

Les deux ont fonctionné pour moi mais je me request s'ils ont n'importe quelle différence dans la représentation et l'utilisation de memory. Et s'il y a de meilleures options, bien sûr.

Bien que je ne puisse pas vraiment dire que ça va aider, je pense que ça vaut la peine d'essayer de pousser le travail vers le GPU. Vous pouvez le faire vous-même en rendant un quad texturé à une taille donnée, ou en utilisant GPUImage et ses capacités de redimensionnement. Bien qu'il présente certaines limitations de taille de texture sur les périphériques plus anciens, il devrait avoir de bien meilleures performances que la solution basée sur le processeur.

Avec libjpeg-turbo, vous pouvez utiliser les champs scale_num et scale_denom de jpeg_decompress_struct , et il ne décodera que les blocs nécessaires d'une image. Il m'a donné 250 ms déencoding + time de mise à l'échelle dans le fil de fond sur 4S avec l'image originale 3264×2448 (à partir de l'appareil photo, datatables d'image placées en memory) à la résolution d'affichage de l'iPhone. Je suppose que c'est OK pour une image aussi grande, mais pas géniale.

(Et oui, la memory est efficace Vous pouvez décoder et stocker l'image presque ligne par ligne)

Ce que vous avez dit sur twitter ne correspond pas à votre question.

Si vous avez des pics de memory, examinez les instruments pour déterminer ce qui consume la memory. Juste datatables seules pour votre image haute résolution est de 10 Mo, et vos images résultantes seront d'environ 750 Ko, si elles ne contiennent pas de canal alpha.

Le premier problème est de limiter l'utilisation de la memory. Pour cela, assurez-vous que toutes les images que vous chargez sont éliminées dès que vous les avez utilisées, afin que l'API C / Objective-C sous-jacente dispose immédiatement de la memory , au lieu d'attendre que le GC fonctionne, donc quelque chose comme:

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

En ce qui concerne la mise à l'échelle, il existe plusieurs façons de mettre à l'échelle les images. Le plus simple est de créer un context, puis de dessiner dessus, puis de sortir l'image du context. C'est ainsi que la méthode UIImage.Scale de MonoTouch est implémentée:

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

Les performances seront régies par les fonctionnalités de context que vous activez. Par exemple, une mise à l'échelle de qualité supérieure nécessiterait de modifier la qualité d'interpolation:

  context.InterpolationQuality = CGInterpolationQuality.High 

L'autre option consiste à exécuter votre mise à l'échelle non pas sur le processeur, mais sur le GPU. Pour ce faire, vous utiliserez l'API CoreImage et utiliserez le filter CIAffineTransform.

Quant à savoir lequel est le plus rapide, il rest quelque chose à quelqu'un d'autre pour évaluer

 CGImage Scale (ssortingng 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 l'un ou l'autre est plus efficace des deux alors ce sera le premier.

Lorsque vous créez une CGImageSource vous créez exactement ce que le nom indique – une sorte de chose opaque à partir de laquelle une image peut être obtenue. Dans votre cas, ce sera une reference à une chose sur le disque. Lorsque vous requestz à ImageIO de créer une miniature, vous lui dites explicitement "faites autant de choses que nécessaire pour produire autant de pixels".

Inversement, si vous dessinez sur un CGBitmapContext à un moment donné vous apportez explicitement l'image entière en memory.

Donc, la deuxième approche a certainement l'image entière en memory à la fois à un moment donné. Inversement, les premiers n'ont pas nécessairement besoin (dans la pratique, il y aura sans doute une sorte de conjecture au sein d'ImageIO quant à la meilleure façon de procéder). Donc, à travers toutes les implémentations possibles de l'OS soit le premier sera avantageux ou il n'y aura pas de différence entre les deux.

J'essaierais d'utiliser une bibliothèque basée sur c comme leptonica. Je ne suis pas sûr que ios optimise Core Graphics avec le relativement récent Accelerate Framework, mais CoreGraphics a probablement plus de frais généraux juste pour resize une image. Enfin … Si vous voulez lancer votre propre implémentation, essayez d'utiliser vImageScale _ ?? format ?? soutenu avec quelques files mappés en memory, je ne vois rien être plus rapide.

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

PS. Assurez-vous également de vérifier les indicateurs d'optimization du compilateur.

Je pense que si vous voulez save la memory, vous pouvez lire l'image source de la mosaïque à la mosaïque et compresser la mosaïque et save dans la mosaïque de destination.

Il y a un exemple de pomme. C'est la mise en place du path.

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

Vous pouvez download ce projet et l'exécuter. C'est MRC donc vous pouvez l'utiliser très facilement.

Que cela aide. 🙂