Using PIL(Pillow) to generate thumbnail

缘起相册,我更换到这个主题。
然而,不完美终究还是不完美。不像CMS类博客,纯静态虽然快,但用起来还是有诸多不便。
比如说相册功能,无论如何简化,你还是需要一行一行插入上传后图床上的照片网址。

为了免去这份劳累,写个脚本生成网址看来是必须的咯。于是,第一版脚本的思路就诞生了。

第一版v1.0

第一版代码对应的是先前的博客、自己设计的相册功能用的。因为不太会js所以相册很简单,因而Python也写的很简洁。

主要是为了生成对应的照片网址。因为我把所有图片传到了另外一个repo避免单一repo过大,所以引用照片时需要使用rawcontent对应的github网址。

需要实现的功能有:

  • 遍历指定目录下所有图片类文件的文件名
  • 将文件名字符串进行处理后变为对应的网址

很简单,不是吗?篇幅缘故,只在文末贴最新版代码。这里指明几个所需函数:

1
2
path = os.getcwd() # 得到当前的绝对路径,windows下生成的字符串是反斜杠
os.path.isfile(dir) # 返回值为真伪,确定文件是否存在

第二版v2.0

用了一段时间后,转到这个相册来啦。虽然布局变为了响应式,风格也炫酷了很多,但本质上并没有改变。几乎所有的静态博客解决方案都有用到fancybox这个js插件,以优雅地展示图片。

因而,我的代码不需要怎么变动,就可以直接迁移到这个博客上来啦
总还是有些变化。因为这个博客支持caption显示,因而代码改变一点

1
link = yourlink + '\r\n' + '{:.hascaption}'

需要注意的是,三种常用桌面系统对换行符的定义不同。windows是CRLF而Macintosh和Linux是LF,windows下编程需要注意一点。或者使用现代化的文本编辑器亦不会因此产生问题,如sublime和notepad++。

第二版v2.1

上线一段时间里,都很安逸。尽管页面访问有些慢,但我并没在意。

后来实在是慢的不行了!主页载入平均时间十五秒!天啊:scream:在发现元凶之前,我还以为是图片造成的。的确博客中有很多图片可能会导致载入过慢。于是我便打起了优化图片的主意。

首先是缩小图片尺寸。博文配图统一缩小到长边1800像素以内。图片质量保留70%足够看了。另外就是主页图,别用png带alpha通道了,直接压成jpg中等质量。灰度图真的无所谓的。

优化了一番以后,并没有特别的好转。还是F12一下。

这一看不要紧,原来之前相册载入的时候都是原图。这还了得!每次点开相册需要把所有的原图都载入后才能显示。GIF相册一共40+MB,配上github时不时抽风的网速……之前最长载入花了三分多钟还没成。原来真凶在这里!

如何解决这个问题呢?催生了第二版代码的第一次迭代;

  • 能够生成缩略图
  • 能够正确resize各种类型的图片
  • 能够生成缩略图路径

先从简单的说起。缩略图路径我想了一想,还是生成在不同路径下用相同文件名比较好。这样在增减原始图时不会因为同目录下有缩略图而产生麻烦。

其次是resize。查了一下Python最常用的图像处理库,貌似就是Pillow了。功能够用,效率够高,很好。只是当我写完代码才想到,其实是可以用MATLAB的,那样更熟悉一些……只是运行启动时间更长了。

最后是缩略图。有几个要求:

  • 匹配最长边相等
  • 比例不能变
  • 画质尽可能好点

画质方面,查了文档后发现有bilinear、bicubic和ANTIALIAS三种选项,其中最后一种最佳。

比例方面,当时不知有现成函数,于是自己写了个判断,最长边的问题也一并解决:

1
2
3
4
5
6
7
8
9
w, h = img.size
if w > h:
div = float(w)/h
h = int(320/div)
img.resize((320, h),Image.ANTIALIAS).save(os.path.join(small_path, f))
else:
div = float(h)/w
w = int(320/div)
img.resize((w, 320),Image.ANTIALIAS).save(os.path.join(small_path, f))

