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.
Related
I have a model where a field exists:
image=models.ImageField(upload_to='products/')
models.py
class Product(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
name = models.CharField(max_length=200)
code_number = models.IntegerField(verbose_name='Bar Code')
image = models.ImageField(upload_to='products/')
purchase_price = models.DecimalField(max_digits=10, decimal_places=2)
sale_price= models.DecimalField(max_digits=10, decimal_places=2)
tax = models.ManyToManyField(Tax, blank=True, related_name="tax")
pro_quantity =models.DecimalField(max_digits=10, decimal_places=2, default=0)
description = models.TextField()
branch = models.ForeignKey(Branch, on_delete=models.CASCADE, related_name="branchesp", blank=True, null=True)
created_date = models.DateTimeField(
default=timezone.now)
published_date = models.DateTimeField(
blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.name
views.py
class ProductCreate(View):
form_class = ProductForm
template_name = 'pos/product_form.html'
def get(self, request, *args, **kwargs):
form = self.form_class()
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST, request.FILES)
form.instance.user = self.request.user
if form.is_valid():
# Procesa la imagen subida
image_content = request.FILES['image'].read()
image = resize_image_with_aspect(image_content, 800, 600)
image_file = tempfile.NamedTemporaryFile(mode='wb', suffix='.webp')
image.save(image_file, format="webp", optimize=True, quality=85)
image_file = open(image_file.name, 'rb')
form.instance.image = image_file.read()
image_file = BytesIO(form.instance.image)
image_file.seek(0)
form.instance.image = File(image_file)
product = form.save(commit=False)
product.save()
image_file.close()
return redirect('inventory')
else:
print(form.errors)
return redirect('inventory')
utils.py
import io
from PIL import Image
def resize_image_with_aspect(image_content, width, height):
image = Image.open(io.BytesIO(image_content))
original_width, original_height = image.size
ratio = original_width / original_height
if width > height:
height = int(width / ratio)manteniendo el ratio
else:
width = int(height * ratio)
image = image.resize((width, height), Image.ANTIALIAS)
return image
Now, it seems to work fine but the image doesn't save. What could I be doing wrong?
I want to optimize, resize, and change to webp any type of image that is uploaded through the form and only save the already-converted image in /media/products and the database
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)
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...
i am generating thumbnail and i want to save image path to database when i upload from admin and api:
class Image(models.Model):
license_type = (
('Royalty-Free','Royalty-Free'),
('Rights-Managed','Rights-Managed')
)
image_number = models.CharField(default=random_image_number,max_length=12,unique=True)
title = models.CharField(default=random_image_number,max_length = 100)
image = models.ImageField(upload_to = 'image' , default = 'demo/demo.png')
thumbnail = models.ImageField(upload_to='thumbs')
category = models.ForeignKey('Category', null=True, blank=True, on_delete=models.CASCADE)
shoot = models.ForeignKey(ImageShoot, on_delete=models.CASCADE, related_name='Image', null=True,blank=True)
image_keyword = models.TextField(max_length=1000)
credit = models.CharField(max_length=150, null=True)
location = models.CharField(max_length=100, null=True)
license_type = models.CharField(max_length=20,choices=license_type, default='')
uploaded_at = models.TimeField(auto_now_add=True)
def __str__(self):
return self.title
def save(self, *args, **kwargs):
super(Image, self).save(*args, **kwargs)
if not self.make_thumbnail():
raise Exception('Could not create thumbnail - is the file type valid?')
def make_thumbnail(self):
fh = storage.open(self.image.path)
image = PILImage.open(fh)
image.thumbnail((400,400),PILImage.ANTIALIAS)
fh.close()
thumb_name, thumb_extension = os.path.splitext(self.image.path)
thumb_extension = thumb_extension.lower()
thumb_filename = thumb_name + '_thumb' + thumb_extension
temp_thumb = BytesIO()
image.save(temp_thumb, FTYPE)
temp_thumb.seek(0)
self.thumbnail.save(thumb_filename, ContentFile(temp_thumb.read()), save=True)
temp_thumb.close()
return True
this if we upload image from admin
admin.py:
#admin.register(Image)
class ImageAdmin(admin.ModelAdmin):
readonly_fields=['image_number','uploaded_at']
fields = ['title','image_number','shoot','category',
'image','image_keyword','thumbnail','credit','license_type','location','uploaded_at']
this is my views for api currently just uploading bulk image with out thumbnail how can i create thumbnail from this :
views.py:
class EventImageUploadView(APIView):
def post(self, request,category):
#title = forms.CharField(max_length = 100)
file = request.data['file']
data={
'image':file,
'category_id':category
}
EventsImage.objects.create(**data)
return JsonResponse(json.dumps({'message': "Uploaded"}), status=200, safe=False)
i get this error and its generating multiple images i dont know what causing recursion:
maximum recursion depth exceeded in comparison
backtrace:
File "/home/tboss/Desktop/environment/live/backend/venv/lib/python3.7/site-packages/PIL/TiffImagePlugin.py", line 319, in __init__
if isinstance(value, Fraction):
File "/home/tboss/Desktop/environment/live/backend/venv/lib/python3.7/abc.py", line 139, in __instancecheck__
return _abc_instancecheck(cls, instance)
RecursionError: maximum recursion depth exceeded in comparison
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.