Python环境Pillow( PIL )图像处理工具使用解析


Posted in Python onSeptember 12, 2019

前言

由于笔者近期的研究课题与图像后处理有关,需要通过图像处理工具对图像进行变换和处理,进而生成合适的训练图像数据。该系列文章即主要记录笔者在不同的环境下进行图像处理时常用的工具和库。在 Python 环境下,对图像的处理笔者主要使用 Pillow 库,主要操作包括对图像的读取、存储和变换等。实际应用中,Pillow 中提供的 Image 模块适合对图像整体进行变换处理操作。

注:以下介绍仅包括对应模块和函数的基础用法,故而在介绍时省略了部分参数和选项,更完备的用法和介绍可参考 Pillow 的官方文档。

安装

用户可通过 pip 直接安装 Pillow,更多安装方式可以参见这里。

pip install Pillow #安装 pillow

使用

在日常应用过程中,使用最多的是 Pillow 提供的 Image 模块,其提供了包括图像存储、变换以及一系列的相关处理功能。Pillow 使用 Image 对象来表示图像对象并基于其定义图像的属性信息以及可针对其进行的操作,后续即主要介绍通过 Image 对象可进行的图像操作。在 Python 中使用时,用户首先需从 PIL 中导入对应的 Image 模块。

from PIL import Image #通过 Image 进行图像处理相关的操作

图像读取与存储

通过 Image 提供的 open 方法读取图像,其以指定的文件名为参数,返回值为对应图像的 Image 对象,后续即可针对图像对应的 Image 对象进行操作。

im = Image.open( "test.png" )  # open 方法以图像名(或图像对象)为参数,返回一个 Image 对象

通过 Image 对象的 save 方法存储图像对象,其使用存储目标文件名为参数,也可通过 format 参数指定存储文件的格式。

im.save( "test.png" )          # im 为 Image 对象,其被保存至 test.png,不指定 format 参数时,该方法通过文件后缀推测文件类型
  im.save( "test.jpg" , format="JPEG")  # 以 JPEG 格式保存 Image 对象 im 至文件 test.jpg 中

基本属性

图像对应的 Image 对象具备基本属性。用户可以通过这些属性获得图像最基本的信息,Image 对象的完整属性信息可以查看这里。

im.filename    # Image 对象 im 对应的文件/路径名
  im.mode      # Image 对象图像数据的解释方式,如灰度图为 “L”,彩色图为 “RGB”等
  im.size      # 返回图像的尺寸信息,为( width , height ) 格式的元祖

图像类型转换

不同的图像数据具有不同的图像格式,进而拥有不同的组织数据的方式。对于 RGB 图像而言,图像拥有 R、G、B 三个通道,像素数据由三个对应三通道的 8 bit 数据组成;对于黑白图像而言,其每个像素由一个 8 bit 字节表示等等。在打开图像时,open 方法会自动解析图像的格式,用户可通过 Image 对象的 mode 属性获得图像的状态。

Image 对象可通过 convert 方法进行图像类型间的转换,其使用转换的目标类型的字符串为参数,返回转换后的 Image 对象,常见的类型包括 RGB(真彩)、L(黑白)、YCbCr(视频图像)、HSV(色调饱和度亮度彩色空间)。

data = im.convert( "L" )    #获得 RGB 图像 im 的灰度图

与 numpy 数组的转换

在程序中,一般使用图像对应的 Image 对象进行图像相关的操作,针对图像数据本身的计算处理一般将 Image 对象的数据转换为 numpy 数据后进行,处理完成之后的 numpy 数据再被转换为 Image 对象进行保存。

a. 将 Image 对象转换为 numpy 数组

使用 numpy.asarray 方法( 不唯一,可参见 Array creation routines )将 Image 对象的数据转换为 numpy 数组,进而可以对其进行计算处理。转换后 numpy 数组的数据类型根据 Image 数据对象本身的数据类型推断获得,使用时也可使用 numpy.asarray 的 dtype 参数指定转换后的数据类型。

im = Image.open( "test.png" )         #打开图像 test.png ,并获得其对应的 Image 对象
  data = numpy.asarray( im )           #将 Image 对象 im 的数据转换为 numpy 数组的形式,data 即为可供运算的 numpy 数组
  data = numpy.asarray( im, dtype=np.uint8 )   #转换图像数据为 numpy 数组,并指定其类型为 np.uint8

b.将 numpy 数组转换为 Image 对象

对于 numpy 数据形式的图像数据( 通过数据处理或其他途径获得 ),可通过 Image.fromarray 方法将已有的 numpy 图像数据转换为 Image 对象。