Python2.7和Python3有诸多不同,浮点数这个就是有点烦。当时debug花了好几分钟才发现是这里有问题。

第二版v2.2

上面一个版本运行很顺畅。上线以后本以为没问题,可一刷新问题又来了:ul li在垂直对齐时存在一些问题,导致有些图片高,有些图片低。反复试验后发现,横版图片会以上边缘对齐左右居中,而竖版图片则会垂直占满li并左右居中。

调了一小时css无果,遂想到了替代方案:给thumbnail加白边,全都变成1:1比例图片。这招其实我也在以前其它工程中使用过,这里拿来恰巧合适。

继续列需求:

  • 给缩略图加白边形成正方形 - 匹配各个图片长宽
  • 缩略图加边框后居中

研究了一下PIL的函数,并没有合适的拼接类函数,裁剪的倒是很多。最后选用了paste。
另外就是改用了thumbnail类,直接压图无需自己算参数。
浮点和整数转换出了一点小差错。对于图片而言像素长宽均需为整数。
用了一个小技巧,用input函数来暂停程序,类似system(“pause”);异曲同工。

最新版代码于下方更新

v2.2 - 加入了缩略图加白边居中功能
v2.1 - 加入了缩略图功能
v2.0 - 加入了新的链接生成

v1.0 - 初始代码,生成链接

最新版代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
from PIL import Image
import os
import os.path
import sys
def GetFileList(dir, fileList):
newDir = dir
if os.path.isfile(dir):
fileList.append(dir.decode('gbk'))
elif os.path.isdir(dir):
for s in os.listdir(dir):
#if s == "xxx":
#continue
newDir=os.path.join(dir,s)
GetFileList(newDir, fileList)
return fileList
# path = os.getcwd()
fname = raw_input("Please input the dir: (example: awards cannot process GIF!)\n")
path = 'C:\Program Files\Git\usr\yo1995.github.io\photos\page-backup\\' + fname
lcur = len(fname) + 2
small_path = (path[:-1] if path[-1]=='/' else path) + '_m'
print small_path
imgblank = 'templ.jpg'
thumbsize = (600,600)
if not os.path.exists(small_path):
os.mkdir(small_path)
for root, dirs, files in os.walk(path):
for f in files:
fp = os.path.join(root, f)
img = Image.open(fp)
imgb = Image.open(imgblank)
(sizeblankw , sizeblankh)= imgb.size
savepath = os.path.join(small_path, f)
if os.path.isfile(savepath + fp[len(savepath)-1:]):
print u'existing, not writing'
else :
img.thumbnail(thumbsize,Image.ANTIALIAS)
w,h = img.size
print (w,h)
wm = (sizeblankw - w) / 2.0
hm = (sizeblankh - h) / 2.0
box2 = (int(wm) , int(hm) , int(sizeblankw - wm), int(sizeblankh - hm))
print box2
imgb.paste(img , box2)
imgb.save(savepath)
print 'writing file ' + fp
l = len(path)
list = GetFileList(path, [])
with open ('result.txt','wb') as result:
for e in list:
e = e[l-lcur+1:]
e = '- image_title: ' + e[lcur:-4] \
+ '\r\n image_path: "https://raw.githubusercontent.com/yo1995/page-backup/master' + e \
+ '" \r\n thumb_path: "https://raw.githubusercontent.com/yo1995/page-backup/master' + e[:lcur-1] + '_m/'+ e[lcur:] + '"'
e = e.replace("\\", '/')
result.write(e + '\r\n')
raw_input('finished ! press any key to exit')
Share Comments
多说已于2017年6月1日到期,此处不再提供评论功能。如需评论请使用About页面的disqus(GFW)或提交issue。
The previous duoshuo comment system stopped service. Please use disqus or submit issues to comment.