0

I need to to convert PNGs coming from chrome-headless to BMPs to work with them, and I also need to do this through pipes without saving those images somewhere.

Chrome is set up to just screencast the images to stdout, which is working fine, but I can't seem to get the imagemagick convert tool to do, what I need.

While testing with

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

seems to work with one input image (Output starts with 'BM', indicating BMP binary garbage),

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

also returns only one image, which seems to be the first one.

How can I get convert to "overwrite" the output for each file it reads through stdin?


EDIT: The cat example is just to demonstrate the input. The final idea is more like

chrome-headless | *solution* | bmpreader

As fmw42 and Mark pointed out, this doesn't seem to be possible with imagemagick out of the box.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
haxti
  • 3
  • 4

2 Answers2

0

I do not think you can do that with pipes with more than one image at a time. You could loop over each png image and use your pipes. The pipes will keep the image showing png suffix, but will actually be bmp files. But this seems confusing to me.

In Imagemagick, if you want to convert a folder of files from one format to the other, then you should just use mogrify. Suppose your images are in folder1 and you want save them to folder2 that already exists. Then you can change directory to folder1 and then do:

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

This will keep the input name but change the suffix while converting to that suffix type.

Convert can only have one output name, which may be the same as one of your inputs. It cannot write to multiple output names. If the output format supports layers or pages, then the output will have the one name with layers/pages otherwise it will be XXX-1.bmp ... XXX-n.bmp where XXX is the output name by doing

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

You can however, do it in a script loop. But mogrify is easier.

fmw42
  • 46,825
  • 10
  • 62
  • 80
  • Thank you for your answer. Since I'm using chrome later, which will directly write the PNGs to stdout, and another tool which expects BMPs coming directly through stdin, this is no solution for me. I should have made this more clear i guess. – haxti Oct 03 '17 at 09:48
0

As Fred (@fmw42) says, you cannot do this with ImageMagick alone, but it should be possible to do something with a small C/C++, Perl, PHP or Python script.

Basically, PNG files are nicely "chunked", see Wikipedia PNG Specification, and each chunk has a type - such as IHDR (header), PLTE (palette), IDAT (image data), IEND (end of file marker) and, crucially, a length in bytes.

So, you could write a script/program that reads data off your pipe, gets the chunks and appends them to an in-memory string and keeps doing so until it reaches an IEND chunk. At that point it would either use ImageMagick or GD or Pillow to convert the in-memory string into a BMP and write it to a further pipe, or write the string to a pipe that ImageMagick was reading from and let ImageMagick convert the file. You would then zero out the accumulated (PNG) string and start reading the next file.

I don't feel like writing and debugging all that for 15 points, but it would look something like this:

chrome-cast | demultiplex

where demultiplex would be like:

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

Note that your program would not have to "understand" all the chunk types or their contents - just recognise the chunk length and the IEND chunk when it arrives.


Note that you may like to run pngcheck or pngsplit to get started in understanding how PNG files are structured in chunks - they are here. In fact, pngsplit may do what you want anyway.

Here is an example of pngcheck in action:

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).

I did a rudimentary version of the PNG chunking in Perl for another answer, so have a quick look here.

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • Thank you very much for this thorough answer. Sadly I can't vote it up yet, but I'll mark it as answer later. – haxti Oct 03 '17 at 09:41