Skip to content

Commit

Permalink
working coloring
Browse files Browse the repository at this point in the history
  • Loading branch information
amueller committed Apr 29, 2015
1 parent 1b54dfc commit eaea49c
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 22 deletions.
Binary file modified examples/alice.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/alice_color.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions wordcloud/color_from_image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
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)
52 changes: 30 additions & 22 deletions wordcloud/wordcloud.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Author: Andreas Christian Mueller <[email protected]>
# Author: Andreas Christian Mueller <[email protected]>
#
# (c) 2012
# Modified by: Paul Nechifor <[email protected]>
#
# License: MIT

import warnings
from random import Random
import os
import re
Expand All @@ -23,7 +25,8 @@
'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.
Default coloring method. This just picks a random hue with value 80% and
Expand Down Expand Up @@ -65,10 +68,11 @@ class WordCloud(object):
The ratio of times to try horizontal fitting as opposed to vertical.
mask : nd-array or None (default=None)
If not None, gives a binary mask on where to draw words. All zero
entries will be considered "free" to draw on, while all non-zero
entries will be deemed occupied. If mask is not None, width and height will be
ignored and the shape of mask will be used instead.
If not None, gives a binary mask on where to draw words. If mask is not
None, width and height will be ignored and the shape of mask will be
used instead. All white (#FF or #FFFFFF) entries will be considerd
"masked out" while other entries will be free to draw on. [This
changed in the most recent version!]
scale : float (default=1)
Scaling between computation and drawing. For large word-cloud images,
Expand Down Expand Up @@ -173,22 +177,21 @@ def generate_from_frequencies(self, frequencies):
% len(frequencies))

if self.mask is not None:
width = self.mask.shape[1]
height = self.mask.shape[0]
mask = self.mask
width = mask.shape[1]
height = mask.shape[0]
if mask.dtype.kind == 'f':
# threshold float images
mask = mask >= .5
elif mask.dtype.kind == 'i':
# threshold ubyte images
mask = mask >= 128
if self.mask.ndim == 3:
# "OR" all channels
mask = mask.sum(axis=-1) > 0
if mask.ndim != 2:
warnings.warn("mask image should be unsigned byte between 0 and"
"255. Got a float array")
if mask.ndim == 2:
boolean_mask = mask == 255
elif mask.ndim == 3:
# "OR" the color channels
boolean_mask = np.sum(mask[:, :, :3] == 255, axis=-1)
else:
raise ValueError("Got mask of invalid shape: %s" % str(mask.shape))
# 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:
height, width = self.height, self.width
integral = np.zeros((height, width), dtype=np.uint32)
Expand Down Expand Up @@ -237,13 +240,16 @@ def generate_from_frequencies(self, frequencies):
positions.append((x, y))
orientations.append(orientation)
font_sizes.append(font_size)
colors.append(self.color_func(word, font_size, (x, y), orientation,
random_state=random_state))
colors.append(self.color_func(word, font_size=font_size,
position=(x, y),
orientation=orientation,
random_state=random_state,
font_path=self.font_path))
# recompute integral image
if self.mask is None:
img_array = np.asarray(img_grey)
else:
img_array = np.asarray(img_grey) + mask
img_array = np.asarray(img_grey) + boolean_mask
# recompute bottom right
# the order of the cumsum's is important for speed ?!
partial_integral = np.cumsum(np.cumsum(img_array[x:, y:], axis=1),
Expand Down Expand Up @@ -406,7 +412,9 @@ def recolor(self, random_state=None, color_func=None):
if color_func is None:
color_func = self.color_func
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_]
return self

Expand Down

0 comments on commit eaea49c

Please sign in to comment.