Django Form Imagefield validation for certain width and height - django

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)])

Related

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

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?

(Django) How to transfer value from field to the custom ImageKit processor inside the model?

I use Django ImageKit to process/crop uploaded photos. I added my own custom processor to add text to the photo (like watermark):
# ./example/processors.py
from django.conf import settings
from PIL import Image, ImageDraw, ImageFont
_default_font = ImageFont.truetype(settings.TEXT_OVERLAY_FONT_REGULAR, 24)
def add_text_overlay(image, text, font=_default_font):
rgba_image = image.convert('RGBA')
text_overlay = Image.new('RGBA', rgba_image.size, (255, 255, 255, 0))
image_draw = ImageDraw.Draw(text_overlay)
text_size_x, text_size_y = image_draw.textsize(text, font=font)
text_xy = ((rgba_image.size[0] / 2) - (text_size_x / 2), (rgba_image.size[1] / 2) - (text_size_y / 2))
image_draw.text(text_xy, text, font=font, fill=(255, 255, 255, 255))
image_with_text_overlay = Image.alpha_composite(rgba_image, text_overlay)
return image_with_text_overlay
class TextOverlayProcessor(object):
def __init__(self, text='Lorem ipsum dolor sit amet'):
"""
:param text: The overlay text, string.
"""
self.text = text
def process(self, img):
return add_text_overlay(image=img, text=self.text)
But how to transfer value from field to the custom ImageKit processor inside the model? Something like this:
# ./example/models.py
from imagekit.models import ImageSpecField
from imagekit.processors import ResizeToFill
from .processors import TextOverlayProcessor
class Example(models.Model):
description = models.CharField('Description', ...)
image = models.ImageField('Picture', default=None)
image_800x800 = ImageSpecField(
source='image',
processors=[
ResizeToFill(800, 800),
TextOverlayProcessor(text=self.description) # but `self` is wrong and raise error
],
format='JPEG',
options={'quality': 100}
)
...
I will be glad to explanatory comments and/or use cases.
The short answer is https://stackoverflow.com/a/27914831/6543526
From official docs:
Often, when using an ImageSpecField, you may want the spec to vary
based on properties of a model. (For example, you might want to store
image dimensions on the model and then use them to generate your
thumbnail.) Now that we know how to access the source image from our
spec, it’s a simple matter to extract its model and use it to create
our processors list. In fact, ImageKit includes a utility for getting
this information.

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()

Upload Image to Amazon S3 with Flask-admin

