【问题标题】:Determining JPG quality in Python (PIL)在 Python (PIL) 中确定 JPG 质量
【发布时间】:2011-05-20 06:22:57
【问题描述】:

我正在使用 Python 中的 PIL 库,我想知道如何确定给定 JPG 图像的质量。我尝试打开 JPG 图像对其进行处理并以原始质量再次保存。 Image.save 让我确定所需的质量:

im.save(name, quality = x)  

但我看不到任何提取原始文件的方法。现在我只是猜测并尝试通过对“质量”参数进行二进制搜索来获得与输入大小相同的输出文件,但这是不可接受的长期解决方案:)
我也尝试过使用:Image.info,但我的大多数图像都没有任何有用的信息(例如:'adobe'、'icc_profile'、'exif'、'adobe_transform')
救命!

【问题讨论】:

  • 一般来说,不可能恢复用于压缩 JPEG 文件的精确质量值,因为有多种不同的方法可以减少存储的信息,并且质量值只是编码器的指导,但是,如@unutbu 指出,有些软件可以做出大胆的猜测。
  • 值得一提的是,JPEG 是一种有损格式,因此即使“质量”选项设置为相同的值,打开和保存图像也会降低图像质量。

标签: python image jpeg


【解决方案1】:

据我了解,这是不可能的。 JPEG 格式通过删除 75% 的颜色数据或通过简化颜色进行压缩。没有办法让颜色质量更高。

【讨论】:

  • JPEG 不会“通过删除 75% 的颜色数据或通过简化颜色进行压缩”。它确实将颜色空间转换为更有效的颜色空间 (YCbCr),但这只是第一步。
  • 仅供参考:JPEG 通常会通过删除 75% 的颜色数据(4:2:0 子采样)进行压缩。
  • 色度二次采样未预定义。您可以使用所需的任何色度二次采样来获得 JPEG。大多数实现都默认使用 4:2:0 二次采样,因为它是有意义的——我们的视觉在 BW 通道上比在色度通道上更清晰。但是 JPEG 图像也可以有 4:4:4(无子采样)或任何其他子采样。在 PIL 中设置 subsampling=0 应该会关闭子采样。
【解决方案2】:

质量是用于生成存储在 JPEG 中的数据的东西。此数字不存储在 JPEG 中。

您可能能够确定质量的一种方法是在编辑之前获取图像的左上角 8x8 像素单元格,然后对其运行 JPEG 压缩公式以接近原始图像。您需要开发一个从结果到原始(像素差异)的距离函数。

您仍将执行高质量的二分搜索,但工作量要少得多。

以下是有关 JPEG 压缩工作原理的信息

https://www.dspguide.com/ch27/6.htm

这是 MS 常见问题解答中的另一种方式

https://support.microsoft.com/kb/324790

你必须从 C# 翻译。

