Skip to content
Advertisement

How to convert PNG to JPEG losslessly?

I’ve converted PNG to JPEG with both java and python PIL. Python code :

from PIL import Image  

img = Image.open('input.png')
img.save('output.jpg', 'JPEG', quality=100)

For converting the Java code I used:

import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;

public class PngToJpeg {
    public static void main(String[] args) {
        // Specify the input and output file paths
        String inputFile = "./test.png";
        String outputFile = "./testout.jpg";

        // Read the input image into a BufferedImage
        BufferedImage inputImage = null;
        try {
            inputImage = ImageIO.read(new File(inputFile));
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Write the input image to the output file as a JPEG image
        try {
            ImageIO.write(inputImage, "jpg", new File(outputFile));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

below code shows me the exact value of each pixel:

from PIL import Image

img = Image.open("test.jpeg")
pixellist = list(img.getdata())
print(pixellist)

When I convert JPEG to PNG with both Python and JAVA the pixel values are exactly the same. But when its the other way around it seems like there were 2 or 3 subtracted from many pixels. For example:

JPEG pixels(after conversion): (22, 22, 22), (219, 219, 219), (219, 219, 219), (219, 219, 219), (219, 219, 219)

PNG pixels(original image): (22, 22, 22), (222, 222, 222), (222, 222, 222), (222, 222, 222), (222, 222, 222)

Things to note here that the PNG has no ALPHA channel.

So what am i missing here? How to achieve the exact same pixel values when converting a PNG to JPEG?

I have also checked imagemagick, convert. They appear to be the same result.

Advertisement

Answer

In short, JPEG is a lossy compression format at all quality levels. It doesn’t matter if you set the quality to 100%. See this earlier question “Is JPEG lossless when quality is set to 100?”. There are extensions such as JPEG-LS that are lossless, but it is a totally different compression algorithm with its own file extension (.jls) and container, and most mainstream software cannot decode it. If you need lossless, here are some options:

  1. Keep it in PNG. If you’re really concerned about PNG filesize, you can recompress with pngcrush, OptiPNG, or AdvancedCOMP.
  2. Use WebP in lossless mode. Supported by most modern browsers and often smaller filesize than PNG. Here is a guide to writing WebP images in Java using ImageIO or JDeli (if you use JDeli, you can call SetCompressionFormat to select lossless). In Python, you can use Pillow’s WebP support and pass lossless=True to save().
  3. Use AVIF (AV1 image format) in lossless mode. (Supported by recent versions of Chrome and Firefox.)
  4. Use JPEG-LS, JPEG 2000 in lossless mode, or JPEG XL in lossless mode. (JPEG XL support can be enabled in certain recent web browser versions.)

This article does a basic comparison of them, and this reddit post does a more thorough comparison. Overall, I’d say that WebP offers the best balance of wide software support and high compression ratios.

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement