Welcome, guest | Sign In | My Account | Store | Cart

aebovl is a script that overlays images of the same scene taken at different exposures. This brightens under-exposed areas and darkens over-exposed areas of the image. This generates a kind of poor man's HDR image.

Python, 131 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
"""aebovl -- overlay bracketed exposures to create a composite image

Usage:
  aebovl -l<val> [-d<val> -a<val> -m -h] <normal> <under> <over>
Where:
  -l <val>  light limit (0-255). Pixels lighter than this value are considered
     to be too light. Default: 127
  -d <val>  dark limit (0-255). Pixels darker than this values are considered
     to be too dark. Optional. If omitted, the dark limit is set to 
     255 - light limit.
  -a <val>  alpha. Adjusts level of blending between images. Optional, range
     1 - 255, default is 1. 1 means use all of the light or dark image, 255
     means use all of the normal image. Values in between give a blend.
  -f <val>  apply a filter. Optional. Default: no filter. Filters are:
     BLUR, CONTOUR, DETAIL, EDGE_ENHANCE, EDGE_ENHANCE_MORE, EMBOSS, 
     FIND_EDGES, SMOOTH, SMOOTH_MORE, and SHARPEN.
  -b Blur masks. Blur the light and dark masks to soften the overlays and help
     smooth transitions.
  -m Save mask image. A composite of the masks is saved for reference as 
     overlay-cm.jpg. Optional. The composite mask shows light areas
     as white, normal areas as gray and dark areas as black.
  -h Help 
  <normal>  Normally exposed image. Required.
  <under>   Under-exposed (dark) image. Required.         
  <over>    Over-exposed (light) image. Required.
Output:
  Results are saved as overlay*.JPG in the current directory
Notes:
  Generally it is easiest to focus on getting the largest uniform section of the
  image right (e.g. the sky), then fine tuning from there. Try using -l120 -m
  initially, and reduce -l by 10 until the sky is uniformaly darkened. Then set
  -d to 255-l, and continue to adjust -l independantly of -d until you are happy
  with the rest of the image. Try high and low values of -l. Finally add -b and
  see if this improves the effect.
Example work flow:
  Start: 
    aebovl -l120 -m normal.jpg under.jpg over.jpg
  Adjust in increments of 10 until sky looks good, perhaps something like:
    aebovl -l70 -m normal.jpg under.jpg over.jpg
  Fix -d at 255-70:
    aebovl -l80 -d185 -m normal.jpg under.jpg over.jpg
  Continue to adjust -l until main subject is how you want it:
    aebovl -l35 -d185 -m normal.jpg under.jpg over.jpg
  Now try -b, and see if it helps:
    aebovl -l35 -d185 -b normal.jpg under.jpg over.jpg 
"""  

import sys
import Image, ImageFilter
import getopt
from getopt import GetoptError

VALID_FILTERS = ["BLUR", "CONTOUR", "DETAIL", "EDGE_ENHANCE", 
    "EDGE_ENHANCE_MORE", "EMBOSS", "FIND_EDGES", "SMOOTH", 
    "SMOOTH_MORE", "SHARPEN"]

def abort(code, msg):
    sys.stderr.write(msg + '\n')
    sys.stderr.write('Run expovl -h for help\n') 
    sys.exit(code)

def main():
    # Get command line parameters
    getoptstring = 'bhml:d:a:f:'
    try:
        opts, args = getopt.getopt(sys.argv[1:], getoptstring)
    except GetoptError:
        abort(2, "Invalid or missing command line option")
    llimit = 127
    dlimit = None
    alpha = 1
    savemasks = False
    filter = None
    blurmasks = False
    help = False
    outfilename = "overlay.jpg"
    for opt, val in opts:
        if opt == "-l": llimit = int(val)    
        if opt == "-d": dlimit = int(val)
        if opt == "-m": savemasks = True    
        if opt == "-h": help = True
        if opt == "-a": alpha = int(val)
        if opt == "-b": blurmasks = True
        if opt == "-f": filter = val.upper()
    if help:
        print __doc__
        sys.exit(0)
    if dlimit is None: dlimit = 255 - llimit
    if len(args) != 3: 
        abort(1, "Three input image files required.")
    if filter is not None and filter not in VALID_FILTERS:
        abort(3, "Invalid filter specified")
    midimgfile, darkimgfile, lightimgfile = args
     
    print "Loading images ..."
    midimg = Image.open(midimgfile).convert("RGB")
    darkimg = Image.open(darkimgfile).convert("RGB")
    lightimg = Image.open(lightimgfile).convert("RGB")
    gsimg = midimg.convert("L")
    
    print "Processing images ..."
    lightmask = gsimg.point(lambda i: ((i < llimit) * alpha) or 255)
    darkmask = gsimg.point(lambda i: ((i > dlimit) * alpha) or 255)
    if blurmasks:
	    lightmask = lightmask.filter(ImageFilter.BLUR)
	    darkmask = darkmask.filter(ImageFilter.BLUR)
    resimg = Image.composite(midimg, lightimg, lightmask)
    resimg = Image.composite(resimg, darkimg, darkmask)
    
    if filter is not None:
        resimg = resimg.filter(getattr(ImageFilter, filter))
    
    print "Saving image file(s) ..."
    resimg.save("overlay.JPG")
    if savemasks:
    	# Recreate the masks without using the alpha factor, so the composite
    	# mask is composed of white, mid-gray and black, not shades in between
        lightmask = gsimg.point(lambda i: (i < llimit) or 255)
        darkmask = gsimg.point(lambda i: (i > dlimit) or 255)
        if blurmasks:
            lightmask = lightmask.filter(ImageFilter.BLUR)
            darkmask = darkmask.filter(ImageFilter.BLUR)
        gimg = Image.new("L", midimg.size, 128)
        wimg = Image.new("L", midimg.size, 0)
        bimg = Image.new("L", midimg.size, 255)
        compmask = Image.composite(gimg, wimg, lightmask)
        compmask = Image.composite(compmask, bimg, darkmask)
        compmask.save("overlay-cm.JPG")
    print "Results saved as overlay*.JPG"
    
main()

Many digital cameras, even low-end consumer models, support automatic exposure bracketing (AEB.) AEB is where the camera takes three or more shots at different exposures, normal, under-exposed and over-exposed. aebovl.py can be used to overlay these images to get a composite effect. This will darken over-exposed areas of and image, such as the sky, or brighten under-exposed areas in shadow.

aebovl is a kind of poor man's HDR tool. Google for "HDR images". It is not nearly as sophisticated as true HDR software, but it can give interesting results.