im = Image.fromarray( data ) # data 为 numpy 数组,im 为转换获得的 Image 对象

注意,在使用 Image.fromarray 方法时可能会出现报错 raise TypeError("Cannot handle this data type") , 这是由于待转换的 numpy 数据类型可能并不符合 Image 对象所需的数据类型( 一般为 8 bit 无符号值 ),解决方法是在转换前先将 numpy 数组的数据类型转换为 np.uint8 .

im = Image.fromarray( data.astype( np.uint8 ) )  #将 numpy 数组的数据类型转换为 np.uint8 后再转换为 Image 对象

常用操作

裁剪图像——crop

可以使用 crop 方法获得图像的指定部分。crop 方法以指定 ( 左,上,右,下 ) 切割位置的元祖来定义待分割的图像部分,可以理解为定义的是切割获得的矩形的左上角和右下角位置的坐标。在 PIL 所支持的坐标系统中,坐标的( 0 , 0 ) 为图像的左上角,注意 ( 0 , 0 ) 指向的不是左上角的第一个像素,而是该像素位置前的位置,后续所有的坐标均为像素间的空隙位置,而不是指向像素。也就是说,第一个像素被 ( 0 , 0 ) 和 ( 0 , 1 ) 两个坐标左右包围。

part = im.crop( ( 0 , 0 , 100 , 100 ) ) #截取获得图像 im 左上角大小为 100 × 100 像素的矩形图像

通道处理——split / getchannel

split 方法将图像数据按通道分离,其返回值为包含各个通道分离数据的元组tuple,如对于 RGB 图像而言,其被分成 R、G、B 三个通道的数据。

R, G, B = im.split() # im 为真彩色 Image 对象,其被分为独立的 R、G、B 通道信息

getchannel 方法以图像的通道的索引或字符名字为参数,返回包含有对应通道数据的 L 类型的图像( 即为黑白模式 )。

R = im.getchannel( 0 )  # 获得 RGB 图像的第一个通道的数据,即 R 通道信息
  R = im.getchannel( "R" ) #同上

缩放图像——resize

resize 方法以缩放目标图像大小的元祖( Width, Heigth ) 为参数,通过指定的采样方法将图像缩放为指定的图像大小。其支持采样的方法包括 PIL.Image.NEAREST、PIL.Image.BILINEAR、PIL.Image.BICUBIC 等,resize 支持的全部采样方式见文档。注意,以上采样方法的全名为 PIL.Image.xxxx,但实际上由于之前已经使用 from PIL import Image 导入了 Image 这个模块名,故而后续可以直接使用 Image.xxxx 的形式调用上述方法,反之,在未导入模块名时需使用完整的名称来使用上述方法,下同。

data = im.resize( ( 100, 100 ) )             #将 im 对应的 Image 对象缩放为 100×100 的大小,默认采用 PIL.Image.NEAREST 方法
  data = im.resize( ( 100, 100 ), Image.BICUBIC )     #使用 PIL.Image.BICUBIC 方法进行采样

翻转图像——rotate/transpose

通过 rotate 方法旋转图像,rotate 方法以旋转的角度为参数,将图像顺时针中心旋转对应的度数,并返回对应的 Image 对象。注意,通过 rotate 方法进行旋转时,结果图像是中心旋转后图像在源图像大小范围内被截取的部分,其他部分为填充。如大小为宽×高 200 * 100 的图像,经过 90 度旋转后,其大小仍为 200 * 100 ,图像内容为旋转后的理论为 100 * 200 的图像与原 200 * 100 区域的重合部分,其余部分为填充。

rotate 方法可以指定 expand 参数为 1 ,此时生成的新图像为完整包含有旋转后图像内容的最小矩形大小( 空白处为填充),如上例中,图像经过 90 度旋转后,获得的新图像的大小即为 100 * 200。更多介绍见 Image.rotate.

data = im.rotate( 90 ) #将图像顺时针旋转 90 度
data = im.rotate( 90 , expand=1 ) #将图像顺时针旋转 90 度,同时保留图像的完整内容

在某些图像训练的数据生成中,将图像进行 90 度为单位的旋转、上下或左右翻转是更为常见的操作。此时可以使用 transpose 方法,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     #类似于左右翻转后再逆时针旋转图像 90 度
  PIL.Image.TRANSVERSE     #类似与左右翻转后再顺时针旋转图像 90 度

