首先说明一下PIL库,PIL(Python Imaging Library)是Python一个强大方便的图像处理库,之前的博客也有用到过PIL模块的demo
不过可惜的是PIL库只支持到Python 2.7。python3以后,我们可以用pillow模块来代替PIL,pillow是PIL的一个派生分支,大概是PIL low的意思
现如今已经发展成为比PIL本身更具活力的图像处理库,支持PIP安装:pip install Pillow及easy_install安装:easy_install Pillow,本文用的是anaconda4.3.0(很全面的python版本,500多M,反正我听说过的库基本都包含了....)https://baike.baidu.com/item/anaconda/20407441#viewPageContent
anaconda4.3.0内置的pillow版本是4.0.0
首先简单说几个常用的方法
和PIL一样,主要是Image类
from PIL import Image
打开图片:
Image = Image.open("fliename")
保存图片:
Image.save(outfile, "JPEG")
获取图片属性
Image.format, Image.size, Image.mode……
裁剪、粘贴、合并
box = Image.copy()
box = (100, 100, 400, 400)
region = Image.crop(box)
Image.paste(region, box)
几何变换
Image.resize((200,200))、Image.rotate(90)、Image.transpose(Image.ROTATE_90)以及参数一大堆的Image.transform()
更多高级姿势参考官方文档http://pillow.readthedocs.io/en/latest/index.html
然后再说一下plist,plist是一种文件格式,其实就是XML标签类型的配置文件。在Mac OS中较为常用,也经常用于前端开发将小图合并成组图的相关配置文件。
以IOS infoplist视图打开
以xml文本视图打开
python中plistlib模块提供一系列对.plist文件操作的函数,在这里我们也可以写一个简单的tree_to_dict方法以key-dict的形式提取数据
def tree_to_dict(tree):
d = {}
for index, item in enumerate(tree):
if item.tag == 'key':
if tree[index+1].tag == 'string':
d[item.text] = tree[index + 1].text
elif tree[index + 1].tag == 'true':
d[item.text] = True
elif tree[index + 1].tag == 'false':
d[item.text] = False
elif tree[index+1].tag == 'dict':
d[item.text] = tree_to_dict(tree[index+1])
return d
以麻将资源图为例:
我们通过对应的plist文件来提取单个麻将,其pilst文件配置如下:
<plist version="1.0">
<dict>
<key>frames</key>
<dict>
<key>M_autumn.png</key>
<dict>
<key>frame</key>
<string>{{79,886},{75,113}}</string>
<key>offset</key>
<string>{0,0}</string>
<key>rotated</key>
<false/>
<key>sourceColorRect</key>
<string>{{0,0},{75,113}}</string>
<key>sourceSize</key>
<string>{75,113}</string>
</dict>
<key>M_bamboo.png</key>
<dict>
<key>frame</key>
<string>{{79,771},{75,113}}</string>
<key>offset</key>
<string>{0,0}</string>
<key>rotated</key>
<false/>
<key>sourceColorRect</key>
<string>{{0,0},{75,113}}</string>
<key>sourceSize</key>
<string>{75,113}</string>
</dict>
<key>M_bamboo_1.png</key>
<dict>
<key>frame</key>
<string>{{424,656},{75,113}}</string>
<key>offset</key>
<string>{0,0}</string>
<key>rotated</key>
<false/>
<key>sourceColorRect</key>
<string>{{0,0},{75,113}}</string>
<key>sourceSize</key>
<string>{75,113}</string>
</dict>
<key>M_bamboo_2.png</key>
<dict>
<key>frame</key>
<string>{{309,618},{75,113}}</string>
<key>offset</key>
<string>{0,0}</string>
<key>rotated</key>
<true/>
<key>sourceColorRect</key>
<string>{{0,0},{75,113}}</string>
<key>sourceSize</key>
<string>{75,113}</string>
</dict> ……省略
读取freames再循环读取每个frame参数,再利用PIL里的几个函数:
crop裁剪
rotate/transpose方法进行角度旋转(plist配置参数rotated)
paste粘贴
save保存
完整python代码:
#!python
import os,sys,plistlib
from xml.etree import ElementTree
from PIL import Image
def tree_to_dict(tree):
d = {}
for index, item in enumerate(tree):
if item.tag == 'key':
if tree[index+1].tag == 'string':
d[item.text] = tree[index + 1].text
elif tree[index + 1].tag == 'true':
d[item.text] = True
elif tree[index + 1].tag == 'false':
d[item.text] = False
elif tree[index+1].tag == 'dict':
d[item.text] = tree_to_dict(tree[index+1])
return d
def gen_png_from_plist(plist_filename, png_filename):
file_path = plist_filename.replace('.plist', '')
big_image = Image.open(png_filename)
root = ElementTree.fromstring(open(plist_filename, 'r',encoding='UTF-8').read())
plist_dict = tree_to_dict(root[0])
to_list = lambda x: x.replace('{','').replace('}','').split(',')
for k,v in plist_dict['frames'].items():
print('v:',v)
rectlist = to_list(v['frame'])
width = int( rectlist[3] if v['rotated'] else rectlist[2] )
height = int( rectlist[2] if v['rotated'] else rectlist[3] )
box=(
int(rectlist[0]),
int(rectlist[1]),
int(rectlist[0]) + width,
int(rectlist[1]) + height,
)
sizelist = [ int(x) for x in to_list(v['sourceSize'])]
rect_on_big = big_image.crop(box)
if v['rotated']:
#rect_on_big = rect_on_big.rotate(90)
rect_on_big=rect_on_big.transpose(Image.ROTATE_90)
result_image = Image.new('RGBA', sizelist, (0,0,0,0))
if v['rotated']:
result_box=(
( sizelist[0] - height )//2,
( sizelist[1] - width )//2,
( sizelist[0] + height )//2,
( sizelist[1] + width )//2
)
else:
result_box=(
( sizelist[0] - width )//2,
( sizelist[1] - height )//2,
( sizelist[0] + width )//2,
( sizelist[1] + height )//2
)
print('rect_on_big:',rect_on_big,'result_box:',result_box)
result_image.paste(rect_on_big, result_box, mask=0)
if not os.path.isdir(file_path):
os.mkdir(file_path)
outfile = (file_path+'/' + k).replace('gift_', '')
print(outfile,"generated")
result_image.save(outfile)
if __name__ == '__main__':
#filename = sys.argv[1]
filename = 'mahjongs'
plist_filename = filename + '.plist'
png_filename = filename + '.png'
if (os.path.exists(plist_filename) and os.path.exists(png_filename)):
gen_png_from_plist( plist_filename, png_filename )
else:
print("make sure you have boith plist and png files in the same directory")
1.读取文件时提示"UnicodeDecodeError: 'gbk' codec can't decode byte 0x80 in position 205: illegal multibyte sequence"
编码格式问题,在open参数后面加上encoding='UTF-8'
root = ElementTree.fromstring(open(plist_filename, 'r',encoding='UTF-8').read())
2.paste 执行报错,integer argument expected, got float
因为在Python 3中,要得到一个整数的结果,你需要使用//而不是/
所以计算box时将/改为//
if v['rotated']:
result_box=(
( sizelist[0] - height )//2,
( sizelist[1] - width )//2,
( sizelist[0] + height )//2,
( sizelist[1] + width )//2
)
else:
result_box=(
( sizelist[0] - width )//2,
( sizelist[1] - height )//2,
( sizelist[0] + width )//2,
( sizelist[1] + height )//2
)
3.paste执行报错,images do not match
由于之前旋转错误导致尺寸异常,将rect_on_big = rect_on_big.rotate(90)换成rect_on_big=rect_on_big.transpose(Image.ROTATE_90)就可以了
可能是这个版本的rotate方法有问题。
if v['rotated']:
#rect_on_big = rect_on_big.rotate(90)
rect_on_big=rect_on_big.transpose(Image.ROTATE_90)
运行截图:
输出文件:
环境:python3.6&Anaconda4.3.0&VS2017