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')