【问题标题】:c# saving very large bitmaps as jpegs (or any other compressed format)c# 将非常大的位图保存为 jpeg(或任何其他压缩格式)
【发布时间】:2011-11-23 19:31:42
【问题描述】:

我目前正在处理非常大的图像,这些图像基本上是通过将许多较小的图像拼接在一起生成的(例如全景图或照片马赛克软件)。为了避免内存不足异常(内存中只是如何排列较小图像的“地图”),我编写了一些代码,使用 BinaryWriter 和 LockBits 将这些图像逐行保存为位图。到目前为止,一切顺利。

现在的问题是我想将这些图像也保存为 Jpeg(或 PNG)。由于我对 c# 很陌生,所以我现在只能想到两种方法:

1) 类似于位图保存过程。生成一些 jpeg 标题并逐行保存大图像,之前以某种方式压缩它们。我不知道如何执行压缩。

2) 将已保存的位图流式传输到内存中,并将其保存为编码的 jpeg。

由于第二种方法似乎更容易,我尝试了这样的方法:

FileStream fsr =
    new FileStream("input.bmp", FileMode.Open, FileAccess.Read);
FileStream fsw =
    new FileStream("output.jpg", FileMode.CreateNew, FileAccess.Write);

EncoderParameters encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] =
    new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 80L);

Bitmap bmp = new Bitmap(fsr);
bmp.Save(fsw, GetEncoder(ImageFormat.Jpeg), encoderParameters);
bmp.Dispose();

现在的问题是保存方法首先尝试将位图完全加载到内存中,导致内存不足异常。

如果有任何关于如何解决或规避此问题的建议,我将非常高兴!

【问题讨论】:

  • OutOfMemoryException 在这个时代?你的位图有多大?
  • 是的,你很容易遇到内存问题,我调整了大约 10K 图像的大小并经常遇到OutOfMemoryException,即使我处理得当。
  • 来自位图的 OOM 非常可疑。这个类会为几乎所有出错的地方抛出内存异常,包括错误的参数等。
  • 也许 .NET wrapperFreeImage 库可能会对您有所帮助。
  • 您的方法是否适用于小位图?先测试一下代码。

标签: c# stream bitmap compression jpeg


【解决方案1】:

请注意,JPEG 压缩有一些重要的重大缺点:

1) JPEG 是有损的,因此您不会重新生成相同的图像。这可能是完全可以接受的,但如果您需要精确地重新生成源图像,那么 PNG 就是要走的路。

2) JPEG 在 8 像素边界上对齐。如果您将尺寸不是 8 的倍数(宽度或高度)的图像拼接在一起,您将在图像边界处得到一些强烈的伪影

考虑到您的用例,我宁愿建议将图像拼接在一起,并在每个较小的图像上使用标准压缩算法,例如 PNG 或 Zlib。您仍然可以将生成的压缩流存储到单个文件中。好处是之后可以直接“跳转”到要提取的小图的位置,这样会节省*很多*的内存。

缺点是您无法将所有较小的图像“直观地预览”成一个大图像(您需要为此创建一个程序)。

【讨论】:

  • 首先,非常感谢。将代码更改为不是按行保存大图像,而是按 8x8px 的块保存应该没问题。由于将其保存为 jpeg 而导致的轻微信息丢失也无关紧要。我现在的问题是,我完全不知道如何逐块编写 jpeg(或 png,就此而言)文件。
  • 如果你想坚持“大图像”的方法,我宁愿建议将它保存在 8 行的波段中。然后,您将在内存中获得一个 Width x 8 像素的位图,与完整图像相比,这将减少内存需求。
  • 我刚刚修改了代码,现在将图像保存在 8 行的波段中。但是仍然存在我不知道如何将其保存为 jpeg 的问题:对于 bmps,我编写了一个标头,然后将 ARGB 字节值一个一个地流式传输。我需要一些函数,它可以编写一个 jpeg-header,然后 - 这是我认为棘手的部分 - 将波段编码为 jpeg 格式并将它们逐块附加到现有文件中。
  • "Save" 方法在这里不适合,不幸的是,但也许 "SaveAdd" 是 (msdn.microsoft.com/en-us/library/tathhx27.aspx, msdn.microsoft.com/en-us/library/dsyxs70s.aspx)。否则,您将需要直接管理流并使用外部库,例如 FreeImage (freeimage.sourceforge.net/index.html)。
  • 我刚刚检查了 FreeImage,但找不到任何关于如何逐块写入 JPG 的方法。
【解决方案2】:

我怀疑您无法在 .NET 中执行此操作,但您始终可以调用 C 库来完成繁重的工作。我自己没有用过,但我建议你看看GraphicsMagick 库。它是一个带有许可的 C 库,允许开源和商业使用,它看起来可以满足您的需求。

【讨论】:

  • 谢谢,我去看看。但我实际上更愿意避免使用额外的库。
  • 我查看了 GraphicsMagick 库,但找不到任何关于如何以块的形式编写 jpeg(或 png)的信息。
【解决方案3】:

(因为标题说“或任何其他压缩格式”)

TIFF 格式具有平铺和条纹的概念。两者都可以用来避免在任何时候将所有大图像都放在内存中。由于您正在拼接小图像,因此瓷砖可能会派上用场。

您可以尝试LibTiff.Net(免费、商业友好、开源、仅用 C# 编写)来完成这项任务。

有一篇文章Basic introduction to the capabilities of the library 介绍了面向条带和平铺的图像 IO(在文章的第二部分)。

免责声明:我为公司工作。

【讨论】:

  • 谢谢,我会试试的。你知道jpegs,pngs,aso是否也有类似的东西?通常,我总是会选择 jpeg。
  • 据我所知,JPEG 没有这样的东西。不幸的是,我不知道 PNG 是否有类似的东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-22
  • 2013-05-28
  • 2015-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-08
相关资源
最近更新 更多