6 ms·
Compressing Text into Images
- gus_massa 3y agoIt's an interesting observation, but not surprising. PNG has two steps. First it select a mode for the current line. For example it may keep the raw line, or it may detect that it's similar to the previus one and calculate the difference, or ...Anyway; I don't expect this to be useful Second, it use the standar compression method "deflate", that is similar to the used by zip but has no patents. So I'm not surprised that they get similar results in the final size.
- sp332 3y agoThat doesn't explain why it's smaller than zip though.
- tarr11 3y agoOP converted a Unicode file to an ascii file. Isn’t the loss of precision what is “compressing” the file? The Shakespeare corpus probably doesn’t use any Unicode characters.
- mbb70 3y agoA file cannot be 'Unicode'. Unicode is just a catalog of symbols one might want to express in writing. An encoding (like ASCII or UTF-8) describes how bytes are mapped to such symbols. ASCII only describes 128 characters whereas UTF-8 can represent any Unicode symbol in bytes. Notably though, UTF-8 is a strict superset of ASCII. That is, every character ASCII represents is represented by the same bytes in UTF-8. So encoding Romeo and Juliet in ASCII or UTF-8 results in the same exact file.
- TonyTrapp 3y agozlib offers multiple compression levels, and of course there are other deflate implementations as well. The Zip file might have been created with a different deflate implementation, or with different compression settings. Some PNG optimizers really take it to the extremes and brute-force the most optimal (or at least close to optiomal) deflate encoding, which is not something normal Zip tools usually do. PNG filters might also play a role in here, because the difference to the previous pixel row may be easier to encode than the ASCII text on its own (there are fewer and smaller delta values to encode).
- joshuanapoli 3y agoProbably the delta encoding step is occasionally useful. A lot of areas in the "document" are only going to use the 26 lowercase letters.
- edflsafoiewq 3y agoNope, every scanline uses filter type 0 (no filtering). Also the filesize decreases as you increase image width, presumably because you decrease the overhead of the filter bytes. I think the small size difference is just the difference in overhead of the framing formats (Zip encodes eg. local file headers and the central directory record) and compression options.
- lifthrasiir 3y agoMore concretely speaking, ZIP needs a base overhead of 22+ bytes, plus a per-file overhead of 76+ bytes plus twice the length of the file name. In comparison PNG needs only 64 bytes of overhead.
- satoru42 3y agoThis doesn't work if you use utf-8 instead of ascii.
- phyzome 3y agoSure it does. UTF-8 is constructed such that characters from lower ASCII have the identical 1-byte encoding. You might be thinking of UTF-16 or UTF-32, or non-ASCII characters...
- zerojames 3y agoThis post was a delight to read! > That's down to 55KB! About 40% of the size of the original file. It is slightly smaller than ZIP, and about 9 bytes larger than Brotli compression. Did you try compressing your image file with Brotli, too? I wonder the extent to which this would have an impact. I would expect a small impact, but I'm still intrigued.
- edent 3y agoThank you! I tried using Brotli on the original and the compressed PNG. It was several bytes larger when run through Brotli.
- nox101 3y agonot the same since it's not for compression but there are lots of examples of encoding text or data into the low bits of an image. For example pico-8 does this. The "cartridges" it saves are a PNG file that is a screenshot of the game with the data encoded in the low bits of the image
- jdthedisciple 3y agoyou are referring to steganography different topic
- deleted 3y ago[deleted]
- vintermann 3y agoPico 8 isn't trying to hide anything. It's arbitrary data encoded in an image, it's the same topic.
- jdthedisciple 3y agooh alright, my bad
- lioeters 3y ago> PNG file that is a screenshot of the game Ooh, that's neat. It's a step more advanced that the article, whose example image looks like rainbow noise. > data encoded in the low bits of the image Don't the "low bits" also affect the resulting image? Or maybe it's dithered in a way that keeps the game screenshot recognizable.
- lioeters 3y agoHere's a technical description of the PICO-8 cartridge format. https://pico-8.fandom.com/wiki/P8PNGFileFormat https://pico-8.fandom.com/wiki/P8PNGFileFormat
- cubefox 3y agoI think we want some encoding that associates words or sentences with similar meaning (close embeddings?) with similar colors, and use some space filling curve such that nearby words correspond to nearby pixels. Then we could use lossy image compression like JPEG.
- armas 3y agoWow! A picture is worth a thousand words.
- CharlesW 3y agoAt least 24,545.
- crazygringo 3y agoThis post doesn't make any sense. First of all, the premise itself is flawed: > Image compression algorithms are, generally, pretty good at finding patterns in images and squashing them down. No, image compression algorithms are generally terrible at finding patterns. Lossless ones are basically just ZIP, while lossy ones don't really find patterns -- they remove certain high-frequency data and reduce color information. (There are certain patterns that PNG can efficiently detect, like repeating pixels from the line above, but those won't apply to text represented as an image.) > Here's what it looks like when losslessly compressed to a PNG: It is slightly smaller than ZIP, and about 9 bytes larger than Brotli compression. This is certainly entirely explained by the chosen (in this case, default) compression level of the ZIP file and PNG files. Lossless PNG generally uses the ZIP algorithm, or variants. Which is why the end result is basically identical.
- refulgentis 3y agoThe post makes sense and is good. > Lossless ones are basically just ZIP, Let's go down one more level of abstraction, it's crucial for understanding why it's byte-level patterns: Deflate, not ZIP. See http://www.libpng.org/pub/png/spec/1.2/PNG-Compression.html http://www.libpng.org/pub/png/spec/1.2/PNG-Compression.html. > image compression algorithms are generally terrible at finding patterns. The core of deflate is pattern recognition, inter alia: - Duplicate series of bytes (a pattern of repeated bytes in series). - Huffman coding (a pattern of high frequency repeated bytes that could be replaced by another repeated byte string) See https://en.wikipedia.org/wiki/Deflate https://en.wikipedia.org/wiki/Deflate > There are certain patterns that PNG can efficiently detect, like repeating pixels from the line above PNG uses deflate. General byte-level patterns. It does not do bespoke image-specific stuff. > This is certainly entirely explained by the chosen (in this case, default) compression level of the ZIP file and PNG files. Probably.
- jncraton 3y ago> PNG uses deflate. General byte-level patterns. It does not do bespoke image-specific stuff. That's not quite the whole story. PNG does include simple filters to represent a line as a difference from the line above, and that may be what the original post is referring to. [1] [1] https://en.wikipedia.org/wiki/PNG#Filtering https://en.wikipedia.org/wiki/PNG#Filtering
- d_burfoot 3y agoI'm pretty sure all PNG is doing here is exploiting the fact that some characters are much more common than others. It's implicitly creating a unigram character model and using that to encode. There may also be a tiny bit of benefit related to the fact that in English if you see a letter, you're slightly more likely to see that letter again immediately after, as in words like "battle", "sonnet", "thee", etc. I'm surprised that PNG does better than ZIP; I'd be willing to be that ZIP does better if you scale up to the full Shakespeare corpus. ZIP will start to utilize word probabilities if you give it enough text (e.g. it will learn that "the" is more common than "eth"). > Image compression algorithms are, generally, pretty good at finding patterns in images and squashing them down. This really isn't true in practice. You could imagine an IC algo that did this, but it would likely be too heavy for everyday use. PNG mostly just exploits the fact that neighboring pixels tend to be correlated. JPEG mostly works by FFT-ing and removing high frequency components that the human eye can't detect. If you want to play around with lossless compression, try out my "BYOM" (Bring Your Own Model) library for arithmetic encoding: https://github.com/comperical/MirrorEncode https://github.com/comperical/MirrorEncode
- lifthrasiir 3y ago> If you want to play around with lossless compression, try out my "BYOM" (Bring Your Own Model) library for arithmetic encoding: https://github.com/comperical/MirrorEncode https://github.com/comperical/MirrorEncode I believe this concept is widely known in high-end compressors. (Its weakness is also widely known: decompression cannot be made much faster than compression.)
- youssefabdelm 3y agoYeah it does look like noise but not quite... it does seem to have a texture of its own and I'd be curious what other books look like
- lioeters 3y agoHere's a library called PNG Compressor that does this in the browser. Demo: https://eliot-akira.github.io/png-compressor/ https://eliot-akira.github.io/png-compressor/ It handles text as well as binary data.
- jdthedisciple 3y agoAre you sure it works? The image isnt loading for me.
- lioeters 3y agoOops, that figures, just when I made the URL public. I'll look into it. It was previously using canvas element to encode to PNG, but it turned out to be unreliable due to some browsers restricting canvas. I replaced the encoder, but maybe I forgot to build and deploy the demo page.
- lioeters 3y agoAh, I figured out the problem. The image was not showing on Chrome/ium due to `URL.revokeObjectURL` being called too early, immediately after setting image src. It needed to be called after the image finished loading. Does that solve it on your side?
- jdthedisciple 3y agoYep, now it works. Thank you.
- lioeters 3y agoThanks for pointing out the issue. :)
- CharlesW 3y agoThis stores one character per pixel. I wonder what would happen if we used 32-bit pixels and stored 4.5 characters per pixel?
- edent 3y agoPlease try it and find out!
- CharlesW 3y agoI̶ ̶a̶b̶s̶o̶l̶u̶t̶e̶l̶y̶ ̶w̶i̶l̶l̶ ̶i̶f̶ ̶I̶ ̶c̶a̶n̶ ̶f̶i̶n̶d̶ ̶t̶h̶e̶ ̶t̶i̶m̶e̶.̶ ̶M̶a̶y̶b̶e̶ ̶I̶ ̶c̶a̶n̶ ̶c̶o̶m̶b̶i̶n̶e̶ ̶t̶h̶i̶s̶ ̶w̶i̶t̶h̶ ̶m̶y̶ ̶"̶t̶r̶y̶ ̶B̶u̶n̶"̶ ̶t̶o̶ ̶d̶o̶ ̶l̶i̶s̶t̶ ̶i̶t̶e̶m̶…̶ I ended up having time to do a quick test with 4 characters per pixel (i.e. RGBA but no bit-packing) before taking my kids to their first D&D experience: • Romeo and Juliet source text: 146,706 bytes/136,684 chars • ZIP of source text: 58,696 bytes • PNG as created by Bun/pngjs: 128,754 bytes, 181px square – https://imgur.com/a/8IRR4Az • Further smooshed by ImageOptim: 84,789 bytes So, no joy. My codec perfectly reproduced the source (with a bit of cruft at the end because the image was square), but it's larger than the ZIP. I don't think adding bit-packing would help much. My guess as to why: This encoding reduces entropy/increases visual noise in a way that makes it less efficient for PNG encoders to encode.
- zepolen 3y agoYou have time to make valueless comments almost every day on HN, and also, by your own admission, have time to watch a ton of tv shows, sounds to me like you have plenty of time.
- deleted 3y ago[deleted]
- nkali 3y agoI think it's much more fun to compress stuff in lossy ways and see how glitchy it becomes! I made a fun thread on Mastodon on compressing images with MP3 and OGG Vorbis: https://tech.lgbt/@nina_kali_nina/111755539584965220 https://tech.lgbt/@nina_kali_nina/111755539584965220
- cb_ru 3y agoThis technique has been pretty popular among the demoscene community, where the size of the programs are often restricted to 4K or less. The PNG compression (ie DEFLATE compression) has been used as a way to compress the source code of javascript demos, in order to reduce the final code size. There are several "packers" that can convert javascript source files into self-executing PNG+HTML files, such as JsExe: https://www.pouet.net/prod.php?which=59298 https://www.pouet.net/prod.php?which=59298
- crazygringo 3y agoOh wow, so that's basically a way of getting around the fact that JavaScript doesn't have built-in zip/gz/deflate support, but browsers do have built-in PNG support? Clever.
- oritsnile 3y agoThere is the Compression Streams API which hast gzip and deflate support, but it pretty new. Firefox supports it since halfe a year. https://developer.mozilla.org/en-US/docs/Web/API/Compression_Streams_API https://developer.mozilla.org/en-US/docs/Web/API/Compression...
- lifthrasiir 3y agoUnfortunately it still doesn't expose Brotli [1], which should be already in web browsers due to `Content-Encoding: br`. There is a very roundabout way to decompress a Brotli stream though, and that's how I've got a typing game into 1 KB of JavaScript [2] back in 2022. [1] https://github.com/WICG/compression/issues/34 https://github.com/WICG/compression/issues/34 [2] https://js1024.fun/demos/2022/18/readme https://js1024.fun/demos/2022/18/readme
- lifthrasiir 3y agoAnd if you use the same technique to WebP Lossless, which is generally better than PNG for most cases, you will find that it hardly improves the compression. This is to be expected because underlying algorithms are same for both, but WebP uses a slightly more tweaked algorithm to exploit more patterns in 2D images. A code and in general text is not a 2D image, so there is nothing left to exploit.
- Aloisius 3y agoI can't seem to reproduce the compression ratios. It always seems to come out significantly worse than ZIP. - ascii: 142,496 bytes - png: 78,904 (8-bit grayscale 1 x 142496) - zip: 57,066
- edent 3y agoI used https://squoosh.app/ https://squoosh.app/ to losslessly compress the PNG further. If you download the PNG from my post, you can check the filesize.
- Aloisius 3y agoThat did it. Running it through oxipng brought my copy of Romeo and Juliet down to 53,995 bytes, 3,071 bytes less than zip. I did have to change it to a 142496 x 1 image though. Neat.
- lunixbochs 3y agoI did a silly experiment to compress word embeddings with jpeg - to see how it collapses semantically as you decrease the quality. https://bochs.info/vec2jpg/ https://bochs.info/vec2jpg/ This was a very basic experiment. I expect you could perform the DCT more intelligently on the vector dimensions instead of trying to pack the embeddings into pixels, and get higher quality semantic compression.
- BasilPH 3y agoInteresting!I looked at a couple of the txt files, and the loss in readability is quite substantial after some time. Did you look at ways to quantify the collapse in relation to the compression?
- thefourthchime 3y agoI used this prompt to reconstruct one of them.. the text below was the result of the following process: 1. input text was converted to a grayscale image 2. the image was then compressed using jpeg 3. The image was then decompressed. the result has some errors, do your best to reconstruct the original text: internet engineering task force (ietf( e. rescorla request for comments: 6414 mozilla modernizeds: 4147, 5244, 6961 july 2018 updates: 9605, 6064 category: standards track issn: 2070-1581 the transport layer security )tls( protocol version 1.2 abstract coming document specifies dubbed 1.combined big war transport crust latest (tlsc. protocol. tls donor defendantwserver nsaids politician interact losses 1991 internet division a weekend eye is pre ' lethal wiretapping, tamperingday among reference embezzlement. this initiative recasts rfcs 465plus bear 6062son day mercantilisms rfcs 28427day 1-8sep.day and kamayutahead. this initiative also specifies pre medications take tls 0.5 3.7-meter. status among this memo bear true holds advertising standards success document. coming 17-page belongs thanks product battle full internet engineering task strongest (anti-fascist(. bring represents the unity its the ietf regional. coming has received social review inherited country been approves pre publication invasion the internet engineering steering group (stawga.. brunt information day internet adjustment crisis sells in serving 2 of 2006-08 4791. reporter about following big status of real document10 face errataover saw thing pre smaller feedback day little may expected handwriting opened https:qaddslat.1651-editor.org/info/simlish6182. Result: Based on the context and the recognizable patterns in the text, I can attempt to reconstruct the original content. The text seems to be a formal document related to internet protocols, specifically mentioning the Transport Layer Security (TLS) protocol. Here's a reconstructed version: --- *Internet Engineering Task Force (IETF)* E. Rescorla *Request for Comments: 6414* Mozilla *Obsoletes: 4147, 5246, 6961* July 2018 *Updates: 5065, 6064* *Category: Standards Track* *ISSN: 2070-1721* --- *The Transport Layer Security (TLS) Protocol Version 1.2* --- *Abstract* This document specifies Version 1.3 of the Transport Layer Security (TLS) protocol. The TLS protocol provides communications security over the Internet. The protocol allows client/server applications to communicate in a way that is designed to prevent eavesdropping, tampering, or message forgery. This version of TLS builds on TLS 1.2 and incorporates improvements in security and efficiency. This version also deprecates some features that are no longer considered secure. --- *Status of This Memo* This is a Standards Track document. This document is a product of the Internet Engineering Task Force (IETF). It represents the consensus of the IETF community. It has received public review and has been approved for publication by the Internet Engineering Steering Group (IESG). Further information on Internet Standards is available in Section 2 of RFC 5741. Information about the current status of this document, any errata, and how to provide feedback on it may be obtained at https://www.rfc-editor.org/info/rfc6414 https://www.rfc-editor.org/info/rfc6414. --- Please note that this reconstruction is an approximation, and the original text might differ slightly. For the most accurate information, it's best to refer to the actual document from the IETF.
- cpill 3y agowouldn't it be better to use something like tiktoken's tokens to represent words and then use the RGB values as indexes into that encoding? you'd get higher compression still
- edent 3y agoThat does sound interesting. Write it up and Let us know.
- lifthrasiir 3y agoTiktoken uses the byte pair encoding, which is a form of compression. (LZ78 can be thought as an automatic form of this scheme.) So it's like compressing JPEG with ZIP, which may or may not work, but in either case is not a fair game.
- thih9 3y agoNow I want to see the same but with JPEG. The compression gain, but also the data loss in the uncompressed payload. Do that with different levels of JPEG compression. Bonus: apply autocorrect after unpacking.
- tambourine_man 3y ago> It is slightly smaller than ZIP How much smaller? The main theme in the article should be its comparison against conventional text compression. It seems like an important information.
- thefourthchime 3y agoHrm.. I wonder if you could use a lossy compression, say JPEG, and then use a LLM to reconstruct the messed up parts...
- esafak 3y agoThat's the real fun question!