Python图像库(PIL),如何将图片压缩到所需的文件大小?

11

我收到了一个要求,需要压缩文件大小小于500kb的所有上传图片。我在谷歌上搜索,看到的都是:

 >>> foo = foo.resize((160,300),Image.ANTIALIAS)
 >>> foo.save("path\\to\\save\\image_scaled.jpg",quality=95)
如果我采用这种方法,那么我将不得不在压缩后检查图像是否小于500kb,如果不是,则选择更低的质量和大小。
有更好的方法吗?

1
更好的方法是使用二分查找质量。因此,从50%的质量开始,然后检查大小,如果太小,则尝试75%,否则尝试25%等等。您将尽可能接近500KB,您还可以设置一些附加参数,例如最小质量或大小容差。您应该能够在最多7次迭代中精确定位到正确的压缩级别。 - Lie Ryan
2个回答

11
JPEG压缩无法预测。你所描述的方法,即压缩、测量、再尝试,是我所知道的唯一方法。
您可以尝试使用不同的质量设置压缩多个典型图像,以了解最佳起点和猜测更改设置将如何影响大小。这将使您能够缩小到最佳大小而不需要太多迭代。
您还可以将类似文件的对象传递给“保存”函数,该函数不会打扰写入磁盘,只计算字节数。一旦确定了最佳设置,就可以再次将其保存到实际文件中。
编辑:这是一个适合计数字节的文件对象的实现。保存后只需检查“size”即可。
class file_counter(object):
    def __init__(self):
        self.position = self.size = 0

    def seek(self, offset, whence=0):
        if whence == 1:
            offset += self.position
        elif whence == 2:
            offset += self.size
        self.position = min(offset, self.size)

    def tell(self):
        return self.position

    def write(self, string):
        self.position += len(string)
        self.size = max(self.size, self.position)

编辑2:以下是使用上述方法进行二分查找,以在最少次尝试中获得最佳质量的代码。
def smaller_than(im, size, guess=70, subsampling=1, low=1, high=100):
    while low < high:
        counter = file_counter()
        im.save(counter, format='JPEG', subsampling=subsampling, quality=guess)
        if counter.size < size:
            low = guess
        else:
            high = guess - 1
        guess = (low + high + 1) // 2
    return low

3

我来提供我的代码,希望对遇到相同问题的人有所帮助。

class PhotoField(forms.FileField, object):

    def __init__(self, *args, **kwargs):
        super(PhotoField, self).__init__(*args, **kwargs)
        self.help_text = "Images over 500kb will be resized to keep under 500kb limit, which may result in some loss of quality"

    def validate(self,image):
        if not str(image).split('.')[-1].lower() in ["jpg","jpeg","png","gif"]:
            raise ValidationError("File format not supported, please try again and upload a JPG/PNG/GIF file")

    def to_python(self, image):
        limit = 500000
        img = Image.open(image.file)
        width, height = img.size
        ratio = float(width) / float(height)
        quality = 100
        while len(image.file.read()) > limit:
            width -= 100
            quality -= 10
            height = int(width / ratio)
            img.resize((width, height), Image.ANTIALIAS)
            img.save(image.file.name, "JPEG", quality=quality)
            image.file = open(image.file.name)
            # reset the file pointer to the beginning so the while loop can read properly
            image.file.seek(0)
        return image

http://james.lin.net.nz/2012/11/19/django-snippet-reduce-image-size-during-upload/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接