Django cropper - getting additional resized images from cropped image - django

I have problem with getting additional resized images from croped image. I am using django-cropper 0.1.
Originally model for Cropped Image have part of code like this:
class Cropped(models.Model):
def __unicode__(self):
return u'%s-%sx%s' % (self.original, self.w, self.h)
def upload_image(self, filename):
return '%s/crop-%s' % (settings.ROOT, filename)
def save(self, *args, **kwargs): #force_insert=False, force_update=False, using=None):
source = self.original.image.path
target = self.upload_image(os.path.basename(source))
Image.open(source).crop([
self.x, # Left
self.y, # Top
self.x + self.w, # Right
self.y + self.h # Bottom
]).save(django_settings.MEDIA_ROOT + os.sep + target)
self.image = target
super(Cropped, self).save(*args, **kwargs)
But i want to have additional resized images from cropped image so I change code a little and now it looks like that:
class Cropped(models.Model):
def __unicode__(self):
return u'%s-%sx%s' % (self.original, self.w, self.h)
def _get_average_path(self):
return _add_average(self.path)
average_path = property(_get_average_path)
def _get_average_url(self):
return _add_average(self.url)
average_url = property(_get_average_url)
def _get_large_path(self):
return _add_large(self.path)
large_path = property(_get_large_path)
def _get_large_url(self):
return _add_large(self.url)
large_url = property(_get_large_url)
def upload_image(self, filename):
return '%s/crop-%s' % (settings.ROOT, filename)
def save(self, *args, **kwargs): #force_insert=False, force_update=False, using=None):
source = self.original.image.path
target = self.upload_image(os.path.basename(source))
img = Image.open(source).crop([
self.x, # Left
self.y, # Top
self.x + self.w, # Right
self.y + self.h # Bottom
]).save(django_settings.MEDIA_ROOT + os.sep + target)
self.image = target
super(Cropped, self).save(*args, **kwargs)
img = Image.open(self.image.path)
img = img.resize((180, 180), Image.ANTIALIAS)
img.save(self.large_path, 'JPEG')
img = img.resize((104, 104), Image.ANTIALIAS)
img.save(self.average_path, 'JPEG')
But it still doing the basic job. Can someone help me and give any suggestion of what should I fix in this code?

I answer my own question because i found solution and maybe it someone. Maybe it is now the best solution, but it works. First it is needed to add additional field for additional, resized image:
average = models.ImageField(
blank=True, null=True,
verbose_name = _('Image'),
upload_to = upload_image,
editable = False,
)
Then it is needed to chnge save() method to something like this:
def save(self, *args, **kwargs): #force_insert=False, force_update=False, using=None):
source = self.original.image.path
target = self.upload_image(os.path.basename(source))
Image.open(source).crop([
self.x, # Left
self.y, # Top
self.x + self.w, # Right
self.y + self.h # Bottom
]).save(django_settings.MEDIA_ROOT + os.sep + target)
self.image = target
splited_target = target.split("/")
avepath = "/average-".join(splited_target)
self.average = avepath
Image.open(self.image).resize((104, 104), Image.ANTIALIAS).save(django_settings.MEDIA_ROOT + avepath, 'JPEG')
super(Cropped, self).save(*args, **kwargs)
And that is all.

Related

Django/Pillow - Image resize only if image is uploaded

I'm able to upload an image and resize it, but if I submit the form without images I get this error
The 'report_image' attribute has no file associated with it.
What should I do if no image is uploaded?
This is my models.py
class Report(models.Model):
options = (
('active', 'Active'),
('archived', 'Archived'),
)
category = models.ForeignKey(Category, on_delete=models.PROTECT)
description = models.TextField()
address = models.CharField(max_length=500)
reporter_first_name = models.CharField(max_length=250)
reporter_last_name = models.CharField(max_length=250)
reporter_email = models.CharField(max_length=250)
reporter_phone = models.CharField(max_length=250)
report_image = models.ImageField(_("Image"), upload_to=upload_to, null=True, blank=True)
date = models.DateTimeField(default=timezone.now)
state = models.CharField(max_length=10, choices=options, default='active')
class Meta:
ordering = ('-date',)
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
img = Image.open(self.report_image.path)
if img.height > 1080 or img.width > 1920:
new_height = 720
new_width = int(new_height / img.height * img.width)
img = img.resize((new_width, new_height))
img.save(self.report_image.path)
def __str__(self):
return self.description
I found the solution. Needed to add this check before the actual resize.
if self.report_image:
This way, if no image has been uploaded it will just ignore the resize and proceed without it.
This is the new relevant part:
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.report_image: #check if image exists before resize
img = Image.open(self.report_image.path)
if img.height > 1080 or img.width > 1920:
new_height = 720
new_width = int(new_height / img.height * img.width)
img = img.resize((new_width, new_height))
img.save(self.report_image.path)

Object of type ImageForm is not JSON serializable

