在Python中确定JPG质量(PIL)
在Python中确定JPG质量(PIL)
我正在使用Python中的PIL库进行实验,想知道如何确定给定的JPG图像的质量。我尝试打开JPG图像,对其进行处理,然后以原始质量再次保存。Image.save允许我确定所需的质量:
im.save(name, quality = x)
但我看不到提取原始质量的任何方法。目前我只是猜测,并通过在“quality”参数上进行二进制搜索来尝试生成与输入文件相同大小的输出文件,但这不是长期可接受的解决方案 🙂
我还尝试使用:Image.info,但是我的大多数图像都没有任何有用的信息(例如:“adobe”,“icc_profile”,“exif”,“adobe_transform”)
求助!
在Python中确定JPEG质量(使用PIL)
JPEG图像的质量是用来生成存储在JPEG中的数据的。这个数字并没有存储在JPEG中。
确定质量的一种方法是在编辑图像之前,取图像的左上角8x8像素单元,并仅对该单元运行JPEG压缩公式,以接近原始图像。您需要开发一个从结果到原始图像的距离函数(像素差异)。
您仍然需要使用质量进行二分搜索,但工作量要小得多。
以下是关于JPEG压缩工作原理的信息:https://www.dspguide.com/ch27/6.htm
这是来自微软FAQ的另一种方法:https://support.microsoft.com/kb/324790,您需要将其从C#翻译为Python。
以下是一个示例代码,它演示了如何在Python中使用PIL库来确定JPEG图像的质量:
from PIL import Image def determine_jpg_quality(image_path): im = Image.open(image_path) # 获取图像的左上角8x8像素单元 crop_box = (0, 0, 8, 8) cropped_image = im.crop(crop_box) # 将图像保存为JPEG cropped_image.save("cropped.jpg", format="JPEG", quality=90) # 比较原始图像和压缩后的图像的像素差异 original_pixels = im.load() compressed_pixels = cropped_image.load() pixel_diff = 0 for i in range(crop_box[2]): for j in range(crop_box[3]): pixel_diff += sum(abs(original_pixels[i, j][k] - compressed_pixels[i, j][k]) for k in range(3)) # 计算平均差异 average_diff = pixel_diff / (crop_box[2] * crop_box[3] * 3) # 估计JPEG质量 estimated_quality = round(100 - average_diff * 2.5) return estimated_quality # 示例用法 image_path = "example.jpg" estimated_quality = determine_jpg_quality(image_path) print("Estimated JPEG quality:", estimated_quality)
这个示例代码打开一个JPEG图像,获取左上角8x8像素单元,并将其保存为90%质量的JPEG图像。然后,它计算原始图像和压缩图像之间的像素差异,并根据差异估计JPEG质量。最后,它打印出估计的JPEG质量。
通过使用PIL库中的函数,我们可以在Python中确定JPEG图像的质量。
文章题目:在Python中确定JPEG质量(PIL)
在PIL(和大多数使用libjpeg的软件/库)中,质量设置用于构建量化表(ref.)。在libjpeg中,质量数字“scale”样本表值(来自JPEG规范第K.1节)。在其他库中,不同的质量分配给不同的表(例如:Photoshop,数码相机)。
因此,换句话说,质量等于量化表,所以它比仅仅是一个数字更复杂。
如果您想以相同的“质量”保存修改后的图像,只需要使用相同的量化表。幸运的是,每个JPEG图像中都嵌入了量化表。不幸的是,在PIL中保存图像时无法指定量化表。带有libjpeg的命令行实用程序cjpeg可以做到。
下面是一些保存具有指定量化表的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),您可以在保存时传递给quality。我的分支。
更新2:我的代码现在是Pillow 2.0的一部分。所以只需执行:
pip install Pillow
量化表也可以在PIL.JpegImagePlugin.JpegImageFile的quantization属性中找到(当您使用Image.open打开JPEG文件时,会得到该类型)。
这很好知道。因此,PIL只缺少一种在保存时指定(传递)量化表的方法。
很好,尤其是它适用于3.3;我很高兴您记住了这个古老的问题 🙂
quality="keep"对我不起作用。我使用的是Pillow 2.5.1。在Pillow的Github存储库中报告了一个问题。能有人帮忙吗?
im.save是否等同于image_file.write?是否有办法将保存质量解析为.write?
仍然,ImageMagick似乎使用启发式方法来计算一个大致的质量数。希望Pillow也能有相同的功能。
使用启发式方法计算一个大致的质量数的优势是什么,而不是使用与原始图像完全相同的量化表?
在这个问题的上下文中,您是正确的(复制/转换jpeg)。我更多地是在说质量在选择是否进一步压缩图像方面是一个很好的简洁代理,即只是说“希望Pillow也能有相同的功能”(不仅仅是对于OP)。请参见下面我的带有代码的答案。
哦,我花了一会儿才理解你写的内容,但现在我明白了!是的,能够从量化表中计算JPEG的“质量数”以做出一些决策可能是有用的。您可以向Pillow提出您的实现建议。
问题的出现原因是使用quality='keep'
与一些PIL操作结合使用时出现问题,因为在rotate()
或transpose()
期间,会创建一个新的Image实例,这会导致丢失一些属性,例如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)
我在这里省略了quality = 100 if quantization is None else 0
这一行,因为对我来说没有起作用。
根据最新版本的Pillow,quality = 100 if quantization is None else -1
是正确的,因为他们进行了一些内部更改。