【问题标题】:Low memory image resizing低内存图像大小调整
【发布时间】:2011-05-17 16:57:16
【问题描述】:

我正在寻找一些关于如何构建一个非常低内存的图像大小调整程序的建议,该程序将作为我的 nodejs 应用程序在 linux 中的子进程运行。

我正在寻找的解决方案是一个 linux 可执行文件,它将使用标准输入获取 base64 字符串图像(从客户端上传),将照片调整为指定大小,然后通过标准输出将生成的图像数据泵回。

我研究了image magick,这可能是我最终使用的,但我想我会问一下,看看是否有人有建议。

我们将不胜感激有关库的建议或 C/C++ 中预编译的可执行文件示例。一个有用的答案还包括调整低内存图像大小的一般策略。

谢谢

【问题讨论】:

  • “尽可能低内存”是什么意思?你可以负担多少内存让它使用? 100KB? 1MB?更多的?较少的?是否总是允许使用与图像本身一样多的内存?在某些时候,降低内存使用意味着性能或图像质量的妥协。这种权衡什么时候可以接受?
  • 我认为问题在于有人可能会上传一个只有几 kb 的 40000x30000 jpeg 文件,然后通过分配 1.2 GB 来拒绝服务器...

标签: c++ c linux image-processing


【解决方案1】:

根据您想要支持的图像格式,几乎可以肯定地执行增量解码和缩放,方法是一次只解码几行并在写入输出后丢弃数据。但是,它可能需要编写您自己的代码或调整现有的解码器库来支持这种操作。

还值得注意的是,通过简单地跳过高频系数并使用更小的 IDCT,可以有效地缩小巨型 jpeg 的大小。例如,要以一半宽度和一半高度进行解码,丢弃除左上象限以外的所有系数(水平和垂直频率 libjpeg 解码器和libavcodec 解码器都支持此操作以进行 2 次方缩放(1/2、1/4 或 1/8)。这种方法可能不需要增量解码/缩放。

您可以通过djpeg -scale 1/4 < src.jpg | cjpeg > dest.jpg 试用。如果您想要一个固定的输出大小,您可能首先按 1/2、1/4 或 1/8 中的任何一个进行缩放,使您最接近所需的大小而不会变低,然后执行插值以进行最后一步,例如djpeg -scale 1/4 < src.jpg | convert pnm:- -scale 640x480 dest.jpg.

【讨论】:

  • djpeg 是否支持标准输入/输出?
  • 是的。在我回答的示例中,它使用标准输入和标准输出。 :-)
  • 对不起我的天真。我现在知道了。您是否认为从一个进程(nodejs)向另一个进程(可能是 djpeg、cjpeg)抽取位会比在同一进程中执行该处理使用更多的内存?如果它会花费系统 2 倍的内存,我不想这样做。
  • 它可能会使用更多(如果两个进程同时拥有图像的完整副本,则为 2x)或更少(如果管道数据使图像作为一个整体不会被保存在内存中),但可能大致相同(如果djpeg 在解码时将图像写出而不将其保留在内存中,并且convert 将整个内容加载到内存中以对其进行缩放,我认为它们的工作方式是这样的......)跨度>
【解决方案2】:

在处理非常大的图像时,例如 0.25 GPix 和更大的图像,ImageMagick 使用约 2 GB 内存,即使首先使用 djpeg 解码 JPEG 图像也是如此。

此命令链将仅使用 ~3 MB 内存调整任意大小的 JPEG 图像的大小:

djpeg my-large.jpg | pnmscale -xysize 16000 16000 | cjpeg > scaled-large.jpg

【讨论】:

  • 这正是我所做的。很好的调用,它很容易实现并且性能非常好。我认为 R 也有同样的想法。
【解决方案3】:

GraphicsMagick 通常是 ImageMagick 的更好版本,我来看看。如果你真的需要快速的东西,你可能想要使用 libjpeg 之类的东西 - 虽然你说你想要一些非阻塞 IO 的东西,但你想做的操作是相对 CPU 密集型的(即解码图像,然后尝试调整大小它)。

【讨论】:

  • 是的,都是 CPU/内存。有道理。
【解决方案4】:

如果有的话,这只是他所描述的示例:

import sys
from PIL import Image
import binascii
import cStringIO
x,y = sys.stdin.readline().strip().split(' ')
x,y = int(x), int(y)
img = Image.open(cStringIO.StringIO(binascii.b2a_base64(sys.stdin.read())).resize(x,y)
img.save(sys.stdout, format="png")

因为它必须读取输入、解码、调整大小和编码,然后将其写出,所以没有办法将所用内存的大小减少到小于输入图像的大小

【讨论】:

  • 请记住,将有 2 个进程共享此数据。正如R所提到的,我认为有一种方法可以在std IO上而不是整个文件上抽取小块,就像在你的例子中一样。这样内存占用可以更小。想法?
  • 是的,但我不认为 nodejs 可以在大多数情况下使用这种图像格式,除非你的图像很大,即使每个分量 10kx10k 8bit RGB 图像也只有 286.2MB,通常有足够的内存,如果没有你不处理这么大的图像,如果你有像 jpeg 这样的格式,你可以解码一个缩小的版本,那么你应该使用它,但如果你可能需要再次缩小解码后的缩减太大,因此您无法流式传输调整大小
【解决方案5】:

就性能而言,没有什么能比英特尔集成性能基元更胜一筹。如果您负担得起,我强烈建议您使用它。

否则,只需实施您自己的调整大小例程即可。 Lanczos 提供了相当不错的结果,尽管速度不会非常快。

编辑:我强烈建议您不要使用 Image Magick 或 Graphics Magick。它们都是很棒的库,但设计目的完全不同——处理许多文件格式、深度、像素格式等。它们牺牲了我提到的性能和内存效率。

【讨论】:

  • 感谢您的回答。我是否正确地假设 IIPP 通过支持多线程获得了优于其他解决方案的最大性能?
  • 它也是矢量化的,这是它如此之快的第一大原因。是的,它还支持多线程,从而进一步提高性能。
【解决方案6】:

你可能需要这个:https://github.com/zhangyuanwei/node-images

Nodejs 的跨平台图像解码器(png/jpeg/gif)和编码器(png/jpeg)

【讨论】:

    猜你喜欢
    • 2012-11-01
    • 2012-08-03
    • 2011-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-24
    • 1970-01-01
    • 2011-02-18
    相关资源
    最近更新 更多