调整大小时某些图像正在旋转

Posted

技术标签:

【中文标题】调整大小时某些图像正在旋转【英文标题】:Some images are being rotated when resized 【发布时间】:2016-01-23 11:24:34 【问题描述】:

简而言之,以下代码的目的是根据目标大小和乘数(1x、2x、3x)调整图像大小。除了由于某种原因我还没有确定某些图像正在旋转之外,这很好用。

public void ResizeImage(TargetSize targetSize, ResizeMultiplier multiplier, Stream input, Stream output)

    using (var image = Image.FromStream(input))
    
        // Calculate the resize factor
        var scaleFactor = targetSize.CalculateScaleFactor(image.Width, image.Height);
        scaleFactor /= (int)multiplier; // Enum is effectively named constant with a value of 1, 2, or 3

        var newWidth = (int)Math.Floor(image.Width / scaleFactor);
        var newHeight = (int)Math.Floor(image.Height / scaleFactor);
        using (var newBitmap = new Bitmap(newWidth, newHeight))
        
            using (var imageScaler = Graphics.FromImage(newBitmap))
            
                imageScaler.CompositingQuality = CompositingQuality.HighQuality;
                imageScaler.SmoothingMode = SmoothingMode.HighQuality;
                imageScaler.InterpolationMode = InterpolationMode.HighQualityBicubic;

                var imageRectangle = new Rectangle(0, 0, newWidth, newHeight);
                imageScaler.DrawImage(image, imageRectangle);

                newBitmap.Save(output, image.RawFormat);
            
        
    


// Class definition for the class used in the method above
public class TargetSize

    /// <summary>
    /// The _width
    /// </summary>
    private readonly int _width;

    /// <summary>
    /// The _height
    /// </summary>
    private readonly int _height;

    /// <summary>
    /// Initializes a new instance of the <see cref="TargetSize"/> class.
    /// </summary>
    /// <param name="width">The width.</param>
    /// <param name="height">The height.</param>
    public TargetSize(int width, int height)
    
        _height = height;
        _width = width;
    

    /// <summary>
    /// Calculates the scale factor.
    /// </summary>
    /// <param name="width">The width.</param>
    /// <param name="height">The height.</param>
    /// <returns></returns>
    public decimal CalculateScaleFactor(int width, int height)
    
        // Scale proportinately
        var heightScaleFactor = decimal.Divide(height, _height);
        var widthScaleFactor = decimal.Divide(width, _width);

        // Use the smaller of the two as the final scale factor so the image is never undersized.
        return widthScaleFactor > heightScaleFactor ? heightScaleFactor : widthScaleFactor;
    


// NUnit integration test case I'm using to exercise the above code
[Test]
public void ResizeImage_Persistant_Single()

    // Read the image from disk
    using (var fileStream = File.OpenRead(@"TestData\dog.jpg"))
    
        using (var outputStream = new MemoryStream())
        
            // Call the resize image method detailed above. ResizeMultiplier.Medium casts to 2.
            _sut.ResizeImage(new TargetSize(200, 200), ResizeMultiplier.Medium, fileStream, outputStream);
            using (var newImage = Image.FromStream(outputStream))
            
                // Save the resized image to disk
                newImage.Save(@"TestData\ImageResizerTests.ResizeImage_Persistant_Single.jpg");
            
        
    

例如这张图片:

已适当缩放,但此图像:

被颠倒过来。值得一提的是,当它在预览窗格中上传到该站点时,图像也出现了颠倒。这个事实(我显然刚刚发现)强烈地让我觉得这张照片很有趣。不管我的代码需要处理它。

Imgur“修复”了上面的文件(因为当我通过我的代码运行它时它现在不会旋转)所以我将它上传到Google Drive。如果您右键单击图像(在 FireFox 中我没有测试过其他浏览器)并单击 Save Image As... 那么图像不会随着我上面的代码旋转。如果您单击标题中的下载按钮,则图像会随我的代码旋转.... 这是狗图像的another copy,它使用我的代码翻转 180 度。这一切都很离奇,不知道自己做错了什么……

要明确我的目标是在不旋转图像的情况下调整图像大小。


基于 cmets 的编辑:

旋转/翻转的图像会以相同的方式始终如一地进行。例如,这张狗图片将始终翻转 180 度。有些图片会侧放(旋转 90 或 270 度)。我已经验证了当狗图片翻转 180 度时,newWidthnewHeightscaleFactortargetSize(私有变量)和image.Height/image.Width 变量都是正数。

我不相信这是特定工具的产物。我看到了旋转; Windows Explorer、Windows Image Viewer、库存 Macintosh 图像查看器、FireFox 等中的预览。这个问题实际上是由我公司的一位 ios 开发人员在我们的应用程序中看到的。我认为有太多工具看到了这一点,而不是查看器问题。

【问题讨论】:

这可能是一个愚蠢的问题,但你确定这不是你用来查看图像的任何工具的产物吗?一些图像查看器可能会尝试通过将面部定向为直立和/或使用其他图像提示来“纠正”方向来帮助您。 相机 EXIF 信息有时可用于自动旋转图像,但我从未见过它会颠倒某些东西。 根据我在regex.info/exif.cgi 找到的第一个在线EXIF 查看器,这只狗确实旋转了180 度(Orientation 字段)。 【参考方案1】:

感谢Chris Farmer1 和Mark Ransom2 的出色帮助,我能够回答自己的问题。

核心问题是某些图像的 EXIF 数据改变了图像的方向。当我调整图像大小时,所有的 EXIF 数据都被剥离了。由于 EXIF 数据被剥离,观众不知道图像应该以不同的方向定位。这使我认为调整大小的代码正在旋转一些图像。值得注意的是,当您在 Windows 资源管理器中右键单击图像时,此方向信息不会显示在详细信息视图中。您需要在图像属性对象中搜索或使用this one 等在线视图。

使用data from here3 我能够创建以下命名常量:

private const int OrientationKey = 0x0112;
private const int NotSpecified = 0;
private const int NormalOrientation = 1;
private const int MirrorHorizontal = 2;
private const int UpsideDown = 3;
private const int MirrorVertical = 4;
private const int MirrorHorizontalAndRotateRight = 5;
private const int RotateLeft = 6;
private const int MirorHorizontalAndRotateLeft = 7;
private const int RotateRight = 8;

使用这些命名常量,我将ResizeImage 方法扩展为读取:

public void ResizeImage(TargetSize targetSize, ResizeMultiplier multiplier, Stream input, Stream output)

    using (var image = Image.FromStream(input))
    
        // Calculate the resize factor
        var scaleFactor = targetSize.CalculateScaleFactor(image.Width, image.Height);
        scaleFactor /= (int)multiplier; 

        var newWidth = (int)Math.Floor(image.Width / scaleFactor);
        var newHeight = (int)Math.Floor(image.Height / scaleFactor);
        using (var newBitmap = new Bitmap(newWidth, newHeight))
        
            using (var imageScaler = Graphics.FromImage(newBitmap))
            
                imageScaler.CompositingQuality = CompositingQuality.HighQuality;
                imageScaler.SmoothingMode = SmoothingMode.HighQuality;
                imageScaler.InterpolationMode = InterpolationMode.HighQualityBicubic;

                var imageRectangle = new Rectangle(0, 0, newWidth, newHeight);
                imageScaler.DrawImage(image, imageRectangle);

                // Fix orientation if needed.
                if (image.PropertyIdList.Contains(OrientationKey))
                
                    var orientation = (int)image.GetPropertyItem(OrientationKey).Value[0];
                    switch (orientation)
                    
                        case NotSpecified: // Assume it is good.
                        case NormalOrientation:
                            // No rotation required.
                            break;
                        case MirrorHorizontal:
                            newBitmap.RotateFlip(RotateFlipType.RotateNoneFlipX);
                            break;
                        case UpsideDown:
                            newBitmap.RotateFlip(RotateFlipType.Rotate180FlipNone);
                            break;
                        case MirrorVertical:
                            newBitmap.RotateFlip(RotateFlipType.Rotate180FlipX);
                            break;
                        case MirrorHorizontalAndRotateRight:
                            newBitmap.RotateFlip(RotateFlipType.Rotate90FlipX);
                            break;
                        case RotateLeft:
                            newBitmap.RotateFlip(RotateFlipType.Rotate90FlipNone);
                            break;
                        case MirorHorizontalAndRotateLeft:
                            newBitmap.RotateFlip(RotateFlipType.Rotate270FlipX);
                            break;
                        case RotateRight:
                            newBitmap.RotateFlip(RotateFlipType.Rotate270FlipNone);
                            break;
                        default:
                            throw new NotImplementedException("An orientation of " + orientation + " isn't implemented.");
                    
                
                newBitmap.Save(output, image.RawFormat);
            
        
    

细心的人会注意到,大部分附加代码都被from this answer 拉到相关问题中。


1谁在钱上是对的,但我不明白他在说什么。

2谁向我展示了 Chris Farmer 想说的话。

3方向键值为confirmed here。

【讨论】:

什么是ResizeMultiplier OrientationKey?

以上是关于调整大小时某些图像正在旋转的主要内容,如果未能解决你的问题,请参考以下文章

旋转和调整图像大小 + iPhone

使用 node sharp 包调整图像大小并上传到 s3 时,它会旋转

Kinetic js拖放,调整大小和旋转图像

调整 uiview 的大小以及 uiimageview 的旋转

XCode:调整图像大小使用纵横比并向下移动图像下方的元素

在图像移动/调整大小/旋转操作中在图像周围放置一些指示符