【问题标题】:Python PIL: Find the size of image without writing it as a filePython PIL:查找图像的大小而不将其写入文件
【发布时间】:2017-03-28 00:10:57
【问题描述】:

编辑:这个问题已被标记为重复?我的问题显然是关于优化这个过程,而不是如何去做。我什至提供了代码来证明我已经弄清楚了后者。在你标记它们之前,你的互联网大厅监视器是否甚至阅读了这些问题的标题?

我有以下代码块来使用 PIL 压缩图像,直到所述图像小于一定大小。

from PIL import Image
import os

def compress(image_file, max_size, scale):
    while os.path.getsize(image_file) > max_size:
        pic = Image.open(image_file)
        original_size = pic.size
        pic = pic.resize((int(original_size[0] * scale),
            int(original_size[1] * scale)),
            Image.ANTIALIAS)
        pic.save(image_file, optimize=True, quality=95)

在这段代码中,我使用os.path.getsize(image_file) 来获取图像的大小。但是,这意味着文件必须在每次循环运行时保存在pic.save(image_file, optimize=True, quality=95

这个过程需要很长时间。

有没有办法通过获取PIL Image 对象pic 中的图像大小来优化这一点?

【问题讨论】:

  • 不清楚您要做什么。循环调整图像大小,因此需要保存。如果您想要的只是大小信息,请不要调整大小或保存文件。
  • 我假设您保存为 JPEG。您可以通过将文件数据保存到内存中的 BytesIO 对象而不是磁盘来节省一些时间。这也将使获得结果文件大小的速度更快。但是,它不会加快编码过程。顺便说一句,使用质量 95 没有多大意义。它非常慢,它产生的文件很大,而且 90 和 95 之间的视觉差异很少被注意到。而且 85 通常就足够了,具体取决于图像的性质。
  • 我还应该提到 PIL / Pillow 中的图像缩放例程质量不是很高,尽管您可能不会注意到如果图像足够大并且是具有许多平滑色调过渡的照片而不是计算机生成的具有许多高对比度区域的图像。此外,您应该逐步编辑 JPEG。不要保存缩放的图像,然后重新加载它,并重新缩放已经缩放的图像。这样你会很快失去质量。如果您必须尝试不同的比例,直到文件大小足够小,请从原始文件生成每个新版本。

标签: python image python-3.x python-imaging-library


【解决方案1】:

您可能会看看this answer 使用 StringIO 在内存中执行操作。

import StringIO

output = StringIO.StringIO() 
image.save(output) 
contents = output.getvalue() 
output.close()

image.save(output, format="GIF")

【讨论】:

  • 这个答案相当陈旧。 StringIO 适用于 Python 2,但在 Python 3 中您需要使用 io.BytesIO()
  • 好吧,很可能这就是 Py3 的方式。但原理是一样的:使用 RAM 中的变量,对其执行操作,然后将图像保存回文件中。
  • 当然。但是 OP 正在寻找 Python 3 解决方案,并且在 Python 3 中 import StringIO 引发 ImportError: No module named 'StringIO'。当然,您不能将图像中的字节放入 Python 3 字符串中,您需要将它们放入字节或字节数组对象中。
  • 如果 op 真的依赖于 python 3,那么 PIL 无论如何都不是正确的解决方案。然后他想使用pip install Pillow,顺便说一句,它提供了python 2和3支持afaik。
  • OP 在问题上有python-3.x 标签,因此他们必须使用 Pillow 叉子,因为没有原始 PIL 的 Python 3 版本(尚未维护几年了)。是的,Pillow 可以替代 PIL。所以你仍然做from PIL import Image等。FWIW,当前的 SO Python 政策是假设 Python 3,除非问题明确提到 Python 2。
【解决方案2】:

使用io.BytesIO() 将图像保存到内存中。每次从原始文件调整大小可能会更好,如下所示:

from PIL import Image
import os
import io

def compress(original_file, max_size, scale):
    assert(0.0 < scale < 1.0)
    orig_image = Image.open(original_file)
    cur_size = orig_image.size

    while True:
        cur_size = (int(cur_size[0] * scale), int(cur_size[1] * scale))
        resized_file = orig_image.resize(cur_size, Image.ANTIALIAS)

        with io.BytesIO() as file_bytes:
            resized_file.save(file_bytes, optimize=True, quality=95, format='jpeg')

            if file_bytes.tell() <= max_size:
                file_bytes.seek(0, 0)
                with open(original_file, 'wb') as f_output:
                    f_output.write(file_bytes.read())
                break

compress(r"c:\mytest.jpg", 10240, 0.9) 

所以这将获取文件并在每次尝试时将其缩小0.9,直到达到合适的大小。然后它会覆盖原始文件。


作为一种替代方法,您可以创建一个音阶列表来尝试,例如[0.01, 0.02 .... 0.99, 1],然后使用二进制印章来确定哪个比例导致文件大小最接近max_size,如下所示:

def compress(original_file, max_size):
    save_opts={'optimize':True, 'quality':95, 'format':'jpeg'}
    orig_image = Image.open(original_file)
    width, height = orig_image.size
    scales = [scale / 1000 for scale in range(1, 1001)]  # e.g. [0.001, 0.002 ... 1.0]

    lo = 0
    hi = len(scales)

    while lo < hi:
        mid = (lo + hi) // 2

        scaled_size = (int(width * scales[mid]), int(height * scales[mid]))
        resized_file = orig_image.resize(scaled_size, Image.ANTIALIAS)

        file_bytes = io.BytesIO()
        resized_file.save(file_bytes, **save_opts)
        size = file_bytes.tell()
        print(size, scales[mid])

        if size < max_size: 
            lo = mid + 1
        else: 
            hi = mid

    scale = scales[max(0, lo-1)]
    print("Using scale:", scale)
    orig_image.resize((int(width * scale), int(height * scale)), Image.ANTIALIAS).save(original_file, **save_opts)

因此,对于10000 中的max_size,循环首先尝试0.501 的比例,如果0.251 尝试太大,以此类推。当max_size=1024 时,将尝试以下比例:

180287 0.501
56945 0.251
17751 0.126
5371 0.063
10584 0.095
7690 0.079
9018 0.087
10140 0.091
9336 0.089
9948 0.09
Using scale: 0.09

【讨论】:

  • 绝对应该检查规模 >0.0 和
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-11-12
  • 2014-02-26
  • 2017-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-05
相关资源
最近更新 更多