Spaces:
Sleeping
Sleeping
| 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) | |
| async def main(): | |
| # واجهة مستخدم بسيطة جداً لرفع الصور | |
| content = """ | |
| <html> | |
| <body style="font-family: Arial; text-align: center; margin-top: 50px;"> | |
| <h2>أداة استخراج وتقطيع الفريمات (Sprite Extractor)</h2> | |
| <form action="/process-image/" enctype="multipart/form-data" method="post"> | |
| <input name="file" type="file" accept="image/*"> | |
| <input type="submit" value="معالجة وتحميل ZIP"> | |
| </form> | |
| </body> | |
| </html> | |
| """ | |
| return HTMLResponse(content=content) | |
| 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') | |