Resize indexed PNG image with ImageMagick while preserving color map

1.5k Views Asked by At

I am using custom batch script to make resized copies (33% and 66%) of all PNG images in folder. Here is my code:

for f in $(find /myFolder -name '*.png'); 
do
sudo cp -a $f "${f/%.png/-3x.png}"; 
sudo convert $f -resize 66.67% "${f/%.png/-2x.png}"; 
sudo convert $f -resize 33.33% $f; 
done

It works fine, except when the original image is indexed. In this case the smaller version of the image is RGB (so even larger file size then original image).

I have try several versions but not worked. One that I guess supposed to sort this out was fallowing:

for f in $(find /myFolder -name '*.png'); 
do
sudo cp -a $f "${f/%.png/-3x.png}"; 
sudo convert $f -define png:preserve-colormap -resize 66.67% "${f/%.png/-2x.png}"; 
sudo convert $f -define png:preserve-colormap -resize 33.33% $f; 
done

But it doesn't work.

EDIT:

This is updated co, but it still doesn't work as it supposed to (see the attached image-left is original, right is resized):

for f in $(find /myFolder -name '*.png');
do
  sudo cp -a $f "${f/%.png/-3x.png}";
  numberOfColors=`identify -format "%k" $f`

  convert "$f"                                                        \
    \( +clone -resize 66.67% -colors $numberOfColors -write "${f/%.png/-2x.png}" +delete \)  \
    -resize 33.33% -colors $numberOfColors "$f"
done

enter image description here

Original image: Original image

Scaled version: Scaled image

2

There are 2 best solutions below

1
On

There are several things to address here...

find vs glob

You say you want to process all files in a folder, then you use find which will search down into sub-directories as well. If you just want to process files in the current directory, you can let bash do the globbing directly for you. So, instead of

for f in $(find . -name "*.png"); do

you can just do:

shopt -s nullglob
for f in *.png; do

Performance

You run convert twice and load the original image twice, and that is not very efficient. You can run a single process that loads a single image and resizes to two different sizes and writes both to disk. So, instead of

for ...; do
   convert ...
   convert ...
done

you can write the following to start one convert, read the image once, clone it in memory and write it out, delete the spare copy in memory and then resize the original image and re-save that.

for ...; do
   convert "$f"                                                        \
      \( +clone -resize 66.67% -write "${f/%.png/-2x.png}" +delete \)  \
      -resize 33.33% "$f"
done 

Palette

It seems you actually only want to output palettised (indexed) images with "any" colormap rather than with a "specific" colormap. Glenn's answer is perfect if you want to retain a specific colormap. However, if any colormap is ok, you can use -colors to reduce the colours in the resulting image to a level where the PNG library can make the decision to create a palettised image. Glenn knows a lot more than me about that as he wrote it! However, I think if you reduce the colours to 250 (or so) you will probably get a 256 entry colormap and if you reduce the colours to around 60 or so, you will get a 64 entry colourmap. So, you would do:

shopt -s nullglob
for f in *.png; do
   sudo cp ... ...
   convert "$f"                                                        \
      \( +clone -resize 66.67% -colors 250 -write "${f/%.png/-2x.png}" +delete \)  \
      -resize 33.33% -colors 250 "$f"
done

You can try experimenting with other numbers of colours and see how that affects filesize - the number you need will depend on your images.

1
On

Use "-sample" instead of "-resize" to preserve the color set. This causes the resizing to be done by nearest-neighbor color selection rather than any kind of interpolation.

Otherwise, the colormap ends up with more than 256 colors and the png encoder can't preserve it, due to the 256-color limit on the size of a PNG PLTE chunk. I cannot guarantee that you'll like the appearance of the result, though.

Also, be sure you are using a recent version of ImageMagick. I'm not observing this problem with the current release (6.9.3-7). Your script works fine and produces clean -2x and -3x images.