Spaces:
Sleeping
Sleeping
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> | |
<title>Part of Idoia's Developer Portfolio - Innovating the Web</title> | |
<link rel="stylesheet" href="/static/styles/style.css"> | |
<!-- Meta Tags for SEO --> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<meta name="description" content="Explore the developer portfolio of Idoia, showcasing expertise in FastAPI, web development, and cutting-edge applications."> | |
<meta name="keywords" content="Idoia, Developer, FastAPI, Web Development, Python Projects, Image Processing, Online Portfolio"> | |
<meta name="author" content="Idoia"> | |
<!-- Open Graph Meta Tags --> | |
<meta property="og:title" content="Idoia's Developer Portfolio - Innovating the Web"> | |
<meta property="og:description" content="Showcasing FastAPI projects, web apps, and image processing expertise. Explore Idoia's developer journey."> | |
<meta property="og:image" content="/static/images/banner.jpg"> | |
<meta property="og:url" content="https://webdevserv.github.io/html_bites/dev/webdev.html"> | |
<meta property="og:type" content="website"> | |
<!-- Twitter Card Meta Tags --> | |
<meta name="twitter:card" content="summary_large_image"> | |
<meta name="twitter:title" content="Idoia's Developer Portfolio - Innovating the Web"> | |
<meta name="twitter:description" content="Discover the developer profile of Idoia. Dive into FastAPI-powered web apps and creative Python projects."> | |
<meta name="twitter:image" content="/static/images/banner.jpg"> | |
<link rel="icon" href="/static/images/6464.ico" type="image/x-icon"> | |
<!-- Google Fonts (Optional for Styling) --> | |
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet"> | |
<!-- Schema.org JSON-LD (Optional for Rich Snippets) --> | |
<script type="application/ld+json"> | |
{ | |
"@context": "https://schema.org", | |
"@type": "Person", | |
"name": "Idoia", | |
"jobTitle": "Web Developer", | |
"url": "https://webdevserv.github.io/html_bites/dev/webdev.html", | |
"image": "https://idoia-dev-portfolio.com/static/images/banner.jpg", | |
"description": "Experienced web developer with a focus on Streamlit, HF, Python, and modern web applications." | |
} | |
</script> | |
</head> | |
<body> | |
<img class="banner" 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> | |
<head> | |
<title>Part of Idoia's Developer Portfolio - Innovating the Web</title> | |
<link rel="stylesheet" href="/static/styles/style.css"> | |
</head> | |
<body> | |
<img class="banner" src="/static/images/banner.jpg" alt="Banner" width="100%"> | |
<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> | |
<head> | |
<title>Part of Idoia's Developer Portfolio - Innovating the Web</title> | |
<link rel="stylesheet" href="/static/styles/style.css"> | |
</head> | |
<body> | |
<img class="banner" 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""" | |
<html> | |
<head> | |
<title>Part of Idoia's Developer Portfolio - Innovating the Web</title> | |
<link rel="stylesheet" href="/static/styles/style.css"> | |
</head> | |
<body> | |
<img class="banner" 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" /> | |
<p><a href="data:image/jpeg;base64,{full_size_encoded_img}" download="squared_image.jpg"> | |
Download Full-Size Image</a></p> | |
<p><a href="/">Back</a></p> | |
</body> | |
</html> | |
""", | |
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))) |