from fastapi import FastAPI, File, UploadFile from fastapi.responses import HTMLResponse from fastapi.staticfiles import StaticFiles import numpy as np from PIL import Image from io import BytesIO import requests import base64 import os app = FastAPI() # Mount the static folder for CSS and other assets app.mount("/static", StaticFiles(directory="static"), name="static") # Function for cropping and filling the image def fill_square_cropper(img): imgsz = [img.height, img.width] avg_color_per_row = np.average(img, axis=0) avg_color = np.average(avg_color_per_row, axis=0) if img.height > img.width: newimg = Image.new( 'RGB', (img.height, img.height), (round(avg_color[0]), round(avg_color[1]), round(avg_color[2])) ) newpos = (img.height - img.width) // 2 newimg.paste(img, (newpos, 0)) return newimg elif img.width > img.height: newimg = Image.new( 'RGB', (img.width, img.width), (round(avg_color[0]), round(avg_color[1]), round(avg_color[2])) ) newpos = (img.width - img.height) // 2 newimg.paste(img, (0, newpos)) return newimg else: return img # Home Page @app.get("/", response_class=HTMLResponse) def home_page(): return """ Part of Idoia's Developer Portfolio - Innovating the Web

Square and Fill Image App

Please select an option below:

Image creations by Adobe Stock User Umami Lab and Shutterstock User PhoArt101.
""" # Demo Page @app.get("/demo", response_class=HTMLResponse) def demo_page(): # URLs for demo images url1 = "https://raw.githubusercontent.com/webdevserv/images_video/main/cowportrait.jpg" url2 = "https://raw.githubusercontent.com/webdevserv/images_video/main/cowlandscape.jpg" # Process the first image response = requests.get(url1) img1 = Image.open(BytesIO(response.content)).convert("RGB") squared_img1 = fill_square_cropper(img1) output1 = BytesIO() squared_img1.save(output1, format="JPEG") encoded_img1 = base64.b64encode(output1.getvalue()).decode("utf-8") # Process the second image response = requests.get(url2) img2 = Image.open(BytesIO(response.content)).convert("RGB") squared_img2 = fill_square_cropper(img2) output2 = BytesIO() squared_img2.save(output2, format="JPEG") encoded_img2 = base64.b64encode(output2.getvalue()).decode("utf-8") return f""" Part of Idoia's Developer Portfolio - Innovating the Web

Square Image Demo

Image will be squared with color filler where applicable.

Result 1:

Result 2:

Back

Image creations by Adobe Stock User Umami Lab and Shutterstock User PhoArt101.
""" # Application Page @app.get("/application", response_class=HTMLResponse) def application_page(): return """ Part of Idoia's Developer Portfolio - Innovating the Web

Square Image Application

Upload a JPG or PNG image to square and fill with color padding.

Back
Image creations by Adobe Stock User Umami Lab and Shutterstock User PhoArt101.
""" @app.post("/upload/") async def upload_file(file: UploadFile = File(...)): try: # Await file upload contents = await file.read() img = Image.open(BytesIO(contents)).convert("RGB") squared_img = fill_square_cropper(img) # Save the squared image (original size) output = BytesIO() squared_img.save(output, format="JPEG") output.seek(0) # Encode the full-size image for download full_size_encoded_img = base64.b64encode(output.getvalue()).decode("utf-8") # Resize the image for display (512px by 512px) display_img = squared_img.copy() display_img.thumbnail((512, 512)) # Resize for display display_output = BytesIO() display_img.save(display_output, format="JPEG") display_output.seek(0) # Encode the resized display image display_encoded_img = base64.b64encode(display_output.getvalue()).decode("utf-8") # Return the HTML response return HTMLResponse( content=f""" Part of Idoia's Developer Portfolio - Innovating the Web

Image successfully squared!

Download Full-Size Image

Back

Image creations by Adobe Stock User Umami Lab and Shutterstock User PhoArt101.
""", media_type="text/html" ) except Exception as e: return HTMLResponse(content=f"

An error occurred: {e}

", media_type="text/html") if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", 7860)))