Saving photo modified by PIL - Python 3+ Django 2+ - django

I am trying to save my photo recive from my form changed by PIL in the ImageField field.
file = cd['custom_img'] #get my file from my form
#resize image
image = Image.open(file)
(w, h) = image.size
if (w > 1000):
h = int(h * 1000. / w)
w = 1000
image = image.resize((w, h), Image.ANTIALIAS)
rgb_image = image.convert('RGB')
#save in object
thumb_io = BytesIO() #create a BytesIO object
rgb_image.save(thumb_io, 'JPEG', quality=80) # save image to BytesIO object
thumbnail = File(thumb_io) #create a django friendly File object
owner.basic_img = thumbnail
owner.save()
My code does not return any result. Nothing is still written in my field.
My attempts:
1.) Checking if the file will be saved
I tried to check if my picture was saved correctly. Everything works well. A modified photo is created from the form sent.
#resize image
print(file)
image = Image.open(file)
print(image)
(w, h) = image.size
if (w > 1000):
h = int(h * 1000. / w)
w = 1000
image = image.resize((w, h), Image.ANTIALIAS)
rgb_image = image.convert('RGB')
#save
rgb_image.save('my_image.jpg')
2.) I tried to save the photo according to this tutorial
thumbnail = File(thumb_io, name=image.name) # create a django friendly File object
My code raises an error here
AttributeError: 'Image' object has no attribute 'name'
How can I save my resize image by PIL in my view?

Related

How to save pillow processed image in already existing Django object

I have created a object model as below
from django.db import models
# Create your models here.
class ImageModel(models.Model):
image = models.ImageField(upload_to='images/')
editedImg = models.ImageField(upload_to='images/')
def delete(self, *args, **kwargs):
self.image.delete()
self.editedImg.delete()
super().delete(*args, **kwargs)
And here is what i am trying to do in a function
from django.shortcuts import render
from EditorApp.forms import ImageForm
from EditorApp.models import ImageModel
from django.http import HttpResponseRedirect
from PIL import Image
def edit_column(request):
codArr = request.POST.getlist('codArr[]')
imgs = ImageModel.objects.first()
orgImage = ImageModel.objects.first().image
orgImage = Image.open(orgImage)
croppedImg = orgImage.crop((int(codArr[0]), int(codArr[1]), int(codArr[2]), int(codArr[3])))
# croppedImg.show()
# imgs.editedImg = croppedImg
# imgs.save()
return HttpResponseRedirect("/editing/")
What i am trying to do is the codArr consists of coordinates of top(x, y) and bottom(x, y) in the array form(Which is not an issue and is tested(croppedImg.show() showed the desired cropped image) and handled and used to crop the image). Image crop is working fine. But what i am trying to do is to save the cropped image in editedImg of the model used above. The above commented one is what i tried but throw a error AttributeError: _committed
As i have not used any name for image in model as its not required.
Kindly help please, Would be very thankfull.
you should do it like this:
from io import BytesIO
from api.models import ProductPicture
from django.core import files
codArr = request.POST.getlist('codArr[]')
img_obj = ImageModel.objects.first()
orgImage = img_obj.image
orgImage = Image.open(orgImage)
croppedImg = orgImage.crop((int(codArr[0]), int(codArr[1]), int(codArr[2]), int(codArr[3])))
thumb_io = BytesIO() # create a BytesIO object
croppedImg.save(thumb_io, 'png')
editedImg = files.File(thumb_io, name=file_name)
img_obj.editedImg = editedImg
img_obj.save()
You can use Python's context manager to open the image and save it to the desired storage in that case I'm using the images dir.
Pillow will crop the image and image.save() will save it to the filesystem and after that, you can add it to Django's ImageField and save it into the DB.
The context manager takes care of the file opening and closing, Pillow
takes care of the image, and Django takes care of the DB.
from PIL import Image
with Image.open(orgImage) as image:
file_name = image.filename # Can be replaced by orgImage filename
cropped_path = f"images/croped-{file_name}"
# The crop method from the Image module takes four coordinates as input.
# The right can also be represented as (left+width)
# and lower can be represented as (upper+height).
(left, upper, right, lower) = (20, 20, 100, 100)
# Here the image "image" is cropped and assigned to new variable im_crop
im_crop = image.crop((left, upper, right, lower))
im_crop.save(cropped_path)
imgs.editedImg = cropped_path
imgs.save()
Pillow's reference

Django Form Imagefield validation for certain width and height

