views:

166

answers:

5

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.

+2  A: 

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.

espais
+1  A: 

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

byoogle
+2  A: 

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.

Wayne Werner
A: 

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.

ΤΖΩΤΖΙΟΥ
A: 

Someone has already attempted to embed a Mario-like platformer game in a PNG image. Using getImageData is the key to reading out the data; the object that that method returns gives a one-dimensional array holding the individual RGBA values of each pixel.

Note that this does not "hide" the data within the area that the user sees, but you could probably use a CSS sprite like technique (or "crop" the image using multiple canvases) to show only the part of a combined image you want to.

idealmachine