2012-11-15 67 views
8

我有一個要求,壓縮超過500KB減去任何上傳的圖像文件的大小,我已搜查谷歌和所有我能看到的是:Python圖像庫(PIL),如何將圖像壓縮成所需的文件大小?

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

如果我用這種方法去,我將不得不檢查壓縮後圖像小於500kb,如果沒有,則質量和尺寸較小。

有沒有更好的方法來做到這一點?

+1

一個更好的辦法來做到這一點是二進制搜索的質量。因此,從50%質量開始,然後檢查尺寸,如果它太小,則嘗試75%,否則嘗試25%等等。您將盡可能接近500KB,另外可以設置一些其他參數,例如。最小的質量或尺寸公差。您應該能夠在最多7次迭代中調入正確的壓縮級別。 –

回答

9

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:下面是一個使用上述獲得的嘗試次數最少的最佳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 
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/