2009-09-27 22 views
53

Zrobiłem małą aplikację C#, aby utworzyć obraz w formacie .jpg.Jakość zapisanego pliku JPG w języku C#

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

Obraz został pomyślnie utworzony. Wpisuję oryginalne zdjęcie, robię z nim pewne rzeczy i zapisuję je. Jakość tego nowego zdjęcia jest jednak niższa niż w oryginale.

Czy istnieje sposób na ustawienie żądanej jakości?

Odpowiedz

71

Poniższy przykład kodu pokazuje, jak utworzyć EncoderParameter za pomocą konstruktora EncoderParameter. Aby uruchomić ten przykład, wklej kod i wywołaj metodę VaryQualityLevel.

Ten przykład wymaga pliku obrazu o nazwie TestPhoto.jpg, który znajduje się pod adresem 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; 
} 

Ref: http://msdn.microsoft.com/en-us/library/system.drawing.imaging.encoderparameter.aspx

+2

To działa. Wydawałoby się, że bez tego wszystkiego używana jest standardowa jakość 50 litrów. – KdgDev

+0

Masz literówkę. jgpEncoder, gdy masz na myśli jpgEncoder;) –

+0

EncoderParameter może używać niezarządzanych zasobów i musi być usuwany. Dokumentacja Msdn trochę brakuje na ten temat. Powinno to oznaczać, że tablica 'Param' jest inicjalizowana z elementami zerowymi (więc nic do dyspozycji przed pierwszym przypisaniem do każdego elementu) i że' EncoderParameters' posiada swoje aktualne parametry na swoim własnym zbiorze. –

13

To jest stary wątek, ale przepisałem Microsoft (jak na odpowiedź Dustina Getza), aby było trochę bardziej przydatne - zmniejszanie GetEncoderInfo i tworzenie rozszerzenia na Obrazie. Zresztą nic nowego, ale może się przydać:

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

Jeśli używasz .NET Compact Framework, alternatywą może być użycie IE bezstratny format PNG:

image.Save(filename, ImageFormat.Png); 
30

Oto nawet bardziej zwarty fragment kodu do zapisywania jako JPEG o określonej jakości:

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

lub, jeśli 120 znaków szerokie linie są zbyt długie dla ciebie:

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

Upewnij się, że jakość jest long lub dostaniesz ArgumentException!

+0

Przechodząc bezpośrednio do mojej biblioteki kodu, dziękuję bardzo! – JustJohn

+0

Zobacz także odpowiedź [bytecode77] (https://stackoverflow.com/a/39493346/199364) dla wersji tego, która używa 'using', aby upewnić się, że' Dispose' dzieje się natychmiast na końcu. – ToolmakerSteve

4

Korzystanie z atrybutów bez nazwy GDI + (https://msdn.microsoft.com/en-us/library/windows/desktop/ms533845(v=vs.85).aspx) do ustawiania jakości JPEG wygląda na przesadę.

bezpośredni sposób powinien wyglądać następująco:

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

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

+0

Niewielka uwaga: ponieważ pierwotnym pytaniem była skarga dotycząca niskiej jakości, w celu rozwiązania tego zastosowania maksymalna jakość: 'encoder.QualityLevel = 100'. – ToolmakerSteve

3

Odpowiedź wiki społeczność, która jest akceptowana, referrs na przykład firmy Microsoft.

Jednakże, w celu zaoszczędzenia czasu niektórzy z was, ja gotuje je w dół do istoty i

  • pakiety go do właściwej metody
  • Wdrożone IDisposable. Nie widziałem using (...) { w żadnej innej odpowiedzi.Aby uniknąć wycieku pamięci, najlepszą praktyką jest pozbycie się wszystkiego, co implementuje 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); 
    } 
} 
+0

Drobna uwaga: "95L", jak pokazano dla jakości, jest dobrą wartością domyślną, ponieważ jest zbliżona do maksymalnej wartości "100L", ale pozwoli zaoszczędzić trochę na rozmiarze pliku, w celu uzyskania bardzo szczegółowych zdjęć. Zwykle używam "90L" do "100L" dla zachowania wysokiej jakości, "70L" do "85L", aby uzyskać przyzwoitą jakość, ale rozsądniejszy rozmiar pliku. Zależy to również od tego, czy robisz "wielokrotną edycję" pliku. Jeśli tak, użyj "100L" do ostatniej edycji (lub edytuj w .png, aby była bezstratna), a następnie wykonaj ostateczny zapis o dowolnej jakości, jakiej potrzebujesz. – ToolmakerSteve