138 lines
3.1 KiB
Python
138 lines
3.1 KiB
Python
import random
|
||
import os
|
||
from PIL import Image
|
||
import base64
|
||
from io import BytesIO
|
||
from collections import deque
|
||
|
||
import os
|
||
|
||
def generate_layers(images, index=0):
|
||
if len(images) == 0:
|
||
return ''
|
||
|
||
|
||
return f'''<label>
|
||
<input id="layer{index}" type="radio" name="captcha{index}" value="{index}">
|
||
<img src="data:image/png;base64,{images[0]}"></img>
|
||
{generate_layers(images[1:], index+1)}
|
||
</label>
|
||
'''
|
||
|
||
def generate_captcha_html(images):
|
||
html = ''
|
||
html_prelude = '''
|
||
<style>
|
||
.grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 0fr);
|
||
}
|
||
|
||
div > div {
|
||
padding: 10px;
|
||
}
|
||
|
||
img {
|
||
background-color: white;
|
||
}
|
||
'''
|
||
|
||
generated_css = ''
|
||
for index, image in enumerate(images):
|
||
generated_css += f'''
|
||
|
||
/* ---- CURRENT LAYER: {index} ---- */
|
||
|
||
#layer{index}[type=radio] {{
|
||
position: absolute;
|
||
opacity: 0;
|
||
width: 0;
|
||
height: 0;
|
||
}}
|
||
|
||
#layer{index}[type=radio] + img {{
|
||
cursor: pointer;
|
||
padding: 5px;
|
||
position: absolute;
|
||
background-color: white;
|
||
}}
|
||
|
||
/* CHECKED STYLES */
|
||
#layer{index}[type=radio]:checked + img {{
|
||
z-index: -1000;
|
||
}}
|
||
'''
|
||
middle_boilerplate = '''
|
||
|
||
#root {
|
||
width: fit-content;
|
||
/* To adjust the height as well */
|
||
height: fit-content;
|
||
}
|
||
|
||
</style>
|
||
<body>
|
||
<div id="root">
|
||
<form method="POST">
|
||
<div class='grid'>
|
||
|
||
'''
|
||
|
||
generated_recursive_layers = generate_layers(images)
|
||
html_postscript = '''
|
||
<input style="position: absolute; top: 300px" type="submit">Submit</input>
|
||
</form>
|
||
</div>
|
||
</body>'''
|
||
html = html_prelude + generated_css + middle_boilerplate + generated_recursive_layers + html_postscript
|
||
return html
|
||
|
||
# Angles we'll rotate the original by
|
||
# when we create n rotations
|
||
def calculate_angles(n):
|
||
return list(range(0, 360, 360 // n))
|
||
|
||
def rotate_img(img_path, angle):
|
||
original_img = Image.open(img_path)
|
||
rotated = original_img.rotate(angle)
|
||
buffered = BytesIO()
|
||
rotated.save(buffered, format="PNG")
|
||
b64_rot = base64.b64encode(buffered.getvalue())
|
||
return b64_rot.decode("utf-8")
|
||
|
||
def captchafy(img_path, n=6):
|
||
angles = calculate_angles(n)
|
||
|
||
rotated_imgs = deque(
|
||
[rotate_img(img_path, angle) for angle in angles][::-1] * 3
|
||
)
|
||
random_shift = random.randint(1, n)
|
||
|
||
rotated_imgs.rotate(-random_shift)
|
||
return random_shift % n, rotated_imgs
|
||
|
||
|
||
def random_image(dir='images/'):
|
||
dir_contents = os.listdir(dir)
|
||
random_image = random.choice(dir_contents)
|
||
return dir + random_image
|
||
|
||
## lol what does this do???
|
||
def resize_dir(size=150, dir='images/'):
|
||
for img_file in os.listdir(dir):
|
||
img = Image.open(dir + img_file)
|
||
width, height = img.size
|
||
print(width, height)
|
||
if not (width > size or height > size):
|
||
continue
|
||
|
||
img.thumbnail((size, size),Image.ANTIALIAS)
|
||
img.save(dir + img_file)
|
||
|
||
if __name__ == '__main__':
|
||
img_path = random_image(dir='images/')
|
||
answer, options = captchafy(img_path)
|
||
for i, img_b64 in enumerate(options):
|
||
print(img_b64)
|
||
im = Image.open(BytesIO(base64.b64decode(img_b64)))
|
||
im.save(f'images/image{i}.png') |