2009-03-08 29 views
14

¿Cómo puedo cambiar el tamaño de una imagen fácilmente después de que se cargó en Django? Estoy usando Django 1.0.2 y he instalado PIL.cambiar el tamaño de la imagen al guardar

Estaba pensando en anular el método save() del Modelo para cambiar su tamaño, pero realmente no sé cómo comenzar y anularlo.

¿Puede alguien señalarme en la dirección correcta? Gracias :-)

@ Guðmundur H: Esto no funcionará porque el paquete django-stdimage no funciona en Windows :-(

Respuesta

12

Debe utilizar un método para manejar el archivo cargado, como se demuestra en la Django documentation.

en este método, se podría concatenar los trozos en una variable (en lugar de escribir en el disco directamente), crear una imagen PIL de esa variable, cambiar el tamaño de la imagen y guardarlo en el disco.

En PIL, debe mirar Image.fromstring y Image.resize .

22

Recomiendo usar StdImageField desde django-stdimage, debe manejar todo el trabajo sucio para usted. Es fácil de usar, que acaba de especificar las dimensiones de la imagen redimensionada en la definición del campo:

class MyModel(models.Model): 
    image = StdImageField(upload_to='path/to/img', size=(640, 480)) 

Mira la documentación - que puede hacer miniaturas también.

+0

Esto no parece funcionar en una máquina con Windows :-(Es un error conocido en el paquete django-stdimage. Existe algún tipo de error de recursión –

+11

Windows no se puede volver a generar. Es una característica solo de Linux :) – Codygman

+0

funciona en win32 para mí. –

6

Recomiendo encarecidamente la aplicación sorl-thumbnail para manejar el cambio de tamaño de imagen de manera fácil y transparente. Va en cada proyecto de Django que empiezo.

+0

El proyecto [sorl-thumbnail] (https://github.com/sorl/sorl-thumbnail) se ha movido a github btw. – Raj

+0

Sorl tiene un nuevo mantainer y se está preparando con una nueva versión próximamente, échale un vistazo http://github.com/mariocesar/sorl-thumbnail –

11

Utilizo este código para manejar las imágenes cargadas, cambiar su tamaño en la memoria (sin guardarlas permanentemente en el disco), y luego guardar el pulgar en un Django ImageField. Hope puede ayudar.

def handle_uploaded_image(i): 
     import StringIO 
     from PIL import Image, ImageOps 
     import os 
     from django.core.files import File 
     # read image from InMemoryUploadedFile 
     image_str = “” 
     for c in i.chunks(): 
      image_str += c 

     # create PIL Image instance 
     imagefile = StringIO.StringIO(image_str) 
     image = Image.open(imagefile) 

     # if not RGB, convert 
     if image.mode not in (“L”, “RGB”): 
      image = image.convert(“RGB”) 

     #define file output dimensions (ex 60x60) 
     x = 130 
     y = 130 

     #get orginal image ratio 
     img_ratio = float(image.size[0])/image.size[1] 

     # resize but constrain proportions? 
     if x==0.0: 
      x = y * img_ratio 
     elif y==0.0: 
      y = x/img_ratio 

     # output file ratio 
     resize_ratio = float(x)/y 
     x = int(x); y = int(y) 

     # get output with and height to do the first crop 
     if(img_ratio > resize_ratio): 
      output_width = x * image.size[1]/y 
      output_height = image.size[1] 
      originX = image.size[0]/2 - output_width/2 
      originY = 0 
     else: 
      output_width = image.size[0] 
      output_height = y * image.size[0]/x 
      originX = 0 
      originY = image.size[1]/2 - output_height/2 

     #crop 
     cropBox = (originX, originY, originX + output_width, originY + output_height) 
     image = image.crop(cropBox) 

     # resize (doing a thumb) 
     image.thumbnail([x, y], Image.ANTIALIAS) 

     # re-initialize imageFile and set a hash (unique filename) 
     imagefile = StringIO.StringIO() 
     filename = hashlib.md5(imagefile.getvalue()).hexdigest()+’.jpg’ 

     #save to disk 
     imagefile = open(os.path.join(‘/tmp’,filename), ‘w’) 
     image.save(imagefile,’JPEG’, quality=90) 
     imagefile = open(os.path.join(‘/tmp’,filename), ‘r’) 
     content = File(imagefile) 

     return (filename, content) 

#views.py 

    form = YourModelForm(request.POST, request.FILES, instance=profile) 
     if form.is_valid(): 
      ob = form.save(commit=False) 
      try: 
       t = handle_uploaded_image(request.FILES[‘icon’]) 
       ob.image.save(t[0],t[1]) 
      except KeyError: 
       ob.save() 
+0

ACTUALIZACIÓN: Esto aceptará el nombre de archivo de imágenes utf8 –

+2

Su método funciona bien. Gracias por el código! Sugeriría, sin embargo, que cambies el nombre de la variable de arriba a str por otra cosa, porque sombrea la función BIF de python str(). Si alguien cambia un poco el código que ha escrito y usa el BIF debajo de la declaración de la variable, se produciría un error (python diría que str no se puede llamar) – rvnovaes

+0

¡Actualizado, gracias! –

2

Sé que esto es viejo, pero para nadie tropezar con ella, hay un paquete, django-thumbs en Django-thumbs - Easy powerful thumbnails for Django integrated with StorageBackend, que genera automáticamente miniaturas en tamaños que especifique, o ninguno si no lo hace. A continuación, llame a la miniatura que desee con las dimensiones que desee. Por ejemplo, si desea que una imagen tenga miniaturas de 64x64 y 128x128, simplemente importe thumbs.models.ImageWithThumbsField, y úselo en lugar de ImageField. Añadir un parámetro sizes=((64,64),(128,128)) a la definición del campo, luego de su plantilla que puede llamar:

{{ ClassName.field_name.url_64x64 }} 

y

{{ ClassName.field_name.url_128x128 }} 

para mostrar las imágenes en miniatura. Voila! Todo el trabajo se hace en este paquete para usted.

+0

Si utiliza Sur para el mantenimiento de bases de datos, también tendrá que añadir una introspección, como por ejemplo: ' – Furbeenator

+0

de add_introspection_rules importación south.modelsinspector add_introspection_rules ([ ( [models.ImageField], # Clase (s) estas situaciones [], # argumentos posicionales (no utilizado) {# palabra clave de argumentos "tamaños": [ "tamaños", { "por defecto": Ninguno}], }, ), ], [ "^ django_thumbs \. db \ .models \ .ImageWithThumbsField "])' – Furbeenator

2

Aquí hay una solución completa para usar un formulario. Solía ​​vistas de administración para esto:

class MyInventoryItemForm(forms.ModelForm): 

    class Meta: 
     model = InventoryItem 
     exclude = ['thumbnail', 'price', 'active'] 

    def clean_photo(self): 
     import StringIO 
     image_field = self.cleaned_data['photo'] 
     photo_new = StringIO.StringIO(image_field.read()) 

     try: 
      from PIL import Image, ImageOps 

     except ImportError: 
      import Image 
      import ImageOps 

     image = Image.open(photo_new) 

     # ImageOps compatible mode 
     if image.mode not in ("L", "RGB"): 
      image = image.convert("RGB") 

     image.thumbnail((200, 200), Image.ANTIALIAS) 

     image_file = StringIO.StringIO() 
     image.save(image_file, 'png') 

     image_field.file = image_file 

     return image_field 

Mi modelo de inventario se ve así:

class InventoryItem(models.Model): 

    class Meta: 
     ordering = ['name'] 
     verbose_name_plural = "Items" 

    def get_absolute_url(self): 
     return "/products/{0}/".format(self.slug) 

    def get_file_path(instance, filename): 

     if InventoryItem.objects.filter(pk=instance.pk): 
      cur_inventory = InventoryItem.objects.get(pk=instance.pk) 
      if cur_inventory.photo: 
       old_filename = str(cur_inventory.photo) 
       os.remove(os.path.join(MEDIA_ROOT, old_filename)) 

     ext = filename.split('.')[-1] 
     filename = "{0}.{1}".format(uuid.uuid4(), ext) 
     return os.path.join('inventory', filename) 
     #return os.path.join(filename) 

    def admin_image(self): 
     return '<img height="50px" src="{0}/{1}"/>'.format(MEDIA_URL, self.photo) 
    admin_image.allow_tags = True 

    photo = models.ImageField(_('Image'), upload_to=get_file_path, storage=fs, blank=False, null=False) 
    thumbnail = models.ImageField(_('Thumbnail'), upload_to="thumbnails/", storage=fs,  blank=True, null=True) 

....

terminé sobrescribir la función del modelo, salvo en vez de guardar la foto y un pulgar en lugar de simplemente cambiar el tamaño de la foto:

def save(self): 

    # Save this photo instance first 
    super(InventoryItem, self).save() 

    from PIL import Image 
    from cStringIO import StringIO 
    from django.core.files.uploadedfile import SimpleUploadedFile 

    # Set our max thumbnail size in a tuple (max width, max height) 
    THUMBNAIL_SIZE = (200, 200) 

    # Open original photo which we want to thumbnail using PIL's Image object 
    image = Image.open(os.path.join(MEDIA_ROOT, self.photo.name)) 

    if image.mode not in ('L', 'RGB'): 
     image = image.convert('RGB') 

    image.thumbnail(THUMBNAIL_SIZE, Image.ANTIALIAS) 

    # Save the thumbnail 
    temp_handle = StringIO() 
    image.save(temp_handle, 'png') # image stored to stringIO 

    temp_handle.seek(0) # sets position of file to 0 

    # Save to the thumbnail field 
    suf = SimpleUploadedFile(os.path.split(self.photo.name)[-1], 
     temp_handle.read(), content_type='image/png') # reads in the file to save it 

    self.thumbnail.save(suf.name+'.png', suf, save=False) 

    #Save this photo instance again to save the thumbnail 
    super(InventoryItem, self).save() 

Ambos funcionan muy bien, aunque dependiendo de lo que quiere hacer :)

Cuestiones relacionadas