Using PIL(Pillow) to generate thumbnail

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

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

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

首先是缩小图片尺寸。博文配图统一缩小到长边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')
Chen Ting

Chen Ting

The page aimed to exhibit activities & achievements during Ting's undergraduate & graduate period. Meanwhile, other aspects such as lifestyles, literary work, travel notes, etc. would interweave in the narration.

Leave a Comment

Disqus might be GFW-ed. Excited!