简体   繁体   English

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

[英]Some images are being rotated when resized

In a nutshell the purpose of the following code is to resize an image based on the target size and the multiplier (1x, 2x, 3x).简而言之,以下代码的目的是根据目标大小和乘数(1x、2x、3x)调整图像大小。 This works fine except for some reason I haven't determined some images are being rotated.这工作正常,但由于某种原因我还没有确定某些图像正在旋转。

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

For instance this image:例如这张图片:

好形象

is scaled appropriately but this image:已适当缩放,但此图像:

坏形象

is flipped upside down.被颠倒了。 It is worth mentioning that the image also appeared to be upside down when it was in the preview pane to upload it to this site.值得一提的是,该图像在预览窗格中将其上传到此站点时也似乎是颠倒的。 That fact (which I obviously just discovered) strongly makes me think something is funny with the image.这个事实(我显然刚刚发现)强烈让我觉得图像有些有趣。 Regardless my code needs to handle it.不管我的代码需要处理它。

Imgur "fixed" the file above (because it doesn't rotate now when I run it through my code) so I uploaded it to Google Drive . Imgur “修复”了上面的文件(因为当我通过我的代码运行它时它现在不会旋转)所以我将它上传到Google Drive If you right click on the image (in FireFox I haven't tested other browsers) and click Save Image As... then the image doesn't rotate with my code above.如果您右键单击图像(在 FireFox 中,我还没有测试过其他浏览器)并单击将图像另存为...,则图像不会随我上面的代码旋转。 If you click the download button in the header then the image does rotate with my code.... Here is another copy of the dog image that flips 180 degrees with my code.如果您单击标题中的下载按钮,则图像会随着我的代码旋转.... 这是狗图像的另一个副本,它用我的代码翻转 180 度。 All of this is very bizarre, and I don't know what I'm doing wrong...所有这一切都非常奇怪,我不知道我做错了什么......

To be clear my goal is to resize the image without rotating the image.明确地说,我的目标是在不旋转图像的情况下调整图像大小。


Edits based on comments:基于评论的编辑:

An image that rotates/flips will do so consistently, and in the same manner.旋转/翻转的图像将以相同的方式一致地进行。 For example this dog picture will always flip 180 degrees.例如,这张狗图片将始终翻转 180 度。 Some pictures will lay on their side (rotate 90 or 270 degrees).有些图片会侧放(旋转 90 或 270 度)。 I've verified that the newWidth , newHeight , scaleFactor , targetSize (private variables), and image.Height/image.Width variables are all positive when the dog picture flips 180 degrees.我验证过newWidthnewHeightscaleFactortargetSize (私有变量)和image.Height/image.Width变量都正值当犬的图像翻转180度。

I don't believe this is an artifact of a particular tool.我不相信这是特定工具的人工制品。 I see the rotation via;我看到旋转通过; the preview in Windows Explorer, Windows Image Viewer, the stock Macintosh image viewer, FireFox, etc. The issue was actually brought to my attention by an iOS dev in my company who saw it in our app. Windows 资源管理器、Windows 图像查看器、Macintosh 图像查看器、FireFox 等中的预览。实际上,我公司的一位 iOS 开发人员在我们的应用程序中看到了这个问题,从而引起了我的注意。 I think too many tools are seeing this for it to be a viewer problem.我认为有太多工具认为这是一个查看器问题。

Thanks to the excellent help by Chris Farmer 1 and Mark Ransom 2 I was able to answer my own question.感谢Chris Farmer 1Mark Ransom 2的出色帮助,我能够回答我自己的问题。

The core problem was the EXIF data on some of the images was altering the orientation of the images.核心问题是某些图像上的 EXIF 数据改变了图像的方向。 When I resized the images all the EXIF data was stripped off.当我调整图像大小时,所有 EXIF 数据都被剥离了。 Since the EXIF data was stripped the viewers didn't know the image should be orientated differently.由于 EXIF 数据被剥离,观众不知道图像的方向应该不同。 That caused me to think the resizer code was rotating some images.这让我认为调整器代码正在旋转一些图像。 It is worth noting that this orientation information doesn't show up in the details view when you right click on the image in Windows Explorer.值得注意的是,当您在 Windows 资源管理器中右键单击图像时,此方向信息不会显示在详细信息视图中。 You need to search for it in the image property objects or use an online view like this one .您需要在图像属性对象中搜索它或使用像这样的在线视图。

Using thedata from here 3 I was able to create the following named constants:使用此处3 中数据,我能够创建以下命名常量:

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;

Using those named constants I extended my ResizeImage method to read:使用这些命名常量,我扩展了我的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);
            }
        }
    }
}

An observant person would notice that most of the additional code was pulled from this answer to a related question.细心的人会注意到,大部分附加代码都是从这个相关问题的答案中提取的。


1 : Who was right on the money but I didn't understand what he was driving at. 1 :谁在钱上是对的,但我不明白他在开车。

2 : Who showed me what Chris Farmer was trying to say. 2 :谁向我展示了 Chris Farmer 想说的话。

3 : The Orientation key value was confirmed here . 3Orientation 键值在此确认

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM