使用Pillow来进行图像处理

Pillow在PIL的基础上,为Python3增加了更多功能和支持。我们将看到如何在图像上执行各种操作,例如裁剪,调整大小,添加文本到图像,旋转,灰阶转换。

介绍

Pillow是Python图像处理函式库(PIL)的一个分支。PIL是一个函式库,提供了几个操作图像的标准程序。它是一个功能强大的函式库,但自2011年以来就没有太多的更新,并且不支持Python3。

Pillow在PIL的基础上,为Python3增加了更多功能和支持。它支持一系列图像文件格式,如PNG,JPEG,PPM,GIF,TIFF和BMP。我们将看到如何在图像上执行各种操作,例如裁剪,调整大小,添加文本到图像,旋转,灰阶转换。

Ps:我喜欢OpenCV的速度与强大的图像处理功能,但是要在OpenCV中使用我们自己喜欢的字体似乎并不容易(预设的字体实在令人退避三舍)。

需求

Python 3.5 
Pillow

安装

pip install Pillow

下载测试用的图像

  1. 下载hugh_jackman到'data/'的子目录中并命名为hugh_jackman.jpg 
  2. 下载thumpup到'data/'的子目录中并命名为thumbup.png。

你的目录结构看起来应该如下:(这里只列出了在这个范例中会用到的相关资料和目录)
- pillow-demo/ 

  - xxx.ipynb 

  - data/ 

     hugh_jackman.jpg 

    thumbup.png 

载入相关函数库

# 把一些警告的讯息暂时关掉
import warnings
warnings.filterwarnings('ignore')

# Utilities 相关函式库
import os

# 图像处理/展现的相关函数库
import matplotlib.pyplot as plt

设定相关设定与参数

# 根目录路径
root_dir = os.getcwd()
# 训练/验证用的资料目录
data_path = os.path.join(root_dir,'data')
# 测试用的图像
test_image = os.path.join(data_path,'hugh_jackman.jpg')

图像物件 Image

Python Imaging Library中的一个关键类别是Image,它定义在Image模组中。这个类别的一个实例可以通过几种方式来创建:从图像档案加载图像,从头开始创建图像或者处理其他图像。

我们将看到所有这些的使用方式。

要从图像档案加载图像,我们使用image模块中的open()函数将路径传递给图像类别。


from PIL import Image

# 载入图像
image = Image.open(test_image)

# 存储图像并转换格式(jpg->png)
image.save(os.path.join(data_path,'new_image.png'))

plt.imshow(image)
plt.show()


上面的程序代码创建了一个加载了图像档案的Image类别的实例,并将其保存到一个新文件new_image.png。Pillow看到文件扩展名已被指定为PNG,所以它将其转换为PNG格式,然后将其保存到文件中。
你也可以提供第二个参数save()来直接指定文件格式。这个image.save('new_image.png','PNG')将和前面的save()一样。通常没有必要提供第二个参数,因为pillow将从文件扩展名中决定使用的文件存储格式。

调整图像的大小

要调整图像大小,可以调用resize()方法,传递一个两个整数的tuple参数,表示调整大小的图像的宽度和高度。该函数不会修改使用的图像,而是返回具有新维度的另一个图像。


# 载入图像
image = Image.open(test_image)
# 调整大小
new_image = image.resize((400,400))

print("原图像的大小:",image.size)
print("新图像的大小:",new_image.size)

plt.imshow(new_image)
plt.show()
原图像的大小: (298, 169)
新图像的大小: (400, 400)


resize()方法返回一个图像,其宽度和高度完全匹配传入的值。这可能是你想要的,但有时你可能会发现这个参数返回的图像并不理想。这主要是因为该功能没有考虑到图像的长宽的比例,所以你最终可能会看到一个图像,看起来被拉长或挤压。
你可以从上面的程序代码中看到新创建的图像:new_image。它看起来有点没挤压了。

图像缩图(thumbnail)

如果要调整图像大小并保持其长宽的比例,则应该使用thumbnail()函数来调整他们的大小。这个需要表示缩略图的最大宽度和最大高度的两个整数元祖参数。


# 载入图像
im = Image.open(test_image)
im.thumbnail((200, 200))
print(im.size)
plt.imshow(im)
plt.show()
(200, 113)


以上将产生一个图像大小为(200,113),它保持了原始图像的宽高比。正如你所看到的,这样的做法会让缩放的效果比较正常。
ps:请注意在PIL的Image.size回传的是(width,height)与一般使用OpenCV所回传的(height,width,channels)的不太一样。

图像裁剪(cropping)

裁剪图像时,图像内的矩形区域将被选中并保留,而区域外的所有其他区域将被移除。使用Pillow库,可以使用Image类的crop()方法裁剪图像。该方法采用一个边界框(bounding box)来定义裁剪区域的位置和大小,并返回一个代表裁剪图像的Image对象。框的坐标是(left,upper,right,lower)或(x1,y1,x2,y2)。


