这只是我目前正在进行的项目的一部分。我正在尝试将图片转换为文本,然后从文本转换回图片,而不会有任何损失或额外的大小。首先,我打开图片,读取像素,然后把它们写下来。图片大小为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的质量,但函数不接受十进制数字作为参数。
以下解释的快速要点。。。
- 保存PNG时不使用
PIL.Image.save
的quality
参数 - JPEG在生成过程中是有损的,所以当你不断重新保存图像时,它们的质量可能会降低,因为该算法会引入更多的伪像(以及其他(
- 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选项下将这些选项公开为optimize
和compress_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的底层库(不会做某些事情,我认为它不会做量化,这是确定需要压缩多少位的重要步骤。