Using PIL(Pillow) to generate thumbnail
缘起相册,我更换到这个主题。 然而,不完美终究还是不完美。不像CMS类博客,纯静态虽然快,但用起来还是有诸多不便。 比如说相册功能,无论如何简化,你还是需要一行一行插入上传后图床上的照片网址。
为了免去这份劳累,写个脚本生成网址看来是必须的咯。于是,第一版脚本的思路就诞生了。
第一版v1.0
第一版代码对应的是先前的博客、自己设计的相册功能用的。因为不太会js所以相册很简单,因而Python也写的很简洁。
主要是为了生成对应的照片网址。因为我把所有图片传到了另外一个repo避免单一repo过大,所以引用照片时需要使用rawcontent对应的github网址。
需要实现的功能有:
- 遍历指定目录下所有图片类文件的文件名
- 将文件名字符串进行处理后变为对应的网址
很简单,不是吗?篇幅缘故,只在文末贴最新版代码。这里指明几个所需函数:
path = os.getcwd() # 得到当前的绝对路径,windows下生成的字符串是反斜杠
os.path.isfile(dir) # 返回值为真伪,确定文件是否存在
第二版v2.0
用了一段时间后,转到这个相册来啦。虽然布局变为了响应式,风格也炫酷了很多,但本质上并没有改变。几乎所有的静态博客解决方案都有用到fancybox这个js插件,以优雅地展示图片。
因而,我的代码不需要怎么变动,就可以直接迁移到这个博客上来啦 总还是有些变化。因为这个博客支持caption显示,因而代码改变一点
- 20180728 更新:hascaption的class已替换为直接alt,即正常的图片插入格式
link = yourlink + '\r\n' + '{:.hascaption}'
需要注意的是,三种常用桌面系统对换行符的定义不同。windows是CRLF而Macintosh和Linux是LF,windows下编程需要注意一点。或者使用现代化的文本编辑器亦不会因此产生问题,如sublime和notepad++。
第二版v2.1
上线一段时间里,都很安逸。尽管页面访问有些慢,但我并没在意。
后来实在是慢的不行了!主页载入平均时间十五秒!天啊在发现元凶之前,我还以为是图片造成的。的确博客中有很多图片可能会导致载入过慢。于是我便打起了优化图片的主意。
首先是缩小图片尺寸。博文配图统一缩小到长边1800像素以内。图片质量保留70%足够看了。另外就是主页图,别用png带alpha通道了,直接压成jpg中等质量。灰度图真的无所谓的。
优化了一番以后,并没有特别的好转。还是F12一下。
这一看不要紧,原来之前相册载入的时候都是原图。这还了得!每次点开相册需要把所有的原图都载入后才能显示。GIF相册一共40+MB,配上github时不时抽风的网速……之前最长载入花了三分多钟还没成。原来真凶在这里!
如何解决这个问题呢?催生了第二版代码的第一次迭代;
- 能够生成缩略图
- 能够正确resize各种类型的图片
- 能够生成缩略图路径
先从简单的说起。缩略图路径我想了一想,还是生成在不同路径下用相同文件名比较好。这样在增减原始图时不会因为同目录下有缩略图而产生麻烦。
其次是resize。查了一下Python最常用的图像处理库,貌似就是Pillow了。功能够用,效率够高,很好。只是当我写完代码才想到,其实是可以用MATLAB的,那样更熟悉一些……只是运行启动时间更长了。
最后是缩略图。有几个要求:
- 匹配最长边相等
- 比例不能变
- 画质尽可能好点
画质方面,查了文档后发现有bilinear、bicubic和ANTIALIAS三种选项,其中最后一种最佳。
比例方面,当时不知有现成函数,于是自己写了个判断,最长边的问题也一并解决:
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.3
又遇到问题了0916.想在目录里放子目录,读取到是没问题,写目录时因为偷懒没有匹配好,导致输出的thumb地址错误。重新考虑了如何匹配,新的代码见下面。
提取文件名换成了os.path.splitext(os.path.basename(path))[0]
最新版代码于下方更新
v2.3 - 修改了文件目录生成的逻辑 v2.2 - 加入了缩略图加白边居中功能 v2.1 - 加入了缩略图功能 v2.0 - 加入了新的链接生成
v1.0 - 初始代码,生成链接
最新版代码
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) # iterate to get the final file
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, fp[len(path)+1:])
savecheck = os.path.join(small_path, fp[len(path)+1:-len(f)])
# print 'sp' + savepath
# print 'fp' + fp
# print 'f' + f
if not os.path.exists(savecheck):
os.mkdir(savecheck)
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
reload(sys)
sys.setdefaultencoding('utf8')
l = len(path)
list = GetFileList(path, [])
with open ('result.txt','wb') as result:
for e in list: # record the main path towards file
e2 = e[l-lcur+1:] # l is the length of PATH while lcur is the length of Current Main Path
e = '- image_title: ' + os.path.splitext(os.path.basename(e))[0] \
+ '\r\n image_path: "https://raw.githubusercontent.com/yo1995/page-backup/master' + e2 \
+ '" \r\n thumb_path: "https://raw.githubusercontent.com/yo1995/page-backup/master' + e2[:lcur-1] + '_m/'+ e2[lcur:] + '"'
e = e.replace("\\", '/')
result.write(e + '\r\n')
raw_input('finished ! press any key to exit')
Leave a Comment