Hot on the heels of Editing JPEG Photos Without Recompressing - Part 2, I've had a few days to ponder, and I'm back with more thoughts on editing JPEGs without recompression, and maybe with a conclusion. Where we left off, we'd determined that the JPEG compression steps that we'd need to deal with, in decoding order, are:
- Huffman decoding
- Run-Length Encoding
- Zig-Zag scan
- DCT transform
We're aiming to do as little of the above as possible, so to affect the image compression as little as possible (if at all).
What Have I Learned? It's Not Looking Good!
After digging into the above encoding and decoding process, it turns out the lossy compresion that we want to avoid takes place at the quantization stage. The quantization process, I now know, is a fairly simple dividing and rounding of values, with more brutal dividing at parts of the frequency domain where these frequencies have less impact on the appearance of that particular image block, something like the following (note I've used a 4x4 block size instead of the normal 8x8 for clarity):
The purpose of quantisation is to reduce the number of unique values, so that they can be effectively compressed using a combination of run-length and huffman encoding. But our saved data is no longer uniform across the block, because the quantisation process isn't uniform across the block - we can't apply any simple operations now to adjust the image brightness. But what if we apply the quantisation process as above to reduce the number of unique values, then remultiply the values so that we can apply a uniform brightness adjustment to the saved data? For example:
This plan falls down too - because I'm making my changes in the frequency domain, I can't do a uniform change across all my values, because they'll each map very differently to the space domain. And we can't apply non-uniform changes to the saved data, because this will bring in a whole host of new values, which will mean either having to massively inflate each file size, or having to recompress the image. Fail again.
The above is at this point all theory, so the next step is to prove this in code. I'm cooking up a little console app to do just that, so watch this space.