如何使用python对图片进行批量压缩详解

2022-07-07 14:01:17
目录
前言使用Python和Pillow模块压缩图片1、优化flag2、渐进式JPEG3、JPEG动态质量使用Python和Selenium模块操纵Squoosh批量压缩图片Python 调用 Selenium总结 

前言

最近在研究怎么对图片资源进行无损压缩,网上也找了一些资料。总而言之,收获不少,所以想对最近的学习做个总结。

无损压缩其实是相对而言的,目的是为了减小图片资源的内存大小但又不影响图片的显示质量。下面我将介绍两种批量压缩图片的方法,方法一是使用python和Pillow模块对图片进行压缩,这个方法对jpeg格式的图片有非常高的压缩效率,但该方法不太适合对png图片进行压缩。另一个方式是使用Python和Selenium模块操纵Squoosh批量压缩图片。

使用Python和Pillow模块压缩图片

Pillow是Python上一个功能非常强大的图形处理库,若本地还没安装,可以通过指令:pip>

我们先用Python写一个简单的保存图片的例子:

from PIL import Image
from io import StringIO
import dynamic_quality

im = Image.open("photo.jpg")
print(im.format,im.size,im.mode)

new_photo = im.copy()
new_photo.thumbnail(im.size,resample=Image.ANTIALIAS)
save_args = {'format':im.format}
if im.format=='JPEG':
    save_args['quality'].value=85

new_photo.save("copy_photo.jpg",**save_args)

1、优化flag

开启optimize设置,这是以CPU耗时为代价节省额外的文件大小,由于本质没变,对图片质量没有丝毫影响。

...
if im.format=='JPEG':
    save_args['quality'].value=85
    save_args['optimize']=True
...

2、渐进式JPEG

