I'm using django. I have a code that will parse an HTML code and try to get and save the image to the database. Here is the code:
link = content[content.find('src=') 5:content.find('alt')-2]
img_data = requests.get(link).content
with open('temp_image.jpg', 'wb') as handler:
handler.write(img_data)
with open('temp_image.jpg', 'rb') as handler:
file_name = link.split("/")[-1]
post.cover.save(file_name, files.File(handler))
os.remove("temp_image.jpg")
But I also need to crop the image. How can I do that? Thank you.
CodePudding user response:
Add a save method to your database in models.py. I have added an example below which crops the image and stores the cropped file. This example crops the image but maintains the aspect ratio where the short side is 200 pixels. Change desired_height
if you need it bigger or smaller.
'''
from PIL import Image
from django.core.files.uploadedfile import InMemoryUploadedFile
class Profile(models.Model):
id = models.UUIDField(primary_key=True,default=uuid.uuid4,editable=False)
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
publish_status = models.BooleanField(max_length=1, null=True, default=False)
hourly_rate = models.DecimalField(max_digits=6, decimal_places=0, null=True)
first_name = models.CharField(max_length=30, null=True)
last_name = models.CharField(max_length=30, null=True)
address = models.CharField(max_length=100, null=True)
birth_date = models.DateField(null=True, blank=True)
bio = models.TextField(validators=[MaxLengthValidator(1200)])
location = models.CharField(max_length=100, null=True)
country = models.CharField(max_length=30, null=True)
affiliation = models.CharField(max_length=100, null=True, blank=True)
profile_image = models.ImageField(blank=False, upload_to='profile_images/', default='profile_images/avatar.png')
cover_image = ResizedImageField(upload_to='cover_images/', size=[768, 432], crop=['middle', 'center'], quality=60,
blank=False, default='cover_images/default_cover_image_1080.jpeg')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return '{} {}'.format(self.first_name, self.last_name)
def save(self, *args, **kwargs):
# Opening the uploaded image
im = Image.open(self.profile_image)
if im.mode == "JPEG":
pass
elif im.mode in ["RGBA", "P"]:
im = im.convert("RGB")
output = BytesIO()
# Resize/modify the image - desired short edge of 200 pixels
original_width, original_height = im.size
if original_width > original_height: # landscape image
aspect_ratio = round(original_width / original_height, 2)
desired_height = 200
desired_width = round(desired_height * aspect_ratio)
im = im.resize((desired_width, desired_height), Image.ANTIALIAS)
elif original_height > original_width: # portrait image
aspect_ratio = round(original_height / original_width, 2)
desired_width = 200
desired_height = round(desired_width * aspect_ratio)
im = im.resize((desired_width, desired_height), Image.ANTIALIAS)
elif original_height == original_width: # square image
desired_width = 200
desired_height = 200
# Resize the image
im = im.resize((desired_width, desired_height), Image.ANTIALIAS)
# after modifications, save it to the output
im.save(output, format='JPEG', subsampling=0, quality=95)
output.seek(0)
# change the imagefield value to be the newly modified image value
self.profile_image = InMemoryUploadedFile(output, 'ImageField',
"%s.jpg" % self.profile_image.name.split('.')[0], 'image/jpeg',
sys.getsizeof(output), None)
super(Profile, self).save()
'''
CodePudding user response:
Using the Pillow library, you can perform all kinds of image processing on your model's save method, including complex crops. Pillow is the gold standard for image processing in Python.
pip install Pillow
You can read more about the Pillow API here in the documentation.
Working Example:
The following is a working example of an overridden Django model save method that crops the largest central square out of an image, regardless of dimensions, then resizes that square to 200 x 200 pixels.
I wrote this as a simple way of handling logo pics or profile pics, where the art or face is in the center of the image. When cropping, it's important to consider the different scenarios that arise when your input file is portrait vs landscape vs square, which is why I've included this example. Handling each indifferently can lead to undesirable resolutions for some images.
from io import BytesIO
import sys
from django.core.files.uploadedfile import InMemoryUploadedFile
from PIL import Image
class MyImage(models.Model):
picture_title = models.CharField(max_length=120)
some_picture = models.ImageField(upload_to='/media')
def __str__(self):
return self.picture_title
def save(self, *args, **kwargs):
if self.pk is not None:
orig = MyImage.objects.get(pk=self.pk)
if self.some_picture:
if orig.some_picture != self.some_picture: # Perform the crop if the image is updated with a different image
im = Image.open(self.some_picture)
im = im.convert("RGB") # To standardize your color space if not already RGB
exif = None
if 'exif' in im.info: # Exif data records orientation, among other things
exif = im.info['exif']
output = BytesIO()
square = 200 # Set the dimension
x_final, y_final = square, square
# Get image dimensions
width = im.size[0]
height = im.size[1]
if width > height: # crop the largest square out of the center of a landscape image and resize
x1 = (float(width/2)-float(height/2))
y1 = (height - height)
x2 = ((float(float(width/2)-float(height/2))) height)
y2 = height
im = im.crop((x1, y1, x2, y2))
im = im.resize((x_final, y_final), Image.ANTIALIAS)
elif width < height: # crop the largest square out of the center of a portrait image and resize
x1 = (width - width)
y1 = (float(height/2)-float(width/2))
x2 = width
y2 = ((float(float(height/2)-float(width/2))) width)
im = im.crop((x1, y1, x2, y2))
im = im.resize((x_final, y_final), Image.ANTIALIAS) # this is Image.LANCZOS in later versions of Pillow
elif width == height: # resize the image if it's already perfectly square
im = im.resize((x_final, y_final), Image.ANTIALIAS)
else:
raise ValueError('Something went wrong.')
if exif:
im.save(output, format='JPEG', exif=exif, quality=60)
else:
im.save(output, format='JPEG', quality=60)
output.seek(0)
self.profile_pic = InMemoryUploadedFile(output,'ImageField', "%s.jpg" % self.some_picture.name.split('.')[0], 'image/jpeg', sys.getsizeof(output), None)
else:
pass
else:
pass
else:
pass
super(MyImage, self).save()
Extra fun fact:
If you're interested in how Pillow's Image.ANTIALIAS
or Image.LANCZOS
method works, check out the fascinating Lanczos algorithm, which is used to downsample images on resize but retain their visual information.