图像压缩可以是有损的,也可以是无损的。无损压缩是档案用途的首选,通常用于医学成像、技术图纸、剪贴画或漫画。有损压缩方法,尤其是在低比特率下使用时,会产生压缩伪影。有损方法特别适用于自然图像,例如照片,在这种应用中,可以接受轻微(有时难以察觉)的保真度损失,以实现比特率的大幅降低。产生可忽略不计的差异的有损压缩可以称为视觉无损。

在给定压缩率(或比特率)下获得最佳图像质量是图像压缩的主要目标,但是,图像压缩方案还有其他重要属性:

可伸缩性通常是指通过操纵比特流或文件(无需解压和重新压缩)实现的质量降低。可伸缩性的其他名称是渐进式编码或嵌入式比特流。尽管其性质相反,但可伸缩性也可以在无损编解码器中找到,通常以从粗到细的像素扫描形式出现。可伸缩性对于在下载图像时预览图像(例如,在 Web 浏览器中)或提供对数据库等的可变质量访问特别有用。可伸缩性有几种类型:

感兴趣区域编码:图像的某些部分的编码质量高于其他部分。这可以与可扩展性相结合(首先对这些部分进行编码,然后再对其他部分进行编码)。元信息:压缩数据可能包含有关图像的信息,可用于对图像进行分类、搜索或浏览。此类信息可能包括颜色和纹理统计信息、小预览图像以及作者或版权信息。

处理能力:压缩算法需要不同数量的处理能力来编码和解码。一些高压缩算法需要高处理能力。

压缩方法的质量通常用峰值信噪比来衡量。它衡量的是图像有损压缩引入的噪声量,然而,观看者的主观判断也被视为一项重要衡量标准,或许是最重要的衡量标准。

在我们深入压缩图像之前,让我们创建一个函数,以友好的格式打印文件大小:

 def get_size_format(b, factor=1024, suffix="B"):
 ​
     for unit in ["", "K", "M", "G", "T", "P", "E", "Z"]:
         if b < factor:
             return f"{b:.2f}{unit}{suffix}"
         b /= factor
     return f"{b:.2f}Y{suffix}"

接下来,让我们来制作压缩图像的核心函数:

 def compress_img(image_name, new_size_ratio=0.9, quality=90, width=None, height=None, to_jpg=True):
 ​
     img = Image.open(image_name)
     print("[*] Image shape:", img.size)
     image_size = os.path.getsize(image_name)
     print("[*] Size before compression:", get_size_format(image_size))
     if new_size_ratio < 1.0:
         img = img.resize((int(img.size[0] * new_size_ratio), int(img.size[1] * new_size_ratio)), Image.ANTIALIAS)
         print("[+] New Image shape:", img.size)
     elif width and height:
         img = img.resize((width, height), Image.ANTIALIAS)
         print("[+] New Image shape:", img.size)
     filename, ext = os.path.splitext(image_name)
     if to_jpg:
         new_filename = f"{filename}_compressed.jpg"
     else:
         new_filename = f"{filename}_compressed{ext}"
     try:
         img.save(new_filename, quality=quality, optimize=True)
     except OSError:
         img = img.convert("RGB")
         img.save(new_filename, quality=quality, optimize=True)
     print("[+] New file saved:", new_filename)
     new_image_size = os.path.getsize(new_filename)
     print("[+] Size after compression:", get_size_format(new_image_size))
     saving_diff = new_image_size - image_size
     print(f"[+] Image size change: {saving_diff/image_size*100:.2f}% of the original image size.")

现在我们已经有了核心函数,让我们使用 argparse 模块将其与命令行参数集成:

 if __name__ == "__main__":
     import argparse
     parser = argparse.ArgumentParser(description="Simple Python script for compressing and resizing images")
     parser.add_argument("image", help="Target image to compress and/or resize")
     parser.add_argument("-j", "--to-jpg", action="store_true", help="Whether to convert the image to the JPEG format")
     parser.add_argument("-q", "--quality", type=int, help="Quality ranging from a minimum of 0 (worst) to a maximum of 95 (best). Default is 90", default=90)
     parser.add_argument("-r", "--resize-ratio", type=float, help="Resizing ratio from 0 to 1, setting to 0.5 will multiply width & height of the image by 0.5. Default is 1.0", default=1.0)
     parser.add_argument("-w", "--width", type=int, help="The new width image, make sure to set it with the `height` parameter")
     parser.add_argument("-hh", "--height", type=int, help="The new height for the image, make sure to set it with the `width` parameter")
     args = parser.parse_args()
 ​
     print("="*50)
     print("[*] Image:", args.image)
     print("[*] To JPEG:", args.to_jpg)
     print("[*] Quality:", args.quality)
     print("[*] Resizing ratio:", args.resize_ratio)
     if args.width and args.height:
         print("[*] Width:", args.width)
         print("[*] Height:", args.height)
     print("="*50)
 ​
     compress_img(args.image, args.resize_ratio, args.quality, args.width, args.height, args.to_jpg)

现在使用我们的脚本。首先,让我们使用不带任何参数的脚本:

 $ python compress_image.py sample-images.png

输出:

 ==================================================
 [*] Image: sample-images.png
 [*] To JPEG: False
 [*] Quality: 90
 [*] Resizing ratio: 1.0
 ==================================================
 [*] Image shape: (953, 496)
 [*] Size before compression: 425.65KB
 [+] New file saved: sample-images_compressed.png
 [+] Size after compression: 379.25KB
 [+] Image size change: -10.90% of the original image size.

图像大小从 425.65KB 减少到 379.25KB,减少了约 11%。接下来,让我们尝试传递 -j 以将 PNG 转换为 JPEG: