I'm trying to get image uploads to also save as thumbnails, which works. The problem was when I did an update the custom method saved the thumbnail again in a different directory. so I modified my save function to look like this.
models.py
class Photo(models.Model):
title = models.CharField(max_length=64)
description = models.CharField(max_length=255)
created = models.DateTimeField(auto_now_add=True)
image = models.ImageField(upload_to='photos/%Y%m')
thumbnail = models.ImageField(blank=True, upload_to='thumbnails/%Y%m')
submitter = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
year = models.ForeignKey(Year, blank=True, on_delete=models.CASCADE)
people = TaggableManager(through=TaggedPeople, verbose_name='People')
tags = TaggableManager(through=TaggedGeneric, verbose_name='Tags')
def save(self, *args, **kwargs):
try:
this = Photo.objects.get(id=self.id)
if this.thumbnail != self.thumbnail:
this.thumbnail.delete(save=False)
except:
if self.thumbnail:
img = Image.open(BytesIO(self.thumbnail.read()))
if hasattr(img, '_getexif'):
exif = img._getexif()
if exif:
for tag, label in ExifTags.TAGS.items():
if label == 'Orientation':
orientation = tag
break
if orientation in exif:
if exif[orientation] == 3:
img = img.rotate(180, expand=True)
elif exif[orientation] == 6:
img = img.rotate(270, expand=True)
elif exif[orientation] == 8:
img = img.rotate(90, expand=True)
img.thumbnail((360,360), Image.ANTIALIAS)
output = BytesIO()
img.save(output, format='JPEG', quality=95)
output.seek(0)
self.thumbnail = File(output, self.thumbnail.name)
return super().save(*args, **kwargs)
def __str__(self):
return self.title
and my views
class PhotoCreateView(LoginRequiredMixin, CreateView):
model = Photo
fields = ['image', 'title', 'description', 'year', 'people', 'tags']
template_name = 'photoapp/create.html'
success_url = '/photo/?page=1'
extra_context = {'tags':GenericTag.objects.all().order_by('name'),'people':PeopleTag.objects.all().order_by('name'),}
def form_valid(self, form):
form.instance.thumbnail = self.request.FILES['image']
form.instance.submitter = self.request.user
return super().form_valid(form)
class PhotoUpdateView(LoginRequiredMixin, UpdateView):
template_name = 'photoapp/update.html'
model = Photo
fields = ['title', 'description', 'year', 'people', 'tags']
success_url = '/photo/?page=1'
So the CreateView now works fine, and I have stopped the duplicate thumbnail files, but the UpdateView does not work. How can I fix this?
I figured it out. I just had to add a save to the try section.
def save(self, *args, **kwargs):
try:
this = Photo.objects.get(id=self.id)
if this.thumbnail != self.thumbnail:
this.thumbnail.delete(save=False)
return super().save(*args, **kwargs)
except:
if self.thumbnail:
img = Image.open(BytesIO(self.thumbnail.read()))
if hasattr(img, '_getexif'):
exif = img._getexif()
if exif:
for tag, label in ExifTags.TAGS.items():
if label == 'Orientation':
orientation = tag
break
if orientation in exif:
if exif[orientation] == 3:
img = img.rotate(180, expand=True)
elif exif[orientation] == 6:
img = img.rotate(270, expand=True)
elif exif[orientation] == 8:
img = img.rotate(90, expand=True)
img.thumbnail((360,360), Image.ANTIALIAS)
output = BytesIO()
img.save(output, format='JPEG', quality=95)
output.seek(0)
self.thumbnail = File(output, self.thumbnail.name)
return super().save(*args, **kwargs)
I had a similar complaint in Django-Wagtail, where saving image.save() programmatically duplicated on the file system the imported image. Do get around this I used Python Glob to get the path, and OS to remove the original image after the save function had been loaded. It worked well enough for Wagtail, I wanted to capture the issue for others - as there isn't much on the web about this. It's frustrating, doubles your disk space usage due to this functionality if you aren't careful! I don't like that it renames the files/moves them, but it is what it is.
from django.core.management.base import BaseCommand, CommandError
from wagtail.images.models import Image
from django.core.files.images import ImageFile
import os
from os import path
import glob
#import sqlite3
class Command(BaseCommand):
help = "just a thing to "
def handle(self, *args, **options):
target_path = "/home/inmyth/inmyth/media/images/"
my_images = []
if os.path.exists(target_path):
my_images = glob.glob(target_path + "*.png")
for mj_imgs in my_images:
print(mj_imgs)
image_file = ImageFile(open(mj_imgs, 'rb'), name=mj_imgs[:-4])
img_label = mj_imgs.rfind("/") + 1
image = Image(title=mj_imgs[img_label:-4], file=image_file)
image.save()
os.remove(mj_imgs)
pass
Related
I coded FaceCropped using cv2 and it works!
I want to put img through facecropped method in ProcessedImageField model.
Is there anyway?
What I want is to automatically cut the face and put it in the database when uploading pictures into student model.
method facecropped
import numpy as np
import cv2
import os
import glob
def FaceCropped(full_path, extra='face', show=False):
face_cascade = cv2.CascadeClassifier('C:../haarcascade_frontalface_default.xml')
full_path = full_path
path,file = os.path.split(full_path)
ff = np.fromfile(full_path, np.uint8)
img = cv2.imdecode(ff, cv2.IMREAD_UNCHANGED)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3,5)
for (x,y,w,h) in faces:
cropped = img[y - int(h/4):y + h + int(h/4), x - int(w/4):x + w + int(w/4)]
result, encoded_img = cv2.imencode(full_path, cropped)
if result:
with open(path + '/' + extra + file, mode='w+b') as f:
encoded_img.tofile(f)
if show:
cv2.imshow('Image view', cropped)
cv2.waitKey(0)
cv2.destroyAllWindows()
my model.py
class Student(models.Model):
picture = ProcessedImageField(
verbose_name = 'picture',
upload_to = 'students/%Y/',
processors=[ResizeToFill(300,300)],
options={'quality':80},
format='JPEG',
null=True,
blank=True,
default='students/no-img.jpg',
)
name = models.CharField(max_length=255)
my views.py
class StudentAdd(FormView):
model = Student
template_name = 'student/list_add.html'
context_object_name = 'student'
form_class = AddStudent
def post(self, request):
form = self.form_class(request.POST, request.FILES)
if form.is_valid():
student = form.save(commit=False)
student.created_by = request.user
student.save()
messages.info(request, student.name + ' addddd', extra_tags='info')
if "save_add" in self.request.POST:
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
return redirect('student_detail', pk=student.pk, school_year=student.school_year)
return FormView.post(self, request)
There several ways to achieve this.
Override Model Save Method
One way to save crop Image into database is that you need to override the save method.
Have a look at this document.
You can do whatever you want to process on model data before you save data into database. You’re free to override these methods (and any other model method) to alter behavior.
The code below is from django document.
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
do_something()
super().save(*args, **kwargs) # Call the "real" save() method.
do_something_else()
Inherit Field class and override clean method
I assumed you are using ProcessedImageField from imagekit
from imagekit.models import ProcessedImageField
You can inherit ProcessedImageField and override the clean method of Field classes.
Clean method ensure that all data in field is cleaned. Image processing before saving can be in clean method.
The code below is from django source code.
def clean(self, value):
"""
Validate the given value and return its "cleaned" value as an
appropriate Python object. Raise ValidationError for any errors.
"""
value = self.to_python(value)
self.validate(value)
self.run_validators(value)
return value
Override Form clean method
This question explain how to override form clean method.
Clean method will run before save method.
This is another common way to change the data before saving.
I solved it ! Cheers Misster Hao!
Can we optimize the code more?
in model.py
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
FaceCropped(self.picture.path)
def FaceCropped(full_path):
face_cascade = cv2.CascadeClassifier('D:/Dropbox/atom/django/Manage/Project/student/haarcascade_frontalface_default.xml')
full_path = full_path
path,file = os.path.split(full_path)
ff = np.fromfile(full_path, np.uint8)
img = cv2.imdecode(ff, cv2.IMREAD_UNCHANGED)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3,5)
for (x,y,w,h) in faces:
cropped = img[y - int(h/4):y + h + int(h/4), x - int(w/4):x + w + int(w/4)]
result, encoded_img = cv2.imencode(full_path, cropped)
if result:
with open(full_path, mode='w+b') as f:
encoded_img.tofile(f)
I am using the django-resized package to resize the image.
When I upload a PNG file using this package, the extension is changed to apng.
I don't want this extension to change..
Other image files are normally uploaded.
What should I fix it?
Background
- django==2.1.5
- django-resized==0.3.9
When use Django's default model.ImageField, the extension of png does not change.
# models.py
def upload_to(instance, filename):
return 'vip/{username}/{filename}'.format(
username=instance.whose.whose.username, filename=filename)
class VipIndex(models.Model):
whose = models.OneToOneField(Profile, on_delete=models.CASCADE, related_name='vipindex')
main_big_image = ResizedImageField(crop=['middle', 'center'], size=[500, 300], quality=50, blank=True, upload_to=upload_to)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-created_at']
# forms.py
class VipMainForm(forms.ModelForm):
class Meta:
model = VipIndex
fields = (
'main_big_image',
)
def __init__(self, *args, **kwargs):
super(VipMainForm, self).__init__(*args, **kwargs)
self.fields['main_big_image'].widget.attrs = {'autocomplete': 'off', 'class': 'form-control'}
# views.py
#login_required
def profile_2(request):
if request.method == 'POST':
form_main = VipMainForm(request.POST, request.FILES)
if form_main.is_valid():
nav = form_main.save(commit=False)
nav.whose = request.user.profiles
nav.save()
return redirect('profile_2')
else:
form_main = VipMainForm()
return render(request, 'accounts/profile_2.html', {
'form_main':form_main,
})
Are there specific settings to keep the extension of the png file?
Add the following to settings.py
DJANGORESIZED_DEFAULT_FORMAT_EXTENSIONS = {'PNG': ".png"}
and it will override saving the file as .apng
I have the following clean method by which I reduce an image if it is too large:
class CompanyForm(forms.ModelForm):
class Meta:
model = Company
exclude = ('limited')
def clean_image(self):
image_field = self.cleaned_data.get('image')
if image_field:
reduced_image = reduce_image(image_field, 550)
return reduced_image
else:
raise forms.ValidationError('You must upload a square logo image')
return image_field
My reduce image looks like the following:
def reduce_image(image_field, height_max):
if image_field:
try:
image_file2 = BytesIO(image_field.read())
image = Image.open(image_file2).convert('RGB')
print(image)
except:
#raise ValidationError('There was a problem with the image, please try another.')
print('returning from image errror')
return image_field
w, h = image.size
print('image width:'+str(w))
print('image height:'+str(h))
if h > height_max:
print('height toolarge')
ratio = h/float(w)
new_height = ratio * height_max
image = image.resize((int(height_max), int(new_height)), Image.ANTIALIAS)
image_file = BytesIO()
image.save(image_file, 'JPEG', quality=90)
image_field.file = image_file
print(image_file)
return image_field
The first time I save, it saves no problem. When I save a second time to update the model, it removes the image.
Why might this be happening?
In the post method of your update view pass the following arguments to your form
get_object it's a custom method that I use to return the concerned object
If you're using CBV
def post(self, request, *args, **kwargs):
self.form_class(request.POST, request.FILES, instance=self.get_object())
....
For function based views
if request.method == 'POST':
form = CompanyForm(request.POST, request.FILES, instance=get_object())
if form.is_valid():
....
I am getting the following error:
'MyProfile' object has no attribute 'get_source_filename'
The following code is from a previously answered SO question, so I'm not sure what get_source_filename() is or does. A google search turned up nothing.
class MyProfile(UserenaBaseProfile):
coverpic = models.ImageField(upload_to="site_media/media/covers/", null=True, blank=True)
def save(self, *args, **kwargs):
# Did we have to resize the image?
# We pop it to remove from kwargs when we pass these along
image_resized = kwargs.pop('image_resized',False)
super(MyProfile, self).save(*args, **kwargs)
if self.coverpic:
print "yes"
basewidth = 300
filename = self.get_source_filename()
image = Image.open(filename)
wpercent = (basewidth/float(image.size[0]))
hsize = int((float(image.size[1])*float(wpercent)))
img = image.resize((basewidth,hsize), PIL.Image.ANTIALIAS)
self.coverpic = img
self.coverpic.save
I'm been working on this Photo Organizer and Sharing App Part I at http://lightbird.net/dbe/photo.html. I'm trying to generate a thumbnail and when I do . I get this error.
I have Windows Vista.
IOError at /admin/photo/image/add/
(13, 'Permission denied')
Request Method: POST
Request URL: http://127.0.0.1:8000/admin/photo/image/add/
Django Version: 1.4.3
Exception Type: IOError
Exception Value: (13, 'Permission denied')
Exception Location:C:\Python26\lib\site-packages\PIL\Image.py in save, line 1399
Python Executable:C:\Python26\python.exe
Python Version: 2.6.0
Python Path:
['C:\\djcode\\mysite',
'C:\\Python26\\python26.zip',
'C:\\Python26\\DLLs',
'C:\\Python26\\lib',
'C:\\Python26\\lib\\plat-win',
'C:\\Python26\\lib\\lib-tk',
'C:\\Python26',
'C:\\Python26\\lib\\site-packages',
'C:\\Python26\\lib\\site-packages\\PIL']
Server time: Sun, 10 Feb 2013 23:49:34 +1100
My models.py is
from django.db import models
from django.contrib.auth.models import User
from django.contrib import admin
from string import join
from django.core.files import File
from os.path import join as pjoin
from tempfile import *
import os
from PIL import Image as PImage
from mysite.settings import MEDIA_ROOT
class Album(models.Model):
title = models.CharField(max_length=60)
public = models.BooleanField(default=False)
def __unicode__(self):
return self.title
class Tag(models.Model):
tag = models.CharField(max_length=50)
def __unicode__(self):
return self.tag
class Image(models.Model):
title = models.CharField(max_length=60, blank=True, null=True)
image = models.FileField(upload_to="images/")
tags = models.ManyToManyField(Tag, blank=True)
albums = models.ManyToManyField(Album, blank=True)
created = models.DateTimeField(auto_now_add=True)
rating = models.IntegerField(default=50)
width = models.IntegerField(blank=True, null=True)
height = models.IntegerField(blank=True, null=True)
user = models.ForeignKey(User, null=True, blank=True)
thumbnail2 = models.ImageField(upload_to="images/", blank=True, null=True)
def __unicode__(self):
return self.image.name
def save(self, *args, **kwargs):
"""Save image dimensions."""
super(Image, self).save(*args, **kwargs)
im = PImage.open(pjoin(MEDIA_ROOT, self.image.name))
self.width, self.height = im.size
# large thumbnail
fn, ext = os.path.splitext(self.image.name)
im.thumbnail((128,128), PImage.ANTIALIAS)
thumb_fn = fn + "-thumb2" + ext
tf2 = NamedTemporaryFile()
im.save(tf2.name, "JPEG")
self.thumbnail2.save(thumb_fn, File(open(tf2.name)), save=False)
tf2.close()
# small thumbnail
im.thumbnail((40,40), PImage.ANTIALIAS)
thumb_fn = fn + "-thumb" + ext
tf = NamedTemporaryFile()
im.save(tf.name, "JPEG")
self.thumbnail.save(thumb_fn, File(open(tf.name)), save=False)
tf.close()
super(Image, self).save(*args, ** kwargs)
def size(self):
"""Image size."""
return "%s x %s" % (self.width, self.height)
def __unicode__(self):
return self.image.name
def tags_(self):
lst = [x[1] for x in self.tags.values_list()]
return str(join(lst, ', '))
def albums_(self):
lst = [x[1] for x in self.albums.values_list()]
return str(join(lst, ', '))
def thumbnail(self):
return """<img border="0" alt="" src="/media/%s" height="40" />""" % (
(self.image.name, self.image.name))
thumbnail.allow_tags = True
class AlbumAdmin(admin.ModelAdmin):
search_fields = ["title"]
list_display = ["title"]
class TagAdmin(admin.ModelAdmin):
list_display = ["tag"]
class ImageAdmin(admin.ModelAdmin):
search_fields = ["title"]
list_display = ["__unicode__", "title", "user", "rating", "size", "tags_", "albums_","thumbnail", "created"]
list_filter = ["tags", "albums"]
def save_model(self, request, obj, form, change):
obj.user = request.user
obj.save()
The problem is here:
from django.core.files import File
from os.path import join as pjoin
from tempfile import *
class Image(models.Model):
# ...
thumbnail2 = models.ImageField(upload_to="images/", blank=True, null=True)
def save(self, *args, **kwargs):
"""Save image dimensions."""
super(Image, self).save(*args, **kwargs)
im = PImage.open(pjoin(MEDIA_ROOT, self.image.name))
self.width, self.height = im.size
# large thumbnail
fn, ext = os.path.splitext(self.image.name)
im.thumbnail((128,128), PImage.ANTIALIAS)
thumb_fn = fn + "-thumb2" + ext
tf2 = NamedTemporaryFile()
im.save(tf2.name, "JPEG")
self.thumbnail2.save(thumb_fn, File(open(tf2.name)), save=False)
tf2.close()
# small thumbnail
im.thumbnail((40,40), PImage.ANTIALIAS)
thumb_fn = fn + "-thumb" + ext
tf = NamedTemporaryFile()
im.save(tf.name, "JPEG")
self.thumbnail.save(thumb_fn, File(open(tf.name)), save=False)
tf.close()
super(Image, self).save(*args, ** kwargs)
How do I fix this error?
It looks to me like django doesn't have the permissions it needs to access your MEDIA_ROOT folder.
Have a look at your MEDIA_ROOT settings in your settings.py file. Then check the permissions on the folder (something like ls -lsa /path/to/media_root from a bash shell). Make sure the user running django as write permission to the folder.
Also, as asermax points out, make sure you have created an images directory within your MEDIA_ROOT.
Have a look at the documentation for serving static files particularly the section on serving other directories
UPDATE
Perhaps it's this issue. Try replacing im.save(tf2.name, "JPEG") with im.save(tf2, "JPEG")
set SELinux permisions
http://wiki.apache.org/httpd/13PermissionDenied
take a look to this, it may be there the solution