【讨论】:

    【解决方案3】:

    在 PIL(以及大多数使用 libjpeg 的所有软件/库)中,质量设置用于构建量化表 (ref.)。在 libjpeg 中,质量数“缩放”样本表值(来自 JPEG 规范第 K.1 节)。在其他库中,有不同的表格分配给不同的质量(例如:Photoshop、数码相机)。

    所以,换句话说,质量等于量化表,所以它比一个数字更复杂。

    如果你想以相同的“质量”保存你的修改图像,你只需要使用相同的量化表。幸运的是,量化表嵌入在每个 JPEG 中。不幸的是,在 PIL 中保存时无法指定量化表。 cjpeg,一个 libjpeg 附带的命令行实用程序,可以做到这一点。

    这里有一些粗略的代码,用指定的量化表保存 jpeg:

    from subprocess import Popen, PIPE
    from PIL import Image, ImageFilter
    
    proc = Popen('%s -sample 1x1 -optimize -progressive -qtables %s -outfile %s' % ('path/to/cjpeg', '/path/ta/qtable', 'out.jpg'), shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
    P = '6'
    if im.mode == 'L':
        P = '5'
    stdout, stderr = proc.communicate('P%s\n%s %s\n255\n%s' % (P, im.size[0], im.size[1], im.tostring()))
    

    您需要找到从原始 jpeg 中提取量化表的方法。 djpeg 可以做到这一点(libjpeg 的一部分):

    djpeg -verbose -verbose image.jpg > /dev/null
    

    您还需要找到并设置采样。有关该检查的更多信息here。也可以看test_subsampling

    更新

    我做了一个 PIL fork 以增加在保存 JPEG 时指定子采样或量化表或两者的可能性。您也可以在保存时指定quality='keep',图像将使用与原始相同的量化表和二次采样(原始需要是JPEG)进行保存。还有一些预设(基于 Photoshop),您可以在保存时将其传递给质量。 My fork.

    更新 2

    我的代码现在是Pillow 2.0 的一部分。所以就这样做吧:

    pip install Pillow
    

    【讨论】:

    • 量化表也可以在PIL.JpegImagePlugin.JpegImageFilequantization 属性中使用(这是您在Image.open JPEG 文件时获得的类型)。
    • 很高兴知道。所以 PIL 中唯一缺少的是一种在保存时指定(传递)量化表的方法。
    • Sweet,尤其是它适用于 3.3;我很高兴你记得这个老问题:)
    • quality="keep" 对我不起作用。我正在使用枕头 2.5.1。在 Pillow Github repo 上报告了一个问题。 github.com/python-pillow/Pillow/issues/857有人可以帮忙吗?
    • im.save 是否等同于 image_file.write,有没有办法将保存质量解析为 .write ?
    【解决方案4】:

    我已经使用 PIL 5.1 测试了 quality='keep' 关键字。它产生与默认质量 75 完全相同的结果。

    from PIL import Image
    img=Image.open('yy.jpg')
    img.save('xx.jpg', quality='keep')
    img.save('xx1.jpg', quality=75)
    img.save('xx2.jpg') # no quality keyword so default is applied
    
    import sh
    for i, f in enumerate(('yy.jpg', 'xx1.jpg', 'xx2.jpg')):
        try:
            a = ('xx.jpg', f)
            r = sh.diff(*a)
        except sh.ErrorReturnCode as e:
            r = e.stdout
        r = r.rstrip()
        r = r if r else 'are the same'
        print i, a, r
    

    【讨论】:

      【解决方案5】:

      我在将 quality='keep' 与一些 PIL 操作结合使用时遇到问题,因为例如在 rotate()transpose() 期间,正在创建一个新的 Image 实例,该实例丢失了一些属性,例如 format 和 @987654325 @。

      我不得不查看 Pillow 的来源才能弄清楚,但似乎你也可以这样做:

      def _save_image_matching_quality(img, original_img, fp):
          frmt = original_img.format
      
          if frmt == 'JPEG':
              quantization = getattr(original_img, 'quantization', None)
              subsampling = JpegImagePlugin.get_sampling(original_img)
              quality = 100 if quantization is None else -1
              img.save(fp, format=frmt, subsampling=subsampling, qtables=quantization, quality=quality)
          else:
              img.save(fp, format=frmt, quality=100)
      

      它应该做 quality='keep' 所做的一切:)

      但是,这个确切的代码可能并不适合每个用例,您可能需要对其进行调整。我试图实现的是尽可能多地节省空间,但不影响图像质量作为最高优先级。

      对于一般用例,这可能会更好:

      def _save_image_matching_quality(img, original_img, fp):
          frmt = original_img.format
      
          if frmt == 'JPEG':
              quantization = getattr(original_img, 'quantization', None)
              subsampling = JpegImagePlugin.get_sampling(original_img)
              img.save(fp, format=frmt, subsampling=subsampling, qtables=quantization)
          else:
              img.save(fp, format=frmt)
      

      【讨论】:

      • quality = 100 if quantization is None else 0 对我不起作用。把它排除在外。
      • @KeesC.Bakker 在最新版本的 Pillow 中,应该是 quality = 100 if quantization is None else -1,因为他们已经进行了一些内部更改。
      猜你喜欢
      • 1970-01-01
      • 2012-05-11
      • 1970-01-01
      • 2023-04-03
      • 2015-12-18
      • 2010-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多