pillow教程
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了pillow教程相关的知识,希望对你有一定的参考价值。
参考技术A 在Python图像库中最重要的类是同名模块中定义的 Image 类。您可以利用以下方法创造该类的实例:从文件中导入图像、处理其他的图像以及从零开始创建图像。从文件中导入图像,使用在 Image 模块中的 open() 函数:
如果成功。该函数返回一个 Image 对象。您现在可以使用实例的属性来检查文件内容了:
format 属性识别图像的来源。如果图像不是从图像中读取,则该属性设置为None。 size 属性是一个一个包含宽和高(像素)的二元组。 mode 属性定义图像频段的数量和名称,以及像素的类型和深度。常用的模式(mode)为表示灰色图像的“L”,表示真彩色图像的处理问题“RGB”,以及印前图像的画面“CMYK”。
如何图像不能被打开,则会报出 OSError 异常。
一旦您有 Image 类的实例,您可以使用类中定义的方法来处理和操作图像。比如,让我们显示导入的图像:
show() 的标准版本不是非常的高效,因为该函数会把图像保存到一个临时文件并调用实用程序来显示图像。如果您没有安装一个合适的实用程序,它甚至不会起作用。虽然当它不起作用时,调试和测试是非常方便的。
下面的章节概括了该库提供的不同函数。
该Python图像库支持大量的图像文件格式。为了从磁盘中阅读文件,使用在 Image 模块中的 open() 。您不需要知道打开文件的文件格式。该库能够自动地根据文件的内容决定格式。
为了保存一个文件,使用 Image 类中的 save() 方法。当保存文件时,名字非常重要。除非您指定格式,该库使用文件名的后缀来发现将要使用的文件存储格式。
提供给 save() 方法的第二个参数精准地制定了一个文件的格式。如果您使用了非标准的后缀,您必须一直使用以下方式指定格式:
值得注意的是,非必要情况该库不会解码或加载栅格数据(raster data)。当您打开一个文件时,文件头将被读取用于确定文件格式以及提取如模式、尺寸等其他解码文件需要的性质,但是文件余下的部分会稍后再处理。
这意味着打开一个图像是最后的操作,它与文件大小和压缩类型无关。这里有一种简单的脚本可以块度地识别图像文件集:
Image类包含允许您操作图像内区域的方法。为了从图像中提取子矩形,使用crop()方法。
一个区域是一个4元组,其中坐标为(左,上,右,下)。该Python图像库使用左上角坐标为(0,0)的坐标系统。同样值得注意的是,坐标是指像素间的位置,因此上例中的区域正好为300x300的像素。
该区域现在能以某种方法进行处理并粘贴回去。
当将区域粘贴回去时,区域的大小必须准确地匹配给定的区域。此外,区域不能拓展到图像之外。然而,原始图像和区域的模式不必相匹。如果相同,则区域会在被粘贴前自动地转换(有关详细信息,请参阅下面的 颜色转换 部分)。
这里有一个额外的例子:
对于更高级的技巧,paste方法可以将透明掩码(transparency mask)作为可选参数。在掩码中,数值255被粘贴的图像在该位置是不透明的(即,被粘贴的图像就是原图粘贴)。数值0表示被粘贴的图像是完全透明的。在0和255之间的数值表示不同级别的透明程度。例如,粘贴一个RGBA图像并将其作为掩码会粘贴图像的不透明部分,但不会粘贴其透明背景。
该Python图像库也允许您在多频段图像中的单个频段中进行工作,例如RGB图像。split方法创造了新的图像集,每一个都包含了来自原始多频段图像的一个频段。合并函数将一个模式和图像组作为输入,并将其组合为新图像。下面示例交换了一个RGB图像的三个频段:
值得注意的是,对一个单波段图像而言, split() 返回图像本身。要在单个颜色频段上工作,您可能需要首先将图像转换为"RGB"。
PIL.Image.Image 类包含调整( resize() )和旋转( rotate() )一个图像的方法。前者通过输入元组来确定新的图片大小,后者通过输入的角度以逆时间旋转图片。
若要90度旋转图像,您即可以使用 rotate() 方法,也可以使用 transpose() 方法。后者还可以在水平或垂直轴周围翻转图像。
transpose(ROTATE)也可以和 rotate() 执行的结果相同,前提是rotate()中的expand标志设置为真,用以提供图像尺寸的相同更改。
图像转换的一种更一般的形式是通过 transform() 方法执行。
该Python图像库允许您使用convert()方法在不同的像素表示间转换图像。
该库可以在每个支持的模式和“L”以及“RGB”模式间进行转换。为了在其他模式间进行转换,您可能会使用到一个中间图像(通常为“RGB”图像)。
该Python图像库提供了大量的方法和模块用于增强图像。
ImageFilter 模块包含了许多能和 filter() 方法一起使用的预定义的增强过滤器。
point() 方法用于翻译图像的像素值(如图像对比度操作)。在多数情况下,一个函数对象期望一个传递给方法的参数。每一个像素都按照函数进行处理:
使用以上方法,您可以快速地在图像上应用任何简单的表达式。您还可以通过结合 point() 和 paste() 方法来有选择性地修改图像:
以下语法用于创造掩码:
Python仅评估确定结果所需的逻辑表达部分,并返回作为表达结果检查的最后值。因此,如果以上表达式为假(0),Python不再查看第二个操作数,并返回0。相反地,返回255。
对更先进的图像增强,您可以使用 ImageEnhance 模块中的类。一旦从图像创建,增强对象可用于快速尝试不同的设置。
您可以通过这种方式调整对比度、亮度、颜色平衡和锐度。
该Python图像库包含一些对图像序列(也称为动画支持)的基础支持。支持的序列格式包括FLI/FLC,GIF,以及一些实验格式。TIgFF文件还可以包含多个帧。
当您打开一个序列文件,PIL自动地导入序列的第一帧。您可以使用seek并告诉方法在不同帧之间移动:
如例所见,当序列结束时,您会得到一个 EOFError 异常。
下列类允许您使用for语句循环序列:
该Python图像库包含在PostScript打印机上打印图像、文本以及图形的功能。下面是一个简单的示例:
如早前描述的一样, Image 模块中的 open() 函数用于打开图像文件。在大部分情况下,您简单地传入文件名作为一个参数。Image.open能作为文本管理器:
您可以使用一个类文件对象来代替文件名。这个对戏必须实现必须实现file.read、file.seek和file.tell方法,且必须以二进制模式打开。
要从二进制数据中读取图像,请使用 Bytes10 类:
请注意,库在阅读图像头部之前会倒带文件(使用seek(0))。此外,当读取图像数据时(通过load方法),还将使用seek。如果图像文件嵌入到较大的文件中,例如tar文件,您可以使用 ContainerIO 或 TarIO 模块来访问它。
一些解码器允许您在从文件中读取图像时对其进行操作。这通常被用于创建缩略图(当速度远大于质量时)和打印到单色激光打印机(当只需要图像的灰度版本时)的解码过程。
draft()方法操纵打开但尚未加载的图像,以便尽可能与给定的模式和大小匹配。这是通过重新配置图像解码器来完成的。
这只适用于JPEG和MPO文件。
打印结果如下:
值得注意的是,生成的图像可能不会精确地匹配要求的模式和尺寸。为了确保图像不大于给定的尺寸,请使用thumbnail方法。
python 基于pillow模块生成随机图片验证码教程
效果图
我们先来看一下大致的效果图。
以上图形都是用非常基础的元素随机构成的:点,线,曲线,文本。而pillow模块远远不止这些功能,如果学好了它,真的就是你想怎么花就怎么花。 那么现在我们就去学习一下它的简单使用吧!
pillow的基本使用
安装pillow模块
pillow属第三方模块,我们需要使用pip进行下载。值得注意的是,早期该模块名为PIL,后来改成pillow。
pip install pillow
创建画布
创建画布前需要导入pillow中的Image。该模块既能生成一张图片在上面做修改,也能导入一张图片在上面做修改。在图片验证码的示例中,我们生成一张图片即可。生成图片我们使用的是Image的new方法,其包含三个参数:
- mode:图片模式(具体可见下图)
- size:图片尺寸
- color:图片颜色
实操:
# 注意这里是PIL而不是pillow
from PIL import Image, ImageDraw, ImageFont # 目前我们只需导入Image模块
if __name__ == '__main__':
# 首先创建一个画布,选择RGB模式,图片尺寸我这里设置为长400,高200,颜色为白色,等价于(255, 255, 255)
img = Image.new(mode="RGB", size=(400, 200), color="white")
我们应该如何查看生成的那张图片呢?使用show方法查看!
img.show()
因为我们定义这个画布的颜色是白色,所以使用show方法看不出效果,如需查看,将color改成其他颜色即可。
使用画笔
画笔可牛了,在上面的验证码中,点,线,曲线,文字都是画笔生成的。因此在本次案例中,我们讲解画笔中的几个图案,点,直线,圆以及文字。
讲图案前,我们得有一个画笔。使用ImageDraw的Draw方法创建画笔,指定在哪张图片上使用(Image对象)以及模式,ImageDraw需要导入才能使用。
pen = ImageDraw.Draw(img, mode="RGB")
点
点(point)有两个参数:位置和颜色。实际上就是将某个像素改成指定颜色。这里位置指的是相对于图片左上角的xy坐标。 这里点的颜色不再是用color声明,取而代之的是fill,表示填充。使用时坐标在前,颜色在后,可以不写参数名。
pen.point((200, 100), fill="black") # 表示将坐标(200,100)的像素颜色填充为黑色
因为要生成图片验证码,因此,点的位置和颜色都应该是随机的,所以我们需要使用random.randint函数。一般的图片验证码中的点有两种形式:一种是很多不同颜色的点随机分布在图片的各个位置,另一种是为图片上的每一个点都设置上随机的颜色。很显然,第二种效率更低,我们选择第一种。
另外,在后续的绘图中,都要涉及随机点坐标,如果我们每次绘图都得用randint生成随机x值,生成随机y值的话非常不方便,所以我们可以定义一个方法,每次调用该方法直接返回一个随机点坐标即可。
注意: 坐标的第一个值是在横轴方向(也就是图形的长),第二个值是在纵轴方向(也就是图形的高),随机生成的横轴坐标不能大于图形的最大长度,纵轴坐标不能大于图形的最大高度。
from random import randint # 先导入randint模块
# 生成随机点坐标
def randomPoint():
# 需要注意他们的取值范围
return randint(0, img.width), randint(0, img.height)
相应的,简化生成随机点坐标步骤,我们也可以简化生成随机颜色的步骤,因为后续直线,圆,文字同样要用到。不过这里的颜色更有讲究了,要更好的生成随机颜色,我们不能使用颜色名称来定义颜色了,应该使用RGB值来定义;除此之外,图案类型的不同,需要的颜色范围也不同。验证码颜色应该要深一点,点的颜色要浅一点。 如果点的颜色与验证码的颜色差不多,那么我们是看不到验证码的。
因此在定义函数时可以使用默认参数,特殊情况将特殊值传入即可,否则默认在0~255之间选取。
# 生成随机颜色
def randomColor(start=0, end=255):
return randint(start, end), randint(start, end), randint(start, end)
RGB:R表示红色,G表示绿色,B表示蓝色,每一元素取值范围都在0~255之间,值越低颜色深,值越高颜色越浅。
办理完上面的手续之后就是要生成点了,那我们该生成多少个点好呢?这就看个人喜好了,没有硬性要求。可以看看上面的三个验证码,不同的点数带来的视觉效果会不同,从左往右点数一直减少。下面的案例中我取的点数是总像素数 / 8,也就是图片长度 x 图片宽度 // 8 。
for i in range(img.width * img.height // 8):
pen.point(randomPoint(), randomColor(150))
因为点是图片的背景,所以颜色一定不要太深,RGB值越高,颜色越浅,所以我选取的范围是150~255之间。
生成点之后的图片是这样的。
直线
直线(line)有四个参数,我们只讲前三个参数。
- xy:第一个参数是元组形式的起始坐标与终点坐标。(SX,SY,FX,FY)或 ((SX,SY),(FX,FY))
- fill:第二个参数是直线的颜色。
- width:第三个参数是直线的粗细。
现在就来试试吧!
pen.line(((100, 100), (120, 180)), fill="blue", width=3)
表示起始坐标(100,100)与终点坐标(120,180)连线,颜色为蓝色,宽度为3的一条直线。
已经知道了如何生成直线,那么要随机生成直线也不成问题了,前面我们已经定义了随机生成坐标的方法,但是在这里来看会不会还是有点麻烦呢?因为要写起始坐标和终点坐标,用前面的方法的话我们就需要使用两次才能确定一条直线。那我们能不能在定义一个方法直接生成一个起始坐标和终点坐标呢?显然是可以的。在这个新生成的方法中调用两次之前的生成随机点坐标方法即可。
# 生成两个随机点坐标
def randomPoints():
return randomPoint(), randomPoint()
有了他我们就能更好办事了,因为在后面的圆中同样需要确定起点和终点坐标。
接下来的问题就是,在图形验证码中我们该生成多少条直线好呢?这个也是看个人,我的话是选择随机生成10 ~ 15条直线,因为直线的起始坐标与终点坐标都是随机的,有长有短,短的话甚至看不见。因此我觉得10 ~ 15挺合适的。
for i in range(randint(10, 16)):
# 直线第一个参数是起始坐标与终点坐标(元组形式)
# 第二个参数指定颜色,第三个参数指定直线的粗细
pen.line(randomPoints(), fill=randomColor(), width=randint(1, 3))
直线的粗细同样能够指定,我这里定义粗细是在1~3之间,直线的颜色就是用默认的就好了。
现在我们看一下效果。
好像这次直线有点多,问题不大,可以根据需求减少一些。
圆
圆(arc)有四个参数:
- xy:圆同样需要有起始坐标与终点坐标。不同的是圆是在 以一个坐标为左上角,另一个坐标为右下角的四边形中。 而且可以生成椭圆也能生成正圆。
- start:圆的起始角度,顺时针旋转。
- end:圆的终点角度。
- fill:圆边框的颜色
- width:圆边框的粗细
单看这几个参数可能还不明白怎么用。实践才是硬道理!
首先我们应该要知道两点的具体位置,但如果画点的话我们很难看清,所以可以先用线将两点连起来,接着我们就能看到直线始终位置,就能够画圆了。
看完这三张图,你是否有些启发呢?除了修改坐标值外,你也可以试试修改角度噢!
正圆样例:
pen.line((50, 50, 100, 100), "red")
pen.arc((50, 50, 100, 100), 0, 360, "red")
要生成随机圆我们可以生成随机坐标,随机颜色,随机角度以及随机粗细。坐标颜色我们都已经定义好了,可以直接调用,角度的话不能太大(我定义的范围是0~180之间),不然看起来有点奇怪,圆粗细的话我这里就不定义了,需要的话可以自行定义。接着的问题就是该生成多少圆合适?不用考虑这么多,直接丢进上面直线的循环就完事!
pen.arc(randomPoints(), 0, randint(0, 180), fill=randomColor())
丢没丢进去呢?
至此,我们图案就告一段落了,看一下现在能够生成怎样的图片。
验证码
内容
一张图片验证码的精髓莫过于是里面的验证码,验证码的内容同样是由画笔使用text()方法画出来的。他的参数有很多,我就挑一些常用的讲讲。
- xy:内容左上角的起始坐标(一个坐标点)
- text:文本内容
- fill:字体颜色
- font:字体样式
…
我们根据以上参数,在原有图案的基础上写点文字看看有什么效果。
pen.text((100, 100), "abcd", "red")
字体
发现有什么问题?内容没有居中;字体太小了。前者容易解决,调一下位置就好了,可字体如何解决呢?text中没有size的参数,似乎没有办法调整字体大小?这里要引入另外一个概念——字体。ImageFont模块能够很好解决与文字相关的问题,它下面的truetype方法能帮助我们个性化文字,了解一下他部分常用参数吧:
- font:指定要使用的字体。(写入ttf格式字体的存放路径)
- size:指定字体大小。
- encoding:指定字体的编码格式。默认为Unicode.
像开头第一张验证码我就用了比较特别的字体,你也去试试呗。
我们回顾一下现在讲了pillow下面的那些模块?有生成图片对象的Image模块,有生成画笔的ImageDraw模块,刚又学了生成字体对象的ImageFont模块。
我选了一个叫hanshand的字体,并将其放在与py文件的同级目录上,这样的话直接输入名字就能调用了。注意: 如果你下载了英文字体,他是不支持中文的,输入中文会显示不出来,这样的话你下载中文字体即可。
myfont = ImageFont.truetype("hanshand.ttf", size=50)
换上字体并调整适当位置和大小就能正常显示了!
每种字体占的位置都不太一样,位置与大小都需要自行调整。
pen.text((100, 75), "abcdeqw", "red", font=myfont)
位置调整
到这一步之后会不会感觉还是差点啥?没错,太紧凑了,不够美观。我们可以根据验证码长度来填充适当的位置,比如说:如果要生成一个长度为5的图片验证码,我们可以把一张图片分成七份,左右两份不显示内容,在中间五份中各显示一个字符,这样他就能居中排开了!
首先定义一个变量用来确定验证码长度,我这里的值为5(可自行调整),然后用总长度除以验证码长度加2得出每部分长度。最后从第二部分开始依次填入验证码即可。
total = 5
part = img.width // (total + 2)
生成随机验证码
全篇倒数第二个问题。如何生成随机验证码?最常见的图片验证码莫过于字母,数字亦或者字母和数字混合。我们就了解一下字母和数字混合是如何实现的。第一种思路是先生成所有字母和数字,然后每次遍历在其中选取一个;另外一种思路是直接在每次遍历中随机生成一个字母和数字。相较而言后者效率高点,我们就选后者。随机生成一个字母的话可以利用chr函数将整数转换成ASCII码对应的字母。接着使用random的choice方法即可随机选取一个字母或数字。(记得导入choice模块)
与chr函数对应的是ord函数,可以得到字符对应的ASCII码。
total = 5 # 定义验证码长度
part = img.width // (total + 2) # 将图片等分为n+2分
pos = 0 # 相当于指针,指在哪个地方哪个地方就填入对应字符
for i in range(total):
pos += part # 每次循环移动一次指针方便字符写入
# 先随机生成一个65~90的数字使用chr将其转换成A~Z的字符,再随机生成一个0~9数字,最后再在二者选其一。
r = choice((chr(randint(65, 90)), str(randint(0, 9))))
# 对于我这个字体样式而言,高度在八分之三的位置比较合适,高度可以根据自己的字体样式自行调整
pen.text((pos, 3 * img.height // 8), text=r, fill=randomColor(30, 200), font=myfont)
这里有几个需要注意的地方:choice是在序列中选择一个元素,所以我们要用括号将生成的一个字母和一个数字括起来将其变为元组序列;文本内容应该是字符串类型,所以生成随机数字时要将其转换成字符串类型;文本内容颜色不宜过浅。
此时效果如下图所示:
匹对验证码
最后一个问题,要不思考一下还缺点啥?现在我们虽然已经生成显示了验证码,但是我们还不知道验证码是多少,实际应用中应当知道验证码的具体值才能进行校验。因此在生成验证码的同时我们记录一下每个字符,在原有的基础上添加如下三行代码即可。
当然记录验证码具体值也可以直接使用字符串相加。
保存图片
走到这里任务已经算完成了,就还差最最最最最后一步——保存图片。直接上代码!
with open(r"C:\\Users\\17591\\Desktop\\t.png", "wb") as fp:
img.save(fp, format="png")
两个需要注意的地方:保存媒体文件需要使用wb形式进行保存——表示以二进制格式覆盖写入文件。另外图片需要保存为png格式。
总结
本篇我们学会了pillow画笔的基本使用,更重要的是能够理解各部分功能的衔接以及内部逻辑如何实现。希望大家学完本篇文章能够有一点点收获收获吧!
我是WA,一直在努力,希望七月份能得到一个心仪的python web offer,接下来的日子与你一起加油!
完整代码
from random import randint, choice
from PIL import Image, ImageDraw, ImageFont
# 生成随机点坐标
def randomPoint():
# 需要注意他们的取值范围
return randint(0, img.width), randint(0, img.height)
# 生成随机颜色
def randomColor(start=0, end=255):
return randint(start, end), randint(start, end), randint(start, end)
# 生成两个随机点坐标
def randomPoints():
return randomPoint(), randomPoint()
if __name__ == '__main__':
# 首先创建一个画布,选择RGB模式,图片尺寸我这里设置为长400,高200,颜色为白色,等价于(255, 255, 255)
img = Image.new(mode="RGB", size=(400, 200), color="white")
# 创建画笔,在图片上绘制图形
pen = ImageDraw.Draw(img, mode="RGB")
pen.point((200, 100), fill="black")
for i in range(img.width * img.height // 8):
pen.point(randomPoint(), randomColor(150))
for i in range(randint(10, 16)):
# 直线第一个参数是起始坐标与终点坐标(元组形式)
# 第二个参数指定颜色,第三个参数指定直线的粗细
pen.line(randomPoints(), fill=randomColor(), width=randint(1, 3))
pen.arc(randomPoints(), 0, randint(0, 180), fill=randomColor())
myfont = ImageFont.truetype("hanshand.ttf", size=50)
total = 5 # 定义验证码长度
part = img.width // (total + 2) # 将图片等分为n+2分
pos = 0 # 相当于指针,指在哪个地方哪个地方就填入对应字符
res = []
for i in range(total):
pos += part # 每次循环移动一次指针方便字符写入
# 先随机生成一个65~90的数字使用chr将其转换成A~Z的字符,再随机生成一个0~9数字,最后再在二者选其一。
r = choice((chr(randint(65, 90)), str(randint(0, 9))))
res.append(r)
# 对于我这个字体样式而言,高度在八分之三的位置比较合适,高度可以根据自己的字体样式自行调整
pen.text((pos, 3 * img.height // 8), text=r, fill=randomColor(30, 200), font=myfont)
res = ''.join(res)
img.show()
print(res)
# 路径换成自己想保存的位置
with open(r"C:\\Users\\17591\\Desktop\\t.png", "wb") as fp:
img.save(fp, format="png")
注: 字体过小可自行调大。
以上是关于pillow教程的主要内容,如果未能解决你的问题,请参考以下文章