Embed text into PNG

13,440

Solution 1

If you want to embed text you may want to look at this in particular, which comes from the PNG specification. It seems a little on the complicated side.

The "easy" steganographic method looks a little more simple.

What may actually be better suited for your purpose is to create a javascript object that contains the image data and the JSON data - then just pass that object around wherever you need it.

Solution 2

I am completely unfamiliar with Python, however if you can access any of the prominent image processing libraries it is possible.

Take a look here for ImageMagick<->Python solutions.


Edit

You may wish to take a look at this blog post for information regarding steganography (hiding information within an image) and an offshoot of the ImageMagick library. It uses C++ here but I'm sure you could figure out a way to incorporate the base processes in Python.

Solution 3

Hmm, here’s a partial libpng implementation in JS: http://www.xarg.org/download/pnglib.js.

Solution 4

Copying another answer of mine in another question:

Here's an old not too-fancy module I did for a friend once (Python 2.x code):

the code

from __future__ import division

import math, os, array, random
import itertools as it
import Image as I
import sys

def encode(txtfn, imgfn):
    with open(txtfn, "rb") as ifp:
        txtdata= ifp.read()
    txtdata= txtdata.encode('zip')

    img= I.open(imgfn).convert("RGB")
    pixelcount= img.size[0]*img.size[1]
##  sys.stderr.write("image %dx%d\n" % img.size)

    factor= len(txtdata) / pixelcount
    width= int(math.ceil(img.size[0]*factor**.5))
    height= int(math.ceil(img.size[1]*factor**.5))

    pixelcount= width * height
    if pixelcount < len(txtdata): # just a sanity check
        sys.stderr.write("phase 2, %d bytes in %d pixels?\n" % (len(txtdata), pixelcount))
        sys.exit(1)
##  sys.stderr.write("%d bytes in %d pixels (%dx%d)\n" % (len(txtdata), pixelcount, width, height))
    img= img.resize( (width, height), I.ANTIALIAS)

    txtarr= array.array('B')
    txtarr.fromstring(txtdata)
    txtarr.extend(random.randrange(256) for x in xrange(len(txtdata) - pixelcount))

    newimg= img.copy()
    newimg.putdata([
        (
            r & 0xf8 |(c & 0xe0)>>5,
            g & 0xfc |(c & 0x18)>>3,
            b & 0xf8 |(c & 0x07),
        )
        for (r, g, b), c in it.izip(img.getdata(), txtarr)])
    newimg.save(os.path.splitext(imgfn)[0]+'.png', optimize=1, compression=9)

def decode(imgfn, txtfn):
    img= I.open(imgfn)
    with open(txtfn, 'wb') as ofp:
        arrdata= array.array('B',
            ((r & 0x7) << 5 | (g & 0x3) << 3 | (b & 0x7)
            for r, g, b in img.getdata())).tostring()
        findata= arrdata.decode('zip')
        ofp.write(findata)

if __name__ == "__main__":
    if sys.argv[1] == 'e':
        encode(sys.argv[2], sys.argv[3])
    elif sys.argv[1] == 'd':
        decode(sys.argv[2], sys.argv[3])

the algorithm

It stores a byte of data per image pixel using: the 3 least-significant bits of the blue band, the 2 LSB of the green one and the 3 LSB of the red one.

encode function: An input text file is compressed by zlib, and the input image is resized (keeping proportions) to ensure that there are at least as many pixels as compressed bytes. A PNG image with the same name as the input image (so don't use a ".png" filename as input if you leave the code as-is :) is saved containing the steganographic data.

decode function: The previously stored zlib-compressed data are extracted from the input image, and saved uncompressed under the provided filename.

I verified the old code still runs, so here's an example image containing steganographic data:

contains steganographic data

You'll notice that the noise added is barely visible.

Solution 5

You can use the recently released JavaScript library steganography.js. Have a look at the showcase and the documentation for the basic usage.
The library makes use of a HTML canvas and embeds the information in the alpha channel of the image. Basically you just need to use steg.encode(message, image) and pass the JSON-data (as string) as message and the image as dataURL. To read this information from the image use steg.decode(image) and pass the image as dataURL again.

After reading the information again you get the JSON-data as string so you will have to parse it to a JavaScript object again. So besides the parsing from/to string I hope it fits your requirements.

Share:
13,440
Cheery
Author by

Cheery

A programmer hobbyist.

Updated on June 12, 2022

Comments

  • Cheery
    Cheery almost 2 years

    I'm looking for a tool that'd let me embed json-formatted information inside a PNG -file.

    This far it's been quite quiet. Do I have to write it myself?

    I'd be especially interested of doing it with javascript. Into an image I extract from a canvas with toDataURL -method.