83 lines
3.4 KiB
Python
83 lines
3.4 KiB
Python
from PIL import Image, ImageDraw, ImageOps
|
|
from sqlalchemy import select
|
|
|
|
from database import models
|
|
from database.transaction import get_session
|
|
|
|
|
|
async def add_circular_avatar(base_image_path, avatar_image_path, output_image_path):
|
|
# Load the base image
|
|
base_image = Image.open(base_image_path)
|
|
base_width, base_height = base_image.size
|
|
|
|
# Calculate the new dimensions for cropping to a 1.9:1 aspect ratio
|
|
new_width = base_width
|
|
new_height = int(new_width / 1.9)
|
|
if new_height > base_height:
|
|
new_height = base_height
|
|
new_width = int(new_height * 1.9)
|
|
|
|
# Center crop the base image to the new dimensions
|
|
left = (base_width - new_width) // 2
|
|
top = (base_height - new_height) // 2
|
|
right = (base_width + new_width) // 2
|
|
bottom = (base_height + new_height) // 2
|
|
base_image = base_image.crop((left, top, right, bottom))
|
|
base_width, base_height = base_image.size
|
|
|
|
# Load the avatar image
|
|
avatar_image = Image.open(avatar_image_path)
|
|
avatar_size = int(base_height * 0.3) # Set the size of the avatar
|
|
avatar_image = avatar_image.resize((avatar_size, avatar_size), Image.LANCZOS)
|
|
|
|
# Create a mask to make the avatar circular
|
|
mask = Image.new('L', (avatar_size, avatar_size), 0)
|
|
draw = ImageDraw.Draw(mask)
|
|
draw.ellipse((0, 0, avatar_size, avatar_size), fill=255)
|
|
|
|
# Apply the mask to the avatar image
|
|
avatar_image = ImageOps.fit(avatar_image, mask.size, centering=(0.5, 0.5))
|
|
avatar_image.putalpha(mask)
|
|
|
|
# Create a border around the avatar
|
|
border_size = int(avatar_size * 0.03)
|
|
border_image = Image.new('RGBA', (avatar_size + 2 * border_size, avatar_size + 2 * border_size), (255, 255, 255, 0))
|
|
border_mask = Image.new('L', (avatar_size + 2 * border_size, avatar_size + 2 * border_size), 0)
|
|
border_draw = ImageDraw.Draw(border_mask)
|
|
border_draw.ellipse((0, 0, avatar_size + 2 * border_size, avatar_size + 2 * border_size), fill=255)
|
|
border_draw.ellipse((border_size, border_size, avatar_size + border_size, avatar_size + border_size), fill=0)
|
|
|
|
avatar_margin = int(avatar_size * 0.1)
|
|
|
|
border_image.putalpha(border_mask)
|
|
border_image.paste(avatar_image, (border_size, border_size), avatar_image)
|
|
|
|
# Calculate the position to paste the avatar (bottom right corner)
|
|
position = (base_width - avatar_size - 2 * border_size - avatar_margin, base_height - avatar_size - 2 * border_size - avatar_margin)
|
|
|
|
# Paste the avatar with border onto the base image
|
|
base_image.paste(border_image, position, border_image)
|
|
|
|
# Save the result
|
|
base_image.save(output_image_path)
|
|
|
|
|
|
async def generate_flight_title_photo(flight_id: int, photo_id: int):
|
|
async with get_session() as db:
|
|
photo = await models.Photo.get_one(db, id=photo_id)
|
|
pilot = (await db.scalars(
|
|
select(models.User)
|
|
.select_from(models.Flight)
|
|
.join(models.Flight.created_by)
|
|
.filter(models.Flight.id == flight_id)
|
|
)).one()
|
|
|
|
photo_filename, photo_ext = photo.filename, photo.filename_extension
|
|
pilot_id, avatar_filename = pilot.id, pilot.avatar_image_filename
|
|
|
|
await add_circular_avatar(
|
|
base_image_path=f'/app/uploads/photos/{flight_id}/{photo_filename}.{photo_ext}',
|
|
avatar_image_path=f'/app/uploads/profile/{pilot_id}/{avatar_filename}',
|
|
output_image_path=f'/app/uploads/photos/{flight_id}/title_photo.jpg',
|
|
)
|