PIL - saving image as .jpg not working - python

I'm trying to overwrite save() method of the model to resize images. Every format works, except when saving a .jpg image. It's not saving images with .jpg extension.
I read the Pillow documentation and there's no JPG format.
class Business(models.Model):
photo = models.ImageField(_('photo'), storage=OverwriteStorage(),
upload_to=image_upload_to, blank=True, null=True)
def save(self, **kwargs):
"""
Changing dimensions of images if they are to big.
Set max height or width to 800px depending on the image is portrait or landscape.
"""
# Opening the uploaded image
im = Image.open(self.photo)
print(im)
output = BytesIO()
# set the max width or height
im.thumbnail((800, 800))
# find the ext of the file
ext = self.photo.name.split('.')[1].upper()
if ext in {'JPEG', 'PNG', 'GIF', 'TIFF'}:
# after modifications, save it to the output
im.save(output, format=ext, quality=100)
output.seek(0)
# change the imagefield value to be the newley modifed image value
self.photo = InMemoryUploadedFile(output, 'ImageField', "%s.jpg" % self.photo.name.split('.')[0],
'image/jpeg', sys.getsizeof(output), None)
super(User, self).save()
I don't know what I am missing here.
And what's the best way to do this on a custom User model. Using a signal, overwriting ImageField or ...
Any help is appreciated :)

You handled extension JPEG, but not JPG.
You may handle it simply with something like that before your if:
if ext == 'JPG':
ext = 'JPEG'

Important: You can not save the files as a .jpg
You must use another extension such as .jpeg
Example:
You have to use the Image object you import
from PIL import Image
def image_ex():
imagefile = 'images/original-image.jpg'
new_name = os.path.splitext('{}'.format(filename))[0]+'.jpeg'
Image.open(imagefile).rotate(270).filter(ImageFilter.DETAIL).save(new_name)
image_ex()
That works. Just remember dont save as a .jpg file extensions.

Related

Does anyone know how to convert an ico to a png using python?

This is my attempt:
import os
from PIL import Image
directory = r'../Icons/ico'
for filename in os.listdir(directory):
if filename.endswith(".ico"):
print(os.path.join(directory, filename))
img = Image.open(os.path.join(directory,filename))
sizes = img.info['sizes']
for i in sizes:
img.size = i
print(img.size)
size_in_string = str(img.size)
img.save('png/' + filename.strip('.ico') + size_in_string + '.png')
else:
continue
I'm afraid that this code is not grabbing the separate ico files and instead, grabbing the largest ico file and resizing it. Can someone please help me?
According to your title.
Here is how to convert a ico to png through python.
from PIL import Image
filename = 'image.ico'
img = Image.open(filename)
img.save('image.png')
#Optinally to save with size
icon_sizes = [...]
img.save('image.png', sizes=icon_sizes)
I am pretty sure you can adapt it in your code.
you can give a try to :
https://www.convertapi.com/ico-to-png
Code snippet is using ConvertAPI Python Client
convertapi.api_secret = '<YOUR SECRET HERE>'
convertapi.convert('png', {
'File': '/path/to/my_file.ico'
}, from_format = 'ico').save_files('/path/to/dir')
In addition, we do have a question in stackoverflow.com:
How to convert an .ICO to .PNG with Python?
or you can just change the end of the .ico file to .png

Django choose in what location images are loaded in model's save method

Here's my save method for model with field image = models.ImageField(upload_to='products/%Y/%m/%d', blank=True):
def save(self, *args, **kwargs):
if self.image:
self.image = compress(self.image)
if self.second_image:
self.second_image = compress(self.second_image)
if self.third_image:
self.third_image = compress(self.third_image)
if self.fourth_image:
self.fourth_image = compress(self.fourth_image)
super().save(*args, **kwargs)
It works fine and compresses all images but changes image's directory every time when I click save in django admin. It makes all images' paths be like:
before edited and saved: products/2020/11/05/img.jpeg
after: products/2020/11/05/products/2020/11/05/img.jpeg
click save one more time: products/2020/11/05/products/2020/11/05/products/2020/11/05/img.jpeg
And then I get this error:
SuspiciousFileOperation at /admin/shop/product/6/change/
Storage can not find an available filename for "products\2020\11\05\products\2020\11\05\products\2020\...... .jpeg".
Please make sure that the corresponding file field allows sufficient "max_length".
How can I fix this problem? I think I need to choose location where saved images would be stored. Django doesn't let me use absolute path in upload_to field so I have no idea.
compress func is:
from io import BytesIO
from PIL import Image
from django.core.files import File
def compress(image):
im = Image.open(image)
if im.mode != 'RGB':
im = im.convert('RGB')
im_io = BytesIO()
im.save(im_io, 'JPEG', quality=70)
compressed_image = File(im_io, name=image.name)
return compressed_image
The problem is that the filename is updated everytime. At each step, you should make sure that the filename is only a filename. Something like this:
import os
if self.image:
self.image = compress(self.image)
self.image.name = os.path.basename(self.image.name)
I don't know exactly what is your compress function, but maybe you could also check that it doesn't do something weird with the filename.

Django converting tiff to jpeg with pillow

I'm working with Django in Python 3, and I have a model with an ImageField and I'm trying to override the .save() to do tiff to jpeg conversion:
from PIL import Image
from io import BytesIO
from django.core.files.base import ContentFile
class MyModel(models.Model):
image = models.ImageField(upload_to=upload_image_to, editable=True, null=True, blank=True)
def save(self, *args, **kwargs):
pil_image_obj = Image.open(self.image)
new_image_io = BytesIO()
rgb_pil_image_obj = pil_image_obj.convert("RGB")
rgb_pil_image_obj.save(new_image_io, quality=90, format='JPEG')
# temp_name = self.image.name
# self.image.delete(save=False)
# self.image.save(
# temp_name,
# content=ContentFile(new_image_io.getvalue()),
# save=False
# )
super().save(*args, **kwargs)
However, this leads to:
tempfile.tif: Cannot read TIFF header.
*** OSError: -2
If I try the save experiment but only opening the TIFF file from disk instead of feeding PIL.Image with the Django InMemoryUploadedFile, then everything works absolutely fine and the tiff is converted to a jpeg.
Also pil_image_obj.verify() doesn't throw any exceptions.
I'm using Pillow==5.3.0
What could be the issue? And is there any other way to do such a conversion?
You need to pass the path of the image file to Image.open(). Currently you're passing the field itself.
pil_image_obj = Image.open(self.image.path) # Pass the path

Python PIL Save Image to Different "Folder" on Amazon S3

I need to save my avatar to the "avatar" folder inside my Amazon S3 bucket.
Bucket
-Static
--Misc
-Media
--Originals
--Avatars
Currently, when I create the avatar, it is saved to the Originals "folder". My goal is to save it to the Avatars "folder".
Here is my code for creating and saving the avatar
def create_avatar(self):
import os
from PIL import Image
from django.core.files.storage import default_storage as storage
if not self.filename:
return ""
file_path = self.filename.name
filename_base, filename_ext = os.path.splitext(file_path)
thumb_file_path = "%s_thumb.jpg" % filename_base
if storage.exists(thumb_file_path):
return "exists"
try:
# resize the original image and return url path of the thumbnail
f = storage.open(file_path, 'r')
image = Image.open(f)
width, height = image.size
size = 128, 128
image.thumbnail(size, Image.ANTIALIAS)
f_thumb = storage.open(thumb_file_path, "w")
image.save(f_thumb, "JPEG", quality=90)
f_thumb.close()
return "success"
except:
return "error"
I was able to save the avatar to the desired "folder" by renaming the file path with a simple python replace() function.
This did the trick if anyone else ever need to "move" a file within the S3 bucket
thumb_file_path = thumb_file_path.replace('originals/', 'avatar/')

Trying to save resized image in Django admin

I want to have an image uploaded in the Django admin, resized and with registries on the database before writing to the disk.
Im using Python Imaging Library (PIL)
I was trying this:
from PIL import Image
class Categorias(models.Model):
image_height = models.PositiveIntegerField(editable=False,null=True)
image_width = models.PositiveIntegerField(editable=False,null=True)
the_image = models.ImageField(upload_to="uploads/image/category",width_field="image_width",height_field="image_height")
def save(self):
if not self.id and not self.the_image:
return;
image = Image.open(self.the_image)
ratio_height = (890*self.image_height)/self.image_width
size = (890,ratio_height)
image = image.resize(size, Image.ANTIALIAS)
image.save(self.the_image.path)
super(Categorias, self).save()
But this was saving the original image and the resized one to the disk, but the resized one has no registries on the database, only the original.
And what i want is just a resized image with registries on the database.
So i looked at this article:
Save image created via PIL to django model
And im trying this but with no sucess:
def save(self):
if not self.id and not self.the_image:
return;
image = Image.open(self.the_image)
ratio_height = (890*self.image_height)/self.image_width
size = (890,ratio_height)
tempfile = image.resize(size, Image.ANTIALIAS)
tempfile_io =StringIO.StringIO()
tempfile.save(tempfile_io, format='JPEG')
image_file = InMemoryUploadedFile(tempfile_io, None, "image.jpg",'image/jpeg',tempfile_io.len, None)
self.the_image = image.save(self.the_image.path,image_file)
super(Categorias, self).save()
Im getting the error:
TypeError at /admin/imagens/categorias/add/
__init__() takes exactly 8 arguments (7 given)
Im trying to solve this with no sucess
Full error on: http://pastebin.com/jxia5wPp
The error is trown when i click on save on the admin page.

Categories