#!/usr/bin/python
# -*- 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
  image.

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
advantages:

- 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
  preserved.

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],
                             stdout=subprocess.PIPE)
    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):
    os.makedirs(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)
            outfile.close()
            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  />')

    listing.write('</body\n>\n')
    listing.close()

if __name__ == '__main__':
    cut(sys.argv[1],
        dimensions(sys.argv[1]),
        sys.argv[2])

# 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