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', )