import traceback from fastapi import FastAPI, UploadFile, File, Form, Request from fastapi.responses import StreamingResponse, HTMLResponse, FileResponse, JSONResponse import cv2 import numpy as np from PIL import Image import io, os, base64 app = FastAPI() BASE = os.getcwd() OUTPUT_DIR = os.path.join(BASE, 'shiny_output') os.makedirs(OUTPUT_DIR, exist_ok=True) HTML = '''

Shiny Monster Maker Pro

全維度色違調整工具 (HSV 模式)

0
1.0
1.0

避免影響灰色/黑色區塊

AWAITING IMAGE...

''' @app.get('/', response_class=HTMLResponse) def home(): return HTML @app.middleware("http") async def catch_exceptions_middleware(request: Request, call_next): try: return await call_next(request) except Exception as e: # 在終端機印出完整的錯誤追蹤 traceback.print_exc() return JSONResponse( status_code=500, content={"message": str(e), "traceback": traceback.format_exc()} ) @app.post('/process_shiny') async def process_shiny( file: UploadFile = File(...), hue_shift: int = Form(...), sat_min: int = Form(...), sat_scale: float = Form(...), # 新增:飽和度倍率 val_scale: float = Form(...) # 新增:亮度倍率 ): try: data = await file.read() nparr = np.frombuffer(data, np.uint8) img = cv2.imdecode(nparr, cv2.IMREAD_UNCHANGED) if img is None: return JSONResponse(status_code=400, content={"message": "解碼失敗"}) # 頻道判斷 if img.ndim == 2: bgr = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR); alpha = None elif img.shape[2] == 3: bgr = img; alpha = None else: bgr = img[:, :, :3]; alpha = img[:, :, 3] hsv = cv2.cvtColor(bgr, cv2.COLOR_BGR2HSV).astype(np.float32) h, s, v = cv2.split(hsv) # 遮罩邏輯 mask = s > (float(sat_min) * 2.55) # --- 核心變色邏輯 --- # 1. 色相旋轉 h[mask] = (h[mask] + float(hue_shift)) % 180 # 2. 飽和度調整 (乘法運算並限制範圍 0-255) s[mask] = np.clip(s[mask] * sat_scale, 0, 255) # 3. 亮度調整 (乘法運算並限制範圍 0-255) v[mask] = np.clip(v[mask] * val_scale, 0, 255) # 合併與轉換回 BGR hsv_new = cv2.merge([h, s, v]).astype(np.uint8) bgr_new = cv2.cvtColor(hsv_new, cv2.COLOR_HSV2BGR) if alpha is not None: res = cv2.merge([bgr_new, alpha]) else: res = bgr_new _, buffer = cv2.imencode('.png', res) return StreamingResponse(io.BytesIO(buffer.tobytes()), media_type='image/png') except Exception as e: traceback.print_exc() return JSONResponse(status_code=500, content={"message": str(e)}) if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8001)