import struct import scipy import scipy.misc import scipy.cluster import codecs # from IPython.display import Markdown, display, HTML import colorsys import math import numpy as np import sklearn.cluster from PIL import Image NUM_CLUSTERS = 64 def getColourAsHex(colour): return '#' + ''.join(format(c, '02x') for c in colour.astype(int)) def getColoursForImageByKMeansClusters(image): """ Adapted on answers by Peter Hansen (http://stackoverflow.com/a/3244061) & Johan Mickos (http://stackoverflow.com/a/34140327) """ im = image.copy().resize((150, 150)) # optional, to reduce time ar = scipy.misc.fromimage(im) shape = ar.shape ar = ar.reshape(scipy.product(shape[:2]), shape[2]) # print( 'finding clusters') codes, dist = scipy.cluster.vq.kmeans(ar.astype(float), NUM_CLUSTERS) # print ('cluster centres:\n', codes) vecs, dist = scipy.cluster.vq.vq(ar, codes) # assign codes counts, bins = scipy.histogram(vecs, len(codes)) # count occurrences # When only looking for single color: # index_max = scipy.argmax(counts) # find most frequent # peak = codes[index_max] # colour = ''.join(chr(c) for c in peak).encode('hex') # print( 'most frequent is %s (#%s)' % (peak, colour)) percentages = 100 * counts / sum(counts) # print("Percentages", percentages) # colours = [ in codes] # print(colours) return list(zip(codes, percentages)) def getColoursForImageByMeanShiftClusters(image, saveImgName = False): """ Adapted on answers by Peter Hansen (http://stackoverflow.com/a/3244061) & Johan Mickos (http://stackoverflow.com/a/34140327) Now using MeanShift clustering if saveImgName set to the path/prefix_ for the layer images, save images of each cluster matched pixels """ imgSize = (170,170) # imgSize = (int(targetWidth), int(image.size[1] / (image.size[0]/targetWidth))) im = image.copy() # optional, to reduce time im.thumbnail(imgSize) imgSize = im.size print("\tRender image of", image.size,"for", imgSize) imgAr = scipy.misc.fromimage(im) shape = imgAr.shape ar = imgAr.reshape(scipy.product(shape[:2]), shape[2]) total = len(ar) # print( 'finding clusters') # codes, dist = scipy.cluster.vq.kmeans(ar.astype(float), NUM_CLUSTERS) bandwidth = sklearn.cluster.estimate_bandwidth(ar.astype(float), quantile=0.1, n_samples=500) ms = sklearn.cluster.MeanShift(bandwidth=bandwidth) ms.fit(ar.astype(float)) labels = ms.labels_ # labels per point cluster_centers = ms.cluster_centers_ # centers of found clusters labels_unique = np.unique(labels) n_clusters_ = len(labels_unique) print("\tClusters found:", n_clusters_) colours = [] for k in labels_unique: cluster_center = cluster_centers[k] # np.array with rgb mask = labels == k # True/False map of flattened array # m = np.ma.masked_where(labels != k, labels) count = len(ar[mask]) # nr of pixels for this label # pass on percentages: colours.append((cluster_center, 100 * count / total)) if saveImgName: # save image as RGBA with transparent pixels, except for there where label == k layer = np.array([np.append(ar[i],255) if l == k else [0,0,0,0] for i,l in enumerate(list(labels))], dtype=np.uint8) layerImgAr = layer.reshape((imgSize[1],imgSize[0],4)) # layers are stacked: vertical, horizontal, rgba layerImg = Image.fromarray(layerImgAr, mode="RGBA") layerFilename = saveImgName + '-%s.png' % k layerImg.save(layerFilename) print(layerFilename) # display(HTML("%s" % (getColourAsHex(cluster_center),getColourAsHex(cluster_center)))) return colours def getColoursForImageByPxAvg(image): im = image.copy().resize((8, 8)) pixels = np.concatenate(scipy.misc.fromimage(im)) # colours = ['#' + ''.join(format(c, '02x') for c in color.astype(int)) for color in pixels] percentages = np.zeros(len(pixels)) + (100 / len(pixels)) return list(zip(pixels, percentages)) def getColoursAsHTML(colours): return " ".join(['%s - (%s %%)' % (getColourAsHex(colour[0]), getColourAsHex(colour[0]), colour[1]) for colour in colours]); def loadColoursFromDbImages(images): return [i.colours for i in images if not i.colours is None] def getSvgFromDbImages(images, elId = ""): # sum concatenates all colour arrays # allColours = [] # for c in loadColoursFromDbImages(images): # allColours += c # box 160, because center or circle = 100 => +/- 50 => + r of colour circle (max: 10) => 160 # svg = '' % (elId, ) svg = '' # because of IE this is fully defined here radius = 100 for image in images: if image.colours is None: continue for i, colour in enumerate(image.colours): colourId = '%s-%s' % (image.id, i) rgb, percentage = colour rgbNorm = rgb/255 hsv = colorsys.rgb_to_hsv(rgbNorm[0], rgbNorm[1], rgbNorm[2]) # find position on circle radians = 2 * math.pi * hsv[0] x = math.cos(radians) y = math.sin(radians) # based on saturation, we move inwards/outwards # min = 0.5, max = 1.5 (dus + 0.5) pos = np.array([x,y]) * (0.5 + hsv[1]) * radius # Posibilitiy: determine position based on avg(saturation, value) => dark & grey inside, shiney and colourful outside # pos = np.array([x,y]) * (0.5 + (hsv[1]+hsv[2])/2) * radius r = max(1,-10/percentage+10) # as r, we converge to maximum radius 10, but don't want to get smaller radi then 1 c = '' % (pos[0], pos[1], r, getColourAsHex(rgb), colourId) svg += c svg += "" return svg