django, upload tif image - django

I am trying to upload a tif image (greyscale), using django ImageField.
I have installed Pillow==8.3.1 and using Python 3.9.
The app works only with PNG/JPEG images.
Here is the model I am using:
class Upload(models.Model):
image = models.ImageField(upload_to='images')
title = models.CharField(max_length=200)
action = models.CharField(max_length=50,choices=ACTION_CHOICES)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
#breakpoint()
# def __str__(self):
# pixels = tfi.imread(self.image)
# return np.shape(np.array(pixels))
def save(self,*args,**kwargs):
#open image
#breakpoint()
if self.action=='tif':
pixels = tfi.imread(self.image)
else:
pixels = Image.open(self.image)
#pixels = tfi.imread(self.image)
pixels = np.array(pixels)
pixels=pixels[:,:,0]
#pixels = pixels[0,:,:]
#use the normalisation method
img = get_image(pixels)
im_pil=Image.fromarray(img)
#save
buffer = BytesIO()
im_pil.save(buffer,format='png')
image_png = buffer.getvalue()
self.image.save(str(self.image), ContentFile(image_png),save=False)
super().save(*args,**kwargs)
#return self.image_png

Django's ImageField requires the third-party package Pillow. It depends on the pillow to verify that a file is indeed an image or not. This is not dependant on the file type, but on the content of the file itself.
please check your version of pillow you are using, and check the corresponding documentation of your pillow version.
https://pypi.org/project/Pillow/
supported image format by pillow 8.3.1
but if still something doesn't work for you you can always use FileField which doesn't look for what file or format u are uploading until u are uploading a file please check out documentation
https://www.geeksforgeeks.org/filefield-django-models/
Although I would recommend you to use imagefield for only image and read the documentation now it totally up to you what you want to use.

Related

ValueError when uploading resized django images to google cloud

i have this model which works fine when uploading resized images to the media file on my django project
class ItemImage(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
item = models.ForeignKey(Item, on_delete=models.CASCADE)
image = models.ImageField(null=True, blank=True,upload_to='item_img/')
created = models.DateTimeField(auto_now_add=True)
def save(self):
im = Image.open(self.image)
im_name = uuid.uuid4()
im = im.convert('RGB')
output = BytesIO()
# Resize/modify the image
im = im.resize((700, 700))
# after modifications, save it to the output
im.save(output, format='JPEG', quality=90)
output.seek(0)
# change the imagefield value to be the newley modifed image value
self.image = InMemoryUploadedFile(output, 'ImageField', "%s.jpg" % self.image.name, 'image/jpeg',
sys.getsizeof(output), None)
super(ItemImage, self).save()
def __str__(self):
return self.item.title
when i changed the file storage to google cloud i faced this error when uploading the images
ValueError at /ar/dashboard/my_items/edit_item/add_item_image/2/
Size 120495 was specified but the file-like object only had 120373 bytes remaining.
note that the images are uploaded successfully when i remove the save method that is added so is there anything that i need to change in that save method when dealing with gcloud?
i found a similar problem on github and he explained the error as follow "I think this is an error in the end user code which GCS rejects and the other services are more liberal about. The call sys.getsizeof(fi_io) yields the size of the BytesIO object, not the size of the buffer"
so i changed sys.getsizeof(output) to len(output.getbuffer())
and that's it it works with both google cloud and local media files

Resize thumbnails django Heroku, 'backend doesn't support absolute paths'

I've got an app deployed on Heroku using Django, and so far it seems to be working but I'm having a problem uploading new thumbnails. I have installed Pillow to allow me to resize images when they're uploaded and save the resized thumbnail, not the original image. However, every time I upload, I get the following error: "This backend doesn't support absolute paths." When I reload the page, the new image is there, but it is not resized. I am using Amazon AWS to store the images.
I'm suspecting it has something to do with my models.py. Here is my resize code:
class Projects(models.Model):
project_thumbnail = models.FileField(upload_to=get_upload_file_name, null=True, blank=True)
def __unicode__(self):
return self.project_name
def save(self):
if not self.id and not self.project_description:
return
super(Projects, self).save()
if self.project_thumbnail:
image = Image.open(self.project_thumbnail)
(width, height) = image.size
image.thumbnail((200,200), Image.ANTIALIAS)
image.save(self.project_thumbnail.path)
Is there something that I'm missing? Do I need to tell it something else?
Working with Heroku and AWS, you just need to change the method of FileField/ImageField 'path' to 'name'. So in your case it would be:
image.save(self.project_thumbnail.name)
instead of
image.save(self.project_thumbnail.path)
I found the answer with the help of others googling as well, since my searches didn't pull the answers I wanted. It was a problem with Pillow and how it uses absolute paths to save, so I switched to using the storages module as a temp save space and it's working now. Here's the code:
from django.core.files.storage import default_storage as storage
...
def save(self):
if not self.id and not self.project_description:
return
super(Projects, self).save()
if self.project_thumbnail:
size = 200, 200
image = Image.open(self.project_thumbnail)
image.thumbnail(size, Image.ANTIALIAS)
fh = storage.open(self.project_thumbnail.name, "w")
format = 'png' # You need to set the correct image format here
image.save(fh, format)
fh.close()
NotImplementedError: This backend doesn't support absolute paths - can be fixed by replacing file.path with file.name
How it looks in the the console
c = ContactImport.objects.last()
>>> c.json_file
<FieldFile: protected/json_files/data_SbLN1MpVGetUiN_uodPnd9yE2prgeTVTYKZ.json>
>>> c.json_file.name
'protected/json_files/data_SbLN1MpVGetUiN_uodPnd9yE2prgeTVTYKZ.json'

How can I programatically save an Image to an ImageField using Django-Cumulus?

I am using Django-Cumulus to store images to Rackspace's Cloudfiles platform.
I want to, dynamically, manipulate my images and save them as a new ImageField for my Model. For example, I have a Photo model with these ImageFields: image, thumb_256x256
In my Form's save() method, I am letting the user specify the cropping locations (using JCrop).
Anyways, I know how to grab the existing image file that the user uploaded. I also know how to apply manipulations with PIL. The problem I'm running into is creating a new Rackspace File and writing to it.
I keep getting the exception "NoSuchObject".
Here's some example code:
def save(self, commit=True):
""" Override the Save method to create a thumbnail of the image. """
m = super(PhotoUpdateForm, self).save(commit=False)
image = Image.open(m.image.file)
image.thumbnail((256,256), Image.ANTIALIAS)
thumb_io = CloudFilesStorageFile(storage=CLOUDFILES_STORAGE, name='foo/bar/test.jpg')
image.save(thumb_io.file, format='JPEG')
Also, once I get to this point -- what's the best way of setting this image to the model's other ImageField? (m.thumb_256x256 in my case)
Thanks in advanced!
Update: The name of the actual Cloudfiles Django app I'm using is "django-cumulus"
Here is a temporary solution. I'm having an issue with setting the new filename properly. It simply appends a _X to the filename. So for example, somefilename.jpg becomes somefilename_1.jpg whenever I save a new version.
This code is a bit ugly but does get the job done. It creates a cropped version of the image and will also generate a thumbnail if needed.
def save(self, commit=True):
""" Override the Save method to create a thumbnail of the image. """
m = super(PhotoUpdateForm, self).save(commit=False)
# Cropped Version
if set(('x1', 'x2', 'y1', 'y2')) <= set(self.cleaned_data):
box = int(self.cleaned_data['x1']), \
int(self.cleaned_data['y1']), \
int(self.cleaned_data['x2']), \
int(self.cleaned_data['y2'])
image = Image.open(m.image.file)
image = image.crop(box)
temp_file = NamedTemporaryFile(delete=True)
image.save(temp_file, format="JPEG")
m.image.save("image.jpg", File(temp_file))
cropped = True # Let's rebuild the thumbnail
# 256x256 Thumbnail
if not m.thumb_256x256 or cropped:
if not image:
image = Image.open(m.image.file)
image.thumbnail((256,256), Image.ANTIALIAS)
temp_file = NamedTemporaryFile(delete=True)
image.save(temp_file, format="JPEG")
m.thumb_256x256.save("thumbnail.jpg", File(temp_file))
if commit: m.save()
return m

Saving JPG format with PIL

I'm using PIL to make thumbnails of images I upload and everything is fine with PNGs or GIFs. However, uploading JPGs is giving me a headache. I kept getting a invalid format type for a while, and then I found this at the bottom of the JPG page on the PIL website...
Note: To enable JPEG support, you need to build and install the IJG
JPEG library before building the Python Imaging Library. See the
distribution README for details.
Anyway, so I deployed to Heroku and for some reason it seems to be no longer giving me the invalid format error that I had been getting on my local... except even though there is now a photo object living in the db, I can't seem to access them. I drop their location into into an image tag but I keep getting a broken image link symbol.
Here is what my override save looks like in models:
def save(self, force_update=False, force_insert=False, thumb_size=(90,150)):
image = Image.open(self.image)
if image.mode not in ('L', 'RGB'):
image = image.convert('RGB')
# save the original size
self.image_width, self.image_height = image.size
image.thumbnail(thumb_size, Image.ANTIALIAS)
# save the thumbnail to memory
temp_handle = StringIO()
image.save(temp_handle, format='JPEG')
temp_handle.seek(0) # rewind the file
# save to the thumbnail field
suf = SimpleUploadedFile(os.path.split(self.image.name)[-1],
temp_handle.read(),
content_type='image/jpg')
self.thumbnail.save(suf.name, suf, save=False)
self.thumbnail_width, self.thumbnail_height = image.size
#save the image object
super(Photo, self).save(force_update, force_insert)

Django admin: restricting image uploads to JPEGs

I have a Django admin setup where users can upload images. The system only allows for JPEG-formatted images. I put together a validation system to check all the images uploaded are JPEGS. In my Images model I have an override for clean():
class Image(models.Model):
image = models.ImageField(upload_to="images/", blank=True, null=True, help_text='JPEG images only', max_length=100)
...
def clean(self):
import Image
if "images/" in str( self.image ):
i = Image.open( "%s/%s" % ( settings.MEDIA_ROOT, self.image ) )
if i.format != "JPEG":
raise validators.ValidationError, u'You can only upload JPEG images'
The problem is that this will only find an image once it's uploaded and the record is being re-saved. If it's being created for the first time clean() function will be called before the image is saved into the media folder.
Is there a function I could override which will be able to raise an issue with a file being uploaded in the wrong format prior to the record being saved but after the file has at least been stored or is there a way of finding the temporary file location during the execution of clean()?
It's not as fool proof as actually loading up the image with PIL and checking its format, but the field has a name attribute that you can check when cleaning the model.
import re
p = re.compile(r'.*\.(jpg|jpeg)$', re.I)
filename = self.your_file_field.name
if not p.match(filename):
raise ValidationError('You must upload a JPEG image')
in uploaded you can use:
...
import Image
from cStringIO import StringIO
self.image.open()
i = Image.open(StringIO(self.image.file.read())
...
for check if file uploaded:
from django.core.files.uploadedfile import InMemoryUploadedFile
if isinstance(self.image.file, InMemoryUploadedFile):
...
I check all it in django 1.3