After image uploading, cropping is a very necessary post-processing step.
Necessity of image cropping:
- To get the exact size of the of the image (e.g : 300 * 300)
- To create a thumbnail
- To create multiple versions of the image for the e-commerce site (left crop, right crop image)
Install required packages:
Make sure you have Django and Django Rest Framework installed also install Pillow.
pip install pillow
Model for Image Upload:
Define a model in your models.py file to handle image uploads. Use ImageField for storing images:
# yourappname/models.py
from django.db import models
class CroppedImage(models.Model):
title = models.CharField(max_length=255)
original_image = models.ImageField(upload_to='original_images/')
cropped_image = models.ImageField(upload_to='cropped_images/', null=True, blank=True)
original_image filed store uploading raw image. It is a backup image. After cropping, the modified image will be saved in the cropped_image field.
Don't forget to run migrations:
python manage.py makemigrations
python manage.py migrate
Serializer:
The serializer is the best place for cropping operations. In serializer, I created a function called center_crop_image that takes the original image file and, using the pillow library, center-crops it and returns it as bytes. For demonstration purposes, I cropped the image to 300 * 300 size. You can change it according to your requirements. At last, save the returned bytes in the cropped_image field.
If the original image width or height is less than the cropped width or height, it will be an exception. You should handle it according to your needs.
# yourappname/serializers.py
from rest_framework import serializers
from myapp.models import CroppedImage
from PIL import Image
from io import BytesIO
from django.core.files.base import ContentFile
class CroppedImageSerializer(serializers.ModelSerializer):
class Meta:
model = CroppedImage
fields = ['pk', 'title', 'original_image', 'cropped_image']
def create(self, validated_data):
original_image = validated_data.get('original_image')
instance = super().create(validated_data)
# Center crop the image
cropped_image = self.center_crop_image(original_image)
instance.cropped_image.save(original_image.name, ContentFile(cropped_image), save=False)
instance.save()
return instance
def center_crop_image(self, image):
img = Image.open(image)
width, height = img.size
min_dim = min(width, height)
left = (width - min_dim) / 2
top = (height - min_dim) / 2
right = (width + min_dim) / 2
bottom = (height + min_dim) / 2
cropped_img = img.crop((left, top, right, bottom))
# Resize the cropped image to a desired size (e.g., 300x300)
cropped_img = cropped_img.resize((300, 300), resample=Image.BICUBIC)
# Convert the cropped image to bytes
buffer = BytesIO()
cropped_img.save(buffer, format='JPEG')
img_bytes = buffer.getvalue()
return img_bytes
center_crop_image function Take a parameter named image; that type is InMemoryUploadedFile. It is a subclass of Python File. So the Image.open function can take the parameter and open it in write mode.
Views:
Create a view in your views.py file to handle image uploads:
# yourappname/views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from myapp.models import CroppedImage
from .serializers import CroppedImageSerializer
@api_view(['POST'])
def cropped_image_api_view(request):
if request.method == 'POST':
serializer = CroppedImageSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
URL Configuration:
Configure your URLs in urls.py to include the DRF view:
# yourappname/urls.py
from myapp.api.views import (
cropped_image_api_view
)
urlpatterns = [
path("api/cropped-image", cropped_image_api_view, name="cropped_image")
]