JPEG and PNG – lossy and lossless image compression
I was reviewing enhancement requests recently when I came across this one: Add support for a 'Quality' parameter when using imwrite to write a PNG file, just like you can currently do when writing a JPEG file.
Well, there is actually a pretty good reason why there is no 'Quality' parameter for writing PNG files, but it's not an obvious reason unless you know more about the differences between various image compression methods.
A very important characteristic of a compression method is whether it is lossless or lossy. With a lossless compression method, the original data can be recovered exactly. When you make a "zip" file on your computer, this is what you certainly expect.
Here's an example: I can use gzip to compress the MATLAB script file I'm using for this blog post.
gzip('lossless_lossy.m'); dir('lossless_lossy.*')
lossless_lossy.m lossless_lossy.m.gz
Let's compare their sizes to see if the output of gzip is really compressed.
d1 = dir('lossless_lossy.m');
d1.bytes
ans = 4098
d2 = dir('lossless_lossy.m.gz');
d2.bytes
ans = 1785
Indeed, the compressed does actually have fewer bytes. Can we get the original file back exactly?
gunzip('lossless_lossy.m.gz','./tmp') isequal(fileread('lossless_lossy.m'),fileread('./tmp/lossless_lossy.m'))
ans = 1
Yes.
Let's switch to image formats. The PNG image format uses lossless compression. When you save image data to a PNG file, you can read the file back in and get back the original pixels, unchanged. For a sample image I'll use my imzoneplate function on the MATLAB Central File Exchange.
I = im2uint8(imzoneplate); imshow(I)
Let's write I out to a PNG file, read it back in, and see if the pixels are the same.
imwrite(I,'zoneplate.png'); I2 = imread('zoneplate.png'); isequal(I,I2)
ans = 1
OK, now let's try the same experiment using JPEG.
imwrite(I,'zoneplate.jpg') I2j = imread('zoneplate.jpg'); isequal(I,I2j)
ans = 0
No, the pixels are not equal! It turns out the JPEG is a lossy image compression format. (Full disclosure: there is a lossless variant of JPEG, but it is rarely used.)
Why in the world would we use a compression format that doesn't preserve the original data? Because by giving up on exact data recovery and by taking advantage of properties of human visual perception, we can make the stored file a lot smaller. Let's compare the file sizes of the PNG file with the JPEG file.
z1 = dir('zoneplate.png');
num_bytes_png = z1.bytes
num_bytes_png = 218864
z2 = dir('zoneplate.jpg');
num_bytes_jpeg = z2.bytes
num_bytes_jpeg = 72660
The JPEG file is only one-third the size of the PNG file! But it looks almost exactly the same.
imshow('zoneplate.jpg')
So, what about that 'Quality' parameter that I mentioned at the top of today's post? It turns out that we can make the JPEG file even smaller if we are willing to put up with some visible distortion in the image. Let's try a quality factor of 25. (The default is 75 on a scale of 0 to 100.)
imwrite(I,'zoneplate_25.jpg','Quality',25) I2j_25 = imread('zoneplate_25.jpg'); imshow(I2j_25)
You can see some distortion, especially around the high-frequency part of the pattern. How about the file size?
z3 = dir('zoneplate_25.jpg');
z3.bytes
ans = 39544
That's about 54% of the size of the zoneplate.jpg.
Sometimes people think they can get "lossless JPEG" by using a 'Quality' factor of 100, but unfortunately that isn't the case. Let's check it:
imwrite(I,'zoneplate_100.jpg','Quality',100) I2j_100 = imread('zoneplate_100.jpg'); isequal(I,I2j_100)
ans = 0
So there you go -- that's why there's no 'Quality' parameter when writing PNG files. PNG files are always perfect!
When I write blog posts, sometimes I use PNG image files and sometimes I use JPEG. My choice is based on what kind of graphics I have in that particular post. And that's a blog topic for another day.
Comments
To leave a comment, please click here to sign in to your MathWorks Account or create a new one.