在.NET中旋转JPEG,质量损失最小

时间:2009-07-27 06:27:42

标签: .net image-processing jpeg

我试图支持从ASP.NET MVC旋转JPEG图像(以90度为增量)。我正在尝试使用System.Drawing(GDI +),但是我遇到了问题。

我尝试使用能够旋转图像但导致质量下降的Image.RotateFlip。即使编码器质量为100,旋转图像上仍然存在可见的伪像,这些伪像不在原始图像上,也不会在我使用其他程序(Gimp等)旋转时显示。

using (Image image = Image.FromFile("C:\\source.jpg")) {
    ImageFormat sourceFormat = image.RawFormat;
    image.RotateFlip(RotateFlipType.Rotate90FlipNone);
    EncoderParameters encoderParams = null;
    try {
        if (sourceFormat == ImageFormat.Jpeg) {
            encoderParams = new EncoderParameters(1);
            encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, 100L);
        }
        image.Save("C:\\target.jpg", GetEncoder(sourceFormat), encoderParams);
    } finally {
        if (encoderParams != null)
            encoderParams.Dispose();
    }
}

我在transforming a JPEG without loss of information找到了一篇文章。使用Encoder.Transformation似乎是.NET中的一个选项 - 但我无法让它导致我的任何JPEG测试图像完全旋转,无论尺寸是否是16的倍数。

using (Image image = Image.FromFile("C:\\source.jpg")) {
    ImageFormat sourceFormat = image.RawFormat;
    EncoderParameters encoderParams = null;
    try {
        if (sourceFormat == ImageFormat.Jpeg) {
            encoderParams = new EncoderParameters(1);
            encoderParams.Param[0] = new EncoderParameter(Encoder.Transformation, 
                (long)EncoderValue.TransformRotate90);
        }
        image.Save("C:\\target.jpg", GetEncoder(sourceFormat), encoderParams);
    } finally {
        if (encoderParams != null)
            encoderParams.Dispose();
    }
}

有没有人知道如何使用上述方法或其他方法,以90度为增量成功地在.NET中旋转JPEG,而质量损失最小或没有损失?感谢。

此外,这是我GetEncoder的实现:

private ImageCodecInfo GetEncoder(ImageFormat format) {
    foreach (var info in ImageCodecInfo.GetImageEncoders())
        if (info.FormatID == format.Guid)
            return info;
    return null;
}

编辑:

我更新了上面的代码,以便更好地匹配我的实际代码。该错误发生在以下行:

if (sourceFormat == ImageFormat.Jpeg) {

应该是:

if (sourceFormat.Guid == ImageFormat.Jpeg.Guid) {

3 个答案:

答案 0 :(得分:2)

使用任何解压缩图像,旋转并压缩图像的方法,都会导致质量下降。

JPEG格式通过获得表示所有四个像素的平均颜色来压缩2x2像素的正方形中的颜色信息,因此如果您的图像宽度和高度可被2整除,则在压缩中移除的大部分信息将会降低质量是在解压缩中插入的信息。

同样,亮度信息以8x8像素的正方形压缩,因此如果您的宽度和高度可以除以8,则在旋转图像后网格将对齐,您将丢失更少的实际信息。

要进行无损旋转,您必须使用完全不同的方法,读取JPEG文件,重新排列和旋转每个方格的信息,使其形成旋转的图像,而不进行解压缩和重新压缩。

答案 1 :(得分:1)

感谢您确认我发布的代码有效。这有助于我解决我的问题。我现在觉得很蠢。我的实际代码在设置encoderParams之前检查了图像格式 - 但它有一个错误:

if (sourceFormat == ImageFormat.Jpeg) {
    // set encoderParams here

我发现上面的条件总是假的,因此没有设置encoderParams。修复很简单:

if (sourceFormat.Guid == ImageFormat.Jpeg.Guid) {

答案 2 :(得分:1)

这是我对迈克·亨利(Mike Henry)的答案的改编,它针对可能会有用的人进行了修改。

public MemoryStream RotateImage(Stream stream, RotateFlipType rotationFlipType)
{
    try
    {
        if (stream != null)
        {
            using (Image image = Image.FromStream(stream))
            {
                ImageFormat sourceFormat = image.RawFormat;
                image.RotateFlip(rotationFlipType);
                EncoderParameters encoderParams = null;
                try
                {
                    if (sourceFormat.Guid == ImageFormat.Jpeg.Guid)
                    {
                        encoderParams = new EncoderParameters(1);
                        encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, 100L);
                    }
                    var ms = new MemoryStream();
                    image.Save(ms,
                        ImageCodecInfo.GetImageEncoders().FirstOrDefault(e => e.FormatID == sourceFormat.Guid),
                        encoderParams);
                    ms.Position = 0;
                    return ms;
                }
                finally
                {
                    if (encoderParams != null)
                        encoderParams.Dispose();
                }
            }

        }
    }
    finally
    {
        if (stream != null)
        {
            stream.Dispose();
        }
    }
    return null;
}