本文一步步为你介绍,如何用Python自动判断多张图片中哪些超出阈值须要压缩,且保持宽高比。若是你想了解Python图像处理的基础知识,欢迎动手来尝试。python
我喜欢用Markdown写文稿,而后发布到不一样写做平台。个人好友数字游民Jarod称其为“矩阵式发布”。能这样作的前提,是Markdown为咱们带来了极低的边际发布成本。试想若是每一个写做平台,都须要我手动插入20-30张图片,想一想都眼晕,我估计马上会打消发布念头。git
我使用七牛做为图床。图片连接成功转换后,选择一款渲染工具,预览文稿格式,看图片、表格、标题等特殊样式是否显示正确。github
我曾经用过多种渲染工具。最近我一直在用Md2All。编程
这款工具最大的特色,是能保证粘贴到各个写做平台时,代码不会乱掉。bash
点击右上方的“复制”按钮,你就能够在任何一个写做平台上,开启富文本编辑器,而后粘贴进去。微信
工做进行到这一步,已近大功告成。这时,若是你遇到“图片上传失败”的报错,想必会很影响心情。网络
图片上传失败,缘由可能有不少。微信公众平台
许多状况下,只是单纯由于网络拥塞。只要你本着愚公移山的精神,往复从新粘贴,总会好的。编辑器
可是微信公众平台是个例外。模块化
你时常会遇到这种状况——就是那两张图片,死活也没法正常传上去。
踩坑屡次,不得不手动上传图片后。我终于发现了问题所在——微信公众平台对图片大小有限制。
一旦你要上传的图片超过2M,就没法正常粘贴上传了。
莫非我写做文章时,还要一一检验每张插图的大小?超过阈值的图片压缩,而后再上传?
对我这种插图爱好者来讲,这个工做太过琐碎和枯燥了。
你可能会问,不是有许多工具能够批量修改图片大小吗?例如JPEGmini和TinyPNG之类的?
确实有,可是它们不彻底符合个人需求。
首先,我并不须要压缩所有图像。压缩后的图片,确实在手机上看起来跟原图毫无区别。但我用的图片,不少是教程里的示例。学生可能须要放大到必定程度,甚至要在大屏幕上打开,来查看代码或者运行结果的细节。只要原图没超过2M,仍是保持原貌比较稳妥。
其次,我懒。每次写完文章,还得手动运行一个应用,找出这篇文章对应的图片,拖动进去……很差意思,这活儿我懒得干。
幸亏,凡是简单重复的枯燥活儿,都是电脑的拿手好戏。不然咱们学编程干什么?
我用Python作个程序,替我找出所有大于2M的图片,进行压缩。压缩的时候,需要保持图片的宽高比例。
若是你对Python图像预处理功能比较感兴趣,不妨跟着个人介绍,一块儿试试看。
我已经为你准备好了样例图片和执行代码,而且存储在了一个Github项目中。请访问这个连接,下载压缩包后,解压查看。
能够看到,在image目录下,有2个png格式的图像文件。
咱们打开来看看,一张cat.png是可爱的猫咪。
另外一张,是小松鼠。
猜猜哪张图片更大?
小松鼠这张图片,尺寸低于2M。猫咪那张,却有2.9M,不符合微信公众平台的要求。
咱们下面要用Python自行判断这些图片中,哪些超过了2M,须要进行压缩。
而后,对超过2M的图片,按照原先的宽高比压缩后,存储到一个指定的文件夹里面去。
咱们使用Python集成运行环境Anaconda。
请到这个网址 下载最新版的Anaconda。下拉页面,找到下载位置。根据你目前使用的系统,网站会自动推荐给你适合的版本下载。我使用的是macOS,下载文件格式为pkg。
下载页面区左侧是Python 3.6版,右侧是2.7版。请选择2.7版本。
双击下载后的pkg文件,根据中文提示一步步安装便可。
安装好Anaconda后,咱们还须要确保安装几个必要的软件包。
请到你的“终端”(Linux, macOS)或者“命令提示符”(Windows)下面,进入我们刚刚下载解压后的样例目录。
执行如下命令:
pip install -U PIL
pip install -U glob
复制代码
安装完毕后,执行:
jupyter notebook
复制代码
这样就进入到了Jupyter笔记本环境。咱们新建一个Python 2笔记本。
这样就出现了一个空白笔记本。
点击左上角笔记本名称,修改成有意义的笔记本名“demo-python-resize-image”。
准备工做完毕,下面咱们就能够用Python读入并处理图像文件了。
咱们首先读入几个后面将用到的软件包。
from glob import glob
from PIL import Image
import os
复制代码
而后,咱们指定图片来源目录。由于图片存储在了样例目录的子目录image下面,因此只须要指定为"image"就行了。
source_dir = 'image'
复制代码
下面咱们设置压缩后图片的输出目录。这里为了对比清晰,咱们将其设定为output,也是样例目录的子目录。注意此时这个目录还不存在。咱们后面会作处理。
target_dir = 'output'
复制代码
下面,是关键环节之一。咱们需要遍历image目录,找出所有的图片名称。
这里咱们用到的,是glob软件包。其中的glob函数能够在咱们指定的目录里,寻找全部符合要求的文件。
filenames = glob('{}/*'.format(source_dir))
复制代码
咱们使用了星号(*)做为通配符,意味着咱们要查找image目录下全部文件的名称。
输出filenames试试看。
print(filenames)
复制代码
['image/squirrel.png', 'image/cat.png']
复制代码
可见filenames是个列表,里面包含了我们须要处理的所有图片文件。
下面,咱们就来尝试检测每张图片的大小。
for filename in filenames:
with Image.open(filename) as im:
width, height = im.size
print(filename, width, height, os.path.getsize(filename))
复制代码
咱们遍历filenames中的全部图片路径,用PIL对象的size属性得到图片的宽度(width)和高度(height)数值。用os.path.getsize()
函数来获取文件大小。
而后,咱们把这些内容按文件分别打印出来。
('image/squirrel.png', 1024, 768, 1466487)
('image/cat.png', 2067, 1163, 2851538)
复制代码
由于咱们须要判断某张图片的大小是否超出微信公众平台设置的2M阈值,所以咱们须要计算一下,2M阈值换算成比特,究竟是个多大的的数字,以便后面的比对。
2*1024*1024
复制代码
计算结果以下:
2097152
复制代码
显然,刚才的打印结果里面,cat.png图像超出了这个阈值。
咱们内心有数了。
下面就把阈值(threshold)设置为这个数值。
threshold = 2*1024*1024
复制代码
咱们来看看本身的直觉和程序判断的实际状况是否一致:
for filename in filenames:
filesize = os.path.getsize(filename)
if filesize >= threshold:
print(filename)
复制代码
此处咱们要求Python打印所有超出阈值的文件路径。结果以下:
image/cat.png
复制代码
测试结果正确。程序只须要调整猫咪照片的尺寸。
正式进行压缩和输出以前,咱们须要创建输出目录。虽然前面咱们设定了,这个子目录叫作output,可是实际的演示目录里,它还还没有建立。
咱们先用os.path.exists()
函数断定这个目录是否存在。当断定为不存在时,咱们采用os.makedirs()
函数来建立它。
if not os.path.exists(target_dir):
os.makedirs(target_dir)
复制代码
下面咱们计算一下,对须要压缩的图片,新的宽度和高度应该是多少。
for filename in filenames:
filesize = os.path.getsize(filename)
if filesize >= threshold:
print(filename)
with Image.open(filename) as im:
width, height = im.size
new_width = 1024
new_height = int(new_width * height * 1.0 / width)
print('adjusted size:', new_width, new_height)
复制代码
咱们把新的宽度设置为了1024,而后按照同等宽高比例算出新的高度取值。
注意这里宽度和高度必须设置为整数类型,不然会报错。
输出结果以下:
image/cat.png
('adjusted size:', 1024, 576)
复制代码
为了把猫咪照片压缩为宽度1024的图片,咱们须要设定高度为576,以保证压缩后的图片与原始图片的宽高比一致。
下面咱们续写函数,正式调用PIL的resize函数将新的图片设定为新的宽度和高度数值。而后,咱们使用PIL的save函数,把生成的图片存储到指定的路径。
for filename in filenames:
filesize = os.path.getsize(filename)
if filesize >= threshold:
print(filename)
with Image.open(filename) as im:
width, height = im.size
new_width = 1024
new_height = int(new_width * height * 1.0 / width)
resized_im = im.resize((new_width, new_height))
output_filename = filename.replace(source_dir, target_dir)
resized_im.save(output_filename)
复制代码
输出结果仍是须要压缩的图片路径。
image/cat.png
复制代码
压缩成功了吗?
咱们打开样例目录看看。
能够看到,output子目录已经自动生成。里面有一张图片。名称依然是cat.png。它的大小已经变成了836KB。咱们打开它,看看显示是否正确。
依然是这张可爱的猫咪。看不出与原图有什么显著的区别,并且宽高比也正常。测试成功。
可是这里,咱们还须要完成一个重要步骤——把以前的代码进行整合。
许多初学者写代码,总会忽略这一步。
虽然你的代码已经成功完成了预期的任务,但如不及时进行整理,过一段时间再来看,你会抓不住头绪。
想一想看,等你回来的时候,你的Jupyter Notebook是这个样子的:
你不只会忘了不一样函数之间的调用关系,并且对于哪些参数须要设定,都一头雾水。
没错,这就是人脑的工做特色——咱们会遗忘。
因此,趁热打铁,把你作过的功能进行模块化整合颇有必要。
整合后,你实现的功能就成了一个有机的总体,只经过参数和外部交互。你只须要用注释告诉本身参数设置的含义。后面再须要调用相关功能的时候,就能够直接经过参数变化,拿来就用了。
趁着记忆犹新,我们把刚刚所有的功能整合到一个函数里面。
def resize_images(source_dir, target_dir, threshold):
filenames = glob('{}/*'.format(source_dir))
if not os.path.exists(target_dir):
os.makedirs(target_dir)
for filename in filenames:
filesize = os.path.getsize(filename)
if filesize >= threshold:
print(filename)
with Image.open(filename) as im:
width, height = im.size
new_width = 1024
new_height = int(new_width * height * 1.0 / width)
resized_im = im.resize((new_width, new_height))
output_filename = filename.replace(source_dir, target_dir)
resized_im.save(output_filename)
复制代码
这个函数暴露给外部的接口,是3个参数:
source_dir
:图片源目录target_dir
:压缩图片输出目录threshold
:阈值检查一下,咱们会发现不对劲的地方——虽然阈值是咱们未来能够调整的选项,可是压缩的时候,图片的宽度倒是手动设定的数值(1024)。这样未来面对一个阈值高出3倍的写做平台,咱们依然把图片压缩到这么小,彷佛有些矫枉过正。
另外,若是这张图片是那种极为长的图,那即使宽度不是很长,也可能会由于高度超出阈值。单纯调整宽度到1024,也许会失效。
解决办法也很简单,咱们设置高度,而后对应调整宽度。
你能够看到,由于咱们把代码集成整理在一处,许多原先咱们可能考虑不周的问题,此时就纷纷显现了出来。
了解了问题所在,咱们来调整一下代码。
咱们由于要经过阈值计算宽度或者高度,因此须要引入数学计算模块。
import math
复制代码
调整后的函数以下:
def resize_images(source_dir, target_dir, threshold):
filenames = glob('{}/*'.format(source_dir))
if not os.path.exists(target_dir):
os.makedirs(target_dir)
for filename in filenames:
filesize = os.path.getsize(filename)
if filesize >= threshold:
print(filename)
with Image.open(filename) as im:
width, height = im.size
if width >= height:
new_width = int(math.sqrt(threshold/2))
new_height = int(new_width * height * 1.0 / width)
else:
new_height = int(math.sqrt(threshold/2))
new_width = int(new_height * width * 1.0 / height)
resized_im = im.resize((new_width, new_height))
output_filename = filename.replace(source_dir, target_dir)
resized_im.save(output_filename)
复制代码
这样,未来不管你的图片目录在哪里,你要知足哪一个写做平台的图片大小要求,均可以经过简单设置这样几个数值,调用函数来完成新需求。
咱们尝试用原先的参数取值,执行一次。
执行以前,咱们删除掉output目录,以测试功能。
而后执行模块化以后的函数。
resize_images(source_dir, target_dir, threshold)
复制代码
执行时,依然只是输出须要压缩的文件路径。
image/cat.png
复制代码
检查刚刚又从新生成的output目录,猫咪照片呢?
没问题。不只显示正常,并且大小也已经正常压缩。
总结一下,经过本文咱们接触到了如下知识点:
更重要的,是咱们尝试了如何用Python这一脚本语言,帮咱们智能化作出判断,而且在后台完成琐碎的重复操做。
另外,你应该已经了解了,完成功能并不意味着完事大吉。为了让本身的代码能够充分重用、易于共享并提升效能,你须要梳理与整合代码,将其充分模块化,只曝露输入输出接口给用户(包括未来的本身),避免固定取值设置。
你以前遇到过须要智能批量调整图片大小的问题吗?你是如何解决的?用过哪些工具?它们能自动帮你判断图片是否须要压缩吗?欢迎留言,把你的经验和思考分享给你们,咱们一块儿交流讨论。
喜欢请点赞。还能够微信关注和置顶个人公众号“玉树芝兰”(nkwangshuyi)。
若是你对数据科学感兴趣,不妨阅读个人系列教程索引贴《如何高效入门数据科学?》,里面还有更多的有趣问题及解法。