与Python PIL图像的奇怪交互.保存质量参数



这只是我目前正在进行的项目的一部分。我正在尝试将图片转换为文本,然后从文本转换回图片,而不会有任何损失或额外的大小。首先,我打开图片,读取像素,然后把它们写下来。图片大小为NxN。

from PIL import Image
import sys 
import zlib 
def rgb_to_hex(rgb):
return '%02x%02x%02x' % rgb
N = im.width
im = Image.open(r"path\pic.png")
px = im.load()
read_pixels = ""
for i in range(N):
for j in range(N):
read_pixels += rgb_to_hex(px[j, i,])

然后,将字符串转换为字节。

data = bytes.fromhex(read_pixels)
img = Image.frombytes("RGB", (N,N), data)
img.save("path\new.png",quality = 92)

根据Pillow的官方文件,他们说质量在0-100之间,应该避免超过95的值。如果未设置任何内容,则默认值为75。

例如,我用了这张照片。原始照片下载时占用917 KB。当程序转换图片时,新图片占用911 KB。然后我拍下我的新照片(911KB(,用同样的程序运行它,我得到了同样大小的911KB,这张照片没有缩小几KB,我不知道为什么。为什么只有当我放917KB的原始图片时,才会发生这种奇怪的互动?有没有办法让我获得100%的原始质量。

我也在一些随机的512x512.jpg图片上尝试过这个。该图片的原始大小是67.4KB;生成";该图片的大小为67.1KB,之后的大小为66.8KB。此外,如果我将质量更改为93或更高(使用.jpg时(,则大小会增加很多(在质量=100时,大小>135KB(。我在"玩"质量值,发现最接近相同大小的是92(<93为.jpg增加了一些额外的KB(。

因此,对于质量92.PNG;生成";但是jpg的大小(以及潜在的质量(会下降。

我的代码中缺少什么吗?我的最佳猜测是.PNG存储了一些关于图片的额外信息,这些信息在转换过程中丢失了,但不确定为什么.jpg图片的大小每一代都会减小。我试着输入92.5的质量,但函数不接受十进制数字作为参数。

以下解释的快速要点。。。

  1. 保存PNG时不使用PIL.Image.savequality参数
  2. JPEG在生成过程中是有损的,所以当你不断重新保存图像时,它们的质量可能会降低,因为该算法会引入更多的伪像(以及其他(
  3. PNG是无损的,您看到的文件大小差异是由于在重新保存图像时PIL剥离元数据造成的

让我们先看看您的PNG文件。PNG是一种无损格式——如果你一次又一次地打开它并将其重新保存为PNG,你提供的图像数据不会遭受世代损失。

PIL的PNG插件甚至无法识别quality参数——如果你查看PngImagePlugin.py/PngStream._save方法,它在其中从未被引用。

您的特定示例图像发生的情况是,当您将其重新保存在代码中时,Pillow会丢弃一些元数据。

在我的测试系统上,我将您的PNG保存为sample.png,我用以下代码进行了简单的加载和保存,并将其保存为output.png(在ipython内(

In [1]: from PIL import Image
In [2]: img = Image.open("sample.png")
In [3]: img.save("output.png")

现在,让我们用ImageMagick:来看看它们的元数据之间的差异

#> diff <(magick identify -verbose output.png) <(magick identify -verbose sample.png)
7c7,9
<   Units: Undefined
---
>   Resolution: 94.48x94.48
>   Print size: 10.8383x10.8383
>   Units: PixelsPerCentimeter
74c76,78
<   Orientation: Undefined
---
>   Orientation: TopLeft
>   Profiles:
>     Profile-exif: 5218 bytes
76,77c80,81
<     date:create: 2022-08-12T21:27:13+00:00
<     date:modify: 2022-08-12T21:27:13+00:00
---
>     date:create: 2022-08-12T21:23:42+00:00
>     date:modify: 2022-08-12T21:23:31+00:00
78a83,85
>     exif:ImageDescription: IMGP5493_seamless_2.jpg
>     exif:ImageLength: 1024
>     exif:ImageWidth: 1024
84a92
>     png:pHYs: x_res=9448, y_res=9448, units=1
85a94,95
>     png:text: 1 tEXt/zTXt/iTXt chunks were found
>     png:text-encoded profiles: 1 were found
86a97
>     unknown: nomacs - Image Lounge 3.14
90c101
<   Filesize: 933730B
---
>   Filesize: 939469B
93c104
<   Pixels per second: 42.9936MP
---
>   Pixels per second: 43.7861MP

您可以看到元数据之间的差异-PIL在重新保存图像时没有保留一些信息,尤其是一些exif属性(您可以看到这个PNG实际上是从JPG转换而来的,EXIF元数据在转换中保留了下来(。

但是,如果使用原始图像的info数据重新保存图像。。。

In [1]: from PIL import Image
In [2]: img = Image.open("sample.png")
In [3]: img.save("output-with-info.png", info=img.info)

您将再次看到这两个文件完全相同:

❯ sha256sum output.png output-with-info.png
37ad78a7b7000c9430f40d63aa2f0afd2b59ffeeb93285b12bbba9c7c3dec4a2  output.png
37ad78a7b7000c9430f40d63aa2f0afd2b59ffeeb93285b12bbba9c7c3dec4a2  output-with-info.png

也许缩小PNG文件大小

虽然是无损的,但PNG格式确实允许通过指定压缩的力度来减小图像的大小(还可以做一些更高级的事情,比如指定压缩字典(。

PIL在PNG选项下将这些选项公开为optimizecompress_level

optimize
If present and true, instructs the PNG writer to make the
output file as small as possible. This includes extra 
processing in order to find optimal encoder settings.
compress_level
ZLIB compression level, a number between 0 and 9: 1 gives
best speed, 9 gives best compression, 0 gives no 
compression at all. Default is 6. When optimize option is 
True compress_level has no effect (it is set to 9 regardless
of a value passed).

看到它在行动。。。

from PIL import Image
img = Image.open("sample.png")
img.save("optimized.png", optimize=True)

我得到的图像比原始图像小60K左右。

❯ ls -lh optimized.png sample.png
-rw-r--r-- 1 wkl staff 843K Aug 12 18:10 optimized.png
-rw-r--r-- 1 wkl staff 918K Aug 12 17:23 sample.png

JPEG文件

现在,JPEG是一种世代有损的图像格式-当你一次又一次地保存它时,你会不断失去质量-无论你的下一代是否以比前一代更高的质量保存它,你都已经丢失了前一代保存的数据。

请注意,如果使用quality=100,您看到文件大小气球的可能原因是,当质量设置得那么高时,libjpeg/libjpeg-turbo(PIL用于JPEG的底层库(不会做某些事情,我认为它不会做量化,这是确定需要压缩多少位的重要步骤。

最新更新