当我们将一张图片保存为>

    标准型: JPEG 图片自上而下载入。渐进式: JPEG 图片从模糊到清晰载入。

    渐进式的选项可以在 Pillow 中轻松的启用 (progressive=True)。渐进式文件的被打包时会有一个小幅的压缩。

    ...
    if im.format=='JPEG':
        save_args['quality'].value=85
        save_args['optimize']=True
        save_args['progressive=True']=True
    ...

    3、JPEG动态质量

    最广为人知的减小>quality。很多应用保存 JPEG 时都会设置一个特定的质量数值。

    质量其实是个很抽象的概念。实际上,一张 JPEG 图片的每个颜色通道都有不同的质量。质量等级从 0 到 100 在不同的颜色通道上都对应不同的量化表,同时也决定了有多少信息会丢失。

    在信号域量化是 JPEG 编码中失去信息的第一个步骤。

    我们可以动态地为每一张图片设置最优的质量等级,在质量和文件大小之间找到一个平衡点。我们有以下两种方法可以做到这点:

    Bottom-up: 这些算法是在 8x8 像素块级别上处理图片来生成调优量化表的。它们会同时计算理论质量丢失量和和人眼视觉信息丢失量。

    Top-down: 这些算法是将一整张图片和它原版进行对比,然后检测出丢失了多少信息。通过不断地用不同的质量参数生成候选图片,然后选择丢失量最小的那一张。

    我们选择第二种方法:使用二分法在不同的质量等级下生成候选图片,然后使用 pyssim 计算它的结构相似矩阵 (SSIM) 来评估每张候选图片损失的质量,直到这个值达到非静态可配置的阈值为止。这个方法让我们可以有选择地降低文件大小(和文件质量),但是只适用于那些即使降低质量用户也察觉不到的图片。

    下面是计算动态质量的代码dynamic_quality.py:

    import PIL.Image
    from math import log
    from SSIM_PIL import compare_ssim
    
    
    def get_ssim_at_quality(photo, quality):
        """Return the ssim for this JPEG image saved at the specified quality"""
        ssim_photo = "tmp.jpg"
        # optimize is omitted here as it doesn't affect
        # quality but requires additional memory and cpu
        photo.save(ssim_photo, format="JPEG", quality=quality, progressive=True)
        ssim_score = compare_ssim(photo, PIL.Image.open(ssim_photo))
        return ssim_score
    
    
    def _ssim_iteration_count(lo, hi):
        """Return the depth of the binary search tree for this range"""
        if lo >= hi:
            return 0
        else:
            return int(log(hi - lo, 2)) + 1
    
    
    def jpeg_dynamic_quality(original_photo):
        """Return an integer representing the quality that this JPEG image should be
        saved at to attain the quality threshold specified for this photo class.
    
        Args:
            original_photo - a prepared PIL JPEG image (only JPEG is supported)
        """
        ssim_goal = 0.95
        hi = 85
        lo = 80
    
        # working on a smaller size image doesn't give worse results but is faster
        # changing this value requires updating the calculated thresholds
        photo = original_photo.resize((400, 400))
    
        # if not _should_use_dynamic_quality():
        #     default_ssim = get_ssim_at_quality(photo, hi)
        #     return hi, default_ssim
    
        # 95 is the highest useful value for JPEG. Higher values cause different behavior
        # Used to establish the image's intrinsic ssim without encoder artifacts
        normalized_ssim = get_ssim_at_quality(photo, 95)
        selected_quality = selected_ssim = None
    
        # loop bisection. ssim function increases monotonically so this will converge
        for i in range(_ssim_iteration_count(lo, hi)):
            curr_quality = (lo + hi) // 2
            curr_ssim = get_ssim_at_quality(photo, curr_quality)
            ssim_ratio = curr_ssim / normalized_ssim
    
            if ssim_ratio >= ssim_goal:
                # continue to check whether a lower quality level also exceeds the goal
                selected_quality = curr_quality
                selected_ssim = curr_ssim
                hi = curr_quality
            else:
                lo = curr_quality
    
        if selected_quality:
            return selected_quality, selected_ssim
        else:
            default_ssim = get_ssim_at_quality(photo, hi)
            return hi, default_ssim

    然后在下面的代码中引用计算动态质量的方法:

    ...
    if im.format=='JPEG':
        save_args['quality'],value=dynamic_quality.jpeg_dynamic_quality(im)
        save_args['optimize']=True
        save_args['progressive']=True
    ...

    使用Python和Selenium模块操纵Squoosh批量压缩图片

    Squoosh>

    Python>

    这是 Squoosh 的主界面,Select an Image 其实是一个输入框,那我们直接用 Selenium 把本地图片的路径输入进去就行了:

    输入图片路径之后就会默认压缩成 75% 质量的 MozJPEG,我觉得无论是压缩比和质量都很不错,所以就没有改,等待页面加载完成之后就直接下载:

    我们可以认为出现 "..% smaller" 就算是压缩完成,这时候直接点击右边的下载按钮即可。

    代码:

    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support.wait import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver.support.ui import Select
    import os
    import re
    driver = webdriver.Chrome('C:/Users/admin/AppData/Local/Google/Chrome/Application/chromedriver.exe')
    # 列出目录下所有的图片,存在 images 这个列表中
    images = os.listdir('C:/Users/admin/Pictures/Saved Pictures')
    # 处理所有图片
    for i in range(len(images)):
        # 构建图片路径
        path = 'C:/Users/admin/Pictures/Saved Pictures/' + images[i]
        # 尝试处理所有图片
        try:
            # 打开 Squoosh
            driver.get('https://squoosh.app')
            # 找到输入框
            input_box = driver.find_element_by_xpath('.//input[@class="_2zg9i"]')
            # 输入图片路径
            input_box.send_keys(path)
            #设置图片格式
            select1 = Select(driver.find_elements_by_css_selector('select')[-1])
            if re.match('.*.png',images[i]):
                select1.select_by_value("png")
            if re.match('.*.jpg',images[i]):
                select1.select_by_value("mozjpeg")
    
            # 等待出现 'smaller'字样,10秒不出现则视为处理失败
            locator = (By.XPATH, './/span[@class="_1eNmr _1U8bE"][last()]')
            WebDriverWait(driver, 25).until(EC.text_to_be_present_in_element(locator, 'smaller'))
    
            # 找到下载按钮
            button = driver.find_elements_by_xpath('.//a[@title="Download"]')[-1]
            # 点击下载按钮
            button.click()
        # 输出处理失败的图片路径
        except:
            print('*'*30)
            print('Error: '+ path +' failed!')
            print('*'*30)
            continue

    总结 

    到此这篇关于如何使用python对图片进行批量压缩的文章就介绍到这了,更多相关python图片批量压缩内容请搜索易采站长站以前的文章或继续浏览下面的相关文章希望大家以后多多支持易采站长站!