I am trying to validate the image dimension in form level and display a message to user if submitted photo does not meet requirement which is image dimension 1080x1920. I dont want to store the width and height size in database. I tried with the Imagefield width and height attribute. But it is not working.
class Adv(models.Model):
image = models.ImageField(upload_to=r'photos/%Y/%m/',
width_field = ?,
height_field = ?,
help_text='Image size: Width=1080 pixel. Height=1920 pixel',
You can do it in two ways
Validation in model
from django.core.exceptions import ValidationError
def validate_image(image):
max_height = 1920
max_width = 1080
height = image.file.height
width = image.file.width
if width > max_width or height > max_height:
raise ValidationError("Height or Width is larger than what is allowed")
class Photo(models.Model):
image = models.ImageField('Image', upload_to=image_upload_path, validators=[validate_image])
Cleaning in forms
def clean_image(self):
image = self.cleaned_data.get('image', False)
if image:
if image._height > 1920 or image._width > 1080:
raise ValidationError("Height or Width is larger than what is allowed")
return image
else:
raise ValidationError("No image found")
We need an image processing library such as PI to detect image dimension, here is the proper solution:
# Custom validator to validate the maximum size of images
def maximum_size(width=None, height=None):
from PIL import Image
def validator(image):
img = Image.open(image)
fw, fh = img.size
if fw > width or fh > height:
raise ValidationError(
"Height or Width is larger than what is allowed")
return validator
then in the model:
class Photo(models.Model):
image = models.ImageField('Image', upload_to=image_upload_path, validators=[maximum_size(128,128)])

How can i display a resized image in python tkinter

I'm developing a GUI in Python using Tkinter to learn image processing. GUI's process flow would be as
Load image (jpg|png|...) => Resize/ thumbnail image (240 * 240) => Preview image
from Tkinter import *
import PIL
class Window:
def __init__(self, master):
master.title("Image Processing test")
master.minsize(800, 400)
from PIL import Image
im = Image.open("IMG_0562.png")
size = 240, 240
im.thumbnail(size)
p = im.tobytes()
# photo = PhotoImage(file="IMG_0562.gif")
# photo = BitmapImage(data=p)
w = Label(root, image=photo, width=240, height=240).grid(row=20, column=2)
self.photo = photo
root = Tk()
window = Window(root)
root.mainloop()
My problem is I couldn't get the image in a proper format to use it in Label. As Label only accepts PhotoImage and BitmapImage. PhotoImage doesn't support png or jpg file. So I used Image from PIL to load and resize my colored image. I've tried Image.tobitmap() and Image.tobytes() too but not useful in this case.
Solved the problem by saving the image in memory using io.BytesIO()
from Tkinter import *
from PIL import Image
import io
class Window:
def __init__(self, master):
master.title("Image Processing test")
master.minsize(800, 400)
im = Image.open("IMG_0562.png")
size = 240, 240
im.thumbnail(size)
b = io.BytesIO()
im.save(b, 'gif')
p = b.getvalue()
photo = BitmapImage(data=p)
w = Label(root, image=photo, width=240, height=240).grid(row=20, column=2)
self.photo = photo
root = Tk()
window = Window(root)
root.mainloop()

displaying matplotlib plot in djangoview

I want to display an image produced by matplotlib in django.
I have a working solution but want to do it without writing to the disk.
Here is the code:
def __get_img_data1(): # not working - returns a white blank image
fig = plt.figure()
imgdata = StringIO.StringIO()
fig.savefig(imgdata, format='png')
imgdata.seek(0)
content = imgdata.getvalue()
imgdata.close()
return content
def __get_img_data2(): # not working - returns a broken image
fig = plt.figure()
imgdata = StringIO.StringIO()
fig.savefig(imgdata, format='png')
imgdata.seek(0)
from PIL import Image
return Image.open(imgdata)
def __get_img_data3(): # working!
img_file = NamedTemporaryFile(delete=False)
plt.savefig(img_file.name, dpi=600)
img_data = open(img_file.name + '.png', 'rb').read()
os.remove(img_file.name)
os.remove(img_file.name + '.png')
return img_data
I have taken 3 approaches shown above.
How to make it work without writing to the disk?
It is supposed to work according to the documentation:
http://matplotlib.org/faq/howto_faq.html
but it displays an empty white image.
uh, I figured out.
The problem is that I needed to get the figure object before doing my plotting.
This works:
def __get_png_img_buff(fig):
buff = StringIO.StringIO()
fig.savefig(buff, format='png')
buff.seek(0)
return buff

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