旅行記を書いているのですが、流石に何百枚とアップロードしているのに無加工は厳しいと思い画像の右下にロゴを入れる簡易アプリケーションを作りました。
写真の取り込みはWindowsで行うため、ローカル環境でWindowマシンからアクセスし、Ubuntu上で加工ができるアプリケーションを作成しました。
まず下記のような画像をアップロードします。
するとHTMLで次の画像が表示されます。
サーバー上では、保存を行うようにしています。
コードは下記の通りです。
Fast APIでリクエストを処理し、OpenCVとPILで画像加工を行っています。
import base64 from datetime import datetime from io import BytesIO from typing import Annotated import cv2 import numpy as np import uvicorn from fastapi import FastAPI, File, Request, UploadFile from fastapi.responses import HTMLResponse from pathlib import Path from PIL import Image, ImageDraw, ImageFont app = FastAPI() OUTPUT_DIR = Path("output") @app.post("/files/") async def create_files(files: Annotated[list[bytes], File()]): return {"file_sizes": [len(file) for file in files]} @app.post("/uploadfiles/") async def create_upload_files(files: list[UploadFile]): return {"filenames": [file.filename for file in files]} @app.post("/add_logo/") async def add_logo(files: list[UploadFile]): # uploadされた画像を読み込む file = files[0] content = await file.read() original_image = Image.open(BytesIO(content)) # 画像に文字を追加 processed_image = add_text_to_image(original_image, "gesoges0") # 加工された画像を保存 save_image(processed_image, file.filename) # 加工された画像をBase64形式にエンコードしHTMLに表示 buffered = BytesIO() processed_image.save(buffered, "PNG") processed_image_data = base64.b64encode(buffered.getvalue()).decode("utf-8") # 加工された画像をBase64形式にエンコードしてHTMLに表示 encoded_image = f"data:image/png;base64,{processed_image_data}" return HTMLResponse(content=f"<img src='{encoded_image}'/ width=50%>") def add_text_to_image(image, text): """画像にtextを書き込む""" # PIL ImageをOpenCV形式に変換 cv_image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR) # 文字を追加 font = ImageFont.truetype("arial.ttf", 30) img_pil = Image.fromarray(cv_image) draw = ImageDraw.Draw(img_pil) w, h = img_pil.size draw.text((w - 150, h - 50), text, font=font, fill=(255, 255, 255)) # OpenCV形式をPIL Imageに戻す processed_image = cv2.cvtColor(np.array(img_pil), cv2.COLOR_BGR2RGB) return Image.fromarray(processed_image) def save_image(image, name): """save PIL Image""" # 本日の日付 today = datetime.today().strftime("%Y%m%d") save_dir = OUTPUT_DIR / today if not save_dir.exists(): save_dir.mkdir() file_path = save_dir / name print(f"saved: {file_path}") image.save(file_path) @app.get("/") async def main(): content = """ <body> <form action="/add_logo/" enctype="multipart/form-data" method="post"> <input name="files" type="file" multiple> <input type="submit"> </form> </body> """ return HTMLResponse(content=content) if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000)
実行は通常のPythonプログラムと同様です。
python main.py