Embed text messages into JPEGs without altering their visual perception.
Atrium is a proof-of-concept command line application that enables you to embed text into a JPEG image without perceptibly altering it.
This is a toy. It's probably not useful in your real-world use case. See the Discussion section for more information.
Steganography is the technique of concealing information within some other artifact that is ostensibly only carrying the immediately or visually apparent information. Any medium can be used as the "host" to carry the secret message.
The JPEG image format is ubiquitous but potentially a poor steganographic carrier because it is a lossy file format. This means that modulated RGB pixel values in an image might be altered during the JPEG image encoding (i.e., compression), rendering the secret message encoded by the modulation unreadable.
Atrium is a proof-of-concept application for embedding information in an image's perceived visual content, rather than its RGB pixels.
Atrium embeds a secret text message into a JPEG by modulating the quantized values of the underlying DCT coefficients to achieve parity with bits in the message. This makes Atrium resistent to JPEG's lossy format.
Additionally, human eyes cannot detect small visual changes - in fact, that is what JPEG is exploiting in the first place - so, the visual changes due to Atrium's encoding are often imperceptible. The file size overhead is minimal (~3KB for a small message) as well.
Atrium embeds one byte of text into each 8x8 pixel region, but it still relies on Java itself to write out the encoded JPEG. This ensures the output image is a valid JPEG image no matter what.
- Download and install a JVM.
- Download and install Scala.
- Download and install maven.
- Download the Atrium Project.
- Compile the project:
$ cd atrium
$ mvn clean package
The basic usage is to encode text into an image, and then decode it.
The application also offers an info command,
which prints descriptive information about a JPEG image.
Run atrium
by itself to see help information.
To encode text into an image:
$ ./atrium encode --out encoded.jpg image.jpg "Hello, atrium."
Input Image | Encoded Image |
---|---|
The output image defaults to the JPEG compression quality of the input image,
but you can configure the encoded output image's quality with the
--quality
argument.
If an output file path is not specified, atrium defaults to the input file name with "-atrium" appended:
$ ./atrium encode image.jpg "Hello, atrium."
$ du -h image*.jpg
124K image-atrium.jpg
120K image.jpg
To decode the text from an Atrium-encoded image:
$ ./atrium decode encoded.jpg
Hello, atrium.
To print file information for a JPG image:
$ ./atrium info image.jpg
As mentioned already, this project is purely a proof-of-concept. There are a number of limitations including:
-
Atrium is untested for long input messages.
-
Atrium doesn't work for pure white backgrounds. This is because Luminance values won't decode if wrapped-around at 248, so Atrium caps them at 248, which chops off the DCT modulation. See the ball image.
-
High and low JPEG compression qualities are untested (i.e., unsupported). Testing showed Atrium to be most effective in the 60 - 80 JPEG quality range. You'll notice that the roman-atrium image fails to decode a long message with default encoding due to its high (99) quality. If you encode with a lower quality (e.g., 80), decoding works.
Still, even with its limitations, Atrium works well under the right conditions. For example, Atrium encoded a message into this Instagram post and successfully decodes the message from the downloaded image. This is despite Instagram's re-encoding and metadata stripping process.
Thanks for checking it out.