Django Models images + Crop

First create an Image model:


class Image(models.Model):
    image = models.ImageField(upload_to="images")
    
    class Meta:
        verbose_name = _('Image')
        verbose_name_plural = _('Images')
Then we add a Crop model to carry crop related data for each image:

class Crop(models.Model):
    image = models.OneToOneField(Image, related_name='crop', on_delete=models.CASCADE)
    x = models.IntegerField()
    y = models.IntegerField()
    width = models.IntegerField()
    height = models.IntegerField()

    class Meta:
        verbose_name = _('Crop')
        verbose_name_plural = _('Crops')
  

The x, y and width and height fields will define the image metadata for our crop.

Like in HTML, these are canvas coordinates where the upper left starts at coordinates (0, 0)

x and y indicates our starting point of the crop relative to the image from the top left x , y axis.

The width indicates distance we go from the x to the right.

And the height indicates distance we go from the y to the bottom.

I recommend a seperate API view to handle the images. The following solution includes the ability to crop and make thumbnails using easy_thumbnails:

class GetImageAPIView(APIView):
	
    def get(self, request, imageid):
        image = get_object_or_404(Image, pk=int(imageid))
        params = request.query_params
        width = int(params.get('width'))
        height = int(params.get('height'))
        to_crop = True if params.get('crop') == 'yes' else False

        try:
            crop = image.crop
        except:
            crop = None
		
        fimage = image.image
        if to_crop and crop:
            x = crop.x
            y = crop.y
            x2 = x + crop.width
            y2 = y + crop.height
            x_width = crop.width
            y_height = crop.height

            crop_box = '%s,%s,%s,%s' % (x,y,x2,y2)
            thumbnailer = get_thumbnailer(fimage)
            cropped_image = thumbnailer.get_thumbnail({
                'size': (x_width, y_height),
                'box': crop_box,
                'detail': True,
                'upscale': True
            })
            fimage = cropped_image
        th = get_thumbnail(fimage, '%sx%s' % (width, height), upscale=True, padding=True, quality=99, padding_color="black")
        url = request.build_absolute_uri(th.url)
        return response.Response({'url': url}, status=status.HTTP_200_OK)

Finally, to actually save the crop information for an image, this all depends on the frontend implementation. Once it's saved you can then serve it in an HTML image tag or whatever.

Comments