在.NET中以最小的质量损失旋转JPEG文件。

问题描述 投票:7回答:3

我试图从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();
    }
}

我找到一篇文章,内容是 不损失信息的JPEG转换. 使用 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();
    }
}

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

另外,这是我对 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) {
.net image-processing jpeg
3个回答
3
投票

使用任何解压图像、旋转和压缩图像的方法,你都会得到质量损失。

JPEG格式将颜色信息压缩在2x2像素的正方形中,通过得到一个平均颜色来代表所有四个像素,所以如果你的图像宽度和高度被2整除,你会损失较少的质量,因为在压缩中删除的大部分信息是在解压中插值的信息。

同样的,亮度信息也是以8x8像素的方块进行压缩的,所以如果你的宽度和高度都是可以被8分割的,那么旋转图像后,网格会对齐,你会损失更少的实际信息。

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


3
投票

谢谢你确认我发布的代码是有效的。这帮我隔离了我的问题。我现在觉得自己很笨。我的实际代码在设置之前有一个图像格式检查 encoderParams - 但它有一个错误。

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

我发现上面的条件总是假的,所以... encoderParams 没有被设置。修复方法很简单。

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

1
投票

这是我对Mike Henry的答案的改编,根据我的需要进行了修改,以方便大家使用。 Here's my adaptation of Mike Henry's answer modified to my needs for whoever may find it useful.

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;
}
© www.soinside.com 2019 - 2024. All rights reserved.