The Million Gallery Scripts

posted by Mc on 2005-06-09 22:25:13

There are two scripts: one with the essential tools, and one with the options for the particular gallery and the actual start of the script.

start [gallerymaker.py]:

    1 """Million Galleries
    2 (c) 2005 Marc Boeren
    3 """
    4 import gallerytools
    5 
    6 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
    7 
    8 src = {'dir': 'test', 'ext':['.jpg', ]}
    9 dsts = [{'dir': '128x96', 'ext':'.jpg', 'size':(128,96), 'bckcolor':(255, 255, 255)},
   10         {'dir': '640x480', 'ext':'.jpg', 'size':(640,480), 'bckcolor':(255, 255, 255)},
   11         ]
   12 mode = 'RGB'
   13 colorprofile = {'dir':'colorprofiles', 
   14                 'src':'EuroscaleCoated.icc', 
   15                 'dst':'sRGB.icm',
   16                 'outputmode':'RGB',
   17                 }
   18 
   19 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
   20 
   21 def reporter(src, dst):
   22     print src, '=>', dst
   23 
   24 gallerytools.convert_images(src, 
   25                             dsts, 
   26                             mode, 
   27                             colorprofile,
   28                             reporter
   29                             )
   30 

tools [gallerytools.py]:

    1 """Million Galleries Tools
    2 (c) 2005 Marc Boeren
    3 """
    4 import os
    5 import sys
    6 import Image
    7 import ImageFilter
    8 #import PyCMS
    9 
   10 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
   11 
   12 def list_files(directory, extlist = None, depth = -1):
   13     """Return a list of files (including relative pathnames) found in
   14     the given directory that have a match in the given extension-list.
   15     Subdirectories are included up to a given depth. The filenames do
   16     not include the base directory.
   17 
   18     Parameters:
   19         directory: base directory to start the listing in
   20         extlist: list of allowed extension, e.g. [".jpg", ".gif"]
   21             if not specified or empty: any extension is allowed
   22         depth: max. depth to descend subdirectories
   23             if not specified all subdirectories are descended into
   24 
   25     Example:
   26         filelist = tools.list_files("sourcedir", [".jpg", ".gif"])
   27     """
   28     dirlist = [os.path.normcase(f) for f in os.listdir(directory)]
   29     filepaths = [f for f in dirlist 
   30                  if os.path.isfile(os.path.join(directory, f)) 
   31                  and (not extlist or os.path.splitext(f)[1] in extlist)]
   32     for p in dirlist:
   33         if depth and os.path.isdir(os.path.join(directory, p)):
   34             filepaths+= [os.path.join(p, f) for f in 
   35                          list_files(os.path.join(directory, p), 
   36                                     extlist, depth-1)]
   37     return filepaths
   38 
   39 def convert_images(src, dsts, mode = None, colorprofile = None, 
   40                    callback = None):
   41     """Convert images from a folder plus all subfolders. The identical subfolder
   42 structure will be copied to the destination folder. The images will be sized 
   43 to a max-box (i.e. aspect ratio will be preserved, image is downscaled so it
   44 fits entirely in the box (note: NO upscale!)) and saved in the destination 
   45 format.
   46 
   47     Parameters:
   48         src: a dictionary consisting of
   49             'dir': source directory for conversion
   50             'ext': optional, a list of extensions of images to be converted.
   51                    default: all files
   52         dsts: a list of dictionaries consisting of
   53              'dir': destination directory for converted images
   54              'ext': the extension to save to
   55              'size': a tuple (maxwidth, maxheight)
   56              'bckcolor': the color to fill the unused space for the given size 
   57                          with
   58         mode: optional, convert to L, RGB or CMYK, or 'profiled' if you wish 
   59               to use the color profiling
   60               NOTE: 'profiled' doesn't work yet as I still need to compile 
   61                     PyCMS!!!!!!!!!!!!!!
   62         colorprofile: a dictionary containing profiling info if 
   63                       mode=='profiled', consisting of:
   64                       'dir': directory containing profiles
   65                       'src': source profile, 
   66                       'dst': destination profile,
   67                       'outputmode': destination output mode, see mode for 
   68                                     settings
   69 
   70     Example:
   71         tools.convert_images({'dir':"source", 'ext':[".jpg", ".gif"]}, 
   72                              [{'dir':"thumb", 'ext':".jpg", 'size':(100,100)}, ]
   73                             )
   74 
   75     Dependencies:
   76         list_files()
   77     """
   78     filelist = list_files(src['dir'], src.has_key('ext') and src['ext'] or None)
   79     for filepath in filelist:
   80         for dst in dsts:
   81             img = None
   82             if 1:
   83                 img = Image.open(os.path.join(src['dir'], filepath))
   84                 if img.mode == '1':
   85                     img = img.convert("L")
   86                 elif img.mode == 'L':
   87                     pass
   88                 else:
   89                     if mode and mode!='profiled':
   90                         img = img.convert(mode)
   91                 img.thumbnail(dst['size'], Image.ANTIALIAS)
   92                 img = img.filter(ImageFilter.SHARPEN)
   93                 # create a canvas with the destination size,
   94                 # fill it with bckcolor, and center the resized image
   95                 thumbsize = img.size
   96                 halfdx = (dst['size'][0] - thumbsize[0]) / 2
   97                 halfdy = (dst['size'][1] - thumbsize[1]) / 1
   98                 imgcopy = img.resize(dst['size'])
   99                 imgcopy.paste(dst['bckcolor'], None)
  100                 imgcopy.paste(img, (halfdx, halfdy, thumbsize[0]+halfdx, 
  101                                                     thumbsize[1]+halfdy))
  102                 img = imgcopy
  103 
  104                 if dst['ext'][-4:] == ".gif":
  105                     img = img.convert("P", None, None, Image.ADAPTIVE, 256)
  106                 try:
  107                     basedir, filename= os.path.split(filepath)
  108                     directory = os.path.join(dst['dir'], basedir)
  109                     if not os.path.exists(directory):
  110                         os.makedirs(directory)
  111                     img.save(os.path.join(directory, 
  112                         os.path.splitext(filename)[0]+dst['ext']))
  113                     if callback:
  114                         callback(os.path.join(src['dir'], filepath), 
  115                                  os.path.join(directory, 
  116                                      os.path.splitext(filename)[0]+dst['ext']),
  117                                  )
  118                 except:
  119                     print "Save error:", filepath
  120             #except:
  121             #    print "Open error:", filepath
  122             del img
  123 

Code

Examples and thoughts

I write a lot of code and sometimes I have something to say about it, or some code snippet that might be useful to others.