def make_wordcloud(words, counts, width=400, height=200): # sort words by counts inds = np.argsort(counts)[::-1] counts = counts[inds] words = words[inds] # create image img = Image.new("L", (width, height)) draw = ImageDraw.Draw(img) #i = 0 integral = np.zeros((height, width), dtype=np.uint) img_array = np.asarray(img) for word, count in zip(words, counts): font_path = "/usr/share/fonts/truetype/droid/DroidSansMono.ttf" #img_array = img_array.sum(axis=2) # set font size font_size = int(np.log(count)) * 20 runs = 0 while True: font = ImageFont.truetype(font_path, font_size) # transpose font optionally orientation = random.choice([None, Image.ROTATE_90]) transposed_font = ImageFont.TransposedFont(font, orientation=orientation) draw.setfont(transposed_font) # get size of resulting text size = draw.textsize(word) # find possible places using integral image: result = query_integral_image(integral, size[1], size[0]) if result is not None: break # if we didn't find a place, make font smaller font_size -= 1 runs += 1 x, y = result draw.text((result[1], result[0]), word, fill="white") # recompute integral image img_array = np.asarray(img) # recompute bottom right partial_integral = np.cumsum(np.cumsum(img_array[x:, y:], axis=1), axis=0) # paste into old image # if x or y is zero it is a bit annoying if x > 0: if y > 0: partial_integral += (integral[x - 1, y:] - integral[x - 1, y - 1]) else: partial_integral += integral[x - 1, y:] if y > 0: partial_integral += integral[x:, y - 1][:, np.newaxis] integral[x:, y:] = partial_integral img.show()
def sample_position(self, size_x, size_y, random_state): return query_integral_image(self.integral, size_x, size_y, random_state)
def make_wordcloud(words, counts, fname, font_path=None, width=400, height=200, margin=5, ranks_only=False): """Build word cloud using word counts, store in image. Parameters ---------- words : numpy array of strings Words that will be drawn in the image. counts : numpy array of word counts Word counts or weighting of words. Determines the size of the word in the final image. Will be normalized to lie between zero and one. font_path : string Font path to the font that will be used. Defaults to DroidSansMono path. fname : sting Output filename. Extension determins image type (written with PIL). width : int (default=400) Width of the word cloud image. height : int (default=200) Height of the word cloud image. ranks_only : boolean (default=False) Only use the rank of the words, not the actual counts. Notes ----- Larger Images with make the code significantly slower. If you need a large image, you can try running the algorithm at a lower resolution and then drawing the result at the desired resolution. In the current form it actually just uses the rank of the counts, i.e. the relative differences don't matter. Play with setting the font_size in the main loop vor differnt styles. Colors are used completely at random. Currently the colors are sampled from HSV space with a fixed S and V. Adjusting the percentages at the very end gives differnt color ranges. Obviously you can also set all at random - haven't tried that. """ if len(counts) <= 0: print("We need at least 1 word to plot a word cloud, got %d." % len(counts)) if font_path is None: font_path = FONT_PATH if not os.path.exists(font_path): raise ValueError("The provided font %s does not exist." % font_path) # normalize counts counts = counts / float(counts.max()) # sort words by counts inds = np.argsort(counts)[::-1] counts = counts[inds] words = words[inds] # create image img_grey = Image.new("L", (width, height)) draw = ImageDraw.Draw(img_grey) integral = np.zeros((height, width), dtype=np.uint32) img_array = np.asarray(img_grey) font_sizes, positions, orientations = [], [], [] # intitiallize font size "large enough" font_size = 1000 # start drawing grey image for word, count in zip(words, counts): # alternative way to set the font size if not ranks_only: font_size = min(font_size, int(100 * np.log(count + 100))) while True: # try to find a position font = ImageFont.truetype(font_path, font_size) # transpose font optionally orientation = random.choice([None, Image.ROTATE_90]) transposed_font = ImageFont.TransposedFont(font, orientation=orientation) draw.setfont(transposed_font) # get size of resulting text box_size = draw.textsize(word) # find possible places using integral image: result = query_integral_image(integral, box_size[1] + margin, box_size[0] + margin) if result is not None or font_size == 0: break # if we didn't find a place, make font smaller font_size -= 1 if font_size == 0: # we were unable to draw any more break x, y = np.array(result) + margin // 2 # actually draw the text draw.text((y, x), word, fill="white") positions.append((x, y)) orientations.append(orientation) font_sizes.append(font_size) # recompute integral image img_array = np.asarray(img_grey) # 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), axis=0) # paste recomputed part into old image # if x or y is zero it is a bit annoying if x > 0: if y > 0: partial_integral += (integral[x - 1, y:] - integral[x - 1, y - 1]) else: partial_integral += integral[x - 1, y:] if y > 0: partial_integral += integral[x:, y - 1][:, np.newaxis] integral[x:, y:] = partial_integral # redraw in color img = Image.new("RGB", (width, height)) draw = ImageDraw.Draw(img) everything = zip(words, font_sizes, positions, orientations) for word, font_size, position, orientation in everything: font = ImageFont.truetype(font_path, font_size) # transpose font optionally transposed_font = ImageFont.TransposedFont(font, orientation=orientation) draw.setfont(transposed_font) draw.text((position[1], position[0]), word, fill="hsl(%d" % random.randint(0, 255) + ", %d" % random.randint(90, 100) + "%, 50%)") #img.show() img.save(fname) return img
def fit_words(words, font_path=None, width=400, height=200, margin=5, ranks_only=False, prefer_horiz=0.90): """Generate the positions for words. Parameters ---------- words : array of tuples A tuple contains the word and its frequency. font_path : string Font path to the font that will be used (OTF or TTF). Defaults to DroidSansMono path, but you might not have it. width : int (default=400) Width of the canvas. height : int (default=200) Height of the canvas. ranks_only : boolean (default=False) Only use the rank of the words, not the actual counts. prefer_horiz : float (default=0.90) The ratio of times to try horizontal fitting as opposed to vertical. Notes ----- Larger canvases with make the code significantly slower. If you need a large word cloud, run this function with a lower canvas size, and draw it with a larger scale. In the current form it actually just uses the rank of the counts, i.e. the relative differences don't matter. Play with setting the font_size in the main loop for different styles. """ if len(words) <= 0: print("We need at least 1 word to plot a word cloud, got %d." % len(words)) if font_path is None: font_path = FONT_PATH if not os.path.exists(font_path): raise ValueError("The font %s does not exist." % font_path) # create image img_grey = Image.new("L", (width, height)) draw = ImageDraw.Draw(img_grey) integral = np.zeros((height, width), dtype=np.uint32) img_array = np.asarray(img_grey) font_sizes, positions, orientations = [], [], [] # intitiallize font size "large enough" font_size = height # start drawing grey image for word, count in words: # alternative way to set the font size if not ranks_only: font_size = min(font_size, int(100 * np.log(count + 100))) while True: # try to find a position font = ImageFont.truetype(font_path, font_size) # transpose font optionally if random.random() < prefer_horiz: orientation = None else: orientation = Image.ROTATE_90 transposed_font = ImageFont.TransposedFont(font, orientation=orientation) draw.setfont(transposed_font) # get size of resulting text box_size = draw.textsize(word) # find possible places using integral image: result = query_integral_image(integral, box_size[1] + margin, box_size[0] + margin) if result is not None or font_size == 0: break # if we didn't find a place, make font smaller font_size -= 1 if font_size == 0: # we were unable to draw any more break x, y = np.array(result) + margin // 2 # actually draw the text draw.text((y, x), word, fill="white") positions.append((x, y)) orientations.append(orientation) font_sizes.append(font_size) # recompute integral image img_array = np.asarray(img_grey) # 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), axis=0) # paste recomputed part into old image # if x or y is zero it is a bit annoying if x > 0: if y > 0: partial_integral += (integral[x - 1, y:] - integral[x - 1, y - 1]) else: partial_integral += integral[x - 1, y:] if y > 0: partial_integral += integral[x:, y - 1][:, np.newaxis] integral[x:, y:] = partial_integral return zip(words, font_sizes, positions, orientations)
def _fit_words(self, words): """Generate the positions for words. Parameters ---------- words : array of tuples A tuple contains the word and its frequency. Returns ------- layout_ : list of tuples (string, int, (int, int), int, color)) Encodes the fitted word cloud. Encodes for each word the string, font size, position, orientation and color. Notes ----- Larger canvases with make the code significantly slower. If you need a large word cloud, run this function with a lower canvas size, and draw it with a larger scale. In the current form it actually just uses the rank of the counts, i.e. the relative differences don't matter. Play with setting the font_size in the main loop for different styles. """ if self.random_state is not None: random_state = self.random_state else: random_state = Random() if len(words) <= 0: print("We need at least 1 word to plot a word cloud, got %d." % len(words)) if self.mask is not None: width = self.mask.shape[1] height = self.mask.shape[0] # the order of the cumsum's is important for speed ?! integral = np.cumsum(np.cumsum(self.mask, axis=1), axis=0).astype(np.uint32) else: height, width = self.height, self.width integral = np.zeros((height, width), dtype=np.uint32) # create image img_grey = Image.new("L", (width, height)) draw = ImageDraw.Draw(img_grey) img_array = np.asarray(img_grey) font_sizes, positions, orientations, colors = [], [], [], [] font_size = self.max_font_size # start drawing grey image for word, count in words: # alternative way to set the font size if not self.ranks_only: font_size = min(font_size, int(100 * np.log(count + 100))) while True: # try to find a position font = ImageFont.truetype(self.font_path, font_size) # transpose font optionally if random_state.random() < self.prefer_horizontal: orientation = None else: orientation = Image.ROTATE_90 transposed_font = ImageFont.TransposedFont( font, orientation=orientation) draw.setfont(transposed_font) # get size of resulting text box_size = draw.textsize(word) # find possible places using integral image: result = query_integral_image(integral, box_size[1] + self.margin, box_size[0] + self.margin, random_state) if result is not None or font_size == 0: break # if we didn't find a place, make font smaller font_size -= 1 if font_size == 0: # we were unable to draw any more break x, y = np.array(result) + self.margin // 2 # actually draw the text draw.text((y, x), word, fill="white") 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)) # recompute integral image if self.mask is None: img_array = np.asarray(img_grey) else: img_array = np.asarray(img_grey) + self.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), axis=0) # paste recomputed part into old image # if x or y is zero it is a bit annoying if x > 0: if y > 0: partial_integral += (integral[x - 1, y:] - integral[x - 1, y - 1]) else: partial_integral += integral[x - 1, y:] if y > 0: partial_integral += integral[x:, y - 1][:, np.newaxis] integral[x:, y:] = partial_integral self.layout_ = zip(words, font_sizes, positions, orientations, colors) return self.layout_
def fit_words(words, font_path=None, width=400, height=200, margin=5, ranks_only=False, prefer_horiz=0.90): """Generate the positions for words. Parameters ---------- words : array of tuples A tuple contains the word and its frequency. font_path : string Font path to the font that will be used (OTF or TTF). Defaults to DroidSansMono path, but you might not have it. width : int (default=400) Width of the canvas. height : int (default=200) Height of the canvas. ranks_only : boolean (default=False) Only use the rank of the words, not the actual counts. prefer_horiz : float (default=0.90) The ratio of times to try horizontal fitting as opposed to vertical. Notes ----- Larger canvases with make the code significantly slower. If you need a large word cloud, run this function with a lower canvas size, and draw it with a larger scale. In the current form it actually just uses the rank of the counts, i.e. the relative differences don't matter. Play with setting the font_size in the main loop for different styles. """ if len(words) <= 0: print("We need at least 1 word to plot a word cloud, got %d." % len(words)) if font_path is None: font_path = FONT_PATH if not os.path.exists(font_path): raise ValueError("The font %s does not exist." % font_path) # create image img_grey = Image.new("L", (width, height)) draw = ImageDraw.Draw(img_grey) integral = np.zeros((height, width), dtype=np.uint32) img_array = np.asarray(img_grey) font_sizes, positions, orientations = [], [], [] # intitiallize font size "large enough" font_size = height # start drawing grey image for word, count in words: # alternative way to set the font size if not ranks_only: font_size = min(font_size, int(100 * np.log(count + 100))) while True: # try to find a position font = ImageFont.truetype(font_path, font_size) # transpose font optionally if random.random() < prefer_horiz: orientation = None else: orientation = Image.ROTATE_90 transposed_font = ImageFont.TransposedFont(font, orientation=orientation) draw.setfont(transposed_font) # get size of resulting text box_size = draw.textsize(word) # find possible places using integral image: result = query_integral_image(integral, box_size[1] + margin, box_size[0] + margin) if result is not None or font_size == 0: break # if we didn't find a place, make font smaller font_size -= 1 if font_size == 0: # we were unable to draw any more break x, y = np.array(result) + margin // 2 # actually draw the text draw.text((y, x), word, fill="white") positions.append((x, y)) orientations.append(orientation) font_sizes.append(font_size) # recompute integral image img_array = np.asarray(img_grey) # 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), axis=0) # paste recomputed part into old image # if x or y is zero it is a bit annoying if x > 0: if y > 0: partial_integral += integral[x - 1, y:] - integral[x - 1, y - 1] else: partial_integral += integral[x - 1, y:] if y > 0: partial_integral += integral[x:, y - 1][:, np.newaxis] integral[x:, y:] = partial_integral return zip(words, font_sizes, positions, orientations)
def make_wordcloud(words, counts, fname="words.png", font_path=None, width=400, height=200, margin=16): """Build word cloud using word counts, store in image. Parameters ---------- words : numpy array of strings Words that will be drawn in the image. counts : numpy array of word counts Word counts or weighting of words. Determines the size of the word in the final image. Will be normalized to lie between zero and one. font_path : sting Font path to the font that will be used. Defaults to DroidSansMono path. fname : sting Output filename. Extension determins image type (written with PIL). width : int (default=400) Width of the word cloud image. height : int (default=200) Height of the word cloud image. Notes ----- Larger Images with make the code significantly slower. If you need a large image, you can try running the algorithm at a lower resolution and then drawing the result at the desired resolution. In the current form it actually just uses the rank of the counts, i.e. the relative differences don't matter. Play with setting the font_size in the main loop vor differnt styles. Colors are used completely at random. Currently the colors are sampled from HSV space with a fixed S and V. Adjusting the percentages at the very end gives differnt color ranges. Obviously you can also set all at random - haven't tried that. """ if len(counts) <= 0: print("We need at least 1 word to plot a word cloud, got %d." % len(counts)) if font_path is None: font_path = FONT_PATH # normalize counts counts = counts / float(counts.max()) # sort words by counts inds = np.argsort(counts)[::-1] counts = counts[inds] words = words[inds] # create image img_grey = Image.new("L", (width, height)) draw = ImageDraw.Draw(img_grey) integral = np.zeros((height, width), dtype=np.uint32) img_array = np.asarray(img_grey) font_sizes, positions, orientations = [], [], [] # intitiallize font size "large enough" font_size = 1000 # start drawing grey image for word, count in zip(words, counts): # alternative way to set the font size #font_size = min(font_size, int(100 * np.log(count + 100))) while True: # try to find a position font = ImageFont.truetype(font_path, font_size) # transpose font optionally orientation = random.choice([None, Image.ROTATE_90]) transposed_font = ImageFont.TransposedFont(font, orientation=orientation) draw.setfont(transposed_font) # get size of resulting text box_size = draw.textsize(word) # find possible places using integral image: result = query_integral_image(integral, box_size[1] + margin, box_size[0] + margin) if result is not None or font_size == 0: break # if we didn't find a place, make font smaller font_size -= 1 if font_size == 0: # we were unable to draw any more break x, y = np.array(result) + margin // 2 # actually draw the text draw.text((y, x), word, fill="white") positions.append((x, y)) orientations.append(orientation) font_sizes.append(font_size) # recompute integral image img_array = np.asarray(img_grey) # 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), axis=0) # paste recomputed part into old image # if x or y is zero it is a bit annoying if x > 0: if y > 0: partial_integral += (integral[x - 1, y:] - integral[x - 1, y - 1]) else: partial_integral += integral[x - 1, y:] if y > 0: partial_integral += integral[x:, y - 1][:, np.newaxis] integral[x:, y:] = partial_integral # redraw in color img = Image.new("RGB", (width, height), "white") draw = ImageDraw.Draw(img) everything = zip(words, font_sizes, positions, orientations) e_length = len(everything) hues = np.round(np.linspace(1, 255 - e_length, e_length)).astype(int) for i, (word, font_size, position, orientation) in enumerate(everything): font = ImageFont.truetype(font_path, font_size) # transpose font optionally transposed_font = ImageFont.TransposedFont(font, orientation=orientation) draw.setfont(transposed_font) draw.text((position[1], position[0]), word, fill="hsl(%d" % hues[i] + ", 55%, 35%)") # img.save(fname) return img
def make_wordcloud(words, counts, fname, width=800, height=400, margin=5, ranks_only=False): if len(counts) <= 0: print("We need at least 1 word to plot a word cloud, got %d." % len(counts)) font_path = FONT_PATH max_count = float(max(counts)) #normalize counts counts = counts/max_count #sort words by count inds = np.argsort(counts)[::-1] counts = counts[inds] words = words[inds] #create image img_grey = Image.new("L", (width, height)) draw = ImageDraw.Draw(img_grey) integral = np.zeros((height, width), dtype=np.uint32) img_array = np.asarray(img_grey) font_sizes, positions, orientations = [], [], [] # intitiallize font size "large enough" font_size = 1000 # start drawing grey image for word, count in zip(words, counts): # alternative way to set the font size if not ranks_only: font_size = min(font_size, int(100 * np.log(count + 100))) while True: #try to find position font = ImageFont.truetype(font_path, font_size) #transpose font optionally orientation = random.choice([None, Image.ROTATE_90]) transposed_font = ImageFont.TransposedFont(font, orientation=orientation) draw.setfont(transposed_font) #get size of resulting text box_size = draw.textsize(word) #finding possible places using integral images result = query_integral_image(integral, box_size[1] + margin, box_size[0] + margin) if (result is not None or font_size == 0) : break #if we didn't find space make the font smaller font_size -= 1 if font_size == 0: break x, y = np.array(result) + margin // 2 # actually draw the text draw.text((y, x), word, fill="white") positions.append((x, y)) orientations.append(orientation) font_sizes.append(font_size) # recompute integral image img_array = np.asarray(img_grey) # 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), axis=0) if x > 0: if y > 0: partial_integral += (integral[x - 1, y:] - integral[x - 1, y - 1]) else: partial_integral += integral[x - 1, y:] if y > 0: partial_integral += integral[x:, y - 1][:, np.newaxis] integral[x:, y:] = partial_integral # redraw in color img = Image.new("RGB", (width, height)) draw = ImageDraw.Draw(img) everything = zip(words, font_sizes, positions, orientations) for word, font_size, position, orientation in everything: font = ImageFont.truetype(font_path, font_size) #transpose font optionally transposed_font = ImageFont.TransposedFont(font, orientation=orientation) draw.setfont(transposed_font) draw.text((position[1], position[0]), word, fill="hsl(%d" % random.randint(0, 255) + ", 80%, 50%)") #img.show() img.save(fname)
def _fit_words(self, words): """Generate the positions for words. Parameters ---------- words : array of tuples A tuple contains the word and its frequency. Returns ------- layout_ : list of tuples (string, int, (int, int), int, color)) Encodes the fitted word cloud. Encodes for each word the string, font size, position, orientation and color. Notes ----- Larger canvases with make the code significantly slower. If you need a large word cloud, run this function with a lower canvas size, and draw it with a larger scale. In the current form it actually just uses the rank of the counts, i.e. the relative differences don't matter. Play with setting the font_size in the main loop for different styles. """ if self.random_state is not None: random_state = self.random_state else: random_state = Random() if len(words) <= 0: print("We need at least 1 word to plot a word cloud, got %d." % len(words)) if self.mask is not None: width = self.mask.shape[1] height = self.mask.shape[0] # the order of the cumsum's is important for speed ?! integral = np.cumsum(np.cumsum(self.mask, axis=1), axis=0).astype(np.uint32) else: height, width = self.height, self.width integral = np.zeros((height, width), dtype=np.uint32) # create image img_grey = Image.new("L", (width, height)) draw = ImageDraw.Draw(img_grey) img_array = np.asarray(img_grey) font_sizes, positions, orientations, colors = [], [], [], [] font_size = self.max_font_size # start drawing grey image for word, count in words: # alternative way to set the font size if not self.ranks_only: font_size = min(font_size, int(100 * np.log(count + 100))) while True: # try to find a position font = ImageFont.truetype(self.font_path, font_size) # transpose font optionally if random_state.random() < self.prefer_horizontal: orientation = None else: orientation = Image.ROTATE_90 transposed_font = ImageFont.TransposedFont(font, orientation=orientation) draw.setfont(transposed_font) # get size of resulting text box_size = draw.textsize(word) # find possible places using integral image: result = query_integral_image(integral, box_size[1] + self.margin, box_size[0] + self.margin, random_state) if result is not None or font_size == 0: break # if we didn't find a place, make font smaller font_size -= 1 if font_size == 0: # we were unable to draw any more break x, y = np.array(result) + self.margin // 2 # actually draw the text draw.text((y, x), word, fill="white") 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)) # recompute integral image if self.mask is None: img_array = np.asarray(img_grey) else: img_array = np.asarray(img_grey) + self.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), axis=0) # paste recomputed part into old image # if x or y is zero it is a bit annoying if x > 0: if y > 0: partial_integral += (integral[x - 1, y:] - integral[x - 1, y - 1]) else: partial_integral += integral[x - 1, y:] if y > 0: partial_integral += integral[x:, y - 1][:, np.newaxis] integral[x:, y:] = partial_integral self.layout_ = zip(words, font_sizes, positions, orientations, colors) return self.layout_
def draw(self): #### create image bwimg = Image.new("L", (self.width, self.height)) draw = ImageDraw.Draw(bwimg) integral = np.zeros((self.height, self.width), dtype=np.uint32) img_array = np.asarray(bwimg) font_sizes, positions, orientations = [], [], [] font_size = 1000 # start drawing grey image for word, count in zip(self.words, self.counts): # alternative way to set the font size while True: # try to find a position font = ImageFont.truetype(self.font_path, font_size) # transpose font optionally orientation = random.choice([None, Image.ROTATE_90]) transposed_font = ImageFont.TransposedFont( font, orientation=orientation) draw.setfont(transposed_font) # get size of resulting text box_size = draw.textsize(word) # find possible places using integral image: result = query_integral_image(integral, box_size[1] + self.margin, box_size[0] + self.margin) if result is not None or font_size == 0: break # if we didn't find a place, make font smaller font_size -= 1 if font_size == 0: # we were unable to draw any more break x, y = np.array(result) + self.margin // 2 # actually draw the text draw.text((y, x), word, fill="white") positions.append((x, y)) orientations.append(orientation) font_sizes.append(font_size) # recompute integral image img_array = np.asarray(bwimg) # 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), axis=0) # paste recomputed part into old image # if x or y is zero it is a bit annoying if x > 0: if y > 0: partial_integral += (integral[x - 1, y:] - integral[x - 1, y - 1]) else: partial_integral += integral[x - 1, y:] if y > 0: partial_integral += integral[x:, y - 1][:, np.newaxis] integral[x:, y:] = partial_integral # redraw in color img = Image.new("RGB", (self.width, self.height)) draw = ImageDraw.Draw(img) everything = zip(self.words, font_sizes, positions, orientations) for word, font_size, position, orientation in everything: font = ImageFont.truetype(self.font_path, font_size) transposed_font = ImageFont.TransposedFont(font, orientation=orientation) draw.setfont(transposed_font) draw.text((position[1], position[0]), word, fill="hsl(%d" % random.randint(0, 255) + ", 80%, 50%)") #img.show() return img
def acomodar_palabras(self): """El 'trabajo pesado' de distribuir las palabras de forma que encajen en la imágen a generar""" altura, largo = self.height, self.width integral = zeros((altura, largo), dtype=uint32) # Creación de imágen imagen = Image.new("L", (largo, altura)) dibujo = ImageDraw.Draw(imagen) img_arreglo = asarray(imagen) tamaños, posiciones, orientaciones, colores = [], [], [], [] font_actual = self.max_font_size # Inicio de dibujado for palabra, freq in self.palabras: # Hay que encontrar una posición para todas las palabras while True: # font_actual = min(self.max_font_size, int(100 * log(freq + 100))) fuente = ImageFont.truetype(self.font_path, font_actual) # Vemos si va horizontal o vertical y dibujamos if self.R.random() < self.horizontales: orientacion = None else: orientacion = Image.ROTATE_90 font_transpuesta = ImageFont.TransposedFont( fuente, orientation=orientacion) dibujo.setfont(font_transpuesta) tamaño_resultante = dibujo.textsize(palabra) # Buscamos posibles lugares resultado = query_integral_image( integral, tamaño_resultante[1] + self.margin, tamaño_resultante[0] + self.margin, self.R) # Si el resultado es posible o no podemos escribir más if resultado is not None or font_actual == 0: break # Si no hay espacio, achicamos la fuente font_actual -= 1 x, y = array(resultado) + self.margin // 2 # Dibujar el resultado dibujo.text((y, x), palabra, fill="white") posiciones.append((x, y)) orientaciones.append(orientacion) tamaños.append(font_actual) colores.append( self.color_func(palabra, font_size=font_actual, position=(x, y), orientation=orientacion)) # Recalcular imágen img_arreglo = asarray(imagen) integral_parcial = cumsum(cumsum(img_arreglo[x:, y:], axis=1), axis=0) # Pegar parte calculada a la imágen acumulada if x > 0: if y > 0: integral_parcial += (integral[x - 1, y:] - integral[x - 1, y - 1]) else: integral_parcial += integral[x - 1, y:] if y > 0: integral_parcial += integral[x:, y - 1][:, newaxis] integral[x:, y:] = integral_parcial self.distribucion = list( zip(self.palabras, tamaños, posiciones, orientaciones, colores)) self.imagen_generada = True
def make_wordcloud(words, counts, font_path, imagineName, width=800, height=600, margin=5): # sort words by counts inds = np.argsort(counts)[::-1] counts = counts[inds] words = words[inds] # create image img_grey = Image.new("L", (width, height)) draw = ImageDraw.Draw(img_grey) integral = np.zeros((height, width), dtype=np.uint32) img_array = np.asarray(img_grey) font_sizes, positions, orientations = [], [], [] # intitiallize fontsize "large enough" font_size = 1000 # start drawing grey image for word, count in zip(words, counts): # set font size #font_size = min(font_size, int(100 * np.log(count + 100))) while True: # try to find a position font = ImageFont.truetype(font_path, font_size) # transpose font optionally orientation = random.choice([None, Image.ROTATE_90]) transposed_font = ImageFont.TransposedFont(font, orientation=orientation) draw.setfont(transposed_font) # get size of resulting text box_size = draw.textsize(word) # find possible places using integral image: result = query_integral_image(integral, box_size[1] + margin, box_size[0] + margin) if result is not None or font_size == 0: break # if we didn't find a place, make font smaller font_size -= 1 if font_size == 0: # we were unable to draw any more break x, y = np.array(result) + margin // 2 # actually draw the text draw.text((y, x), word, fill="white") positions.append((x, y)) orientations.append(orientation) font_sizes.append(font_size) # recompute integral image img_array = np.asarray(img_grey) # 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), axis=0) # paste recomputed part into old image # if x or y is zero it is a bit annoying if x > 0: if y > 0: partial_integral += (integral[x - 1, y:] - integral[x - 1, y - 1]) else: partial_integral += integral[x - 1, y:] if y > 0: partial_integral += integral[x:, y - 1][:, np.newaxis] integral[x:, y:] = partial_integral # redraw in color img = Image.new("RGB", (width, height)) draw = ImageDraw.Draw(img) everything = zip(words, font_sizes, positions, orientations) for word, font_size, position, orientation in everything: font = ImageFont.truetype(font_path, font_size) # transpose font optionally transposed_font = ImageFont.TransposedFont(font, orientation=orientation) draw.setfont(transposed_font) draw.text((position[1], position[0]), word, fill="hsl(%d" % random.randint(0, 255) + ", 80%, 50%)") name = imagineName + '.png' # img.show() img.save(name) img.show()
def make_wordcloud(words, counts, font_path, imagineName,width=400, height=200, margin=5): # sort words by counts inds = np.argsort(counts)[::-1] counts = counts[inds] words = words[inds] # create image img_grey = Image.new("L", (width, height)) draw = ImageDraw.Draw(img_grey) integral = np.zeros((height, width), dtype=np.uint32) img_array = np.asarray(img_grey) font_sizes, positions, orientations = [], [], [] # intitiallize fontsize "large enough" font_size = 1000 # start drawing grey image for word, count in zip(words, counts): # set font size #font_size = min(font_size, int(100 * np.log(count + 100))) while True: # try to find a position font = ImageFont.truetype(font_path, font_size) # transpose font optionally orientation = random.choice([None, Image.ROTATE_90]) transposed_font = ImageFont.TransposedFont(font, orientation=orientation) draw.setfont(transposed_font) # get size of resulting text box_size = draw.textsize(word) # find possible places using integral image: result = query_integral_image(integral, box_size[1] + margin, box_size[0] + margin) if result is not None or font_size == 0: break # if we didn't find a place, make font smaller font_size -= 1 if font_size == 0: # we were unable to draw any more break x, y = np.array(result) + margin // 2 # actually draw the text draw.text((y, x), word, fill="white") positions.append((x, y)) orientations.append(orientation) font_sizes.append(font_size) # recompute integral image img_array = np.asarray(img_grey) # 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), axis=0) # paste recomputed part into old image # if x or y is zero it is a bit annoying if x > 0: if y > 0: partial_integral += (integral[x - 1, y:] - integral[x - 1, y - 1]) else: partial_integral += integral[x - 1, y:] if y > 0: partial_integral += integral[x:, y - 1][:, np.newaxis] integral[x:, y:] = partial_integral # redraw in color img = Image.new("RGB", (width, height)) draw = ImageDraw.Draw(img) everything = zip(words, font_sizes, positions, orientations) for word, font_size, position, orientation in everything: font = ImageFont.truetype(font_path, font_size) # transpose font optionally transposed_font = ImageFont.TransposedFont(font, orientation=orientation) draw.setfont(transposed_font) draw.text((position[1], position[0]), word, fill="hsl(%d" % random.randint(0, 255) + ", 80%, 50%)") name=imagineName+'.png' # img.show() img.save(name) img.show()
def sample_position(self, size_x, size_y, random_state): return query_integral_image(self.integral, size_x, size_y, random_state)
def make_wordcloud(words, counts, fname=None, font_path=None, width=400, height=200, margin=5, ranks_only=False, backgroundweight=255): """Build word cloud using word counts, store in image. Parameters ---------- words : numpy array of strings Words that will be drawn in the image. counts : numpy array of word counts Word counts or weighting of words. Determines the size of the word in the final image. Will be normalized to lie between zero and one. font_path : string Font path to the font that will be used. Defaults to DroidSansMono path. fname : sting Output filename. Extension determins image type (written with PIL). width : int (default=400) Width of the word cloud image. height : int (default=200) Height of the word cloud image. ranks_only : boolean (default=False) Only use the rank of the words, not the actual counts. backgroundweight : int (default=255) Weight that the background of the wordcloud is multiplied by. Applies in cases where there are more than 2 dimensions which charecterize the cloud; in our case it is the logged number of community population whose tweets resulted in the cloud. Notes ----- Larger Images with make the code significantly slower. If you need a large image, you can try running the algorithm at a lower resolution and then drawing the result at the desired resolution. In the current form it actually just uses the rank of the counts, i.e. the relative differences don't matter. Play with setting the font_size in the main loop vor differnt styles. Colors are used completely at random. Currently the colors are sampled from HSV space with a fixed S and V. Adjusting the percentages at the very end gives differnt color ranges. Obviously you can also set all at random - haven't tried that. """ if len(counts) <= 0: print("We need at least 1 word to plot a word cloud, got %d." % len(counts)) if font_path is None: font_path = FONT_PATH if not os.path.exists(font_path): raise ValueError("The provided font %s does not exist." % font_path) # normalize counts counts=[float(i/max(counts)) for i in counts] # sort words by counts inds = np.argsort(counts)[::-1] counts = [counts[i] for i in inds] words = [words[i] for i in inds] # create image img_grey = Image.new("L", (width, height)) draw = ImageDraw.Draw(img_grey) integral = np.zeros((height, width), dtype=np.uint32) img_array = np.asarray(img_grey) font_sizes, positions, orientations = [], [], [] # intitiallize font size "large enough" font_size = 1000 # start drawing grey image for word, count in zip(words, counts): # alternative way to set the font size if not ranks_only: font_size = min(font_size, int(100 * np.log(count + 100))) while True: # try to find a position font = ImageFont.truetype(font_path, font_size, encoding = 'unic') # transpose font optionally orientation = random.choice([None, Image.ROTATE_90]) transposed_font = ImageFont.TransposedFont(font, orientation=orientation) draw.setfont(transposed_font) # get size of resulting text box_size = draw.textsize(word) # find possible places using integral image: result = query_integral_image(integral, box_size[1] + margin, box_size[0] + margin) if result is not None or font_size == 0: break # if we didn't find a place, make font smaller font_size -= 1 if font_size == 0: # we were unable to draw any more break x, y = np.array(result) + margin // 2 # actually draw the text draw.text((y, x), word, fill="white") positions.append((x, y)) orientations.append(orientation) font_sizes.append(font_size) # recompute integral image img_array = np.asarray(img_grey) # 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), axis=0) # paste recomputed part into old image # if x or y is zero it is a bit annoying if x > 0: if y > 0: partial_integral += (integral[x - 1, y:] - integral[x - 1, y - 1]) else: partial_integral += integral[x - 1, y:] if y > 0: partial_integral += integral[x:, y - 1][:, np.newaxis] integral[x:, y:] = partial_integral # redraw in color img = Image.new("RGB", (width, height), (backgroundweight,backgroundweight,backgroundweight)) draw = ImageDraw.Draw(img) everything = zip(words, font_sizes, positions, orientations) for word, font_size, position, orientation in everything: font = ImageFont.truetype(font_path, font_size) # transpose font optionally transposed_font = ImageFont.TransposedFont(font, orientation=orientation) draw.setfont(transposed_font) draw.text((position[1], position[0]), word, #fill = "red") fill="hsl(%d" % random.randint(0, 50) + ", 80%, 50%)") #img.show() try: img.save(fname) except: pass return img
def acomodar_palabras(self): """El 'trabajo pesado' de distribuir las palabras de forma que encajen en la imágen a generar""" altura, largo = self.height, self.width integral = zeros((altura, largo), dtype=uint32) # Creación de imágen imagen = Image.new("L", (largo, altura)) dibujo = ImageDraw.Draw(imagen) img_arreglo = asarray(imagen) tamaños, posiciones, orientaciones, colores = [], [], [], [] font_actual = self.max_font_size __ = 0 # Inicio de dibujado for palabra, freq in self.palabras: print(str(__) + "/" + str(len(self.palabras))) __ += 1 # Hay que encontrar una posición para todas las palabras while True: # font_actual = min(self.max_font_size, int(100 * log(freq + 100))) fuente = ImageFont.truetype(self.font_path, font_actual) # Vemos si va horizontal o vertical y dibujamos if self.R.random() < self.horizontales: orientacion = None else: orientacion = Image.ROTATE_90 font_transpuesta = ImageFont.TransposedFont(fuente, orientation=orientacion) dibujo.setfont(font_transpuesta) tamaño_resultante = dibujo.textsize(palabra) # Buscamos posibles lugares resultado = query_integral_image(integral, tamaño_resultante[1] + self.margin, tamaño_resultante[0] + self.margin, self.R) # Si el resultado es posible o no podemos escribir más if resultado is not None or font_actual == 0: break # Si no hay espacio, achicamos la fuente font_actual -= 1 x, y = array(resultado) + self.margin // 2 # Dibujar el resultado dibujo.text((y, x), palabra, fill="white") posiciones.append((x, y)) orientaciones.append(orientacion) tamaños.append(font_actual) colores.append(self.color_func(palabra, font_size=font_actual, position=(x, y), orientation=orientacion)) # Recalcular imágen img_arreglo = asarray(imagen) integral_parcial = cumsum( cumsum(img_arreglo[x:, y:], axis=1), axis=0) # Pegar parte calculada a la imágen acumulada if x > 0: if y > 0: integral_parcial += (integral[x - 1, y:] - integral[x - 1, y - 1]) else: integral_parcial += integral[x - 1, y:] if y > 0: integral_parcial += integral[x:, y - 1][:, newaxis] integral[x:, y:] = integral_parcial print(__) self.distribucion = list( zip(self.palabras, tamaños, posiciones, orientaciones, colores)) self.imagen_generada = True