from fastapi import FastAPI, File, UploadFile
from fastapi.responses import FileResponse, HTMLResponse
import cv2
import numpy as np
from rembg import remove
from PIL import Image
import io
import zipfile
import os
app = FastAPI()
# دالة لتحسين حدة الصورة (Sharpening) للحفاظ على جودة البكسل
def sharpen_image(image_cv):
kernel = np.array([[0, -1, 0],
[-1, 5,-1],
[0, -1, 0]])
return cv2.filter2D(image_cv, -1, kernel)
@app.get("/")
async def main():
# واجهة مستخدم بسيطة جداً لرفع الصور
content = """
أداة استخراج وتقطيع الفريمات (Sprite Extractor)
"""
return HTMLResponse(content=content)
@app.post("/process-image/")
async def process_image(file: UploadFile = File(...)):
# 1. قراءة الصورة المرفوعة
contents = await file.read()
input_image = Image.open(io.BytesIO(contents)).convert("RGBA")
# 2. إزالة الخلفية باستخدام الذكاء الاصطناعي (Rembg)
output_image = remove(input_image)
# تحويل الصورة إلى مصفوفة (Array) لكي يفهمها OpenCV
open_cv_image = np.array(output_image)
# 3. تحسين حدة الصورة (حتى لا تكون ضبابية)
open_cv_image = sharpen_image(open_cv_image)
# 4. تحويل الصورة إلى الأبيض والأسود لاكتشاف الأشكال (الفريمات)
# نأخذ قناة الشفافية (Alpha Channel) لأننا أزلنا الخلفية
alpha_channel = open_cv_image[:, :, 3]
_, thresh = cv2.threshold(alpha_channel, 10, 255, cv2.THRESH_BINARY)
# 5. البحث عن الفريمات (Contours)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# ترتيب الفريمات من اليسار إلى اليمين (حتى تكون الأنيميشن مرتبة)
boundingBoxes = [cv2.boundingRect(c) for c in contours]
(contours, boundingBoxes) = zip(*sorted(zip(contours, boundingBoxes), key=lambda b: b[1][0]))
# تجهيز ملف الـ ZIP
zip_filename = "/code/temp/sprites.zip"
with zipfile.ZipFile(zip_filename, 'w') as zipf:
frame_number = 1
for contour in contours:
# 6. الحصول على أبعاد كل فريم بدون الفراغ حوله
x, y, w, h = cv2.boundingRect(contour)
# تجاهل النقاط الصغيرة جداً (التي قد تكون أخطاء بكسل)
if w > 5 and h > 5:
# قص الفريم
cropped_img = open_cv_image[y:y+h, x:x+w]
# تحويله إلى صورة PIL للحفظ
pil_img = Image.fromarray(cropped_img)
# حفظ الفريم مؤقتاً
frame_name = f"frame_{frame_number}.png"
temp_path = f"/code/temp/{frame_name}"
pil_img.save(temp_path, "PNG")
# إضافته إلى ملف ZIP
zipf.write(temp_path, arcname=frame_name)
# حذف الملف المؤقت لتوفير المساحة
os.remove(temp_path)
frame_number += 1
# 7. إرسال ملف ZIP للمستخدم
return FileResponse(path=zip_filename, filename="sprites_ready.zip", media_type='application/zip')