mirror of
https://github.com/simon987/od-database.git
synced 2025-04-10 14:06:45 +00:00
Fix captcha part 1: more human readable (but less cool)
This commit is contained in:
parent
32c1c861ad
commit
e8965497d4
196
captcha.py
196
captcha.py
@ -1,20 +1,10 @@
|
||||
import os
|
||||
import random
|
||||
import string
|
||||
|
||||
import numpy
|
||||
import pylab
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
import mpl_toolkits.mplot3d.axes3d as axes3d
|
||||
import io
|
||||
from wand.image import Image as WImage
|
||||
from flask import request, session
|
||||
|
||||
import config
|
||||
from common import logger
|
||||
|
||||
SIZE = (60, 20)
|
||||
with open("words.txt") as f:
|
||||
WORDS = f.read().splitlines(keepends=False)
|
||||
|
||||
|
||||
def get_code():
|
||||
@ -51,41 +41,175 @@ def verify():
|
||||
return False
|
||||
|
||||
|
||||
cfg = {
|
||||
"image": {
|
||||
"size": (200, 72),
|
||||
"supersampling": 2
|
||||
},
|
||||
"noise": {
|
||||
"min": 100,
|
||||
"max": 250
|
||||
},
|
||||
"colors": {
|
||||
"green": [(1, 51, 1), (34, 204, 34)],
|
||||
"yellow": [(67, 67, 1), (221, 221, 0)],
|
||||
"cyan": [(17, 51, 85), (85, 187, 254)],
|
||||
"magenta": [(51, 1, 51), (254, 0, 254)],
|
||||
"red": [(67, 1, 1), (254, 68, 68)],
|
||||
"orange": [(68, 51, 1), (255, 153, 0)]
|
||||
},
|
||||
"lines": {
|
||||
"back_thin": {"n": 3, "w": 5},
|
||||
"back_thick": {"n": 3, "w": 6},
|
||||
"back_positions": [
|
||||
{
|
||||
"ax": (0, 10),
|
||||
"ay": (0, 36),
|
||||
"bx": (150, 200),
|
||||
"by": (18, 50)
|
||||
},
|
||||
{
|
||||
"ax": (0, 10),
|
||||
"ay": (18, 50),
|
||||
"bx": (150, 200),
|
||||
"by": (0, 17)
|
||||
}
|
||||
],
|
||||
"front_horizontal_thin": {"n": 2, "w": 3},
|
||||
"front_horizontal_thick": {"n": 2, "w": 4},
|
||||
"front_horizontal_positions": [
|
||||
{
|
||||
"ax": (0, 20),
|
||||
"ay": (0, 34),
|
||||
"bx": (150, 200),
|
||||
"by": (18, 50)
|
||||
},
|
||||
{
|
||||
"ax": (0, 20),
|
||||
"ay": (18, 72),
|
||||
"bx": (140, 200),
|
||||
"by": (0, 36)
|
||||
},
|
||||
],
|
||||
"front_vertical": {"n": 2, "w": 4},
|
||||
"front_vertical_positions": {
|
||||
"outside": 5,
|
||||
"font_width": 13,
|
||||
"ay": (0, 16),
|
||||
"by": (54, 72)
|
||||
}
|
||||
},
|
||||
"text": {
|
||||
"font": {
|
||||
"path": "static/Hack-Regular.ttf",
|
||||
"size": 60,
|
||||
"outline": [1, 2]
|
||||
},
|
||||
"letters": {
|
||||
"3": {
|
||||
"count": 3,
|
||||
"x_min": 35,
|
||||
"x_max": 50,
|
||||
"y_min": -5,
|
||||
"y_max": 8
|
||||
},
|
||||
"4": {
|
||||
"count": 4,
|
||||
"x_min": 20,
|
||||
"x_max": 35,
|
||||
"y_min": -5,
|
||||
"y_max": 8
|
||||
},
|
||||
"5": {
|
||||
"count": 5,
|
||||
"x_min": 5,
|
||||
"x_max": 20,
|
||||
"y_min": -5,
|
||||
"y_max": 8
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size = cfg["image"]["size"]
|
||||
c = cfg["image"]["supersampling"]
|
||||
|
||||
# Additional config
|
||||
letter_count = "4"
|
||||
|
||||
|
||||
def horizontal_lines(draw, c, line_par, line_pos, fill):
|
||||
for _ in range(line_par["n"]):
|
||||
pos = random.randrange(0, len(line_pos))
|
||||
ax = random.randint(*line_pos[pos]["ax"])
|
||||
ay = random.randint(*line_pos[pos]["ay"])
|
||||
bx = random.randint(*line_pos[pos]["bx"])
|
||||
by = random.randint(*line_pos[pos]["by"])
|
||||
draw.line([(ax*c, ay*c), (bx*c, by*c)], width=line_par["w"]*c, fill=fill)
|
||||
|
||||
|
||||
def make_captcha():
|
||||
word = random.choice(WORDS)
|
||||
path = get_path(word)
|
||||
|
||||
logger.info("generating CAPTCHA: " + word)
|
||||
color_name, color = random.choice(list(cfg["colors"].items()))
|
||||
text = ''.join(random.choices(string.ascii_uppercase + string.digits, k=cfg["text"]["letters"][letter_count]["count"]))
|
||||
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
path = get_path(text)
|
||||
|
||||
image = Image.new('L', SIZE, 255)
|
||||
image_draw = ImageDraw.Draw(image)
|
||||
font = ImageFont.truetype("static/Hack-Regular.ttf", 12)
|
||||
w = size[0]*c
|
||||
h = size[1]*c
|
||||
|
||||
image_draw.text((5, 3), word, font=font)
|
||||
img = Image.new('RGB', (w, h))
|
||||
pixels = img.load()
|
||||
|
||||
x, y = numpy.meshgrid(range(SIZE[0]), range(SIZE[1]))
|
||||
z = 1 - numpy.asarray(image) / 255
|
||||
# noise
|
||||
for x in range(w):
|
||||
for y in range(h):
|
||||
rcol = random.randint(cfg["noise"]["min"], cfg["noise"]["max"])
|
||||
pixels[x, y] = (rcol, rcol, rcol)
|
||||
|
||||
fig = pylab.figure()
|
||||
ax = axes3d.Axes3D(fig)
|
||||
ax.plot_wireframe(x, -y, z, rstride=1, cstride=1)
|
||||
ax.set_zlim((0, 20))
|
||||
ax.set_axis_off()
|
||||
pylab.close(fig)
|
||||
# background lines
|
||||
draw = ImageDraw.Draw(img)
|
||||
|
||||
buf = io.BytesIO()
|
||||
fig.savefig(buf, dpi=150)
|
||||
buf.seek(0)
|
||||
image.close()
|
||||
horizontal_lines(draw, c, cfg["lines"]["back_thin"], cfg["lines"]["back_positions"], color[0])
|
||||
horizontal_lines(draw, c, cfg["lines"]["back_thick"], cfg["lines"]["back_positions"], color[0])
|
||||
|
||||
with WImage(blob=buf.read()) as img:
|
||||
img.trim()
|
||||
img.save(filename=path)
|
||||
# text
|
||||
ctx = cfg["text"]["font"]
|
||||
font = ImageFont.truetype(ctx["path"], ctx["size"]*c)
|
||||
outline = random.choice(ctx["outline"])
|
||||
|
||||
return word
|
||||
ctx = cfg["text"]["letters"][letter_count]
|
||||
x = random.randint(ctx["x_min"], ctx["x_max"])
|
||||
y = random.randint(ctx["y_min"], ctx["y_max"])
|
||||
draw.text((x*c-outline*c, y*c-outline*c), text, color[0], font=font)
|
||||
draw.text((x*c-outline*c, y*c), text, color[0], font=font)
|
||||
draw.text((x*c-outline*c, y*c+outline*c), text, color[0], font=font)
|
||||
draw.text((x*c, y*c-outline*c), text, color[0], font=font)
|
||||
draw.text((x*c, y*c+outline*c), text, color[0], font=font)
|
||||
draw.text((x*c+outline*c, y*c-outline*c), text, color[0], font=font)
|
||||
draw.text((x*c+outline*c, y*c), text, color[0], font=font)
|
||||
draw.text((x*c+outline*c, y*c+outline*c), text, color[0], font=font)
|
||||
draw.text((x*c, y*c), text, color[1], font=font)
|
||||
|
||||
# foreground lines
|
||||
horizontal_lines(draw, c, cfg["lines"]["front_horizontal_thin"], cfg["lines"]["front_horizontal_positions"], color[1])
|
||||
horizontal_lines(draw, c, cfg["lines"]["front_horizontal_thick"], cfg["lines"]["front_horizontal_positions"], color[1])
|
||||
|
||||
# vertical lines
|
||||
line_par = cfg["lines"]["front_vertical"]
|
||||
line_pos = cfg["lines"]["front_vertical_positions"]
|
||||
|
||||
for _ in range(line_par["n"]):
|
||||
ax = random.randint(x-line_pos["outside"], x+line_pos["outside"] + cfg["text"]["letters"][letter_count]["count"]*line_pos["font_width"])
|
||||
bx = ax + random.randint(-line_pos["font_width"], line_pos["font_width"])
|
||||
ay = random.randint(*line_pos["ay"])
|
||||
by = random.randint(*line_pos["by"])
|
||||
draw.line([(ax*c, ay*c), (bx*c, by*c)], width=line_par["w"]*c, fill=color[1])
|
||||
|
||||
img.thumbnail(cfg["image"]["size"], Image.ANTIALIAS)
|
||||
img.save(path, "png")
|
||||
|
||||
return text
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
Loading…
x
Reference in New Issue
Block a user