859 字
4 分钟
Python 压缩图像
2022-01-24

前言#

由于服务器中的部分磁盘空间拿去做深度学习的虚拟内存,导致服务器的剩余磁盘空间捉襟见肘,所以就想着去删掉点文件或者压缩一些文件,首要目标就盯上博客的配图了。

我在网上找的了 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 cv2
from 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 转换带 8Bit
def 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)

Footnotes#

  1. [Python] - 图像处理 ------ img.convert()

Python 压缩图像
https://fuwari.vercel.app/posts/编程/python/python-压缩图像/
作者
Asuwee
发布于
2022-01-24
许可协议
CC BY-NC-SA 4.0