web-dev-qa-db-fra.com

Quel niveau de qualité Image.Save () utilise-t-il pour les fichiers jpeg?

Je viens d'avoir une vraie surprise quand j'ai chargé un fichier jpg, que je me suis retourné et que je l'ai sauvegardé avec une qualité de 100 et la taille était presque 4 fois l'original. Pour approfondir mes recherches, j'ouvre et enregistre sans définir explicitement la qualité et la taille du fichier est exactement la même. Je pensais que c'était parce que rien ne changeait, il est donc juste d'écrire exactement les mêmes bits dans un fichier. Pour tester cette hypothèse, j’ai tracé une grosse ligne en diagonale sur l’image et l’ai enregistrée à nouveau sans régler la qualité (cette fois-ci, j’espérais que le fichier s’écrasera car il serait "sale"), mais il a diminué de 10 Ko!

À ce stade, je ne comprends vraiment pas ce qui se passe lorsque j'appelle simplement Image.Save () en spécifiant une qualité de compression. Comment la taille du fichier est-elle si proche (après modification de l'image) de la taille d'origine alors qu'aucune qualité n'est définie pour le moment; lorsque je règle la qualité sur 100 (essentiellement aucune compression), la taille du fichier est plusieurs fois supérieure à celle de l'original?

J'ai lu la documentation sur Image.Save () et il ne manque aucun détail sur ce qui se passe dans les coulisses. J'ai cherché dans tous les sens, mais je ne trouve aucune information supplémentaire qui expliquerait ce que je vois. Je travaille depuis 31 heures d'affilée, alors il me manque peut-être quelque chose d'évident; 0)

Tout cela est arrivé alors que j'implémentais des méthodes de bibliothèque pour sauvegarder des images dans une base de données. J'ai surchargé notre méthode "SaveImage" pour permettre de définir explicitement une qualité et lors de mes tests, je suis tombé sur les résultats étranges (pour moi) expliqués ci-dessus. Toute lumière que vous pouvez jeter sera appréciée.

Voici un code qui illustrera ce que je vis:

string filename = @"C:\temp\image testing\hh.jpg";
string destPath = @"C:\temp\image testing\";

using(Image image = Image.FromFile(filename))
{
    ImageCodecInfo codecInfo = ImageUtils.GetEncoderInfo(ImageFormat.Jpeg);

    //  Set the quality
    EncoderParameters parameters = new EncoderParameters(1);

    // Quality: 10
    parameters.Param[0] = new EncoderParameter(
        System.Drawing.Imaging.Encoder.Quality, 10L);
    image.Save(destPath + "10.jpg", codecInfo, parameters);

    // Quality: 75
    parameters.Param[0] = new EncoderParameter(
        System.Drawing.Imaging.Encoder.Quality, 75L);
    image.Save(destPath + "75.jpg", codecInfo, parameters);

    // Quality: 100
    parameters.Param[0] = new EncoderParameter(
        System.Drawing.Imaging.Encoder.Quality, 100L);
    image.Save(destPath + "100.jpg", codecInfo, parameters);

    //  default
    image.Save(destPath + "default.jpg", ImageFormat.Jpeg);

    //  Big line across image
    using (Graphics g = Graphics.FromImage(image))
    {
        using(Pen pen = new Pen(Color.Red, 50F))
        {
            g.DrawLine(pen, 0, 0, image.Width, image.Height);
        }
    }

    image.Save(destPath + "big red line.jpg", ImageFormat.Jpeg);
}

public static ImageCodecInfo GetEncoderInfo(ImageFormat format)
{
    return ImageCodecInfo.GetImageEncoders().ToList().Find(delegate(ImageCodecInfo codec)
    {
        return codec.FormatID == format.Guid;
    });
}
33
Steve K

À l'aide d'un réflecteur, Image.Save() se résume à la fonction GDI + GdipSaveImageToFile , avec la variable encoderParams NULL. Je pense donc que la question est de savoir ce que le codeur JPEG fait quand il obtient une valeur null encoderParams. 75% a été suggéré ici, mais je ne trouve aucune référence solide.

EDIT Vous pourriez probablement le découvrir vous-même en exécutant votre programme ci-dessus pour des valeurs de qualité de 1..100 et en les comparant avec le jpg enregistré avec la qualité par défaut (en utilisant, par exemple, fc.exe/B)

19
Ohad Schneider

IIRC, c'est 75%, mais je ne me rappelle pas où j'ai lu ceci.

5
leppie

Je ne connais pas grand chose à la méthode Image.Save, mais je peux vous dire que l’ajout de cette ligne grasse réduirait logiquement la taille de l’image jpg. Cela est dû à la façon dont un jpg est enregistré (et encodé).

La ligne noire épaisse permet un encodage très simple et plus petit (si je me souviens bien, c'est surtout après la transformation en cosinus discrète), de sorte que l'image modifiée peut être stockée avec moins de données (octets).

étapes d'encodage jpg

En ce qui concerne les changements de taille (sans la ligne ajoutée), je ne sais pas quelle image vous avez rouverte et sauvegardée 

Pour approfondir mes recherches, j'ouvre et enregistre sans définir explicitement la qualité et la taille du fichier est exactement la même

Si vous avez ouvert l'ancienne image (taille normale d'origine) et que vous l'avez réenregistrée, la compression par défaut et la compression de l'image d'origine sont identiques .. Si vous ouvrez la nouvelle image (4 fois plus grande) et que vous la réenregistrez, il la compression pour la méthode de sauvegarde est dérivée de l'image (telle qu'elle était lors du chargement).

Encore une fois, je ne connais pas la méthode de sauvegarde, alors je ne fais que lancer des idées (peut-être qu’ils vous donneront l’avance).

0
Neowizard

Lorsque vous enregistrez une image au format JPEG avec un niveau de qualité <100%, vous introduisez des artefacts dans l'image enregistrée, qui sont un effet secondaire du processus de compression. C'est pourquoi la ré-enregistrement de l'image à 100% augmente en réalité la taille de votre fichier au-delà de l'original. Ironiquement, le bitmap contient plus d'informations.

C’est aussi la raison pour laquelle vous devriez toujours essayer de sauvegarder dans un format sans perte (tel que PNG) si vous envisagez d’apporter des modifications à votre fichier par la suite, sinon vous affecterez la qualité de la sortie par le biais de multiples transformations avec perte.

0
Dave R.