Alex Michael

Engineer @tictail, founder @hackcyprus

 

12 January 2014

Which letter consumes the most pixels on screen?

TL;DR It's M, but do read on.

I recently discovered an interesting question on Stack Overflow:

I am trying to do some dynamic programming based on the number of chars in a sentence. I need to know which letter of the English alphabet takes up the most pixels in the screen???

The most popular answer was great – simple and to the point. But, counting the background as pixels consumed by the letter didn’t feel right to me so I wrote a little script to find the actual number. You can look at the code and results here.

Counting Helvetica pixels

Here’s the whole script below. It uses Pillow to draw black letters on a white background and then count them. The top 3 letters are: M (2493 pixels), W (2414) and B (1909).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from operator import itemgetter
from PIL import Image, ImageDraw, ImageFont


# Make a lowercase + uppercase alphabet.
alphabet = 'abcdefghijklmnopqrstuvwxyz'
alphabet += ''.join(map(str.upper, alphabet))


# We'll use Helvetica in big type.
helvetica = ImageFont.truetype('Helvetica.ttf', 100)


def draw_letter(letter, save=True):
    img = Image.new('RGB', (100, 100), 'white')

    draw = ImageDraw.Draw(img)
    draw.text((0,0), letter, font=helvetica, fill='#000000')

    if save:
        img.save("imgs/{}.png".format(letter), 'PNG')

    return img


def count_black_pixels(img):
    pixels = list(img.getdata())
    return len(filter(lambda rgb: sum(rgb) == 0, pixels))


if __name__ == '__main__':
    counts = [
        (letter, count_black_pixels(draw_letter(letter)))
        for letter in alphabet
    ]

    print sorted(counts, key=itemgetter(1), reverse=True)

What about other fonts?

Let’s now generalize this to a number of fonts since pixel count should vary between different font families. I’m on a Mac so I used Font Book to export a collection of fonts and then modified my script to calculate the mean and standard deviation for each letter.

The results are more or less the same: M (2217.51 ± 945.19), W (2139.06 ± 945.29) and B (1841.38 ± 685.26).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# -*- coding: utf-8 -*-
from __future__ import division
import os
from collections import defaultdict
from math import sqrt
from PIL import Image, ImageDraw, ImageFont


# Make a lowercase + uppercase alphabet.
alphabet = 'abcdefghijklmnopqrstuvwxyz'
alphabet += ''.join(map(str.upper, alphabet))


def draw_letter(letter, font, save=True):
    img = Image.new('RGB', (100, 100), 'white')

    draw = ImageDraw.Draw(img)
    draw.text((0,0), letter, font=font, fill='#000000')

    if save:
        img.save("imgs/{}.png".format(letter), 'PNG')

    return img


def count_black_pixels(img):
    pixels = list(img.getdata())
    return len(filter(lambda rgb: sum(rgb) == 0, pixels))


def available_fonts():
    fontdir = '/Users/alex/Desktop/English'
    for root, dirs, filenames in os.walk(fontdir):
        for name in filenames:
            path = os.path.join(root, name)
            try:
                yield ImageFont.truetype(path, 100)
            except IOError:
                pass


def letter_statistics(counts):
    for letter, counts in sorted(counts.iteritems()):
        n = len(counts)
        mean = sum(counts) / n
        sd = sqrt(sum((x - mean) ** 2 for x in counts) / n)
        yield letter, mean, sd


def main():
    counts = defaultdict(list)

    for letter in alphabet:
        for font in available_fonts():
            img = draw_letter(letter, font, save=False)
            count = count_black_pixels(img)
            counts[letter].append(count)

    for letter, mean, sd in letter_statistics(counts):
        print u"{0}: {1:.2f} ± {2:.2f}".format(letter, mean, sd)


if __name__ == '__main__':
    main()

That’s all. Now on to more productive things.

Thanks for reading – if you liked this, you can follow me on Twitter.