简而言之,以下代码的目的是根据目标大小和乘数(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中我没有测试过其他浏览器)并单击将图像另存为... ,则图像不会随上面的代码一起旋转。如果你单击标题中的下载按钮,那么图像会随我的代码旋转....这是狗图像的another copy,它与我的代码翻转180度。所有这一切都很奇怪,我不知道我做错了什么......
要明确我的目标是在不旋转图像的情况下调整图像大小。
根据评论进行编辑:
旋转/翻转的图像将以相同的方式一致地执行。例如,这张狗图片将始终翻转180度。有些照片会放在一边(旋转90度或270度)。我已经确认,当狗图片翻转180时,newWidth
,newHeight
,scaleFactor
,targetSize
(私有变量)和image.Height/image.Width
变量都是正数度。
我不相信这是特定工具的神器。我看到旋转通过; Windows资源管理器,Windows图像查看器,Macintosh图像查看器,FireFox等中的预览实际上我的公司的iOS开发人员在我们的应用程序中看到了这个问题。我认为太多的工具都会看到它成为观众的问题。
答案 0 :(得分:18)
感谢Chris Farmer 1 和Mark Ransom 2 的出色帮助,我能够回答我自己的问题。
核心问题是某些图像上的EXIF数据正在改变图像的方向。当我调整图像大小时,所有EXIF数据都被删除了。由于EXIF数据被剥离,观众并不知道图像应该以不同方式定向。这让我觉得缩放器代码正在旋转一些图像。值得注意的是,当您在Windows资源管理器中右键单击图像时,此方向信息不会显示在详细信息视图中。您需要在图像属性对象中搜索它,或使用this one等在线视图。
使用data from here 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;
使用那些命名常量我将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 : Orientation键值为confirmed here。