2012-11-15 135 views
8

tôi nhận được một yêu cầu để nén bất kỳ hình ảnh được tải lên ít hơn 500kb trong kích thước tập tin, tôi đã tìm kiếm trên google và tất cả những gì có thể thấy là:Thư viện hình ảnh Python (PIL), cách nén hình ảnh thành kích thước tệp mong muốn?

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

Nếu tôi đi với cách tiếp cận này, tôi sẽ phải kiểm tra xem các hình ảnh nhỏ hơn 500kb sau khi nén, nếu không thì hãy giảm chất lượng và kích cỡ.

Có cách nào tốt hơn để làm điều đó không?

+1

Một cách tốt hơn để làm điều đó là để nhị phân tìm kiếm chất lượng. Vì vậy, bắt đầu với chất lượng 50%, sau đó kiểm tra kích thước, nếu nó quá nhỏ, sau đó thử 75%, nếu không thử 25%, v.v. Bạn sẽ nhận được càng gần càng tốt đến 500KB, bạn có thể bổ sung thêm một số tham số bổ sung, ví dụ. chất lượng tối thiểu hoặc dung sai kích thước. Bạn sẽ có thể không ở mức độ nén chính xác trong tối đa 7 lần lặp. –

Trả lời

9

Không thể dự đoán trước khi nén JPEG. Phương pháp bạn mô tả, hãy nén & đo & thử lại, là cách duy nhất tôi biết.

Bạn có thể thử nén một số hình ảnh thông thường với các cài đặt chất lượng khác nhau để có ý tưởng về điểm bắt đầu tối ưu, cộng với cách đoán thay đổi cài đặt sẽ ảnh hưởng như thế nào đến kích thước. Điều đó sẽ giúp bạn đạt được kích thước tối ưu mà không cần quá nhiều lần lặp lại.

Bạn cũng có thể chuyển đối tượng giống như tệp tới hàm save không bận tâm để ghi vào đĩa, chỉ cần đếm byte. Khi bạn đã xác định cài đặt tốt nhất thì bạn có thể lưu lại cài đặt đó vào tệp thực.

Chỉnh sửa: Dưới đây là triển khai đối tượng tệp đếm byte phù hợp. Chỉ cần kiểm tra size sau khi lưu.

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) 

Chỉnh sửa 2: Dưới đây là một tìm kiếm nhị phân sử dụng ở trên để có được tối ưu quality trong số nhỏ nhất của nỗ lực.

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 tôi sẽ cung cấp mã của tôi ở đây vì thế nó có thể hữu ích một người nào đó có cùng một vấn đề

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/

Các vấn đề liên quan