I wanted to post image processing from views to celery, but it shows me a JSON-related problem
"Object of type ImageForm is not JSON serializable"
Here the code from models.py
class ImageModel(models.Model):
title = models.CharField(max_length=20, null=True)
sec_title = models.CharField(max_length=20, null=True)
img = models.ImageField(upload_to='upload_p/', blank=False)
slug = models.SlugField(max_length=250, null=True)
def delete(self, *args, **kwargs):
self.img.delete()
super().delete(*args, **kwargs)
forms.py
class ImageForm(forms.ModelForm):
class Meta:
model = ImageModel
fields = ('title', 'img', 'sec_title')
views.py
def upload_image(request):
if request.method == 'POST':
form_w = ImageForm(request.POST, request.FILES)
if form_w.is_valid():
water_mark.delay(form_w)
else:
form = ImageForm()
return render(request, 'Luki/upload_img.html', {
'form': form,
})
tasks.py
#shared_task
def water_mark(form_w):
instance = form_w.save(commit=False)
cd = form_w.cleaned_data['img']
if instance.img:
im = Image.open(instance.img)
width, height = im.size
draw = ImageDraw.Draw(im)
text = "TEST WATERMARK"
font = ImageFont.truetype('arial.ttf', 36)
textwidth, textheight = draw.textsize(text, font)
# calculate the x,y coordinates of the text
margin = 10
x = width - textwidth - margin
y = height - textheight - margin
draw.text((x, y), text, font=font)
thumb_io = BytesIO()
print('BYTES IO: ', thumb_io)
im.save(thumb_io, im.format, quality=100)
instance.img.save(str(cd), ContentFile(thumb_io.getvalue()), save=False)
instance.save()
return redirect('Luki:gallery')
Of course, all the libraries are imported and the code from the views without celera is executed and it looks like this. This works so that the photo you add in the form gets a watermark, saves it and takes you to the gallery.
views.py
def upload_image(request):
if request.method == 'POST':
form_w = ImageForm(request.POST, request.FILES)
if form_w.is_valid():
instance = form_w.save(commit=False)
cd = form_w.cleaned_data['img']
if instance.img:
im = Image.open(instance.img)
width, height = im.size
draw = ImageDraw.Draw(im)
text = "TEST WATERMARK"
font = ImageFont.truetype('arial.ttf', 36)
textwidth, textheight = draw.textsize(text, font)
# calculate the x,y coordinates of the text
margin = 10
x = width - textwidth - margin
y = height - textheight - margin
# draw watermark in the bottom right corner
draw.text((x, y), text, font=font)
thumb_io = BytesIO()
im.save(thumb_io, im.format, quality=100)
instance.img.save(str(cd), ContentFile(thumb_io.getvalue()), save=False)
instance.save()
return redirect('Luki:gallery')
else:
form_w = ImageForm()
return render(request, 'Luki/upload_img.html', {
'form_w': form_w,
})
As per this blog, you can not pass object in celery.
Since Celery is a distributed system, you can't know in which process,
or even on what machine the task will run. So you shouldn't pass
Django model objects as arguments to tasks, its almost always better
to re-fetch the object from the database instead, as there are
possible race conditions involved.
So in your case, save the image in model in view itself and pass pk of the model to celery task, and then fetch those details again from the model.
This is what the code with tasks.py looks like and I still have an error.
def upload_image(request):
if request.method == 'POST':
form_w = ImageForm(request.POST, request.FILES)
# files = request.FILES.getlist('img')
if form_w.is_valid():
wm = form_w.save()
water_mark.delay(wm.pk)
return redirect('Luki:gallery')
else:
form_w = ImageForm()
return render(request, 'Luki/upload_img.html', {
'form_w': form_w,
})
And tasks.py
def water_mark(wm):
instance = ImageModel.objects.get(id=wm)
# im = Image.open(instance.img)
# print('im before saving: ', im)
# Writes the image correctly
# im.save('Luki/media/upload_p/zzzta.jpg')
if instance.img:
# upload_p/img-8945.jpg
print('img before opening: ', instance.img)
# returns upload_p/img-8945.jpg
im = Image.open(instance.img)
print('img after: ', im)
# returns <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=1920x1080 at 0x5AD75F0>
width, height = im.size
draw = ImageDraw.Draw(im)
text = "TEST WATERMARK"
font = ImageFont.truetype('arial.ttf', 36)
textwidth, textheight = draw.textsize(text, font)
# calculate the x,y coordinates of the text
margin = 10
x = width - textwidth - margin
y = height - textheight - margin
# draw watermark in the bottom right corner
draw.text((x, y), text, font=font)
thumb_io = BytesIO()
print('BYTES IO: ', thumb_io)
im.save(thumb_io, im.format, quality=100)
# FieldFile.save(name, content, save=True)
instance.img.save(str('zzjca.jpg'), ContentFile(thumb_io.getvalue()), save=False)
instance.save()
And it returns an error
File "f:\python\portfoliolk\venv\lib\site-packages\eventlet\greenio\base.py", line 100, in set_nonblocking raise NotImplementedError("set_nonblocking() on a file object " NotImplementedError: set_nonblocking() on a file object with no setblocking() method (Windows pipes don't support non-blocking I/O)
I don't know if it's not windows fault...

How to copy image to a new variable

I'm trying this code, but I'm doing something wrong.
class Photo(TimestampedModel):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
image = models.ImageField(upload_to="photos")
image_600 = ImageSpecField(source='image',
processors=[ResizeToFill(600, 600)],
format='JPEG',
options={'quality': 70})
def save(self, *args, **kwargs):
if not self.id:
self.image = self.compressImage(self.image)
super(Photo, self).save(*args, **kwargs)
def compressImage(self, image):
imageTemporary = Image.open(image)
format = imageTemporary.format
outputIoStream = BytesIO()
imageTemporary.save(outputIoStream, format=format, quality=70)
outputIoStream.seek(0)
image = InMemoryUploadedFile(outputIoStream, 'ImageField', image.name,
format, None, None)
return image
def resizeImage(self, image):
basewidth = 600
img = Image.open(image)
format = img.format
wpercent = (basewidth/float(img.size[0]))
hsize = int((float(img.size[1])*float(wpercent)))
img = img.resize((basewidth,hsize), Image.ANTIALIAS)
outputIoStream = BytesIO()
img.save(outputIoStream, format=format, quality=70)
outputIoStream.seek(0)
image = InMemoryUploadedFile(outputIoStream, 'ImageField', image.name,
format, None, None)
return image
The idea is to create a second image (image_card), with a new size.
In the end, I get an image for image_card but not for image.
The methods compressImage() and resizeImage() works correctly, but I suspect that resizeImage is getting the same image instance.
If I comment the line self.image_card = self.resizeImage(self.image) , image works again.

Extract image from Django media video file

I'm trying to store an image from a video file in django in the save method of a model. I've based in on this thread
This is my save method for the model
def save(self, *args, **kwargs):
if not self.slug:
orig = self.slug = slugify(unidecode(self.name))
for x in itertools.count(1):
if not Project.objects.filter(slug=self.slug).exists() or (Project.objects.filter(slug=self.slug).exists() and self == Project.objects.get(slug=self.slug)):
break
self.slug = '%s-%d' % (orig, x)
super(Project, self).save(*args, **kwargs)
if not self.image and self.project_type=='video':
vidcap = cv2.VideoCapture(settings.MEDIA_ROOT + self.video.url)
success,image = vidcap.read()
self.image = image
self.image.name = 'video_images/' + self.slug + '.jpg'
new_path = settings.MEDIA_ROOT + self.image.name
cv2.imwrite(new_path, image)
super(Project, self).save(*args, **kwargs)
I've taken a look at success and it always comes out False (I've tried looping while not success and it eventually timesout)
I changed the save to this. It works when the video name is latin characters but not for Greek
if not self.image and self.project_type=='video':
vidcap = cv2.VideoCapture(settings.BASE_DIR + self.video.url)
success,image = vidcap.read()
new_path = settings.MEDIA_ROOT + '/video_images/' + self.slug + '.jpg'
cv2.imwrite(new_path, image)
self.image = new_path
self.image.name = 'video_images/' + self.slug + '.jpg'
Just realised it doesn't work when video has Greek character set

Can't save avatar field from Model method in Django

I'm following this tutorial for uploading (to S3 Amazon) and manipulating an image with Django.
I'm stuck somewhere, because I'm able to upload the file, but whenever the method that should create the thumb is called, I think something's missed.
This is the code:
def upload_avatar_to(instance, filename):
import os
from django.utils.timezone import now
filename_base, filename_ext = os.path.splitext(filename)
return 'media/images/avatars/%s%s' % (
now().strftime("%Y%m%d%H%M%S"),
filename_ext.lower(),
)
class CustomUser(AbstractBaseUser, PermissionsMixin):
avatar = models.ImageField('profile picture', upload_to=upload_avatar_to, null=True, blank=True)
def save(self, *args, **kwargs):
super(CustomUser, self).save(*args, **kwargs)
self.create_avatar_thumb()
def create_avatar_thumb(self):
import os
from django.core.files.storage import default_storage as storage
if not self.avatar:
return ""
file_path = self.avatar.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:
f = storage.open(file_path, 'r')
image = Image.open(f)
width, height = image.size
if width > height:
delta = width - height
left = int(delta/2)
upper = 0
right = height + left
lower = height
else:
delta = height - width
left = 0
upper = int(delta/2)
right = width
lower = width + upper
image = image.crop((left, upper, right, lower))
image = image.resize((250, 250), Image.ANTIALIAS)
f_thumb = storage.open(thumb_file_path, "w")
image.save(f_thumb, "JPEG")
f_thumb.close()
return "success"
except:
return "error"
def get_avatar_thumb_url(self):
import os
from django.core.files.storage import default_storage as storage
if not self.avatar:
return ""
file_path = self.avatar.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 storage.url(thumb_file_path)
return ""
It seems that everything's ok, but there should be something wrong with the code.
I upload the image, no errors are caught, but:
The image doesn't get resized
No "avatar-name_thumb.jpg" is created and uploaded on the bucket
What could I do?