В питоне есть замечательная библиотека PIL, я ее немного нарастил для удобства.

#!/usr/bin/python
# -*- coding: utf-8 -*-


from PIL import  Image, ImageEnhance, ImageFont, ImageDraw, ImageFilter
import re
import math


def reduce_opacity(im, opacity):
    """Returns an image with reduced opacity."""
    assert opacity >= 0 and opacity <= 1
    if im.mode != 'RGBA':
        im = im.convert('RGBA')
    else:
        im = im.copy()
    #im = im.filter( ImageFilter.EMBOSS )
    #return im
    #im = im.copy()
    #print im.split()[3]




    alpha = im.split()[3]
    alpha = ImageEnhance.Brightness(alpha).enhance(opacity)
    im.putalpha(alpha)
    return im



##### установка на изображение логотипа 
def watermark(im, mark, position, opacity=1):
    """Adds a watermark to an image."""
    #print im.mode
    if opacity < 1:
        mark = reduce_opacity(mark, opacity)




    #if im.mode != 'RGBA':
    #    im = im.convert('RGBA')
    # create a transparent layer the size of the image and draw the
    # watermark in that layer.
    layer = Image.new('RGBA', im.size, (0,0,0,0))
    if position == 'tile':
        for y in range(0, im.size[1], mark.size[1]):
            for x in range(0, im.size[0], mark.size[0]):
                layer.paste(mark, (x, y))
    elif position == 'right_top':
        #print 1
        (im_w, im_h) = im.size
        (mark_w , mark_h) = mark.size
        new_w = (im_w - mark_w) - 2
        new_h = 2
        
        layer.paste(mark, (new_w, new_h ) )
        
    elif position == 'scale':
        
        ratio = min( float(im.size[0]) / mark.size[0], float(im.size[1]) / mark.size[1])
        w = int(mark.size[0] * ratio)
        h = int(mark.size[1] * ratio)
        mark = mark.resize((w, h))
        layer.paste(mark, ((im.size[0] - w) / 2, (im.size[1] - h) / 2))
    elif position == 'center':
        (iIW, iIH) = im.size
        (iLW , iLH) = mark.size
        iLWNew = int(float(iIW) / 2)
        
        iLHNew = int( float(iLH) / ( float(iLW) / float(iLWNew) ) )
        oNewLogo = mark.resize( (iLWNew, iLHNew ), Image.ANTIALIAS)
        



        
        iNewW = (iIW / 2) - (iLWNew / 2)
        iNewH = (iIH / 2) - (iLHNew / 2)
        layer.paste(oNewLogo, (iNewW, iNewH ) )
    else:
        layer.paste(mark, position)

    return Image.composite(layer, im, layer)

#### уменьшение и обрезание изображений
def scale_and_crop(im, requested_size, opts = {} ):
    x, y = [float(v) for v in im.size]
    xr, yr = [float(v) for v in requested_size]




    if 'crop' in opts or 'max' in opts:
        r = max(xr / x, yr / y)
    else:
        r = min(xr / x, yr / y)




    if r < 1.0 or (r > 1.0 and 'upscale' in opts):
        #print int(x * r), int(y * r)
        im = im.resize((int(x * r), int(y * r)), resample=Image.ANTIALIAS)




    crop = opts.get('crop') or 'crop' in opts
    #print crop
    if crop:
        # Difference (for x and y) between new image size and requested size.
        x, y = [float(v) for v in im.size]
        dx, dy = (x - min(x, xr)), (y - min(y, yr))
        if dx or dy:
            # Center cropping (default).
            ex, ey = dx / 2, dy / 2
            box = [ex, ey, x - ex, y - ey]
            # See if an edge cropping argument was provided.
            edge_crop = (isinstance(crop, basestring) and
                           re.match(r'(?:(-?)(\d+))?,(?:(-?)(\d+))?$', crop))
            if edge_crop and filter(None, edge_crop.groups()):
                x_right, x_crop, y_bottom, y_crop = edge_crop.groups()
                if x_crop:
                    offset = min(x * int(x_crop) / 100, dx)
                    if x_right:
                        box[0] = dx - offset
                        box[2] = x - offset
                    else:
                        box[0] = offset
                        box[2] = x - (dx - offset)
                if y_crop:
                    offset = min(y * int(y_crop) / 100, dy)
                    if y_bottom:
                        box[1] = dy - offset
                        box[3] = y - offset
                    else:
                        box[1] = offset
                        box[3] = y - (dy - offset)
            # See if the image should be "smart cropped".
            elif crop == 'smart':
                left = top = 0
                right, bottom = x, y
                while dx:
                    slice = min(dx, 10)
                    l_sl = im.crop((0, 0, slice, y))
                    r_sl = im.crop((x - slice, 0, x, y))
                    if image_entropy(l_sl) >= image_entropy(r_sl):
                        right -= slice
                    else:
                        left += slice
                    dx -= slice
                while dy:
                    slice = min(dy, 10)
                    t_sl = im.crop((0, 0, x, slice))
                    b_sl = im.crop((0, y - slice, x, y))
                    if image_entropy(t_sl) >= image_entropy(b_sl):
                        bottom -= slice
                    else:
                        top += slice
                    dy -= slice
                box = (left, top, right, bottom)
            # Finally, crop the image!
            im = im.crop([int(v) for v in box])
    return im

def image_entropy(im):
    """
    Calculate the entropy of an image. Used for "smart cropping".
    """
    hist = im.histogram()
    hist_size = float(sum(hist))
    hist = [h / hist_size for h in hist]
    return -sum([p * math.log(p, 2) for p in hist if p != 0])

#### открываем изображение
def open( sPath, bIsAlfa = False ):
    #print sPath
    oImage = Image.open( sPath )
    #print oImage.mode
    if bIsAlfa and oImage.mode == 'RGBA':
        return oImage
    if oImage.mode in ('L', 'RGB', 'RGBA', 'CMYK'):
        oImage = oImage.convert("RGB")
    return oImage
#### Сохраняем изображение 
def save( oImage, sFileName, **oOpt ):
    #print oOpt
    oImage.save( sFileName, oImage.format, **oOpt)