2012-11-15 9 views
8

Mam wymóg kompresowac przesłanych zdjęć mniej niż 500kb wielkości plików, Szukałem na google i wszystko, co mogę zobaczyć, to:Python image library (PIL), jak skompresować obraz do pożądanego rozmiaru pliku?

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

Jeśli pójdę z tym podejściem będę musiał sprawdzić, czy obraz jest mniejszy niż 500kb po kompresji, jeśli nie, to przejdź do niższej jakości i rozmiaru.

Czy jest lepszy sposób to zrobić?

+1

lepszym sposobem na to jest do binarnego wyszukiwania jakości. Zacznij więc od 50% jakości, następnie sprawdź rozmiar, jeśli jest za mały, spróbuj 75%, spróbuj 25% itd. Dostaniesz się tak blisko jak to tylko możliwe do 500KB, możesz dodatkowo ustawić dodatkowe parametry, np. minimalna jakość lub tolerancja rozmiaru. Powinieneś być w stanie wyzerować do właściwego poziomu kompresji w co najwyżej 7 iteracjach. –

Odpowiedz

9

Kompresja JPEG nie jest przewidywalna wcześniej. Metoda, którą opisałeś, kompresuj & miara & spróbuj ponownie, to jedyny sposób, jaki znam.

Można spróbować skompresować wiele typowych obrazów o różnych ustawieniach jakości, aby uzyskać optymalny punkt początkowy, a także sposób zgadywania, w jaki sposób zmiany ustawień wpłyną na rozmiar. W ten sposób uzyskasz optymalny rozmiar bez zbyt wielu iteracji.

Można również przekazać obiekt podobny do pliku do funkcji save, która nie zajmuje się zapisywaniem na dysk, po prostu zlicza bajty. Po określeniu najlepszych ustawień można zapisać go ponownie do rzeczywistego pliku.

Edytuj: Oto implementacja odpowiedniego obiektu pliku zliczającego bajty. Sprawdź po prostu 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) 

Edit 2: Oto binarne wyszukiwania przy użyciu wyżej, aby uzyskać optymalne quality w najmniejszej liczbie prób.

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

Guess podam mój kod tutaj, więc może to być pomocne ktoś ten sam problem

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/

Powiązane problemy