Skydata001 commited on
Commit
465be14
·
verified ·
1 Parent(s): afaa35e

Rename app.py to main.py

Browse files
Files changed (2) hide show
  1. app.py +0 -320
  2. main.py +92 -0
app.py DELETED
@@ -1,320 +0,0 @@
1
- import streamlit as st
2
- import cv2
3
- import numpy as np
4
- from PIL import Image
5
- import io
6
- import zipfile
7
- import os
8
- from datetime import datetime
9
- import tempfile
10
-
11
- from sprite_processor import SpriteProcessor
12
- from frame_detector import FrameDetector
13
- from frame_namer import FrameNamer
14
-
15
- # Page config
16
- st.set_page_config(
17
- page_title="AI Sprite Frame Extractor",
18
- page_icon="🎮",
19
- layout="wide",
20
- initial_sidebar_state="expanded"
21
- )
22
-
23
- # Custom CSS
24
- st.markdown("""
25
- <style>
26
- .main-header {
27
- text-align: center;
28
- padding: 2rem 0;
29
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
30
- border-radius: 15px;
31
- margin-bottom: 2rem;
32
- }
33
- .main-header h1 {
34
- color: white;
35
- font-size: 3rem;
36
- margin: 0;
37
- text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
38
- }
39
- .main-header p {
40
- color: rgba(255,255,255,0.9);
41
- font-size: 1.2rem;
42
- margin-top: 0.5rem;
43
- }
44
- .feature-card {
45
- background: linear-gradient(145deg, #f5f7fa 0%, #c3cfe2 100%);
46
- padding: 1.5rem;
47
- border-radius: 10px;
48
- margin: 1rem 0;
49
- border-left: 4px solid #667eea;
50
- }
51
- .stButton>button {
52
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
53
- color: white;
54
- font-weight: bold;
55
- padding: 0.75rem 2rem;
56
- border-radius: 25px;
57
- border: none;
58
- font-size: 1.1rem;
59
- transition: all 0.3s ease;
60
- }
61
- .stButton>button:hover {
62
- transform: translateY(-2px);
63
- box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
64
- }
65
- .success-box {
66
- background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
67
- color: white;
68
- padding: 1rem;
69
- border-radius: 10px;
70
- text-align: center;
71
- }
72
- .frame-preview {
73
- border: 2px solid #667eea;
74
- border-radius: 8px;
75
- padding: 5px;
76
- margin: 5px;
77
- }
78
- </style>
79
- """, unsafe_allow_html=True)
80
-
81
- # Header
82
- st.markdown("""
83
- <div class="main-header">
84
- <h1>🎮 AI Sprite Frame Extractor</h1>
85
- <p>استخرج وحسّن إطارات Sprite Sheets بذكاء اصطناعي</p>
86
- </div>
87
- """, unsafe_allow_html=True)
88
-
89
- # Sidebar
90
- with st.sidebar:
91
- st.markdown("## ⚙️ الإعدادات")
92
-
93
- st.markdown("### 🔧 خيارات المعالجة")
94
-
95
- enhance_quality = st.checkbox("✨ تحسين الجودة (Upscale)", value=True,
96
- help="رفع دقة الصورة وإزالة الضبابية")
97
-
98
- if enhance_quality:
99
- scale_factor = st.slider("📏 معامل التكبير", 2, 4, 4,
100
- help="2x = ضعف الحجم، 4x = أربع أضعاف")
101
- else:
102
- scale_factor = 1
103
-
104
- auto_detect = st.checkbox("🤖 اكتشاف تلقائي للإطارات", value=True,
105
- help="تحديد الإطارات تلقائياً")
106
-
107
- if not auto_detect:
108
- manual_frames = st.number_input("عدد الإطارات", min_value=1, max_value=50, value=8)
109
-
110
- smart_naming = st.checkbox("🏷️ تسمية ذكية", value=True,
111
- help="تحديد نوع الحركة تلقائياً")
112
-
113
- padding = st.slider("📐 الهوامش الداخلية", 0, 20, 2,
114
- help="إضافة هوامش حول كل إطار")
115
-
116
- st.markdown("---")
117
- st.markdown("### 📊 معلومات")
118
- st.info("💡 **نصيحة:** تأكد من أن الصورة بخلفية شفافة للحصول على أفضل نتائج")
119
-
120
- # Main content
121
- st.markdown("## 📤 رفع الصورة")
122
-
123
- uploaded_file = st.file_uploader(
124
- "اختر صورة Sprite Sheet",
125
- type=['png', 'jpg', 'jpeg', 'webp'],
126
- help="صورة تحتوي على إطارات متحركة متتالية"
127
- )
128
-
129
- if uploaded_file is not None:
130
- # Read image
131
- file_bytes = np.asarray(bytearray(uploaded_file.read()), dtype=np.uint8)
132
- original_image = cv2.imdecode(file_bytes, cv2.IMREAD_UNCHANGED)
133
-
134
- # Display original
135
- col1, col2 = st.columns(2)
136
-
137
- with col1:
138
- st.markdown("### 🖼️ الصورة الأصلية")
139
-
140
- # Convert for display
141
- if len(original_image.shape) == 3 and original_image.shape[2] == 4:
142
- display_img = cv2.cvtColor(original_image, cv2.COLOR_BGRA2RGBA)
143
- else:
144
- display_img = cv2.cvtColor(original_image, cv2.COLOR_BGR2RGB)
145
-
146
- st.image(display_img, use_column_width=True)
147
-
148
- st.markdown(f"""
149
- **الأبعاد:** {original_image.shape[1]} × {original_image.shape[0]} px
150
- **القنوات:** {original_image.shape[2] if len(original_image.shape) == 3 else 1}
151
- """)
152
-
153
- # Process button
154
- st.markdown("---")
155
-
156
- if st.button("🚀 بدء المعالجة", use_container_width=True):
157
- with st.spinner("⏳ جاري المعالجة... قد يستغرق هذا ب��ع دقائق"):
158
-
159
- # Initialize processors
160
- progress_bar = st.progress(0)
161
- status_text = st.empty()
162
-
163
- # Step 1: Enhance quality
164
- if enhance_quality:
165
- status_text.text("✨ جاري تحسين جودة الصورة...")
166
- processor = SpriteProcessor()
167
- enhanced_image = processor.enhance_image(original_image, scale_factor)
168
- progress_bar.progress(25)
169
- else:
170
- enhanced_image = original_image
171
- progress_bar.progress(25)
172
-
173
- # Step 2: Detect frames
174
- status_text.text("🔍 جاري اكتشاف الإطارات...")
175
- detector = FrameDetector()
176
-
177
- if auto_detect:
178
- frames, frame_boxes = detector.detect_frames_auto(enhanced_image, padding)
179
- else:
180
- frames, frame_boxes = detector.detect_frames_manual(enhanced_image, manual_frames, padding)
181
-
182
- progress_bar.progress(60)
183
-
184
- # Step 3: Smart naming
185
- if smart_naming:
186
- status_text.text("🏷️ جاري تحليل وتسمية الإطارات...")
187
- namer = FrameNamer()
188
- frame_names = namer.name_frames(frames)
189
- else:
190
- frame_names = [f"frame_{i:03d}" for i in range(len(frames))]
191
-
192
- progress_bar.progress(80)
193
-
194
- # Step 4: Create ZIP
195
- status_text.text("📦 جاري إنشاء ملف ZIP...")
196
- zip_buffer = io.BytesIO()
197
-
198
- with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
199
- for i, (frame, name) in enumerate(zip(frames, frame_names)):
200
- # Convert frame to PNG bytes
201
- if len(frame.shape) == 3 and frame.shape[2] == 4:
202
- frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGRA2RGBA)
203
- else:
204
- frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
205
-
206
- pil_img = Image.fromarray(frame_rgb)
207
- img_buffer = io.BytesIO()
208
- pil_img.save(img_buffer, format='PNG')
209
- img_buffer.seek(0)
210
-
211
- zip_file.writestr(f"{name}.png", img_buffer.getvalue())
212
-
213
- # Add info file
214
- info_content = f"""Sprite Frame Extractor - Export Report
215
- Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
216
- Total Frames: {len(frames)}
217
- Original Size: {original_image.shape[1]}x{original_image.shape[0]}
218
- Enhanced: {'Yes (' + str(scale_factor) + 'x)' if enhance_quality else 'No'}
219
-
220
- Frame List:
221
- """
222
- for i, name in enumerate(frame_names):
223
- info_content += f" {i+1}. {name}.png\n"
224
-
225
- zip_file.writestr("info.txt", info_content)
226
-
227
- zip_buffer.seek(0)
228
- progress_bar.progress(100)
229
- status_text.empty()
230
-
231
- # Display results
232
- with col2:
233
- st.markdown("### ✅ النتيجة")
234
-
235
- if enhance_quality:
236
- st.markdown(f"**الأبعاد بعد التحسين:** {enhanced_image.shape[1]} × {enhanced_image.shape[0]} px")
237
-
238
- st.markdown(f"**عدد الإطارات المكتشفة:** {len(frames)}")
239
-
240
- # Show frame previews
241
- st.markdown("### 👁️ معاينة الإطارات")
242
-
243
- preview_cols = st.columns(min(4, len(frames)))
244
- for i, (frame, name) in enumerate(zip(frames[:8], frame_names[:8])):
245
- with preview_cols[i % 4]:
246
- if len(frame.shape) == 3 and frame.shape[2] == 4:
247
- display_frame = cv2.cvtColor(frame, cv2.COLOR_BGRA2RGBA)
248
- else:
249
- display_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
250
-
251
- st.image(display_frame, caption=name, use_column_width=True)
252
-
253
- if len(frames) > 8:
254
- st.info(f"... و {len(frames) - 8} إطارات أخرى")
255
-
256
- # Download button
257
- st.markdown("---")
258
- st.markdown("### 📥 التحميل")
259
-
260
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
261
-
262
- st.download_button(
263
- label="⬇️ تحميل ملف ZIP",
264
- data=zip_buffer,
265
- file_name=f"sprite_frames_{timestamp}.zip",
266
- mime="application/zip",
267
- use_container_width=True
268
- )
269
-
270
- st.markdown("""
271
- <div class="success-box">
272
- <h3>🎉 تمت المعالجة بنج��ح!</h3>
273
- <p>تم استخراج {} إطار وجاهزة للاستخدام</p>
274
- </div>
275
- """.format(len(frames)), unsafe_allow_html=True)
276
-
277
- else:
278
- # Show features when no image uploaded
279
- st.markdown("---")
280
- st.markdown("## ✨ مميزات الأداة")
281
-
282
- col1, col2, col3 = st.columns(3)
283
-
284
- with col1:
285
- st.markdown("""
286
- <div class="feature-card">
287
- <h3>🔍 تحسين الجودة</h3>
288
- <p>استخدام تقنية Real-ESRGAN لرفع دقة الصور وإزالة الضبابية مع الحفاظ على جودة البكسل</p>
289
- </div>
290
- """, unsafe_allow_html=True)
291
-
292
- with col2:
293
- st.markdown("""
294
- <div class="feature-card">
295
- <h3>🤖 اكتشاف ذكي</h3>
296
- <p>اكتشاف الإطارات تلقائياً باستخدام خوارزميات متقدمة للرؤية الحاسوبية</p>
297
- </div>
298
- """, unsafe_allow_html=True)
299
-
300
- with col3:
301
- st.markdown("""
302
- <div class="feature-card">
303
- <h3>🏷️ تسمية ذكية</h3>
304
- <p>تحديد نوع الحركة (Idle, Run, Attack...) تلقائياً باستخدام نماذج التعلم العميق</p>
305
- </div>
306
- """, unsafe_allow_html=True)
307
-
308
- # Example section
309
- st.markdown("---")
310
- st.markdown("## 📋 كيفية الاستخدام")
311
-
312
- st.markdown("""
313
- 1. **📤 ارفع الصورة** - اختر ملف Sprite Sheet بخلفية شفافة
314
- 2. **⚙️ اضبط الإعدادات** - فعّل خيارات التحسين والتسمية الذكية حسب الحاجة
315
- 3. **🚀 ابدأ المعالجة** - اضغط على زر المعالجة وانتظر قليلاً
316
- 4. **📥 حمل النتيجة** - احصل على ملف ZIP يحتوي على جميع الإطارات
317
- """)
318
-
319
- st.markdown("---")
320
- st.info("💡 **ملاحظة:** للحصول على أفضل النتائج، تأكد من أن الصورة بخلفية شفافة (PNG مع قناة ألفا)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
main.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, File, UploadFile
2
+ from fastapi.responses import FileResponse, HTMLResponse
3
+ import cv2
4
+ import numpy as np
5
+ from rembg import remove
6
+ from PIL import Image
7
+ import io
8
+ import zipfile
9
+ import os
10
+
11
+ app = FastAPI()
12
+
13
+ # دالة لتحسين حدة الصورة (Sharpening) للحفاظ على جودة البكسل
14
+ def sharpen_image(image_cv):
15
+ kernel = np.array([[0, -1, 0],
16
+ [-1, 5,-1],
17
+ [0, -1, 0]])
18
+ return cv2.filter2D(image_cv, -1, kernel)
19
+
20
+ @app.get("/")
21
+ async def main():
22
+ # واجهة مستخدم بسيطة جداً لرفع الصور
23
+ content = """
24
+ <html>
25
+ <body style="font-family: Arial; text-align: center; margin-top: 50px;">
26
+ <h2>أداة استخراج وتقطيع الفريمات (Sprite Extractor)</h2>
27
+ <form action="/process-image/" enctype="multipart/form-data" method="post">
28
+ <input name="file" type="file" accept="image/*">
29
+ <input type="submit" value="معالجة وتحميل ZIP">
30
+ </form>
31
+ </body>
32
+ </html>
33
+ """
34
+ return HTMLResponse(content=content)
35
+
36
+ @app.post("/process-image/")
37
+ async def process_image(file: UploadFile = File(...)):
38
+ # 1. قراءة الصورة المرفوعة
39
+ contents = await file.read()
40
+ input_image = Image.open(io.BytesIO(contents)).convert("RGBA")
41
+
42
+ # 2. إزالة الخلفية باستخدام الذكاء الاصطناعي (Rembg)
43
+ output_image = remove(input_image)
44
+
45
+ # تحويل الصورة إلى مصفوفة (Array) لكي يفهمها OpenCV
46
+ open_cv_image = np.array(output_image)
47
+
48
+ # 3. تحسين حدة الصورة (حتى لا تكون ضبابية)
49
+ open_cv_image = sharpen_image(open_cv_image)
50
+
51
+ # 4. تحويل الصورة إلى الأبيض والأسود لاكتشاف الأشكال (الفريمات)
52
+ # نأخذ قناة الشفافية (Alpha Channel) لأننا أزلنا الخلفية
53
+ alpha_channel = open_cv_image[:, :, 3]
54
+ _, thresh = cv2.threshold(alpha_channel, 10, 255, cv2.THRESH_BINARY)
55
+
56
+ # 5. البحث عن الفريمات (Contours)
57
+ contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
58
+
59
+ # ترتيب الفريمات من اليسار إلى اليمين (حتى تكون الأنيميشن مرتبة)
60
+ boundingBoxes = [cv2.boundingRect(c) for c in contours]
61
+ (contours, boundingBoxes) = zip(*sorted(zip(contours, boundingBoxes), key=lambda b: b[1][0]))
62
+
63
+ # تجهيز ملف الـ ZIP
64
+ zip_filename = "/code/temp/sprites.zip"
65
+ with zipfile.ZipFile(zip_filename, 'w') as zipf:
66
+ frame_number = 1
67
+ for contour in contours:
68
+ # 6. الحصول على أبعاد كل فريم بدون الفراغ حوله
69
+ x, y, w, h = cv2.boundingRect(contour)
70
+
71
+ # تجاهل النقاط الصغيرة جداً (التي قد تكون أخطاء بكسل)
72
+ if w > 5 and h > 5:
73
+ # قص الفريم
74
+ cropped_img = open_cv_image[y:y+h, x:x+w]
75
+
76
+ # تحويله إلى صورة PIL للحفظ
77
+ pil_img = Image.fromarray(cropped_img)
78
+
79
+ # حفظ الفريم مؤقتاً
80
+ frame_name = f"frame_{frame_number}.png"
81
+ temp_path = f"/code/temp/{frame_name}"
82
+ pil_img.save(temp_path, "PNG")
83
+
84
+ # إضافته إلى ملف ZIP
85
+ zipf.write(temp_path, arcname=frame_name)
86
+
87
+ # حذف الملف المؤقت لتوفير المساحة
88
+ os.remove(temp_path)
89
+ frame_number += 1
90
+
91
+ # 7. إرسال ملف ZIP للمستخدم
92
+ return FileResponse(path=zip_filename, filename="sprites_ready.zip", media_type='application/zip')