|
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() |
|
|
|
|
|
app.mount("/static", StaticFiles(directory="static"), name="static") |
|
|
|
|
|
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 |
|
|
|
|
|
@app.get("/", response_class=HTMLResponse) |
|
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> |
|
""" |
|
|
|
|
|
@app.get("/demo", response_class=HTMLResponse) |
|
def demo_page(): |
|
|
|
url1 = "https://raw.githubusercontent.com/webdevserv/images_video/main/cowportrait.jpg" |
|
url2 = "https://raw.githubusercontent.com/webdevserv/images_video/main/cowlandscape.jpg" |
|
|
|
|
|
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") |
|
|
|
|
|
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> |
|
""" |
|
|
|
|
|
@app.get("/application", response_class=HTMLResponse) |
|
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> |
|
""" |
|
|
|
@app.post("/upload/") |
|
async def upload_file(file: UploadFile = File(...)): |
|
try: |
|
|
|
contents = await file.read() |
|
img = Image.open(BytesIO(contents)).convert("RGB") |
|
squared_img = fill_square_cropper(img) |
|
|
|
|
|
output = BytesIO() |
|
squared_img.save(output, format="JPEG") |
|
output.seek(0) |
|
|
|
|
|
full_size_encoded_img = base64.b64encode(output.getvalue()).decode("utf-8") |
|
|
|
|
|
display_img = squared_img.copy() |
|
display_img.thumbnail((512, 512)) |
|
display_output = BytesIO() |
|
display_img.save(display_output, format="JPEG") |
|
display_output.seek(0) |
|
|
|
|
|
display_encoded_img = base64.b64encode(display_output.getvalue()).decode("utf-8") |
|
|
|
|
|
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))) |