将多个 png 转换为 bmp 从标准输入到标准输出

Posted

技术标签:

【中文标题】将多个 png 转换为 bmp 从标准输入到标准输出【英文标题】:convert multiple pngs to bmps from stdin to stdout 【发布时间】:2018-03-14 01:22:42 【问题描述】:

我需要将来自 chrome-headless 的 PNG 转换为 BMP 以使用它们,并且我还需要通过管道执行此操作,而无需将这些图像保存在某处。

Chrome 设置为仅将图像截屏到标准输出,这工作正常,但我似乎无法使用 imagemagick 转换工具来完成我需要的操作。

在测试时

cat foo.png | convert PNG:- BMP:-

似乎适用于一个输入图像(输出以'BM'开头,表示BMP二进制垃圾),

cat foo.png bar.png | convert PNG:- BMP:-

也只返回一张图片,好像是第一张。

如何将其通过标准输入读取的每个文件转换为“覆盖”输出?


编辑: cat 示例只是为了演示输入。最终的想法更像是

chrome-headless | *solution* | bmpreader

正如 fmw42 和 Mark 所指出的,这似乎是不可能的 imagemagick 开箱即用。

【问题讨论】:

【参考方案1】:

正如 Fred (@fmw42) 所说,您不能单独使用 ImageMagick 来做到这一点,但应该可以使用小型 C/C++、Perl、php 或 Python 脚本来做一些事情。

基本上,PNG 文件很好地“分块”,参见Wikipedia PNG Specification,每个块都有一个类型 - 例如IHDR(标题)、PLTE(调色板)、@987654326 @(图像数据)、IEND(文件结束标记)以及至关重要的字节长度。

因此,您可以编写一个脚本/程序,从您的管道中读取数据,获取块并将它们附加到内存中的字符串,并一直这样做,直到它到达 IEND 块。此时,它会使用 ImageMagickGDPillow 将内存中的字符串转换为 BMP 并将其写入另一个管道, 或将字符串写入 ImageMagick 正在读取的管道,并让 ImageMagick 转换文件。然后,您会将累积的 (PNG) 字符串清零并开始读取下一个文件。

我不想为 15 分编写和调试所有这些,但它看起来像这样:

chrome-cast | demultiplex

demultiplex 是这样的:

while not error
   clear accumulated string of PNG data
   done = false
   while not done
      read chunk type and length of chunk
      read whole chunk and append to accumulated string of PNG data
      if chunk = IEND
         pass accumulated PNG string to ImageMagick to make BMP
         done = true
      endif
   end
end

请注意,您的程序不必“理解”所有块类型或其内容 - 只需识别块长度和到达时的 IEND 块。


请注意,您可能希望运行 pngcheckpngsplit 以开始了解 PNG 文件是如何按块构建的 - 它们是 here。事实上,pngsplit 无论如何都可以为所欲为。

下面是pngcheck 的示例:

pngcheck -v file.png

File: file.png (462308 bytes)
  chunk IHDR at offset 0x0000c, length 13
    800 x 468 image, 32-bit RGB+alpha, non-interlaced
  chunk gAMA at offset 0x00025, length 4: 0.45455
  chunk cHRM at offset 0x00035, length 32
    White x = 0.3127 y = 0.329,  Red x = 0.64 y = 0.33
    Green x = 0.3 y = 0.6,  Blue x = 0.15 y = 0.06
  chunk bKGD at offset 0x00061, length 6
    red = 0x00ff, green = 0x00ff, blue = 0x00ff
  chunk pHYs at offset 0x00073, length 9: 3779x3779 pixels/meter (96 dpi)
  chunk tIME at offset 0x00088, length 7:  2 Oct 2017 21:34:26 UTC
  chunk IDAT at offset 0x0009b, length 32768
    zlib: deflated, 32K window, maximum compression
  chunk IDAT at offset 0x080a7, length 32768
  chunk IDAT at offset 0x100b3, length 32768
  chunk IDAT at offset 0x180bf, length 32768
  chunk IDAT at offset 0x200cb, length 32768
  chunk IDAT at offset 0x280d7, length 32768
  chunk IDAT at offset 0x300e3, length 32768
  chunk IDAT at offset 0x380ef, length 32768
  chunk IDAT at offset 0x400fb, length 32768
  chunk IDAT at offset 0x48107, length 32768
  chunk IDAT at offset 0x50113, length 32768
  chunk IDAT at offset 0x5811f, length 32768
  chunk IDAT at offset 0x6012b, length 32768
  chunk IDAT at offset 0x68137, length 32768
  chunk IDAT at offset 0x70143, length 3115
  chunk tEXt at offset 0x70d7a, length 37, keyword: date:create
  chunk tEXt at offset 0x70dab, length 37, keyword: date:modify
  chunk IEND at offset 0x70ddc, length 0
No errors detected in file.png (24 chunks, 69.1% compression).

我在 Perl 中做了一个基本版本的 PNG 分块以获得另一个答案,所以请快速查看 here。

【讨论】:

非常感谢您的详尽回答。遗憾的是我还不能投票,但我稍后会将其标记为答案。【参考方案2】:

我认为您不能使用一次包含多个图像的管道来做到这一点。您可以遍历每个 png 图像并使用您的管道。管道将保持图像显示 png 后缀,但实际上是 bmp 文件。但这让我感到困惑。

在 Imagemagick 中,如果您想将文件夹中的文件从一种格式转换为另一种格式,那么您应该使用 mogrify。假设您的图像在文件夹 1 中,并且您希望将它们保存到已经存在的文件夹 2 中。然后您可以将目录更改为文件夹1,然后执行:

mogrify -format bmp -path path2/folder2 *.png

这将保留输入名称,但在转换为该后缀类型时更改后缀。

Convert 只能有一个输出名称,可能与您的输入之一相同。它不能写入多个输出名称。如果输出格式支持图层或页面,则输出将具有图层/页面的一个名称,否则它将是 XXX-1.bmp ... XXX-n.bmp,其中 XXX 是输出名称

convert image1.png image2.png ... imageN.png newimage.suffix

但是,您可以在脚本循环中执行此操作。但是 mogrify 更容易。

【讨论】:

感谢您的回答。由于我稍后使用 chrome,它将直接将 PNG 写入标准输出,以及另一个期望 BMP 直接通过标准输入的工具,这对我来说不是解决方案。我想我应该更清楚地说明这一点。

以上是关于将多个 png 转换为 bmp 从标准输入到标准输出的主要内容,如果未能解决你的问题,请参考以下文章

已有一个bmp图片用标准c获取其任意点(给定坐标)像素信息?

支持的 libvips CLI 输出到 Windows 标准输出的格式

如何在 C++ 中将图像从 .bmp 转换为 .png

将位图 (bmp) 转换为具有透明度的 png (Windows c++)

到作地于出JPG,BMP,PNG,TIFF转换为PDF七海地

如何将 .png 文件转换为 .bmp?