Reputation: 22827
I have a folder with small images (Facebook profile pictures). I want to make a new mosaic-like picture where all the small pictures are laid out in the form of a number, like in this example (source).
Is there a software program that can do this (and that runs on Windows 7)? Otherwise I'm also open to writing a small script to do the same. I know how I can add a white border to the images with PIL/Pillow, but my searches for how to layout the images have turned up fruitless.
Can anyone point me in the right direction?
Upvotes: 4
Views: 333
Reputation: 879371
jsheperd shows how to convert text into ASCII art. You can modify that code slightly to obtain a glyph mask -- 1 where the font is black, and 0 where there is background. We can then use PIL to randomly rotate and paste a face wherever the mask is 1.
Below I used matplotlib
just to obtain an image (of Ada Lovelace) that we would all have assuming you have matplotlib
installed. You can remove the matplotlib dependency and just redefine faces
to be a sequence of PIL images.
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
import itertools as IT
import numpy as np
import matplotlib.cbook as cbook
def text_to_pixels(text, path='arialbd.ttf', fontsize=14):
"""
https://stackoverflow.com/a/27753869/190597 (jsheperd)
https://stackoverflow.com/a/36386628/190597 (unutbu)
"""
font = ImageFont.truetype(path, fontsize)
w, h = font.getsize(text)
h *= 2
image = Image.new('L', (w, h), 1)
draw = ImageDraw.Draw(image)
draw.text((0, 0), text, font=font)
arr = np.asarray(image)
arr = np.where(arr, 0, 1)
arr = arr[(arr != 0).any(axis=1)]
return arr
def get_image():
fn = cbook.get_sample_data("ada.png")
face_img = Image.open(fn).convert('RGBA')
face_img = face_img.resize((30, 40), Image.ANTIALIAS)
# give image a white background
img = Image.new('RGBA', size=(36, 46), color=(255, 255, 255))
img.paste(face_img, (3, 3))
return img
def sqdist(a, b):
return ((a -b)**2).sum()
def pics_in_text(text, faces, img_width=600, img_height=250, path='arialbd.ttf',
fontsize=20, minsep=1000):
arr = text_to_pixels(text, path=path, fontsize=fontsize)
yx = np.column_stack(np.where(arr)).astype(float)
yx /= arr.shape
yx *= (0.75, 0.90)
yx += 0.05
yx *= (img_height, img_width)
yx = yx.astype('int')
np.random.shuffle(yx)
keep = []
for coord in yx:
if all(sqdist(item, coord) > minsep for item in keep):
keep.append(coord)
yx = IT.cycle(keep)
img = Image.new('RGBA', size=(img_width, img_height), color=(255, 255, 255, 255))
seen = list()
for face, coord in zip(faces, yx):
deg = np.random.uniform(-45, 45)
face = face.rotate(deg, resample=Image.BICUBIC, expand=False)
img.paste(face, tuple(coord[::-1]), mask=face)
return img
def get_image():
import matplotlib.cbook as cbook
fn = cbook.get_sample_data("ada.png")
face_img = Image.open(fn).convert('RGBA')
face_img = face_img.resize((30, 40), Image.ANTIALIAS)
# give image a white background
img = Image.new('RGBA', size=(36, 46), color=(255, 255, 255))
img.paste(face_img, (3, 3))
return img
num_faces = 650
faces = IT.islice(IT.cycle([get_image()]), num_faces)
img = pics_in_text('800', faces, img_width=1200, img_height=500,
path='/usr/share/fonts/truetype/msttcorefonts/Comic_Sans_MS.ttf',
fontsize=40, minsep=375)
img.save('/tmp/out.png', 'PNG')
min_sep
is the minimum squared distance between face images. If you increase the min_sep
parameter, the faces will be spaced farther apart. If you decrease min_sep
then the faces may overlap more densely.
Upvotes: 1