How to save pillow image object to Django ImageField? - django

Having a Django model for thumbnail image like:
class Thumb(models.Model):
thumb = models.ImageField(upload_to='uploads/thumb/', null=True, default=None)
The view generates a thumbnail with the pillow package, and should save this in a Thumb instance, using code like:
image.thumbnail((50, 50))
inst.thumb.save('thumb.jpg', ???)
What is the right way to make image data for the inst.thumb.save at ????
I was able to get the below to work:
thumb_temp = NamedTemporaryFile()
image.save(thumb_temp, 'JPEG', quality=80)
thumb_temp.flush()
inst.thumb.save('thumb.jpg', File(thumb_temp))
thumb_temp.close() # Probably required to ensure temp file delete at close
But it seems rather clumsy to write a temporary file just to pass internal data to inst.thumb.save, so I wonder if there is a more elegant way to do it. Documentation for Django class NamedTemporaryFile.

Here's a working example (Python3, django 1.11) that takes the image from an Model.ImageField, performs a resize operation on it using PIL (Pillow), and then saves the resulting file to the same ImageField. Hopefully this should be easy to adapt to any processing you have to do to your models' images.
from io import BytesIO
from django.core.files.uploadedfile import InMemoryUploadedFile
from django.core.files.base import ContentFile
from PIL import Image
IMAGE_WIDTH = 100
IMAGE_HEIGHT = 100
def resize_image(image_field, width=IMAGE_WIDTH, height=IMAGE_HEIGHT, name=None):
"""
Resizes an image from a Model.ImageField and returns a new image as a ContentFile
"""
img = Image.open(image_field)
if img.size[0] > width or img.size[1] > height:
new_img = img.resize((width, height))
buffer = BytesIO()
new_img.save(fp=buffer, format='JPEG')
return ContentFile(buffer.getvalue())
#assuming your Model instance is called `instance`
image_field = instance.image_field
img_name = 'my_image.jpg'
img_path = settings.MEDIA_ROOT + img_name
pillow_image = resize_image(
image_field,
width=IMAGE_WIDTH,
height=IMAGE_HEIGHT,
name=img_path)
image_field.save(img_name, InMemoryUploadedFile(
pillow_image, # file
None, # field_name
img_name, # file name
'image/jpeg', # content_type
pillow_image.tell, # size
None) # content_type_extra
)

You can create pre_save receiver for your Model:
from io import BytesIO
from functools import partial
from django.db import models
from django.core.files.uploadedfile import InMemoryUploadedFile
from PIL import Image
class Article(models.Model):
title = models.CharField(max_length=120)
slug = models.SlugField(max_length=120, unique=True)
image = models.ImageField(
upload_to=upload_image_location
)
thumbnail_image = models.ImageField(
upload_to=partial(upload_image_location, thumbnail=True),
editable=False, blank=True
)
def create_thumbnail(self):
image = Image.open(self.image.file.file)
image.thumbnail(size=(310, 230))
image_file = BytesIO()
image.save(image_file, image.format)
self.thumbnail_image.save(
self.image.name,
InMemoryUploadedFile(
image_file,
None, '',
self.image.file.content_type,
image.size,
self.image.file.charset,
),
save=False
)
#receiver(models.signals.pre_save, sender=Article)
def prepare_images(sender, instance, **kwargs):
if instance.pk:
try:
article = Article.objects.get(pk=instance.pk)
old_image = article.image
old_thumbnail_image = article.thumbnail_image
except Article.DoesNotExist:
return
else:
new_image_extension = os.path.splitext(instance.image.name)[1]
if old_image and not old_image.name.endswith(new_image_extension):
old_image.delete(save=False)
old_thumbnail_image.delete(save=False)
if not instance.thumbnail_image or not instance.image._committed:
instance.create_thumbnail()
Thumbnail of image is created in create_thumbnail method using Pillow. This method work fine with django-storages and saving thumbnail in custom storage with name like 'article_slug_thumbnail.jpeg'.
My upload_image_location method:
def upload_image_location(instance, filename, thumbnail=False):
_, ext = os.path.splitext(filename)
return f'articles/{instance.slug}{f"_thumbnail" if thumbnail else ""}{ext}'

class Profile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
image = models.ImageField(default = 'default.jpg',upload_to='profile_pics')
def __str__(self):
return f'{self.user.username} Profile'
def save(self):
super().save()
img = Image.open(self.image.path)
if img.height >300 or img.width >300:
oputput_size = (300,300)
img.thumbnail(oputput_size)
img.save(self.image.path)

