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

Eri*_*rik 10 c# image image-processing image-rotation

简而言之,以下代码的目的是根据目标大小和乘数(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");
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

例如这张图片:

良好的形象

是适当缩放但这个图像:

坏形象

颠倒了.值得一提的是,当图像在预览窗格中上传到此站点时,图像似乎也是颠倒的.那个事实(我很明显刚刚发现)强烈地让我觉得这个形象很有趣.无论我的代码需要处理它.

Imgur "修复"上面的文件(因为当我通过我的代码运行时它现在不会旋转)所以我将它上传到Google云端硬盘.如果您右键单击图像(在FireFox中我没有测试过其他浏览器)并单击,Save Image As...则图像不会随上面的代码一起旋转.如果单击标题中的下载按钮,则图像会随我的代码一起旋转....这是狗图像的另一个副本,它与我的代码翻转180度.所有这一切都非常离奇,我不知道我做错了什么......

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


根据评论进行编辑:

旋转/翻转的图像将以相同的方式一致地执行.例如,这张狗图片将始终翻转180度.有些照片会放在一边(旋转90度或270度).我验证过的newWidth,newHeight,scaleFactor,targetSize(私有变量)和image.Height/image.Width变量都是正值当狗图片翻转180度.

我不相信这是特定工具的神器.我看到旋转通过; Windows资源管理器,Windows图像查看器,Macintosh图像查看器,FireFox等中的预览实际上我的公司的iOS开发人员在我们的应用程序中看到了这个问题.我认为有太多的工具可以看到它,因为它是一个观众问题.

Eri*_*rik 22

感谢Chris Farmer 1Mark Ransom 2的出色帮助,我能够回答我自己的问题.

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

使用此处数据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;
Run Code Online (Sandbox Code Playgroud)

使用那些命名常量我扩展我的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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

一个善于观察的人会注意到大多数附加代码都是从这个相关问题的答案中提取出来的.


1:谁对这笔钱不对,但我不明白他在开什么车.

2:谁向我展示了克里斯·法默试图说的话.

3:此处确认了 Orientation键值.