web-dev-qa-db-fra.com

Qualité d'un JPG enregistré en C #

J'ai créé une petite application C # pour créer une image au format .jpg.

pictureBox.Image.Save(name,ImageFormat.Jpeg);

L'image est créée avec succès. Je saisis une photo originale, je fais quelques trucs avec et je la sauvegarde. La qualité de cette nouvelle photo est cependant inférieure à celle de l'original.

Existe-t-il un moyen de définir la qualité souhaitée?

61
KdgDev

L'exemple de code suivant montre comment créer un EncoderParameter à l'aide du constructeur EncoderParameter. Pour exécuter cet exemple, collez le code et appelez la méthode VaryQualityLevel.

Cet exemple nécessite un fichier image nommé TestPhoto.jpg situé à c :.

private void VaryQualityLevel()
{
    // Get a bitmap.
    Bitmap bmp1 = new Bitmap(@"c:\TestPhoto.jpg");
    ImageCodecInfo jgpEncoder = GetEncoder(ImageFormat.Jpeg);

    // Create an Encoder object based on the GUID
    // for the Quality parameter category.
    System.Drawing.Imaging.Encoder myEncoder =
        System.Drawing.Imaging.Encoder.Quality;

    // Create an EncoderParameters object.
    // An EncoderParameters object has an array of EncoderParameter
    // objects. In this case, there is only one
    // EncoderParameter object in the array.
    EncoderParameters myEncoderParameters = new EncoderParameters(1);

    EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, 
        50L);
    myEncoderParameters.Param[0] = myEncoderParameter;
    bmp1.Save(@"c:\TestPhotoQualityFifty.jpg", jgpEncoder, 
        myEncoderParameters);

    myEncoderParameter = new EncoderParameter(myEncoder, 100L);
    myEncoderParameters.Param[0] = myEncoderParameter;
    bmp1.Save(@"c:\TestPhotoQualityHundred.jpg", jgpEncoder, 
        myEncoderParameters);

    // Save the bitmap as a JPG file with zero quality level compression.
    myEncoderParameter = new EncoderParameter(myEncoder, 0L);
    myEncoderParameters.Param[0] = myEncoderParameter;
    bmp1.Save(@"c:\TestPhotoQualityZero.jpg", jgpEncoder, 
        myEncoderParameters);

}

private ImageCodecInfo GetEncoder(ImageFormat format)
{
    ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
    foreach (ImageCodecInfo codec in codecs)
    {
        if (codec.FormatID == format.Guid)
        {
            return codec;
        }
    }
    return null;
}

Réf: http://msdn.Microsoft.com/en-us/library/system.drawing.imaging.encoderparameter.aspx

78
Dustin Getz

Voici un morceau de code encore plus compact pour l'enregistrement au format JPEG avec une qualité spécifique:

var encoder = ImageCodecInfo.GetImageEncoders().First(c => c.FormatID == ImageFormat.Jpeg.Guid);
var encParams = new EncoderParameters() { Param = new[] { new EncoderParameter(Encoder.Quality, 90L) } };
image.Save(path, encoder, encParams);

Ou, si les lignes larges de 120 caractères sont trop longues pour vous:

var encoder = ImageCodecInfo.GetImageEncoders()
                            .First(c => c.FormatID == ImageFormat.Jpeg.Guid);
var encParams = new EncoderParameters(1);
encParams.Param[0] = new EncoderParameter(Encoder.Quality, 90L);
image.Save(path, encoder, encParams);

Assurez-vous que la qualité est un long ou vous obtiendrez un ArgumentException!

37
Roman Starkov

Ceci est un vieux fil, mais j'ai réécrit Microsoft (selon la réponse de Dustin Getz) pour être un peu plus utile - réduire GetEncoderInfo et faire une extension sur Image. Quoi qu'il en soit, rien de vraiment nouveau, mais peut être utile:

    /// <summary>
    /// Retrieves the Encoder Information for a given MimeType
    /// </summary>
    /// <param name="mimeType">String: Mimetype</param>
    /// <returns>ImageCodecInfo: Mime info or null if not found</returns>
    private static ImageCodecInfo GetEncoderInfo(String mimeType)
    {
        var encoders = ImageCodecInfo.GetImageEncoders();
        return encoders.FirstOrDefault( t => t.MimeType == mimeType );
    }

    /// <summary>
    /// Save an Image as a JPeg with a given compression
    ///  Note: Filename suffix will not affect mime type which will be Jpeg.
    /// </summary>
    /// <param name="image">Image: Image to save</param>
    /// <param name="fileName">String: File name to save the image as. Note: suffix will not affect mime type which will be Jpeg.</param>
    /// <param name="compression">Long: Value between 0 and 100.</param>
    private static void SaveJpegWithCompressionSetting(Image image, string fileName, long compression)
    {
        var eps = new EncoderParameters(1);
        eps.Param[0] = new EncoderParameter(Encoder.Quality, compression);
        var ici = GetEncoderInfo("image/jpeg");
        image.Save(fileName, ici, eps);
    }

    /// <summary>
    /// Save an Image as a JPeg with a given compression
    /// Note: Filename suffix will not affect mime type which will be Jpeg.
    /// </summary>
    /// <param name="image">Image: This image</param>
    /// <param name="fileName">String: File name to save the image as. Note: suffix will not affect mime type which will be Jpeg.</param>
    /// <param name="compression">Long: Value between 0 and 100.</param>
    public static void SaveJpegWithCompression(this Image image, string fileName, long compression)
    {
        SaveJpegWithCompressionSetting( image, fileName, compression );
    }
14
Wolf5370

Utilisation d'attributs de style GDI + sans type ( https://msdn.Microsoft.com/en-us/library/windows/desktop/ms533845 (v = vs.85) .aspx ) pour définir la qualité JPEG semble exagéré .

Un moyen direct devrait ressembler à ceci:

FileStream stream = new FileStream("new.jpg", FileMode.Create);
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.QualityLevel = 100;   // "100" for maximum quality (largest file size).
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Save(stream);

Réf: https://msdn.Microsoft.com/en-us/library/system.windows.media.imaging.jpegbitmapencoder.rotation (v = vs.110) .aspx # Anchor_2

6
epox

La réponse wiki communautaire, qui est acceptée, renvoie à un exemple de Microsoft.

Cependant, afin de gagner du temps pour certains d'entre vous, je l'ai résumée à l'essentiel et

  • Emballé dans une méthode appropriée
  • Implémenté IDisposable. Je n'ai vu using (...) { dans aucune autre réponse. Afin d'éviter les fuites de mémoire, il est recommandé de supprimer tout ce qui implémente IDisposable.

public static void SaveJpeg(string path, Bitmap image)
{
    SaveJpeg(path, image, 95L);
}
public static void SaveJpeg(string path, Bitmap image, long quality)
{
    using (EncoderParameters encoderParameters = new EncoderParameters(1))
    using (EncoderParameter encoderParameter = new EncoderParameter(Encoder.Quality, quality))
    {
        ImageCodecInfo codecInfo = ImageCodecInfo.GetImageDecoders().First(codec => codec.FormatID == ImageFormat.Jpeg.Guid);
        encoderParameters.Param[0] = encoderParameter;
        image.Save(path, codecInfo, encoderParameters);
    }
}
5
bytecode77

Consultez l'article MSDN sur comment définir le niveau de compression JPEG .

Vous devez utiliser l'autre surcharge Save () qui prend un Image Encoder et ses paramètres.

4
JoshJordan

Si vous utilisez le .NET Compact Framework, une alternative pourrait être d'utiliser le format PNG sans perte, c'est-à-dire:

image.Save(filename, ImageFormat.Png);
2
AlainD