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)
Related
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
I'm trying to compress the images before uploading them to the amazon s3 server, but I couldn't do it, I used 'PIL', to do it, but it didn't work
This is the code I used with the library 'PIL':
from io import BytesIO
from PIL import Image
from django.core.files import File
def compress(image):
im = Image.open(image)
im_io = BytesIO()
im.save(im_io,'PNG', quality=70)
new_image = File(im_io, name=image.name)
return new_image
class MyModel(models.Model):
file = models.FileField(blank=True, null=True, validators=[validateFileSize])
def save(self, *args, **kwargs):
new_image = compress(self.file)
self.file = new_image
super().save(*args, **kwargs)
I solved it using the following code
def compress(image):
im = Image.open(image)
# create a BytesIO object
im_io = BytesIO()
# save image to BytesIO object
#im = im.resize([500,500])
im = im.convert("RGB")
im = im.save(im_io,'JPEG', quality=70)
# create a django-friendly Files object
new_image = File(im_io, name=image.name)
return new_image
class Media(models.Model):
file = models.FileField(blank=True, null=True, validators=[validateFileSize])
def __str__(self):
return str(self.id)
def save(self, *args, **kwargs):
if self.file:
if self.file.size > (300 * 1024):
# call the compress function
new_image = compress(self.file)
# set self.image to new_image
self.file = new_image
# save
super().save(*args, **kwargs)
In my model I want to format the imagefield by overridding the save method
I have done this in my model
from PIL import Image as Img
from io import StringIO
from django.core.files.uploadedfile import InMemoryUploadedFile
class Blog(models.Model):
Blog_image= models.ImageField(upload_to="...", blank=True)
def save(self, *args, **kwargs):
if self.Blog_image:
image = Img.open(StringIO.StringIO(self.Blog_image.read()))
image.thumbnail((900,300), Img.ANTIALIAS)
output = StringIO.StringIO()
image.save(output, format='JPEG', quality=150)
output.seek(0)
self.Blog_image = InMemoryUploadedFile(output,'ImageField', "%s.jpg" %self.Blog_image.name, 'image/jpeg', output.len, None)
super(Blog, self).save(*args, **kwargs)
But getting this Attribute error
AttributeError : type object '_io.StringIO' has no attribute 'StringIO'
Can anyone explain me why I am getting this error???
My python version is 3.6.4
My Django version is 2.0.7
output = StringIO.StringIO()
change it to:
output = StringIO()
you have already imported io.StringIO().
Got the solution
This works on Python 3.6.2 but I don't know where it was saved and from what folder it calls:
from PIL import Image
from io import BytesIO
from django.core.files.uploadedfile import InMemoryUploadedFile
import sys
def save(self, *args, **kwargs):
imageTemproary = Image.open(self.Blog_image)
outputIoStream = BytesIO()
imageTemproaryResized = imageTemproary.resize( (900,300) )
imageTemproaryResized.save(outputIoStream , format='JPEG', quality=150)
outputIoStream.seek(0)
self.Blog_image = InMemoryUploadedFile(outputIoStream,'ImageField', "%s.jpg" %self.Blog_image.name.split('.')[0], 'image/jpeg', sys.getsizeof(outputIoStream), None)
super(Blog, self).save(*args, **kwargs)
Done it using BytesIO and it worked fine
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
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'