Python 使用不同字体创建3D验证码
我对制作3D CAPTCHA感兴趣,我使用的是单一字体,如下所示:Python 使用不同字体创建3D验证码,python,arrays,numpy,matplotlib,Python,Arrays,Numpy,Matplotlib,我对制作3D CAPTCHA感兴趣,我使用的是单一字体,如下所示: import string from matplotlib.font_manager import findSystemFonts import random from PIL import ImageFont, Image, ImageDraw def rand_font(fonts=[], fontpaths=None, fontext='ttf', font_encoding='', min_size=24, max_s
import string
from matplotlib.font_manager import findSystemFonts
import random
from PIL import ImageFont, Image, ImageDraw
def rand_font(fonts=[], fontpaths=None, fontext='ttf', font_encoding='', min_size=24, max_size=36):
if fonts == []:
fonts = findSystemFonts(fontpaths, fontext)
requested_font = fonts[random.randint(0, len(fonts)-1)]
font_size = random.randint(min_size, max_size)
return ImageFont.truetype(requested_font, font_size, encoding=font_encoding)
def create_captcha(text):
def _rand_color():
colors = ['red', 'orange', 'white', 'purple', 'green', 'yellow']
return colors[random.randint(0, len(colors)-1)]
width = random.randint(400, 700)
height = random.randint(150, 200)
angle = angle if angle else uniform(-20, 20)
font = rand_font()
text_width, text_height = font.getsize(text)
img = Image.new("L", (text_width * 3, text_height * 3), "white")
draw = ImageDraw.Draw(img)
draw.text((text_width, text_height), text, font=font)
fig = pylab.figure(figsize=(width/100.0, height/100.0), dpi=4000)
axes = Axes3D(fig)
X, Y = numpy.meshgrid(range(img.size[0]), range(img.size[1]))
Z = 1 - numpy.asarray(img) / 255
func = Axes3D.plot_surface if random.randint(0,1) == 0 else Axes3D.plot_wireframe
func(axes, X, -Y, Z, rstride=1, cstride=1, color=_rand_color())
axes.set_zlim((-3, 3))
axes.set_xlim((text_width * 1.1, text_width * 1.9))
axes.set_ylim((-text_height * 1.9, -text_height* 1.1))
axes.set_axis_off()
axes.view_init(elev=60, azim=-90)
这很好,它能让我创造出这样的东西:和
我想做的是创建一个验证码,每个字符使用不同的字体和大小,并将每个字符的
y
偏移一点
由于它基于numpy
数组进行打印,因此我尝试循环遍历文本中的每个字符,如下所示:
prev_x = 0
x = []
y = []
z = []
for character in text:
X, Y = numpy.meshgrid(range(prev_x, prev_x + img.size[0]), range(img.size[1]))
for v in X.tolist():
x.append(v)
for v in Y.tolist():
y.append(v)
# same for z
prev_x += 40 # trying to offset the characters by an x value so they dont overlap
x = numpy.array(x)
# same for y and z to convert back to numpy array
Axes3D.plot_wireframe(x, -y, z, rstride=1, cstride=1)
这导致
形状不匹配:两个或多个数组在轴1上具有不兼容的维度。
这让我感到困惑,因为我认为维度会完全相同,因为我对每个数组都执行相同的调用。我是numpy和3D的新手,所以如果有人有建议,请告诉我 你是如何开始这项工作的,肯定有很多很酷的因素,而且大部分都在那里;下面是我制作的mods,它可以根据每个字符获得随机字体和可能的颜色。我确实改变了一些尺寸,以更好地适应我的屏幕
from matplotlib.font_manager import findSystemFonts
import random
from PIL import ImageFont, Image, ImageDraw
import numpy
from mpl_toolkits.mplot3d.axes3d import Axes3D
import matplotlib.pyplot as plt
def rand_font(fonts=[], fontpaths=None, fontext='ttf', font_encoding='', min_size=24, max_size=36):
if fonts == []:
fonts = findSystemFonts(fontpaths, fontext)
requested_font = fonts[random.randint(0, len(fonts)-1)]
font_size = random.randint(min_size, max_size)
return ImageFont.truetype(requested_font, font_size, encoding=font_encoding)
def create_captcha(text):
def _rand_color():
colors = ['red', 'orange', 'purple', 'green', 'yellow']
return colors[random.randint(0, len(colors)-1)]
# First font just gets the general offsets
font = rand_font()
text_width, text_height = font.getsize(text)
# Dont draw text here first
img = Image.new("L", (text_width * 3, text_height * 3), "white")
draw = ImageDraw.Draw(img)
fig = plt.figure(figsize=(12, 8))
axes = Axes3D(fig)
# Do this way if you want random fonts AND colors
#=================
prev_x = 0
for character in text:
cfont = rand_font()
char_wid, char_height = cfont.getsize(character)
draw.text((prev_x+text_width, text_height), character, font=cfont)
X, Y = numpy.meshgrid(range(prev_x+text_width, prev_x+text_width+char_wid),
range(text_height, text_height+char_height))
Z = 1 - numpy.asarray(img.crop((prev_x+text_width, text_height,
prev_x+text_width+char_wid,
text_height+char_height))) / 255
axes.plot_wireframe(X, -Y, Z, rstride=1, cstride=1, color=_rand_color())
prev_x += char_wid # trying to offset the characters by an x value so they dont overlap
#=================
# Do this way if you want just random fonts on each letter all one color
#=================
prev_x = 0
for character in text:
cfont = rand_font()
char_wid, char_height = cfont.getsize(character)
draw.text((prev_x+text_width, text_height), character, font=cfont)
prev_x += char_wid # trying to offset the characters by an x value so they dont overlap
X, Y = numpy.meshgrid(range(img.size[0]), range(img.size[1]))
Z = 1 - numpy.asarray(img) / 255
axes.plot_wireframe(X, -Y, Z, rstride=1, cstride=1, color=_rand_color())
#=================
axes.set_zlim((-3, 3))
axes.set_xlim((text_width * 1.1, text_width * 1.9))
axes.set_ylim((-text_height * 1.9, -text_height* 1.1))
axes.set_axis_off()
axes.view_init(elev=60, azim=-90)
plt.show()
create_captcha('TEST')
多字体单色版本如下所示:
多字体多色版本如下所示:
如果你把它做成一个曲面并改变视角/高度以避开背景,它可能会看起来更好。。。也许是这样的:
至少,这应该是一个起点 你为什么不仔细检查一下尺寸呢?您在这里跳过了一些代码,因此无法对其进行准确调试。我检查了尺寸,它们不匹配。我没有跳过代码,我提供了一切。第二部分可以很容易地将pastad复制到第一部分中—您肯定跳过了代码—为了使其正常工作,我不得不添加大量导入内容,并替换您写有“#z相同”之类的行。另外,您的
Axes3D.plot\u wireframe()
调用应该是axes.plot\u wireframe()
,因此,除非您实际上没有复制并粘贴真正的代码到问题中,否则其他事情会完全混乱。鉴于您的问题标题与问题内容不匹配,我不确定您到底在寻找什么。它是在这里键入的,因此我为z编写了#相同的。很抱歉,让您导入模块给您带来了不便Axes3D.plot\u线框
应使用axes
作为self
调用。原因可以在第一个示例中看到,我将方法分配给它,然后调用它。问题是我如何实现这一点。正如我所说,我的代码不适用于不同的字体,相反,我提供了足够的上下文,让一个像样的开发人员能够理解我要做的事情。如果你想让我复制/粘贴我能复制的所有东西,但结果不会有任何作用,因为它不工作。实际上,在我提到的更改之后,我让它工作得很好,字体和颜色都是随机的。当我回到一台真正的电脑上时,我会把它贴出来。