Share the Knowledge
RSS icon Home icon
  • Python script for auto-renaming your image files

    Posted on January 19th, 2012 webmaster No comments         

    I like to rename my pictures a certain way as soon as I import them to my computer.  I usually follow this format: description_datetaken_3digitnumbering.jpg

    I was using Metamorphose to do this for a while.  But one time I wanted to rename my pictures while I was in New Zealand and realized I didn’t have the application installed on my netbook.  I didn’t want to pay the $5 for the Internet access so I decided to just hack together a simple Python script to do it.  I haven’t gone back to Metamorphose ever since.  I found my script easier to use as I had it customized for my needs.

    I’ve tweaked it quite a bit to make it even more user friendly.  Here’s the script if you’re interested. Note that I’m not sure whether the EXIF tag attributes are the same for your camera, so you might need to modify it a little to work for you.

    import os
    import sys
    import shutil
    import datetime
    from PIL import Image
    from PIL.ExifTags import TAGS
    
    # Check first if the given path exists before continuing to the next step.
    while True:
        folderpath = raw_input('Folder path: ')
        print "\nChecking if folder path exists...\n"
        if os.path.exists(folderpath):
            print "Ok!\n"
            break;
        else:
            print "%s does not exist. Please enter a valid path.\n" % folderpath
    
    filename_pattern = raw_input('Filename pattern: ')
    
    def get_exif_data(filename):
        """Get embedded EXIF data from image file.
    
        Source: http://www.endlesslycurious.com/2011/05/11/extracting-image-
                exif-data-with-python/
        """
        ret = {}
        try:
            img = Image.open(filename)
            if hasattr( img, '_getexif' ):
                exifinfo = img._getexif()
                if exifinfo != None:
                    for tag, value in exifinfo.items():
                        decoded = TAGS.get(tag, tag)
                        ret[decoded] = value
        except IOError:
            print 'IOERROR ' + filename
        return ret
    
    def get_date_taken(filename):
        datestring = get_exif_data(current_file)['DateTimeOriginal']
        return datetime.datetime.strptime(datestring, '%Y:%m:%d %H:%M:%S')
    
    def get_filenames():
        os.chdir(folderpath)
        return os.listdir(os.getcwd())
    
    def get_numbering_format(digits, num):
        if digits == 1:
            numberfmt = '00%s' % num
        elif digits == 2:
            numberfmt = '0%s' % num
        else:
            numberfmt = '%s' % num
        return numberfmt
    
    def date_to_string(dateobj, format):
        return datetime.datetime.strftime(dateobj, format)
    
    def multi_replace(text, dictobj):
        """Replace characters in the text based on the given dictionary."""
        for k, v in dictobj.iteritems():
            text = text.replace(k, v)
        return text
    
    if __name__ == '__main__':
        filenames = get_filenames()
        for i in xrange(len(filenames)):
            num = i + 1
            digits = len(str(num))
            current_file = filenames[i]
    
            # Only rename files, ignore directories.
            if not os.path.isdir(current_file):
                # Key, value pairs of what to replace.
                dictobj = {
                    '<num>': get_numbering_format(digits, num),
                    '<datetaken>': date_to_string(get_date_taken(current_file),
                                                  '%Y%m%d')
                }
                new_filename = multi_replace(filename_pattern, dictobj)
                shutil.move(current_file, new_filename)
    

    Sample Usage

    Notes

    • PIL (Python Imaging Library) is required to retrieve the EXIF data.
    • I decided not to traverse subdirectories as that could be pretty dangerous if the wrong path is accidentally entered.
    • It’s set to pad the numbering to 3 digits.
    • It will rename any file that is not a directory (maybe add a check later for file type).
  • Errors when installing the Python ‘lxml’ library using pip on Ubuntu

    Posted on August 28th, 2011 webmaster No comments         

    I thought I’d post this here, this is the second time I ran into this issue on Ubuntu and forgot what I did the first time.

    If you get errors about things missing when you do ‘sudo pip install lxml’ on Ubuntu, you’ll probably need to install the following development packages (source):

    • sudo apt-get install python-dev
    • sudo apt-get install libxml2-dev
    • sudo apt-get install libxslt1-dev

    You may also want to do ‘sudo pip install lxml –upgrade’ if pip tells you that it’s already installed but you know it wasn’t properly installed.

  • How to configure the ‘logging’ module using dictionaries in Python 2.6

    Posted on August 21st, 2011 webmaster No comments         

    The logging.config module was updated in Python 2.7 and included a function called dictConfig() which takes a dictionary as an argument used to configure the logging module.

    I wanted to use this in my new project so I can keep all my configurations/settings in one Python file but we’re not ready to upgrade to Python 2.7 just yet.  The good news is you can just get the dictconfig module on its own and add it to your project.  I actually just took it from the Django 1.3 package and put it in a package within the project I’m working on.

    Here’s a sample configuration/usage for the ‘root’ logger:

    Configuration:

    LOG_SETTINGS = {
        'version': 1,
        'root': {
            'level': 'NOTSET',
            'handlers': ['console', 'file', 'smtp', 'mongodb'],
        },
        'handlers': {
            'console': {
                'class': 'logging.StreamHandler',
                'level': 'INFO',
                'formatter': 'detailed',
                'stream': 'ext://sys.stdout',
            },
            'file': {
                'class': 'logging.handlers.RotatingFileHandler',
                'level': 'INFO',
                'formatter': 'detailed',
                'filename': 'logs/MyProject.log',
                'mode': 'a',
                'maxBytes': 10485760,
                'backupCount': 5,
            },
            'smtp': {
                'class': 'logging.handlers.SMTPHandler',
                'level': 'ERROR',
                'formatter': 'email',
                'mailhost': 'localhost',
                'fromaddr': 'alerts@calazan.com',
                'toaddrs': ['admin@calazan.com', 'support@calazan.com'],
                'subject': '[My Project] Error encountered.',
            },
            'mongodb': {
                'class': 'log4mongo.handlers.MongoHandler',
                'level': 'DEBUG',
                'host': 'localhost',
                'port': 27017,
                'database_name': 'myproject',
                'collection': 'logs',
                'username': 'logger',
                'password': 'password',
            },
        },
        'formatters': {
            'detailed': {
                'format': '%(asctime)s %(module)-17s line:%(lineno)-4d ' \
                '%(levelname)-8s %(message)s',
            },
            'email': {
                'format': 'Timestamp: %(asctime)s\nModule: %(module)s\n' \
                'Line: %(lineno)d\nMessage: %(message)s',
            },
        },
    }
    
    

    Usage:

    
    import logging
    import dictconfig
    
    from settings import LOG_SETTINGS
    
    dictconfig.dictConfig(LOG_SETTINGS)
    
    logging.debug('This is a debug message!')
    logging.info('This is an info message!')
    logging.error('This is an error message!')