Gabor Cselle commited on
Commit
5d8063e
1 Parent(s): c99617f

HuggingFace commit

Browse files
Files changed (6) hide show
  1. .gitattributes +1 -0
  2. .gitignore +2 -1
  3. README.md +5 -1
  4. consts.py +2 -0
  5. gen_sample_data.py +60 -44
  6. visualize.ipynb +0 -0
.gitattributes ADDED
@@ -0,0 +1 @@
 
 
1
+ *.pth filter=lfs diff=lfs merge=lfs -text
.gitignore CHANGED
@@ -4,4 +4,5 @@ train_test_images
4
  .ipynb_checkpoints/visualize-checkpoint.ipynb
5
  font_identifier_model.pth
6
  *.pyc
7
- __pycache__
 
 
4
  .ipynb_checkpoints/visualize-checkpoint.ipynb
5
  font_identifier_model.pth
6
  *.pyc
7
+ __pycache__
8
+ google_fonts
README.md CHANGED
@@ -8,5 +8,9 @@ Follow along:
8
  - [On Twitter](https://twitter.com/gabor/status/1722300841691103467)
9
 
10
  Generate sample images (note this will work only on Mac): [gen_sample_data.py](gen_sample_data.py)
 
11
  Arrange test images into test and train: [arrange_train_test_images.py](arrange_train_test_images.py)
12
- Train a ResNet18 on the data: [train_font_identifier.py](train_font_identifier.py)
 
 
 
 
8
  - [On Twitter](https://twitter.com/gabor/status/1722300841691103467)
9
 
10
  Generate sample images (note this will work only on Mac): [gen_sample_data.py](gen_sample_data.py)
11
+
12
  Arrange test images into test and train: [arrange_train_test_images.py](arrange_train_test_images.py)
13
+
14
+ Train a ResNet18 on the data: [train_font_identifier.py](train_font_identifier.py)
15
+
16
+ License: MIT
consts.py CHANGED
@@ -30,3 +30,5 @@ GEN_IMAGES_DIR = './generated_images'
30
  TRAIN_TEST_IMAGES_DIR = './train_test_images'
31
  # where to grab the font files from
32
  FONT_FILE_DIRS = ['/System/Library/Fonts/', '/System/Library/Fonts/Supplemental/']
 
 
 
30
  TRAIN_TEST_IMAGES_DIR = './train_test_images'
31
  # where to grab the font files from
32
  FONT_FILE_DIRS = ['/System/Library/Fonts/', '/System/Library/Fonts/Supplemental/']
33
+ # where to grab the Google Fonts, all of which are allowed
34
+ GOOGLE_FONTS_DIR = "./google_fonts"
gen_sample_data.py CHANGED
@@ -6,66 +6,82 @@ from PIL import Image, ImageDraw, ImageFont
6
  import nltk
7
  from nltk.corpus import brown
8
  import random
9
- from consts import FONT_ALLOWLIST, IMAGES_PER_FONT, GEN_IMAGES_DIR, FONT_FILE_DIRS
10
 
11
  # Download the necessary data from nltk
12
- nltk.download('brown')
13
 
14
  os.makedirs(GEN_IMAGES_DIR, exist_ok=True)
15
 
16
- all_brown_words = sorted(set(brown.words(categories='news')))
17
-
18
- def wrap_text(text, line_length=10):
19
  """Wraps the provided text every 'line_length' words."""
20
  words = text.split()
21
  return "\n".join([" ".join(words[i:i+line_length]) for i in range(0, len(words), line_length)])
22
 
23
- def random_prose_text(words, num_words=200):
24
- """Returns a random selection of 'num_words' words from the provided list of words."""
25
- random_words = " ".join(random.sample(words, num_words))
26
- return wrap_text(random_words)
27
-
28
- def random_code_text(base_code, num_lines=15):
29
- """Returns a random selection of 'num_lines' lines from the provided code."""
30
- lines = base_code.split("\n")
31
- return "\n".join(random.sample(lines, min(num_lines, len(lines))))
32
 
33
  def main():
 
 
 
 
 
 
 
 
 
 
34
  for font_dir in FONT_FILE_DIRS:
35
  for font_file in os.listdir(font_dir):
36
  if font_file.endswith('.ttf') or font_file.endswith('.ttc'):
37
  font_path = os.path.join(font_dir, font_file)
38
  font_name = font_file.split('.')[0]
39
- if font_name not in FONT_ALLOWLIST:
40
- continue
41
- # Output the font name so we can see the progress
42
- print(font_path, font_name)
43
-
44
- if font_file.endswith('.ttc'):
45
- # ttc fonts have multiple fonts in one file, so we need to specify which one we want
46
- font = ImageFont.truetype(font_path, random.choice(range(32, 128)), index=0)
47
- else:
48
- # ttf fonts have only one font in the file
49
- font_size = random.choice(range(32, 128)) # Increased minimum font size
50
- font = ImageFont.truetype(font_path, font_size)
51
-
52
- # Counter for the image filename
53
- j = 0
54
- for i in range(IMAGES_PER_FONT): # Generate 50 images per font - reduced to 10 for now to make things faster
55
- prose_sample = random_prose_text(all_brown_words)
56
-
57
- for text in [prose_sample]:
58
- img = Image.new('RGB', (800, 400), color="white") # Canvas size
59
- draw = ImageDraw.Draw(img)
60
-
61
- # Random offsets, but ensuring that text isn't too far off the canvas
62
- offset_x = random.randint(-20, 10)
63
- offset_y = random.randint(-20, 10)
64
- draw.text((offset_x, offset_y), text, fill="black", font=font)
65
-
66
- j += 1
67
- output_file = os.path.join(GEN_IMAGES_DIR, f"{font_name}_{j}.png")
68
- img.save(output_file)
 
 
 
 
 
 
 
 
 
 
 
69
 
70
  if __name__ == '__main__':
71
  main()
 
6
  import nltk
7
  from nltk.corpus import brown
8
  import random
9
+ from consts import FONT_ALLOWLIST, IMAGES_PER_FONT, GEN_IMAGES_DIR, FONT_FILE_DIRS, GOOGLE_FONTS_DIR
10
 
11
  # Download the necessary data from nltk
12
+ nltk.download('inaugural')
13
 
14
  os.makedirs(GEN_IMAGES_DIR, exist_ok=True)
15
 
16
+ def wrap_text(text, line_length=4):
 
 
17
  """Wraps the provided text every 'line_length' words."""
18
  words = text.split()
19
  return "\n".join([" ".join(words[i:i+line_length]) for i in range(0, len(words), line_length)])
20
 
21
+ def random_prose_text(line_length=4):
22
+ """Returns a random snippet from the Gutenberg corpus."""
23
+ corpus = nltk.corpus.inaugural.raw()
24
+ start = random.randint(0, len(corpus) - 800)
25
+ end = start + 800
26
+ return wrap_text(corpus[start:end], line_length=line_length)
 
 
 
27
 
28
  def main():
29
+ # Collect all allowed font files
30
+ font_files = []
31
+ # all of the Google fonts are allowed, no matter what
32
+ for font_file in os.listdir(GOOGLE_FONTS_DIR):
33
+ if font_file.endswith('.ttf') or font_file.endswith('.ttc'):
34
+ font_path = os.path.join(GOOGLE_FONTS_DIR, font_file)
35
+ font_name = font_file.split('.')[0]
36
+ font_files.append((font_path, font_name))
37
+
38
+ # for the system font directories, use the FONT_ALLOWLIST
39
  for font_dir in FONT_FILE_DIRS:
40
  for font_file in os.listdir(font_dir):
41
  if font_file.endswith('.ttf') or font_file.endswith('.ttc'):
42
  font_path = os.path.join(font_dir, font_file)
43
  font_name = font_file.split('.')[0]
44
+ if font_name in FONT_ALLOWLIST:
45
+ font_files.append((font_path, font_name))
46
+
47
+ # Generate images for each font file
48
+ for font_path, font_name in font_files:
49
+ # Output the font name so we can see the progress
50
+ print(font_path, font_name)
51
+
52
+ # Random font size
53
+ font_size = random.choice(range(18, 72))
54
+
55
+ if font_path.endswith('.ttc'):
56
+ # ttc fonts have multiple fonts in one file, so we need to specify which one we want
57
+ font = ImageFont.truetype(font_path, font_size, index=0)
58
+ else:
59
+ # ttf fonts have only one font in the file
60
+ font = ImageFont.truetype(font_path, font_size)
61
+
62
+ # Counter for the image filename
63
+ j = 0
64
+ for i in range(IMAGES_PER_FONT): # Generate 50 images per font - reduced to 10 for now to make things faster
65
+ # Determine the number of words that will fit on a line
66
+ font_avg_char_width = font.getbbox('x')[2]
67
+ words_per_line = int(800 / (font_avg_char_width*5))
68
+ prose_sample = random_prose_text(line_length=words_per_line)
69
+
70
+ for text in [prose_sample]:
71
+ img = Image.new('RGB', (800, 400), color="white") # Canvas size
72
+ draw = ImageDraw.Draw(img)
73
+
74
+ # Random offsets, but ensuring that text isn't too far off the canvas
75
+ offset_x = random.randint(-20, 10)
76
+ offset_y = random.randint(-20, 10)
77
+
78
+ # vary the line height
79
+ line_height = random.uniform(0, 1.25) * font_size
80
+ draw.text((offset_x, offset_y), text, fill="black", font=font, spacing=line_height)
81
+
82
+ j += 1
83
+ output_file = os.path.join(GEN_IMAGES_DIR, f"{font_name}_{j}.png")
84
+ img.save(output_file)
85
 
86
  if __name__ == '__main__':
87
  main()
visualize.ipynb CHANGED
The diff for this file is too large to render. See raw diff