#!/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('
' % (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('' % (tw, th, x, y, outbasename)) listing.write('