可以直接使用上述参数对图像进行变换,transpose 方法返回变换后的完整图像( 由于是 90 度倍数的变换,也不存在空白区域 )。

data = im.transpose( Image.FLIP_LEFT_RIGHT ) #获得 im 图像经过左右旋转后的数据

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python模拟登陆Tom邮箱示例分享
Jan 13 Python
python实现实时监控文件的方法
Aug 26 Python
Python利用flask sqlalchemy实现分页效果
Aug 02 Python
Python爬虫实现爬取京东手机页面的图片(实例代码)
Nov 30 Python
你真的了解Python的random模块吗?
Dec 12 Python
python实现机器人行走效果
Jan 29 Python
Python实现string字符串连接的方法总结【8种方式】
Jul 06 Python
浅谈pycharm的xmx和xms设置方法
Dec 03 Python
TensorFlow 多元函数的极值实例
Feb 10 Python
Python如何在循环内使用list.remove()
Jun 01 Python
浅谈pymysql查询语句中带有in时传递参数的问题
Jun 05 Python
5个pandas调用函数的方法让数据处理更加灵活自如
Apr 24 Python
Python列表删除元素del、pop()和remove()的区别小结
Sep 11 #Python
python conda操作方法
Sep 11 #Python
多版本python的pip 升级后, pip2 pip3 与python版本失配解决方法
Sep 11 #Python
浅谈Django+Gunicorn+Nginx部署之路
Sep 11 #Python
初次部署django+gunicorn+nginx的方法步骤
Sep 11 #Python
python 如何将数据写入本地txt文本文件的实现方法
Sep 11 #Python
学习Django知识点分享
Sep 11 #Python
You might like
php 正确解码javascript中通过escape编码后的字符
2010/01/28 PHP
Zend Framework自定义Helper类相关注意事项总结
2016/03/14 PHP
PHP XML和数组互相转换详解
2016/10/26 PHP
PHP中mysqli_get_server_version()的实例用法
2020/02/03 PHP
JavaScript入门教程(11) js事件处理
2009/01/31 Javascript
JavaScript 精粹读书笔记(1,2)
2010/02/07 Javascript
boxy基于jquery的弹出层对话框插件扩展应用 弹出层选择器
2010/11/21 Javascript
为EasyUI的Tab标签添加右键菜单的方法
2012/07/14 Javascript
javascript/jquery获取地址栏url参数的方法
2014/03/05 Javascript
禁止iframe页面的所有js脚本如alert及弹出窗口等
2014/09/03 Javascript
javascript 动态创建表格的2种方法总结
2015/03/04 Javascript
javascript中JSON.parse()与eval()解析json的区别
2016/05/19 Javascript
jQuery简单实现页面元素置顶时悬浮效果示例
2016/08/01 Javascript
JS及JQuery对Html内容编码,Html转义
2017/02/17 Javascript
微信小程序获取微信运动步数的实例代码
2017/07/20 Javascript
解决vue-cli脚手架打包后vendor文件过大的问题
2018/09/27 Javascript
教你如何编写Vue.js的单元测试的方法
2018/10/17 Javascript
Vue路由管理器Vue-router的使用方法详解
2020/02/05 Javascript
JavaScript 引用类型实例详解【数组、对象、严格模式等】
2020/05/13 Javascript
[03:01]2014DOTA2国际邀请赛 小组赛7月13日TOPPLAY
2014/07/14 DOTA
[01:34:42]NAVI vs EG 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
Python 中urls.py:URL dispatcher(路由配置文件)详解
2017/03/24 Python
放弃 Python 转向 Go语言有人给出了 9 大理由
2017/10/20 Python
深入理解python中函数传递参数是值传递还是引用传递
2017/11/07 Python
Python简单计算数组元素平均值的方法示例
2017/12/26 Python
Python+matplotlib实现计算两个信号的交叉谱密度实例
2018/01/08 Python
Python序列对象与String类型内置方法详解
2019/10/22 Python
TensorFlow2.X结合OpenCV 实现手势识别功能
2020/04/08 Python
CSS3旋转——彩色扇子兼容firefox浏览器
2013/06/04 HTML / CSS
ReVive利维肤美国官网:RéVive Skincare
2018/04/18 全球购物
小学毕业感言300字
2014/02/19 职场文书
高中学生干部学习的自我评价
2014/02/21 职场文书
新农村建设典型材料
2014/05/31 职场文书
人力资源管理毕业求职信
2014/08/05 职场文书
邀请书格式范文
2015/02/02 职场文书
大学生干部培训心得体会
2016/01/06 职场文书