High-Color GIF Images
One common reason suggested to prefer PNG over GIF, was that GIF files are limited to using 256-color palettes, and therefore unsuitable for rendering color images faithfully. It turns out this isn't quite true! GIF files can, in fact, contain an arbitrary number of colors, and in this post I will show how such high-color GIF images can be constructed that are quite suitable for web use.
Technical detailsAlthought it's true that GIF files are limited to an 8-bit pixel format (each pixel value indexing a table of at most 256 colors) a single GIF image may contain multiple graphic rendering blocks, each of which may include its own local color table of 256 colors drawn from a 24-bit color space.
Since a single image can display multiple graphic rendering blocks at once (either by rendering separate blocks into separate regions of the canvas, or by using transparency to overlap blocks without overwriting all previous pixels) the final image can contain an arbitrary number of different colors.
An easy approach to generating high-color GIF images is to partition the set of colors used over multiple frames which are then rendered on top of each other. The first frame contains the pixels with the 256 most-commonly used colors, while each next frame adds 255 new colors (reserving one palette index for transparency).
|170x240 pixels, 36330 colors,|
24-bit PNG, 88 KB
|170x240 pixels, 36330 colors,|
143 frames GIF, 242 KB
|170x240 pixels, 36330 colors,|
143 frames GIF, 337 KB
In the table above, the image on the left is a full-color (and rather colorful) PNG image. The middle image is a GIF file encoded using the technique described above. Some problems with the approach are apparent. The first is that modern browsers insert an artificial rendering delay between frames (refresh the page if you missed it while reading the introduction!) As a consequence, it takes a while for the picture to take shape, because each frame only contains a small fraction of the total image. And finally, because there are so many frames, the resulting file is rather large!
The awkwardness of incremental rendering can be ameliorated by encoding more image information into the first few frames. Instead of rendering only the pixels with exactly matching colors on each frame, we can approximate the other pixels with the nearest available color too. This means the first frame already contains an approximation of the full image, and that subsequent frames improve on (part of) the image by rendering additional details. Besides looking better when rendered incrementally, this has the advantage that GIF renderers that fail to render all frames will still display a reasonable approximation of the final image. The image on the right in the table above is the result of approximating the first few frames this way.
The table below shows the result of this approximate encoding of frames. The first row contains the first few frames of the GIF file. Note that the first image contains an approximation of all the pixels, while subsequent frames use transparency on pixels that cannot be better approximated using the frame-local palette. The second row shows the composite of the frames rendered so far, while the third row shows the symmetric difference with the final image (which should fade to black eventually).
This approximation technique, unfortunately, creates somewhat larger files, because frames tend to contain fewer transparent pixels and therefore aren't compressed as much.
However, the more important cause of the large file size of these GIF files, is that they span a lot of frames (143 for just this small image!) despite the fact (as the image above shows) that after a few frames, the end result is already approximated relatively well. It seems dubious whether the last hundred frames or so really add much information to the image, even though they are required to reproduce all pixels exactly.
In a GIF file ony pixel data is compressed while color tables are not. Therefore, every additional color requires at least three more bytes in the file (one byte for each component: red, green and blue) and using a large number of distinct colors isn't particularly efficient.
From a practical point of view, it makes more sense to reduce the number of different colors in the image. For example, if we want to use just 10 frames, we can still render 2551 different colors, which is certainly a big improvement over the 256 color palette GIF files typically use. This seems like a good compromise between image quality and file size.
To show how well this works, here is a 2551-color high-resolution version of the image shown before:
Yes, that is really a GIF image you're looking at! The table below summarizes the results for various image formats. Note that the simple 10-frame GIF image is actually smaller than the PNG image after quantization.
|Full image||Full image||Full image||Full image||Full image|
ConclusionAlthough the PNG file format is arguably superior to GIF in many ways, it is possible to render colorful images from GIF files. Even so, it makes sense to reduce the color palette somewhat to avoid creating excessively large files. One practical problem with this approach is web browsers' failure to render multiple blocks without delay; this is arguably a bug that ought to be fixed.
Tools usedIn case you want to perform similar experiments, I will list the tools I've used to create the images in this post (mostly written in Python). Feel free to use them however you like.
- high-color-gif-simple.py, for the straightforward/smaller file encoding.
- high-color-gif-fancy.py: for the approximating/larger file encoding
Since I couldn't find a good color quantization tool that works with more than 256 colors, I implemented my own using K-means clustering:
- quantize-kmeans2.py is simple and works well, but rather slow (especially for large images and color palettes).
- quantize-kmeans.c is my own implementation of Lloyd's algorithm in C instead, which is what I used to create the 2551 color image above.
When I compare the high color png to the medium and high color gif it is indeed hard to see any real difference (at least on my crappy laptop screen ).
The area that still does catch my eye is around the nose, there it's clear that png is superior.
If you continue on your topic of comparing file formats, you could look into the fact that small size (80x80 for example) images look a lot better with gif than with jpeg. It first occurred to me when I was trying to create a suitable thumbnail for tweakers .
I couldn't get the file under 29kb with jpeg while gif easily created 3kb files, this has obviously to do with the amount of color information available.
- Render 255 colours at a time
- Render a full image and continue approximating
- Render a full image and then add 255 colours at a time
If you'd want to save it lossles, saving it as a 24-bit bmp and then RAR-ing it gives you a 1130kb file. Which is smaller than the 24bit PNG.
Isn't that interesting?
I think an even better option that I failed to explore before, would be to approximate pixels in every frame, but only if the new approximation is better than the previous one by at least some constant factor. For example, applying this idea to the large 2551-color image (at least halving the Euclidean distance) yields this 1617 KB GIF. However, since this only improves the combined image for the middle frames of the GIF, I find it hard to justify the increase in file size.
[Comment edited on woensdag 20 februari 2013 16:07]
Yes, but not with more than 256 colors.Infant wrote on Wednesday 20 February 2013 @ 15:10:
Is PNG also capable of palleted images?
I'm not surprised — PNG is basically Paeth-filtering followed by DEFLATE compression (the same method used in ZIP files) and RAR is a comparatively much more powerful compression algorithm.If you'd want to save it lossles, saving it as a 24-bit bmp and then RAR-ing it gives you a 1130kb file. Which is smaller than the 24bit PNG.
I've experimented with simple yet efficient lossless image compression before, and I got the best results by separating the image by color channel, then Paeth-filtering, then LZMA compression. This would beat PNG almost every time, mainly due to the fact that LZMA is a superior compression algorithm.
Comments are closed