【问题标题】:Python image library (PIL), how to compress image into desired file size?Python图像库(PIL),如何将图像压缩成所需的文件大小?
【发布时间】:2012-11-15 22:56:01
【问题描述】:

我需要压缩任何文件大小小于 500kb 的上传图片,我在 google 上搜索过,我只能看到:

 >>> foo = foo.resize((160,300),Image.ANTIALIAS)
 >>> foo.save("path\\to\\save\\image_scaled.jpg",quality=95)

如果我采用这种方法,我将不得不检查压缩后图像是否小于 500kb,如果不是,则使用较低的质量和大小。

有没有更好的方法?

【问题讨论】:

  • 更好的方法是对质量进行二分搜索。所以从 50% 的质量开始,然后检查大小,如果太小则尝试 75%,否则尝试 25%,等等。您将尽可能接近 500KB,您可以另外设置一些附加参数,例如。最低质量或尺寸公差。您应该能够在最多 7 次迭代中将正确的压缩级别归零。

标签: python image compression


【解决方案1】:

JPEG 压缩无法事先预测。你描述的方法,压缩&测量&重试,是我知道的唯一方法。

您可以尝试使用不同的质量设置压缩许多典型图像,以了解最佳起点,以及猜测设置更改将如何影响大小的方法。这将使您在没有太多迭代的情况下将最佳尺寸归零。

您还可以将类似文件的对象传递给save 函数,该函数不需要写入磁盘,只计算字节数。确定最佳设置后,您可以再次将其保存到实际文件中。

编辑:这是一个合适的字节计数文件对象的实现。保存后查看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:这是使用上述方法进行的二分搜索,以最少的尝试次数获得最佳 quality

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

【讨论】:

    【解决方案2】:

    我想我会在这里提供我的代码,这样可能会对遇到同样问题的人有所帮助

    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/

    【讨论】:

      猜你喜欢
      • 2018-05-29
      • 2018-08-01
      • 1970-01-01
      • 2018-08-13
      • 2016-01-29
      • 2013-09-03
      • 2021-10-25
      • 2013-09-11
      相关资源
      最近更新 更多