Simon Volpert minipos / master tridenticon.py
master

Tree @master (Download .tar.gz)

tridenticon.py @masterraw · history · blame

#!/usr/bin/env python3
# tridenticon - A visual hash generator
# Author: Simon Volpert <simon@simonvolpert.com>
# Project page: https://simonvolpert.com/tridenticon/
# This program is free software, released under the Apache License, Version 2.0. See the LICENSE file for more information
# Consult the README file for usage instructions and other helpful hints
# Running with --test requires ImageMagick

import hashlib
import colorsys
import PIL.Image
import os
import sys
import subprocess

def generate(data, scale=1):
	'''Generate an identicon from the passed data, scaled to the given value'''
	image = PIL.Image.new('RGB', (7, 7), '#ffffff')
	# Prepare data
	if type(data) is str:
		data = bytes(data, 'UTF-8')
	elif type(data) is not bytes:
		data = bytes(data)
	# Calculate a checksum
	digest = hashlib.md5(data).hexdigest()
	c = 0
	iterator = iter(range(len(digest)))
	# Get the next color
	def get_color():
		try:
			color = digest[iterator.__next__()] + digest[iterator.__next__()]
		except StopIteration:
			return
		color = int(color, 16)
		color = tuple(int(i * 255) for i in colorsys.hsv_to_rgb(color / 255, 0.5, 0.75))
		return color

	color = get_color()
	erase = False
	for i in iterator:
		# Calculate coordinates to paint
		p = int(digest[i], 16)
		y, x = divmod(p, 3)
		# Treat out-of-bounds as an erase command
		if y == 5:
			erase = True
			continue
		if erase:
			image.putpixel((x + 1, y + 1), (255, 255, 255))
			image.putpixel((x * -1 + 5, y + 1), (255, 255, 255))
			erase = False
		# Paint the coordinates symmetrically
		elif image.getpixel((x + 1, y + 1)) == (255, 255, 255):
			image.putpixel((x + 1, y + 1), color)
			image.putpixel((x * -1 + 5, y + 1), color)
			# Switch colors for a 7/4/3/2 distribution
			c += 1
			if c == 7 or c == 11:
				color = get_color()
			elif c == 14:
				break
	# Output image
	image = image.resize((7 * scale, 7 * scale))
	return image # Use .show() to preview

if __name__ == '__main__':
	usage = '''Usage:	tridenticon DATA [SCALE]
	tridenticon --test'''
	try:
		data = sys.argv[1]
	except (IndexError):
		print(usage)
		sys.exit(1)
	try:
		scale = int(sys.argv[2])
	except IndexError:
		scale = 8
	except ValueError:
		print(usage)
	if data == '--test' and scale == 8:
		t = 0
		tmp = []
		for i in range(32, 127): # All printable characters on the keyboard
			image = generate(chr(i), 8)
			image.save('tridenticon.tmp%s.png' % t)
			tmp.append('tridenticon.tmp%s.png' % t)
			t += 1
		try:
			subprocess.call(['montage', '-geometry', '+0+0'] + tmp + ['tridenticon.png'])
			print('tridenticon.png written')
		finally:
			for i in tmp:
				os.unlink(i)
	else:
		image = generate(data, scale)
		image.save('tridenticon.png')