如何在缩小图像时获得更好的结果

10

我正在使用c#对图像进行缩小,并且已经将我的方法与Photoshop cs5中最佳方法进行了比较,但无法复制它。

在PS中,我使用的是bicubic sharper,看起来非常好。然而,在尝试在c#中进行相同操作时,我没有得到如此高质量的结果。我尝试过bicubic interpolation以及HQ bicubic,平滑模式HQ/None/AA。我尝试了约50种不同的合成模式,每一种都与右侧的图像非常接近。

您会注意到她的背部和标题周围的像素化,以及作者姓名不太清晰。

(左侧为PS,右侧为c#。)

输入图像描述

似乎即使将平滑设置为none,c#的bicubic也会过度平滑处理。我一直在尝试以下代码的许多变体:

 g.CompositingQuality = CompositingQuality.HighQuality;
 g.InterpolationMode = InterpolationMode.HighQualityBicubic;
 g.PixelOffsetMode = PixelOffsetMode.None;
 g.SmoothingMode = SmoothingMode.None;

编辑: 根据要求,这是起始图像(1mb)。 输入图像描述


提供原始链接可能会有所帮助。 - Marcelo Cantos
这是一个非常高质量的JPEG文件,我会发布它。 - The Muffin Man
1
你所说的正确图像是使用C#生成的,但存在JPEG压缩伪影。 - Rick Sladkey
@Rick,让我感到困惑的是为什么?我无法通过上面代码的各种变化来摆脱它们。HQ双三次平滑图像,不像Photoshop的双三次版本那样模糊。 - The Muffin Man
@Nick:任何插值模式都不会产生JPEG伪影。它们要么存在于插值之前,要么在保存时引入。 - Rick Sladkey
@Rick,那我做错了什么?在Ps和C#中,我都使用了上面的大图像。 - The Muffin Man
3个回答

12

也许我有所遗漏,但我通常使用以下代码来调整/压缩JPEG图像。个人认为,根据您的源图像,结果还不错。该代码不能处理一些输入参数的边缘情况,但总体上完成了工作(如果您感兴趣,我有其他扩展方法用于裁剪和组合图像变换)。

将图像缩小至原始大小的25%并使用90%压缩。 (输出文件约30KB)

SampleImage

图像缩放扩展方法:

public static Image Resize(this Image image, Single scale)
{
  if (image == null)
    return null;

  scale = Math.Max(0.0F, scale);

  Int32 scaledWidth = Convert.ToInt32(image.Width * scale);
  Int32 scaledHeight = Convert.ToInt32(image.Height * scale);

  return image.Resize(new Size(scaledWidth, scaledHeight));
}

public static Image Resize(this Image image, Size size)
{
  if (image == null || size.IsEmpty)
    return null;

  var resizedImage = new Bitmap(size.Width, size.Height, image.PixelFormat);
  resizedImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);

  using (var g = Graphics.FromImage(resizedImage))
  {
    var location = new Point(0, 0);
    g.InterpolationMode = InterpolationMode.HighQualityBicubic;
    g.DrawImage(image, new Rectangle(location, size), new Rectangle(location, image.Size), GraphicsUnit.Pixel);
  }

  return resizedImage;
}

压缩扩展方法:

public static Image Compress(this Image image, Int32 quality)
{
  if (image == null)
    return null;

  quality = Math.Max(0, Math.Min(100, quality));

  using (var encoderParameters = new EncoderParameters(1))
  {
    var imageCodecInfo = ImageCodecInfo.GetImageEncoders().First(encoder => String.Compare(encoder.MimeType, "image/jpeg", StringComparison.OrdinalIgnoreCase) == 0);
    var memoryStream = new MemoryStream();

    encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, Convert.ToInt64(quality));

    image.Save(memoryStream, imageCodecInfo, encoderParameters);

    return Image.FromStream(memoryStream);
  }
}

使用方法:

  using(var source = Image.FromFile(@"C:\~\Source.jpg"))
  using(var resized = source.Resize(0.25F))
  using(var compressed = resized.Compress(90))
    compressed.Save(@"C:\~\Output.jpg");

注意: 对于可能发表评论的任何人,您不能在压缩方法中创建的MemoryStream被释放之前将其dispose。如果反射到MemoryStream上的Dispose实现,实际上不必显式调用dispose是安全的。唯一的替代方案是将图像/内存流包装在实现了Image/IDisposable接口的自定义类中。


1
@Tim - 如果你的样例图片能正常运行,那上面的代码就没有问题了...如果你有特定的问题,最好是开一个新的问题来问。 - Chris Baxter
1
@Tim,不是的;上面的代码就是整个压缩/调整大小功能。我唯一可用的额外项目是基本的裁剪支持(以及一些混合扩展来组合步骤)。你有特别需要吗? - Chris Baxter
@卡尔加里程序员,我按照您的代码进行了操作,但是得到了一张黑色的奇怪图片。您能否告诉我出了什么问题?我给定的尺寸是100x50。 - Anish Karunakaran
@Anish 我不确定你的意思,建议你提出一个新问题并附加更多细节。 - Chris Baxter
@ Calgary Coder:我已经按照您的建议提出了一个单独的问题。 http://stackoverflow.com/questions/17140710/re-size-image-not-working-c-sharp - Anish Karunakaran
显示剩余7条评论

3

从图片上看,特别是在顶部,JPEG图像的伪像很多,我认为你将jpg压缩设置得太高了。这会导致文件大小更小,但降低了图像质量并增加了模糊度。

你能否尝试以更高的质量保存它?我假设包含CompositingQuality.HighQuality的行已经实现了这一点,但也许你可以找到更高质量的模式。Photoshop和C#之间的文件大小有什么区别?在保存并重新打开Photoshop图像后,它的效果如何?仅在Photoshop中调整大小不会导致任何jpg数据丢失。只有在将图像保存为jpg并关闭并重新打开它后才会注意到这一点。


PS是34kb,而21kb。Graphics类有详尽的属性和方法列表,但我认为以上4个是主要的。我不知道如何获得比HighQuality设置更高质量的内容。 - The Muffin Man
我也不知道,但你在这里粘贴的图像似乎没有很高的质量。在Photoshop中,你得到了High和Very High两个选项。每个“命名”级别代表一系列实际值,其中Very High表示80到100的质量,而High表示60。我不知道这些数字如何转换为C#,但正如你所看到的,Photoshop图像的大小几乎是原来的两倍,因此它可能以更高的质量存储。 - GolezTrol
@Nick - 质量是一个介于0和100之间的数字值。数值越高,压缩越少,图像质量越高。 - ChrisF
我偶然发现了这个问答。也许对你有用:https://dev59.com/iXVC5IYBdhLWcg3wliGe - GolezTrol
Drawing2D类也有一个QualityMode属性,但由于它是一个通用的绘图类,我不知道这些属性是否会在存储文件时实际影响Jpeg压缩。在我看来,这些属性似乎是无关的,主要影响屏幕渲染质量。 - GolezTrol

1

我偶然发现了这个问题。

我使用了这段代码来取消对jpeg图像的压缩,结果看起来与PS版本相似:

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

是的,基本上与上面显示的压缩扩展方法相同。实际上,您可以将图像质量降低到90/95,而不会有任何明显的质量损失,以减小图像大小。 - Chris Baxter
1
你可以使用100L作为参数,而不是(long)100或者仅仅是100。C#会在需要时将int转换为long,而100L是一个long字面量。 - Kim Johansson
@Kim,是的,我知道,你知道为什么它首先被转换为长整型吗?如果它期望一个0-100之间的数字,那么int16(短整型)就可以工作。 - The Muffin Man

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