在Python中确定JPG质量(PIL)
我正在玩Python中的PIL库,想知道怎么判断一个JPG图片的质量。我尝试打开JPG图片,做一些处理后再保存,想保持它的原始质量。Image.save让我可以设置想要的质量:
im.save(name, quality = x)
但是我找不到提取原始质量的方法。目前我只能猜测,通过对“质量”参数进行二分查找,试图让输出文件的大小和输入文件一样,但这不是一个长久之计 :)
我也试过使用:Image.info,但我大部分的图片在这里没有任何有用的信息(比如:'adobe','icc_profile','exif','adobe_transform')
求助!
6 个回答
我在使用 quality='keep'
和一些PIL操作时遇到了一些问题,比如在调用 rotate()
或 transpose()
时,会创建一个新的图像实例,这样就会丢失一些属性,比如 format
(格式)和 quantization
(量化)。
我不得不查看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)
质量是用来生成存储在JPEG文件中的数据的一个指标。这个数字并没有被直接存储在JPEG文件里。
你可以通过一种方法来判断质量,那就是在编辑图片之前,取出左上角的8x8像素的小块,然后对这部分使用JPEG压缩公式,这样可以得到一个接近原始图像的结果。接下来,你需要开发一个距离函数,用来计算这个结果和原始图像之间的像素差异。
虽然你还是需要进行质量的二分查找,但这样做的工作量会小很多。
这里有关于JPEG压缩工作原理的信息
https://www.dspguide.com/ch27/6.htm
这是来自微软常见问题解答的另一种方法
https://support.microsoft.com/kb/324790
你需要将这些内容从C#转换过来。
在PIL(以及大多数使用libjpeg的所有软件/库中,质量设置用于构建量化表(参考)。在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
你还需要找到并设置采样。有关更多信息,可以查看这里。你也可以查看test_subsampling
。
更新
我对PIL进行了一个分支,增加了在保存JPEG时指定子采样或量化表或两者的功能。你还可以在保存时指定quality='keep'
,这样图像将以与原始图像相同的量化表和子采样保存(原始图像需要是JPEG格式)。在保存时,还有一些基于Photoshop的预设可以传递给质量设置。我的分支。
更新 2
我的代码现在是Pillow 2.0的一部分。所以只需执行:
pip install Pillow