The source is an RGBA PNG image (color_type=6 and bit_depth=8).
I need an image with indexed color and 2 palette items (color_type=3, bit_depth=1).
I tried with ImageMagick, but was able to reach only 1bit grayscale image (color_type=0, bit_depth=1) or 2bit indexed color image (color_type=3, bit_depth=2) in which only 2 colors are in use.
According to the PNG specification such image is possible, but how to create it?
Here is an image I am trying to convert:

The result of "convert input.png -type palette -depth 1 output.png":

The result of "convert input.png -type palette -depth 1 -colors 2 output.png"

Both results have bit_depth=2, but the second one uses only 2 colors of 4 possible.
Use one of the following 3, ordered from least to most ancillary tags in the output file:
convert -colors 2 -define png:include-chunk=none -verbose input.png output.png
convert -colors 2 -define png:exclude-chunk=bkgd -verbose input.png output.png
convert -colors 2 -background #000000 -verbose input.png output.png
The root of this problem is that ImageMagick by default tags the image with a white background color (bKGD) PNG chunk (unnecessarily I'd say), and then adds this color to the palette if it's not already there, even if the image has no pixels of that color. Your particular image doesn't have white after converting to 2-color, so the unneeded background color tag becomes a 3rd color and it can no longer be saved as a 1-bit indexed color image. See also this from the author of IM.
The reason others have failed to reproduce the problem is probably that they've tested with images where one of the 2 colors happened to be white.
The 1st option with -define png:include-chunk=none avoids the problem by not outputting any ancillary PNG chunks at all (e.g. bKGD, gAMA, cHRM, tEXt, tIME). Like pngcrush -rem alla. I'd prefer this for a less cluttered file (without it IM will add a few of these even if they weren't in the input file). Note: There's also the simple -strip option which should avoid most of these, but as of v6.9.3 it won't cut bKGD due to a bug.
The 2nd with -define png:exclude-chunk=bkgd removes only the offending background chunk.
The 3rd option with -background #000000 retains all ancillary PNG chunks including bKGD but sets it black, one of the 2 colors present in your image, so it can still be 1-bit. Adjust the color for an image with neither white nor black in it.
Note that for all these I'm including the -verbose switch. Not overly verbose; just goes from zero to two lines of status output where you'll notice a "2c" if the image stayed 2-color or a "3c" if not. It will also tell you if the palette reduction was lossy or not. It also outputs "8-bit sRGB" for most images, even paletted ones with fewer than 8 bits-per-pixel; that's not a bug as in your comment to another answer @johnfound, this refers not to the bits-per-pixel but to the bits-per-color component. It can be more than 8-bit for (rare) deep color images.
convert input.png -background white -type palette -depth 1 -colors 2 output.png
works for me.
(Why -depth=1 is not enough? No idea.)
BTW, the tweakpng tool is useful for checking this kind of things.
Update: if the images have transparency, you might play safer by removing it explicitly:
convert input.png -background white -flatten -type palette -depth 1 -colors 2 output.png
(You can replace white for your preference)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With