I am using Flask-Admin and is very happy with it. However, the sample in Flask-Admin only provides to upload the image to static folder. Is it possible to upload it to S3 directly with Flask-Admin? Thanks.
Regards
Alex
Thanks for your sample code, Alex Chan. I needed this functionality too, so I decided to write more complete S3FileUploadField and S3ImageUploadField classes, based on your code and various other snippets.
You can find my code at:
https://github.com/Jaza/flask-admin-s3-upload
Also up on pypi, so you can install with:
pip install flask-admin-s3-upload
I've documentated a basic usage example in the readme (can see it on the github project page). Hope this helps, for anyone else who needs S3 file uploads in flask-admin.
Here it is but not clean the code..
import os
import os.path as op
import cStringIO
import logging
import config
from flask import url_for
from werkzeug import secure_filename
from werkzeug.datastructures import FileStorage
import boto
from boto.s3.key import Key
from wtforms import ValidationError, fields
from wtforms.widgets import HTMLString, html_params
from flask.ext.admin.babel import gettext
from flask.ext.admin._compat import string_types, urljoin
from flask.ext.admin.form.upload import ImageUploadField
try:
from PIL import Image, ImageOps
except ImportError:
Image = None
ImageOps = None
__all__ = ['FileUploadInput', 'FileUploadField',
'ImageUploadInput', 'ImageUploadField',
'namegen_filename', 'thumbgen_filename']
class ImageUploadInput(object):
"""
Renders a image input chooser field.
You can customize `empty_template` and `data_template` members to customize
look and feel.
"""
empty_template = ('<input %(file)s>')
data_template = ('<div class="image-thumbnail">'
' <img %(image)s>'
' <input type="checkbox" name="%(marker)s">Delete</input>'
'</div>'
'<input %(file)s>')
def __call__(self, field, **kwargs):
kwargs.setdefault('id', field.id)
kwargs.setdefault('name', field.name)
args = {
'file': html_params(type='file',
**kwargs),
'marker': '_%s-delete' % field.name
}
if field.data and isinstance(field.data, string_types):
url = self.get_url(field)
args['image'] = html_params(src=url)
template = self.data_template
else:
template = self.empty_template
return HTMLString(template % args)
def get_url(self, field):
if field.thumbnail_size:
filename = field.thumbnail_fn(field.data)
else:
filename = field.data
if field.url_relative_path:
filename = urljoin(field.url_relative_path, filename)
return field.data
#return url_for(field.endpoint, filename=field.data)
class s3ImageUploadField(ImageUploadField):
"""
Image upload field.
Does image validation, thumbnail generation, updating and deleting images.
Requires PIL (or Pillow) to be installed.
"""
widget = ImageUploadInput()
keep_image_formats = ('PNG',)
"""
If field detects that uploaded image is not in this list, it will save image
as PNG.
"""
def __init__(self, label=None, validators=None,
base_path=None, relative_path=None,
namegen=None, allowed_extensions=None,
max_size=None,
thumbgen=None, thumbnail_size=None,
permission=0o666,
url_relative_path=None, endpoint='static',
**kwargs):
"""
Constructor.
:param label:
Display label
:param validators:
Validators
:param base_path:
Absolute path to the directory which will store files
:param relative_path:
Relative path from the directory. Will be prepended to the file name for uploaded files.
Flask-Admin uses `urlparse.urljoin` to generate resulting filename, so make sure you have
trailing slash.
:param namegen:
Function that will generate filename from the model and uploaded file object.
Please note, that model is "dirty" model object, before it was committed to database.
For example::
import os.path as op
def prefix_name(obj, file_data):
parts = op.splitext(file_data.filename)
return secure_filename('file-%s%s' % parts)
class MyForm(BaseForm):
upload = FileUploadField('File', namegen=prefix_name)
:param allowed_extensions:
List of allowed extensions. If not provided, will allow any file.
:param max_size:
Tuple of (width, height, force) or None. If provided, Flask-Admin will
resize image to the desired size.
:param thumbgen:
Thumbnail filename generation function. All thumbnails will be saved as JPEG files,
so there's no need to keep original file extension.
For example::
import os.path as op
def thumb_name(filename):
name, _ = op.splitext(filename)
return secure_filename('%s-thumb.jpg' % name)
class MyForm(BaseForm):
upload = ImageUploadField('File', thumbgen=prefix_name)
:param thumbnail_size:
Tuple or (width, height, force) values. If not provided, thumbnail won't be created.
Width and height is in pixels. If `force` is set to `True`, will try to fit image into dimensions and
keep aspect ratio, otherwise will just resize to target size.
:param url_relative_path:
Relative path from the root of the static directory URL. Only gets used when generating
preview image URLs.
For example, your model might store just file names (`relative_path` set to `None`), but
`base_path` is pointing to subdirectory.
:param endpoint:
Static endpoint for images. Used by widget to display previews. Defaults to 'static'.
"""
# Check if PIL is installed
if Image is None:
raise Exception('PIL library was not found')
self.max_size = max_size
self.thumbnail_fn = thumbgen or thumbgen_filename
self.thumbnail_size = thumbnail_size
self.endpoint = endpoint
self.image = None
self.url_relative_path = url_relative_path
if not allowed_extensions:
allowed_extensions = ('gif', 'jpg', 'jpeg', 'png', 'tiff')
super(ImageUploadField, self).__init__(label, validators,
base_path=base_path,
relative_path=relative_path,
namegen=namegen,
allowed_extensions=allowed_extensions,
permission=permission,
**kwargs)
def pre_validate(self, form):
super(ImageUploadField, self).pre_validate(form)
if self.data and isinstance(self.data, FileStorage):
try:
self.image = Image.open(self.data)
except Exception as e:
raise ValidationError('Invalid image: %s' % e)
# Deletion
def _delete_file(self, filename):
super(ImageUploadField, self)._delete_file(filename)
self._delete_thumbnail(filename)
def _delete_thumbnail(self, filename):
path = self._get_path(self.thumbnail_fn(filename))
if op.exists(path):
os.remove(path)
# Saving
def _save_file(self, data, filename):
path = self._get_path(filename)
if not op.exists(op.dirname(path)):
os.makedirs(os.path.dirname(path), self.permission)
# Figure out format
filename, format = self._get_save_format(filename, self.image)
if self.image and (self.image.format != format or self.max_size):
if self.max_size:
image = self._resize(self.image, self.max_size)
else:
image = self._resize(self.image, (500, 500))
#image = self.image
self._save_image(image, filename, format)
else:
data.seek(0)
data.save(path)
savedUrl=self._save_image(self.image, filename, format)
self._save_thumbnail(data, filename, format)
return savedUrl
def _save_thumbnail(self, data, filename, format):
if self.image and self.thumbnail_size:
path = self._get_path(self.thumbnail_fn(filename))
savedUrl=self._save_image(self._resize(self.image, self.thumbnail_size),
thumbgen_filename(filename),
format)
return savedUrl
def _resize(self, image, size):
(width, height, force) = size
if image.size[0] > width or image.size[1] > height:
if force:
return ImageOps.fit(self.image, (width, height), Image.ANTIALIAS)
else:
thumb = self.image.copy()
thumb.thumbnail((width, height), Image.ANTIALIAS)
return thumb
return image
def _save_image(self, image, path, format='JPEG'):
if image.mode not in ('RGB', 'RGBA'):
image = image.convert('RGBA')
conn =boto.connect_s3( config.AWS_KEY, config.AWS_SECRET,)
bucket = conn.get_bucket("vipbutton")
k = Key(bucket)
k.key= path
tempFile = cStringIO.StringIO()
image.save(tempFile,format)
#image.seek(0)
#tempFile.seek(0)
#k.set_contents_from_string('This is a test of S3')
k.set_contents_from_string(tempFile.getvalue())
k.set_acl('public-read')
#k.set_contents_from_file(tempFile.getValue())
#with open(path, 'wb') as fp:
# image.save(fp, format)
return k.generate_url(expires_in=0, query_auth=False)
def _get_save_format(self, filename, image):
if image.format not in self.keep_image_formats:
name, ext = op.splitext(filename)
filename = '%s.jpg' % name
return filename, 'JPEG'
return filename, image.format
# Helpers
def namegen_filename(obj, file_data):
"""
Generate secure filename for uploaded file.
"""
return secure_filename(file_data.filename)
def thumbgen_filename(filename):
"""
Generate thumbnail name from filename.
"""
name, ext = op.splitext(filename)
return '%s_thumb%s' % (name, ext)

Django: How to set the size for the image? (models.ImageField)

I use Django 1.5.
How to set the size for the image? For example, the user must add images more than 200 * 200.
models.py:
class UserNew(models.Model):
photo = models.ImageField(upload_to='photo')
You can override the forms' clean method.
class UserNewForm(forms.ModelForm):
def clean_photo(self):
photo = self.cleaned_data.get('photo',None)
if not photo:
raise ValidationError("Something went wrong")
if photo._height < 200 or photo._width < 200:
raise ValidationError("Photo dimensions are too small (minimum 200X200 )")
return photo