Redimensionnement De L'Image: Système.Dessin vs système.Windows.Média

j'ai une situation où j'ai besoin de redimensionner un grand nombre d'images. Ces images sont stockées sous .les fichiers jpg sur le système de fichiers actuellement, mais je m'attends à avoir byte[] en mémoire plus tard dans le projet. La taille de l'image source est variable, mais la sortie devrait être de 3 tailles prédéterminées différentes. Les ratios d'Aspect doivent être préservés, en remplaçant l'image originale par un espace blanc (c'est-à-dire qu'une image vraiment haute serait redimensionnée pour s'adapter à la taille de l'image cible carrée, avec de grandes zones de blanc à gauche et à droite).

j'ai initialement construit le projet en ciblant .NET 2.0, et en utilisant le système.Cours de dessin pour effectuer le chargement/redimensionner/enregistrer. Le code pertinent comprend:

original = Image.FromFile(inputFile); //NOTE: Reused for each of the 3 target sizes
Bitmap resized = new Bitmap(size, size);
//Draw the image to a new image of the intended size
Graphics g = Graphics.FromImage(resized);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.Clear(Color.White);
g.DrawImage(original, center - width / 2f, center - height / 2f, width, height);
g.Dispose();
//Save the new image to the output path
resized.Save(outputFile, ImageFormat.Jpeg);

je voulais porter ce projet à .NET 3.5, donc j'ai essayé d'utiliser le système.Windows.Classes de Médias pour remplir la même fonction. Je l'ai fait fonctionner, cependant la performance est terrible; le temps de traitement par image est environ 50x plus long. La grande majorité du temps est consacré au chargement de l'image. Le code pertinent comprend:

BitmapImage original = new BitmapImage(); //Again, reused for each of the 3 target sizes
original.BeginInit();
original.StreamSource = new MemoryStream(imageData); //imageData is a byte[] of the data loaded from a FileStream
original.CreateOptions = BitmapCreateOptions.None;
original.CacheOption = BitmapCacheOption.Default;
original.EndInit(); //Here's where the vast majority of the time is spent
original.Freeze();

// Target Rect for the resize operation
Rect rect = new Rect(center - width / 2d, center - height / 2d, width, height);

// Create a DrawingVisual/Context to render with
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
    drawingContext.DrawImage(original, rect);
}

// Use RenderTargetBitmap to resize the original image
RenderTargetBitmap resizedImage = new RenderTargetBitmap(
    size, size,                         // Resized dimensions
    96, 96,                             // Default DPI values
    PixelFormats.Default);              // Default pixel format
resizedImage.Render(drawingVisual);

// Encode the image using the original format and save the modified image
SaveImageData(resizedImage, outputFile);

je fais quelque chose de mal ici, de prendre autant de temps? J'ai essayé juste d'utiliser le constructeur sur BitmapImage qui prend une URI, même problème de performance là. Quelqu'un a déjà fait quelque chose comme ça, tu sais s'il y a une façon plus axée sur la performance de faire ça? Ou je vais juste avoir besoin d'utiliser le système.Dessin? Merci!

10
demandé sur Chadd Nervig 2009-11-17 02:06:10

4 réponses

et après avoir tapé tout cela, il m'est apparu que je pouvais charger les symboles DE MS pour le système.Windows.Des cours sur les médias, et passer à travers là où c'était lent. Immédiatement trouvé la cause et la solution. Les images d'entrée ont été enregistrées avec un profil de couleur, et il essayait de charger ce profil de couleur (à partir du système de fichiers) de chaque image. En passant de BitmapCreateOptions.Aucun à BitmapCreateOptions.IgnoreColorProfile dans le code ci-dessus, il ne fait plus cela, et effectue juste aussi vite que Système.Le dessin n'.

J'espère que cela aidera n'importe qui d'autre qui se heurte à ce problème!

10
répondu Chadd Nervig 2009-11-16 23:39:49

Vous semblez faire cela à la manière dure. Vous pouvez laisser WPF faire le travail pour vous en mettant juste DecodePixelHeight et DecodePixelWidth. Cela provoquera le redimensionnement pendant le chargement de l'image:

BitmapImage resizedImage = new BitmapImage
{
  StreamSource = new MemoryStream(imageData),
  CreateOptions = BitmapCreateOptions.IgnoreColorProfile,
  DecodePixelHeight = height,
  DecodePixelWidth = width,
}
resizedImage.BeginInit();  // Needed only so we can call EndInit()
resizedImage.EndInit();    // This does the actual loading and resizing

imageSaveImageData(resizedImage, outputFile);

j'ai aussi inclus la solution IgnoreColorProfile que vous avez trouvé dans mon code.

mise à Jour j'ai relu votre question et j'ai réalisé que la raison pour laquelle vous utilisez DrawingVisual est que vous avez besoin d'espace autour de votre image pour la rendre carrée. DecodePixelHeight et DecodePixelWidth n'atteindraient pas cet objectif, donc ma solution ne répond pas à votre question.

je vais laisser ma réponse ici au cas où quelqu'un qui a juste besoin d'un redimensionnement sans espace blanc tombe sur cette question.

5
répondu Ray Burns 2009-11-17 05:41:01

je pense que cela à partir de la System.Drawing la page sur MSDN peuvent être pertinentes:

Le Système.Drawing namespace permet d'accéder aux fonctionnalités graphiques de base de GDI+. Des fonctionnalités plus avancées sont fournies dans le système.Dessin.Drawing2D, Système.Dessin.Imagerie, et système.Dessin.Texte des espaces de noms. La classe Graphics fournit des méthodes pour dessiner sur le dispositif d'affichage. Les Classes telles que les primitives Rectangle et Point encapsulent GDI+. La classe Pen est utilisée pour dessinez des lignes et des courbes, tandis que les classes dérivées de la classe abstraite pinceau sont utilisées pour remplir les intérieurs de formes.

En utilisant System.Drawing vous êtes plus proche de la fonctionnalité graphique de base réelle que si vous allez via System.Windows.Media:

définit les objets qui permettent l'intégration de médias riches, y compris les dessins, le texte et le contenu audio/vidéo dans Windows Presentation Foundation (WPF) application.

System.Drawing est toujours supporté, donc je m'en tiens à ça.

0
répondu ChrisF 2009-11-16 23:17:11

j'ai trouvé une situation intéressante dans votre code. Supprimerusing à partir de la ligne suivante:

using(DrawingContext drawingContext = drawingVisual.RenderOpen())

Je ne sais pas pourquoi ce code accélère, mais vous pouvez essayer.

0
répondu Rubens Farias 2009-11-16 23:59:41