# -*- coding: utf-8 -*-
"""Cut a JPEG losslessly into bite-size chunks.

This program depends on the `jhead` and `jpegtran` programs.

Given as input a JPEG file and a name for a directory to
create into which to place the output, this program
losslessly cuts the JPEG into 65536-pixel-or-smaller JPEG
files in that directory, while writing an index file
`index.html` in that directory describing the structure of
the image. This index file is simultaneously:

- valid HTML 5 (I think!) which will render to an
  approximation of the image, when viewed in a browser with a
  wide enough window;
- well-formed XML 1.0 (although not valid XHTML!);
- readable, nicely-indented ASCII text;
- a version-controllable complete textual description of the

The whole directory is only marginally larger than the
original image.

The sort of crazy idea is that if you represent an image by a
directory full of files like this, you get some cool

- Changes to the image (touch-ups, etc.) can be represented
  in the `index.html` file, as filter tags enclosing some
  part of the file, and referring to other necessary raster
  data (e.g. pasted-in images, transparency masks, brush
  strokes, etc.) with more filenames (although of course this
  breaks the clever-clever HTML and ASCII punning);
- Thus the image can be version-controlled with a
  version-control system that really only knows how to handle
  text files, but can still do a reasonable job of merging
  edits between different versions of the image;
- Managing image edits with such an ‘edit decision list’
  would make it possible to perform edits initially on a
  low-resolution version of an image, then later reapply them
  to a high-resolution version.
- You can edit JPEGs without suffering progressive
  degradation, since the original image data is always

However, I haven’t implemented the whole image retouching
system, only a simple carving system that generates a punny
HTML/XML output file.


import subprocess, sys, os

tile_width = 256

class FuckedInTheJhead(Exception): pass
class JpegtranFailed(Exception): pass

def dimensions(fname):
    jhead = subprocess.Popen(['jhead', fname],
    for line in jhead.communicate()[0].split('\n'):
        if line.startswith('Resolution'):
            _, _, w, _, h = line.split()

    if jhead.returncode:
        raise FuckedInTheJhead(jhead.returncode)

    return int(w), int(h)

def cut(infname, (w, h), outdirname):
    listing = open(os.path.join(outdirname, 'index.html'), 'w')
    listing.write('<body data-width="%d"\n'
                  '      data-height="%d"\n    >'
                  % (w, h))

    d = tile_width
    for y in range(0, h, d):
        for x in range(0, w, d):
            tw, th = min(d, w-x),  min(d, h-y)
            geom = '%sx%s+%s+%s' % (tw, th, x, y)
            args = ['jpegtran', '-crop', geom, infname]

            outbasename = '%s_%s.jpg' % (x, y)
            outfname = os.path.join(outdirname, outbasename)
            outfile = open(outfname, 'wb')

            retcode = subprocess.call(args, stdout=outfile)
            if retcode:
                raise JpegtranFailed(retcode)

            listing.write('<img width="%d" height="%d" '
                          'data-x="%d" data-y="%d" '
                          'src="%s"\n    />'
                          % (tw, th, x, y, outbasename))

        listing.write('<br\n  />')


if __name__ == '__main__':

# Like everything else posted to kragen-hacks without a notice to the
# contrary, this software is in the public domain.
To unsubscribe: http://lists.canonical.org/mailman/listinfo/kragen-hacks

Reply via email to