高质量的JPEG压缩与C#

39

我正在使用C#,希望使用JPEG格式保存图像。然而,.NET会降低图像的质量并以不足够的压缩保存它们。

我想要以原始的质量和大小保存文件。我正在使用以下代码,但压缩和质量与原始图像不同。

Bitmap bm = (Bitmap)Image.FromFile(FilePath); 
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders(); 
ImageCodecInfo ici = null; 

foreach (ImageCodecInfo codec in codecs)
{ 
    if (codec.MimeType == "image/jpeg") 
    ici = codec; 
} 

EncoderParameters ep = new EncoderParameters(); 
ep.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, (long)100); 
bm.Save("C:\\quality" + x.ToString() + ".jpg", ici, ep);

我正在归档工作室照片,质量和压缩非常重要。谢谢。

6个回答

21

库内置的.Net编码器(至少是由Microsoft提供的默认Windows库)非常糟糕:

http://b9dev.blogspot.com/2013/06/nets-built-in-jpeg-encoder-convenient.html

部分更新

我现在使用这里概述的方法,使用ImageMagick进行调整大小,然后使用jpegoptim进行最终压缩,效果更好。我知道这只是一个部分答案,但一旦有时间,我会进一步扩展。

旧答案

到目前为止,我发现ImageMagick是最好的选择。它可以相对稳定地进行jpeg压缩。

http://magick.codeplex.com/

它有几个缺点:
  1. 虽然更好但并不完美。特别是,其色度子采样设置为90%或以上的高详细级别,然后跳到较低的详细级别 - 这可能会引入许多伪影。如果您想忽略子采样,则实际上相当方便。但是,如果您想在例如50%处进行高详细级别的子采样,则需要面对更大的挑战。它仍然无法达到Photoshop或Google PageSpeed的质量/压缩水平。
  2. 它对服务器有一个特殊的部署负担,很容易被忽视。它需要安装Visual Studio 2008 SDK lib。这个lib在任何带有Visual Studio的开发机器上都可用,但是当您第一次访问服务器时,它会因为一个晦涩的错误而崩溃。这是大多数人不会脚本化/自动化的那些潜在问题之一,在未来的某个服务器迁移期间您将会遇到它。

最早的答案

我找到了一个项目,通过翻译C项目来实现C#JPEG编码器:

http://www.codeproject.com/Articles/83225/A-Simple-JPEG-Encoder-in-C

我稍微简化了一下:

https://github.com/b9chris/ArpanJpegEncoder

这个程序生成的JPEG图像质量比.Net内置的要高得多,但仍然不如Gimp或Photoshop。文件大小也往往更大。

BitMiracle的实现与.Net内置的几乎完全相同 - 存在相同的质量问题。

最有效的选择可能是将现有的开源实现(例如PageSpeed工具中的Google jpeg_optimizer - 底层似乎是libjpeg)包装起来。

更新

ArpanJpegEncoder似乎存在一些问题,一旦部署就会出现问题 - 也许我需要提高代码的信任级别,或者可能发生了其他事情。在本地,它可以正常写入图像,但一旦部署,每次都会得到一个空的黑色图像。如果我确定原因,我会进行更新。只是提醒其他人考虑这个问题。


2
+1 表示.NET内置的JPEG编码器不好。即使在质量为100的情况下,它仍会明显降低质量。质量100会产生大文件和低质量。使用这个.NET内置编码器,我发现最好(即在最小质量损失的情况下,文件大小最合理)使用70(适用于大图片)到90(适用于小图片)之间的Encoder.Quality。 - Aximili
5
我是Magick.NET的创建者,我想告诉您我最近添加了一个JpegOptimizer类,可以用来获得与Google PageSpeed相同的大小。您可能希望更新这个答案。 - dlemstra
很棒的答案。我使用了 .Net 库,但压缩级别损坏了图像质量。然后我尝试了 Magick,它非常简单,并且在压缩图像后质量更好。 - Misha Zaslavsky
@dlemstra 感谢您的更新!不幸的是,我仍在使用jpegoptim.exe,因为它支持流,所以我可以在RAM中完成整个图像处理流程,而无需写入磁盘或更复杂的RAM磁盘。有没有可能将jpegOptim/libjpeg的压缩方法重载扩展为接受流?https://github.com/dlemstra/Magick.NET/tree/master/Source/Magick.NET.Native - Chris Moschini
1
@ChrisMoschini 最新版本的Magick.NET支持从流压缩图像。 - dlemstra
显示剩余2条评论

15

看起来你将图像质量设置为100%。这意味着没有压缩。

如果你改变了压缩级别(80、50等),并且对质量不满意,你可能需要尝试不同的图像库。 LEADTools 有一个良好的(非免费)引擎。

更新:正如评论者所提到的,即使使用JPEG,100%的质量仍然不意味着无损压缩。加载图像,对其进行某些操作,然后再次保存它最终会导致图像退化。如果您需要修改和保存图像而不会失去任何数据,则需要使用无损格式,例如TIFF,PNG或BMP。我建议使用压缩的TIFF(因为即使它被压缩,它仍然是无损的)或PNG。


3
将质量设置为100%并不意味着在使用.NET JPG编码时它是无损的。我个人使用上述提到的LEADTools(免费!)JPG编码器从我的.NET代码中。这是一个可执行文件,我提供一个图像源,启动可执行文件,然后将结果读回内存以进行进一步处理。 - Andy
1
不仅如此,.Net内置的JPEG编码器也非常糟糕。你可以在蓝色背景上画一个黄色框,将编码器设置为jpeg 100%,而且即使不重新保存,结果仍然非常糟糕。http://brass9.com/test/netjpeg/comparison.png(我知道几何图形不是JPEG的最佳选择;想象一下在加载的照片上用.Net绘制几何形状并希望将其保存出来;照片使JPEG成为最佳选择,但即使在100%的情况下,.Net的质量仍然无法接受。) - Chris Moschini

6

压缩与质量总是需要权衡的。

JPEG始终会有损失。

您可以考虑使用PNG,并使用PNGCrush或PNGauntlet对文件进行最小化处理。


2
关于在.NET中设置压缩级别,请查看此链接(包括所有内容):http://msdn.microsoft.com/en-us/library/bb882583.aspx 关于您的问题: 通常情况下,您会将用户上传的图像保存为PNG格式,然后使用此PNG作为基础生成不同尺寸的JPG图像(并且仅在JPG图像上放置水印,而不是原始PNG图像!) 这样做的好处是:如果以后更改平台的图像尺寸,您可以保存原始PNG并基于此重新计算任何新的图像尺寸。

0
它必须以原始质量和大小保存文件。这并不是很有意义。使用有损压缩时,根据定义,您将失去一些信息。压缩图像的目的是减小文件大小。如果您需要高质量且JPEG无法满足您的需求,则可能必须使用某种无损压缩,但您的文件大小不会减少太多。您可以尝试使用“标准”库进行JPEG压缩(libjpeg),看看是否会产生不同的结果(我怀疑,但我不知道.NET在幕后使用了什么)。

0

JPEG格式本身的压缩会降低质量。也许你应该考虑文件压缩,比如#ziplib。这样你或许可以在一组文件上获得合理的压缩。


2
JPEG算法的一部分是zip压缩——压缩单个JPEG或多个JPEG通常不会带来实质性的节省。 - Chris Moschini

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接