from io import BytesIO
from PIL import Image
from django.core.files.images import ImageFile
import requests
img_url = 'https://cdn.pixabay.com/photo/2021/08/25/20/42/field-6574455__340.jpg'
res = Image.open(requests.get(img_url, stream=True).raw)
filename = 'sample.jpeg'
img_object= ImageFile(BytesIO(res.fp.getvalue()), name=filename)
// django_image_field = img_object

Related

Django Change Image Format and Quality

Django project
Im trying to compress uploaded images and change their format
Currently it only compresses png images but when a jpg image is uploaded it does not change the format nor does it compress the image
please help sir
models.py
import sys
from PIL import Image
from io import BytesIO
from django.db import models
from django.contrib.auth.models import User
from django.core.files.uploadedfile import InMemoryUploadedFile
from django.core.exceptions import ValidationError
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(upload_to='images/userpfp/', blank=True)
def __str__(self):
return self.user.username
def compressImageMain(self,image):
if self.image:
def save(self, *args, **kwargs):
if not self.id:
self.image = self.compressImage(self.image)
super(UserProfile, self).save(*args, **kwargs)
def compressImage(self,image):
imageTemproary = Image.open(image)
outputIoStream = BytesIO()
basewidth = 520
wpercent = (basewidth/float(imageTemproary.size[0]))
hsize = int((float(imageTemproary.size[1])*float(wpercent)))
imageTemproaryResized = imageTemproary.resize( (basewidth,hsize) )
imageTemproaryResized.save(outputIoStream , format='PNG', quality=50)
outputIoStream.seek(0)
image = InMemoryUploadedFile(outputIoStream,'ImageField', "%s.png" % image.name.split('.')[0], 'images/userpfp/png', sys.getsizeof(outputIoStream), None)
return image
else:
pass
I found the sollution
first I needed to add: .convert("RGB") when I opened the image
See: this post
img = Image.open(image).convert("RGB")
To maintain image quality over png and jpef image uploads:
See: this post
I compressed png to png and saved it as png
and compressed jpeg to jpeg and saved it as jpeg
(jpg and jpeg are the same)
import sys
from PIL import Image
from io import BytesIO
from django.core.files import File
from django import forms
from django.db import models
class Trade(models.Model):
image = models.ImageField(upload_to='images/trades/')
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.user.username
def save(self):
if not self.id:
self.image = self.compressImage(self.image)
super(Trade, self).save()
def compressImage(self,image):
img = Image.open(image).convert("RGB")
im_io = BytesIO()
if image.name.split('.')[1] == 'jpeg' or image.name.split('.')[1] == 'jpg':
img.save(im_io , format='jpeg', optimize=True, quality=55)
new_image = File(im_io, name="%s.jpeg" %image.name.split('.')[0],)
else:
img.save(im_io , format='png', optimize=True, quality=55)
new_image = File(im_io, name="%s.png" %image.name.split('.')[0],)
return new_image

How to compress uploaded image in django python

have been going through many tutorials on python on how to compress images, Have used PILLOW but its not working. I will be grateful if I can get a full documentation on how to use PILLOW for image compression inside my django views.py
i had used thumbnail() in pillow to compress images previously inside models.py The thumbnail() doesnt change the aspect ratio of image when resizing the image.
import sys
from django.db import models
from PIL import Image
from io import BytesIO
from django.core.files.uploadedfile import InMemoryUploadedFile
def compressImage(uploaded_image):
image_temp = Image.open(uploaded_image)
outputIoStream = BytesIO()
image_temp.thumbnail( (1600,1600) )
image_temp.save(outputIoStream , format='JPEG', quality=100)
outputIoStream.seek(0)
uploaded_image = InMemoryUploadedFile(outputIoStream,'ImageField', "%s.jpg" % uploaded_image.name.split('.')[0], 'image/jpeg', sys.getsizeof(outputIoStream), None)
return uploaded_image
class ABC(models.Model):
name = models.CharField(max_length=200)
image = models.ImageField(upload_to=/upload/path/here/, null=True, blank=True)
def save(self, *args, **kwargs):
if not self.id:
self.image = compressImage(self.image)
super(ABC, self).save(*args, **kwargs)

How to use opencv in Django?

