home *** CD-ROM | disk | FTP | other *** search
/ Hackers Magazine 57 / CdHackersMagazineNr57.iso / Software / Multimedia / k3d-setup-0.7.11.0.exe / lib / site-packages / cgkit / stitch.py < prev    next >
Encoding:
Python Source  |  2007-01-11  |  7.9 KB  |  248 lines

  1. #!/usr/bin/env python
  2. # ***** BEGIN LICENSE BLOCK *****
  3. # Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4. #
  5. # The contents of this file are subject to the Mozilla Public License Version
  6. # 1.1 (the "License"); you may not use this file except in compliance with
  7. # the License. You may obtain a copy of the License at
  8. # http://www.mozilla.org/MPL/
  9. #
  10. # Software distributed under the License is distributed on an "AS IS" basis,
  11. # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. # for the specific language governing rights and limitations under the
  13. # License.
  14. #
  15. # The Original Code is the Python Computer Graphics Kit.
  16. #
  17. # The Initial Developer of the Original Code is Matthias Baas.
  18. # Portions created by the Initial Developer are Copyright (C) 2004
  19. # the Initial Developer. All Rights Reserved.
  20. #
  21. # Contributor(s):
  22. #
  23. # Alternatively, the contents of this file may be used under the terms of
  24. # either the GNU General Public License Version 2 or later (the "GPL"), or
  25. # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  26. # in which case the provisions of the GPL or the LGPL are applicable instead
  27. # of those above. If you wish to allow use of your version of this file only
  28. # under the terms of either the GPL or the LGPL, and not to allow others to
  29. # use your version of this file under the terms of the MPL, indicate your
  30. # decision by deleting the provisions above and replace them with the notice
  31. # and other provisions required by the GPL or the LGPL. If you do not delete
  32. # the provisions above, a recipient may use your version of this file under
  33. # the terms of any one of the MPL, the GPL or the LGPL.
  34. #
  35. # ***** END LICENSE BLOCK *****
  36. # $Id: stitch.py,v 1.3 2005/10/16 12:29:04 mbaas Exp $
  37.  
  38. """Image stitching module.
  39.  
  40. This file can also be used as a command line tool.
  41. """
  42.  
  43. import sys, os, os.path, glob, optparse
  44. import _Image as Image
  45.  
  46. # isTile
  47. def isTile(name):
  48.     """Check if a filename is the name of a tile.
  49.     """
  50.     try:
  51.         splitTileName(name)
  52.     except ValueError:
  53.         return False
  54.     return True
  55.  
  56. # splitTileName
  57. def splitTileName(name):
  58.     """Split the components of a tile name.
  59.  
  60.     The return value is a tuple (basename, extension, coords) where
  61.     coords is a tuple (x1,x2,y1,y2).
  62.     A ValueError exception is thrown if name is not a valid tile name.
  63.     """
  64.     coords = []
  65.     n,ext = os.path.splitext(name)
  66.     for i in range(4):
  67.         j = n.rfind("_")
  68.         if j==-1:
  69.             raise ValueError, "no tile name"
  70.         try:
  71.             coords = [float(n[j+1:])]+coords
  72.         except:
  73.             raise ValueError, "no tile name (invalid coordinate)"
  74.         n = n[:j]
  75.  
  76.     return n,ext,tuple(coords)
  77.  
  78. # outputSpecs
  79. def outputSpecs(tiles):
  80.     """Return the final image resolution and the pixel positions of the tiles.
  81.  
  82.     Returns a tuple (width, height, xposs, yposs) where xposs/yposs are
  83.     dictionaries that store the x and y positions of the tiles. The key
  84.     is the x1 (resp. y1) value and the value is the position.
  85.     """
  86.     xsplits = {}
  87.     ysplits = {}
  88.  
  89.     xres = 0
  90.     yres = 0
  91.     # Sum up the width (height) of each tile with a new x1 (y1) value...
  92.     for tilename, coords, img in tiles:
  93.         x = coords[0]
  94.         y = coords[2]
  95.         w,h = img.size
  96.         if x in xsplits:
  97.             if w!=xsplits[x]:
  98.                 raise ValueError, "%s: Inconsistent tile resolution, expected a width of %d but got %d"%(tilename, xsplits[x], w)
  99.         else:
  100.             xsplits[x] = w
  101.             xres += w
  102.         if y in ysplits:
  103.             if h!=ysplits[y]:
  104.                 raise ValueError, "%s: Inconsistent tile resolution, expected a height of %d but got %d"%(tilename, ysplits[y], h)
  105.         else:
  106.             ysplits[y] = h
  107.             yres += h
  108.  
  109.     # Create a dict with the x pixel positions of the tiles...
  110.     xv = xsplits.keys()
  111.     xv.sort()
  112.     xposs = {}
  113.     xpos = 0
  114.     for x in xv:
  115.         xposs[x] = xpos
  116.         xpos += xsplits[x]
  117.  
  118.     # Create a dict with the y pixel positions of the tiles...
  119.     yv = ysplits.keys()
  120.     yv.sort()
  121.     yposs = {}
  122.     ypos = 0
  123.     for y in yv:
  124.         yposs[y] = ypos
  125.         ypos += ysplits[y]
  126.  
  127.     return xres, yres, xposs, yposs
  128.  
  129. # stitch
  130. def stitch(filename, removetiles=False, infostream=None):
  131.     """Stitch several image tiles together.
  132.     
  133.     filename is the base name of the image that determines the file names
  134.     of the tiles. filename is also the name of the output image.
  135.     If removetiles is True, the individual image files will be deleted
  136.     after the image has been stitched.
  137.     If infostream is set to a file like object it is used to output
  138.     status information about the stitching process.
  139.  
  140.     The name of an image tile must contain the crop information that was
  141.     used to create the image. For example, the name of a tile for an
  142.     image "out.tif" could look like this: "out_0.0_0.5_0.75_1.0.tif".
  143.     The four values are the x1,x2,y1,y2 values of the crop window. Those
  144.     values together with the resolution of the tile determine the resolution
  145.     of the entire image. The position of the tile within that image is
  146.     given by x1,y1.
  147.     """
  148.     
  149.     inname,inext = os.path.splitext(filename)
  150.     tilenames = glob.glob("%s*%s"%(inname,inext))
  151.  
  152.     # A list of tuples (tilename, coords, img)
  153.     tiles = []
  154.     mode = None
  155.     # Filter tiles and fill the tiles list...
  156.     for tilename in tilenames:
  157.         # No tile at all?
  158.         if not isTile(tilename):
  159.             continue
  160.         
  161.         name,ext,coords = splitTileName(tilename)
  162.         
  163.         # A tile of another image?
  164.         if name!=inname or ext!=inext:
  165.             continue
  166.  
  167.         # Open the tile image...
  168.         img = Image.open(tilename)
  169.  
  170.         # Set required mode
  171.         if mode==None:
  172.             mode = img.mode
  173.         if img.mode!=mode:
  174.             raise ValueError, "%s: Mode mismatch, %s instead of %s"%(tilename, img.mode, mode)
  175.  
  176.         tiles.append((tilename, coords, img))
  177.  
  178.     # No tiles? then exit
  179.     if len(tiles)==0:
  180.         if infostream!=None:
  181.             print >>infostream, 'No image tiles found for image "%s"'%filename
  182.         return
  183.  
  184.  
  185.     # Create the output image...
  186.     width, height, xposs, yposs = outputSpecs(tiles)
  187.     mode = tiles[0][2].mode
  188.     if infostream!=None:
  189.         print >>infostream, "Final resolution: %dx%d, mode: %s"%(width, height, mode)
  190.     outimg = Image.new(mode, (width, height))
  191.  
  192.     # Paste the tiles into the output image...
  193.     for tilename, coords, img in tiles:
  194.         x = xposs[coords[0]]
  195.         y = yposs[coords[2]]
  196.         outimg.paste(img, (x, y))
  197.  
  198.     # Delete the reference to the image
  199.     img = None
  200.  
  201.     # Save the output image
  202.     if infostream!=None:
  203.         print >>infostream, "Stitched %d tiles"%len(tiles)
  204.         print >>infostream, 'Saving "%s"...'%filename
  205.     outimg.save(filename)
  206.  
  207.     # Remove tiles (if desired)
  208.     if removetiles:
  209.         if infostream!=None:
  210.             print >>infostream, "Removing tiles..."
  211.         # Get the tile names and delete the tiles list
  212.         # so that the images are no longer referenced
  213.         # (otherwise they couldn't be deleted)
  214.         tilenames = map(lambda x: x[0], tiles)
  215.         tiles = None
  216.         for tilename in tilenames:
  217.             os.remove(tilename)
  218.  
  219.  
  220. ######################################################################
  221.  
  222. def main():
  223.     """Main function for the stitch command line tool.
  224.     """
  225.     
  226.     usage = "usage: %prog [options] basenames"
  227.     parser = optparse.OptionParser(usage)
  228.     parser.add_option("-r", "--remove-tiles",
  229.                       action="store_true", default=False,
  230.                       help="Remove the tiles after stitching")
  231.  
  232.     opts, args = parser.parse_args()
  233.  
  234.     if len(args)==0:
  235.         parser.print_help()
  236.         sys.exit(0)
  237.         
  238.     for filename in args:
  239.         stitch(filename, removetiles=opts.remove_tiles, infostream=sys.stdout)
  240.  
  241. ######################################################################
  242.         
  243. if __name__=="__main__":
  244.     try:
  245.         main()
  246.     except ValueError, e:
  247.         print e
  248.