# 载入图像
image = Image.open(test_image)

# 定义要裁剪的边界框坐标
x1 = 0
y1 = 50
x2 = 460
y2 = 320
bbox = (x1,y1,x2,y2)

# 进行裁剪
cropped_image = image.crop(bbox)

plt.imshow(cropped_image)
plt.show()


将图像粘贴在另一个图像上

Pillow函式库能够将图像粘贴到另一个图像上。一些使用案例是通过在其图像添加水印来保护公开可用的图像,或是添加公司的商标。
使用paste()函数完成粘贴的动作。这个动作修改了Image对象,它不像我们目前看到的其他处理函数返回一个新的Image对象。因此,在执行粘贴之前,我们首先复制我们的原始图像,以便我们可以用未修改的图像继续进行其他处理。

# 载入图像
image = Image.open(test_image)
# 载入水印图像
logo = Image.open(os.path.join(data_path,'thumbsup.png'))

# 改成成合适的大小
logo.thumbnail((100,100))

# 复制图像
image_copy = image.copy()

# 指定要粘贴的左上角坐标
position = ((image_copy.width-logo.width),(image_copy.height-logo.height))

# 进行粘贴
image_copy.paste(logo,position)

plt.imshow(image_copy)
plt.show()

在上面,我们加载两个图像Hugh_jackman.jpg 和 brain.png,然后用copy()复制前者。我们希望将微标图像粘贴到复制的图像上,我们希望将其放置在右下角,结果如上所示。
但是结果跟我们期待的有一些差异,那个粘贴上来的图像的背景让整个图片的一个整个角down掉了
预设的情况下,当你执行粘贴时,透明像素将被粘贴为实心像素,因此微标周围的黑色(某些操作系统上的白色)框会被粘贴进去。大多数时候,这不是你想要的。你不能让你的水印覆盖底层图像的内容。我们宁愿透明的像素出现。

为了达到这个目的,你需要把第三个参数传递给paste()函数。这个参数是透明度掩码/遮罩图像物件对象。蒙版是一个图像对象,其中的alpha值是重要的,但其绿色,红色和蓝色值将被忽略。如果给出遮罩,则paste()仅更新由掩码指示的区域。您可以使用RGBA图像作为遮罩。粘贴RGBA图像并将其用作遮罩将粘贴图像的不透明部分,但不粘贴透明背景。如果你修改粘贴如下所示,你应该有一个透明像素粘贴微标。


# 载入图像
image = Image.open(test_image)

# 载入水印图像
logo = Image.open(os.path.join(data_path,'thumbsup.png'))

# 修改成合适大小
logo.thumbnail((50,50))

# 复制图像
image_copy = image.copy()

# 指定要粘贴的左上角坐标
position = ((image_copy.width - logo.width),(image_copy.height - logo.height))

image_copy.paste(logo,position,logo)

plt.imshow(image_copy)
plt.show()


图像旋转

你可以使用rotate()方法逆时针地旋转图像。这需要一个整数或浮点型参数来表示旋转图像的角度,并返回旋转图像的新Image对象。

# 载入图像
image = Image.open(test_image)
# 逆时针地旋转图像90度
image_rot_90 = image.rotate(90)

plt.imshow(image_rot_90)
plt.show()

# 逆时针地旋转图像180度
image_rot_180 = image.rotate(180)

plt.imshow(image_rot_180)
plt.show()

在上面,我们将两张图像保存到磁盘上:一个以90度旋转,一个以180度旋转。
预设的情况下,旋转的图像会保持原始图像的尺寸。这意味着,除了180的倍数以外的角度,图像将被剪切和/或填充以适应原始尺寸。如果仔细观察上面的第一张图片,你会注意到其中一些已经被裁剪以适应原始图像的高度,并且其边缘已经用黑色背景(某些操作系统上的透明像素)填充以适应原始的宽度。

下面的例子更清楚地展示了这一点。


# 载入图像
image = Image.open(test_image)

# 逆时针地旋转图像18度
image_rot_18 = image.rotate(18)

plt.imshow(image_rot_18)
plt.show()


要扩展让旋转图像的尺寸以适应整个图像,可以将第二个参数传递给rotate(),如下所示。


# 载入图像
image = Image.open(test_image)

# 逆时针地旋转图像18度并让图像扩大来包含新的图像
image_rot_18_expand = image.rotate(18,expand=True)

plt.imshow(image_rot_18_expand)
plt.show()


现在图像的内容将完全可见,并且图像的尺寸将会增加以解决这个问题。

图像翻转(Flipping)

