愚蠢的地球人

用Python批量压缩图片来给你的手机相册瘦身

我每次换手机都会把旧手机里的照片备份出来拷贝进新手机里,于是现在我的手机里面就有了几十个GB的照片。但是以手机摄像头的成像质量,一张照片动不动就5到10MB的尺寸实在是太浪费手机的存储空间了。

于是我开始在网上寻找能给相册瘦身的APP,终于我找到了一款叫“相册管家”的应用。这是一个腾讯出品的软件,功能非常多,照片压缩只是它附带的一个小功能。

经过几个小时的等待,我手机上30多个G的照片被压缩到了不到5个G。

虽然这个压缩比令我非常满意,但是我却发现了两个问题。

首先,这个软件默认的jpeg输出质量大约是60(根据尺寸推算),而且无法调整,虽然按这个压缩率输出的照片质量勉强能接受,但是仔细看还是能看出来明显的画质损失。

仔细看以上两张照片上天空的色彩,质量60的那张照片出现了明显的条纹。


下面这张图片是质量设置为80之后输出的照片,几乎跟原图没有肉眼可见的质量损失。


再来看看文件尺寸,原图8.69MB,质量80压缩后是2.01MB,质量60压缩之后是1.14MB。

综合考虑,我个人认为质量设置为80更合适一些。

画质问题勉强还能接受,最终让我卸载掉“相册管家”这个APP的原因是这个软件在压缩图片的时候居然把图像的Exif信息丢弃了。没有了Exif信息,手机相册APP就无法对照片按时间来排序,失去了时间排序这个重要功能之后,你就很难从上万张照片里找到你想要找的照片。这是绝对令人无法接受的!

怎么办?突然想到了最近热门的Python,据说入门超级简单,说干就干,马上下载了几本Python的入门教程,现学现用,自己撸了一个批量压缩图片的程序。


首先导入pillow这个模块:

from PIL import Image

然后压缩:

img = Image.open(file)
img.save(file, quality=80)

没错,只需要三行代码!python的图像的压缩就是这么简单!


不过上面的代码还是有一点小问题:跟“相册管家”一样,保存的图片没有exif信息。

别急,pillow的save方法默认是不保留exif信息的,想要保留exif信息,需要先读出图片的exif,然后作为参数传入到save里面。代码修改一下:

img = Image.open(file)
exif = img.info['exif']
img.save(fileexif=exif, quality=80)

大功告成!


不过上万张图片可不能一张图片一张图片的来处理,我们需要做一个批量处理:

先把压缩单张图片的代码写成一个函数(加入了try语句是为了在压缩出现错误的情况下,不中断操作,能继续处理下一个文件),然后使用glob.glob()来遍历目录下所有的jpg图片:

完整代码如下:

from PIL import Image


def zip_jpg(file):
    try:
        img = Image.open(file)
        exif = img.info['exif']
        img.save(fileexif=exif, quality=80)
        return os.path.getsize(file)
    except Exception as err:
        print(err)
        print(traceback.format_exc())
        return 0


WORK_DIR = '/storage/emulated/0/DCIM/Camera'
JPG_FILTER = os.path.join(WORK_DIR, '*.[jJ][pP][gG]')

for file in glob.glob(JPG_FILTER):
    filesize = os.path.getsize(file)
    new_size = zip_jpg(file)
    print('%s %d --> %d' % (os.path.split(file)[1], filesize, new_size))

一共17行代码,搞定!

可以在PC上运行,也可以在安卓手机上安装Pydroid 3来运行。


最后考虑到手机的相机图片相册里会经常新增图片,为了不每次运行程序的时候都把所有的图片文件都压缩一遍,我将代码进一步完善,加入了记录已压缩过的图片的功能,避免每次运行都重复压缩文件,最终代码如下:

from PIL import Image


WORK_DIR = '/storage/emulated/0/DCIM/Camera'
JPG_FILTER = os.path.join(WORK_DIR, '*.[jJ][pP][gG]')


def zip_jpg(file):
    try:
        img = Image.open(file)
        exif = img.info['exif']
        img.save(fileexif=exif, quality=80)
        return os.path.getsize(file)
    except Exception as err:
        print(err)
        print(traceback.format_exc())
        return 0


def zip_jpgs():
    filecount = 0
    filetotal = len(glob.glob(JPG_FILTER))
    zipped = set()
    list_file =  os.path.join(WORK_DIR, 'zipped.list')
    with open(list_file, mode='a+'encoding='utf-8'as f:
        f.seek(0)
        for line in f.readlines():
            line = line.strip()
            if not line:
                continue
            zipped.add(line.split(',')[0])

        for file in glob.glob(JPG_FILTER):
            filecount += 1
            if os.path.split(file)[1in zipped:
                print(os.path.split(file)[1], 'Already Zipped''(%d/%d)' % (filecount, filetotal))
            else:
                filesize = os.path.getsize(file)
                new_size = zip_jpg(file)
                f.write('%s,%d,%d\n' % (os.path.split(file)[1], filesize, new_size))
                print('%s %d --> %d (%d/%d)' % (os.path.split(file)[1], filesize, new_size, filecount, filetotal))


if __name__ == '__main__':
    zip_jpgs()

最后,为了假装自己是个程序猿,我在GitHub注册了一个账号,把这几十行的代码Push了上去,瞬间感觉高大上了。

https://github.com/laukeng/jpeg


图片压缩的问题算是解决了,但是我又发现了一些新的问题:很多以前的手机拍摄的照片中exif信息不完整,或者exif中的拍摄时间是错误的,导致了相册里排序出现了混乱,还有就是很多照片的命名不统一(现在的智能手机会以“IMG_20191129_182330.jpg”这样的格式来给照片命名),下次我再做一个exif日期修复和文件名批量重命名的程序。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

相关推荐

友情链接

网站分类

最新留言

最近发表