Pixelating black and white images
Simple optimisation

Instead of doing color = pixelate_mode(mode)(l) in the nested loop, you could compute modefunc = pixelate_mode(mode) once at the beginning of the function then call modefunc(l) as you need it.

Other optimisation

Instead of checking if x and y are in the range, you could just generate them so that they always are in the range :

l = []
for x in xrange(pixel_width * W, min(width, pixel_width * (W + 1))):
    for y in xrange(pixel_height * H, min(height, pixel_height * (H + 1))):
        l.append(img.getpixel((x, y)))
color = modefunc(l)
for x in xrange(pixel_width * W, min(width, pixel_width * (W + 1))):
    for y in xrange(pixel_height * H, min(height, pixel_height * (H + 1))):
        img.putpixel((x, y), color)


Also, for a more optimised version, you could compute limits only once :

minx, maxx = pixel_width * W, min(width, pixel_width * (W + 1))
miny, maxy = pixel_height * H, min(height, pixel_height * (H + 1))
l = []
for x in xrange(minx, maxx):
    for y in xrange(miny, maxy):
        l.append(img.getpixel((x, y)))
color = modefunc(l)
for x in xrange(minx, maxx):
    for y in xrange(miny, maxy):
        img.putpixel((x, y), color)


List expressions

You could use a list expression to define l. Even better you do not need l as a list at all and could just use a generator expression (Edit: Apparently this does not work for np.array so you have to build the string anyway, may it be with multiple appends or with list comprehension).

minx, maxx = pixel_width * W, min(width, pixel_width * (W + 1))
miny, maxy = pixel_height * H, min(height, pixel_height * (H + 1))
color = modefunc(img.getpixel(x, y)
                for x in xrange(minx, maxx)
                for y in xrange(miny, maxy))
for x in xrange(minx, maxx):
    for y in xrange(miny, maxy): 
        img.putpixel((x, y), color)


More optimisations

You could compute the x limits outside the nested loop.

Your while loops are actually for loop in disguise and you could make things more efficient by using for W in range(width/pixel_width) or you could use the step argument of range to use the values you'll actually need : for w in range(0, width, pixel_width): (in both cases, there might be an off-by-one issue but I'll let you check and fix).

Final (untested) version of the code

def pixelate(img, (pixel_width, pixel_height), mode):
    width, height = img.size
    modefunc = pixelate_mode(mode)
    for w in range(0, width, pixel_width):
        minx, maxx = w, min(width, w + pixel_width)
        for h in range(0, height, pixel_height):
            miny, maxy = h, min(height, h + pixel_height)
            color = modefunc(img.getpixel(x, y)
                    for x in xrange(minx, maxx)
                    for y in xrange(miny, maxy))
            for x in xrange(minx, maxx):
                for y in xrange(miny, maxy):
                img.putpixel((x, y), color)
    return img