Commit eaea49cb authored by Andreas Mueller's avatar Andreas Mueller
Browse files

working coloring

parent 1b54dfc0
examples/alice.png

145 KB | W: | H:

examples/alice.png

159 KB | W: | H:

examples/alice.png
examples/alice.png
examples/alice.png
examples/alice.png
  • 2-up
  • Swipe
  • Onion skin
import numpy as np
from PIL import ImageFont
class ImageColorGenerator(object):
# returns the average color of the image in that region
def __init__(self, image):
if image.ndim not in [2, 3]:
raise ValueError("ImageColorGenerator needs an image with ndim 2 or"
" 3, got %d" % image.ndim)
if image.ndim == 3 and image.shape[2] not in [3, 4]:
raise ValueError("A color image needs to have 3 or 4 channels, got %d"
% image.shape[2])
self.image = image
def __call__(self, word, font_size, font_path, position, orientation, **kwargs):
# get the font to get the box size
font = ImageFont.truetype(font_path, font_size)
transposed_font = ImageFont.TransposedFont(font,
orientation=orientation)
# get size of resulting text
box_size = transposed_font.getsize(word[0])
x = position[0]
y = position[1]
# cut out patch under word box
patch = self.image[x:x + box_size[0], y:y + box_size[1]]
if patch.ndim == 3:
# drop alpha channel if any
patch = patch[:, :, :3]
if patch.ndim == 2:
raise NotImplementedError("Gray-scale images TODO")
color = np.mean(patch.reshape(-1, 3), axis=0)
return "rgb(%d, %d, %d)" % tuple(color)
# Author: Andreas Christian Mueller <amueller@ais.uni-bonn.de> # Author: Andreas Christian Mueller <t3kcit@gmail.com>
#
# (c) 2012 # (c) 2012
# Modified by: Paul Nechifor <paul@nechifor.net> # Modified by: Paul Nechifor <paul@nechifor.net>
# #
# License: MIT # License: MIT
import warnings
from random import Random from random import Random
import os import os
import re import re
...@@ -23,7 +25,8 @@ STOPWORDS = set([x.strip() for x in open(os.path.join(os.path.dirname(__file__), ...@@ -23,7 +25,8 @@ STOPWORDS = set([x.strip() for x in open(os.path.join(os.path.dirname(__file__),
'stopwords')).read().split('\n')]) 'stopwords')).read().split('\n')])
def random_color_func(word, font_size, position, orientation, random_state=None): def random_color_func(word=None, font_size=None, position=None,
orientation=None, font_path=None, random_state=None):
"""Random hue color generation. """Random hue color generation.
Default coloring method. This just picks a random hue with value 80% and Default coloring method. This just picks a random hue with value 80% and
...@@ -65,10 +68,11 @@ class WordCloud(object): ...@@ -65,10 +68,11 @@ class WordCloud(object):
The ratio of times to try horizontal fitting as opposed to vertical. The ratio of times to try horizontal fitting as opposed to vertical.
mask : nd-array or None (default=None) mask : nd-array or None (default=None)
If not None, gives a binary mask on where to draw words. All zero If not None, gives a binary mask on where to draw words. If mask is not
entries will be considered "free" to draw on, while all non-zero None, width and height will be ignored and the shape of mask will be
entries will be deemed occupied. If mask is not None, width and height will be used instead. All white (#FF or #FFFFFF) entries will be considerd
ignored and the shape of mask will be used instead. "masked out" while other entries will be free to draw on. [This
changed in the most recent version!]
scale : float (default=1) scale : float (default=1)
Scaling between computation and drawing. For large word-cloud images, Scaling between computation and drawing. For large word-cloud images,
...@@ -173,22 +177,21 @@ class WordCloud(object): ...@@ -173,22 +177,21 @@ class WordCloud(object):
% len(frequencies)) % len(frequencies))
if self.mask is not None: if self.mask is not None:
width = self.mask.shape[1]
height = self.mask.shape[0]
mask = self.mask mask = self.mask
width = mask.shape[1]
height = mask.shape[0]
if mask.dtype.kind == 'f': if mask.dtype.kind == 'f':
# threshold float images warnings.warn("mask image should be unsigned byte between 0 and"
mask = mask >= .5 "255. Got a float array")
elif mask.dtype.kind == 'i': if mask.ndim == 2:
# threshold ubyte images boolean_mask = mask == 255
mask = mask >= 128 elif mask.ndim == 3:
if self.mask.ndim == 3: # "OR" the color channels
# "OR" all channels boolean_mask = np.sum(mask[:, :, :3] == 255, axis=-1)
mask = mask.sum(axis=-1) > 0 else:
if mask.ndim != 2:
raise ValueError("Got mask of invalid shape: %s" % str(mask.shape)) raise ValueError("Got mask of invalid shape: %s" % str(mask.shape))
# the order of the cumsum's is important for speed ?! # the order of the cumsum's is important for speed ?!
integral = np.cumsum(np.cumsum(mask, axis=1), axis=0).astype(np.uint32) integral = np.cumsum(np.cumsum(boolean_mask * 255, axis=1), axis=0).astype(np.uint32)
else: else:
height, width = self.height, self.width height, width = self.height, self.width
integral = np.zeros((height, width), dtype=np.uint32) integral = np.zeros((height, width), dtype=np.uint32)
...@@ -237,13 +240,16 @@ class WordCloud(object): ...@@ -237,13 +240,16 @@ class WordCloud(object):
positions.append((x, y)) positions.append((x, y))
orientations.append(orientation) orientations.append(orientation)
font_sizes.append(font_size) font_sizes.append(font_size)
colors.append(self.color_func(word, font_size, (x, y), orientation, colors.append(self.color_func(word, font_size=font_size,
random_state=random_state)) position=(x, y),
orientation=orientation,
random_state=random_state,
font_path=self.font_path))
# recompute integral image # recompute integral image
if self.mask is None: if self.mask is None:
img_array = np.asarray(img_grey) img_array = np.asarray(img_grey)
else: else:
img_array = np.asarray(img_grey) + mask img_array = np.asarray(img_grey) + boolean_mask
# recompute bottom right # recompute bottom right
# the order of the cumsum's is important for speed ?! # the order of the cumsum's is important for speed ?!
partial_integral = np.cumsum(np.cumsum(img_array[x:, y:], axis=1), partial_integral = np.cumsum(np.cumsum(img_array[x:, y:], axis=1),
...@@ -406,7 +412,9 @@ class WordCloud(object): ...@@ -406,7 +412,9 @@ class WordCloud(object):
if color_func is None: if color_func is None:
color_func = self.color_func color_func = self.color_func
self.layout_ = [(word, font_size, position, orientation, self.layout_ = [(word, font_size, position, orientation,
color_func(word, font_size, position, orientation, random_state)) color_func(word=word, font_size=font_size,
position=position, orientation=orientation,
random_state=random_state, font_path=self.font_path))
for word, font_size, position, orientation, _ in self.layout_] for word, font_size, position, orientation, _ in self.layout_]
return self return self
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment