Created
April 17, 2024 00:36
-
-
Save MarkGamed7794/0aa8d70eee6bd59f75c289c794f33f14 to your computer and use it in GitHub Desktop.
California DMV Bad Apple generator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Requires Pillow to be installed. | |
Place the licence plate font in the same directory as this file | |
with the name "licenceplate.ttf", then run it. It should create | |
a file named "font_atlas.png", which is used when generating | |
the frames. | |
""" | |
from PIL import Image, ImageFont, ImageDraw # type: ignore | |
import math | |
CHAR_SIZE = 360 // 10 | |
CHAR_SET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 " | |
CHAR_AMOUNT = len(CHAR_SET) | |
GRID_SIZE = math.ceil(math.sqrt(CHAR_AMOUNT)) | |
with Image.new("RGBA", (CHAR_SIZE * GRID_SIZE, CHAR_SIZE * GRID_SIZE), (0, 0, 0, 255)) as img: | |
artist = ImageDraw.Draw(img) | |
font = ImageFont.truetype("licenseplate.ttf", size=CHAR_SIZE) | |
for i, char in enumerate(CHAR_SET): | |
x = (i % GRID_SIZE) * CHAR_SIZE | |
y = (i // GRID_SIZE) * CHAR_SIZE | |
artist.text((x, y), char, font=font, fill=(255, 255, 255, 255)) | |
img.save("font_atlas.png") | |
img.show() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Requires Pillow to be installed. | |
Run this in a directory set up like so: | |
* source\ | |
- apple-0001.jpeg | |
- apple-0002.jpeg | |
- apple-0003.jpeg | |
... (etc. up to however many frames you have. i exported it at 20FPS and had a total of 4,383 frames) | |
- font_atlas.png (generated by create_atlas.py) | |
- template.png (the base license place image) | |
- licenceplate.ttf (the licence plate font) | |
- generate_frames.py (this file) | |
Each frame in the source video should be 480x320. The template should be 1280x720. | |
This will create a directory named "frames\" with each frame in it named "0001.jpeg", "0002.jpeg", etc. | |
Adjust the number of frames by changing FRAME_COUNT near the bottom. | |
Sorry for the code quality. It's not a very efficient program (generating the video took my laptop about two hours). | |
""" | |
from PIL import Image, ImageFont, ImageDraw # type: ignore | |
import math | |
import time | |
CHAR_SIZE = 360 // 10 | |
CHAR_SET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 " | |
CHAR_AMOUNT = len(CHAR_SET) | |
GRID_SIZE = math.ceil(math.sqrt(CHAR_AMOUNT)) | |
CHAR_DIMENSIONS = (16, 36) | |
PARAGRAPH_DIMENSIONS = (30, 10) | |
FONT_SIZE = 32 | |
DRAW_DIMENSIONS = (16, 36) | |
DRAW_OFFSET = (((1280 - DRAW_DIMENSIONS[0] * 30) // 2) + 10, 264) | |
font_atlas = Image.open("font_atlas.png").convert("L") | |
template = Image.open("template_modified.png") | |
def evaluate_difference(imga: Image, imgb: Image): | |
# grounds for improvement | |
a, b = list(imga.getdata()), list(imgb.getdata()) | |
difference_sum = 0 | |
#print(imga.mode, imgb.mode) | |
for n in range(len(a)): | |
difference_sum += abs(a[n] - b[n]) | |
#print(f"Difference: {difference_sum}") | |
return difference_sum | |
def best_match(crop: Image): | |
best_char = " " | |
best_value = 9999999999999999 | |
for i, char in enumerate(CHAR_SET): | |
x = (i % GRID_SIZE) * CHAR_SIZE | |
y = (i // GRID_SIZE) * CHAR_SIZE | |
with font_atlas.crop((x, y, x + CHAR_DIMENSIONS[0], y + CHAR_DIMENSIONS[1])) as letter: | |
value = evaluate_difference(crop, letter) | |
if(value < best_value): | |
best_char = char | |
best_value = value | |
#print(f"Best Fit: {char} (score {best_value})") | |
#raise RuntimeError | |
return best_char | |
font = ImageFont.truetype("licenseplate.ttf", size=FONT_SIZE) | |
start_time = time.time() | |
FRAME_NUM = 1 | |
FRAME_COUNT = 4383 | |
while FRAME_NUM <= FRAME_COUNT: | |
if(FRAME_NUM > 1): | |
fps = 1 / ((time.time() - start_time) / (FRAME_NUM - 1)) | |
timeleft = math.floor((FRAME_COUNT - FRAME_NUM) / fps) | |
print("Frame %04d/%04d (%.4f FPS, ETA %02d:%02d:%02d)" % (FRAME_NUM, FRAME_COUNT, fps, (timeleft // 3600), (timeleft // 60) % 60, timeleft % 60)) | |
with Image.open("source/apple-%04d.jpeg" % FRAME_NUM).convert("L") as frame: | |
#frame.show() | |
out_frame = template.copy() | |
artist = ImageDraw.Draw(out_frame) | |
for y in range(PARAGRAPH_DIMENSIONS[1]): | |
img_y = y * CHAR_DIMENSIONS[1] | |
draw_y = y * DRAW_DIMENSIONS[1] + DRAW_OFFSET[1] | |
for x in range(PARAGRAPH_DIMENSIONS[0]): | |
img_x = x * CHAR_DIMENSIONS[0] | |
with frame.crop((img_x, img_y, img_x + CHAR_DIMENSIONS[0], img_y + CHAR_DIMENSIONS[1])) as crop: | |
best_char = best_match(crop) | |
print(best_match(crop), end="") | |
draw_x = x * DRAW_DIMENSIONS[0] + DRAW_OFFSET[0] | |
artist.text((draw_x, draw_y), best_char, (31, 41, 100), font=font) | |
print("") | |
#out_frame.show() | |
out_frame.save("frames/%04d.jpeg" % FRAME_NUM) | |
out_frame.close() | |
FRAME_NUM += 1 | |
font_atlas.close() | |
template.close() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment