How to convert 32MB of PNG file to 200Kbs without losing its colors

I have couple of images that are of 32MBs of size and I want to change their size from 32MBs to 100Kbs or any KBs of size without affecting its colours.

The commands I am trying are:

muhammad@muhammad-mohsin:~/scans$ find . -iname '*.png' -exec mogrify -format jpg "*.png" {} +


muhammad@muhammad-mohsin:~/$ find . -type f -iname \*.png -delete


muhammad@muhammad-mohsin:~/$ find . -iname '*.jpg' -exec mogrify -define jpeg:extent=300kb -strip -quality 90 -scale 90% *.jpg {} +

Here, first I convert a PNG to JPG that reduce its size from 32Mbs to 5.8Mbs and everything stays same but when I use 3rd command, it removes background color in image and making it grayscale sort of blurry.

However, the text is still readable but colors and background logo does not.

How I can achieve this with convert, mogrify or any other tool? I tried every possible thing so far.

This is part of original image

This is part of changed image after command


Solution 1:

It's because of how JPEG compression works. It attempts to round adjoining pixels that are similar to eachother to similar values. This causes loss of details, and blockyness.

This becomes more noticeable as you increase the compression level, which is exactly what you're doing. In addition you're doing it in two steps:

  1. Lossless (PNG) to lossy (JPEG) compression.
  2. Lossy to lossy compression.

You will probably get a better result by going lossless to lossy at final quality, thus only applying lossy compression once, e.g. using jpeg:extent=300kb -strip -quality 90 -scale 90% in the first conversion.

Furthermore, you say nothing about the size of the image and level of detail. It may not be feasible to get it down to 300kB and retain the desired quality.

To get rid of background blotches, you can try to apply thresholds to your document in some image editing software, forcing anything less than a certain shade of gray to be white, for instance.

However, no matter what you do, compressing from a 30MB lossless format to a 300kB lossy format will lead to visibly reduced quality.

Solution 2:

I am trying find . -iname '*.png' -exec convert -resize 60% -quality 60 "*.jpg" {} + but that does not work.

Ref. https://superuser.com/questions/71028/batch-converting-png-to-jpg-in-linux

$ ls -1 *.png | xargs -n 1 bash -c 'convert -quality 60 "$0" "${0%.*}.jpg"'

Converts my example 31MB.png to 1.4MB.jpg . ... You may have to repeat with e.g. $ ls -1 *.PNG | ... etc.

Ref. comment by @steeldriver : "slightly better is xargs -d '\n' -n 1 "