我必须旋转.net(90°| 180°| 270°)中无损的JPG图像。以下文章显示了如何做到这一点:
这些例子似乎很简单;但是,我没有运气去工作。我的源数据是一个数组(各种JPG文件,来自互联网的相机等),因此我想将旋转后的图像也作为字节数组返回。这里是(简化的)代码:
Image image;
using (var ms = new MemoryStream(originalImageData)) {
image = System.Drawing.Image.FromStream(ms);
}
// If I don't copy the image into a new bitmap, every try to save the image fails with a general GDI+ exception. This seems to be another bug of GDI+.
var bmp = new Bitmap(image);
// Creating the parameters for saving
var encParameters = new EncoderParameters(1);
encParameters.Param[0] = new EncoderParameter(Encoder.Transformation, (long)EncoderValue.TransformRotate90);
using (var ms = new MemoryStream()) {
// Now saving the image, what fails always with an ArgumentException from GDI+
// There is no difference, if I try to save to a file or to a stream.
bmp.Save(ms, GetJpgEncoderInfo(), encParameters);
return ms.ToArray();
}
我总是在没有任何有用信息的情况下从GDI +获得ArgumentException
:
操作失败,并出现最终异常[ArgumentException]。
来源:System.Drawing
我尝试了很多事情,但从未成功。
主要代码似乎是正确的,因为如果我将EncoderParameter
更改为Encoder.Quality
,则代码可以正常工作:
encParameters.Param[0] = new EncoderParameter(Encoder.Quality, 50L);
我在互联网上找到了一些有关此问题的有趣帖子,但是没有真正的解决方案。一位成员发表了汉斯·帕桑(Hans Passant)的声明,说这似乎是一个错误,来自MS员工的回复,我不理解,或者可能也很奇怪:
然而,这篇文章已有10年历史了,我不敢相信,这个问题是不固定的,特别是因为该转换在MSDN文档中有一个明确的示例。
有人告诉我我在做什么错吗?或者,如果这确实是一个错误,我该如何规避?
请注意,我必须使变换无损(在像素大小允许的范围内)。因此,Image.RotateFlip
不是一个选择。
Windows版本是10.0.17763,.Net是4.7.2
答案 0 :(得分:4)
using (var ms = new MemoryStream(originalImageData)) {
image = System.Drawing.Image.FromStream(ms);
}
这是万恶之源,使第一次尝试失败。它违反了the documentation“备注”部分中规定的规则,您必须在图像的生命周期内保持流开放。。违反规则不会导致一致的麻烦,请注意Save()调用失败了,但是Bitmap(image)构造函数成功了。 GDI +有点懒,您有很好的证据表明JPEG编解码器确实试图避免重新压缩图像。但这是行不通的,因为流被处置后,流中的原始数据不再可访问。例外是糟糕的,因为本机GDI +代码不了解有关MemoryStream的bean。修复很简单,只需在Save()调用之后移动右括号即可。
从那里出现了另一种错误,主要是由新的bmp
对象触发的。 image
和bmp
对象均未处置。这会很快占用地址空间,因为位图的数据存储在非托管内存中,所以GC不能足够频繁地运行以使您摆脱麻烦。现在,当MemoryStream不再分配内存时,Save()调用将失败。
您必须在这些对象上使用using
语句,以免发生这种情况。
应该解决该问题,请摆脱Bitmap解决方法,因为这会强制重新压缩JPEG。从技术上讲,当图像很大时,您仍然会遇到麻烦,因为32位进程中的地址空间会碎片化。请密切注意此过程的“专用字节”内存计数器,理想情况下,它保持在1 GB以下。如果没有,请使用“项目”>“属性”>“构建”选项卡,取消选中“首选32位”。