The crop Sizer is super-useful for creating images at a specific size/aspect-ratio however, sometimes you want the âcrop centerpointâ to be somewhere other than the center of a particular image. In fact, the initial inspiration for django-versatileimagefield
came as a result of tackling this very problem.
The crop
Sizerâs core functionality (located in the versatileimagefield.versatileimagefield.CroppedImage.crop_on_centerpoint
method) was inspired by PILâs ImageOps.fit function (by Kevin Cazabon) which takes an optional keyword argument, centering
, that expects a 2-tuple comprised of floats which are greater than or equal to 0 and less than or equal to 1. These two values together form a cartesian coordinate system which dictates the percentage of pixels to âtrimâ off each of the long sides (i.e. left/right or top/bottom, depending on the aspect ratio of the cropped size vs. the original size):
(0.0, 0.0)
(0.0, 0.5)
(0.0, 1.0)
Middle (0.5, 0.0)
(0.5, 0.5)
(0.5, 1.0)
Bottom (1.0, 0.0)
(1.0, 0.5)
(1.0, 1.0)
The crop
Sizer works in a similar way but converts the 2-tuple into an exact (x, y) pixel coordinate which is then used as the âcenterpointâ of the crop. This approach gives significantly more accurate results than using ImageOps.fit
, especially when dealing with PPOI values located near the edges of an image or aspect ratios that differ significantly from the original image.
Note
Even though the PPOI value is used as a crop âcenterpointâ, the pixel it corresponds to wonât necessarily be in the center of the cropped image, especially if its near the edges of the original image.
Note
At present, only the crop
Sizer changes how it creates images based on PPOI but a VersatileImageField
makes its PPOI value available to ALL its attached Filters and Sizers. Get creative!
Each image managed by a VersatileImageField
can store its own, unique PPOI in the database via the easy-to-use PPOIField
. Hereâs how to integrate it into our example model (relevant lines highlighted in the code block below):
# models.py with `VersatileImageField` & `PPOIField` from django.db import models from versatileimagefield.fields import VersatileImageField, \ PPOIField class ImageExampleModel(models.Model): name = models.CharField( 'Name', max_length=80 ) image = VersatileImageField( 'Image', upload_to='images/testimagemodel/', width_field='width', height_field='height', ppoi_field='ppoi' ) height = models.PositiveIntegerField( 'Image Height', blank=True, null=True ) width = models.PositiveIntegerField( 'Image Width', blank=True, null=True ) ppoi = PPOIField( 'Image PPOI' ) class Meta: verbose_name = 'Image Example' verbose_name_plural = 'Image Examples'
As you can see, youâll need to add a new PPOIField
field to your model and then include the name of that field in the VersatileImageField
âs ppoi_field
keyword argument. Thatâs it!
Note
PPOIField
is fully-compatible with south so migrate to your heartâs content!
The Primary Point of Interest is stored in the database as a string with the x and y coordinates limited to two decimal places and separated by an âxâ (for instance: '0.5x0.5'
or '0.62x0.28'
).
PPOI is set via the ppoi
attribute on a VersatileImageField
..
When you save a model instance, VersatileImageField
will ensure its currently-assigned PPOI value is âsentâ to the PPOIField
associated with it (if any) prior to writing to the database.
# Importing our example Model >>> from someapp.models import ImageExampleModel # Retrieving a model instance >>> example = ImageExampleModel.objects.all()[0] # Retrieving the current PPOI value associated with the image field # A `VersatileImageField`'s PPOI value is ALWAYS associated with the `ppoi` # attribute, irregardless of what you named the `PPOIField` attribute on your model >>> example.image.ppoi (0.5, 0.5) # Creating a cropped image >>> example.image.crop['400x400'].url u'/media/__sized__/images/testimagemodel/test-image-crop-c0-5__0-5-400x400.jpg' # Changing the PPOI value >>> example.image.ppoi = (1, 1) # Creating a new cropped image with the new PPOI value >>> example.image.crop['400x400'].url u'/media/__sized__/images/testimagemodel/test-image-crop-c1__1-400x400.jpg' # PPOI values can be set as either a tuple or a string >>> example.image.ppoi = '0.1x0.55' >>> example.image.ppoi (0.1, 0.55) >>> example.image.ppoi = (0.75, 0.25) >>> example.image.crop['400x400'].url u'/media/__sized__/images/testimagemodel/test-image-crop-c0-75__0-25-400x400.jpg' # u'0.75x0.25' is written to the database in the 'ppoi' column associated with # our example model >>> example.save()
As you can see, changing an imageâs PPOI changes the filename of the cropped image. This ensures updates to a VersatileImageField
âs PPOI value will result in unique cache entries for each unique image it creates.
Note
Each time a fieldâs PPOI is set, its attached Filters & Sizers will be immediately updated with the new value.
FormField/Admin Integration¶Itâs pretty hard to accurately set a particular imageâs PPOI when working in the Python shell so django-versatileimagefield
ships with an admin-ready formfield. Simply add an image, click âSave and continue editingâ, click where youâd like the PPOI to be and then save your model instance again. A helpful translucent red square will indicate where the PPOI value is currently set to on the image:
django-versatileimagefield PPOI admin widget example
Note
PPOIField
is not editable so it will be automatically excluded from the admin.
VersatileImageField
fields¶
If youâre using a required (i.e. blank=False
) VersatileImageField
on a project running Django 1.5 youâll need a custom form class to circumvent an already-fixed-in-Django-1.6 issue (that has to do with required fields associated with a MultiValueField
/MultiWidget
used in a ModelForm
).
The example below uses an example model YourModel
that has a required VersatileImageField
as the image
attribute.
# yourapp/forms.py from django.forms import ModelForm from versatileimagefield.fields import SizedImageCenterpointClickDjangoAdminField from .models import YourModel class YourModelForm(VersatileImageTestModelForm): image = SizedImageCenterpointClickDjangoAdminField(required=False) class Meta: model = YourModel fields = ('image',)
Note the required=False
in the formfield definition in the above example.
Integrating the custom form into the admin:
# yourapp/admin.py from django.contrib import admin from .forms import YourModelForm from .models import YourModel class YourModelAdmin(admin.ModelAdmin): form = YourModelForm admin.site.register(YourModel, YourModelAdmin)
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4