前言
由于服务器中的部分磁盘空间拿去做深度学习的虚拟内存,导致服务器的剩余磁盘空间捉襟见肘,所以就想着去删掉点文件或者压缩一些文件,首要目标就盯上博客的配图了。
我在网上找的了 TinyPNG 这个在线压缩图片的网站,基本上每张图片能减小 80% 左右的存储大小,我就很好奇它怎么实现的,就自己上手尝试了一下进行图片压缩。
图像压缩
尝试使用了 opencv 和 Image 两个库对图片进行压缩,流程和结果差别都不大,都是读取图片后,以更低质量保存图片(应该是编码方式的变化),但实际试过之后,发现这种方法相比 TinyPNG 差距非常大:
左图是 TinyPng 压缩,右图是使用 cv 方法压缩
在仔细阅读了官网信息,发现了下面这句话:
By reducing the number of colors, 24-bit PNG files can be converted to much smaller 8-bit indexed color images. 通过减少颜色数量,24 位 PNG 文件可以转换为更小的 8 位索引彩色图像。
TinyPNG 通过将像素表示从 24 bit 转到 8 bit 从而实现压缩,并且它 80% 左右的压缩率也符合这一规律,这个过程和彩色图像转换到灰度图很相似,我就去查询相关方法,找到了 Image 中的 Image.convert() 1,发现里面有个 ‘P’ 参数:
P: 8位像素,使用调色板映射到任何其他模式
通过这个方法对图片进行转换,下面是尝试过后的结果:
左边是原始图像,中间是用 Python 压缩的结果,右边是 TinyPNG 压缩的
图像的压缩效果比 TinyPNG 都要好??
下图中,左边是通过 Python 压缩的,边是 TinyPNG 压缩的,中间是原图像
整体上三者差距比较小,但在膝盖、头发部位能明显看到 TinyPNG 相比通过 Python 压缩的要好很多,他们应该还在很多地方做了不少优化。
测试代码
import os# import cv2from PIL import Image
# 获取当前目录下所有 png 图像的路径def getFilePaths(): fileList = [] for path in os.listdir(os.getcwd()): root, ext = os.path.splitext(path) if ext == ".png" or ext == ".PNG": fileList.append(path) return fileList
# 获取文件大小(kB)def getFileSize(path): size = os.path.getsize(path) return size / 1024
# # opencv 对图像进行压缩# def cv2Compress(fileList):# # TODO: 在当前目录下创建 runs 文件夹,用来保存压缩过后的图像# if not os.path.exists("./runs/"):# os.makedirs("./runs/")# # TODO: 逐一压缩图像并进行保存# for path in fileList:# img = cv2.imread(path)# cv2.imwrite("./runs/"+os.path.basename(path), img, [cv2.IMWRITE_PNG_COMPRESSION, 9])
# Image 对图像进行压缩def ImageCompress(fileList): # TODO: 在当前目录下创建 runs 文件夹,用来保存压缩过后的图像 if not os.path.exists("./runs/"): os.makedirs("./runs/") # TODO: 逐一压缩图像并进行保存 for path in fileList: img = Image.open(path) img.save("./runs/"+os.path.basename(path), quality=90) analyze(path, "./runs/"+os.path.basename(path))
# Image 对图像从 24Bit 转换带 8Bitdef ImageConvert8Bit(fileList): # TODO: 在当前目录下创建 runs 文件夹,用来保存压缩过后的图像 if not os.path.exists("./runs/"): os.makedirs("./runs/") # TODO: 逐一压缩图像并进行保存 for path in fileList: img24 = Image.open(path) # https://blog.csdn.net/icamera0/article/details/50843172 img8 = img24.convert('P') img8.save("./runs/"+os.path.basename(path)) analyze(path, "./runs/"+os.path.basename(path))
# 对原始图像和压缩图像进行对比分析def analyze(origin, compress): originSize = getFileSize(origin) compressSize = getFileSize(compress) print(os.path.basename(origin)+ " 减小大小:{:.2f}kB {:.1%}".format(originSize-compressSize, 1-compressSize/originSize))
if __name__ == "__main__": fileList = getFilePaths() ImageConvert8Bit(fileList)