先容我啰嗦一下做这个实验的“历史”:
这事儿大概可以追溯到13年10月,当时老陈拉我入伙做一个旅行游记的项目(很可惜我们没有坚持下去),不知道他怎么突发其想(fā shén jīng)打算用python来做后台,我说卧槽哥我不会python啊,他说没事,我也不会。行呗,那就学呗(这就是为什么本文代码会用python来实现)。
然后有一天,老陈突然发了一个“广告帖”给我看,大体内容是关于花瓣网的,其中鼓吹了花瓣网的图片处理技术多么多么牛逼。老陈说他还真试了一下,发现几大游记网站(马蜂窝、蝉游记等)的照片上传后的缩略图质量确实不如上传到花瓣网的,要不咱研究研究?研究成了咱也能吹吹咱网站的图片处理技术。(这就是本文的主题:得到效果更好的缩略图)
1. 几个网站的效果对比
通常,相机(手机)的照片大小都是几个mb的。对于图片类网站(包括旅行游记类网站),不可能在展示时候直接就给你展示原图,那刷图得慢死你。一般的操作是在用户上传照片时就将照片压缩为两个等级的大小,我们暂且称为大缩略图跟小缩略图(服务器空间大的离谱的话你还可以保存原图)。本文主要比较的是大缩略图。
照片原图 | |
面包旅行 40kb 640×424 |
|
蝉游记 35kb 600×300 |
(呐别问我为啥图被裁了,我怎么知道) |
花瓣网 133kb 580×384 |
|
ACDSee 130kb 580×384 |
从上面几张我们可以看到,缩略图效果最好是花瓣网,其次是ACDSee软件缩放处理后的(使用默认参数),然后是蝉游记,面包旅行。缩放到相同尺寸的照片,花瓣跟ACDSee的画面会更清晰,颜色更鲜艳,锯齿也少,当然这也导致他们需要更多的字节来存储。但对于一张用来做展示的照片,100多kb,我觉得是可以接受的。
2. 算法研究
Resize
单纯的只对图像进行缩放,我试验过了python的PIL库跟OPENCV库的几种缩放算法。得出的结果分两种:
第一种,如NEAREST、BILINEAR、BICUBIC算法。缩放后颜色保持的好,但线条处会有锯齿状。
第二中,如PIL的ANTIALIAS算法和OPENCV的AREA算法。缩放后图像有些模糊,但不会出现锯齿状。
Sharpen
为了让缩放后图像又无锯齿,又能保持清晰。我们可以对经过反锯齿缩放(模糊缩放)后得到的图像,再进行一次锐化操作,使图像更加清晰。
经过试验,发现使用 模糊缩放 + 锐化增强 完全可以达到等同于花瓣网和ACDSee的处理效果。
3. 算法实现
现在就是比较python的PIL和OPENCV库中哪个算法实现的效率更快。
读取图像:PIL快,OPENCV慢
缩放:PIL慢,OPENCV快
锐化:PIL快,OPENCV没找到
存图:PIL快,OPENCV快
经过多次试验比较,发现最快效率的步骤是:
使用OPENCV读取图像,
CV2_ImgOriginal = cv2.imread(strSourceFilePathName)
使用OPENCV的模糊缩放,
CV2_ImgResized = cv2.resize(CV2_ImgOriginal, (nWidth, nHeight), None, 0, 0, cv2.INTER_AREA)
将OPENCV图像转换为PIL图像,
CV2_ImgResized = cv2.cvtColor(CV2_ImgResized, cv2.COLOR_BGR2RGB)
PIL_ImgResized = Image.fromarray(CV2_ImgResized)
使用PIL的锐化增强,
PIL_ImgEnhanced = ImageEnhance.Sharpness(PIL_ImgResized).enhance(2.0)
使用PIL的存图函数。
PIL_ImgEnhanced.save(‘_resized.jpg’, ‘JPEG’, quality = 95)
最后的缩放效果与三个参数有关:缩放参数、锐化参数、jpeg存图质量参数。你可以通过调整这几个参数达到最优的效果。
4. 实验效果
花瓣网 133kb 580×384 |
|
缩放+锐化 104kb 580×384 |
照片原图 | |
蝉游记 45kb 530×800 |
|
花瓣网 182kb 580×876 |
|
缩放+锐化 149kb 580×876 |
5. pyton实现代码
import cv2, Image, ImageEnhance, time, os strSourceFolder = raw_input("Input Source Images Folder:") strOutputFolder = raw_input("Input Output Images Folder:") nWidth = int(raw_input("Input Resized Width:")) print "resizing..." StartTime = time.clock() nCounter = 0 for file in os.listdir(strSourceFolder): strSourceFilePathName = os.path.join(strSourceFolder, file) CV2_ImgOriginal = cv2.imread(strSourceFilePathName) fRatio = float(CV2_ImgOriginal.shape[1]) / nWidth nHeight = int(CV2_ImgOriginal.shape[0] / fRatio) CV2_ImgResized = cv2.resize(CV2_ImgOriginal, (nWidth, nHeight), None, 0, 0, cv2.INTER_AREA) CV2_ImgResized = cv2.cvtColor(CV2_ImgResized, cv2.COLOR_BGR2RGB) PIL_ImgResized = Image.fromarray(CV2_ImgResized) PIL_ImgEnhanced = ImageEnhance.Sharpness(PIL_ImgResized).enhance(2.0) strFilename = os.path.splitext(file)[0] PIL_ImgEnhanced.save(os.path.join(strOutputFolder, strFilename) + "_resized.jpg", 'JPEG', quality = 95) nCounter = nCounter + 1 print file, "processed" EndTime = time.clock() print "Processed", nCounter, "images" print "Total elapsed time:", EndTime - StartTime, "Seconds"