Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python3
- from collections import namedtuple
- from math import sqrt
- import colorsys
- import random
- try:
- import Image
- except ImportError:
- from PIL import Image
- import sys
- import re
- import os.path
- import argparse
- import json
- Pr, Pg, Pb = .299, .587, .114
- Point = namedtuple('Point', ('coords', 'n', 'ct'))
- Cluster = namedtuple('Cluster', ('points', 'center', 'n'))
- def get_points(img):
- points = []
- w, h = img.size
- for count, color in img.getcolors(w * h):
- points.append(Point(color, 3, count))
- return points
- rtoh = lambda rgb: '#%s' % ''.join(('%02x' % p for p in rgb))
- rgb_to_hsp = lambda rgb: sqrt(
- sum([rgb[0] ** 2 * Pr, rgb[1] ** 2 * Pg, rgb[2] ** 2 * Pb]))
- def colorize(filename, n=16):
- img = Image.open(filename)
- img.thumbnail((200, 200))
- w, h = img.size
- points = get_points(img)
- clusters = kmeans(points, n, 1)
- rgbs = [list(map(int, c.center.coords)) for c in clusters]
- return list(map(rtoh, sorted(rgbs, key=rgb_to_hsp)))
- def euclidean(p1, p2):
- return sqrt(sum([
- (p1.coords[i] - p2.coords[i]) ** 2 for i in range(p1.n)
- ]))
- def calculate_center(points, n):
- vals = [0.0 for i in range(n)]
- plen = 0
- for p in points:
- plen += p.ct
- for i in range(n):
- vals[i] += (p.coords[i] * p.ct)
- return Point([(v / plen) for v in vals], n, 1)
- def kmeans(points, k, min_diff):
- clusters = [Cluster([p], p, p.n) for p in random.sample(points, k)]
- while True:
- plists = [[] for i in range(k)]
- for p in points:
- smallest_distance = float('Inf')
- for i in range(k):
- distance = euclidean(p, clusters[i].center)
- if distance < smallest_distance:
- smallest_distance = distance
- idx = i
- plists[idx].append(p)
- diff = 0
- for i in range(k):
- old = clusters[i]
- center = calculate_center(plists[i], old.n)
- new = Cluster(plists[i], center, old.n)
- clusters[i] = new
- diff = max(diff, euclidean(old.center, new.center))
- if diff < min_diff:
- break
- return clusters
- def make_file(colors, patterns):
- def _proccess_color_pattern(pattern, colors):
- return ''.join(pattern.format(i, color) for i, color in enumerate(colors))
- def _add(pattern, colors):
- return ''.join(to_add['pattern'].format(colors[to_add['pos']]) for to_add in pattern['add'])
- fake_patterns = list(
- (dict(expression='', out='', add=[], pattern='Color{0}: {1}\n'),))
- try:
- with open(patterns, 'r') as input:
- try:
- patterns = json.loads(input.read())
- except ValueError:
- sys.stdout.write('Cant parse JSON file')
- patterns = fake_patterns
- except (FileNotFoundError, TypeError):
- sys.stdout.write('JSON file not found')
- patterns = fake_patterns
- for pattern in patterns:
- result = (_proccess_color_pattern(
- pattern['pattern'], colors) or '') + (_add(pattern, colors) or '')
- out = os.path.expanduser(pattern['out'])
- if out:
- with open(out, 'r') as input:
- data = input.read()
- with open(out, 'w') as output:
- p = re.compile(pattern['expression'], re.S)
- if p.search(data):
- output.write(p.sub(r'\1{0}\2'.format(result), data))
- else:
- sys.stdout.write(result)
- def main():
- parser = argparse.ArgumentParser(
- description='Proccess dominant colors on input image and print it.')
- parser.add_argument('-i', '--image', type=str, help='image')
- parser.add_argument(
- '-c', '--count', type=int, help='count of out colors', default=16)
- parser.add_argument(
- '-p', '--patterns', type=str, help='patterns of out colors', default=None)
- args = parser.parse_args()
- colors = colorize(args.image, args.count)
- make_file(colors, args.patterns)
- if __name__ == '__main__':
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement