Django converting tiff to jpeg with pillow - python

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

Related

images do not match pil, django model

I'm trying to create a QR Code in my Django model which creates itself whenever a new record is saved. The problem is that when testing in my dev environment everything is fine but in production I get an error on the specified line below saying: ValueError, images do not match.
from django.core.files import File
import qrcode
from io import BytesIO
from PIL import Image
from datetime import date
# this is the save method of the django model class, I just left out the other methods
def save(self, *args, **kwargs):
img = qrcode.make(self.account_id)
canvas = Image.new('RGB', (400, 400), 'white')
canvas.paste(img) # Error occurs on this line
buffer = BytesIO()
canvas.save(buffer, 'PNG')
canvas.close()
fname = f'pdf-qr-code-{self.account_id}.png'
self.menu_qr_code.save(fname, File(buffer), save=False)
super(PDFMenu, self).save(*args, **kwargs)
I don't know if maybe the problem is with my configurations on my ubuntu server since that's the place where the problem takes place but maybe a new way of creating a qr code within this custom save method is necessary.
Thanks in advance for the help.

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.

PIL - saving image as .jpg not working

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.

how to get full path from binary image in odoo

I want to use PIL function Image.open(), but it only works if I pass the image path as an argument. I have to find a way to get this image path. I'm using widget='image' and odoo 8
The images are stored in database, base64 encoded. You will have to save them to a file yourself.
import tempfile
import base64
import os
from PIL import Image
from openerp import models, fields, api
from openerp.exceptions import UserError
class MyModel(models.Model):
[...]
image = fields.Binary()
#api.multi
def open_image(self):
self.ensure_one()
if not self.image:
raise UserError("no image on this record")
# decode the base64 encoded data
data = base64.decodestring(self.image)
# create a temporary file, and save the image
fobj = tempfile.NamedTemporaryFile(delete=False)
fname = fobj.name
fobj.write(data)
fobj.close()
# open the image with PIL
try:
image = Image.open(fname)
# do stuff here
finally:
# delete the file when done
os.unlink(fname)

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