我试图从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) {
使用任何解压图像、旋转和压缩图像的方法,你都会得到质量损失。
JPEG格式将颜色信息压缩在2x2像素的正方形中,通过得到一个平均颜色来代表所有四个像素,所以如果你的图像宽度和高度被2整除,你会损失较少的质量,因为在压缩中删除的大部分信息是在解压中插值的信息。
同样的,亮度信息也是以8x8像素的方块进行压缩的,所以如果你的宽度和高度都是可以被8分割的,那么旋转图像后,网格会对齐,你会损失更少的实际信息。
如果要进行无损旋转,你必须使用完全不同的方法,读取JPEG文件,并对每个方块的信息进行重新排列和旋转,使其形成旋转后的图像,而不需要解压和重新压缩。
谢谢你确认我发布的代码是有效的。这帮我隔离了我的问题。我现在觉得自己很笨。我的实际代码在设置之前有一个图像格式检查 encoderParams
- 但它有一个错误。
if (sourceFormat == ImageFormat.Jpeg) {
// set encoderParams here
我发现上面的条件总是假的,所以... encoderParams
没有被设置。修复方法很简单。
if (sourceFormat.Guid == ImageFormat.Jpeg.Guid) {
这是我对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;
}