2023.06.30 更新:扩充了很多功能,结果还是上传 github了……
GitHub - Eterance/Adaptive-Image-Resize-and-Conversion
警告
下面的内容已过时。请查看上面的 Github 链接。
上传一些 pixiv 美图的时候,原画师放出来的图非常大,如果直接上传对我的穷逼服务器非常不友好。特别是我关闭了 WordPress 自动生成不同尺寸缩略图的功能,所以都是直接发原图给访问者,如果不压缩,十几 M 的图片能让浏览者等到不耐烦
一开始我是用 PS 手动压缩分辨率。但用了一会儿发现很麻烦:
- 不能精准指定把大小压缩到某一个区间,要么过大要么过小,尝试合适的压缩很耗时间;
- 另存为 webp 格式时不支持调整大小。也就是说,要把一个 1000² 的 jpg 压缩并转换格式为 500² 的 webp,需要先压缩成 500² 的 jpg,再另存为 500² 的 webp。
这个时候我就想到:能用程序自动化解决的,就不要靠手工。
于是写了一个小程序。因为太小懒得传 github 了,直接丢博客里。
安装依赖
pip install pillow
或者
conda install pillow
代码
from PIL import Image
import os
import time
# 必要修改:改成你的 被压缩图片 所在的文件夹
picture_folder = r"E:\图片与视频\pixiv"
# 必要修改:改成你的 压缩后图片 保存的文件夹
save_folder = r"C:\Users\Eterance\Desktop"
# 必要修改:改成你的 被压缩图片 的文件名
image_name = "illust_66747002_20191010_103808.jpg"
# 可选修改:改成你的 压缩后图片 的格式,支持 jpg、png、webp
extension = 'webp'
# 可选修改:改成你的 压缩后图片 的后缀,例如 “-已缩小”。如果不需要后缀,改成空字符串 ""
suffix = "-已缩小"
# 可选修改:改成你的 压缩后图片 的质量,范围 0-100,越大越清晰,但是文件越大。推荐 90。
_quality = 90
# 可选修改:改成你的 压缩后图片 的目标大小上限,单位 KB。
target_size_kb_upper = 600
# 可选修改:改成你的 压缩后图片 的目标大小下限,单位 KB。不得大于上限。
target_size_kb_lower = 550
# 下面的代码不懂就不要改
multiple_upper = 1.00
multiple_lower = 0.10
current_multiple = 1
start_time = time.time()
image_file = Image.open(os.path.join(picture_folder, image_name))
image_file = image_file.convert("RGB") # PNG转换为RGB模式,否则无法保存为jpg
width, height = image_file.size
target_file_name = os.path.join(save_folder, f"{image_name.split('.')[0]}{suffix}.{extension}")
# 先尝试原尺寸压缩,如果满足要求就不用缩小了
image_file.save(target_file_name, quality= _quality)
file_size = os.path.getsize(target_file_name)
if (file_size < target_size_kb_upper * 1024):
print(f"{image_name} 分辨率未改变,大小为 {int(file_size/1024)} KB,耗时 {round(time.time() - start_time, 2)} s,质量为 {_quality}")
else:
os.remove(target_file_name)
# 二分法寻找最适合压缩的分辨率
while (current_multiple > 0.1):
current_multiple = round(((multiple_upper + multiple_lower) / 2), 2)
img_resized = image_file.resize((int(width*current_multiple), int(height*current_multiple)), Image.LANCZOS)
img_resized.save(target_file_name, quality= _quality)
file_size = os.path.getsize(target_file_name)
if (file_size > target_size_kb_upper * 1024):
multiple_upper = current_multiple
os.remove(target_file_name)
elif (file_size < target_size_kb_lower * 1024):
multiple_lower = current_multiple
os.remove(target_file_name)
else:
break
resized_width, resized_height = img_resized.size
pixel_percentage = round((resized_width * resized_height) / (width * height), 2)
print(f"已经将 {image_name} 的分辨率缩小到 {int(width*current_multiple)}x{int(height*current_multiple)} ({current_multiple*100}% 边长,{(pixel_percentage*100)}% 像素),大小为 {int(file_size/1024)} KB,耗时 {round(time.time() - start_time, 2)} s,质量为 {_quality}")
如果 压缩后图片保存的文件夹 有重名文件,会直接覆盖。请再三检查目标文件夹有没有重名文件。
上下限区间越小,二分查找到符合要求的分辨率所花费的时间越长。如果你对大小要求不是非常精准,可以适当放宽上下限以减少运行时间。
例子

修改上下限:
target_size_kb_upper = 1400
target_size_kb_lower = 1300
控制台输出
已经将 84603466.jpg 的分辨率缩小到 3360x1519 (42.0% 边长,18.0% 像素),大小为 1345 KB,耗时 15.33 s,质量为 90

作为对比,使用 jpg:
extension = 'jpg'
target_size_kb_upper = 1400
target_size_kb_lower = 1300
控制台输出
已经将 84603466.jpg 的分辨率缩小到 3200x1447 (40.0% 边长,16.0% 像素),大小为 1342 KB,耗时 4.14 s,质量为 90

这就是为什么推荐使用 webp:几乎相同的大小,webp 分辨率更胜一筹;反过来,相同分辨率,webp 更小。
Comments NOTHING