你也可以翻转图像来获得他们的镜像版本。这是用transpose()函数来完成的。你可以采用以下选项:
- PIL.Image.FLIP_LEFT_RIGHT - PIL.Image.FLIP_TOP_BOTTOM - PIL.Image.ROTATE_90 - PIL.Image.ROTATE_180 - PIL.Image.ROTATE_270 - PIL.Image.TRANSPOSE


# 载入图像
image = Image.open(test_image)
# 左右互换
image_flip = image.transpose(Image.FLIP_LEFT_RIGHT)

plt.imshow(image_flip)
plt.show()

# 上下互换
image_flip = image.transpose(Image.FLIP_TOP_BOTTOM)
plt.imshow(image_flip)
plt.show()


在图像上绘图

使用Pillow函式库,你可以使用ImageDraw模块来绘制图像。您可以绘制直线,点,椭圆,矩形,弧,二元图,和弦,pieslices,多边形,形状和文本。


from PIL import Image,ImageDraw,ImageFont

# 产生一个有4个颜色channels的空白图像
blank_image = Image.new('RGBA',(400,300),'white')

# 在blank_image 图像上绘图
img_draw = ImageDraw.Draw(blank_image)

# 画一个矩形
img_draw.rectangle((70,50,270,200),outline='red',fill='blue')

# 取得字形物件
fnt = ImageFont.truetype('scotsdi.ttf',40) # 修改电脑上的字型,字体可自行下载

# 放上文字信息到图像上
img_draw.text((70,250),'hello world',font=fnt,fill='green')

plt.imshow(blank_image)
plt.show()


在这个例子中,我们用new()方法创建一个Image对象。这将返回一个没有加载图像的Image对象。然后,我们添加一个矩形和一些文本的图像。
ps:特别注意的字型的设定,PIL可以支持TrueType和OpenType字体(你必须指定字型的完整目录)


from PIL import Image,ImageDraw,ImageFont

# 产生一个有四个颜色channels的空白图像
blank_image = Image.new('RGBA',(400,300),'white')
img_draw = ImageDraw.Draw(blank_image) # 在blank_image上绘图

# 在PIL中可以用rectangle来画一个四方形,但是无法控制框线的粗细
img_draw.rectangle((70,50,270,200),outline=None,fill='pink')

# 通过画线来画一个四方框的框线并控制粗细
img_draw.line([(70,50),(270,50),(270,200),(70,200),(70,50)],fill='red',width=4)

# 在PIL中要画一个可以控制大小的图要通过以下方式
r = 10 # 设定半径

# 以图的中心点(x,y)来计算框住图的边界框坐标[(x1,y1),(x2,y2)]
img_draw.ellipse((270-r,200-r,270+r,200+r),fill='orange')

# 画一个多边形
img_draw.polygon([(40,40),(40,80),(80,60),(60,40)],fill='green',outline=None)

plt.imshow(blank_image)
plt.show()


颜色变换

Pillow函式库允许你使用convert()方法在不同的像素表示之间转换图像。它支持L(灰度),RGB和CMYK模式之间的转换。
在下面的例子中,我们将图像从RGBA转换为L模式,这将彩色图像转换成灰阶图像。


# 载入图像
image = Image.open(test_image)

# 将彩色转换成灰阶
greyscale_image = image.convert('L')

# 注意要注明cmap='gray'才能够正确秀出灰阶图像
plt.imshow(greyscale_image,cmap='gray')
plt.show()


参考:

- 本文来自网络,如有侵权,请联系本站处理。

2022-03   阅读(0)   评论(0)
 标签: program Python Pillow

涨知识
LED

发光二极管,简称为LED,是一种常用的发光器件,通过电子与空穴复合释放能量发光, 发光二极管可高效地将电能转化为光能,在现代社会具有广泛的用途,如照明、平板显示、医疗器件等。

评论:
相关文章
Python 66 个内置函数!附代码

Python有许多内置函数,共有66个。以下是这些内置函数的详细解释和示例代码


一文详尽 Python 函数式编程技术

本文对 Python 中的函数式编程技术进行了简单的入门介绍。


用Python优雅地编写LaTeX

latexify是用于生成 LaTeX 数学公式的 Python 库。LaTeX 是一种基于 ΤΕΧ 的排版系统,对于展示复杂的数学公式表现极为出色。


使用Python开发Android应用的简易教程

在过去,如果你想要开发Android应用,你需要掌握Java或者Kotlin等编程语言,但是现在,Python也可以用来开发Android应用了。


Thonny 4.1.3 下载

Thonny是一款免费的、开源的、易于使用的编程语言,旨在为初学者提供简单易用的编程环境。 Thonny的语法简洁易懂,同时还支持自动补全和语法高亮,使得编程变得更加便捷。

搜索
最新课件
小鹏STEM教研服务

专属教研服务系统,助您构建STEM课程体系,打造一站式教学环境。