Encoding and decoding data in PNG with compression
I want to talk about a couple of interesting ways to encode data - encoding in the form of a picture and embedding them into an existing picture. I experimented with the PNG format because it uses lossless compression and is supported in browsers in the canvas
element. I am interested in JavaScript, so the implementation will be written in it. The code is implemented as a JS library and posted on GitHub under the MIT license.
The first encoding option is to generate a new picture based on arbitrary data. To do this, each byte of data is recorded sequentially in the RGB channels of the PNG picture, while the alpha channel is not touched, since when the alpha channel changes, the RGB colors partially change when unloading from canvas
to PNG. In this variant, WIDTH * HEIGHT * 3
bytes of data can be packed in PNG. When encoding text, the image size is smaller than the source text, since Deflate compression is applied to the data.
// encode file to PNG
asPNG.encode(file).then(blob => {
// encoded blob
});
// decode file from PNG
asPNG.decode(file).then(blob => {
// decoded blob
});
The second encoding option is even more interesting and belongs to the area of steganography. The essence of the approach is that the encoded data is hidden among the original image data in such a way that the visually obtained image is almost indistinguishable from the original. The algorithm of my implementation is as follows:
- We align the data of each color channel in steps of 7, do not touch the alpha channel. This reduces the color gradation of the image by 7 times, instead of 256 shades per channel, we get approximately 36 shades per channel. This alignment degrades the image quality, but improves its compression and when adding a small amount of data, the size of the original PNG file decreases.
- We add data in such a way that each byte of data can be encoded in one pixel of the image. To do this, add 1 byte of data in a sevenfold format to the aligned data.
- We distribute the data evenly throughout the image if there is less data than can be placed in the image.
In this variant, up to WIDTH * HEIGHT
bytes of data can be packed in PNG.
// inject data to PNG image
asPNG.inject(data, img).then(blob => {
// modified image
});
// extract data from PNG image
asPNG.extract(img).then(blob => {
// extracted data
});
Comments