Created
May 27, 2019 11:17
-
-
Save Josef-Friedrich/51350435faee48c0f9d9ba0cc4efe778 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# http://kneasle.weebly.com/hama-beads.html | |
from PIL import Image | |
import random, math | |
import tkinter as tk | |
def diff( num1 , num2 ): | |
return max( num1 , num2 ) - min( num1 , num2 ) | |
#create a class to store the HAMA bead colors | |
class Color: | |
def __init__( self , r , g , b , name = 'NoName' ): | |
self.r = r | |
self.g = g | |
self.b = b | |
self.name = name | |
self.index = 0 | |
#returns the difference between this color, and | |
#another one | |
def dist( self , r1 , g1 , b1 ): | |
r = diff( self.r , r1 ) | |
g = diff( self.g , g1 ) | |
b = diff( self.b , b1 ) | |
return ( r + g + b ) / 3 | |
#returns a tuple representing the color | |
def get( self ): | |
return ( self.r , self.g , self.b ) | |
#override the string so I can print colors easily | |
def __str__( self ): | |
return '<COL:(' + str( self.r ) + \ | |
',' + str( self.g ) + \ | |
',' + str( self.b ) + \ | |
') ' + self.name + '>' | |
# pick the best colored bead for a given pixel colour | |
# (r1 and r2 the randomness to add a bit of variety/texturing | |
# to the image) | |
# r1: total amount of randomness | |
# r2: bias towards the "correct" color | |
def pick_best( cols , r , g , b , r1 = 0 , r2 = 2 ): | |
# create a function to find the appropriateness of all the colors | |
func = lambda p: p.dist( r , g , b ) | |
#sort the colors (so the first one will be the best) | |
sort = sorted( cols , key = func ) | |
#return the one of the first colours (will be a good match) | |
return sort[ int( round( r1 * random.random() ** r2 ) ) ] | |
def mousemove( evt ): | |
# calculate which dot the mouse is hovering over | |
x = math.floor( evt.x / grid_size ) | |
y = math.floor( evt.y / grid_size ) | |
# try to output the colour (sometimes the program crashes doing this, | |
# so I added a work-around | |
try: | |
out.config( text = '(' + str( x + 1 ) + ',' + str( y + 1 ) + '): ' \ | |
+ str( cols[ x ][ y ].name ) + ' #' + \ | |
str( cols[ x ][ y ].index ) ) | |
except IndexError: | |
print( 'error' , x , y ) | |
colors = [ | |
Color( 236 , 237 , 237 , 'H01-white' ) , | |
Color( 240 , 232 , 185 , 'H02-cream' ) , | |
Color( 240 , 185 , 1 , 'H03-yellow' ) , | |
Color( 230 , 79 , 39 , 'H04-orange' ) , | |
Color( 182 , 49 , 54 , 'H05-red' ) , | |
Color( 225 , 136 , 159 , 'H06-pink' ) , | |
Color( 105 , 74 , 130 , 'H07-purple' ) , | |
Color( 44 , 70 , 144 , 'H08-dark blue' ) , | |
Color( 48 , 92 , 176 , 'H09-light blue' ) , | |
Color( 37 , 104 , 71 , 'H10-mid green' ) , | |
Color( 73 , 174 , 137 , 'H11-light green' ) , | |
Color( 83 , 65 , 55 , 'H12-brown' ) , | |
Color( 131 , 136 , 138 , 'H17-grey' ) , | |
Color( 46 , 47 , 49 , 'H18-black' ) , | |
Color( 127 , 51 , 42 , 'H20-reddish brown' ) , | |
Color( 165 , 105 , 63 , 'H21-light brown' ) , | |
Color( 222 , 155 , 144 , 'H26-flesh' ) , | |
Color( 222 , 180 , 139 , 'H27-beige' ) , | |
Color( 54 , 63 , 56 , 'H28-dark green' ) , | |
Color( 185 , 57 , 94 , 'H29-claret' ) , | |
Color( 103 , 151 , 174 , 'H31-turquoise' ) , | |
Color( 167 , 98 , 36 , 'N05-caramel' ) | |
] | |
# number and darken the colours | |
darkening = 0 | |
for ind , i in enumerate( colors ): | |
# index the colours for quick reference | |
i.index = ind + 1 | |
# darken all of the colours (clamping at zero, | |
# as negative colours don't exist) | |
i.r = max( 0 , i.r - darkening ) | |
i.g = max( 0 , i.g - darkening ) | |
i.b = max( 0 , i.b - darkening ) | |
if __name__ == '__main__': | |
# open file | |
file = 'home.png' | |
img = Image.open( file ) | |
# load the pixel map for the image | |
pixels = img.load() | |
# create a matrix of all the colours | |
cols = [ | |
[ None for i in range( img.size[ 1 ] ) ] | |
for i in range( img.size[ 0 ] ) | |
] | |
for i in range(img.size[0]): # for every pixel: | |
for j in range(img.size[1]): | |
# get the colour from the matrix | |
r , g , b = pixels[ i , j ] | |
# set up the random number generator so that the numbers are always | |
# the same. This means that if you refresh the image, it stays | |
# identical | |
random.seed( str( i ) + '-' + str( j ) ) | |
# find the best colour, add it to the matrix, and change the pixel | |
col = pick_best( colors , r , g , b , 1 , 10 ) | |
cols[ i ][ j ] = col | |
pixels[i,j] = ( col.r , col.g , col.b ) # set the colour accordingly | |
# save the image as an export copy | |
img.save( 'E_' + file , 'PNG' ) | |
#create global variable for the grid size | |
global grid_size | |
grid_size = 20 | |
# create tkinter elements | |
master = tk.Tk() | |
master.title( 'BUGELPERLEN: ' + file ) | |
# create a canvas big enough to hold the entire image (zoomed in) | |
canvas = tk.Canvas( | |
master , | |
width = img.size[ 0 ] * grid_size + 1, | |
height = img.size[ 1 ] * grid_size + 1, | |
highlightthickness = 0 | |
) | |
# create an output label | |
out = tk.Label( | |
master , | |
font = ( 'Courier' , 30 ) | |
) | |
#create the ovals | |
for i in range( img.size[ 0 ] ): | |
for j in range( img.size[ 1 ] ): | |
# 'b' stores how much overlap is required between the ovals | |
b = 3 | |
canvas.create_oval( | |
i * grid_size - b , j * grid_size - b , | |
( i + 1 ) * grid_size + b , ( j + 1 ) * grid_size + b , | |
fill = "#%02x%02x%02x" % cols[ i ][ j ].get() , | |
width = 0 | |
) | |
# bind an on-mouse-move command to update the output box | |
canvas.bind( '<Motion>' , mousemove ) | |
# start the app | |
canvas.pack() | |
out.pack() | |
master.mainloop() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment