2012-11-15 21 views
8

ho avuto l'obbligo di comprimere qualsiasi immagini caricate meno di 500 KB di dimensione del file, ho cercato su google e tutto quello che posso vedere è:Libreria di immagini Python (PIL), come comprimere l'immagine nella dimensione del file desiderata?

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

Se vado con questo approccio dovrò verificare se la l'immagine è inferiore a 500kb dopo la compressione, se non lo è, vai per qualità e dimensioni inferiori.

C'è un modo migliore per farlo?

+1

Un modo migliore per farlo è quello di ricercare la qualità in binario. Quindi inizia con il 50% di qualità, quindi controlla le dimensioni, se è troppo piccolo, prova il 75%, altrimenti prova il 25%, ecc. Ti avvicinerai il più possibile a 500 KB, potresti inoltre impostare alcuni parametri aggiuntivi, ad es. qualità minima o tolleranza dimensionale. Dovresti essere in grado di azzerare il livello di compressione corretto in al massimo 7 iterazioni. –

risposta

9

La compressione JPEG non è prevedibile in anticipo. Il metodo che hai descritto, comprimere & misurare & riprovare, è l'unico modo che conosco.

È possibile provare a comprimere un numero di immagini tipiche con impostazioni di qualità diverse per avere un'idea del punto di partenza ottimale, oltre a un modo per indovinare in che modo le modifiche all'impostazione influiscono sulle dimensioni. Ciò ti porterà a zero sulla dimensione ottimale senza troppe iterazioni.

È anche possibile passare un oggetto di tipo file alla funzione save che non si preoccupa di scrivere sul disco, conta solo i byte. Una volta determinate le migliori impostazioni, puoi salvarle nuovamente in un file reale.

Modifica: ecco un'implementazione di un oggetto file di conteggio di byte adatto. Basta controllare size dopo il salvataggio.

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) 

Edit 2: Ecco una ricerca binaria utilizzando il sopra per ottenere l'ottimale quality nel minor numero di tentativi.

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

Indovinate fornirò il mio codice qui in modo che potrebbe essere utile a qualcuno che ha lo stesso problema

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/

Problemi correlati