Spaces:
Running
Running
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 | |
def home_page(): | |
return """ | |
<html> | |
<head> | |
<link rel="stylesheet" href="/static/styles/style.css"> | |
</head> | |
<body> | |
<img src="/static/images/banner.jpg" alt="Banner" width="100%"> | |
<h2>Square and Fill Image App</h2> | |
<p>Please select an option below:</p> | |
<ul> | |
<li><a href="/demo">Demo</a></li> | |
<li><a href="/application">Application</a></li> | |
</ul> | |
</body> | |
</html> | |
""" | |
# Demo Page | |
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""" | |
<html> | |
<body> | |
<h2>Square Image Demo</h2> | |
<p>Image will be squared with color filler where applicable.</p> | |
<h3>Result 1:</h3> | |
<img src="data:image/jpeg;base64,{encoded_img1}" /> | |
<h3>Result 2:</h3> | |
<img src="data:image/jpeg;base64,{encoded_img2}" /> | |
<p><a href="/">Back</a></p> | |
</body> | |
</html> | |
""" | |
# Application Page | |
def application_page(): | |
return """ | |
<html> | |
<body> | |
<img src="/static/images/banner.jpg" alt="Banner" width="100%"> | |
<h2>Square Image Application</h2> | |
<p>Upload a JPG image to square and fill with color filler.</p> | |
<form action="/upload/" enctype="multipart/form-data" method="post"> | |
<input name="file" type="file"> | |
<input type="submit" value="Square It"> | |
</form> | |
<a href="/">Back</a> | |
</body> | |
</html> | |
""" | |
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""" | |
<img src="/static/images/banner.jpg" alt="Banner" width="100%"> | |
<h2>Image successfully squared!</h2> | |
<img src='data:image/jpeg;base64,{display_encoded_img}' width="512" height="512" /> | |
<br/> | |
<a href="data:image/jpeg;base64,{full_size_encoded_img}" download="squared_image.jpg"> | |
View Full-Size Image | |
</a> | |
""", | |
media_type="text/html" | |
) | |
except Exception as e: | |
return HTMLResponse(content=f"<h3>An error occurred: {e}</h3>", media_type="text/html") | |
if __name__ == "__main__": | |
import uvicorn | |
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", 7860))) |