HOME/Articles/

pil example filters (snippet)

Article Outline

Python pil example 'filters'

Functions in program:

  • def process(image_name, transformation_class_map, queue):
  • def transform_queue(image, queue, bijection):
  • def threshold(n):
  • def exp_map(n):
  • def log_map(n):
  • def quadratic_map(n):
  • def sine_map(n):
  • def prime_factor_dist_convolve(scalar_list):
  • def counter_to_coordinate(d):
  • def primes(n):
  • def mean(scalar_list):
  • def median(scalar_list):
  • def collatz_map(n, minimum = 0, maximum = 255):
  • def collatz(n):

Modules used in program:

  • import time, sys, random, math, functools, collections
  • import PIL.Image, numpy, scipy.misc, scipy.ndimage, scipy.spatial

python filters

Python pil example: filters

# much (some) of this code isn't mine
import PIL.Image, numpy, scipy.misc, scipy.ndimage, scipy.spatial
import time, sys, random, math, functools, collections

# image = PIL.Image.open("4.2.06.tiff") # sailboat on lake
# image = PIL.Image.open("lena.tiff") # lena
pixel_definition = ["r", "g", "b"] # definition of pixel

# neighborhood_kernel = numpy.array([[1, 1, 1],
#                                     [1, 1, 1],
#                                     [1, 1, 1]])
# 
neighborhood_kernel = numpy.ones((5,5))

self_kernel = numpy.array([[1]])

# some example transformations

def collatz(n):
    if n == 0: return 0
    i = 0
    while n != 1:
        i += 1
        if n % 2 == 0:
            n = n // 2
        else:
            n = 3 * n + 1
    return i

collatz_list = [min(collatz(n), 255) for n in range(256)]

def collatz_map(n, minimum = 0, maximum = 255):
    return collatz_list[int(n[0])]

def median(scalar_list):
    return sorted(scalar_list)[len(scalar_list) // 2]

def mean(scalar_list):
    return sum(scalar_list) // len(scalar_list)

def primes(n):
    primfac = []
    d = 2
    while d * d <= n:
        while (n % d) == 0:
            primfac.append(d)  # supposing you want multiple factors repeated
            n /= d
        d += 1
    if n > 1:
       primfac.append(n)
    return list(map(int, primfac))

def counter_to_coordinate(d):
    # d might look like e.g. n = 12, d = {2:2 , 3: 1}
    dimension = max(some_primes)
    coordinate = [0] * (dimension + 1)
    for index, (prime, times) in enumerate(zip(d, d.values())):
        coordinate[prime] = times
    return coordinate

prime_factors = [primes(n) for n in range(256)] # e.g. prime_factors[12] = [2, 2, 3]
some_primes = list(set.union(*list(map(set, prime_factors))))
coordinate = lambda n: counter_to_coordinate(dict(collections.Counter(prime_factors[n])))
cache = {i: numpy.array(coordinate(i)) for i in range(len(prime_factors))}
nums = numpy.array(list(cache.values()))
grid = scipy.spatial.distance.cdist(nums, nums, 'cityblock')

def prime_factor_dist_convolve(scalar_list):
    middle = scalar_list[len(scalar_list)//2]
    prime_space_distances = numpy.array([grid[middle][x] for x in scalar_list])
    norm = numpy.linalg.norm(prime_space_distances)
    if norm == 0: return middle
    normalized_distances = prime_space_distances / norm
    products = numpy.array([normalized_distances[i] * scalar_list[i] for i in range(len(scalar_list))])
    return sum(products)

sine_cache = [255 * math.sin((n) * (math.pi / 2) / 255) for n in range(256)]
def sine_map(n):
    return sine_cache[int(n[0])]

quadratic_cache = [255 * (n / 255) ** 2 for n in range(256)]
def quadratic_map(n):
    return quadratic_cache[int(n[0])]

log_cache = [0] + [math.log(n) for n in range(1, 256)]
def log_map(n):
    return log_cache[int(n[0])]

exp_cache = [(255/148) * math.e ** n for n in range(256)]
def exp_map(n):
    return exp_cache[int(n[0])]

def threshold(n):
    return 0 if 128 < n else 255

pixel_transformations = functools.partial(scipy.ndimage.generic_filter, footprint(= self_kernel))
window_transformations = functools.partial(scipy.ndimage.generic_filter, footprint(= neighborhood_kernel))

def transform_queue(image, queue, bijection):
    pixel_system = numpy.array(image) # each pixel_system[y, x] = 3vector <R, G, B>
    RGBimages = [pixel_system[:,:,index] for index, color in enumerate(pixel_definition)] # split into RGB images

    # guarantee every transformation has a partial type class
    transformed_images = [] # will contain transformed RGB images
    for image in RGBimages:
        for transformation in queue:
            transformation_partial = list(filter(lambda key: transformation in bijection[key], bijection.keys()))[0] # each transformation maps to one partial; filter away irrelevant partials
            image = transformation_partial(image, transformation)
        transformed_images.append(image)
    return numpy.uint8(numpy.dstack(transformed_images)) # make one RGB image from the R, G, B planes

def process(image_name, transformation_class_map, queue):
    image = PIL.Image.open(image_name)
    result = transform_queue(image, queue, transformation_class_map)
    image = PIL.Image.fromarray(result)
    image.show()
    image.save(''.join(list(map(lambda f: f.__name__ + '_', queue))) + str(time.time()) + '_' + image_name)

if __name__ == '__main__':
    transformation_class_map = {pixel_transformations: set([collatz_map,
                                                                 sine_map,
                                                                 quadratic_map,
                                                                 log_map, 
                                                                 exp_map,
                                                                 threshold]),

                                window_transformations: set([median,
                                                               mean,
                                                             random.choice,
                                                             prime_factor_dist_convolve])}
    queue = [sine_map] * 3 + [prime_factor_dist_convolve]
    process(sys.argv[1], transformation_class_map, queue)