I'm a beginner in Django and I want to convert my image to grayscale before to upload it with opencv.
This is what I've tried to do but it doesn't work
`
from __future__ import unicode_literals
from django.db import models
import cv2
class Personne(models.Model):
nom = models.CharField(max_length=50, unique=True, help_text="Le nom est unique")
description = models.TextField(blank=True)
def __unicode__(self):
return self.nom[:50]
def traitement(filename):
img = cv2.imread(filename, 0)
cv2.imwrite(filename, img)
class Visage(models.Model):
personne = models.ForeignKey(Personne)
def generate_filename(self, filename):
return "Images/%s/%s" % (self.personne.nom, filename)
traitement
image = models.ImageField(blank=False, upload_to=generate_filename)
ajoute_le = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.image.path`
You have to override save() method of model Visage to do some actions with data after saving.
Change your_project_name appropriately.
from your_project_name.settings import MEDIA_ROOT
class Visage(models.Model):
...
def save(self):
super(Visage, self).save()
filename = os.path.abspath(os.path.join(MEDIA_ROOT, self.image.name)
traitement(filename)

Django save image from url and connect with ImageField

I have in models.py:
class Item(models.Model):
image_file = models.ImageField(upload_to='images')
image_url = models.URLField()
I want django automatically download and locally save image from image_url and "connect" it with image_file
How it should act:
I Paste https://docs.djangoproject.com/s/img/site/hdr_logo.gif
into image_url field in admin
Click "save"
In templates write <img src="{{ item.image_file.url }}">. It shows
image from my server, not djangoproject.com
What I've tried:
I've overwritten save method of Item class. I saved image locally via urllib, but I am stuck on connecting this saved image with image_file field
from django.core.files import File
import os
class Item(models.Model):
image_file = models.ImageField(upload_to='images')
image_url = models.URLField()
...
def get_remote_image(self):
if self.image_url and not self.image_file:
result = urllib.urlretrieve(self.image_url)
self.image_file.save(
os.path.basename(self.image_url),
File(open(result[0]))
)
self.save()
You can override the default save() method to automatically invoke get_remote_image().
See: https://docs.djangoproject.com/en/dev/topics/db/models/#overriding-model-methods
from django.db import models
from django.core.files import File
from urllib.request import urlopen
from tempfile import NamedTemporaryFile
class Item(models.Model):
image_file = models.ImageField(upload_to='images')
image_url = models.URLField()
...
def get_remote_image(self):
if self.image_url and not self.image_file:
img_temp = NamedTemporaryFile(delete=True)
img_temp.write(urlopen(self.image_url).read())
img_temp.flush()
self.image_file.save(f"image_{self.pk}", File(img_temp))
self.save()
This solution avoid any utf-8 errors received during url process.
It works only with python 3.6+ because the f string.
Check this link: https://twigstechtips.blogspot.com/2012/04/django-programmatically-saving-image.html
For python3
from django.core.files import File
from urllib import request
import os
class Item(models.Model):
image_file = models.ImageField(upload_to='images')
image_url = models.URLField()
...
def get_remote_image(self):
if self.image_url and not self.image_file:
result = request.urlretrieve(self.image_url)
self.image_file.save(
os.path.basename(self.image_url),
File(open(result[0], 'rb'))
)
self.save()
from here: http://stackoverflow.com/questions/17960942/attributeerror-module-object-has-no-attribute-urlretrieve
Python3
from django.db import models
from django.core.files import File
from urllib.request import urlopen
from tempfile import NamedTemporaryFile
class Item(models.Model):
image_file = models.ImageField(upload_to='images')
image_url = models.URLField()
def save(self, *args, **kwargs):
if self.image_url and not self.image_file:
img_temp = NamedTemporaryFile(delete=True)
img_temp.write(urlopen(self.image_url).read())
img_temp.flush()
self.image_file.save(f"image_{self.pk}", File(img_temp))
super(Item, self).save(*args, **kwargs)
It`s similar but with automated save 'image_file', when add 'image_url'

Django Error (13, 'Permission denied')

I'm been working on this Photo Organizer and Sharing App Part I at http://lightbird.net/dbe/photo.html. I'm trying to generate a thumbnail and when I do . I get this error.
I have Windows Vista.
IOError at /admin/photo/image/add/
(13, 'Permission denied')
Request Method: POST
Request URL: http://127.0.0.1:8000/admin/photo/image/add/
Django Version: 1.4.3
Exception Type: IOError
Exception Value: (13, 'Permission denied')
Exception Location:C:\Python26\lib\site-packages\PIL\Image.py in save, line 1399
Python Executable:C:\Python26\python.exe
Python Version: 2.6.0
Python Path:
['C:\\djcode\\mysite',
'C:\\Python26\\python26.zip',
'C:\\Python26\\DLLs',
'C:\\Python26\\lib',
'C:\\Python26\\lib\\plat-win',
'C:\\Python26\\lib\\lib-tk',
'C:\\Python26',
'C:\\Python26\\lib\\site-packages',
'C:\\Python26\\lib\\site-packages\\PIL']
Server time: Sun, 10 Feb 2013 23:49:34 +1100
My models.py is
from django.db import models
from django.contrib.auth.models import User
from django.contrib import admin
from string import join
from django.core.files import File
from os.path import join as pjoin
from tempfile import *
import os
from PIL import Image as PImage
from mysite.settings import MEDIA_ROOT
class Album(models.Model):
title = models.CharField(max_length=60)
public = models.BooleanField(default=False)
def __unicode__(self):
return self.title
class Tag(models.Model):
tag = models.CharField(max_length=50)
def __unicode__(self):
return self.tag
class Image(models.Model):
title = models.CharField(max_length=60, blank=True, null=True)
image = models.FileField(upload_to="images/")
tags = models.ManyToManyField(Tag, blank=True)
albums = models.ManyToManyField(Album, blank=True)
created = models.DateTimeField(auto_now_add=True)
rating = models.IntegerField(default=50)
width = models.IntegerField(blank=True, null=True)
height = models.IntegerField(blank=True, null=True)
user = models.ForeignKey(User, null=True, blank=True)
thumbnail2 = models.ImageField(upload_to="images/", blank=True, null=True)
def __unicode__(self):
return self.image.name
def save(self, *args, **kwargs):
"""Save image dimensions."""
super(Image, self).save(*args, **kwargs)
im = PImage.open(pjoin(MEDIA_ROOT, self.image.name))
self.width, self.height = im.size
# large thumbnail
fn, ext = os.path.splitext(self.image.name)
im.thumbnail((128,128), PImage.ANTIALIAS)
thumb_fn = fn + "-thumb2" + ext
tf2 = NamedTemporaryFile()
im.save(tf2.name, "JPEG")
self.thumbnail2.save(thumb_fn, File(open(tf2.name)), save=False)
tf2.close()
# small thumbnail
im.thumbnail((40,40), PImage.ANTIALIAS)
thumb_fn = fn + "-thumb" + ext
tf = NamedTemporaryFile()
im.save(tf.name, "JPEG")
self.thumbnail.save(thumb_fn, File(open(tf.name)), save=False)
tf.close()
super(Image, self).save(*args, ** kwargs)
def size(self):
"""Image size."""
return "%s x %s" % (self.width, self.height)
def __unicode__(self):
return self.image.name
def tags_(self):
lst = [x[1] for x in self.tags.values_list()]
return str(join(lst, ', '))
def albums_(self):
lst = [x[1] for x in self.albums.values_list()]
return str(join(lst, ', '))
def thumbnail(self):
return """<img border="0" alt="" src="/media/%s" height="40" />""" % (
(self.image.name, self.image.name))
thumbnail.allow_tags = True
class AlbumAdmin(admin.ModelAdmin):
search_fields = ["title"]
list_display = ["title"]
class TagAdmin(admin.ModelAdmin):
list_display = ["tag"]
class ImageAdmin(admin.ModelAdmin):
search_fields = ["title"]
list_display = ["__unicode__", "title", "user", "rating", "size", "tags_", "albums_","thumbnail", "created"]
list_filter = ["tags", "albums"]
def save_model(self, request, obj, form, change):
obj.user = request.user
obj.save()
The problem is here:
from django.core.files import File
from os.path import join as pjoin
from tempfile import *
class Image(models.Model):
# ...
thumbnail2 = models.ImageField(upload_to="images/", blank=True, null=True)
def save(self, *args, **kwargs):
"""Save image dimensions."""
super(Image, self).save(*args, **kwargs)
im = PImage.open(pjoin(MEDIA_ROOT, self.image.name))
self.width, self.height = im.size
# large thumbnail
fn, ext = os.path.splitext(self.image.name)
im.thumbnail((128,128), PImage.ANTIALIAS)
thumb_fn = fn + "-thumb2" + ext
tf2 = NamedTemporaryFile()
im.save(tf2.name, "JPEG")
self.thumbnail2.save(thumb_fn, File(open(tf2.name)), save=False)
tf2.close()
# small thumbnail
im.thumbnail((40,40), PImage.ANTIALIAS)
thumb_fn = fn + "-thumb" + ext
tf = NamedTemporaryFile()
im.save(tf.name, "JPEG")
self.thumbnail.save(thumb_fn, File(open(tf.name)), save=False)
tf.close()
super(Image, self).save(*args, ** kwargs)
How do I fix this error?
It looks to me like django doesn't have the permissions it needs to access your MEDIA_ROOT folder.
Have a look at your MEDIA_ROOT settings in your settings.py file. Then check the permissions on the folder (something like ls -lsa /path/to/media_root from a bash shell). Make sure the user running django as write permission to the folder.
Also, as asermax points out, make sure you have created an images directory within your MEDIA_ROOT.
Have a look at the documentation for serving static files particularly the section on serving other directories
UPDATE
Perhaps it's this issue. Try replacing im.save(tf2.name, "JPEG") with im.save(tf2, "JPEG")
set SELinux permisions
http://wiki.apache.org/httpd/13PermissionDenied
take a look to this, it may be there the solution